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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1076 by rousseau, Fri Jul 16 06:31:15 2004 UTC revision 2110 by rousseau, Wed Jul 12 09:52:30 2006 UTC
# Line 1  Line 1 
1  /*  /*
2      commands.c: Commands sent to the card      commands.c: Commands sent to the card
3      Copyright (C) 2003   Ludovic Rousseau      Copyright (C) 2003-2004   Ludovic Rousseau
4    
5      This program is free software; you can redistribute it and/or modify      This library is free software; you can redistribute it and/or
6      it under the terms of the GNU General Public License as published by      modify it under the terms of the GNU Lesser General Public
7      the Free Software Foundation; either version 2 of the License, or      License as published by the Free Software Foundation; either
8      (at your option) any later version.      version 2.1 of the License, or (at your option) any later version.
9    
10      This program is distributed in the hope that it will be useful,      This library is distributed in the hope that it will be useful,
11      but WITHOUT ANY WARRANTY; without even the implied warranty of      but WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      GNU General Public License for more details.      Lesser General Public License for more details.
14    
15      You should have received a copy of the GNU General Public License      You should have received a copy of the GNU Lesser General Public
16      along with this program; if not, write to the Free Software      License along with this library; if not, write to the Free Software
17      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
18  */  */
19    
20  /*  /*
# Line 23  Line 23 
23    
24  #include <string.h>  #include <string.h>
25  #include <stdlib.h>  #include <stdlib.h>
26  #include <PCSC/pcsclite.h>  #include <pcsclite.h>
27  #include <PCSC/ifdhandler.h>  #include <ifdhandler.h>
28    #include <reader.h>
29    
30  #include "commands.h"  #include "commands.h"
31  #include "openct/proto-t1.h"  #include "openct/proto-t1.h"
# Line 34  Line 35 
35  #include "config.h"  #include "config.h"
36  #include "debug.h"  #include "debug.h"
37    
38  /*  /* All the pinpad readers I used are more or less bogus
39   * Possible values :   * I use code to change the user command and make the firmware happy */
40   * 3 -> 1.8V, 3V, 5V  #define BOGUS_PINPAD_FIRMWARE
41   * 2 -> 3V, 5V  
42   * 1 -> 5V only  #define max( a, b )   ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) )
43   */  #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
44  /*  #define IFD_ERROR_INSUFFICIENT_BUFFER 700
  * To be safe we only use 5V  
  * otherwise we would have to parse the ATR and get the value of TAi (i>2) when  
  * in T=15  
  */  
 #define DEFAULT_VOLTAGE 1 /* start with 5 volts */  
45    
46  /* internal functions */  /* internal functions */
47  static RESPONSECODE CmdXfrBlockTPDU_T0(unsigned int lun, unsigned int tx_length,  static RESPONSECODE CmdXfrBlockAPDU_extended(unsigned int reader_index,
48          unsigned char tx_buffer[], unsigned int *rx_length,          unsigned int tx_length, unsigned char tx_buffer[], unsigned int *rx_length,
49          unsigned char rx_buffer[]);          unsigned char rx_buffer[]);
50    
51  static RESPONSECODE CmdXfrBlockTPDU_T1(unsigned int lun, unsigned int tx_length,  static RESPONSECODE CmdXfrBlockTPDU_T0(unsigned int reader_index,
52          unsigned char tx_buffer[], unsigned int *rx_length,          unsigned int tx_length, unsigned char tx_buffer[], unsigned int *rx_length,
53            unsigned char rx_buffer[]);
54    
55    static RESPONSECODE CmdXfrBlockCHAR_T0(unsigned int reader_index, unsigned int
56            tx_length, unsigned char tx_buffer[], unsigned int *rx_length, unsigned
57            char rx_buffer[]);
58    
59    static RESPONSECODE CmdXfrBlockTPDU_T1(unsigned int reader_index,
60            unsigned int tx_length, unsigned char tx_buffer[], unsigned int *rx_length,
61          unsigned char rx_buffer[]);          unsigned char rx_buffer[]);
62    
63  static void i2dw(int value, unsigned char *buffer);  static void i2dw(int value, unsigned char *buffer);
# Line 64  static void i2dw(int value, unsigned cha Line 68  static void i2dw(int value, unsigned cha
68   *                                      CmdPowerOn   *                                      CmdPowerOn
69   *   *
70   ****************************************************************************/   ****************************************************************************/
71  RESPONSECODE CmdPowerOn(unsigned int lun, unsigned int * nlength,  RESPONSECODE CmdPowerOn(unsigned int reader_index, unsigned int * nlength,
72          unsigned char buffer[])          unsigned char buffer[], int voltage)
73  {  {
74          unsigned char cmd[10];          unsigned char cmd[10];
75          status_t res;          status_t res;
76          int atr_len, length, count = 1;          int length, count = 1;
77            unsigned int atr_len;
78          RESPONSECODE return_value = IFD_SUCCESS;          RESPONSECODE return_value = IFD_SUCCESS;
79          _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);          _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index);
         char voltage;  
80    
81          /* store length of buffer[] */          /* store length of buffer[] */
82          length = *nlength;          length = *nlength;
83    
84          if (ccid_descriptor->dwFeatures & CCID_CLASS_AUTO_VOLTAGE)          if (ccid_descriptor->dwFeatures & CCID_CLASS_AUTO_VOLTAGE)
85                  voltage = 0;    /* automatic voltage selection */                  voltage = 0;    /* automatic voltage selection */
         else  
                 voltage = DEFAULT_VOLTAGE;  
86    
87  again:  again:
88          cmd[0] = 0x62; /* IccPowerOn */          cmd[0] = 0x62; /* IccPowerOn */
89          cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0;  /* dwLength */          cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0;  /* dwLength */
90          cmd[5] = 0;     /* slot number */          cmd[5] = ccid_descriptor->bCurrentSlotIndex;    /* slot number */
91          cmd[6] = ccid_descriptor->bSeq++;          cmd[6] = (*ccid_descriptor->pbSeq)++;
92          cmd[7] = voltage;          cmd[7] = voltage;
93          cmd[8] = cmd[9] = 0; /* RFU */          cmd[8] = cmd[9] = 0; /* RFU */
94    
95          res = WritePort(lun, sizeof(cmd), cmd);          res = WritePort(reader_index, sizeof(cmd), cmd);
96          if (res != STATUS_SUCCESS)          if (res != STATUS_SUCCESS)
97                  return IFD_COMMUNICATION_ERROR;                  return IFD_COMMUNICATION_ERROR;
98    
# Line 98  again: Line 100  again:
100          /* needed if we go back after a switch to ISO mode */          /* needed if we go back after a switch to ISO mode */
101          *nlength = length;          *nlength = length;
102    
103          res = ReadPort(lun, nlength, buffer);          res = ReadPort(reader_index, nlength, buffer);
104          if (res != STATUS_SUCCESS)          if (res != STATUS_SUCCESS)
105                  return IFD_COMMUNICATION_ERROR;                  return IFD_COMMUNICATION_ERROR;
106    
# Line 106  again: Line 108  again:
108          {          {
109                  ccid_error(buffer[ERROR_OFFSET], __FILE__, __LINE__, __FUNCTION__);    /* bError */                  ccid_error(buffer[ERROR_OFFSET], __FILE__, __LINE__, __FUNCTION__);    /* bError */
110    
111                  /* Protocol error in EMV mode */                  if (0xBB == buffer[ERROR_OFFSET] &&     /* Protocol error in EMV mode */
112                  if (buffer[ERROR_OFFSET] == 0xBB &&                          ((GEMPC433 == ccid_descriptor->readerID)
113                          ccid_descriptor->readerID == GEMPC433)                          || (CHERRYXX33 == ccid_descriptor->readerID)))
114                  {                  {
115                          unsigned char cmd[] = "\x1F\x01";                          unsigned char cmd[] = {0x1F, 0x01};
116                          unsigned char res[1];                          unsigned char res[1];
117                          unsigned int res_length = sizeof(res);                          unsigned int res_length = sizeof(res);
118    
119                          if ((return_value = CmdEscape(lun, cmd, sizeof(cmd)-1, res,                          if ((return_value = CmdEscape(reader_index, cmd, sizeof(cmd), res,
120                                  &res_length)) != IFD_SUCCESS)                                  &res_length)) != IFD_SUCCESS)
121                                  return return_value;                                  return return_value;
122    
# Line 128  again: Line 130  again:
130                  /* continue with 3 volts and 5 volts */                  /* continue with 3 volts and 5 volts */
131                  if (voltage > 1)                  if (voltage > 1)
132                  {                  {
133                            char *voltage_code[] = { "auto", "5V", "3V", "1.8V" };
134    
135                            DEBUG_INFO3("Power up with %s failed. Try with %s.",
136                                    voltage_code[voltage], voltage_code[voltage-1]);
137                          voltage--;                          voltage--;
138                          goto again;                          goto again;
139                  }                  }
# Line 150  again: Line 156  again:
156    
157  /*****************************************************************************  /*****************************************************************************
158   *   *
159   *                                      SecurePIN   *                                      SecurePINVerify
160   *   *
161   ****************************************************************************/   ****************************************************************************/
162  RESPONSECODE SecurePIN(unsigned int lun, const unsigned char TxBuffer[],  RESPONSECODE SecurePINVerify(unsigned int reader_index,
163          unsigned int TxLength, unsigned char RxBuffer[], unsigned int *RxLength)          unsigned char TxBuffer[], unsigned int TxLength,
164            unsigned char RxBuffer[], unsigned int *RxLength)
165  {  {
166          unsigned char cmd[11+14+CMD_BUF_SIZE];          unsigned char cmd[11+14+CMD_BUF_SIZE];
167          _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);          unsigned int a, b;
168          int length = 0;          _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index);
169            int old_read_timeout;
170            RESPONSECODE ret;
171    
172            cmd[0] = 0x69;  /* Secure */
173            cmd[5] = ccid_descriptor->bCurrentSlotIndex;    /* slot number */
174            cmd[6] = (*ccid_descriptor->pbSeq)++;
175            cmd[7] = 0;             /* bBWI */
176            cmd[8] = 0;             /* wLevelParameter */
177            cmd[9] = 0;
178            cmd[10] = 0;    /* bPINOperation: PIN Verification */
179    
180          /* PIN verification data structure WITHOUT TeoPrologue */          /* 19 is the size of the PCSCv2 PIN verify structure
181          if (TxBuffer[4] /* Lc */           * The equivalent CCID structure is only 14-bytes long */
182                  + 5 /* CLA, INS, P1, P2, Lc */          if (TxLength > 19+CMD_BUF_SIZE) /* command too large? */
                 + 11 /* CCID PIN verification data structure */ == TxLength)  
183          {          {
184                  i2dw(TxLength+3+1, cmd+1);      /* command length */                  DEBUG_INFO3("Command too long: %d > %d", TxLength, 19+CMD_BUF_SIZE);
185                    *RxLength = 0;
186                    return IFD_NOT_SUPPORTED;
187            }
188    
189                  /* copy the CCID data structure */          if (TxLength < 19+4 /* 4 = APDU size */)        /* command too short? */
190                  memcpy(cmd +11, TxBuffer + TxBuffer[4] + 5, 11);          {
191                    DEBUG_INFO3("Command too short: %d < %d", TxLength, 19+4);
192                    *RxLength = 0;
193                    return IFD_NOT_SUPPORTED;
194            }
195    
196                  /* TeoPrologue not used */          if (dw2i(TxBuffer, 15) + 19 != TxLength) /* ulDataLength field coherency */
197                  memset(cmd +11 +11, 0, 3);          {
198                    DEBUG_INFO3("Wrong lengths: %d %d", dw2i(TxBuffer, 15) + 19, TxLength);
199                    *RxLength = 0;
200                    return IFD_NOT_SUPPORTED;
201            }
202    
203                  /* copy the APDU */          /* make sure bEntryValidationCondition is valid
204                  memcpy(cmd +11 +14, TxBuffer, TxLength-11);           * The Cherry XX44 reader crashes with a wrong value */
205            if ((0x00 == TxBuffer[7]) || (TxBuffer[7] > 0x07))
206            {
207                    DEBUG_INFO2("Correct bEntryValidationCondition (was 0x%02X)",
208                            TxBuffer[7]);
209                    TxBuffer[7] = 0x02;
210            }
211    
212                  length = 14 + TxLength;  #ifdef BOGUS_PINPAD_FIRMWARE
213            /* bug circumvention for the GemPC Pinpad */
214            if (GEMPCPINPAD == ccid_descriptor->readerID)
215            {
216                    /* the firmware reject the cases: 00h No string and FFh default
217                     * CCID message. The only value supported is 01h (display 1 message) */
218                    if (0x01 != TxBuffer[8])
219                    {
220                            DEBUG_INFO2("Correct bNumberMessage for GemPC Pinpad (was %d)",
221                                    TxBuffer[8]);
222                            TxBuffer[8] = 0x01;
223                    }
224          }          }
225          /* PIN verification data structure WITH TeoPrologue */  #endif
226          else if (TxBuffer[4] /* Lc */  
227                  + 5 /* CLA, INS, P1, P2, Lc */          /* T=1 Protocol Management for a TPDU reader */
228                  + 14 /* CCID PIN verification data structure */ == TxLength)          if ((SCARD_PROTOCOL_T1 == ccid_descriptor->cardProtocol)
229                    && (CCID_CLASS_TPDU == (ccid_descriptor->dwFeatures & CCID_CLASS_EXCHANGE_MASK)))
230          {          {
231                  i2dw(TxLength+1, cmd+1);        /* command length */                  ct_buf_t sbuf;
232                    unsigned char sdata[T1_BUFFER_SIZE];
233    
234                  /* copy the CCID data structure */                  /* Initialize send buffer with the APDU */
235                  memcpy(cmd +11, TxBuffer + TxBuffer[4] + 5, 14);                  ct_buf_set(&sbuf,
236                            (void *)(TxBuffer + offsetof(PIN_VERIFY_STRUCTURE, abData)),
237                            TxLength - offsetof(PIN_VERIFY_STRUCTURE, abData));
238    
239                    /* Create T=1 block */
240                    ret = t1_build(&((get_ccid_slot(reader_index))->t1),
241                            sdata, 0, T1_I_BLOCK, &sbuf, NULL);
242    
243                    /* Increment the sequence numbers  */
244                    get_ccid_slot(reader_index)->t1.ns ^= 1;
245                    get_ccid_slot(reader_index)->t1.nr ^= 1;
246    
247                    /* Copy the generated T=1 block prologue into the teoprologue
248                     * of the CCID command */
249                    memcpy(TxBuffer + offsetof(PIN_VERIFY_STRUCTURE, bTeoPrologue),
250                            sdata, 3);
251            }
252    
253                  /* copy the APDU */          /* Build a CCID block from a PC/SC V2.1.2 Part 10 block */
254                  memcpy(cmd +11 +14, TxBuffer, TxLength-14);          for (a = 11, b = 0; b < TxLength; b++)
255            {
256                    if (1 == b) /* bTimeOut2 field */
257                            /* Ignore the second timeout as there's nothing we can do with
258                             * it currently */
259                            continue;
260    
261                    if ((b >= 15) && (b <= 18)) /* ulDataLength field (4 bytes) */
262                            /* the ulDataLength field is not present in the CCID frame
263                             * so do not copy */
264                            continue;
265    
266                    /* copy the CCID block 'verbatim' */
267                    cmd[a] = TxBuffer[b];
268                    a++;
269            }
270    
271                  length = 11 + TxLength;          /* SPR532 and Case 1 APDU */
272            if ((SPR532 == ccid_descriptor->readerID) && (TxBuffer[15] == 4))
273            {
274                    RESPONSECODE return_value;
275                    unsigned char cmd[] = { 0x80, 0x02, 0x00 };
276                    unsigned char res[1];
277                    unsigned int res_length = sizeof(res);
278    
279                    /* the SPR532 will append the PIN code without any padding */
280                    return_value = CmdEscape(reader_index, cmd, sizeof(cmd), res,
281                            &res_length);
282                    if (return_value != IFD_SUCCESS)
283                    {
284                            ccid_error(res[ERROR_OFFSET], __FILE__, __LINE__, __FUNCTION__);
285                            return return_value;
286                    }
287          }          }
288          else  
289            i2dw(a - 10, cmd + 1);  /* CCID message length */
290    
291            old_read_timeout = ccid_descriptor -> readTimeout;
292            ccid_descriptor -> readTimeout = max(30, TxBuffer[0]);  /* at least 30 seconds */
293    
294            if (WritePort(reader_index, a, cmd) != STATUS_SUCCESS)
295          {          {
296                  *RxLength = 0;                  *RxLength = 0;
297                  return IFD_COMMUNICATION_ERROR;                  return IFD_COMMUNICATION_ERROR;
298          }          }
299    
300            ret = CCID_Receive(reader_index, RxLength, RxBuffer, NULL);
301    
302            /* T=1 Protocol Management for a TPDU reader */
303            if ((IFD_SUCCESS == ret)
304                    && (SCARD_PROTOCOL_T1 == ccid_descriptor->cardProtocol)
305                    && (CCID_CLASS_TPDU == (ccid_descriptor->dwFeatures & CCID_CLASS_EXCHANGE_MASK)))
306            {
307                    /* timeout and cancel cases are faked by CCID_Receive() */
308                    if (2 == *RxLength)
309                    {
310                            /* Decrement the sequence numbers since no TPDU was sent */
311                            get_ccid_slot(reader_index)->t1.ns ^= 1;
312                            get_ccid_slot(reader_index)->t1.nr ^= 1;
313                    }
314                    else
315                    {
316                            /* get only the T=1 data */
317                            /* FIXME: manage T=1 error blocks */
318                            memmove(RxBuffer, RxBuffer+3, *RxLength -4);
319                            *RxLength -= 4; /* remove NAD, PCB, LEN and CRC */
320                    }
321            }
322    
323            ccid_descriptor -> readTimeout = old_read_timeout;
324            return ret;
325    } /* SecurePINVerify */
326    
327    
328    /*****************************************************************************
329     *
330     *                                      SecurePINModify
331     *
332     ****************************************************************************/
333    RESPONSECODE SecurePINModify(unsigned int reader_index,
334            unsigned char TxBuffer[], unsigned int TxLength,
335            unsigned char RxBuffer[], unsigned int *RxLength)
336    {
337            unsigned char cmd[11+19+CMD_BUF_SIZE];
338            unsigned int a, b;
339            _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index);
340            int old_read_timeout;
341            RESPONSECODE ret;
342    
343          cmd[0] = 0x69;  /* Secure */          cmd[0] = 0x69;  /* Secure */
344          cmd[5] = 0;             /* slot number */          cmd[5] = ccid_descriptor->bCurrentSlotIndex;    /* slot number */
345          cmd[6] = ccid_descriptor->bSeq++;          cmd[6] = (*ccid_descriptor->pbSeq)++;
346          cmd[7] = 0;             /* bBWI */          cmd[7] = 0;             /* bBWI */
347          cmd[8] = 0;             /* wLevelParameter */          cmd[8] = 0;             /* wLevelParameter */
348          cmd[9] = 0;          cmd[9] = 0;
349          cmd[10] = 0;    /* bPINOperation: PIN Verification */          cmd[10] = 1;    /* bPINOperation: PIN Modification */
350    
351          if (WritePort(lun, length, cmd) != STATUS_SUCCESS)          /* 24 is the size of the PCSC PIN modify structure
352                  return IFD_COMMUNICATION_ERROR;           * The equivalent CCID structure is only 18 or 19-bytes long */
353            if (TxLength > 24+CMD_BUF_SIZE) /* command too large? */
354            {
355                    DEBUG_INFO3("Command too long: %d > %d", TxLength, 24+CMD_BUF_SIZE);
356                    *RxLength = 0;
357                    return IFD_NOT_SUPPORTED;
358            }
359    
360            if (TxLength < 24+4 /* 4 = APDU size */) /* command too short? */
361            {
362                    DEBUG_INFO3("Command too short: %d < %d", TxLength, 24+4);
363                    *RxLength = 0;
364                    return IFD_NOT_SUPPORTED;
365            }
366    
367            if (dw2i(TxBuffer, 20) + 24 != TxLength) /* ulDataLength field coherency */
368            {
369                    DEBUG_INFO3("Wrong lengths: %d %d", dw2i(TxBuffer, 20) + 24, TxLength);
370                    *RxLength = 0;
371                    return IFD_NOT_SUPPORTED;
372            }
373    
374            /* Make sure in the beginning if bNumberMessage is valid or not */
375            if (TxBuffer[11] > 3)
376            {
377                    DEBUG_INFO2("Wrong bNumberMessage: %d", TxBuffer[11]);
378                    *RxLength = 0;
379                    return IFD_NOT_SUPPORTED;
380            }
381    
382            /* Make sure bEntryValidationCondition is valid
383             * The Cherry XX44 reader crashes with a wrong value */
384            if ((0x00 == TxBuffer[10]) || (TxBuffer[10] > 0x07))
385            {
386                    DEBUG_INFO2("Correct bEntryValidationCondition (was 0x%02X)",
387                            TxBuffer[10]);
388                    TxBuffer[10] = 0x02;
389            }
390    
391    #ifdef BOGUS_PINPAD_FIRMWARE
392            /* some firmwares are buggy so we try to "correct" the frame */
393            /*
394             * SPR 532 and Cherry ST 2000C has no display but requires _all_
395             * bMsgIndex fields with bNumberMessage set to 0.
396             */
397            if ((SPR532 == ccid_descriptor->readerID)
398                    || (CHERRYST2000 == ccid_descriptor->readerID))
399            {
400                    TxBuffer[11] = 0x03; /* set bNumberMessages to 3 so that
401                                                                    all bMsgIndex123 are filled */
402                    TxBuffer[14] = TxBuffer[15] = TxBuffer[16] = 0; /* bMsgIndex123 */
403            }
404    
405            /* the bug is a bit different than for the Cherry ST 2000C
406             * with bNumberMessages < 3 the command seems to be accepted
407             * and the card sends 6B 80 */
408            if (CHERRYXX44 == ccid_descriptor->readerID)
409            {
410                    TxBuffer[11] = 0x03; /* set bNumberMessages to 3 so that
411                                                                    all bMsgIndex123 are filled */
412            }
413    
414            /* bug circumvention for the GemPC Pinpad */
415            if (GEMPCPINPAD == ccid_descriptor->readerID)
416            {
417                    /* The reader does not support, and actively reject, "max size reached"
418                     * and "timeout occured" validation conditions */
419                    if (0x02 != TxBuffer[10])
420                    {
421                            DEBUG_INFO2("Correct bEntryValidationCondition for GemPC Pinpad (was %d)",
422                                    TxBuffer[10]);
423                            TxBuffer[10] = 0x02;    /* validation key pressed */
424                    }
425    
426                    /* the reader does not support any other value than 3 for the number
427                     * of messages */
428                    if (0x03 != TxBuffer[11])
429                    {
430                            DEBUG_INFO2("Correct bNumberMessages for GemPC Pinpad (was %d)",
431                                    TxBuffer[11]);
432                            TxBuffer[11] = 0x03; /* 3 messages */
433                    }
434            }
435    #endif
436    
437            /* T=1 Protocol Management for a TPDU reader */
438            if ((SCARD_PROTOCOL_T1 == ccid_descriptor->cardProtocol)
439                    && (CCID_CLASS_TPDU == (ccid_descriptor->dwFeatures & CCID_CLASS_EXCHANGE_MASK)))
440            {
441                    ct_buf_t sbuf;
442                    unsigned char sdata[T1_BUFFER_SIZE];
443    
444                    /* Initialize send buffer with the APDU */
445                    ct_buf_set(&sbuf,
446                            (void *)(TxBuffer + offsetof(PIN_MODIFY_STRUCTURE, abData)),
447                            TxLength - offsetof(PIN_MODIFY_STRUCTURE, abData));
448    
449                    /* Create T=1 block */
450                    ret = t1_build(&((get_ccid_slot(reader_index))->t1),
451                            sdata, 0, T1_I_BLOCK, &sbuf, NULL);
452    
453                    /* Increment the sequence numbers  */
454                    get_ccid_slot(reader_index)->t1.ns ^= 1;
455                    get_ccid_slot(reader_index)->t1.nr ^= 1;
456    
457                    /* Copy the generated T=1 block prologue into the teoprologue
458                     * of the CCID command */
459                    memcpy(TxBuffer + offsetof(PIN_MODIFY_STRUCTURE, bTeoPrologue),
460                            sdata, 3);
461            }
462    
463          return CCID_Receive(lun, RxLength, RxBuffer);          /* Build a CCID block from a PC/SC V2.1.2 Part 10 block */
464  } /* SecurePIN */  
465            /* Do adjustments as needed - CCID spec is not exact with some
466             * details in the format of the structure, per-reader adaptions
467             * might be needed.
468             */
469            for (a = 11, b = 0; b < TxLength; b++)
470            {
471                    if (1 == b) /* bTimeOut2 */
472                            /* Ignore the second timeout as there's nothing we can do with it
473                             * currently */
474                            continue;
475    
476                    if (15 == b) /* bMsgIndex2 */
477                    {
478                            /* in CCID the bMsgIndex2 is present only if bNumberMessage != 0 */
479                            if (0 == TxBuffer[11])
480                                    continue;
481                    }
482    
483                    if (16 == b) /* bMsgIndex3 */
484                    {
485                            /* in CCID the bMsgIndex3 is present only if bNumberMessage == 3 */
486                            if (TxBuffer[11] < 3)
487                                    continue;
488                    }
489    
490                    if ((b >= 20) && (b <= 23)) /* ulDataLength field (4 bytes) */
491                            /* the ulDataLength field is not present in the CCID frame
492                             * so do not copy */
493                            continue;
494    
495                    /* copy to the CCID block 'verbatim' */
496                    cmd[a] = TxBuffer[b];
497                    a++;
498            }
499    
500    #ifdef BOGUS_PINPAD_FIRMWARE
501            if ((SPR532 == ccid_descriptor->readerID)
502                    || (CHERRYST2000 == ccid_descriptor->readerID))
503            {
504                    cmd[21] = 0x00; /* set bNumberMessages to 0 */
505            }
506    #endif
507    
508            /* We know the size of the CCID message now */
509            i2dw(a - 10, cmd + 1);  /* command length (includes bPINOperation) */
510    
511            old_read_timeout = ccid_descriptor -> readTimeout;
512            ccid_descriptor -> readTimeout = max(30, TxBuffer[0]);  /* at least 30 seconds */
513    
514            if (WritePort(reader_index, a, cmd) != STATUS_SUCCESS)
515            {
516                    *RxLength = 0;
517                    return IFD_COMMUNICATION_ERROR;
518            }
519    
520            ret = CCID_Receive(reader_index, RxLength, RxBuffer, NULL);
521    
522            /* T=1 Protocol Management for a TPDU reader */
523            if ((IFD_SUCCESS == ret)
524                    && (SCARD_PROTOCOL_T1 == ccid_descriptor->cardProtocol)
525                    && (CCID_CLASS_TPDU == (ccid_descriptor->dwFeatures & CCID_CLASS_EXCHANGE_MASK)))
526            {
527                    /* timeout and cancel cases are faked by CCID_Receive() */
528                    if (2 == *RxLength)
529                    {
530                            /* Decrement the sequence numbers since no TPDU was sent */
531                            get_ccid_slot(reader_index)->t1.ns ^= 1;
532                            get_ccid_slot(reader_index)->t1.nr ^= 1;
533                    }
534                    else
535                    {
536                            /* get only the T=1 data */
537                            /* FIXME: manage T=1 error blocks */
538                            memmove(RxBuffer, RxBuffer+3, *RxLength -4);
539                            *RxLength -= 4; /* remove NAD, PCB, LEN and CRC */
540                    }
541            }
542    
543            ccid_descriptor -> readTimeout = old_read_timeout;
544            return ret;
545    } /* SecurePINModify */
546    
547    
548  /*****************************************************************************  /*****************************************************************************
# Line 219  RESPONSECODE SecurePIN(unsigned int lun, Line 550  RESPONSECODE SecurePIN(unsigned int lun,
550   *                                      Escape   *                                      Escape
551   *   *
552   ****************************************************************************/   ****************************************************************************/
553  RESPONSECODE CmdEscape(unsigned int lun, const unsigned char TxBuffer[],  RESPONSECODE CmdEscape(unsigned int reader_index,
554          unsigned int TxLength, unsigned char RxBuffer[], unsigned int *RxLength)          const unsigned char TxBuffer[], unsigned int TxLength,
555            unsigned char RxBuffer[], unsigned int *RxLength)
556  {  {
557          unsigned char *cmd_in, *cmd_out;          unsigned char *cmd_in, *cmd_out;
558          status_t res;          status_t res;
559          unsigned int length_in, length_out;          unsigned int length_in, length_out;
560          RESPONSECODE return_value = IFD_SUCCESS;          RESPONSECODE return_value = IFD_SUCCESS;
561          _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);          _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index);
562    
563    again:
564          /* allocate buffers */          /* allocate buffers */
565          length_in = 10 + TxLength;          length_in = 10 + TxLength;
566          if (NULL == (cmd_in = malloc(length_in)))          if (NULL == (cmd_in = malloc(length_in)))
# Line 242  RESPONSECODE CmdEscape(unsigned int lun, Line 575  RESPONSECODE CmdEscape(unsigned int lun,
575    
576          cmd_in[0] = 0x6B; /* PC_to_RDR_Escape */          cmd_in[0] = 0x6B; /* PC_to_RDR_Escape */
577          i2dw(length_in - 10, cmd_in+1); /* dwLength */          i2dw(length_in - 10, cmd_in+1); /* dwLength */
578          cmd_in[5] = 0;  /* slot number */          cmd_in[5] = ccid_descriptor->bCurrentSlotIndex; /* slot number */
579          cmd_in[6] = ccid_descriptor->bSeq++;          cmd_in[6] = (*ccid_descriptor->pbSeq)++;
580          cmd_in[7] = cmd_in[8] = cmd_in[9] = 0; /* RFU */          cmd_in[7] = cmd_in[8] = cmd_in[9] = 0; /* RFU */
581    
582          /* copy the command */          /* copy the command */
583          memcpy(&cmd_in[10], TxBuffer, TxLength);          memcpy(&cmd_in[10], TxBuffer, TxLength);
584    
585          res = WritePort(lun, length_in, cmd_in);          res = WritePort(reader_index, length_in, cmd_in);
586          free(cmd_in);          free(cmd_in);
587          if (res != STATUS_SUCCESS)          if (res != STATUS_SUCCESS)
588          {          {
# Line 257  RESPONSECODE CmdEscape(unsigned int lun, Line 590  RESPONSECODE CmdEscape(unsigned int lun,
590                  return IFD_COMMUNICATION_ERROR;                  return IFD_COMMUNICATION_ERROR;
591          }          }
592    
593          res = ReadPort(lun, &length_out, cmd_out);          res = ReadPort(reader_index, &length_out, cmd_out);
594    
595            /* replay the command if NAK
596             * This (generally) happens only for the first command sent to the reader
597             * with the serial protocol so it is not really needed for all the other
598             * ReadPort() calls */
599            if (STATUS_COMM_NAK == res)
600            {
601                    free(cmd_out);
602                    goto again;
603            }
604    
605          if (res != STATUS_SUCCESS)          if (res != STATUS_SUCCESS)
606          {          {
607                  free(cmd_out);                  free(cmd_out);
# Line 288  RESPONSECODE CmdEscape(unsigned int lun, Line 632  RESPONSECODE CmdEscape(unsigned int lun,
632   *                                      CmdPowerOff   *                                      CmdPowerOff
633   *   *
634   ****************************************************************************/   ****************************************************************************/
635  RESPONSECODE CmdPowerOff(unsigned int lun)  RESPONSECODE CmdPowerOff(unsigned int reader_index)
636  {  {
637          unsigned char cmd[10];          unsigned char cmd[10];
638          status_t res;          status_t res;
639          unsigned int length;          unsigned int length;
640          RESPONSECODE return_value = IFD_SUCCESS;          RESPONSECODE return_value = IFD_SUCCESS;
641          _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);          _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index);
642    
643          cmd[0] = 0x63; /* IccPowerOff */          cmd[0] = 0x63; /* IccPowerOff */
644          cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0;  /* dwLength */          cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0;  /* dwLength */
645          cmd[5] = 0;     /* slot number */          cmd[5] = ccid_descriptor->bCurrentSlotIndex;    /* slot number */
646          cmd[6] = ccid_descriptor->bSeq++;          cmd[6] = (*ccid_descriptor->pbSeq)++;
647          cmd[7] = cmd[8] = cmd[9] = 0; /* RFU */          cmd[7] = cmd[8] = cmd[9] = 0; /* RFU */
648    
649          res = WritePort(lun, sizeof(cmd), cmd);          res = WritePort(reader_index, sizeof(cmd), cmd);
650          if (res != STATUS_SUCCESS)          if (res != STATUS_SUCCESS)
651                  return IFD_COMMUNICATION_ERROR;                  return IFD_COMMUNICATION_ERROR;
652    
653          length = sizeof(cmd);          length = sizeof(cmd);
654          res = ReadPort(lun, &length, cmd);          res = ReadPort(reader_index, &length, cmd);
655          if (res != STATUS_SUCCESS)          if (res != STATUS_SUCCESS)
656                  return IFD_COMMUNICATION_ERROR;                  return IFD_COMMUNICATION_ERROR;
657    
# Line 326  RESPONSECODE CmdPowerOff(unsigned int lu Line 670  RESPONSECODE CmdPowerOff(unsigned int lu
670   *                                      CmdGetSlotStatus   *                                      CmdGetSlotStatus
671   *   *
672   ****************************************************************************/   ****************************************************************************/
673  RESPONSECODE CmdGetSlotStatus(unsigned int lun, unsigned char buffer[])  RESPONSECODE CmdGetSlotStatus(unsigned int reader_index, unsigned char buffer[])
674  {  {
675          unsigned char cmd[10];          unsigned char cmd[10];
676          status_t res;          status_t res;
677          unsigned int length;          unsigned int length;
678          RESPONSECODE return_value = IFD_SUCCESS;          RESPONSECODE return_value = IFD_SUCCESS;
679          _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);          _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index);
680    
681          cmd[0] = 0x65; /* GetSlotStatus */          cmd[0] = 0x65; /* GetSlotStatus */
682          cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0;  /* dwLength */          cmd[1] = cmd[2] = cmd[3] = cmd[4] = 0;  /* dwLength */
683          cmd[5] = 0;     /* slot number */          cmd[5] = ccid_descriptor->bCurrentSlotIndex;    /* slot number */
684          cmd[6] = ccid_descriptor->bSeq++;          cmd[6] = (*ccid_descriptor->pbSeq)++;
685          cmd[7] = cmd[8] = cmd[9] = 0; /* RFU */          cmd[7] = cmd[8] = cmd[9] = 0; /* RFU */
686    
687          res = WritePort(lun, sizeof(cmd), cmd);          res = WritePort(reader_index, sizeof(cmd), cmd);
688          if (res != STATUS_SUCCESS)          if (res != STATUS_SUCCESS)
689                  return IFD_COMMUNICATION_ERROR;                  return IFD_COMMUNICATION_ERROR;
690    
691          length = SIZE_GET_SLOT_STATUS;          length = SIZE_GET_SLOT_STATUS;
692          res = ReadPort(lun, &length, buffer);          res = ReadPort(reader_index, &length, buffer);
693          if (res != STATUS_SUCCESS)          if (res != STATUS_SUCCESS)
694                  return IFD_COMMUNICATION_ERROR;                  return IFD_COMMUNICATION_ERROR;
695    
696          if (buffer[STATUS_OFFSET] & CCID_COMMAND_FAILED)          if (buffer[STATUS_OFFSET] & CCID_COMMAND_FAILED)
697          {          {
698                  ccid_error(buffer[ERROR_OFFSET], __FILE__, __LINE__, __FUNCTION__);    /* bError */                  ccid_error(buffer[ERROR_OFFSET], __FILE__, __LINE__, __FUNCTION__);    /* bError */
699                  return_value = IFD_COMMUNICATION_ERROR;  
700                    /* card absent or mute is not an communication error */
701                    if (buffer[ERROR_OFFSET] != 0xFE)
702                            return_value = IFD_COMMUNICATION_ERROR;
703          }          }
704    
705          return return_value;          return return_value;
# Line 364  RESPONSECODE CmdGetSlotStatus(unsigned i Line 711  RESPONSECODE CmdGetSlotStatus(unsigned i
711   *                                      CmdXfrBlock   *                                      CmdXfrBlock
712   *   *
713   ****************************************************************************/   ****************************************************************************/
714  RESPONSECODE CmdXfrBlock(unsigned int lun, unsigned int tx_length,  RESPONSECODE CmdXfrBlock(unsigned int reader_index, unsigned int tx_length,
715          unsigned char tx_buffer[], unsigned int *rx_length,          unsigned char tx_buffer[], unsigned int *rx_length,
716          unsigned char rx_buffer[], int protocol)          unsigned char rx_buffer[], int protocol)
717  {  {
718          RESPONSECODE return_value = IFD_SUCCESS;          RESPONSECODE return_value = IFD_SUCCESS;
719          _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);          _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index);
   
         /* command length too big for CCID reader? */  
         if (tx_length > ccid_descriptor->dwMaxCCIDMessageLength)  
         {  
                 DEBUG_CRITICAL3("Command too long (%d bytes) for max: %d bytes",  
                         tx_length, ccid_descriptor->dwMaxCCIDMessageLength);  
                 return_value = IFD_COMMUNICATION_ERROR;  
                 goto clean_up_and_return;  
         }  
   
         /* command length too big for CCID driver? */  
         if (tx_length > CMD_BUF_SIZE)  
         {  
                 DEBUG_CRITICAL3("Command too long (%d bytes) for max: %d bytes",  
                         tx_length, CMD_BUF_SIZE);  
                 return_value = IFD_COMMUNICATION_ERROR;  
                 goto clean_up_and_return;  
         }  
720    
721          /* APDU or TPDU? */          /* APDU or TPDU? */
722          switch (ccid_descriptor->dwFeatures & CCID_CLASS_EXCHANGE_MASK)          switch (ccid_descriptor->dwFeatures & CCID_CLASS_EXCHANGE_MASK)
723          {          {
724                  case CCID_CLASS_TPDU:                  case CCID_CLASS_TPDU:
725                          if (protocol == T_0)                          if (protocol == T_0)
726                                  return_value = CmdXfrBlockTPDU_T0(lun, tx_length, tx_buffer,                                  return_value = CmdXfrBlockTPDU_T0(reader_index,
727                                          rx_length, rx_buffer);                                          tx_length, tx_buffer, rx_length, rx_buffer);
728                          else                          else
729                                  if (protocol == T_1)                                  if (protocol == T_1)
730                                          return_value = CmdXfrBlockTPDU_T1(lun, tx_length,                                          return_value = CmdXfrBlockTPDU_T1(reader_index, tx_length,
731                                                  tx_buffer, rx_length, rx_buffer);                                                  tx_buffer, rx_length, rx_buffer);
732                                  else                                  else
733                                          return_value = IFD_PROTOCOL_NOT_SUPPORTED;                                          return_value = IFD_PROTOCOL_NOT_SUPPORTED;
734                          break;                          break;
735    
736                  case CCID_CLASS_SHORT_APDU:                  case CCID_CLASS_SHORT_APDU:
737                            return_value = CmdXfrBlockTPDU_T0(reader_index,
738                                    tx_length, tx_buffer, rx_length, rx_buffer);
739                            break;
740    
741                  case CCID_CLASS_EXTENDED_APDU:                  case CCID_CLASS_EXTENDED_APDU:
742                          /* We only support extended APDU if the reader can support the                          return_value = CmdXfrBlockAPDU_extended(reader_index,
743                           * command length. See test above */                                  tx_length, tx_buffer, rx_length, rx_buffer);
744                          return_value = CmdXfrBlockTPDU_T0(lun, tx_length, tx_buffer,                          if (return_value != IFD_SUCCESS)
745                                  rx_length, rx_buffer);                                  *rx_length = 0;
746                            break;
747    
748                    case CCID_CLASS_CHARACTER:
749                            if (protocol == T_0)
750                                    return_value = CmdXfrBlockCHAR_T0(reader_index, tx_length,
751                                            tx_buffer, rx_length, rx_buffer);
752                            else
753                                    if (protocol == T_1)
754                                            return_value = CmdXfrBlockTPDU_T1(reader_index, tx_length,
755                                                    tx_buffer, rx_length, rx_buffer);
756                                    else
757                                            return_value = IFD_PROTOCOL_NOT_SUPPORTED;
758                          break;                          break;
759    
760                  default:                  default:
# Line 417  RESPONSECODE CmdXfrBlock(unsigned int lu Line 762  RESPONSECODE CmdXfrBlock(unsigned int lu
762                          return_value = IFD_COMMUNICATION_ERROR;                          return_value = IFD_COMMUNICATION_ERROR;
763          }          }
764    
 clean_up_and_return:  
765          return return_value;          return return_value;
766  } /* CmdXfrBlock */  } /* CmdXfrBlock */
767    
# Line 427  clean_up_and_return: Line 771  clean_up_and_return:
771   *                                      CCID_Transmit   *                                      CCID_Transmit
772   *   *
773   ****************************************************************************/   ****************************************************************************/
774  RESPONSECODE CCID_Transmit(unsigned int lun, unsigned int tx_length,  RESPONSECODE CCID_Transmit(unsigned int reader_index, unsigned int tx_length,
775          const unsigned char tx_buffer[], unsigned char bBWI)          const unsigned char tx_buffer[], unsigned short rx_length, unsigned char bBWI)
776  {  {
777          unsigned char cmd[10+CMD_BUF_SIZE];     /* CCID + APDU buffer */          unsigned char cmd[10+CMD_BUF_SIZE];     /* CCID + APDU buffer */
778          _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);          _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index);
779    
780          cmd[0] = 0x6F; /* XfrBlock */          cmd[0] = 0x6F; /* XfrBlock */
781          i2dw(tx_length, cmd+1); /* APDU length */          i2dw(tx_length, cmd+1); /* APDU length */
782          cmd[5] = 0;     /* slot number */          cmd[5] = ccid_descriptor->bCurrentSlotIndex;    /* slot number */
783          cmd[6] = ccid_descriptor->bSeq++;          cmd[6] = (*ccid_descriptor->pbSeq)++;
784          cmd[7] = bBWI;  /* extend block waiting timeout */          cmd[7] = bBWI;  /* extend block waiting timeout */
785          cmd[8] = cmd[9] = 0; /* RFU */          cmd[8] = rx_length & 0xFF;      /* Expected length, in character mode only */
786            cmd[9] = (rx_length >> 8) & 0xFF;
787    
788            /* check that the command is not too large */
789            if (tx_length > CMD_BUF_SIZE)
790            {
791                    DEBUG_CRITICAL2("TX Length too big: %d", tx_length);
792                    return IFD_NOT_SUPPORTED;
793            }
794    
795          memcpy(cmd+10, tx_buffer, tx_length);          memcpy(cmd+10, tx_buffer, tx_length);
796    
797          if (WritePort(lun, 10+tx_length, cmd) != STATUS_SUCCESS)          if (WritePort(reader_index, 10+tx_length, cmd) != STATUS_SUCCESS)
798                  return IFD_COMMUNICATION_ERROR;                  return IFD_COMMUNICATION_ERROR;
799    
800          return IFD_SUCCESS;          return IFD_SUCCESS;
# Line 453  RESPONSECODE CCID_Transmit(unsigned int Line 806  RESPONSECODE CCID_Transmit(unsigned int
806   *                                      CCID_Receive   *                                      CCID_Receive
807   *   *
808   ****************************************************************************/   ****************************************************************************/
809  RESPONSECODE CCID_Receive(unsigned int lun, unsigned int *rx_length,  RESPONSECODE CCID_Receive(unsigned int reader_index, unsigned int *rx_length,
810          unsigned char rx_buffer[])          unsigned char rx_buffer[], unsigned char *chain_parameter)
811  {  {
812          unsigned char cmd[10+CMD_BUF_SIZE];     /* CCID + APDU buffer */          unsigned char cmd[10+CMD_BUF_SIZE];     /* CCID + APDU buffer */
813          unsigned int length;          unsigned int length;
814            RESPONSECODE return_value = IFD_SUCCESS;
815    
816  time_request:  time_request:
817          length = sizeof(cmd);          length = sizeof(cmd);
818          if (ReadPort(lun, &length, cmd) != STATUS_SUCCESS)          if (ReadPort(reader_index, &length, cmd) != STATUS_SUCCESS)
819          {          {
820                  *rx_length = 0;                  *rx_length = 0;
821                  return IFD_COMMUNICATION_ERROR;                  return IFD_COMMUNICATION_ERROR;
822          }          }
823    
824            if (length < STATUS_OFFSET)
825            {
826                    DEBUG_CRITICAL2("Not enough data received: %d bytes", length);
827                    *rx_length = 0;
828                    return IFD_COMMUNICATION_ERROR;
829            }
830    
831          if (cmd[STATUS_OFFSET] & CCID_COMMAND_FAILED)          if (cmd[STATUS_OFFSET] & CCID_COMMAND_FAILED)
832          {          {
833                  ccid_error(cmd[ERROR_OFFSET], __FILE__, __LINE__, __FUNCTION__);    /* bError */                  ccid_error(cmd[ERROR_OFFSET], __FILE__, __LINE__, __FUNCTION__);    /* bError */
834                  *rx_length = 0; /* nothing received */                  switch (cmd[ERROR_OFFSET])
835                  if (0xFD == cmd[ERROR_OFFSET]) /* Parity error during exchange */                  {
836                          return IFD_PARITY_ERROR;                          case 0xEF:      /* cancel */
837                  else                                  if (*rx_length < 2)
838                          return IFD_COMMUNICATION_ERROR;                                          return IFD_COMMUNICATION_ERROR;
839                                    rx_buffer[0]= 0x64;
840                                    rx_buffer[1]= 0x01;
841                                    *rx_length = 2;
842                                    return IFD_SUCCESS;
843    
844                            case 0xF0:      /* timeout */
845                                    if (*rx_length < 2)
846                                            return IFD_COMMUNICATION_ERROR;
847                                    rx_buffer[0]= 0x64;
848                                    rx_buffer[1]= 0x00;
849                                    *rx_length = 2;
850                                    return IFD_SUCCESS;
851    
852                            case 0xFD:      /* Parity error during exchange */
853                                    *rx_length = 0; /* nothing received */
854                                    return IFD_PARITY_ERROR;
855    
856                            default:
857                                    *rx_length = 0; /* nothing received */
858                                    return IFD_COMMUNICATION_ERROR;
859                    }
860          }          }
861    
862          if (cmd[STATUS_OFFSET] & CCID_TIME_EXTENSION)          if (cmd[STATUS_OFFSET] & CCID_TIME_EXTENSION)
863          {          {
864                  DEBUG_CRITICAL2("Time extension requested: 0x%02X", cmd[ERROR_OFFSET]);                  DEBUG_COMM2("Time extension requested: 0x%02X", cmd[ERROR_OFFSET]);
865                  goto time_request;                  goto time_request;
866          }          }
867    
868          length = dw2i(cmd, 1);          length = dw2i(cmd, 1);
869          if (length < *rx_length)          if (length <= *rx_length)
870                  *rx_length = length;                  *rx_length = length;
871          else          else
872            {
873                    DEBUG_CRITICAL2("overrun by %d bytes", length - *rx_length);
874                  length = *rx_length;                  length = *rx_length;
875                    return_value = IFD_ERROR_INSUFFICIENT_BUFFER;
876            }
877          memcpy(rx_buffer, cmd+10, length);          memcpy(rx_buffer, cmd+10, length);
878    
879          return IFD_SUCCESS;          /* Extended case?
880             * Only valid for RDR_to_PC_DataBlock frames */
881            if (chain_parameter)
882                    *chain_parameter = cmd[CHAIN_PARAMETER_OFFSET];
883    
884            return return_value;
885  } /* CCID_Receive */  } /* CCID_Receive */
886    
887    
888  /*****************************************************************************  /*****************************************************************************
889   *   *
890     *                                      CmdXfrBlockAPDU_extended
891     *
892     ****************************************************************************/
893    static RESPONSECODE CmdXfrBlockAPDU_extended(unsigned int reader_index,
894            unsigned int tx_length, unsigned char tx_buffer[], unsigned int *rx_length,
895            unsigned char rx_buffer[])
896    {
897            RESPONSECODE return_value;
898            _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index);
899            unsigned char chain_parameter;
900            unsigned int local_tx_length, sent_length;
901            unsigned int local_rx_length, received_length;
902            int buffer_overflow = 0;
903    
904            DEBUG_COMM2("T=0 (extended): %d bytes", tx_length);
905    
906            /* send the APDU */
907            sent_length = 0;
908    
909            /* we suppose one command is enough */
910            chain_parameter = 0x00;
911    
912            local_tx_length = tx_length - sent_length;
913            if (local_tx_length > CMD_BUF_SIZE)
914            {
915                    local_tx_length = CMD_BUF_SIZE;
916                    /* the command APDU begins with this command, and continue in the next
917                     * PC_to_RDR_XfrBlock */
918                    chain_parameter = 0x01;
919            }
920            if (local_tx_length > ccid_descriptor->dwMaxCCIDMessageLength-10)
921            {
922                    local_tx_length = ccid_descriptor->dwMaxCCIDMessageLength-10;
923                    chain_parameter = 0x01;
924            }
925    
926    send_next_block:
927            return_value = CCID_Transmit(reader_index, local_tx_length, tx_buffer,
928                    chain_parameter, 0);
929            if (return_value != IFD_SUCCESS)
930                    return return_value;
931    
932            sent_length += local_tx_length;
933            tx_buffer += local_tx_length;
934    
935            /* we just sent the last block (0x02) or only one block was needded (0x00) */
936            if ((0x02 == chain_parameter) || (0x00 == chain_parameter))
937                    goto receive_block;
938    
939            /* read a nul block */
940            return_value = CCID_Receive(reader_index, &local_rx_length, NULL, NULL);
941            if (return_value != IFD_SUCCESS)
942                    return return_value;
943    
944            /* size of the next block */
945            if (tx_length - sent_length > local_tx_length)
946            {
947                    /* the abData field continues a command APDU and
948                     * another block is to follow */
949                    chain_parameter = 0x03;
950            }
951            else
952            {
953                    /* this abData field continues a command APDU and ends
954                     * the APDU command */
955                    chain_parameter = 0x02;
956    
957                    /* last (smaller) block */
958                    local_tx_length = tx_length - sent_length;
959            }
960    
961            goto send_next_block;
962    
963    receive_block:
964            /* receive the APDU */
965            received_length = 0;
966    
967    receive_next_block:
968            local_rx_length = *rx_length - received_length;
969            return_value = CCID_Receive(reader_index, &local_rx_length, rx_buffer,
970                    &chain_parameter);
971            if (IFD_ERROR_INSUFFICIENT_BUFFER == return_value)
972            {
973                    buffer_overflow = 1;
974    
975                    /* we continue to read all the response APDU */
976                    return_value = IFD_SUCCESS;
977            }
978    
979            if (return_value != IFD_SUCCESS)
980                    return return_value;
981    
982            /* advance in the reiceiving buffer */
983            rx_buffer += local_rx_length;
984            received_length += local_rx_length;
985    
986            switch (chain_parameter)
987            {
988                    /* the response APDU begins and ends in this command */
989                    case 0x00:
990                    /* this abData field continues the response APDU and ends the response
991                     * APDU */
992                    case 0x02:
993                            break;
994    
995                    /* the response APDU begins with this command and is to continue */
996                    case 0x01:
997                    /* this abData field continues the response APDU and another block is
998                     * to follow */
999                    case 0x03:
1000                    /* empty abData field, continuation of the command APDU is expected in
1001                     * next PC_to_RDR_XfrBlock command */
1002                    case 0x10:
1003                            /* send a nul block */
1004                            return_value = CCID_Transmit(reader_index, 0, NULL, 0, 0);
1005                            if (return_value != IFD_SUCCESS)
1006                                    return return_value;
1007    
1008                            goto receive_next_block;
1009            }
1010    
1011            *rx_length = received_length;
1012    
1013            /* generate an overflow detected by pcscd */
1014            if (buffer_overflow)
1015                    (*rx_length)++;
1016    
1017            return IFD_SUCCESS;
1018    } /* CmdXfrBlockAPDU_extended */
1019    
1020    
1021    /*****************************************************************************
1022     *
1023   *                                      CmdXfrBlockTPDU_T0   *                                      CmdXfrBlockTPDU_T0
1024   *   *
1025   ****************************************************************************/   ****************************************************************************/
1026  static RESPONSECODE CmdXfrBlockTPDU_T0(unsigned int lun, unsigned int tx_length,  static RESPONSECODE CmdXfrBlockTPDU_T0(unsigned int reader_index,
1027          unsigned char tx_buffer[], unsigned int *rx_length,          unsigned int tx_length, unsigned char tx_buffer[], unsigned int *rx_length,
1028          unsigned char rx_buffer[])          unsigned char rx_buffer[])
1029  {  {
1030          RESPONSECODE return_value = IFD_SUCCESS;          RESPONSECODE return_value = IFD_SUCCESS;
1031            _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index);
1032    
1033          DEBUG_COMM2("T=0: %d bytes", tx_length);          DEBUG_COMM2("T=0: %d bytes", tx_length);
1034    
1035          return_value = CCID_Transmit(lun, tx_length, tx_buffer, 0);          /* command length too big for CCID reader? */
1036            if (tx_length > ccid_descriptor->dwMaxCCIDMessageLength-10)
1037            {
1038                    DEBUG_CRITICAL3("Command too long (%d bytes) for max: %d bytes",
1039                                    tx_length, ccid_descriptor->dwMaxCCIDMessageLength-10);
1040                    return IFD_COMMUNICATION_ERROR;
1041            }
1042    
1043            /* command length too big for CCID driver? */
1044            if (tx_length > CMD_BUF_SIZE)
1045            {
1046                    DEBUG_CRITICAL3("Command too long (%d bytes) for max: %d bytes",
1047                                    tx_length, CMD_BUF_SIZE);
1048                    return IFD_COMMUNICATION_ERROR;
1049            }
1050    
1051            return_value = CCID_Transmit(reader_index, tx_length, tx_buffer, 0, 0);
1052          if (return_value != IFD_SUCCESS)          if (return_value != IFD_SUCCESS)
1053                  return return_value;                  return return_value;
1054    
1055          return CCID_Receive(lun, rx_length, rx_buffer);          return CCID_Receive(reader_index, rx_length, rx_buffer, NULL);
1056  } /* CmdXfrBlockTPDU_T0 */  } /* CmdXfrBlockTPDU_T0 */
1057    
1058    
1059  /*****************************************************************************  /*****************************************************************************
1060   *   *
1061     *                                      T0CmdParsing
1062     *
1063     ****************************************************************************/
1064    static RESPONSECODE T0CmdParsing(unsigned char *cmd, unsigned int cmd_len,
1065            unsigned int *exp_len)
1066    {
1067            *exp_len = 0;
1068    
1069            /* Ref: 7816-4 Annex A */
1070            switch (cmd_len)
1071            {
1072                    case 4: /* Case 1 */
1073                            *exp_len = 2; /* SW1 and SW2 only */
1074                            break;
1075    
1076                    case 5: /* Case 2 */
1077                            if (cmd[4] != 0)
1078                                    *exp_len = cmd[4] + 2;
1079                            else
1080                                    *exp_len = 256 + 2;
1081                            break;
1082    
1083                    default: /* Case 3 */
1084                            if (cmd_len > 5 && cmd_len == (unsigned int)(cmd[4] + 5))
1085                                    *exp_len = 2; /* SW1 and SW2 only */
1086                            else
1087                                    return IFD_COMMUNICATION_ERROR; /* situation not supported */
1088                            break;
1089            }
1090    
1091            return IFD_SUCCESS;
1092    } /* T0CmdParsing */
1093    
1094    
1095    /*****************************************************************************
1096     *
1097     *                                      T0ProcACK
1098     *
1099     ****************************************************************************/
1100    static RESPONSECODE T0ProcACK(unsigned int reader_index,
1101            unsigned char **snd_buf, unsigned int *snd_len,
1102            unsigned char **rcv_buf, unsigned int *rcv_len,
1103            unsigned char **in_buf, unsigned int *in_len,
1104            unsigned int proc_len, int is_rcv)
1105    {
1106            RESPONSECODE return_value;
1107            unsigned int remain_len;
1108            unsigned char tmp_buf[512];
1109            unsigned int ret_len;
1110    
1111            DEBUG_COMM2("Enter, is_rcv = %d", is_rcv);
1112    
1113            if (is_rcv == 1)
1114            {       /* Receiving mode */
1115                    if (*in_len > 0)
1116                    {       /* There are still available data in our buffer */
1117                            if (*in_len >= proc_len)
1118                            {
1119                                    /* We only need to get the data from our buffer */
1120                                    memcpy(*rcv_buf, *in_buf, proc_len);
1121                                    *rcv_buf += proc_len;
1122                                    *in_buf += proc_len;
1123                                    *rcv_len += proc_len;
1124                                    *in_len -= proc_len;
1125    
1126                                    return IFD_SUCCESS;
1127                            }
1128                            else
1129                            {
1130                                    /* Move all data in the input buffer to the reply buffer */
1131                                    remain_len = proc_len - *in_len;
1132                                    memcpy(*rcv_buf, *in_buf, *in_len);
1133                                    *rcv_buf += *in_len;
1134                                    *in_buf += *in_len;
1135                                    *rcv_len += *in_len;
1136                                    *in_len = 0;
1137                            }
1138                    }
1139                    else
1140                            /* There is no data in our tmp_buf,
1141                             * we have to read all data we needed */
1142                            remain_len = proc_len;
1143    
1144                    /* Read the expected data from the smartcard */
1145                    if (*in_len != 0)
1146                    {
1147                            DEBUG_CRITICAL("*in_len != 0");
1148                            return IFD_COMMUNICATION_ERROR;
1149                    }
1150    
1151                    memset(tmp_buf, 0, sizeof(tmp_buf));
1152    
1153                    ret_len = remain_len;
1154                    return_value = CCID_Transmit(reader_index, 0, *snd_buf, ret_len, 0);
1155                    if (return_value != IFD_SUCCESS)
1156                            return return_value;
1157    
1158                    return_value = CCID_Receive(reader_index, &ret_len, tmp_buf, NULL);
1159                    if (return_value != IFD_SUCCESS)
1160                            return return_value;
1161    
1162                    memcpy(*rcv_buf, tmp_buf, remain_len);
1163                    *rcv_buf += remain_len, *rcv_len += remain_len;
1164    
1165                    /* If ret_len != remain_len, our logic is erroneous */
1166                    if (ret_len != remain_len)
1167                    {
1168                            DEBUG_CRITICAL("ret_len != remain_len");
1169                            return IFD_COMMUNICATION_ERROR;
1170                    }
1171            }
1172            else
1173            {       /* Sending mode */
1174    
1175                    return_value = CCID_Transmit(reader_index, proc_len, *snd_buf, 1, 0);
1176                    if (return_value != IFD_SUCCESS)
1177                            return return_value;
1178    
1179                    *snd_len -= proc_len;
1180                    *snd_buf += proc_len;
1181            }
1182    
1183            DEBUG_COMM("Exit");
1184    
1185            return IFD_SUCCESS;
1186    } /* T0ProcACK */
1187    
1188    
1189    /*****************************************************************************
1190     *
1191     *                                      T0ProcSW1
1192     *
1193     ****************************************************************************/
1194    static RESPONSECODE T0ProcSW1(unsigned int reader_index,
1195            unsigned char *rcv_buf, unsigned int *rcv_len,
1196            unsigned char *in_buf, unsigned int in_len)
1197    {
1198            RESPONSECODE return_value = IFD_SUCCESS;
1199            UCHAR tmp_buf[512];
1200            unsigned char *rcv_buf_tmp = rcv_buf;
1201            const unsigned int rcv_len_tmp = *rcv_len;
1202            unsigned char sw1, sw2;
1203    
1204            /* store the SW1 */
1205            sw1 = *rcv_buf = *in_buf;
1206            rcv_buf++;
1207            in_buf++;
1208            in_len--;
1209            (*rcv_len)++;
1210    
1211            /* store the SW2 */
1212            if (0 == in_len)
1213            {
1214                    return_value = CCID_Transmit(reader_index, 0, rcv_buf, 1, 0);
1215                    if (return_value != IFD_SUCCESS)
1216                            return return_value;
1217    
1218                    in_len = 1;
1219    
1220                    return_value = CCID_Receive(reader_index, &in_len, tmp_buf, NULL);
1221                    if (return_value != IFD_SUCCESS)
1222                            return return_value;
1223    
1224                    in_buf = tmp_buf;
1225            }
1226            sw2 = *rcv_buf = *in_buf;
1227            rcv_buf++;
1228            in_buf++;
1229            in_len--;
1230            (*rcv_len)++;
1231    
1232            if (return_value != IFD_SUCCESS)
1233            {
1234                    rcv_buf_tmp[0] = rcv_buf_tmp[1] = 0;
1235                    *rcv_len = rcv_len_tmp;
1236            }
1237    
1238            DEBUG_COMM3("Exit: SW=%02X %02X", sw1, sw2);
1239    
1240            return return_value;
1241    } /* T0ProcSW1 */
1242    
1243    
1244    /*****************************************************************************
1245     *
1246     *                                      CmdXfrBlockCHAR_T0
1247     *
1248     ****************************************************************************/
1249    static RESPONSECODE CmdXfrBlockCHAR_T0(unsigned int reader_index,
1250            unsigned int snd_len, unsigned char snd_buf[], unsigned int *rcv_len,
1251            unsigned char rcv_buf[])
1252    {
1253            int is_rcv;
1254            unsigned char cmd[5];
1255            unsigned char tmp_buf[512];
1256            unsigned int exp_len, in_len;
1257            unsigned char ins, *in_buf;
1258            RESPONSECODE return_value = IFD_SUCCESS;
1259    
1260            DEBUG_COMM2("T=0: %d bytes", snd_len);
1261    
1262            in_buf = tmp_buf;
1263            in_len = 0;
1264            *rcv_len = 0;
1265    
1266            return_value = T0CmdParsing(snd_buf, snd_len, &exp_len);
1267            if (return_value != IFD_SUCCESS)
1268            {
1269                    DEBUG_CRITICAL("T0CmdParsing failed");
1270                    return IFD_COMMUNICATION_ERROR;
1271            }
1272    
1273            if (snd_len == 5 || snd_len == 4)
1274                    is_rcv = 1;
1275            else
1276                    is_rcv = 0;
1277    
1278            /* Command to send to the smart card (must be 5 bytes, from 7816 p.15) */
1279            memset(cmd, 0, sizeof(cmd));
1280            if (snd_len == 4)
1281            {
1282                    memcpy(cmd, snd_buf, 4);
1283                    snd_buf += 4;
1284                    snd_len -= 4;
1285            }
1286            else
1287            {
1288                    memcpy(cmd, snd_buf, 5);
1289                    snd_buf += 5;
1290                    snd_len -= 5;
1291            }
1292    
1293            /* Make sure this is a valid command by checking the INS field */
1294            ins = cmd[1];
1295            if ((ins & 0xF0) == 0x60 ||     /* 7816-3 8.3.2 */
1296                    (ins & 0xF0) == 0x90)
1297            {
1298                    DEBUG_CRITICAL2("fatal: INS (0x%02X) = 0x6X or 0x9X", ins);
1299                    return IFD_COMMUNICATION_ERROR;
1300            }
1301    
1302            return_value = CCID_Transmit(reader_index, 5, cmd, 1, 0);
1303            if (return_value != IFD_SUCCESS)
1304                    return return_value;
1305    
1306            while (1)
1307            {
1308                    if (in_len == 0)
1309                    {
1310                            in_len = 1;
1311                            return_value = CCID_Receive(reader_index, &in_len, tmp_buf, NULL);
1312                            if (return_value != IFD_SUCCESS)
1313                            {
1314                                    DEBUG_CRITICAL("CCID_Receive failed");
1315                                    return return_value;
1316                            }
1317                            in_buf = tmp_buf;
1318                    }
1319                    if (in_len == 0)
1320                    {
1321                            /* Suppose we should be able to get data.
1322                             * If not, error. Set the time-out error */
1323                            DEBUG_CRITICAL("error: in_len = 0");
1324                            return IFD_RESPONSE_TIMEOUT;
1325                    }
1326    
1327                    /* Start to process the procedure bytes */
1328                    if (*in_buf == 0x60)
1329                    {
1330                            in_len = 0;
1331                            return_value = CCID_Transmit(reader_index, 0, cmd, 1, 0);
1332    
1333                            if (return_value != IFD_SUCCESS)
1334                                    return return_value;
1335    
1336                            continue;
1337                    }
1338                    else if (*in_buf == ins || *in_buf == (ins ^ 0x01))
1339                    {
1340                            /* ACK => To transfer all remaining data bytes */
1341                            in_buf++, in_len--;
1342                            if (is_rcv)
1343                                    return_value = T0ProcACK(reader_index, &snd_buf, &snd_len,
1344                                            &rcv_buf, rcv_len, &in_buf, &in_len, exp_len - *rcv_len, 1);
1345                            else
1346                                    return_value = T0ProcACK(reader_index, &snd_buf, &snd_len,
1347                                            &rcv_buf, rcv_len, &in_buf, &in_len, snd_len, 0);
1348    
1349                            if (*rcv_len == exp_len)
1350                                    return return_value;
1351    
1352                            continue;
1353                    }
1354                    else if (*in_buf == (ins ^ 0xFF) || *in_buf == (ins ^ 0xFE))
1355                    {
1356                            /* ACK => To transfer 1 remaining bytes */
1357                            in_buf++, in_len--;
1358                            return_value = T0ProcACK(reader_index, &snd_buf, &snd_len,
1359                                    &rcv_buf, rcv_len, &in_buf, &in_len, 1, is_rcv);
1360    
1361                            if (return_value != IFD_SUCCESS)
1362                                    return return_value;
1363    
1364                            continue;
1365                    }
1366                    else if ((*in_buf & 0xF0) == 0x60 || (*in_buf & 0xF0) == 0x90)
1367                            /* SW1 */
1368                            return T0ProcSW1(reader_index, rcv_buf, rcv_len, in_buf, in_len);
1369    
1370                    /* Error, unrecognized situation found */
1371                    DEBUG_CRITICAL2("Unrecognized Procedure byte (0x%02X) found!", *in_buf);
1372                    return return_value;
1373            }
1374    
1375            return return_value;
1376    } /* CmdXfrBlockCHAR_T0 */
1377    
1378    
1379    /*****************************************************************************
1380     *
1381   *                                      CmdXfrBlockTPDU_T1   *                                      CmdXfrBlockTPDU_T1
1382   *   *
1383   ****************************************************************************/   ****************************************************************************/
1384  static RESPONSECODE CmdXfrBlockTPDU_T1(unsigned int lun, unsigned int tx_length,  static RESPONSECODE CmdXfrBlockTPDU_T1(unsigned int reader_index,
1385          unsigned char tx_buffer[], unsigned int *rx_length,          unsigned int tx_length, unsigned char tx_buffer[], unsigned int *rx_length,
1386          unsigned char rx_buffer[])          unsigned char rx_buffer[])
1387  {  {
1388          RESPONSECODE return_value = IFD_SUCCESS;          RESPONSECODE return_value = IFD_SUCCESS;
1389          int ret;          int ret;
1390    
1391          DEBUG_COMM2("T=1: %d bytes", tx_length);          DEBUG_COMM3("T=1: %d and %d bytes", tx_length, *rx_length);
1392    
1393          ret = t1_transceive(&((get_ccid_slot(lun)) -> t1), 0, tx_buffer, tx_length, rx_buffer, *rx_length);          ret = t1_transceive(&((get_ccid_slot(reader_index)) -> t1), 0,
1394                    tx_buffer, tx_length, rx_buffer, *rx_length);
1395    
1396          if (ret < 0)          if (ret < 0)
1397          {          {
# Line 548  static RESPONSECODE CmdXfrBlockTPDU_T1(u Line 1410  static RESPONSECODE CmdXfrBlockTPDU_T1(u
1410   *                                      SetParameters   *                                      SetParameters
1411   *   *
1412   ****************************************************************************/   ****************************************************************************/
1413  RESPONSECODE SetParameters(unsigned int lun, char protocol, unsigned int length,  RESPONSECODE SetParameters(unsigned int reader_index, char protocol,
1414          unsigned char buffer[])          unsigned int length, unsigned char buffer[])
1415  {  {
1416          unsigned char cmd[10+CMD_BUF_SIZE];     /* CCID + APDU buffer */          unsigned char cmd[10+CMD_BUF_SIZE];     /* CCID + APDU buffer */
1417          _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(lun);          _ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index);
1418    
1419          DEBUG_COMM2("length: %d bytes", length);          DEBUG_COMM2("length: %d bytes", length);
1420    
1421          cmd[0] = 0x61; /* SetParameters */          cmd[0] = 0x61; /* SetParameters */
1422          i2dw(length, cmd+1);    /* APDU length */          i2dw(length, cmd+1);    /* APDU length */
1423          cmd[5] = 0;     /* slot number */          cmd[5] = ccid_descriptor->bCurrentSlotIndex;    /* slot number */
1424          cmd[6] = ccid_descriptor->bSeq++;          cmd[6] = (*ccid_descriptor->pbSeq)++;
1425          cmd[7] = protocol;      /* bProtocolNum */          cmd[7] = protocol;      /* bProtocolNum */
1426          cmd[8] = cmd[9] = 0; /* RFU */          cmd[8] = cmd[9] = 0; /* RFU */
1427    
1428            /* check that the command is not too large */
1429            if (length > CMD_BUF_SIZE)
1430                    return IFD_NOT_SUPPORTED;
1431    
1432          memcpy(cmd+10, buffer, length);          memcpy(cmd+10, buffer, length);
1433    
1434          if (WritePort(lun, 10+length, cmd) != STATUS_SUCCESS)          if (WritePort(reader_index, 10+length, cmd) != STATUS_SUCCESS)
1435                  return IFD_COMMUNICATION_ERROR;                  return IFD_COMMUNICATION_ERROR;
1436    
1437          length = sizeof(cmd);          length = sizeof(cmd);
1438          if (ReadPort(lun, &length, cmd) != STATUS_SUCCESS)          if (ReadPort(reader_index, &length, cmd) != STATUS_SUCCESS)
1439                  return IFD_COMMUNICATION_ERROR;                  return IFD_COMMUNICATION_ERROR;
1440    
1441          if (cmd[STATUS_OFFSET] & CCID_COMMAND_FAILED)          if (cmd[STATUS_OFFSET] & CCID_COMMAND_FAILED)
1442          {          {
1443                  ccid_error(cmd[ERROR_OFFSET], __FILE__, __LINE__, __FUNCTION__);    /* bError */                  ccid_error(cmd[ERROR_OFFSET], __FILE__, __LINE__, __FUNCTION__);    /* bError */
1444                  return IFD_COMMUNICATION_ERROR;                  if (0x00 == cmd[ERROR_OFFSET])  /* command not supported */
1445                            return IFD_NOT_SUPPORTED;
1446                    else
1447                            if ((cmd[ERROR_OFFSET] >= 1) && (cmd[ERROR_OFFSET] <= 127))
1448                                    /* a parameter is not changeable */
1449                                    return IFD_SUCCESS;
1450                            else
1451                                    return IFD_COMMUNICATION_ERROR;
1452          }          }
1453    
1454          return IFD_SUCCESS;          return IFD_SUCCESS;
# Line 582  RESPONSECODE SetParameters(unsigned int Line 1456  RESPONSECODE SetParameters(unsigned int
1456    
1457    
1458  /*****************************************************************************  /*****************************************************************************
1459     *
1460     *                                      isCharLevel
1461     *
1462     ****************************************************************************/
1463    int isCharLevel(int reader_index)
1464    {
1465            return CCID_CLASS_CHARACTER == (get_ccid_descriptor(reader_index)->dwFeatures & CCID_CLASS_EXCHANGE_MASK);
1466    } /* isCharLevel */
1467    
1468    
1469    /*****************************************************************************
1470   *   *
1471   *                                      i2dw   *                                      i2dw
1472   *   *

Legend:
Removed from v.1076  
changed lines
  Added in v.2110

  ViewVC Help
Powered by ViewVC 1.1.5