/* * ccid_serial.c: communicate with a GemPC Twin smart card reader * Copyright (C) 2001-2003 Ludovic Rousseau * * Thanks to Niki W. Waibel for a prototype version * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * $Id$ */ #include #include #include #include #include #include #include #include #include #include "pcscdefines.h" #include "config.h" #include "debug.h" #include "ccid.h" #include "utils.h" /* communication timeout in seconds */ #define SERIAL_TIMEOUT 10 #define SYNC 0x03 #define CTRL_ACK 0x06 #define CTRL_NAK 0x15 #define RDR_to_PC_NotifySlotChange 0x50 #define CARD_ABSENT 0x02 #define CARD_PRESENT 0x03 /* * normal command: * 1 : SYNC * 1 : CTRL * 10 +data length : CCID command * 1 : LRC * * SYNC : 0x03 * CTRL : ACK (0x06) or NAK (0x15) * CCID command : see USB CCID specs * LRC : xor of all the previous byes * * Error message: * 1 : SYNC (0x03) * 1 : CTRL (NAK: 0x15) * 1 : LRC (0x16) * * Card insertion/withdrawal * 1 : SYNC * 1 : CTRL * 1 : RDR_to_PC_NotifySlotChange (0x50) * 1 : bmSlotIccState * 0x02 if card absent * 0x03 is card present * 1 : LRC * * Time request * T=1 : normal CCID command * T=0 : 1 byte (value between 0x80 and 0xFF) * */ typedef struct { /* * File handle on the serial port */ int fd; /* * channel used (1..4) */ int channel; /* * CCID infos common to USB and serial */ _ccid_descriptor ccid; } _serialDevice; /* The _serialDevice structure must be defined before including ccid_serial.h */ #include "ccid_serial.h" static _serialDevice serialDevice[PCSCLITE_MAX_READERS] = { [ 0 ... (PCSCLITE_MAX_READERS-1) ] = { -1, -1 } }; /* 271 = max size for short APDU * 2 bytes for header * 1 byte checksum * doubled for echo */ #define GEMPCTWIN_MAXBUF (271 +2 +1) *2 /***************************************************************************** * * WriteSerial: Send bytes to the card reader * *****************************************************************************/ status_t WriteSerial(int lun, int length, unsigned char *buffer) { int i; int reader = LunToReaderIndex(lun); unsigned char lrc; unsigned char low_level_buffer[GEMPCTWIN_MAXBUF]; #ifdef DEBUG_LEVEL_COMM char debug_header[] = "-> 121234 "; sprintf(debug_header, "-> %06X ", (int)lun); #endif if (length > GEMPCTWIN_MAXBUF-3) { DEBUG_CRITICAL3("WriteSerial: command too long: %d for max %d", length, GEMPCTWIN_MAXBUF-3); return STATUS_UNSUCCESSFUL; } /* header */ low_level_buffer[0] = 0x03; /* SYNC */ low_level_buffer[1] = 0x06; /* ACK */ /* CCID command */ memcpy(low_level_buffer+2, buffer, length); /* checksum */ lrc = 0; for(i=0; i= 0x80)) { DEBUG_COMM2("Time request: 0x%02X", buffer[0]); goto time_request; } /* too short */ if (rv < min_length) { #ifdef DEBUG_LEVEL_COMM DEBUG_XXD(debug_header, buffer, rv); #endif DEBUG_COMM3("ReadSerial: only %d byte(s) read, needed %d", rv, min_length); return -1; } return rv; } /* ReadChunk */ /***************************************************************************** * * OpenSerial: open the port * *****************************************************************************/ status_t OpenSerial(int lun, int channel) { char dev_name[FILENAME_MAX]; struct termios current_termios; int i; int reader = LunToReaderIndex(lun); DEBUG_COMM3("OpenSerial: Lun: %X, Channel: %d", lun, channel); /* * Conversion of old-style ifd-hanler 1.0 CHANNELID */ if (channel == 0x0103F8) channel = 1; else if (channel == 0x0102F8) channel = 2; else if (channel == 0x0103E8) channel = 3; else if (channel == 0x0102E8) channel = 4; if (channel < 0) { DEBUG_CRITICAL2("wrong port number: %d", (int) channel); return STATUS_UNSUCCESSFUL; } sprintf(dev_name, "/dev/pcsc/%d", (int) channel); /* check if the same channel is not already used */ for (i=0; i