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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1330 - (hide annotations) (download)
Mon Feb 21 14:06:53 2005 UTC (8 years, 3 months ago) by rousseau
File MIME type: text/plain
File size: 15424 byte(s)
OpenSerialByName(): use tx_buffer[] = { 0x02 } insead of "\x02" since we
want 1 byte only and not a null-terminated string
1 rousseau 414 /*
2     * ccid_serial.c: communicate with a GemPC Twin smart card reader
3 rousseau 1147 * Copyright (C) 2001-2004 Ludovic Rousseau <ludovic.rousseau@free.fr>
4 rousseau 414 *
5     * Thanks to Niki W. Waibel <niki.waibel@gmx.net> for a prototype version
6     *
7     * This program is free software; you can redistribute it and/or modify it
8     * under the terms of the GNU General Public License as published by the
9     * Free Software Foundation; either version 2 of the License, or (at your
10     * option) any later version.
11     *
12     * This program is distributed in the hope that it will be useful, but
13     * WITHOUT ANY WARRANTY; without even the implied warranty of
14     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15     * General Public License for more details.
16     *
17     * You should have received a copy of the GNU General Public License along
18     * with this program; if not, write to the Free Software Foundation, Inc.,
19     * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20     */
21    
22     /*
23     * $Id$
24     */
25    
26     #include <stdio.h>
27     #include <stdlib.h>
28     #include <fcntl.h>
29     #include <unistd.h>
30     #include <termios.h>
31     #include <string.h>
32     #include <errno.h>
33     #include <sys/time.h>
34     #include <sys/types.h>
35 rousseau 1022 #include <PCSC/ifdhandler.h>
36 rousseau 414
37 rousseau 877 #include "defs.h"
38     #include "ccid_ifdhandler.h"
39 rousseau 414 #include "config.h"
40     #include "debug.h"
41     #include "ccid.h"
42     #include "utils.h"
43 rousseau 1022 #include "commands.h"
44 rousseau 414
45 rousseau 1147 /* communication timeout in seconds
46     * the value is set at the end of OpenSerialByName() */
47     int SerialTimeout;
48 rousseau 414
49     #define SYNC 0x03
50     #define CTRL_ACK 0x06
51     #define CTRL_NAK 0x15
52     #define RDR_to_PC_NotifySlotChange 0x50
53     #define CARD_ABSENT 0x02
54     #define CARD_PRESENT 0x03
55    
56     /*
57     * normal command:
58     * 1 : SYNC
59     * 1 : CTRL
60     * 10 +data length : CCID command
61     * 1 : LRC
62     *
63     * SYNC : 0x03
64     * CTRL : ACK (0x06) or NAK (0x15)
65     * CCID command : see USB CCID specs
66     * LRC : xor of all the previous byes
67     *
68     * Error message:
69     * 1 : SYNC (0x03)
70     * 1 : CTRL (NAK: 0x15)
71     * 1 : LRC (0x16)
72     *
73     * Card insertion/withdrawal
74     * 1 : RDR_to_PC_NotifySlotChange (0x50)
75     * 1 : bmSlotIccState
76     * 0x02 if card absent
77     * 0x03 is card present
78     *
79     * Time request
80     * T=1 : normal CCID command
81     * T=0 : 1 byte (value between 0x80 and 0xFF)
82     *
83     */
84    
85 rousseau 453 /*
86     * You may get read timeout after a card movement.
87     * This is because you will get the echo of the CCID command
88     * but not the result of the command.
89     *
90     * This is not an applicative issue since the card is either removed (and
91     * powered off) or just inserted (and not yet powered on).
92     */
93    
94     /* 271 = max size for short APDU
95     * 2 bytes for header
96     * 1 byte checksum
97     * doubled for echo
98     */
99     #define GEMPCTWIN_MAXBUF (271 +2 +1) * 2
100    
101 rousseau 414 typedef struct
102     {
103     /*
104     * File handle on the serial port
105     */
106     int fd;
107    
108     /*
109 rousseau 649 * device used ("/dev/ttyS?" under Linux)
110 rousseau 414 */
111 rousseau 1054 /*@null@*/ char *device;
112 rousseau 414
113     /*
114 rousseau 453 * serial communication buffer
115     */
116     unsigned char buffer[GEMPCTWIN_MAXBUF];
117    
118     /*
119     * next available byte
120     */
121     int buffer_offset;
122    
123     /*
124     * number of available bytes
125     */
126     int buffer_offset_last;
127    
128     /*
129 rousseau 414 * CCID infos common to USB and serial
130     */
131     _ccid_descriptor ccid;
132    
133     } _serialDevice;
134    
135     /* The _serialDevice structure must be defined before including ccid_serial.h */
136     #include "ccid_serial.h"
137    
138 rousseau 892 /* no need to initialize to 0 since it is static */
139 rousseau 1077 static _serialDevice serialDevice[CCID_DRIVER_MAX_READERS];
140 rousseau 414
141 rousseau 1050 /* unexported functions */
142 rousseau 1106 static int ReadChunk(unsigned int reader_index, unsigned char *buffer,
143     int buffer_length, int min_length);
144 rousseau 1050
145 rousseau 1106 static int get_bytes(unsigned int reader_index, unsigned char *buffer,
146     int length);
147 rousseau 1050
148    
149 rousseau 414 /*****************************************************************************
150     *
151     * WriteSerial: Send bytes to the card reader
152     *
153     *****************************************************************************/
154 rousseau 1106 status_t WriteSerial(unsigned int reader_index, unsigned int length,
155     unsigned char *buffer)
156 rousseau 414 {
157     int i;
158     unsigned char lrc;
159     unsigned char low_level_buffer[GEMPCTWIN_MAXBUF];
160    
161     #ifdef DEBUG_LEVEL_COMM
162 rousseau 1047 char debug_header[] = "-> 123456 ";
163 rousseau 414
164 rousseau 1106 sprintf(debug_header, "-> %06X ", reader_index);
165 rousseau 414 #endif
166    
167     if (length > GEMPCTWIN_MAXBUF-3)
168     {
169 rousseau 608 DEBUG_CRITICAL3("command too long: %d for max %d",
170 rousseau 414 length, GEMPCTWIN_MAXBUF-3);
171     return STATUS_UNSUCCESSFUL;
172     }
173    
174     /* header */
175     low_level_buffer[0] = 0x03; /* SYNC */
176     low_level_buffer[1] = 0x06; /* ACK */
177    
178     /* CCID command */
179     memcpy(low_level_buffer+2, buffer, length);
180    
181     /* checksum */
182     lrc = 0;
183     for(i=0; i<length+2; i++)
184     lrc ^= low_level_buffer[i];
185     low_level_buffer[length+2] = lrc;
186    
187     #ifdef DEBUG_LEVEL_COMM
188     DEBUG_XXD(debug_header, low_level_buffer, length+3);
189     #endif
190    
191 rousseau 1106 if (write(serialDevice[reader_index].fd, low_level_buffer,
192 rousseau 453 length+3) != length+3)
193 rousseau 414 {
194 rousseau 608 DEBUG_CRITICAL2("write error: %s", strerror(errno));
195 rousseau 414 return STATUS_UNSUCCESSFUL;
196     }
197    
198     return STATUS_SUCCESS;
199     } /* WriteSerial */
200    
201    
202     /*****************************************************************************
203     *
204     * ReadSerial: Receive bytes from the card reader
205     *
206     *****************************************************************************/
207 rousseau 1106 status_t ReadSerial(unsigned int reader_index,
208     /*@unused@*/ unsigned int *length, unsigned char *buffer)
209 rousseau 414 {
210 rousseau 453 unsigned char c;
211     int rv;
212     int echo;
213     int to_read;
214     int i;
215 rousseau 414
216 rousseau 453 /* we get the echo first */
217     echo = TRUE;
218 rousseau 414
219 rousseau 453 start:
220 rousseau 462 DEBUG_COMM("start");
221 rousseau 1106 if ((rv = get_bytes(reader_index, &c, 1)) != STATUS_SUCCESS)
222 rousseau 453 return rv;
223 rousseau 414
224 rousseau 453 if (c == RDR_to_PC_NotifySlotChange)
225     goto slot_change;
226 rousseau 414
227 rousseau 453 if (c == SYNC)
228     goto sync;
229 rousseau 414
230 rousseau 453 if (c >= 0x80)
231 rousseau 462 {
232     DEBUG_COMM2("time request: 0x%02X", c);
233 rousseau 453 goto start;
234 rousseau 462 }
235 rousseau 414
236 rousseau 453 DEBUG_CRITICAL2("Got 0x%02X", c);
237     return STATUS_COMM_ERROR;
238 rousseau 414
239 rousseau 453 slot_change:
240 rousseau 462 DEBUG_COMM("slot change");
241 rousseau 1106 if ((rv = get_bytes(reader_index, &c, 1)) != STATUS_SUCCESS)
242 rousseau 453 return rv;
243 rousseau 414
244 rousseau 453 if (c == CARD_ABSENT)
245 rousseau 774 {
246 rousseau 453 DEBUG_COMM("Card removed");
247 rousseau 774 }
248 rousseau 453 else
249     if (c == CARD_PRESENT)
250 rousseau 774 {
251 rousseau 453 DEBUG_COMM("Card inserted");
252 rousseau 774 }
253 rousseau 453 else
254 rousseau 774 {
255 rousseau 1044 DEBUG_COMM2("Unknown card movement: %d", c);
256 rousseau 774 }
257 rousseau 453 goto start;
258 rousseau 414
259 rousseau 453 sync:
260 rousseau 462 DEBUG_COMM("sync");
261 rousseau 1106 if ((rv = get_bytes(reader_index, &c, 1)) != STATUS_SUCCESS)
262 rousseau 453 return rv;
263 rousseau 414
264 rousseau 453 if (c == CTRL_ACK)
265     goto ack;
266 rousseau 414
267 rousseau 453 if (c == CTRL_NAK)
268     goto nak;
269 rousseau 414
270 rousseau 453 DEBUG_CRITICAL2("Got 0x%02X instead of ACK/NAK", c);
271     return STATUS_COMM_ERROR;
272 rousseau 414
273 rousseau 453 nak:
274 rousseau 462 DEBUG_COMM("nak");
275 rousseau 1106 if ((rv = get_bytes(reader_index, &c, 1)) != STATUS_SUCCESS)
276 rousseau 453 return rv;
277 rousseau 414
278 rousseau 453 if (c != (SYNC ^ CTRL_NAK))
279 rousseau 414 {
280 rousseau 453 DEBUG_CRITICAL2("Wrong LRC: 0x%02X", c);
281     return STATUS_COMM_ERROR;
282 rousseau 414 }
283 rousseau 453 else
284     goto start;
285 rousseau 414
286 rousseau 453 ack:
287 rousseau 462 DEBUG_COMM("ack");
288 rousseau 453 /* normal CCID frame */
289 rousseau 1106 if ((rv = get_bytes(reader_index, buffer, 5)) != STATUS_SUCCESS)
290 rousseau 453 return rv;
291 rousseau 414
292 rousseau 453 /* total frame size */
293     to_read = 10+dw2i(buffer, 1);
294 rousseau 414
295 rousseau 462 DEBUG_COMM2("frame size: %d", to_read);
296 rousseau 1106 if ((rv = get_bytes(reader_index, buffer+5, to_read-5)) != STATUS_SUCCESS)
297 rousseau 453 return rv;
298 rousseau 414
299 rousseau 462 #ifdef DEBUG_LEVEL_COMM
300     DEBUG_XXD("frame: ", buffer, to_read);
301     #endif
302    
303 rousseau 453 /* lrc */
304 rousseau 462 DEBUG_COMM("lrc");
305 rousseau 1106 if ((rv = get_bytes(reader_index, &c, 1)) != STATUS_SUCCESS)
306 rousseau 453 return rv;
307    
308 rousseau 462 DEBUG_COMM2("lrc: 0x%02X", c);
309 rousseau 414 for (i=0; i<to_read; i++)
310 rousseau 453 c ^= buffer[i];
311 rousseau 414
312 rousseau 453 if (c != (SYNC ^ CTRL_ACK))
313     DEBUG_CRITICAL2("Wrong LRC: 0x%02X", c);
314 rousseau 414
315 rousseau 453 if (echo)
316     {
317     echo = FALSE;
318     goto start;
319     }
320 rousseau 414
321     return STATUS_SUCCESS;
322     } /* ReadSerial */
323    
324    
325     /*****************************************************************************
326     *
327 rousseau 453 * get_bytes: get n bytes
328 rousseau 414 *
329     *****************************************************************************/
330 rousseau 1106 int get_bytes(unsigned int reader_index, unsigned char *buffer, int length)
331 rousseau 414 {
332 rousseau 1106 int offset = serialDevice[reader_index].buffer_offset;
333     int offset_last = serialDevice[reader_index].buffer_offset_last;
334 rousseau 414
335 rousseau 608 DEBUG_COMM3("available: %d, needed: %d", offset_last-offset,
336 rousseau 462 length);
337 rousseau 453 /* enough data are available */
338     if (offset + length <= offset_last)
339 rousseau 414 {
340 rousseau 608 DEBUG_COMM("data available");
341 rousseau 1106 memcpy(buffer, serialDevice[reader_index].buffer + offset, length);
342     serialDevice[reader_index].buffer_offset += length;
343 rousseau 414 }
344 rousseau 453 else
345 rousseau 414 {
346 rousseau 453 int present, rv;
347 rousseau 414
348 rousseau 453 /* copy available data */
349     present = offset_last - offset;
350 rousseau 414
351 rousseau 453 if (present > 0)
352 rousseau 462 {
353 rousseau 608 DEBUG_COMM2("some data available: %d", present);
354 rousseau 1106 memcpy(buffer, serialDevice[reader_index].buffer + offset,
355 rousseau 462 present);
356     }
357 rousseau 414
358 rousseau 453 /* get fresh data */
359 rousseau 608 DEBUG_COMM2("get more data: %d", length - present);
360 rousseau 1106 rv = ReadChunk(reader_index, serialDevice[reader_index].buffer,
361     sizeof(serialDevice[reader_index].buffer), length - present);
362 rousseau 453 if (rv < 0)
363     return STATUS_COMM_ERROR;
364 rousseau 414
365 rousseau 453 /* fill the buffer */
366 rousseau 1106 memcpy(buffer + present, serialDevice[reader_index].buffer,
367 rousseau 453 length - present);
368 rousseau 1106 serialDevice[reader_index].buffer_offset = length - present;
369     serialDevice[reader_index].buffer_offset_last = rv;
370 rousseau 608 DEBUG_COMM3("offset: %d, last_offset: %d",
371 rousseau 1106 serialDevice[reader_index].buffer_offset,
372     serialDevice[reader_index].buffer_offset_last);
373 rousseau 414 }
374    
375 rousseau 453 return STATUS_SUCCESS;
376     } /* get_bytes */
377 rousseau 414
378 rousseau 453
379 rousseau 414 /*****************************************************************************
380     *
381     * ReadChunk: read a minimum number of bytes
382     *
383     *****************************************************************************/
384 rousseau 1106 static int ReadChunk(unsigned int reader_index, unsigned char *buffer,
385 rousseau 1050 int buffer_length, int min_length)
386 rousseau 414 {
387 rousseau 1106 int fd = serialDevice[reader_index].fd;
388 rousseau 1045 # ifndef S_SPLINT_S
389 rousseau 414 fd_set fdset;
390 rousseau 1045 # endif
391 rousseau 414 struct timeval t;
392 rousseau 462 int i, rv = 0;
393     int already_read;
394 rousseau 414 #ifdef DEBUG_LEVEL_COMM
395 rousseau 1047 char debug_header[] = "<- 123456 ";
396 rousseau 414
397 rousseau 1106 sprintf(debug_header, "<- %06X ", reader_index);
398 rousseau 414 #endif
399    
400 rousseau 462 already_read = 0;
401     while (already_read < min_length)
402     {
403     /* use select() to, eventually, timeout */
404     FD_ZERO(&fdset);
405     FD_SET(fd, &fdset);
406 rousseau 1147 t.tv_sec = SerialTimeout;
407 rousseau 462 t.tv_usec = 0;
408 rousseau 414
409 rousseau 462 i = select(fd+1, &fdset, NULL, NULL, &t);
410     if (i == -1)
411 rousseau 414 {
412 rousseau 462 DEBUG_CRITICAL2("select: %s", strerror(errno));
413 rousseau 414 return -1;
414     }
415 rousseau 462 else
416     if (i == 0)
417     {
418 rousseau 1147 DEBUG_COMM2("Timeout! (%d sec)", SerialTimeout);
419 rousseau 462 return -1;
420     }
421 rousseau 414
422 rousseau 462 rv = read(fd, buffer + already_read, buffer_length - already_read);
423     if (rv < 0)
424     {
425     DEBUG_COMM2("read error: %s", strerror(errno));
426     return -1;
427     }
428 rousseau 414
429 rousseau 453 #ifdef DEBUG_LEVEL_COMM
430 rousseau 462 DEBUG_XXD(debug_header, buffer + already_read, rv);
431 rousseau 453 #endif
432 rousseau 414
433 rousseau 462 already_read += rv;
434 rousseau 608 DEBUG_COMM3("read: %d, to read: %d", already_read,
435 rousseau 414 min_length);
436     }
437    
438 rousseau 462 return already_read;
439 rousseau 414 } /* ReadChunk */
440    
441    
442     /*****************************************************************************
443     *
444     * OpenSerial: open the port
445     *
446     *****************************************************************************/
447 rousseau 1106 status_t OpenSerial(unsigned int reader_index, int channel)
448 rousseau 414 {
449     char dev_name[FILENAME_MAX];
450    
451 rousseau 1106 DEBUG_COMM3("Reader index: %X, Channel: %d", reader_index, channel);
452 rousseau 414
453     /*
454     * Conversion of old-style ifd-hanler 1.0 CHANNELID
455     */
456     if (channel == 0x0103F8)
457     channel = 1;
458     else
459     if (channel == 0x0102F8)
460     channel = 2;
461     else
462     if (channel == 0x0103E8)
463     channel = 3;
464     else
465     if (channel == 0x0102E8)
466     channel = 4;
467    
468     if (channel < 0)
469     {
470     DEBUG_CRITICAL2("wrong port number: %d", (int) channel);
471     return STATUS_UNSUCCESSFUL;
472     }
473    
474     sprintf(dev_name, "/dev/pcsc/%d", (int) channel);
475    
476 rousseau 1106 return OpenSerialByName(reader_index, dev_name);
477 rousseau 649 } /* OpenSerial */
478    
479     /*****************************************************************************
480     *
481     * OpenSerialByName: open the port
482     *
483     *****************************************************************************/
484 rousseau 1106 status_t OpenSerialByName(unsigned int reader_index, char *dev_name)
485 rousseau 649 {
486     struct termios current_termios;
487     int i;
488 rousseau 1106 unsigned int reader = reader_index;
489 rousseau 649
490 rousseau 1292 DEBUG_COMM3("Reader index: %X, Device: %s", reader_index, dev_name);
491 rousseau 649
492 rousseau 414 /* check if the same channel is not already used */
493 rousseau 1077 for (i=0; i<CCID_DRIVER_MAX_READERS; i++)
494 rousseau 414 {
495 rousseau 649 if (serialDevice[i].device &&
496     strcmp(serialDevice[i].device, dev_name) == 0)
497 rousseau 414 {
498 rousseau 649 DEBUG_CRITICAL2("Device %s already in use", dev_name);
499 rousseau 414 return STATUS_UNSUCCESSFUL;
500     }
501     }
502    
503     serialDevice[reader].fd = open(dev_name, O_RDWR | O_NOCTTY);
504    
505 rousseau 892 if (-1 == serialDevice[reader].fd)
506 rousseau 414 {
507     DEBUG_CRITICAL3("open %s: %s", dev_name, strerror(errno));
508     return STATUS_UNSUCCESSFUL;
509     }
510    
511     /* set channel used */
512 rousseau 649 serialDevice[reader].device = strdup(dev_name);
513 rousseau 414
514     /* empty in and out serial buffers */
515     if (tcflush(serialDevice[reader].fd, TCIOFLUSH))
516     DEBUG_INFO2("tcflush() function error: %s", strerror(errno));
517    
518     /* get config attributes */
519     if (tcgetattr(serialDevice[reader].fd, &current_termios) == -1)
520     {
521     DEBUG_INFO2("tcgetattr() function error: %s", strerror(errno));
522     close(serialDevice[reader].fd);
523     serialDevice[reader].fd = -1;
524    
525     return STATUS_UNSUCCESSFUL;
526     }
527    
528     /* IGNBRK: ignore BREAK condition on input
529     * IGNPAR: ignore framing errors and parity errors. */
530     current_termios.c_iflag = IGNBRK | IGNPAR;
531     current_termios.c_oflag = 0; /* Raw output modes */
532     /* CS8: 8-bits character size
533     * CSTOPB: set two stop bits
534     * CREAD: enable receiver
535     * CLOCAL: ignore modem control lines */
536     current_termios.c_cflag = CS8 | CSTOPB | CREAD | CLOCAL;
537    
538     /* Do not echo characters because if you connect to a host it or your modem
539     * will echo characters for you. Don't generate signals. */
540     current_termios.c_lflag = 0;
541    
542     /* set serial port speed to 115200 bauds */
543     cfsetspeed(&current_termios, B115200);
544    
545     DEBUG_INFO("Set serial port baudrate to 115200 and correct configuration");
546     if (tcsetattr(serialDevice[reader].fd, TCSANOW, &current_termios) == -1)
547     {
548     close(serialDevice[reader].fd);
549     serialDevice[reader].fd = -1;
550     DEBUG_INFO2("tcsetattr error: %s", strerror(errno));
551    
552     return STATUS_UNSUCCESSFUL;
553     }
554    
555 rousseau 1149 serialDevice[reader].ccid.real_bSeq = 0;
556     serialDevice[reader].ccid.pbSeq = &serialDevice[reader].ccid.real_bSeq;
557 rousseau 414 serialDevice[reader].ccid.readerID = GEMPCTWIN;
558     serialDevice[reader].ccid.dwMaxCCIDMessageLength = 271;
559 rousseau 672 serialDevice[reader].ccid.dwMaxIFSD = 254;
560 rousseau 414 serialDevice[reader].ccid.dwFeatures = 0x00010230;
561 rousseau 900 serialDevice[reader].ccid.bPINSupport = 0x0;
562 rousseau 694 serialDevice[reader].ccid.dwDefaultClock = 4000;
563     serialDevice[reader].ccid.dwMaxDataRate = 344086;
564 rousseau 1091 serialDevice[reader].ccid.bMaxSlotIndex = 0;
565     serialDevice[reader].ccid.bCurrentSlotIndex = 0;
566 rousseau 414
567 rousseau 453 serialDevice[reader].buffer_offset = 0;
568     serialDevice[reader].buffer_offset_last = 0;
569    
570 rousseau 1022 /* perform a command to be sure a GemPC Twin reader is connected
571     * get the reader firmware */
572     {
573 rousseau 1330 unsigned char tx_buffer[] = { 0x02 };
574 rousseau 1048 unsigned char rx_buffer[50];
575     unsigned int rx_length = sizeof(rx_buffer);
576 rousseau 1022
577 rousseau 1147 /* 2 seconds timeout to not wait too long if no reader is connected */
578     SerialTimeout = 2;
579    
580 rousseau 1106 if (IFD_SUCCESS != CmdEscape(reader_index, tx_buffer, sizeof(tx_buffer),
581 rousseau 1022 rx_buffer, &rx_length))
582     {
583 rousseau 1147 DEBUG_CRITICAL("Get firmware failed. Maybe the reader is not connected");
584 rousseau 1022 return STATUS_UNSUCCESSFUL;
585     }
586    
587 rousseau 1147 /* normal timeout: 1 minute to allow long time APDU */
588     SerialTimeout = 60;
589    
590 rousseau 1022 rx_buffer[rx_length] = '\0';
591     DEBUG_INFO2("Firmware: %s", rx_buffer);
592     }
593    
594 rousseau 414 return STATUS_SUCCESS;
595 rousseau 649 } /* OpenSerialByName */
596 rousseau 414
597    
598     /*****************************************************************************
599     *
600     * CloseSerial: close the port
601     *
602     *****************************************************************************/
603 rousseau 1106 status_t CloseSerial(unsigned int reader_index)
604 rousseau 414 {
605 rousseau 1106 unsigned int reader = reader_index;
606 rousseau 414
607     close(serialDevice[reader].fd);
608     serialDevice[reader].fd = -1;
609    
610 rousseau 649 free(serialDevice[reader].device);
611     serialDevice[reader].device = NULL;
612    
613 rousseau 414 return STATUS_SUCCESS;
614     } /* CloseSerial */
615    
616    
617     /*****************************************************************************
618     *
619     * get_ccid_descriptor
620     *
621     ****************************************************************************/
622 rousseau 1106 _ccid_descriptor *get_ccid_descriptor(unsigned int reader_index)
623 rousseau 414 {
624 rousseau 1106 return &serialDevice[reader_index].ccid;
625 rousseau 414 } /* get_ccid_descriptor */
626    
627    

Properties

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

  ViewVC Help
Powered by ViewVC 1.1.5