/[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 611 - (hide annotations) (download)
Thu Jan 15 13:41:01 2004 UTC (9 years, 5 months ago) by rousseau
File MIME type: text/plain
File size: 13359 byte(s)
use an (int *) instead of a PDWORD for rx_length argument
1 rousseau 269 /*
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 "commands.h"
28 rousseau 407 #include "ccid.h"
29 rousseau 269 #include "defs.h"
30 rousseau 611 #include "ifdhandler.h"
31 rousseau 269 #include "config.h"
32     #include "debug.h"
33    
34    
35 rousseau 407 /*****************************************************************************
36     *
37     * CmdPowerOn
38     *
39     ****************************************************************************/
40 rousseau 269 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 rousseau 407 _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);
47 rousseau 269
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 rousseau 407 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 rousseau 269 cmd[8] = cmd[9] = 0; /* RFU */
60    
61 rousseau 407 res = WritePort(lun, sizeof(cmd), cmd);
62 rousseau 269 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 rousseau 407 res = ReadPort(lun, nlength, buffer);
70 rousseau 269 if (res != STATUS_SUCCESS)
71     return IFD_COMMUNICATION_ERROR;
72    
73 rousseau 407 if (buffer[STATUS_OFFSET] & CCID_COMMAND_FAILED)
74 rousseau 269 {
75     ccid_error(buffer[ERROR_OFFSET], __FILE__, __LINE__); /* bError */
76    
77     /* Protocol error in EMV mode */
78 rousseau 407 if (buffer[ERROR_OFFSET] == 0xBB &&
79     ccid_descriptor->readerID == GEMPC433)
80 rousseau 269 {
81 rousseau 407 if ((return_value = CmdEscape(lun, ESC_GEMPC_SET_ISO_MODE)) != IFD_SUCCESS)
82 rousseau 269 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 rousseau 407
107     /*****************************************************************************
108     *
109     * Escape
110     *
111     ****************************************************************************/
112     RESPONSECODE CmdEscape(int lun, int command)
113 rousseau 269 {
114     unsigned char cmd[12];
115     status_t res;
116     int length;
117     RESPONSECODE return_value = IFD_SUCCESS;
118 rousseau 407 _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);
119 rousseau 269
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 rousseau 407 cmd[6] = ccid_descriptor->bSeq++;
124 rousseau 269 cmd[7] = cmd[8] = cmd[9] = 0; /* RFU */
125    
126     switch (command)
127     {
128     /* Gemplus proprietary */
129 rousseau 407 case ESC_GEMPC_SET_ISO_MODE:
130     DEBUG_INFO("Switch reader in ISO mode");
131 rousseau 269 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 rousseau 407 /* 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 rousseau 269 default:
147     DEBUG_CRITICAL2("Unkown Escape command: %d", command);
148     return return_value;
149     }
150    
151 rousseau 407 res = WritePort(lun, length, cmd);
152 rousseau 269 if (res != STATUS_SUCCESS)
153     return IFD_COMMUNICATION_ERROR;
154    
155     length = sizeof(cmd);
156 rousseau 407 res = ReadPort(lun, &length, cmd);
157 rousseau 269 if (res != STATUS_SUCCESS)
158     return IFD_COMMUNICATION_ERROR;
159    
160 rousseau 407 if (cmd[STATUS_OFFSET] & CCID_COMMAND_FAILED)
161 rousseau 269 {
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 rousseau 407
170     /*****************************************************************************
171     *
172     * CmdPowerOff
173     *
174     ****************************************************************************/
175 rousseau 269 RESPONSECODE CmdPowerOff(int lun)
176     {
177     unsigned char cmd[10];
178     status_t res;
179     int length;
180     RESPONSECODE return_value = IFD_SUCCESS;
181 rousseau 407 _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);
182 rousseau 269
183     cmd[0] = 0x63; /* IccPowerOff */
184     cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0; /* dwLength */
185     cmd[5] = 0; /* slot number */
186 rousseau 407 cmd[6] = ccid_descriptor->bSeq++;
187 rousseau 269 cmd[7] = cmd[8] = cmd[9] = 0; /* RFU */
188    
189 rousseau 407 res = WritePort(lun, sizeof(cmd), cmd);
190 rousseau 269 if (res != STATUS_SUCCESS)
191     return IFD_COMMUNICATION_ERROR;
192    
193     length = sizeof(cmd);
194 rousseau 407 res = ReadPort(lun, &length, cmd);
195 rousseau 269 if (res != STATUS_SUCCESS)
196     return IFD_COMMUNICATION_ERROR;
197    
198 rousseau 407 if (cmd[STATUS_OFFSET] & CCID_COMMAND_FAILED)
199 rousseau 269 {
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 rousseau 407
208     /*****************************************************************************
209     *
210     * CmdGetSlotStatus
211     *
212     ****************************************************************************/
213 rousseau 269 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 rousseau 407 _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);
220 rousseau 269
221     cmd[0] = 0x65; /* GetSlotStatus */
222     cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0; /* dwLength */
223     cmd[5] = 0; /* slot number */
224 rousseau 407 cmd[6] = ccid_descriptor->bSeq++;
225 rousseau 269 cmd[7] = cmd[8] = cmd[9] = 0; /* RFU */
226    
227 rousseau 407 res = WritePort(lun, sizeof(cmd), cmd);
228 rousseau 269 if (res != STATUS_SUCCESS)
229     return IFD_COMMUNICATION_ERROR;
230    
231     length = SIZE_GET_SLOT_STATUS;
232 rousseau 407 res = ReadPort(lun, &length, buffer);
233 rousseau 269 if (res != STATUS_SUCCESS)
234     return IFD_COMMUNICATION_ERROR;
235    
236 rousseau 407 if (buffer[STATUS_OFFSET] & CCID_COMMAND_FAILED)
237 rousseau 269 {
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 rousseau 407
246     /*****************************************************************************
247     *
248     * CmdXfrBlock
249     *
250     ****************************************************************************/
251 rousseau 269 RESPONSECODE CmdXfrBlock(int lun, int tx_length, unsigned char tx_buffer[],
252 rousseau 611 int *rx_length, unsigned char rx_buffer[])
253 rousseau 269 {
254 rousseau 407 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 rousseau 611 unsigned char tx_buffer[], int *rx_length, unsigned char rx_buffer[])
307 rousseau 407 {
308 rousseau 269 unsigned char cmd[10+CMD_BUF_SIZE]; /* CCID + APDU buffer */
309     status_t res;
310     int length;
311     RESPONSECODE return_value = IFD_SUCCESS;
312 rousseau 407 _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);
313 rousseau 269
314 rousseau 407 DEBUG_COMM2("ShortAPDU: %d bytes", tx_length);
315    
316 rousseau 269 cmd[0] = 0x6F; /* IccPowerOff */
317     i2dw(tx_length, cmd+1); /* APDU length */
318     cmd[5] = 0; /* slot number */
319 rousseau 407 cmd[6] = ccid_descriptor->bSeq++;
320 rousseau 269 cmd[7] = cmd[8] = cmd[9] = 0; /* RFU */
321     memcpy(cmd+10, tx_buffer, tx_length);
322    
323 rousseau 407 res = WritePort(lun, 10+tx_length, cmd);
324 rousseau 269 if (res != STATUS_SUCCESS)
325     return IFD_COMMUNICATION_ERROR;
326    
327 rousseau 407 time_request:
328 rousseau 269 length = sizeof(cmd);
329 rousseau 407 res = ReadPort(lun, &length, cmd);
330 rousseau 269 if (res != STATUS_SUCCESS)
331     return IFD_COMMUNICATION_ERROR;
332    
333 rousseau 407 if (cmd[STATUS_OFFSET] & CCID_COMMAND_FAILED)
334 rousseau 269 {
335     ccid_error(cmd[ERROR_OFFSET], __FILE__, __LINE__); /* bError */
336     *rx_length = 0; /* nothing received */
337     return_value = IFD_COMMUNICATION_ERROR;
338     }
339    
340 rousseau 407 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 rousseau 269 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 rousseau 407 } /* CmdXfrBlockShortAPDU */
355 rousseau 269
356 rousseau 407
357     /*****************************************************************************
358     *
359     * CmdXfrBlockTPDU
360     *
361     ****************************************************************************/
362     RESPONSECODE CmdXfrBlockTPDU(int lun, int tx_length, unsigned char tx_buffer[],
363 rousseau 611 int *rx_length, unsigned char rx_buffer[])
364 rousseau 407 {
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 rousseau 550 #if 0
405 rousseau 407 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 rousseau 522 *rx_length = old_rx_length;
418    
419 rousseau 407 /* 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 rousseau 522 *rx_length = old_rx_length;
439    
440 rousseau 407 /* 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 rousseau 550 #endif
449 rousseau 407 }
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 rousseau 269 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