pcsc-lite  2.2.3
testpcsc.c
Go to the documentation of this file.
1 /*
2  * MUSCLE SmartCard Development ( https://pcsclite.apdu.fr/ )
3  *
4  * Copyright (C) 1999
5  * David Corcoran <corcoran@musclecard.com>
6  * Copyright (C) 2004-2022
7  * Ludovic Rousseau <ludovic.rousseau@free.fr>
8  *
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions
11 are met:
12 
13 1. Redistributions of source code must retain the above copyright
14  notice, this list of conditions and the following disclaimer.
15 2. Redistributions in binary form must reproduce the above copyright
16  notice, this list of conditions and the following disclaimer in the
17  documentation and/or other materials provided with the distribution.
18 3. The name of the author may not be used to endorse or promote products
19  derived from this software without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
38 #include "config.h"
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 
43 #include "pcsclite.h"
44 #include "winscard.h"
45 #include "reader.h"
46 
47 #define PANIC 0
48 #define DONT_PANIC 1
49 
50 #define USE_AUTOALLOCATE
51 
52 #define BLUE "\33[34m"
53 #define RED "\33[31m"
54 #define BRIGHT_RED "\33[01;31m"
55 #define GREEN "\33[32m"
56 #define NORMAL "\33[0m"
57 #define MAGENTA "\33[35m"
58 
59 static void test_rv(LONG rv, SCARDCONTEXT hContext, int dont_panic)
60 {
61  if (rv != SCARD_S_SUCCESS)
62  {
63  if (dont_panic)
64  printf(BLUE "%s (don't panic)\n" NORMAL, pcsc_stringify_error(rv));
65  else
66  {
67  printf(RED "%s\n" NORMAL, pcsc_stringify_error(rv));
68  (void)SCardReleaseContext(hContext);
69  exit(-1);
70  }
71  }
72  else
73  (void)puts(pcsc_stringify_error(rv));
74 }
75 
76 int main(/*@unused@*/ int argc, /*@unused@*/ char **argv)
77 {
78  SCARDHANDLE hCard;
79  SCARDCONTEXT hContext;
80  SCARD_READERSTATE rgReaderStates[1];
81  DWORD dwReaderLen, dwState, dwProt, dwAtrLen;
82  DWORD dwPref, dwReaders = 0;
83  char *pcReader = NULL, *mszReaders;
84 #ifdef USE_AUTOALLOCATE
85  unsigned char *pbAtr = NULL;
86 #else
87  unsigned char pbAtr[MAX_ATR_SIZE];
88 #endif
89  union {
90  unsigned char as_char[100];
91  DWORD as_DWORD;
92  uint32_t as_uint32_t;
93  } buf;
94  DWORD dwBufLen;
95  unsigned char *pbAttr = NULL;
96  DWORD pcbAttrLen;
97  char *mszGroups;
98  DWORD dwGroups = 0;
99  long rv;
100  DWORD i;
101  int p, iReader;
102  int iList[16] = {0};
103  SCARD_IO_REQUEST ioRecvPci = *SCARD_PCI_T0; /* use a default value */
104  const SCARD_IO_REQUEST *pioSendPci;
105  unsigned char bSendBuffer[MAX_BUFFER_SIZE];
106  unsigned char bRecvBuffer[MAX_BUFFER_SIZE];
107  DWORD send_length, length;
108 
109  (void)argc;
110  (void)argv;
111 
112  printf("\nMUSCLE PC/SC Lite unitary test Program\n\n");
113 
114  printf(MAGENTA "THIS PROGRAM IS NOT DESIGNED AS A TESTING TOOL FOR END USERS!\n");
115  printf("Do NOT use it unless you really know what you do.\n\n" NORMAL);
116 
117  printf("Testing SCardEstablishContext\t: ");
118  rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
119  test_rv(rv, hContext, PANIC);
120 
121  printf("Testing SCardIsValidContext\t: ");
122  rv = SCardIsValidContext(hContext);
123  test_rv(rv, hContext, PANIC);
124 
125  printf("Testing SCardIsValidContext\t: ");
126  rv = SCardIsValidContext(hContext+1);
127  test_rv(rv, hContext, DONT_PANIC);
128 
129  printf("Testing SCardListReaderGroups\t: ");
130 #ifdef USE_AUTOALLOCATE
131  dwGroups = SCARD_AUTOALLOCATE;
132  rv = SCardListReaderGroups(hContext, (LPSTR)&mszGroups, &dwGroups);
133 #else
134  rv = SCardListReaderGroups(hContext, NULL, &dwGroups);
135  test_rv(rv, hContext, PANIC);
136 
137  printf("Testing SCardListReaderGroups\t: ");
138  mszGroups = calloc(dwGroups, sizeof(char));
139  rv = SCardListReaderGroups(hContext, mszGroups, &dwGroups);
140 #endif
141  test_rv(rv, hContext, PANIC);
142 
143  /*
144  * Have to understand the multi-string here
145  */
146  p = 0;
147  for (i = 0; i+1 < dwGroups; i++)
148  {
149  ++p;
150  printf(GREEN "Group %02d: %s\n" NORMAL, p, &mszGroups[i]);
151  while (mszGroups[++i] != 0) ;
152  }
153 
154 #ifdef USE_AUTOALLOCATE
155  printf("Testing SCardFreeMemory\t\t: ");
156  rv = SCardFreeMemory(hContext, mszGroups);
157  test_rv(rv, hContext, PANIC);
158 #else
159  free(mszGroups);
160 #endif
161 
162 wait_for_card_again:
163  mszGroups = NULL;
164  printf("Testing SCardListReaders\t: ");
165  rv = SCardListReaders(hContext, mszGroups, NULL, &dwReaders);
166  test_rv(rv, hContext, DONT_PANIC);
168  {
169  printf("Testing SCardGetStatusChange \n");
170  printf("Please insert a working reader\t: ");
171  (void)fflush(stdout);
172  rgReaderStates[0].szReader = "\\\\?PnP?\\Notification";
173  rgReaderStates[0].dwCurrentState = SCARD_STATE_EMPTY;
174 
175  rv = SCardGetStatusChange(hContext, INFINITE, rgReaderStates, 1);
176  test_rv(rv, hContext, PANIC);
177  }
178 
179  printf("Testing SCardListReaders\t: ");
180 #ifdef USE_AUTOALLOCATE
181  dwReaders = SCARD_AUTOALLOCATE;
182  rv = SCardListReaders(hContext, mszGroups, (LPSTR)&mszReaders, &dwReaders);
183 #else
184  rv = SCardListReaders(hContext, mszGroups, NULL, &dwReaders);
185  test_rv(rv, hContext, PANIC);
186 
187  printf("Testing SCardListReaders\t: ");
188  mszReaders = calloc(dwReaders, sizeof(char));
189  rv = SCardListReaders(hContext, mszGroups, mszReaders, &dwReaders);
190 #endif
191  test_rv(rv, hContext, DONT_PANIC);
192 
193  /*
194  * Have to understand the multi-string here
195  */
196  p = 0;
197  for (i = 0; i+1 < dwReaders; i++)
198  {
199  ++p;
200  printf(GREEN "Reader %02d: %s\n" NORMAL, p, &mszReaders[i]);
201  iList[p] = i;
202  while (mszReaders[++i] != 0) ;
203  }
204 
205  if (p > 1)
206  {
207  int again = 0;
208 
209  do
210  {
211  char input[80];
212  char *r;
213 
214  printf("Enter the reader number\t\t: ");
215  r = fgets(input, sizeof(input), stdin);
216  if (NULL == r)
217  iReader = -1;
218  else
219  iReader = atoi(input);
220 
221  if (iReader > p || iReader <= 0)
222  {
223  printf("Invalid Value - try again\n");
224  again = 1;
225  }
226  }
227  while (again);
228  }
229  else
230  iReader = 1;
231 
232  rgReaderStates[0].szReader = &mszReaders[iList[iReader]];
233  rgReaderStates[0].dwCurrentState = SCARD_STATE_EMPTY;
234 
235  printf("Waiting for card insertion\t: ");
236  (void)fflush(stdout);
237  rv = SCardGetStatusChange(hContext, INFINITE, rgReaderStates, 1);
238  test_rv(rv, hContext, PANIC);
239  if (rgReaderStates[0].dwEventState & SCARD_STATE_UNKNOWN)
240  {
241  printf("\nA reader has been connected/disconnected\n");
242  goto wait_for_card_again;
243  }
244 
245  printf("Testing SCardConnect\t\t: ");
246  rv = SCardConnect(hContext, &mszReaders[iList[iReader]],
248  &hCard, &dwPref);
249  test_rv(rv, hContext, PANIC);
250 
251  switch(dwPref)
252  {
253  case SCARD_PROTOCOL_T0:
254  pioSendPci = SCARD_PCI_T0;
255  break;
256  case SCARD_PROTOCOL_T1:
257  pioSendPci = SCARD_PCI_T1;
258  break;
259  case SCARD_PROTOCOL_RAW:
260  pioSendPci = SCARD_PCI_RAW;
261  break;
262  default:
263  printf("Unknown protocol\n");
264  return -1;
265  }
266 
267  /* APDU select file */
268  printf("Select file:");
269  send_length = 7;
270  memcpy(bSendBuffer, "\x00\xA4\x00\x00\x02\x3F\x00", send_length);
271  for (i=0; i<send_length; i++)
272  printf(" %02X", bSendBuffer[i]);
273  printf("\n");
274  length = sizeof(bRecvBuffer);
275 
276  printf("Testing SCardTransmit\t\t: ");
277  rv = SCardTransmit(hCard, pioSendPci, bSendBuffer, send_length,
278  &ioRecvPci, bRecvBuffer, &length);
279  test_rv(rv, hContext, PANIC);
280  printf(" card response:" GREEN);
281  for (i=0; i<length; i++)
282  printf(" %02X", bRecvBuffer[i]);
283  printf("\n" NORMAL);
284 
285  printf("Testing SCardControl\t\t: ");
286  {
287  char buffer[1024] = { 0x02 };
288  DWORD cbRecvLength = sizeof(buffer);
289 
290  rv = SCardControl(hCard, SCARD_CTL_CODE(1), buffer, 1, buffer,
291  sizeof(buffer), &cbRecvLength);
292  if (cbRecvLength && (SCARD_S_SUCCESS == rv))
293  {
294  for (i=0; i<cbRecvLength; i++)
295  printf("%c", buffer[i]);
296  printf(" ");
297  }
298  }
299  test_rv(rv, hContext, DONT_PANIC);
300 
301  printf("Testing SCardGetAttrib\t\t: ");
302 #ifdef USE_AUTOALLOCATE
303  pcbAttrLen = SCARD_AUTOALLOCATE;
304  rv = SCardGetAttrib(hCard, SCARD_ATTR_DEVICE_FRIENDLY_NAME, (unsigned char *)&pbAttr,
305  &pcbAttrLen);
306 #else
307  rv = SCardGetAttrib(hCard, SCARD_ATTR_DEVICE_FRIENDLY_NAME, NULL, &pcbAttrLen);
308  test_rv(rv, hContext, DONT_PANIC);
309  if (rv == SCARD_S_SUCCESS)
310  {
311  printf("SCARD_ATTR_DEVICE_FRIENDLY_NAME length: " GREEN "%ld\n" NORMAL, pcbAttrLen);
312  pbAttr = malloc(pcbAttrLen);
313  }
314 
315  printf("Testing SCardGetAttrib\t\t: ");
316  rv = SCardGetAttrib(hCard, SCARD_ATTR_DEVICE_FRIENDLY_NAME, pbAttr, &pcbAttrLen);
317 #endif
318  test_rv(rv, hContext, DONT_PANIC);
319  if (rv == SCARD_S_SUCCESS)
320  printf("SCARD_ATTR_DEVICE_FRIENDLY_NAME: " GREEN "%s\n" NORMAL, pbAttr);
321 
322 #ifdef USE_AUTOALLOCATE
323  printf("Testing SCardFreeMemory\t\t: ");
324  rv = SCardFreeMemory(hContext, pbAttr);
325  test_rv(rv, hContext, PANIC);
326 #else
327  if (pbAttr)
328  free(pbAttr);
329 #endif
330 
331  printf("Testing SCardGetAttrib\t\t: ");
332 #ifdef USE_AUTOALLOCATE
333  pcbAttrLen = SCARD_AUTOALLOCATE;
334  rv = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, (unsigned char *)&pbAttr,
335  &pcbAttrLen);
336 #else
337  rv = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, NULL, &pcbAttrLen);
338  test_rv(rv, hContext, DONT_PANIC);
339  if (rv == SCARD_S_SUCCESS)
340  {
341  printf("SCARD_ATTR_ATR_STRING length: " GREEN "%ld\n" NORMAL, pcbAttrLen);
342  pbAttr = malloc(pcbAttrLen);
343  }
344 
345  printf("Testing SCardGetAttrib\t\t: ");
346  rv = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, pbAttr, &pcbAttrLen);
347 #endif
348  test_rv(rv, hContext, DONT_PANIC);
349  if (rv == SCARD_S_SUCCESS)
350  {
351  printf("SCARD_ATTR_ATR_STRING length: " GREEN "%ld\n" NORMAL, pcbAttrLen);
352  printf("SCARD_ATTR_ATR_STRING: " GREEN);
353  for (i = 0; i < pcbAttrLen; i++)
354  printf("%02X ", pbAttr[i]);
355  printf("\n" NORMAL);
356  }
357 
358 #ifdef USE_AUTOALLOCATE
359  printf("Testing SCardFreeMemory\t\t: ");
360  rv = SCardFreeMemory(hContext, pbAttr);
361  test_rv(rv, hContext, PANIC);
362 #else
363  if (pbAttr)
364  free(pbAttr);
365 #endif
366 
367  printf("Testing SCardGetAttrib\t\t: ");
368  dwBufLen = sizeof(buf);
369  rv = SCardGetAttrib(hCard, SCARD_ATTR_VENDOR_IFD_VERSION, buf.as_char, &dwBufLen);
370  test_rv(rv, hContext, DONT_PANIC);
371  if (rv == SCARD_S_SUCCESS)
372  {
373  int valid = 1; /* valid value by default */
374  long value;
375  printf("Vendor IFD version\t\t: ");
376  if (dwBufLen == sizeof(DWORD))
377  value = buf.as_DWORD;
378  else
379  {
380  if (dwBufLen == sizeof(uint32_t))
381  value = buf.as_uint32_t;
382  else
383  {
384  printf(RED "Unsupported size\n" NORMAL);
385  valid = 0; /* invalid value */
386  }
387  }
388 
389  if (valid)
390  {
391  int M = (value & 0xFF000000) >> 24; /* Major */
392  int m = (value & 0x00FF0000) >> 16; /* Minor */
393  int b = (value & 0x0000FFFF); /* build */
394  printf(GREEN "%d.%d.%d\n" NORMAL, M, m, b);
395  }
396  }
397 
398  printf("Testing SCardGetAttrib\t\t: ");
399  dwBufLen = sizeof(buf);
400  rv = SCardGetAttrib(hCard, SCARD_ATTR_MAXINPUT, buf.as_char, &dwBufLen);
401  test_rv(rv, hContext, DONT_PANIC);
402  if (rv == SCARD_S_SUCCESS)
403  {
404  if (dwBufLen == sizeof(uint32_t))
405  printf("Max message length\t\t: " GREEN "%d\n" NORMAL,
406  buf.as_uint32_t);
407  else
408  printf(RED "Wrong size" NORMAL);
409  }
410 
411  printf("Testing SCardGetAttrib\t\t: ");
412  dwBufLen = sizeof(buf);
413  rv = SCardGetAttrib(hCard, SCARD_ATTR_VENDOR_NAME, buf.as_char, &dwBufLen);
414  test_rv(rv, hContext, DONT_PANIC);
415  if (rv == SCARD_S_SUCCESS)
416  printf("Vendor name\t\t\t: " GREEN "%s\n" NORMAL, buf.as_char);
417 
418  printf("Testing SCardSetAttrib\t\t: ");
419  rv = SCardSetAttrib(hCard, SCARD_ATTR_ATR_STRING, (LPCBYTE)"", 1);
420  test_rv(rv, hContext, DONT_PANIC);
421 
422  printf("Testing SCardStatus\t\t: ");
423 
424 #ifdef USE_AUTOALLOCATE
425  dwReaderLen = SCARD_AUTOALLOCATE;
426  dwAtrLen = SCARD_AUTOALLOCATE;
427  rv = SCardStatus(hCard, (LPSTR)&pcReader, &dwReaderLen, &dwState, &dwProt,
428  (LPBYTE)&pbAtr, &dwAtrLen);
429 #else
430  dwReaderLen = 100;
431  pcReader = malloc(sizeof(char) * 100);
432  dwAtrLen = MAX_ATR_SIZE;
433 
434  rv = SCardStatus(hCard, pcReader, &dwReaderLen, &dwState, &dwProt,
435  pbAtr, &dwAtrLen);
436 #endif
437  test_rv(rv, hContext, PANIC);
438 
439  printf("Current Reader Name\t\t: " GREEN "%s\n" NORMAL, pcReader);
440  printf("Current Reader State\t\t: " GREEN "0x%.4lx\n" NORMAL, dwState);
441  printf("Current Reader Protocol\t\t: T=" GREEN "%ld\n" NORMAL, dwProt - 1);
442  printf("Current Reader ATR Size\t\t: " GREEN "%ld" NORMAL " bytes\n",
443  dwAtrLen);
444  printf("Current Reader ATR Value\t: " GREEN);
445 
446  for (i = 0; i < dwAtrLen; i++)
447  {
448  printf("%02X ", pbAtr[i]);
449  }
450  printf(NORMAL "\n");
451 
452 #ifdef USE_AUTOALLOCATE
453  printf("Testing SCardFreeMemory\t\t: ");
454  rv = SCardFreeMemory(hContext, pcReader);
455  test_rv(rv, hContext, PANIC);
456  printf("Testing SCardFreeMemory\t\t: ");
457  rv = SCardFreeMemory(hContext, pbAtr);
458  test_rv(rv, hContext, PANIC);
459 #else
460  if (pcReader)
461  free(pcReader);
462 #endif
463 
464  printf("Press enter: ");
465  (void)getchar();
466  printf("Testing SCardReconnect\t\t: ");
467  rv = SCardReconnect(hCard, SCARD_SHARE_SHARED,
469  test_rv(rv, hContext, PANIC);
470 
471  printf("Testing SCardDisconnect\t\t: ");
472  rv = SCardDisconnect(hCard, SCARD_UNPOWER_CARD);
473  test_rv(rv, hContext, PANIC);
474 
475 #ifdef USE_AUTOALLOCATE
476  printf("Testing SCardFreeMemory\t\t: ");
477  rv = SCardFreeMemory(hContext, mszReaders);
478  test_rv(rv, hContext, PANIC);
479 #else
480  free(mszReaders);
481 #endif
482 
483  printf("Testing SCardReleaseContext\t: ");
484  rv = SCardReleaseContext(hContext);
485  test_rv(rv, hContext, PANIC);
486 
487  printf("\n");
488  printf("PC/SC Test Completed Successfully !\n");
489 
490  return 0;
491 }
SCARD_S_SUCCESS
#define SCARD_S_SUCCESS
No error was encountered.
Definition: pcsclite.h:107
SCARD_ATTR_DEVICE_FRIENDLY_NAME
#define SCARD_ATTR_DEVICE_FRIENDLY_NAME
Reader's display name.
Definition: reader.h:111
SCARD_STATE_UNKNOWN
#define SCARD_STATE_UNKNOWN
Reader unknown.
Definition: pcsclite.h:269
SCARD_READERSTATE
Definition: pcsclite.h:68
SCARD_SHARE_SHARED
#define SCARD_SHARE_SHARED
Shared mode only.
Definition: pcsclite.h:250
SCARD_AUTOALLOCATE
#define SCARD_AUTOALLOCATE
see SCardFreeMemory()
Definition: pcsclite.h:234
SCARDHANDLE
LONG SCARDHANDLE
hCard returned by SCardConnect()
Definition: pcsclite.h:55
SCARD_ATTR_VENDOR_NAME
#define SCARD_ATTR_VENDOR_NAME
Vendor name.
Definition: reader.h:58
SCARD_CTL_CODE
#define SCARD_CTL_CODE(code)
Provide source compatibility on different platforms.
Definition: reader.h:118
SCARD_PROTOCOL_T0
#define SCARD_PROTOCOL_T0
T=0 active protocol.
Definition: pcsclite.h:242
MAX_ATR_SIZE
#define MAX_ATR_SIZE
Maximum ATR size.
Definition: pcsclite.h:59
SCARD_E_NO_READERS_AVAILABLE
#define SCARD_E_NO_READERS_AVAILABLE
Cannot find a smart card reader.
Definition: pcsclite.h:202
SCARD_PROTOCOL_T1
#define SCARD_PROTOCOL_T1
T=1 active protocol.
Definition: pcsclite.h:243
reader.h
This keeps a list of defines shared between the driver and the application.
SCARD_PCI_RAW
#define SCARD_PCI_RAW
protocol control information (PCI) for RAW protocol
Definition: pcsclite.h:97
SCARD_PROTOCOL_RAW
#define SCARD_PROTOCOL_RAW
Raw active protocol.
Definition: pcsclite.h:244
INFINITE
#define INFINITE
Infinite timeout.
Definition: pcsclite.h:280
SCARD_UNPOWER_CARD
#define SCARD_UNPOWER_CARD
Power down on close.
Definition: pcsclite.h:255
SCARD_PCI_T1
#define SCARD_PCI_T1
protocol control information (PCI) for T=1
Definition: pcsclite.h:96
winscard.h
This handles smart card reader communications.
pcsc_stringify_error
PCSC_API const char * pcsc_stringify_error(const LONG pcscError)
Returns a human readable text for the given PC/SC error code.
Definition: error.c:82
SCARD_ATTR_ATR_STRING
#define SCARD_ATTR_ATR_STRING
Answer to reset (ATR) string.
Definition: reader.h:91
SCARDCONTEXT
LONG SCARDCONTEXT
hContext returned by SCardEstablishContext()
Definition: pcsclite.h:52
SCARD_ATTR_VENDOR_IFD_VERSION
#define SCARD_ATTR_VENDOR_IFD_VERSION
Vendor-supplied interface device version (DWORD in the form 0xMMmmbbbb where MM = major version,...
Definition: reader.h:60
MAX_BUFFER_SIZE
#define MAX_BUFFER_SIZE
Maximum Tx/Rx Buffer for short APDU.
Definition: pcsclite.h:298
SCARD_IO_REQUEST
Protocol Control Information (PCI)
Definition: pcsclite.h:80
SCARD_SCOPE_SYSTEM
#define SCARD_SCOPE_SYSTEM
Scope in system.
Definition: pcsclite.h:237
SCARD_PCI_T0
#define SCARD_PCI_T0
protocol control information (PCI) for T=0
Definition: pcsclite.h:95
pcsclite.h
This keeps a list of defines for pcsc-lite.
SCARD_STATE_EMPTY
#define SCARD_STATE_EMPTY
Card removed.
Definition: pcsclite.h:271
SCARD_ATTR_MAXINPUT
#define SCARD_ATTR_MAXINPUT
FIXME.
Definition: reader.h:97