/[pcsclite]/trunk/Drivers/ccid/examples/scardcontrol.c
ViewVC logotype

Contents of /trunk/Drivers/ccid/examples/scardcontrol.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4337 - (show annotations) (download)
Tue Jul 21 14:39:15 2009 UTC (3 years, 10 months ago) by rousseau
File MIME type: text/plain
File size: 16832 byte(s)
update copyright date
1 /*
2 scardcontrol.c: sample code to use/test SCardControl() API
3 Copyright (C) 2004-2009 Ludovic Rousseau
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc., 51
17 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /*
21 * $Id$
22 */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <sys/time.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <arpa/inet.h>
30 #ifdef __APPLE__
31 #include <PCSC/winscard.h>
32 #include <PCSC/wintypes.h>
33 #else
34 #include <winscard.h>
35 #endif
36 #include <reader.h>
37
38 #undef VERIFY_PIN
39 #define MODIFY_PIN
40 #undef GET_GEMPC_FIRMWARE
41
42 #ifndef TRUE
43 #define TRUE 1
44 #define FALSE 0
45 #endif
46
47 #define IOCTL_SMARTCARD_VENDOR_IFD_EXCHANGE SCARD_CTL_CODE(1)
48
49 /* PCSC error message pretty print */
50 #define PCSC_ERROR_EXIT(rv, text) \
51 if (rv != SCARD_S_SUCCESS) \
52 { \
53 printf(text ": %s (0x%lX)\n", pcsc_stringify_error(rv), rv); \
54 goto end; \
55 } \
56 else \
57 printf(text ": OK\n\n");
58
59 #define PCSC_ERROR_CONT(rv, text) \
60 if (rv != SCARD_S_SUCCESS) \
61 printf(text ": %s (0x%lX)\n", pcsc_stringify_error(rv), rv); \
62 else \
63 printf(text ": OK\n\n");
64
65 int main(int argc, char *argv[])
66 {
67 LONG rv;
68 SCARDCONTEXT hContext;
69 DWORD dwReaders;
70 LPSTR mszReaders = NULL;
71 char *ptr, **readers = NULL;
72 int nbReaders;
73 SCARDHANDLE hCard;
74 DWORD dwActiveProtocol, dwReaderLen, dwState, dwProt, dwAtrLen;
75 BYTE pbAtr[MAX_ATR_SIZE] = "";
76 char pbReader[MAX_READERNAME] = "";
77 int reader_nb;
78 unsigned int i;
79 unsigned char bSendBuffer[MAX_BUFFER_SIZE];
80 unsigned char bRecvBuffer[MAX_BUFFER_SIZE];
81 DWORD send_length, length;
82 DWORD verify_ioctl = 0;
83 DWORD modify_ioctl = 0;
84 SCARD_IO_REQUEST pioRecvPci;
85 SCARD_IO_REQUEST pioSendPci;
86 PCSC_TLV_STRUCTURE *pcsc_tlv;
87 #if defined(VERIFY_PIN) | defined(MODIFY_PIN)
88 int offset;
89 #endif
90 #ifdef VERIFY_PIN
91 PIN_VERIFY_STRUCTURE *pin_verify;
92 #endif
93 #ifdef MODIFY_PIN
94 PIN_MODIFY_STRUCTURE *pin_modify;
95 #endif
96
97 printf("SCardControl sample code\n");
98 printf("V 1.3 © 2004-2009, Ludovic Rousseau <ludovic.rousseau@free.fr>\n");
99
100 printf("\nTHIS PROGRAM IS NOT DESIGNED AS A TESTING TOOL!\n");
101 printf("Do NOT use it unless you really know what you do.\n\n");
102
103 rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
104 if (rv != SCARD_S_SUCCESS)
105 {
106 printf("SCardEstablishContext: Cannot Connect to Resource Manager %lX\n", rv);
107 return 1;
108 }
109
110 /* Retrieve the available readers list */
111 rv = SCardListReaders(hContext, NULL, NULL, &dwReaders);
112 PCSC_ERROR_EXIT(rv, "SCardListReaders")
113
114 mszReaders = malloc(sizeof(char)*dwReaders);
115 if (mszReaders == NULL)
116 {
117 printf("malloc: not enough memory\n");
118 goto end;
119 }
120
121 rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
122 if (rv != SCARD_S_SUCCESS)
123 printf("SCardListReader: %lX\n", rv);
124
125 /* Extract readers from the null separated string and get the total
126 * number of readers */
127 nbReaders = 0;
128 ptr = mszReaders;
129 while (*ptr != '\0')
130 {
131 ptr += strlen(ptr)+1;
132 nbReaders++;
133 }
134
135 if (nbReaders == 0)
136 {
137 printf("No reader found\n");
138 goto end;
139 }
140
141 /* allocate the readers table */
142 readers = calloc(nbReaders, sizeof(char *));
143 if (NULL == readers)
144 {
145 printf("Not enough memory for readers[]\n");
146 goto end;
147 }
148
149 /* fill the readers table */
150 nbReaders = 0;
151 ptr = mszReaders;
152 printf("Available readers (use command line argument to select)\n");
153 while (*ptr != '\0')
154 {
155 printf("%d: %s\n", nbReaders, ptr);
156 readers[nbReaders] = ptr;
157 ptr += strlen(ptr)+1;
158 nbReaders++;
159 }
160 printf("\n");
161
162 if (argc > 1)
163 {
164 reader_nb = atoi(argv[1]);
165 if (reader_nb < 0 || reader_nb >= nbReaders)
166 {
167 printf("Wrong reader index: %d\n", reader_nb);
168 goto end;
169 }
170 }
171 else
172 reader_nb = 0;
173
174 /* connect to a reader (even without a card) */
175 dwActiveProtocol = -1;
176 printf("Using reader: %s\n", readers[reader_nb]);
177 rv = SCardConnect(hContext, readers[reader_nb], SCARD_SHARE_DIRECT,
178 SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
179 printf(" Protocol: %ld\n", dwActiveProtocol);
180 PCSC_ERROR_EXIT(rv, "SCardConnect")
181
182 #ifdef GET_GEMPC_FIRMWARE
183 /* get GemPC firmware */
184 printf(" Get GemPC Firmware\n");
185
186 /* this is specific to Gemplus readers */
187 bSendBuffer[0] = 0x02;
188 rv = SCardControl(hCard, IOCTL_SMARTCARD_VENDOR_IFD_EXCHANGE, bSendBuffer,
189 1, bRecvBuffer, sizeof(bRecvBuffer), &length);
190
191 printf(" Firmware: ");
192 for (i=0; i<length; i++)
193 printf("%02X ", bRecvBuffer[i]);
194 printf("\n");
195
196 bRecvBuffer[length] = '\0';
197 printf(" Firmware: %s (length %ld bytes)\n", bRecvBuffer, length);
198
199 PCSC_ERROR_CONT(rv, "SCardControl")
200 #endif
201
202 /* get card status */
203 dwAtrLen = sizeof(pbAtr);
204 dwReaderLen = sizeof(pbReader);
205 rv = SCardStatus(hCard, pbReader, &dwReaderLen, &dwState, &dwProt,
206 pbAtr, &dwAtrLen);
207 printf(" Reader: %s (length %ld bytes)\n", pbReader, dwReaderLen);
208 printf(" State: 0x%04lX\n", dwState);
209 printf(" Prot: %ld\n", dwProt);
210 printf(" ATR (length %ld bytes):", dwAtrLen);
211 for (i=0; i<dwAtrLen; i++)
212 printf(" %02X", pbAtr[i]);
213 printf("\n");
214 PCSC_ERROR_CONT(rv, "SCardStatus")
215
216 if (dwState & SCARD_ABSENT)
217 {
218 printf("No card inserted\n");
219 goto end;
220 }
221
222 /* does the reader support PIN verification? */
223 rv = SCardControl(hCard, CM_IOCTL_GET_FEATURE_REQUEST, NULL, 0,
224 bRecvBuffer, sizeof(bRecvBuffer), &length);
225 PCSC_ERROR_EXIT(rv, "SCardControl")
226
227 printf(" TLV (%ld): ", length);
228 for (i=0; i<length; i++)
229 printf("%02X ", bRecvBuffer[i]);
230 printf("\n");
231
232 PCSC_ERROR_CONT(rv, "SCardControl(CM_IOCTL_GET_FEATURE_REQUEST)")
233
234 if (length % sizeof(PCSC_TLV_STRUCTURE))
235 {
236 printf("Inconsistent result! Bad TLV values!\n");
237 goto end;
238 }
239
240 /* get the number of elements instead of the complete size */
241 length /= sizeof(PCSC_TLV_STRUCTURE);
242
243 pcsc_tlv = (PCSC_TLV_STRUCTURE *)bRecvBuffer;
244 for (i = 0; i < length; i++)
245 {
246 if (pcsc_tlv[i].tag == FEATURE_VERIFY_PIN_DIRECT)
247 verify_ioctl = ntohl(pcsc_tlv[i].value);
248 if (pcsc_tlv[i].tag == FEATURE_MODIFY_PIN_DIRECT)
249 modify_ioctl = ntohl(pcsc_tlv[i].value);
250 }
251
252 if (0 == verify_ioctl)
253 {
254 printf("Reader %s does not support PIN verification\n",
255 readers[reader_nb]);
256 goto end;
257 }
258
259 /* connect to a reader (even without a card) */
260 dwActiveProtocol = -1;
261 rv = SCardReconnect(hCard, SCARD_SHARE_SHARED,
262 SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1, SCARD_LEAVE_CARD,
263 &dwActiveProtocol);
264 printf(" Protocol: %ld\n", dwActiveProtocol);
265 PCSC_ERROR_EXIT(rv, "SCardReconnect")
266
267 switch(dwActiveProtocol)
268 {
269 case SCARD_PROTOCOL_T0:
270 pioSendPci = *SCARD_PCI_T0;
271 break;
272 case SCARD_PROTOCOL_T1:
273 pioSendPci = *SCARD_PCI_T1;
274 break;
275 default:
276 printf("Unknown protocol. No card present?\n");
277 return -1;
278 }
279
280 /* APDU select applet */
281 printf("Select applet: ");
282 send_length = 11;
283 memcpy(bSendBuffer, "\x00\xA4\x04\x00\x06\xA0\x00\x00\x00\x18\xFF",
284 send_length);
285 for (i=0; i<send_length; i++)
286 printf(" %02X", bSendBuffer[i]);
287 printf("\n");
288 length = sizeof(bRecvBuffer);
289 rv = SCardTransmit(hCard, &pioSendPci, bSendBuffer, send_length,
290 &pioRecvPci, bRecvBuffer, &length);
291 printf(" card response:");
292 for (i=0; i<length; i++)
293 printf(" %02X", bRecvBuffer[i]);
294 printf("\n");
295 PCSC_ERROR_EXIT(rv, "SCardTransmit")
296 if ((bRecvBuffer[0] != 0x90) || (bRecvBuffer[1] != 0x00))
297 {
298 printf("Error: test applet not found!\n");
299 goto end;
300 }
301
302 #ifdef VERIFY_PIN
303 /* verify PIN */
304 printf(" Secure verify PIN\n");
305 pin_verify = (PIN_VERIFY_STRUCTURE *)bSendBuffer;
306
307 /* PC/SC v2.02.05 Part 10 PIN verification data structure */
308 pin_verify -> bTimerOut = 0x00;
309 pin_verify -> bTimerOut2 = 0x00;
310 pin_verify -> bmFormatString = 0x82;
311 pin_verify -> bmPINBlockString = 0x04;
312 pin_verify -> bmPINLengthFormat = 0x00;
313 pin_verify -> wPINMaxExtraDigit = HOST_TO_CCID_16(0x0408); /* Min Max */
314 pin_verify -> bEntryValidationCondition = 0x02; /* validation key pressed */
315 pin_verify -> bNumberMessage = 0x01;
316 pin_verify -> wLangId = HOST_TO_CCID_16(0x0904);
317 pin_verify -> bMsgIndex = 0x00;
318 pin_verify -> bTeoPrologue[0] = 0x00;
319 pin_verify -> bTeoPrologue[1] = 0x00;
320 pin_verify -> bTeoPrologue[2] = 0x00;
321 /* pin_verify -> ulDataLength = 0x00; we don't know the size yet */
322
323 /* APDU: 00 20 00 00 08 30 30 30 30 00 00 00 00 */
324 offset = 0;
325 pin_verify -> abData[offset++] = 0x00; /* CLA */
326 pin_verify -> abData[offset++] = 0x20; /* INS: VERIFY */
327 pin_verify -> abData[offset++] = 0x00; /* P1 */
328 pin_verify -> abData[offset++] = 0x00; /* P2 */
329 pin_verify -> abData[offset++] = 0x08; /* Lc: 8 data bytes */
330 pin_verify -> abData[offset++] = 0x30; /* '0' */
331 pin_verify -> abData[offset++] = 0x30; /* '0' */
332 pin_verify -> abData[offset++] = 0x30; /* '0' */
333 pin_verify -> abData[offset++] = 0x30; /* '0' */
334 pin_verify -> abData[offset++] = 0x00; /* '\0' */
335 pin_verify -> abData[offset++] = 0x00; /* '\0' */
336 pin_verify -> abData[offset++] = 0x00; /* '\0' */
337 pin_verify -> abData[offset++] = 0x00; /* '\0' */
338 pin_verify -> ulDataLength = HOST_TO_CCID_32(offset); /* APDU size */
339
340 length = sizeof(PIN_VERIFY_STRUCTURE) + offset -1; /* -1 because PIN_VERIFY_STRUCTURE contains the first byte of abData[] */
341
342 printf(" command:");
343 for (i=0; i<length; i++)
344 printf(" %02X", bSendBuffer[i]);
345 printf("\n");
346 printf("Enter your PIN: ");
347 fflush(stdout);
348 rv = SCardControl(hCard, verify_ioctl, bSendBuffer,
349 length, bRecvBuffer, sizeof(bRecvBuffer), &length);
350
351 {
352 #ifndef S_SPLINT_S
353 fd_set fd;
354 #endif
355 struct timeval timeout;
356
357 FD_ZERO(&fd);
358 FD_SET(STDIN_FILENO, &fd); /* stdin */
359 timeout.tv_sec = 0; /* timeout = 0.1s */
360 timeout.tv_usec = 100000;
361
362 /* we only try to read stdin if the pinpad is on a keyboard
363 * we do not read stdin for a SPR 532 for example */
364 if (select(1, &fd, NULL, NULL, &timeout) > 0)
365 {
366 /* read the fake digits */
367 char in[40]; /* 4 digits + \n + \0 */
368 (void)fgets(in, sizeof(in), stdin);
369
370 printf("keyboard sent: %s", in);
371 }
372 else
373 /* if it is not a keyboard */
374 printf("\n");
375 }
376
377 printf(" card response:");
378 for (i=0; i<length; i++)
379 printf(" %02X", bRecvBuffer[i]);
380 printf("\n");
381 PCSC_ERROR_CONT(rv, "SCardControl")
382
383 /* verify PIN dump */
384 printf("\nverify PIN dump: ");
385 send_length = 5;
386 memcpy(bSendBuffer, "\x00\x40\x00\x00\xFF",
387 send_length);
388 for (i=0; i<send_length; i++)
389 printf(" %02X", bSendBuffer[i]);
390 printf("\n");
391 length = sizeof(bRecvBuffer);
392 rv = SCardTransmit(hCard, &pioSendPci, bSendBuffer, send_length,
393 &pioRecvPci, bRecvBuffer, &length);
394 printf(" card response:");
395 for (i=0; i<length; i++)
396 printf(" %02X", bRecvBuffer[i]);
397 printf("\n");
398 PCSC_ERROR_EXIT(rv, "SCardTransmit")
399
400 if ((2 == length) && (0x6C == bRecvBuffer[0]))
401 {
402 printf("\nverify PIN dump: ");
403 send_length = 5;
404 memcpy(bSendBuffer, "\x00\x40\x00\x00\xFF",
405 send_length);
406 bSendBuffer[4] = bRecvBuffer[1];
407 for (i=0; i<send_length; i++)
408 printf(" %02X", bSendBuffer[i]);
409 printf("\n");
410 length = sizeof(bRecvBuffer);
411 rv = SCardTransmit(hCard, &pioSendPci, bSendBuffer, send_length,
412 &pioRecvPci, bRecvBuffer, &length);
413 printf(" card response:");
414 for (i=0; i<length; i++)
415 printf(" %02X", bRecvBuffer[i]);
416 printf("\n");
417 PCSC_ERROR_EXIT(rv, "SCardTransmit")
418 }
419 #endif
420
421 /* check if the reader supports Modify PIN */
422 if (0 == modify_ioctl)
423 {
424 printf("Reader %s does not support PIN modification\n",
425 readers[reader_nb]);
426 goto end;
427 }
428
429 #ifdef MODIFY_PIN
430 /* Modify PIN */
431 printf(" Secure modify PIN\n");
432 pin_modify = (PIN_MODIFY_STRUCTURE *)bSendBuffer;
433
434 /* Table for bConfirmPIN and bNumberMessage
435 * bConfirmPIN = 3, bNumberMessage = 3: "Enter Pin" "New Pin" "Confirm Pin"
436 * bConfirmPIN = 2, bNumberMessage = 2: "Enter Pin" "New Pin"
437 * bConfirmPIN = 1, bNumberMessage = 2: "New Pin" "Confirm Pin"
438 * bConfirmPIN = 0, bNumberMessage = 1: "New Pin"
439 */
440 /* PC/SC v2.02.05 Part 10 PIN modification data structure */
441 pin_modify -> bTimerOut = 0x00;
442 pin_modify -> bTimerOut2 = 0x00;
443 pin_modify -> bmFormatString = 0x82;
444 pin_modify -> bmPINBlockString = 0x04;
445 pin_modify -> bmPINLengthFormat = 0x00;
446 pin_modify -> bInsertionOffsetOld = 0x00; /* offset from APDU start */
447 pin_modify -> bInsertionOffsetNew = 0x04; /* offset from APDU start */
448 pin_modify -> wPINMaxExtraDigit = HOST_TO_CCID_16(0x0408); /* Min Max */
449 pin_modify -> bConfirmPIN = 0x03; /* b0 set = confirmation requested */
450 /* b1 set = current PIN entry requested */
451 pin_modify -> bEntryValidationCondition = 0x02; /* validation key pressed */
452 pin_modify -> bNumberMessage = 0x03; /* see table above */
453 pin_modify -> wLangId = HOST_TO_CCID_16(0x0904);
454 pin_modify -> bMsgIndex1 = 0x00;
455 pin_modify -> bMsgIndex2 = 0x00;
456 pin_modify -> bMsgIndex3 = 0x00;
457 pin_modify -> bTeoPrologue[0] = 0x00;
458 pin_modify -> bTeoPrologue[1] = 0x00;
459 pin_modify -> bTeoPrologue[2] = 0x00;
460 /* pin_modify -> ulDataLength = 0x00; we don't know the size yet */
461
462 /* APDU: 00 20 00 00 08 30 30 30 30 00 00 00 00 */
463 offset = 0;
464 pin_modify -> abData[offset++] = 0x00; /* CLA */
465 pin_modify -> abData[offset++] = 0x24; /* INS: CHANGE/UNBLOCK */
466 pin_modify -> abData[offset++] = 0x00; /* P1 */
467 pin_modify -> abData[offset++] = 0x00; /* P2 */
468 pin_modify -> abData[offset++] = 0x08; /* Lc: 2x8 data bytes */
469 pin_modify -> abData[offset++] = 0x30; /* '0' old PIN */
470 pin_modify -> abData[offset++] = 0x30; /* '0' */
471 pin_modify -> abData[offset++] = 0x30; /* '0' */
472 pin_modify -> abData[offset++] = 0x30; /* '0' */
473 pin_modify -> abData[offset++] = 0x30; /* '0' new PIN */
474 pin_modify -> abData[offset++] = 0x30; /* '0' */
475 pin_modify -> abData[offset++] = 0x30; /* '0' */
476 pin_modify -> abData[offset++] = 0x30; /* '0' */
477 pin_modify -> ulDataLength = HOST_TO_CCID_32(offset); /* APDU size */
478
479 length = sizeof(PIN_MODIFY_STRUCTURE) + offset -1; /* -1 because PIN_MODIFY_STRUCTURE contains the first byte of abData[] */
480
481 printf(" command:");
482 for (i=0; i<length; i++)
483 printf(" %02X", bSendBuffer[i]);
484 printf("\n");
485 printf("Enter your PIN: ");
486 fflush(stdout);
487 rv = SCardControl(hCard, modify_ioctl, bSendBuffer,
488 length, bRecvBuffer, sizeof(bRecvBuffer), &length);
489
490 printf(" card response:");
491 for (i=0; i<length; i++)
492 printf(" %02X", bRecvBuffer[i]);
493 printf("\n");
494 PCSC_ERROR_CONT(rv, "SCardControl")
495
496 {
497 #ifndef S_SPLINT_S
498 fd_set fd;
499 #endif
500 struct timeval timeout;
501
502 /* old PIN, new PIN, confirmation PIN */
503 /* if the command is aborted we will not read every "PIN" */
504 for (i=0; i<3; i++)
505 {
506 FD_ZERO(&fd);
507 FD_SET(STDIN_FILENO, &fd); /* stdin */
508 timeout.tv_sec = 0; /* timeout = 0.1s */
509 timeout.tv_usec = 100000;
510
511 /* we only try to read stdin if the pinpad is on a keyboard
512 * we do not read stdin for a SPR 532 for example */
513 if (select(1, &fd, NULL, NULL, &timeout) > 0)
514 {
515 /* read the fake digits */
516 char in[40]; /* 4 digits + \n + \0 */
517
518 (void)fgets(in, sizeof(in), stdin);
519 printf("keyboard sent: %s", in);
520 }
521 }
522 }
523
524 /* modify PIN dump */
525 printf("\nmodify PIN dump: ");
526 send_length = 5;
527 memcpy(bSendBuffer, "\x00\x40\x00\x00\xFF",
528 send_length);
529 for (i=0; i<send_length; i++)
530 printf(" %02X", bSendBuffer[i]);
531 printf("\n");
532 length = sizeof(bRecvBuffer);
533 rv = SCardTransmit(hCard, &pioSendPci, bSendBuffer, send_length,
534 &pioRecvPci, bRecvBuffer, &length);
535 printf(" card response:");
536 for (i=0; i<length; i++)
537 printf(" %02X", bRecvBuffer[i]);
538 printf("\n");
539 PCSC_ERROR_EXIT(rv, "SCardTransmit")
540
541 if ((2 == length) && (0x6C == bRecvBuffer[0]))
542 {
543 printf("\nverify PIN dump: ");
544 send_length = 5;
545 memcpy(bSendBuffer, "\x00\x40\x00\x00\xFF",
546 send_length);
547 bSendBuffer[4] = bRecvBuffer[1];
548 for (i=0; i<send_length; i++)
549 printf(" %02X", bSendBuffer[i]);
550 printf("\n");
551 length = sizeof(bRecvBuffer);
552 rv = SCardTransmit(hCard, &pioSendPci, bSendBuffer, send_length,
553 &pioRecvPci, bRecvBuffer, &length);
554 printf(" card response:");
555 for (i=0; i<length; i++)
556 printf(" %02X", bRecvBuffer[i]);
557 printf("\n");
558 PCSC_ERROR_EXIT(rv, "SCardTransmit")
559 }
560 #endif
561
562 /* card disconnect */
563 rv = SCardDisconnect(hCard, SCARD_UNPOWER_CARD);
564 PCSC_ERROR_CONT(rv, "SCardDisconnect")
565
566 end:
567 /* We try to leave things as clean as possible */
568 rv = SCardReleaseContext(hContext);
569 if (rv != SCARD_S_SUCCESS)
570 printf("SCardReleaseContext: %s (0x%lX)\n", pcsc_stringify_error(rv),
571 rv);
572
573 /* free allocated memory */
574 if (mszReaders)
575 free(mszReaders);
576 if (readers)
577 free(readers);
578
579 return 0;
580 } /* main */
581

Properties

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

  ViewVC Help
Powered by ViewVC 1.1.5