/*
* Copyright (C) 2009 Frank Morgner
*
* This file is part of ccid.
*
* ccid 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 3 of the License, or (at your option) any later
* version.
*
* ccid 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
* ccid. If not, see .
*/
#include
#include
#include
#include
#ifndef USB_REQ_CCID
#define USB_REQ_CCID 0xA1
#define CCID_CONTROL_ABORT 0x01
#define CCID_CONTROL_GET_CLOCK_FREQUENCIES 0x02
#define CCID_CONTROL_GET_DATA_RATES 0x03
struct ccid_class_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__le16 bcdCCID;
__u8 bMaxSlotIndex;
__u8 bVoltageSupport;
__le32 dwProtocols;
__le32 dwDefaultClock;
__le32 dwMaximumClock;
__u8 bNumClockSupport;
__le32 dwDataRate;
__le32 dwMaxDataRate;
__u8 bNumDataRatesSupported;
__le32 dwMaxIFSD;
__le32 dwSynchProtocols;
__le32 dwMechanical;
__le32 dwFeatures;
__le32 dwMaxCCIDMessageLength;
__u8 bClassGetResponse;
__u8 bclassEnvelope;
__le16 wLcdLayout;
__u8 bPINSupport;
__u8 bMaxCCIDBusySlots;
} __attribute__ ((packed));
typedef struct {
__u8 bmFindexDindex;
__u8 bmTCCKST0;
__u8 bGuardTimeT0;
__u8 bWaitingIntegerT0;
__u8 bClockStop;
} __attribute__ ((packed)) abProtocolDataStructure_T0_t;
typedef struct {
__u8 bmFindexDindex;
__u8 bmTCCKST1;
__u8 bGuardTimeT1;
__u8 bWaitingIntegersT1;
__u8 bClockStop;
__u8 bIFSC;
__u8 bNadValue;
} __attribute__ ((packed)) abProtocolDataStructure_T1_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bBWI;
__le16 wLevelParameter;
} __attribute__ ((packed)) PC_to_RDR_XfrBlock_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 abRFU1;
__le16 abRFU2;
} __attribute__ ((packed)) PC_to_RDR_IccPowerOff_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 abRFU1;
__le16 abRFU2;
} __attribute__ ((packed)) PC_to_RDR_GetSlotStatus_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 abRFU1;
__le16 abRFU2;
} __attribute__ ((packed)) PC_to_RDR_GetParameters_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 abRFU1;
__le16 abRFU2;
} __attribute__ ((packed)) PC_to_RDR_ResetParameters_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bProtocolNum;
__le16 abRFU;
} __attribute__ ((packed)) PC_to_RDR_SetParameters_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bPowerSelect;
__le16 abRFU;
} __attribute__ ((packed)) PC_to_RDR_IccPowerOn_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bStatus;
__u8 bError;
__u8 bClockStatus;
} __attribute__ ((packed)) RDR_to_PC_SlotStatus_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bStatus;
__u8 bError;
__u8 bChainParameter;
} __attribute__ ((packed)) RDR_to_PC_DataBlock_t;
typedef struct {
__u8 bMessageType;
__le32 dwLength;
__u8 bSlot;
__u8 bSeq;
__u8 bStatus;
__u8 bError;
__u8 bProtocolNum;
} __attribute__ ((packed)) RDR_to_PC_Parameters_t;
typedef struct {
__u8 bMessageType;
__u8 bmSlotICCState;
} __attribute__ ((packed)) RDR_to_PC_NotifySlotChange_t;
#endif
struct hid_class_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__le16 bcdHID;
__le32 bCountryCode;
__u8 bNumDescriptors;
} __attribute__ ((packed));
static struct ccid_class_descriptor
ccid_desc = {
.bLength = sizeof ccid_desc,
.bDescriptorType = 0x21,
.bcdCCID = __constant_cpu_to_le16(0x0110),
.bMaxSlotIndex = 0,
.bVoltageSupport = 0x01,
.dwProtocols = __constant_cpu_to_le32(0x01| // T=0
0x02), // T=1
.dwDefaultClock = __constant_cpu_to_le32(0xDFC),
.dwMaximumClock = __constant_cpu_to_le32(0xDFC),
.bNumClockSupport = 1,
.dwDataRate = __constant_cpu_to_le32(0x2580),
.dwMaxDataRate = __constant_cpu_to_le32(0x2580),
.bNumDataRatesSupported = 1,
.dwMaxIFSD = __constant_cpu_to_le32(0xFF), // FIXME
.dwSynchProtocols = __constant_cpu_to_le32(0),
.dwMechanical = __constant_cpu_to_le32(0),
.dwFeatures = __constant_cpu_to_le32(0x2|
// Automatic parameter configuration based on ATR data
0x8| // Automatic ICC voltage selection
0x10| // Automatic ICC clock frequency change
0x20| // Automatic baud rate change
0x40| // Automatic parameters negotiation
0x80| // Automatic PPS
0x20000| // Short APDU level exchange
0x100000),// USB Wake up signaling supported
.dwMaxCCIDMessageLength = __constant_cpu_to_le32(261+10),
.bClassGetResponse = 0xFF,
.bclassEnvelope = 0xFF,
.wLcdLayout = __constant_cpu_to_le16(0),
.bPINSupport = 0,
.bMaxCCIDBusySlots = 1,
};
SCARDCONTEXT hcontext = 0;
SCARDHANDLE hcard = 0;
SCARD_READERSTATE rstate;
DWORD dwActiveProtocol;
char reader_name[MAX_READERNAME];
int reader_num;
char* perform_initialization(int num)
{
char *readers, *str;
DWORD size;
LONG r;
reader_num = num;
r = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hcontext);
if (r != SCARD_S_SUCCESS) {
fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(r));
SCardReleaseContext(hcontext);
return NULL;
}
r = SCardListReaders(hcontext, NULL, NULL, &size);
if (size == 0)
r = SCARD_E_UNKNOWN_READER;
if (r != SCARD_S_SUCCESS) {
fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(r));
SCardReleaseContext(hcontext);
return NULL;
}
/* get all readers */
readers = (char *) malloc(size);
if (readers == NULL) {
fprintf(stderr, "pc/sc error: %s\n",
pcsc_stringify_error(SCARD_E_NO_MEMORY));
SCardReleaseContext(hcontext);
return NULL;
}
r = SCardListReaders(hcontext, NULL, readers, &size);
if (r != SCARD_S_SUCCESS) {
free(readers);
fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(r));
SCardReleaseContext(hcontext);
return NULL;
}
/* name of reader number num */
str = readers;
for (size = 0; size < num; size++) {
/* go to the next name */
str += strlen(str) + 1;
/* no more readers available? */
if (strlen(str) == 0) {
free(readers);
fprintf(stderr, "pc/sc error: %s\n",
pcsc_stringify_error(SCARD_E_UNKNOWN_READER));
SCardReleaseContext(hcontext);
return NULL;
}
}
strncpy(reader_name, str, MAX_READERNAME);
free(readers);
rstate.dwCurrentState = SCARD_STATE_UNAWARE;
rstate.dwEventState = SCARD_STATE_UNAWARE;
rstate.szReader = reader_name;
return reader_name;
}
int perform_shutdown()
{
SCardDisconnect(hcard, SCARD_UNPOWER_CARD);
hcard = 0;
rstate.dwCurrentState = SCARD_STATE_UNAWARE;
rstate.dwEventState = SCARD_STATE_UNAWARE;
return SCardReleaseContext(hcontext);
}
__u8 get_bError(LONG pcsc_result)
{
switch (pcsc_result) {
case SCARD_S_SUCCESS : /**< No error was encountered. */
// Command not supported
return 0;
case SCARD_E_CANCELLED : /**< The action was cancelled by an SCardCancel request. */
fprintf(stderr, "CMD_ABORTED\n");
return 0xFF;
case SCARD_E_INVALID_HANDLE : /**< The supplied handle was invalid. */
case SCARD_E_NO_SMARTCARD : /**< The operation requires a Smart Card, but no Smart Card is currently in the device. */
case SCARD_E_UNKNOWN_CARD : /**< The specified smart card name is not recognized. */
case SCARD_E_NOT_READY : /**< The reader or smart card is not ready to accept commands. */
case SCARD_W_UNRESPONSIVE_CARD : /**< The smart card is not responding to a reset. */
case SCARD_W_UNPOWERED_CARD : /**< Power has been removed from the smart card, so that further communication is not possible. */
case SCARD_W_REMOVED_CARD : /**< The smart card has been removed, so further communication is not possible. */
fprintf(stderr, "ICC_MUTE\n");
return 0xFE;
case SCARD_E_SHARING_VIOLATION : /**< The smart card cannot be accessed because of other connections outstanding. */
fprintf(stderr, "CMD_SLOT_BUSY\n");
return 0xE0;
case SCARD_E_PROTO_MISMATCH : /**< The requested protocols are incompatible with the protocol currently in use with the smart card. */
fprintf(stderr, "ICC_PROTOCOL_NOT_SUPPORTED\n");
return 0xF6;
// case SCARD_F_INTERNAL_ERROR : /**< An internal consistency check failed. */
// case SCARD_E_INVALID_PARAMETER : /**< One or more of the supplied parameters could not be properly interpreted. */
// case SCARD_E_INVALID_TARGET : /**< Registry startup information is missing or invalid. */
// case SCARD_E_NO_MEMORY : /**< Not enough memory available to complete this command. */
// case SCARD_F_WAITED_TOO_LONG : /**< An internal consistency timer has expired. */
// case SCARD_E_INSUFFICIENT_BUFFER : /**< The data buffer to receive returned data is too small for the returned data. */
// case SCARD_E_UNKNOWN_READER : /**< The specified reader name is not recognized. */
// case SCARD_E_TIMEOUT : /**< The user-specified timeout value has expired. */
// case SCARD_E_CANT_DISPOSE : /**< The system could not dispose of the media in the requested manner. */
// case SCARD_E_INVALID_VALUE : /**< One or more of the supplied parameters values could not be properly interpreted. */
// case SCARD_E_SYSTEM_CANCELLED : /**< The action was cancelled by the system, presumably to log off or shut down. */
// case SCARD_F_COMM_ERROR : /**< An internal communications error has been detected. */
// case SCARD_F_UNKNOWN_ERROR : /**< An internal error has been detected, but the source is unknown. */
// case SCARD_E_INVALID_ATR : /**< An ATR obtained from the registry is not a valid ATR string. */
// case SCARD_E_NOT_TRANSACTED : /**< An attempt was made to end a non-existent transaction. */
// case SCARD_E_READER_UNAVAILABLE : /**< The specified reader is not currently available for use. */
// case SCARD_W_UNSUPPORTED_CARD : /**< The reader cannot communicate with the card, due to ATR string configuration conflicts. */
// case SCARD_W_RESET_CARD : /**< The smart card has been reset, so any shared state information is invalid. */
// case SCARD_E_PCI_TOO_SMALL : /**< The PCI Receive buffer was too small. */
// case SCARD_E_READER_UNSUPPORTED : /**< The reader driver does not meet minimal requirements for support. */
// case SCARD_E_DUPLICATE_READER : /**< The reader driver did not produce a unique reader name. */
// case SCARD_E_CARD_UNSUPPORTED : /**< The smart card does not meet minimal requirements for support. */
// case SCARD_E_NO_SERVICE : /**< The Smart card resource manager is not running. */
// case SCARD_E_SERVICE_STOPPED : /**< The Smart card resource manager has shut down. */
// case SCARD_E_NO_READERS_AVAILABLE : /**< Cannot find a smart card reader. */
default:
fprintf(stderr, "HW_ERROR\n");
return 0xFB;
}
}
__u8 get_bStatus(LONG pcsc_result)
{
__u8 bStatus = 0;
if (rstate.dwEventState & SCARD_STATE_PRESENT) {
if (rstate.dwEventState & SCARD_STATE_MUTE ||
rstate.dwEventState & SCARD_STATE_UNPOWERED) {
// inactive
fprintf(stderr, "card inactive\n");
bStatus = 1;
} else {
// active
fprintf(stderr, "card active\n");
bStatus = 0;
}
} else {
// absent
fprintf(stderr, "card absent\n");
bStatus = 2;
if (hcard != 0) {
pcsc_result = SCardDisconnect(hcard, SCARD_UNPOWER_CARD);
hcard = 0;
}
}
if (pcsc_result != SCARD_S_SUCCESS) {
bStatus |= (1<<6);
fprintf(stderr, "pc/sc error: %s\n", pcsc_stringify_error(pcsc_result));
}
return bStatus;
}
RDR_to_PC_SlotStatus_t get_RDR_to_PC_SlotStatus(__u8 bSlot, __u8 bSeq,
LONG pcsc_result)
{
RDR_to_PC_SlotStatus_t result;
result.bMessageType = 0x81;
result.dwLength = __constant_cpu_to_le32(0);
result.bSlot = bSlot;
result.bSeq = bSeq;
result.bStatus = get_bStatus(pcsc_result);
result.bError = get_bError(pcsc_result);
result.bClockStatus = 0;
return result;
}
RDR_to_PC_DataBlock_t get_RDR_to_PC_DataBlock(__u8 bSlot, __u8 bSeq,
LONG pcsc_result, __le32 dwLength)
{
RDR_to_PC_DataBlock_t result;
result.bMessageType = 0x80;
result.dwLength = dwLength;
result.bSlot = bSlot;
result.bSeq = bSeq;
result.bStatus = get_bStatus(pcsc_result);
result.bError = get_bError(pcsc_result);
result.bChainParameter = 0;
return result;
}
RDR_to_PC_SlotStatus_t
perform_PC_to_RDR_GetSlotStatus(const PC_to_RDR_GetSlotStatus_t request)
{
if ( request.bMessageType != 0x65 ||
request.dwLength != __constant_cpu_to_le32(0) ||
request.bSlot != 0 ||
request.abRFU1 != 0 ||
request.abRFU2 != 0)
fprintf(stderr, "warning: malformed PC_to_RDR_GetSlotStatus\n");
return get_RDR_to_PC_SlotStatus(request.bSlot, request.bSeq,
SCardGetStatusChange(hcontext, 1, &rstate, 1));
}
RDR_to_PC_SlotStatus_t
perform_PC_to_RDR_IccPowerOn(const PC_to_RDR_IccPowerOn_t request, char ** pATR)
{
if ( request.bMessageType != 0x62 ||
request.dwLength != __constant_cpu_to_le32(0) ||
request.bSlot != 0 ||
!( request.bPowerSelect == 0 ||
request.bPowerSelect & ccid_desc.bVoltageSupport ) ||
request.abRFU != 0)
fprintf(stderr, "warning: malformed PC_to_RDR_IccPowerOn\n");
LONG pcsc_result;
if (hcard) {
pcsc_result = SCardReconnect(hcard, SCARD_SHARE_EXCLUSIVE,
SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1, SCARD_LEAVE_CARD,
&dwActiveProtocol);
} else {
pcsc_result = SCardConnect(hcontext, reader_name,
SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1,
&hcard, &dwActiveProtocol);
}
if (pcsc_result == SCARD_S_SUCCESS)
pcsc_result = SCardGetStatusChange(hcontext, 1, &rstate, 1);
RDR_to_PC_SlotStatus_t result = get_RDR_to_PC_SlotStatus(request.bSlot,
request.bSeq, pcsc_result);
if (pcsc_result != SCARD_S_SUCCESS) {
*pATR = NULL;
result.dwLength = __constant_cpu_to_le32(0);
} else {
*pATR = (char*) rstate.rgbAtr;
result.dwLength = __cpu_to_le32(rstate.cbAtr);
}
return result;
}
RDR_to_PC_SlotStatus_t
perform_PC_to_RDR_IccPowerOff(const PC_to_RDR_IccPowerOff_t request)
{
if ( request.bMessageType != 0x63 ||
request.dwLength != __constant_cpu_to_le32(0) ||
request.bSlot != 0 ||
request.abRFU1 != 0 ||
request.abRFU2 != 0)
fprintf(stderr, "warning: malformed PC_to_RDR_IccPowerOff\n");
LONG result = SCardDisconnect(hcard, SCARD_UNPOWER_CARD);
hcard = 0;
if (result == SCARD_E_INVALID_HANDLE) {
result = SCardGetStatusChange(hcontext, 1, &rstate, 1);
}
return get_RDR_to_PC_SlotStatus(request.bSlot, request.bSeq, result);
}
RDR_to_PC_DataBlock_t
perform_PC_to_RDR_XfrBlock(const PC_to_RDR_XfrBlock_t request, const __u8*
abDataIn, __u8** abDataOut)
{
if ( request.bMessageType != 0x6F ||
request.bSlot != 0 ||
request.bBWI != 0)
fprintf(stderr, "warning: malformed PC_to_RDR_XfrBlock\n");
DWORD dwRecvLength = MAX_BUFFER_SIZE;
*abDataOut = (__u8 *) malloc(dwRecvLength);
if (*abDataOut == NULL) {
return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
SCARD_E_NO_MEMORY, __constant_cpu_to_le32(0));
}
LPCSCARD_IO_REQUEST pioSendPci;
if (dwActiveProtocol == SCARD_PROTOCOL_T0)
pioSendPci = SCARD_PCI_T0;
else
pioSendPci = SCARD_PCI_T1;
int pcsc_result = SCardTransmit(hcard, pioSendPci, abDataIn,
__le32_to_cpu(request.dwLength), NULL, *abDataOut, &dwRecvLength);
return get_RDR_to_PC_DataBlock(request.bSlot, request.bSeq,
pcsc_result, __cpu_to_le32(dwRecvLength));
}
RDR_to_PC_Parameters_t
get_RDR_to_PC_Parameters(__u8 bSlot, __u8 bSeq, LONG pcsc_result, __u8
**abProtocolDataStructure)
{
RDR_to_PC_Parameters_t result;
result.bMessageType = 0x82;
result.bSlot = bSlot;
result.bSeq = bSeq;
if (pcsc_result == SCARD_S_SUCCESS) {
if (dwActiveProtocol == SCARD_PROTOCOL_T0) {
result.bProtocolNum = 0;
*abProtocolDataStructure = (__u8 *) malloc(sizeof
(abProtocolDataStructure_T0_t));
if (*abProtocolDataStructure) {
fprintf (stderr, "T0\n");
result.dwLength = __constant_cpu_to_le32(sizeof
(abProtocolDataStructure_T0_t));
abProtocolDataStructure_T0_t * t0 =
*(abProtocolDataStructure_T0_t**) abProtocolDataStructure;
/* values taken from ISO 7816-3 defaults
* FIXME analyze ATR to get values */
t0->bmFindexDindex = 1<<4| // index to table 7 ISO 7816-3 (Fi)
1; // index to table 8 ISO 7816-3 (Di)
t0->bmTCCKST0 = 0<<1; // convention (direct)
t0->bGuardTimeT0 = 0xFF;
t0->bWaitingIntegerT0 = 0x10;
t0->bClockStop = 0; // (not allowed)
} else {
// error malloc
result.dwLength = __constant_cpu_to_le32(0);
*abProtocolDataStructure = NULL;
pcsc_result = SCARD_E_INSUFFICIENT_BUFFER;
}
} else {
result.bProtocolNum = 1;
*abProtocolDataStructure = (__u8 *) malloc(sizeof
(abProtocolDataStructure_T1_t));
if (*abProtocolDataStructure) {
fprintf (stderr, "T1\n");
result.dwLength = __constant_cpu_to_le32(sizeof
(abProtocolDataStructure_T1_t));
abProtocolDataStructure_T1_t * t1 =
*(abProtocolDataStructure_T1_t**) abProtocolDataStructure;
/* values taken from OpenPGP-card
* FIXME analyze ATR to get values */
t1->bmFindexDindex = 1<<4| // index to table 7 ISO 7816-3 (Fi)
3; // index to table 8 ISO 7816-3 (Di)
t1->bmTCCKST1 = 0| // checksum type (CRC)
0<<1| // convention (direct)
0x10;
t1->bGuardTimeT1 = 0xFF;
t1->bWaitingIntegersT1 = 4<<4| // BWI
5; // CWI
t1->bClockStop = 0; // (not allowed)
t1->bIFSC = 0x80;
t1->bNadValue = 0; // see 7816-3 9.4.2.1 (only default value)
} else {
// error malloc
result.dwLength = __constant_cpu_to_le32(0);
*abProtocolDataStructure = NULL;
pcsc_result = SCARD_E_INSUFFICIENT_BUFFER;
}
}
} else {
result.dwLength = __constant_cpu_to_le32(0);
*abProtocolDataStructure = NULL;
}
result.bStatus = get_bStatus(pcsc_result);
result.bError = get_bError(pcsc_result);
return result;
}
RDR_to_PC_Parameters_t
perform_PC_to_RDR_GetParamters(const PC_to_RDR_GetParameters_t request,
__u8** abProtocolDataStructure)
{
if ( request.bMessageType != 0x6C ||
request.dwLength != __constant_cpu_to_le32(0) ||
request.bSlot != 0)
fprintf(stderr, "warning: malformed PC_to_RDR_GetParamters\n");
LONG pcsc_result = SCardReconnect(hcard, SCARD_SHARE_EXCLUSIVE,
SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1, SCARD_LEAVE_CARD,
&dwActiveProtocol);
return get_RDR_to_PC_Parameters(request.bSlot, request.bSeq,
pcsc_result, abProtocolDataStructure);
}
RDR_to_PC_NotifySlotChange_t
get_RDR_to_PC_NotifySlotChange ()
{
RDR_to_PC_NotifySlotChange_t result;
result.bMessageType = 0x50;
result.bmSlotICCState = 0; // no change
DWORD current = rstate.dwEventState;
if (SCARD_S_SUCCESS != SCardGetStatusChange(hcontext, 1, &rstate, 1)) {
fprintf(stderr, "state changed: error\n");
result.bmSlotICCState = 2; // changed (error)
} else if (!(current & rstate.dwEventState)) {
fprintf(stderr, "state changed\n");
result.bmSlotICCState = 2; // changed
}
return result;
}
RDR_to_PC_SlotStatus_t
perform_unknown(const PC_to_RDR_GetSlotStatus_t request)
{
RDR_to_PC_SlotStatus_t result;
switch (request.bMessageType) {
case 0x62:
case 0x6F:
case 0x69:
result.bMessageType = 0x80;
break;
case 0x63:
case 0x65:
case 0x6E:
case 0x6A:
case 0x71:
case 0x72:
result.bMessageType = 0x81;
break;
case 0x61:
case 0x6C:
case 0x6D:
result.bMessageType = 0x82;
break;
case 0x6B:
result.bMessageType = 0x83;
break;
case 0x73:
result.bMessageType = 0x84;
break;
default:
fprintf(stderr, "unknown message type\n");
result.bMessageType = 0;
}
result.dwLength = __constant_cpu_to_le32(0);
result.bSlot = request.bSlot,
result.bSeq = request.bSeq;
result.bStatus = get_bStatus(SCARD_F_UNKNOWN_ERROR);
result.bError = 0;
result.bClockStatus = 0;
return result;
}
int parse_ccid(const __u8* inbuf, __u8** outbuf) {
if (inbuf == NULL)
return 0;
int result = -1;
if (SCardIsValidContext(hcontext) != SCARD_S_SUCCESS) {
if (perform_initialization(reader_num) == NULL)
goto error;
}
switch (*inbuf) {
case 0x62:
{ fprintf(stderr, "PC_to_RDR_IccPowerOn\n");
char* atr;
PC_to_RDR_IccPowerOn_t input =
*(PC_to_RDR_IccPowerOn_t*) inbuf;
RDR_to_PC_SlotStatus_t output =
perform_PC_to_RDR_IccPowerOn(input, &atr);
result = sizeof output + __le32_to_cpu(output.dwLength);
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
memcpy(*outbuf + sizeof output, atr,
__le32_to_cpu(output.dwLength));
} break;
case 0x63:
{ fprintf(stderr, "PC_to_RDR_IccPowerOff\n");
PC_to_RDR_IccPowerOff_t input =
*(PC_to_RDR_IccPowerOff_t*) inbuf;
RDR_to_PC_SlotStatus_t output =
perform_PC_to_RDR_IccPowerOff(input);
result = sizeof output;
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
} break;
case 0x65:
{ fprintf(stderr, "PC_to_RDR_GetSlotStatus\n");
PC_to_RDR_GetSlotStatus_t input =
*(PC_to_RDR_GetSlotStatus_t*) inbuf;
RDR_to_PC_SlotStatus_t output =
perform_PC_to_RDR_GetSlotStatus(input);
result = sizeof output;
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
} break;
case 0x6F:
{ fprintf(stderr, "PC_to_RDR_XfrBlock\n");
__u8* rapdu;
PC_to_RDR_XfrBlock_t input = *(PC_to_RDR_XfrBlock_t*) inbuf;
RDR_to_PC_DataBlock_t output =
perform_PC_to_RDR_XfrBlock(input, inbuf + sizeof input,
&rapdu);
result = sizeof output + __le32_to_cpu(output.dwLength);
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
free(rapdu);
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
memcpy(*outbuf + sizeof output, rapdu,
__le32_to_cpu(output.dwLength));
free(rapdu);
} break;
case 0x6C:
{ fprintf(stderr, "PC_to_RDR_GetParameters\n");
__u8* abProtocolDataStructure;
PC_to_RDR_GetParameters_t input = *(PC_to_RDR_GetParameters_t*)
inbuf;
RDR_to_PC_Parameters_t output = perform_PC_to_RDR_GetParamters(
input, &abProtocolDataStructure );
result = sizeof output + __le32_to_cpu(output.dwLength);
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
if (abProtocolDataStructure)
free(abProtocolDataStructure);
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
memcpy(*outbuf + sizeof output, abProtocolDataStructure,
__le32_to_cpu(output.dwLength));
if (abProtocolDataStructure)
free(abProtocolDataStructure);
} break;
default:
error:
{ fprintf(stderr, "unknown ccid command: 0x%4X\n", *inbuf);
PC_to_RDR_GetSlotStatus_t input =
*(PC_to_RDR_GetSlotStatus_t*) inbuf;
RDR_to_PC_SlotStatus_t output =
perform_unknown(input);
result = sizeof output;
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
memcpy(*outbuf, &output, sizeof output);
}
}
return result;
}
int parse_ccid_control(struct usb_ctrlrequest *setup, __u8 **outbuf) {
int result = -1;
__u16 value, index, length;
value = __le16_to_cpu(setup->wValue);
index = __le16_to_cpu(setup->wIndex);
length = __le16_to_cpu(setup->wLength);
if (setup->bRequestType == USB_REQ_CCID)
switch(setup->bRequest) {
case CCID_CONTROL_ABORT:
{
fprintf(stderr, "ABORT\n");
if (length != 0x00) {
fprintf(stderr, "warning: malformed ABORT\n");
}
result = SCardCancel(hcontext);
if (result != SCARD_S_SUCCESS)
fprintf(stderr, "pc/sc error: %s\n",
pcsc_stringify_error(result));
result = 0;
} break;
case CCID_CONTROL_GET_CLOCK_FREQUENCIES:
{
fprintf(stderr, "GET_CLOCK_FREQUENCIES\n");
if (value != 0x00) {
fprintf(stderr,
"warning: malformed GET_CLOCK_FREQUENCIES\n");
}
result = sizeof(__le32);
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
__le32 clock = ccid_desc.dwDefaultClock;
memcpy(*outbuf, &clock, sizeof (__le32));
} break;
case CCID_CONTROL_GET_DATA_RATES:
{
fprintf(stderr, "GET_DATA_RATES\n");
if (value != 0x00) {
fprintf(stderr, "warning: malformed GET_DATA_RATES\n");
}
result = sizeof (__le32);
*outbuf = realloc(*outbuf, result);
if (*outbuf == NULL) {
result = -1;
break;
}
__le32 drate = ccid_desc.dwDataRate;
memcpy(*outbuf, &drate, sizeof (__le32));
} break;
}
return result;
}