/[pcsclite]/trunk/Drivers/ccid/src/commands.c
ViewVC logotype

Contents of /trunk/Drivers/ccid/src/commands.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 550 - (show annotations) (download)
Tue Nov 4 21:57:19 2003 UTC (9 years, 6 months ago) by rousseau
File MIME type: text/plain
File size: 13364 byte(s)
comment out the automatic GET RESPONSE part. I don't think it should be
in the driver. Maybe in pcscd instead?
1 /*
2 commands.c: Commands sent to the card
3 Copyright (C) 2003 Ludovic Rousseau
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 /*
21 * $Id$
22 */
23
24 #include <string.h>
25
26 #include "pcscdefines.h"
27 #include "ifdhandler.h"
28 #include "commands.h"
29 #include "ccid.h"
30 #include "defs.h"
31 #include "config.h"
32 #include "debug.h"
33
34
35 /*****************************************************************************
36 *
37 * CmdPowerOn
38 *
39 ****************************************************************************/
40 RESPONSECODE CmdPowerOn(int lun, int * nlength, unsigned char buffer[])
41 {
42 unsigned char cmd[10];
43 status_t res;
44 int atr_len, length, count = 1;
45 RESPONSECODE return_value = IFD_SUCCESS;
46 _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);
47
48 /* store length of buffer[] */
49 length = *nlength;
50 again:
51 cmd[0] = 0x62; /* IccPowerOn */
52 cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0; /* dwLength */
53 cmd[5] = 0; /* slot number */
54 cmd[6] = ccid_descriptor->bSeq++;
55 if (ccid_descriptor->dwFeatures & CCID_CLASS_AUTO_VOLTAGE)
56 cmd[7] = 0x00; /* Automatic voltage selection */
57 else
58 cmd[7] = 0x01; /* 5.0V */
59 cmd[8] = cmd[9] = 0; /* RFU */
60
61 res = WritePort(lun, sizeof(cmd), cmd);
62 if (res != STATUS_SUCCESS)
63 return IFD_COMMUNICATION_ERROR;
64
65 /* reset available buffer size */
66 /* needed if we go back after a switch to ISO mode */
67 *nlength = length;
68
69 res = ReadPort(lun, nlength, buffer);
70 if (res != STATUS_SUCCESS)
71 return IFD_COMMUNICATION_ERROR;
72
73 if (buffer[STATUS_OFFSET] & CCID_COMMAND_FAILED)
74 {
75 ccid_error(buffer[ERROR_OFFSET], __FILE__, __LINE__); /* bError */
76
77 /* Protocol error in EMV mode */
78 if (buffer[ERROR_OFFSET] == 0xBB &&
79 ccid_descriptor->readerID == GEMPC433)
80 {
81 if ((return_value = CmdEscape(lun, ESC_GEMPC_SET_ISO_MODE)) != IFD_SUCCESS)
82 return return_value;
83
84 /* avoid looping if we can't switch mode */
85 if (count--)
86 goto again;
87 else
88 DEBUG_CRITICAL("Can't set reader in ISO mode");
89 }
90
91 return IFD_COMMUNICATION_ERROR;
92 }
93
94 /* extract the ATR */
95 atr_len = dw2i(buffer, 1); /* ATR length */
96 if (atr_len > *nlength)
97 atr_len = *nlength;
98 else
99 *nlength = atr_len;
100
101 memcpy(buffer, buffer+10, atr_len);
102
103 return return_value;
104 } /* CmdPowerOn */
105
106
107 /*****************************************************************************
108 *
109 * Escape
110 *
111 ****************************************************************************/
112 RESPONSECODE CmdEscape(int lun, int command)
113 {
114 unsigned char cmd[12];
115 status_t res;
116 int length;
117 RESPONSECODE return_value = IFD_SUCCESS;
118 _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);
119
120 cmd[0] = 0x6B; /* PC_to_RDR_Escape */
121 cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0; /* dwLength */
122 cmd[5] = 0; /* slot number */
123 cmd[6] = ccid_descriptor->bSeq++;
124 cmd[7] = cmd[8] = cmd[9] = 0; /* RFU */
125
126 switch (command)
127 {
128 /* Gemplus proprietary */
129 case ESC_GEMPC_SET_ISO_MODE:
130 DEBUG_INFO("Switch reader in ISO mode");
131 cmd[10] = 0x1F; /* switch mode */
132 cmd[11] = 0x01; /* set ISO mode */
133 cmd[1] = 2; /* length of data */
134 length = 12;
135 break;
136
137 /* Gemplus proprietary */
138 case ESC_GEMPC_SET_APDU_MODE:
139 DEBUG_INFO("Switch reader in APDU mode");
140 cmd[10] = 0xA0; /* switch mode */
141 cmd[11] = 0x02; /* set APDU mode */
142 cmd[1] = 2; /* length of data */
143 length = 12;
144 break;
145
146 default:
147 DEBUG_CRITICAL2("Unkown Escape command: %d", command);
148 return return_value;
149 }
150
151 res = WritePort(lun, length, cmd);
152 if (res != STATUS_SUCCESS)
153 return IFD_COMMUNICATION_ERROR;
154
155 length = sizeof(cmd);
156 res = ReadPort(lun, &length, cmd);
157 if (res != STATUS_SUCCESS)
158 return IFD_COMMUNICATION_ERROR;
159
160 if (cmd[STATUS_OFFSET] & CCID_COMMAND_FAILED)
161 {
162 ccid_error(cmd[ERROR_OFFSET], __FILE__, __LINE__); /* bError */
163 return_value = IFD_COMMUNICATION_ERROR;
164 }
165
166 return return_value;
167 } /* Escape */
168
169
170 /*****************************************************************************
171 *
172 * CmdPowerOff
173 *
174 ****************************************************************************/
175 RESPONSECODE CmdPowerOff(int lun)
176 {
177 unsigned char cmd[10];
178 status_t res;
179 int length;
180 RESPONSECODE return_value = IFD_SUCCESS;
181 _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);
182
183 cmd[0] = 0x63; /* IccPowerOff */
184 cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0; /* dwLength */
185 cmd[5] = 0; /* slot number */
186 cmd[6] = ccid_descriptor->bSeq++;
187 cmd[7] = cmd[8] = cmd[9] = 0; /* RFU */
188
189 res = WritePort(lun, sizeof(cmd), cmd);
190 if (res != STATUS_SUCCESS)
191 return IFD_COMMUNICATION_ERROR;
192
193 length = sizeof(cmd);
194 res = ReadPort(lun, &length, cmd);
195 if (res != STATUS_SUCCESS)
196 return IFD_COMMUNICATION_ERROR;
197
198 if (cmd[STATUS_OFFSET] & CCID_COMMAND_FAILED)
199 {
200 ccid_error(cmd[ERROR_OFFSET], __FILE__, __LINE__); /* bError */
201 return_value = IFD_COMMUNICATION_ERROR;
202 }
203
204 return return_value;
205 } /* CmdPowerOff */
206
207
208 /*****************************************************************************
209 *
210 * CmdGetSlotStatus
211 *
212 ****************************************************************************/
213 RESPONSECODE CmdGetSlotStatus(int lun, unsigned char buffer[])
214 {
215 unsigned char cmd[10];
216 status_t res;
217 int length;
218 RESPONSECODE return_value = IFD_SUCCESS;
219 _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);
220
221 cmd[0] = 0x65; /* GetSlotStatus */
222 cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0; /* dwLength */
223 cmd[5] = 0; /* slot number */
224 cmd[6] = ccid_descriptor->bSeq++;
225 cmd[7] = cmd[8] = cmd[9] = 0; /* RFU */
226
227 res = WritePort(lun, sizeof(cmd), cmd);
228 if (res != STATUS_SUCCESS)
229 return IFD_COMMUNICATION_ERROR;
230
231 length = SIZE_GET_SLOT_STATUS;
232 res = ReadPort(lun, &length, buffer);
233 if (res != STATUS_SUCCESS)
234 return IFD_COMMUNICATION_ERROR;
235
236 if (buffer[STATUS_OFFSET] & CCID_COMMAND_FAILED)
237 {
238 ccid_error(buffer[ERROR_OFFSET], __FILE__, __LINE__); /* bError */
239 return_value = IFD_COMMUNICATION_ERROR;
240 }
241
242 return return_value;
243 } /* CmdGetSlotStatus */
244
245
246 /*****************************************************************************
247 *
248 * CmdXfrBlock
249 *
250 ****************************************************************************/
251 RESPONSECODE CmdXfrBlock(int lun, int tx_length, unsigned char tx_buffer[],
252 PDWORD rx_length, unsigned char rx_buffer[])
253 {
254 RESPONSECODE return_value = IFD_SUCCESS;
255 _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);
256
257 /* command length too big for CCID reader? */
258 if (tx_length > ccid_descriptor->dwMaxCCIDMessageLength)
259 {
260 DEBUG_CRITICAL3("Command too long (%d bytes) for max: %d bytes",
261 tx_length, ccid_descriptor->dwMaxCCIDMessageLength);
262 return_value = IFD_COMMUNICATION_ERROR;
263 goto clean_up_and_return;
264 }
265
266 /* command length too big for CCID driver? */
267 if (tx_length > CMD_BUF_SIZE)
268 {
269 DEBUG_CRITICAL3("Command too long (%d bytes) for max: %d bytes",
270 tx_length, CMD_BUF_SIZE);
271 return_value = IFD_COMMUNICATION_ERROR;
272 goto clean_up_and_return;
273 }
274
275 /* APDU or TPDU? */
276 switch (ccid_descriptor->dwFeatures & CCID_CLASS_EXCHANGE_MASK)
277 {
278 case CCID_CLASS_TPDU:
279 return_value = CmdXfrBlockTPDU(lun, tx_length, tx_buffer, rx_length,
280 rx_buffer);
281 break;
282
283 case CCID_CLASS_SHORT_APDU:
284 case CCID_CLASS_EXTENDED_APDU:
285 /* We only support extended APDU if the reader can support the
286 * command length. See test above */
287 return_value = CmdXfrBlockShortAPDU(lun, tx_length, tx_buffer,
288 rx_length, rx_buffer);
289 break;
290
291 default:
292 return_value = IFD_COMMUNICATION_ERROR;
293 }
294
295 clean_up_and_return:
296 return return_value;
297 } /* CmdXfrBlock */
298
299
300 /*****************************************************************************
301 *
302 * CmdXfrBlockShortAPDU
303 *
304 ****************************************************************************/
305 RESPONSECODE CmdXfrBlockShortAPDU(int lun, int tx_length,
306 unsigned char tx_buffer[], PDWORD rx_length, unsigned char rx_buffer[])
307 {
308 unsigned char cmd[10+CMD_BUF_SIZE]; /* CCID + APDU buffer */
309 status_t res;
310 int length;
311 RESPONSECODE return_value = IFD_SUCCESS;
312 _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);
313
314 DEBUG_COMM2("ShortAPDU: %d bytes", tx_length);
315
316 cmd[0] = 0x6F; /* IccPowerOff */
317 i2dw(tx_length, cmd+1); /* APDU length */
318 cmd[5] = 0; /* slot number */
319 cmd[6] = ccid_descriptor->bSeq++;
320 cmd[7] = cmd[8] = cmd[9] = 0; /* RFU */
321 memcpy(cmd+10, tx_buffer, tx_length);
322
323 res = WritePort(lun, 10+tx_length, cmd);
324 if (res != STATUS_SUCCESS)
325 return IFD_COMMUNICATION_ERROR;
326
327 time_request:
328 length = sizeof(cmd);
329 res = ReadPort(lun, &length, cmd);
330 if (res != STATUS_SUCCESS)
331 return IFD_COMMUNICATION_ERROR;
332
333 if (cmd[STATUS_OFFSET] & CCID_COMMAND_FAILED)
334 {
335 ccid_error(cmd[ERROR_OFFSET], __FILE__, __LINE__); /* bError */
336 *rx_length = 0; /* nothing received */
337 return_value = IFD_COMMUNICATION_ERROR;
338 }
339
340 if (cmd[STATUS_OFFSET] & CCID_TIME_EXTENSION)
341 {
342 DEBUG_CRITICAL2("Time extension requested: 0x%02X", cmd[ERROR_OFFSET]);
343 goto time_request;
344 }
345
346 length = dw2i(cmd, 1);
347 if (length < *rx_length)
348 *rx_length = length;
349 else
350 length = *rx_length;
351 memcpy(rx_buffer, cmd+10, length);
352
353 return return_value;
354 } /* CmdXfrBlockShortAPDU */
355
356
357 /*****************************************************************************
358 *
359 * CmdXfrBlockTPDU
360 *
361 ****************************************************************************/
362 RESPONSECODE CmdXfrBlockTPDU(int lun, int tx_length, unsigned char tx_buffer[],
363 PDWORD rx_length, unsigned char rx_buffer[])
364 {
365 RESPONSECODE return_value = IFD_SUCCESS;
366 int Le;
367
368 DEBUG_COMM2("TPDU: %d bytes", tx_length);
369
370 /* Size should be command + one byte of length for
371 * an outgoing TPDU (CLA, INS, P1, P2, P3) */
372 if (tx_length == (ISO_CMD_SIZE + ISO_LENGTH_SIZE))
373 {
374 return_value = CmdXfrBlockShortAPDU(lun, tx_length, tx_buffer, rx_length,
375 rx_buffer);
376 }
377 else
378 {
379 /* just (CLA, INS, P1, P2) for an APDU */
380 if (tx_length == ISO_CMD_SIZE)
381 {
382 return_value = CmdXfrBlockShortAPDU(lun, tx_length, tx_buffer,
383 rx_length, rx_buffer);
384 }
385 else
386 {
387 DWORD ntestlength;
388
389 if (tx_length > (ISO_CMD_SIZE + ISO_LENGTH_SIZE))
390 {
391 /* Check length to see if it is a full APDU or a TPDU */
392 ntestlength = tx_buffer[ISO_OFFSET_LENGTH] +
393 ISO_CMD_SIZE + ISO_LENGTH_SIZE;
394
395 if (tx_length == (ntestlength + ISO_LENGTH_SIZE))
396 {
397 DWORD old_rx_length = *rx_length;
398
399 /* tx_buffer holds a proper APDU */
400 Le = tx_buffer[tx_length-1];
401 return_value = CmdXfrBlockShortAPDU(lun, tx_length,
402 tx_buffer, rx_length, rx_buffer);
403
404 #if 0
405 if ((return_value == IFD_SUCCESS) && (*rx_length == 2))
406 {
407 /* Buffer for Get Response */
408 tx_buffer[0] = 0x00;
409 tx_buffer[1] = 0xC0;
410 tx_buffer[2] = 0x00;
411 tx_buffer[3] = 0x00;
412
413 if (rx_buffer[0] == 0x61)
414 {
415 /* Get Response */
416 DEBUG_COMM2("TPDU: Automatic Get Response after 61%02X", rx_buffer[1]);
417 *rx_length = old_rx_length;
418
419 /* Card sent 61 xx
420 * xx = 0 means 256 */
421 if (rx_buffer[1] == 0)
422 /* we want to receive what the APDU asked */
423 rx_buffer[1] = Le;
424 if (Le == 0)
425 /* we want what the card want to send */
426 Le = rx_buffer[1];
427
428 /* Get Response with P3 = min(Le, Lx) */
429 tx_buffer[4] = rx_buffer[1] < Le ? rx_buffer[1] : Le;
430
431 return_value = CmdXfrBlockShortAPDU(lun, tx_length,
432 tx_buffer, rx_length, rx_buffer);
433 }
434 if ((rx_buffer[0] == 0x90) && (rx_buffer[1] == 0x00))
435 {
436 /* Get Response */
437 DEBUG_COMM("TPDU: Automatic Get Response after 9000");
438 *rx_length = old_rx_length;
439
440 /* Card sent 90 00
441 * Get Response with P3 = Le */
442 tx_buffer[4] = Le;
443
444 return_value = CmdXfrBlockShortAPDU(lun, tx_length,
445 tx_buffer, rx_length, rx_buffer);
446 }
447 }
448 #endif
449 }
450 else
451 {
452 if (tx_length > (ntestlength + ISO_LENGTH_SIZE))
453 {
454 /* Data are too long */
455 return_value = IFD_COMMUNICATION_ERROR;
456
457 goto clean_up_and_return;
458 }
459 else
460 {
461 /* tx_buffer holds a proper TPDU */
462 return_value = CmdXfrBlockShortAPDU(lun, tx_length, tx_buffer, rx_length, rx_buffer);
463 }
464 }
465 }
466 else
467 {
468 /* tx_buffer holds too little data to form an APDU+length */
469 return_value = IFD_COMMUNICATION_ERROR;
470
471 goto clean_up_and_return;
472 }
473 }
474 }
475
476 clean_up_and_return:
477 return return_value;
478 } /* CmdXfrBlockTPDU */
479
480
481 /*****************************************************************************
482 *
483 * i2dw
484 *
485 ****************************************************************************/
486 void i2dw(int value, unsigned char buffer[])
487 {
488 buffer[0] = value & 0xFF;
489 buffer[1] = (value >> 8) & 0xFF;
490 buffer[2] = (value >> 16) & 0xFF;
491 buffer[3] = (value >> 24) & 0xFF;
492 } /* i2dw */
493

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

  ViewVC Help
Powered by ViewVC 1.1.5