#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include "misc.h"
#include "config.h"
#include <pcsclite.h>
#include <ifdhandler.h>
#include <reader.h>
#include "ccid.h"
#include "defs.h"
#include "ccid_ifdhandler.h"
#include "debug.h"
#include "utils.h"
#include "commands.h"
#include "towitoko/atr.h"
#include "towitoko/pps.h"
#include "parser.h"
#ifdef HAVE_PTHREAD
#include <pthread.h>
#endif
static CcidDesc CcidSlots[CCID_DRIVER_MAX_READERS];
#ifdef HAVE_PTHREAD
static pthread_mutex_t ifdh_context_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
int LogLevel = DEBUG_LEVEL_CRITICAL | DEBUG_LEVEL_INFO;
int DriverOptions = 0;
int PowerOnVoltage = VOLTAGE_5V;
static int DebugInitialized = FALSE;
#if HAVE_DECL_TAG_IFD_POLLING_THREAD && !defined(TWIN_SERIAL) && defined(USE_USB_INTERRUPT)
static RESPONSECODE IFDHPolling(DWORD Lun);
static RESPONSECODE IFDHSleep(DWORD Lun);
#endif
static void init_driver(void);
static void extra_egt(ATR_t *atr, _ccid_descriptor *ccid_desc, DWORD Protocol);
static char find_baud_rate(unsigned int baudrate, unsigned int *list);
static unsigned int T0_card_timeout(double f, double d, int TC1, int TC2,
int clock_frequency);
static unsigned int T1_card_timeout(double f, double d, int TC1, int BWI,
int CWI, int clock_frequency);
EXTERNAL RESPONSECODE IFDHCreateChannelByName(DWORD Lun, LPSTR lpcDevice)
{
RESPONSECODE return_value = IFD_SUCCESS;
int reader_index;
status_t ret;
if (! DebugInitialized)
init_driver();
DEBUG_INFO3("lun: %X, device: %s", Lun, lpcDevice);
if (-1 == (reader_index = GetNewReaderIndex(Lun)))
return IFD_COMMUNICATION_ERROR;
CcidSlots[reader_index].nATRLength = 0;
*CcidSlots[reader_index].pcATRBuffer = '\0';
CcidSlots[reader_index].bPowerFlags = POWERFLAGS_RAZ;
CcidSlots[reader_index].readerName = strdup(lpcDevice);
#ifdef HAVE_PTHREAD
(void)pthread_mutex_lock(&ifdh_context_mutex);
#endif
ret = OpenPortByName(reader_index, lpcDevice);
if (ret != STATUS_SUCCESS)
{
DEBUG_CRITICAL("failed");
if (STATUS_NO_SUCH_DEVICE == ret)
return_value = IFD_NO_SUCH_DEVICE;
else
return_value = IFD_COMMUNICATION_ERROR;
ReleaseReaderIndex(reader_index);
}
else
{
(void)ccid_open_hack_pre(reader_index);
if ((IFD_COMMUNICATION_ERROR == IFDHICCPresence(Lun))
&& (IFD_COMMUNICATION_ERROR == IFDHICCPresence(Lun))
&& (IFD_COMMUNICATION_ERROR == IFDHICCPresence(Lun)))
{
DEBUG_CRITICAL("failed");
return_value = IFD_COMMUNICATION_ERROR;
(void)ClosePort(reader_index);
ReleaseReaderIndex(reader_index);
}
else
(void)ccid_open_hack_post(reader_index);
}
#ifdef HAVE_PTHREAD
(void)pthread_mutex_unlock(&ifdh_context_mutex);
#endif
return return_value;
}
EXTERNAL RESPONSECODE IFDHCreateChannel(DWORD Lun, DWORD Channel)
{
RESPONSECODE return_value = IFD_SUCCESS;
int reader_index;
if (! DebugInitialized)
init_driver();
DEBUG_INFO2("lun: %X", Lun);
if (-1 == (reader_index = GetNewReaderIndex(Lun)))
return IFD_COMMUNICATION_ERROR;
CcidSlots[reader_index].nATRLength = 0;
*CcidSlots[reader_index].pcATRBuffer = '\0';
CcidSlots[reader_index].bPowerFlags = POWERFLAGS_RAZ;
CcidSlots[reader_index].readerName = strdup("no name");
#ifdef HAVE_PTHREAD
(void)pthread_mutex_lock(&ifdh_context_mutex);
#endif
if (OpenPort(reader_index, Channel) != STATUS_SUCCESS)
{
DEBUG_CRITICAL("failed");
return_value = IFD_COMMUNICATION_ERROR;
ReleaseReaderIndex(reader_index);
}
else
{
(void)ccid_open_hack_pre(reader_index);
if ((IFD_COMMUNICATION_ERROR == IFDHICCPresence(Lun))
&& (IFD_COMMUNICATION_ERROR == IFDHICCPresence(Lun))
&& (IFD_COMMUNICATION_ERROR == IFDHICCPresence(Lun)))
{
DEBUG_CRITICAL("failed");
return_value = IFD_COMMUNICATION_ERROR;
(void)ClosePort(reader_index);
ReleaseReaderIndex(reader_index);
}
else
(void)ccid_open_hack_post(reader_index);
}
#ifdef HAVE_PTHREAD
(void)pthread_mutex_unlock(&ifdh_context_mutex);
#endif
return return_value;
}
EXTERNAL RESPONSECODE IFDHCloseChannel(DWORD Lun)
{
int reader_index;
if (-1 == (reader_index = LunToReaderIndex(Lun)))
return IFD_COMMUNICATION_ERROR;
DEBUG_INFO3("%s (lun: %X)", CcidSlots[reader_index].readerName, Lun);
get_ccid_descriptor(reader_index)->readTimeout = DEFAULT_COM_READ_TIMEOUT;
(void)CmdPowerOff(reader_index);
#ifdef HAVE_PTHREAD
(void)pthread_mutex_lock(&ifdh_context_mutex);
#endif
(void)ClosePort(reader_index);
ReleaseReaderIndex(reader_index);
free(CcidSlots[reader_index].readerName);
memset(&CcidSlots[reader_index], 0, sizeof(CcidSlots[reader_index]));
#ifdef HAVE_PTHREAD
(void)pthread_mutex_unlock(&ifdh_context_mutex);
#endif
return IFD_SUCCESS;
}
#if HAVE_DECL_TAG_IFD_POLLING_THREAD && !defined(TWIN_SERIAL) && defined(USE_USB_INTERRUPT)
static RESPONSECODE IFDHPolling(DWORD Lun)
{
int reader_index;
int ret;
if (-1 == (reader_index = LunToReaderIndex(Lun)))
return IFD_COMMUNICATION_ERROR;
if (LogLevel & DEBUG_LEVEL_PERIODIC)
DEBUG_INFO3("%s (lun: %X)", CcidSlots[reader_index].readerName, Lun);
ret = InterruptRead(reader_index, 2*1000);
if (ret > 0)
return IFD_SUCCESS;
if (0 == ret)
return IFD_NO_SUCH_DEVICE;
return IFD_COMMUNICATION_ERROR;
}
static RESPONSECODE IFDHSleep(DWORD Lun)
{
int reader_index;
if (-1 == (reader_index = LunToReaderIndex(Lun)))
return IFD_COMMUNICATION_ERROR;
DEBUG_INFO3("%s (lun: %X)", CcidSlots[reader_index].readerName, Lun);
(void)sleep(600);
return IFD_SUCCESS;
}
#endif
EXTERNAL RESPONSECODE IFDHGetCapabilities(DWORD Lun, DWORD Tag,
PDWORD Length, PUCHAR Value)
{
int reader_index;
if (-1 == (reader_index = LunToReaderIndex(Lun)))
return IFD_COMMUNICATION_ERROR;
DEBUG_INFO4("tag: 0x%X, %s (lun: %X)", Tag,
CcidSlots[reader_index].readerName, Lun);
switch (Tag)
{
case TAG_IFD_ATR:
case SCARD_ATTR_ATR_STRING:
*Length = (*Length < CcidSlots[reader_index].nATRLength) ?
*Length : CcidSlots[reader_index].nATRLength;
if (*Length)
memcpy(Value, CcidSlots[reader_index].pcATRBuffer, *Length);
break;
#ifdef HAVE_PTHREAD
case TAG_IFD_SIMULTANEOUS_ACCESS:
if (*Length >= 1)
{
*Length = 1;
*Value = CCID_DRIVER_MAX_READERS;
}
break;
case TAG_IFD_THREAD_SAFE:
if (*Length >= 1)
{
*Length = 1;
#ifdef __APPLE__
*Value = 0;
#else
*Value = 1;
#endif
}
break;
#endif
case TAG_IFD_SLOTS_NUMBER:
if (*Length >= 1)
{
*Length = 1;
*Value = 1 + get_ccid_descriptor(reader_index) -> bMaxSlotIndex;
#ifdef USE_COMPOSITE_AS_MULTISLOT
{
int readerID = get_ccid_descriptor(reader_index) -> readerID;
if ((GEMALTOPROXDU == readerID) || (GEMALTOPROXSU == readerID))
*Value = 2;
}
#endif
DEBUG_INFO2("Reader supports %d slot(s)", *Value);
}
break;
case TAG_IFD_SLOT_THREAD_SAFE:
if (*Length >= 1)
{
*Length = 1;
*Value = 0;
}
break;
case SCARD_ATTR_VENDOR_IFD_VERSION:
*Length = sizeof(DWORD);
if (Value)
*(DWORD *)Value = CCID_VERSION;
break;
case SCARD_ATTR_VENDOR_NAME:
#define VENDOR_NAME "Ludovic Rousseau"
*Length = sizeof(VENDOR_NAME);
if (Value)
memcpy(Value, VENDOR_NAME, sizeof(VENDOR_NAME));
break;
case SCARD_ATTR_MAXINPUT:
*Length = sizeof(uint32_t);
if (Value)
*(uint32_t *)Value = get_ccid_descriptor(reader_index) -> dwMaxCCIDMessageLength -10;
break;
#if HAVE_DECL_TAG_IFD_POLLING_THREAD && !defined(TWIN_SERIAL) && defined(USE_USB_INTERRUPT)
case TAG_IFD_POLLING_THREAD:
{
_ccid_descriptor *ccid_desc;
*Length = 0;
ccid_desc = get_ccid_descriptor(reader_index);
if ((0 == ccid_desc -> bInterfaceProtocol)
&& (3 == ccid_desc -> bNumEndpoints))
{
*Length = sizeof(void *);
if (Value)
*(void **)Value = IFDHPolling;
}
if ((ICCD_A == ccid_desc->bInterfaceProtocol)
|| (ICCD_B == ccid_desc->bInterfaceProtocol))
{
*Length = sizeof(void *);
if (Value)
*(void **)Value = IFDHSleep;
}
}
break;
case TAG_IFD_POLLING_THREAD_KILLABLE:
{
_ccid_descriptor *ccid_desc;
*Length = 0;
ccid_desc = get_ccid_descriptor(reader_index);
if ((ICCD_A == ccid_desc->bInterfaceProtocol)
|| (ICCD_B == ccid_desc->bInterfaceProtocol))
{
*Length = 1;
if (Value)
*Value = 1;
}
}
break;
#endif
default:
return IFD_ERROR_TAG;
}
return IFD_SUCCESS;
}
EXTERNAL RESPONSECODE IFDHSetCapabilities(DWORD Lun, DWORD Tag,
DWORD Length, PUCHAR Value)
{
int reader_index;
if (-1 == (reader_index = LunToReaderIndex(Lun)))
return IFD_COMMUNICATION_ERROR;
DEBUG_INFO4("tag: 0x%X, %s (lun: %X)", Tag,
CcidSlots[reader_index].readerName, Lun);
return IFD_NOT_SUPPORTED;
}
EXTERNAL RESPONSECODE IFDHSetProtocolParameters(DWORD Lun, DWORD Protocol,
UCHAR Flags, UCHAR PTS1, UCHAR PTS2, UCHAR PTS3)
{
BYTE pps[PPS_MAX_LENGTH];
ATR_t atr;
unsigned int len;
int convention;
int reader_index;
CcidDesc *ccid_slot;
_ccid_descriptor *ccid_desc;
if (-1 == (reader_index = LunToReaderIndex(Lun)))
return IFD_COMMUNICATION_ERROR;
DEBUG_INFO4("protocol T=%d, %s (lun: %X)", Protocol-SCARD_PROTOCOL_T0,
CcidSlots[reader_index].readerName, Lun);
memset(pps, 0, sizeof(pps));
memset(&atr, 0, sizeof(atr));
ccid_slot = get_ccid_slot(reader_index);
ccid_desc = get_ccid_descriptor(reader_index);
if (ccid_desc->dwFeatures & CCID_CLASS_AUTO_PPS_PROP)
goto end;
(void)ATR_InitFromArray(&atr, ccid_slot->pcATRBuffer,
ccid_slot->nATRLength);
extra_egt(&atr, ccid_desc, Protocol);
if (SCARD_PROTOCOL_T0 == Protocol)
pps[1] |= ATR_PROTOCOL_TYPE_T0;
else
if (SCARD_PROTOCOL_T1 == Protocol)
pps[1] |= ATR_PROTOCOL_TYPE_T1;
else
return IFD_PROTOCOL_NOT_SUPPORTED;
if (atr.ib[1][ATR_INTERFACE_BYTE_TA].present)
{
if (pps[1] != (atr.ib[1][ATR_INTERFACE_BYTE_TA].value & 0x0F))
{
DEBUG_COMM3("Specific mode in T=%d and T=%d requested",
atr.ib[1][ATR_INTERFACE_BYTE_TA].value & 0x0F, pps[1]);
return IFD_PROTOCOL_NOT_SUPPORTED;
}
}
if (SCARD_PROTOCOL_T1 == Protocol)
{
t1_state_t *t1 = &(ccid_slot -> t1);
int i;
for (i=2; i<ATR_MAX_PROTOCOLS; i++)
if (atr.ib[i][ATR_INTERFACE_BYTE_TC].present)
{
if (0 == atr.ib[i][ATR_INTERFACE_BYTE_TC].value)
{
DEBUG_COMM("Use LRC");
(void)t1_set_param(t1, IFD_PROTOCOL_T1_CHECKSUM_LRC, 0);
}
else
if (1 == atr.ib[i][ATR_INTERFACE_BYTE_TC].value)
{
DEBUG_COMM("Use CRC");
(void)t1_set_param(t1, IFD_PROTOCOL_T1_CHECKSUM_CRC, 0);
}
else
DEBUG_COMM2("Wrong value for TCi: %d",
atr.ib[i][ATR_INTERFACE_BYTE_TC].value);
break;
}
}
if (Flags & IFD_NEGOTIATE_PTS1)
{
pps[1] |= 0x10;
pps[2] = PTS1;
}
else
{
if (atr.ib[0][ATR_INTERFACE_BYTE_TA].present)
{
unsigned int card_baudrate;
unsigned int default_baudrate;
double f, d;
(void)ATR_GetParameter(&atr, ATR_PARAMETER_D, &d);
(void)ATR_GetParameter(&atr, ATR_PARAMETER_F, &f);
if ((0 == f) || (0 == d))
{
f = 372;
d = 1;
}
card_baudrate = (unsigned int) (1000 * ccid_desc->dwDefaultClock
* d / f);
default_baudrate = (unsigned int) (1000 * ccid_desc->dwDefaultClock
* ATR_DEFAULT_D / ATR_DEFAULT_F);
if ((card_baudrate > default_baudrate)
&& (card_baudrate <= ccid_desc->dwMaxDataRate))
{
if ((NULL == ccid_desc->arrayOfSupportedDataRates)
|| find_baud_rate(card_baudrate,
ccid_desc->arrayOfSupportedDataRates))
{
pps[1] |= 0x10;
pps[2] = atr.ib[0][ATR_INTERFACE_BYTE_TA].value;
DEBUG_COMM2("Set speed to %d bauds", card_baudrate);
}
else
{
DEBUG_COMM2("Reader does not support %d bauds",
card_baudrate);
if (atr.ib[1][ATR_INTERFACE_BYTE_TA].present)
return IFD_COMMUNICATION_ERROR;
}
}
else
{
if ((card_baudrate > ccid_desc->dwMaxDataRate +2)
&& (atr.ib[0][ATR_INTERFACE_BYTE_TA].value <= 0x97)
&& ccid_desc->arrayOfSupportedDataRates)
{
unsigned char old_TA1;
old_TA1 = atr.ib[0][ATR_INTERFACE_BYTE_TA].value;
while (atr.ib[0][ATR_INTERFACE_BYTE_TA].value > 0x94)
{
atr.ib[0][ATR_INTERFACE_BYTE_TA].value--;
(void)ATR_GetParameter(&atr, ATR_PARAMETER_D, &d);
(void)ATR_GetParameter(&atr, ATR_PARAMETER_F, &f);
card_baudrate = (unsigned int) (1000 *
ccid_desc->dwDefaultClock * d / f);
if (find_baud_rate(card_baudrate,
ccid_desc->arrayOfSupportedDataRates))
{
pps[1] |= 0x10;
pps[2] = atr.ib[0][ATR_INTERFACE_BYTE_TA].value;
DEBUG_COMM2("Set adapted speed to %d bauds",
card_baudrate);
break;
}
}
atr.ib[0][ATR_INTERFACE_BYTE_TA].value = old_TA1;
}
}
}
}
if (Flags & IFD_NEGOTIATE_PTS2)
{
pps[1] |= 0x20;
pps[3] = PTS2;
}
if (Flags & IFD_NEGOTIATE_PTS3)
{
pps[1] |= 0x40;
pps[4] = PTS3;
}
pps[0] = 0xFF;
if ((! (ccid_desc->dwFeatures & CCID_CLASS_AUTO_PPS_CUR))
&& (! atr.ib[1][ATR_INTERFACE_BYTE_TA].present))
{
int default_protocol;
if (ATR_MALFORMED == ATR_GetDefaultProtocol(&atr, &default_protocol))
return IFD_PROTOCOL_NOT_SUPPORTED;
if (((pps[1] & 0x0F) != default_protocol) || (PPS_HAS_PPS1(pps)))
{
#ifdef O2MICRO_OZ776_PATCH
if ((OZ776 == ccid_desc->readerID)
|| (OZ776_7772 == ccid_desc->readerID))
{
Protocol = default_protocol +
(SCARD_PROTOCOL_T0 - ATR_PROTOCOL_TYPE_T0);
DEBUG_INFO2("PPS not supported on O2Micro readers. Using T=%d",
Protocol - SCARD_PROTOCOL_T0);
}
else
#endif
if (PPS_Exchange(reader_index, pps, &len, &pps[2]) != PPS_OK)
{
DEBUG_INFO("PPS_Exchange Failed");
return IFD_ERROR_PTS_FAILURE;
}
}
}
(void)ATR_GetConvention(&atr, &convention);
if (atr.ib[1][ATR_INTERFACE_BYTE_TA].present
&& (atr.ib[1][ATR_INTERFACE_BYTE_TA].value & 0x10))
return IFD_COMMUNICATION_ERROR;
if (SCARD_PROTOCOL_T1 == Protocol)
{
BYTE param[] = {
0x11,
0x10,
0x00,
0x4D,
0x00,
0x20,
0x00
};
int i;
t1_state_t *t1 = &(ccid_slot -> t1);
RESPONSECODE ret;
double f, d;
if (PPS_HAS_PPS1(pps))
param[0] = pps[2];
if (2 == t1->rc_bytes)
param[1] |= 0x01;
if (ATR_CONVENTION_INVERSE == convention)
param[1] |= 0x02;
if (atr.ib[0][ATR_INTERFACE_BYTE_TC].present)
param[2] = atr.ib[0][ATR_INTERFACE_BYTE_TC].value;
for (i=2; i<ATR_MAX_PROTOCOLS; i++)
if (atr.ib[i][ATR_INTERFACE_BYTE_TB].present)
{
DEBUG_COMM3("BWI/CWI (TB%d) present: 0x%02X", i+1,
atr.ib[i][ATR_INTERFACE_BYTE_TB].value);
param[3] = atr.ib[i][ATR_INTERFACE_BYTE_TB].value;
{
char openpgp_atr[] = { 0x3B, 0xFA, 0x13, 0x00, 0xFF, 0x81,
0x31, 0x80, 0x45, 0x00, 0x31, 0xC1, 0x73, 0xC0,
0x01, 0x00, 0x00, 0x90, 0x00, 0xB1 };
if (0 == memcmp(ccid_slot->pcATRBuffer, openpgp_atr,
ccid_slot->nATRLength))
{
param[3] = 0x75;
DEBUG_COMM2("OpenPGP hack, using 0x%02X", param[3]);
}
}
break;
}
(void)ATR_GetParameter(&atr, ATR_PARAMETER_F, &f);
(void)ATR_GetParameter(&atr, ATR_PARAMETER_D, &d);
ccid_desc->readTimeout = T1_card_timeout(f, d, param[2],
(param[3] & 0xF0) >> 4 , param[3] & 0x0F ,
ccid_desc->dwDefaultClock);
for (i=2; i<ATR_MAX_PROTOCOLS; i++)
if (atr.ib[i][ATR_INTERFACE_BYTE_TA].present)
{
DEBUG_COMM3("IFSC (TA%d) present: %d", i+1,
atr.ib[i][ATR_INTERFACE_BYTE_TA].value);
param[5] = atr.ib[i][ATR_INTERFACE_BYTE_TA].value;
break;
}
DEBUG_COMM2("Timeout: %d seconds", ccid_desc->readTimeout);
ret = SetParameters(reader_index, 1, sizeof(param), param);
if (IFD_SUCCESS != ret)
return ret;
}
else
{
BYTE param[] = {
0x11,
0x00,
0x00,
0x0A,
0x00
};
RESPONSECODE ret;
double f, d;
if (PPS_HAS_PPS1(pps))
param[0] = pps[2];
if (ATR_CONVENTION_INVERSE == convention)
param[1] |= 0x02;
if (atr.ib[0][ATR_INTERFACE_BYTE_TC].present)
param[2] = atr.ib[0][ATR_INTERFACE_BYTE_TC].value;
if (atr.ib[1][ATR_INTERFACE_BYTE_TC].present)
param[3] = atr.ib[1][ATR_INTERFACE_BYTE_TC].value;
(void)ATR_GetParameter(&atr, ATR_PARAMETER_F, &f);
(void)ATR_GetParameter(&atr, ATR_PARAMETER_D, &d);
ccid_desc->readTimeout = T0_card_timeout(f, d, param[2] ,
param[3] , ccid_desc->dwDefaultClock);
DEBUG_COMM2("Communication timeout: %d seconds",
ccid_desc->readTimeout);
ret = SetParameters(reader_index, 0, sizeof(param), param);
if (IFD_SUCCESS != ret)
return ret;
}
if (SCARD_PROTOCOL_T1 == Protocol)
{
t1_state_t *t1 = &(ccid_slot -> t1);
int i;
for (i=2; i<ATR_MAX_PROTOCOLS; i++)
if (atr.ib[i][ATR_INTERFACE_BYTE_TA].present)
{
DEBUG_COMM3("IFSC (TA%d) present: %d", i+1,
atr.ib[i][ATR_INTERFACE_BYTE_TA].value);
(void)t1_set_param(t1, IFD_PROTOCOL_T1_IFSC,
atr.ib[i][ATR_INTERFACE_BYTE_TA].value);
break;
}
if (! (ccid_desc->dwFeatures & CCID_CLASS_AUTO_IFSD))
{
DEBUG_COMM2("Negociate IFSD at %d", ccid_desc -> dwMaxIFSD);
if (t1_negotiate_ifsd(t1, 0, ccid_desc -> dwMaxIFSD) < 0)
return IFD_COMMUNICATION_ERROR;
}
(void)t1_set_param(t1, IFD_PROTOCOL_T1_IFSD, ccid_desc -> dwMaxIFSD);
DEBUG_COMM3("T=1: IFSC=%d, IFSD=%d", t1->ifsc, t1->ifsd);
}
end:
ccid_desc->cardProtocol = Protocol;
return IFD_SUCCESS;
}
EXTERNAL RESPONSECODE IFDHPowerICC(DWORD Lun, DWORD Action,
PUCHAR Atr, PDWORD AtrLength)
{
unsigned int nlength;
RESPONSECODE return_value = IFD_SUCCESS;
unsigned char pcbuffer[RESP_BUF_SIZE];
int reader_index;
const char *actions[] = { "PowerUp", "PowerDown", "Reset" };
unsigned int oldReadTimeout;
_ccid_descriptor *ccid_descriptor;
*AtrLength = 0;
if (-1 == (reader_index = LunToReaderIndex(Lun)))
return IFD_COMMUNICATION_ERROR;
DEBUG_INFO4("action: %s, %s (lun: %X)", actions[Action-IFD_POWER_UP],
CcidSlots[reader_index].readerName, Lun);
switch (Action)
{
case IFD_POWER_DOWN:
CcidSlots[reader_index].nATRLength = 0;
*CcidSlots[reader_index].pcATRBuffer = '\0';
CcidSlots[reader_index].bPowerFlags |= MASK_POWERFLAGS_PDWN;
if (IFD_SUCCESS != CmdPowerOff(reader_index))
{
DEBUG_CRITICAL("PowerDown failed");
return_value = IFD_ERROR_POWER_ACTION;
goto end;
}
t1_release(&(get_ccid_slot(reader_index) -> t1));
break;
case IFD_POWER_UP:
case IFD_RESET:
ccid_descriptor = get_ccid_descriptor(reader_index);
oldReadTimeout = ccid_descriptor->readTimeout;
ccid_descriptor->readTimeout = 60;
nlength = sizeof(pcbuffer);
return_value = CmdPowerOn(reader_index, &nlength, pcbuffer,
PowerOnVoltage);
ccid_descriptor->readTimeout = oldReadTimeout;
if (return_value != IFD_SUCCESS)
{
get_ccid_descriptor(reader_index)->dwSlotStatus
= IFD_ICC_NOT_PRESENT;;
DEBUG_CRITICAL("PowerUp failed");
return_value = IFD_ERROR_POWER_ACTION;
goto end;
}
CcidSlots[reader_index].bPowerFlags |= MASK_POWERFLAGS_PUP;
CcidSlots[reader_index].bPowerFlags &= ~MASK_POWERFLAGS_PDWN;
CcidSlots[reader_index].nATRLength = *AtrLength =
(nlength < MAX_ATR_SIZE) ? nlength : MAX_ATR_SIZE;
memcpy(Atr, pcbuffer, *AtrLength);
memcpy(CcidSlots[reader_index].pcATRBuffer, pcbuffer, *AtrLength);
(void)t1_init(&(get_ccid_slot(reader_index) -> t1), reader_index);
break;
default:
DEBUG_CRITICAL("Action not supported");
return_value = IFD_NOT_SUPPORTED;
}
end:
return return_value;
}
EXTERNAL RESPONSECODE IFDHTransmitToICC(DWORD Lun, SCARD_IO_HEADER SendPci,
PUCHAR TxBuffer, DWORD TxLength,
PUCHAR RxBuffer, PDWORD RxLength, PSCARD_IO_HEADER RecvPci)
{
RESPONSECODE return_value;
unsigned int rx_length;
int reader_index;
if (-1 == (reader_index = LunToReaderIndex(Lun)))
return IFD_COMMUNICATION_ERROR;
DEBUG_INFO3("%s (lun: %X)", CcidSlots[reader_index].readerName, Lun);
rx_length = *RxLength;
return_value = CmdXfrBlock(reader_index, TxLength, TxBuffer, &rx_length,
RxBuffer, SendPci.Protocol);
if (IFD_SUCCESS == return_value)
*RxLength = rx_length;
else
*RxLength = 0;
return return_value;
}
EXTERNAL RESPONSECODE IFDHControl(DWORD Lun, DWORD dwControlCode,
PUCHAR TxBuffer, DWORD TxLength, PUCHAR RxBuffer, DWORD RxLength,
PDWORD pdwBytesReturned)
{
RESPONSECODE return_value = IFD_COMMUNICATION_ERROR;
int reader_index;
reader_index = LunToReaderIndex(Lun);
if ((-1 == reader_index) || (NULL == pdwBytesReturned))
return return_value;
DEBUG_INFO4("ControlCode: 0x%X, %s (lun: %X)", dwControlCode,
CcidSlots[reader_index].readerName, Lun);
DEBUG_INFO_XXD("Control TxBuffer: ", TxBuffer, TxLength);
*pdwBytesReturned = 0;
if (IOCTL_SMARTCARD_VENDOR_IFD_EXCHANGE == dwControlCode)
{
if (FALSE == (DriverOptions & DRIVER_OPTION_CCID_EXCHANGE_AUTHORIZED))
{
DEBUG_INFO("ifd exchange (Escape command) not allowed");
return_value = IFD_COMMUNICATION_ERROR;
}
else
{
unsigned int iBytesReturned;
iBytesReturned = RxLength;
return_value = CmdEscape(reader_index, TxBuffer, TxLength, RxBuffer,
&iBytesReturned);
*pdwBytesReturned = iBytesReturned;
}
}
if (CM_IOCTL_GET_FEATURE_REQUEST == dwControlCode)
{
unsigned int iBytesReturned = 0;
PCSC_TLV_STRUCTURE *pcsc_tlv = (PCSC_TLV_STRUCTURE *)RxBuffer;
if (RxLength < 4 * sizeof(PCSC_TLV_STRUCTURE))
return IFD_COMMUNICATION_ERROR;
if (get_ccid_descriptor(reader_index) -> bPINSupport
& CCID_CLASS_PIN_VERIFY)
{
pcsc_tlv -> tag = FEATURE_VERIFY_PIN_DIRECT;
pcsc_tlv -> length = 0x04;
pcsc_tlv -> value = htonl(IOCTL_FEATURE_VERIFY_PIN_DIRECT);
pcsc_tlv++;
iBytesReturned += sizeof(PCSC_TLV_STRUCTURE);
}
if (get_ccid_descriptor(reader_index) -> bPINSupport
& CCID_CLASS_PIN_MODIFY)
{
pcsc_tlv -> tag = FEATURE_MODIFY_PIN_DIRECT;
pcsc_tlv -> length = 0x04;
pcsc_tlv -> value = htonl(IOCTL_FEATURE_MODIFY_PIN_DIRECT);
pcsc_tlv++;
iBytesReturned += sizeof(PCSC_TLV_STRUCTURE);
}
#ifdef FEATURE_IFD_PIN_PROPERTIES
pcsc_tlv -> tag = FEATURE_IFD_PIN_PROPERTIES;
pcsc_tlv -> length = 0x04;
pcsc_tlv -> value = htonl(IOCTL_FEATURE_IFD_PIN_PROPERTIES);
pcsc_tlv++;
iBytesReturned += sizeof(PCSC_TLV_STRUCTURE);
#endif
if (KOBIL_TRIBANK == get_ccid_descriptor(reader_index) -> readerID)
{
pcsc_tlv -> tag = FEATURE_MCT_READERDIRECT;
pcsc_tlv -> length = 0x04;
pcsc_tlv -> value = htonl(IOCTL_FEATURE_MCT_READERDIRECT);
pcsc_tlv++;
iBytesReturned += sizeof(PCSC_TLV_STRUCTURE);
}
*pdwBytesReturned = iBytesReturned;
return_value = IFD_SUCCESS;
}
#ifdef FEATURE_IFD_PIN_PROPERTIES
if (IOCTL_FEATURE_IFD_PIN_PROPERTIES == dwControlCode)
{
PIN_PROPERTIES_STRUCTURE *caps = (PIN_PROPERTIES_STRUCTURE *)RxBuffer;
if (RxLength < sizeof(PIN_PROPERTIES_STRUCTURE))
return IFD_COMMUNICATION_ERROR;
caps -> wLcdLayout = get_ccid_descriptor(reader_index) -> wLcdLayout;
caps -> wLcdMaxCharacters = 0x0000;
caps -> wLcdMaxLines = 0x0000;
caps -> bEntryValidationCondition = 0x07;
caps -> bTimeOut2 = 0x00;
*pdwBytesReturned = sizeof(*caps);
return_value = IFD_SUCCESS;
}
#endif
if (IOCTL_FEATURE_VERIFY_PIN_DIRECT == dwControlCode)
{
unsigned int iBytesReturned;
iBytesReturned = RxLength;
return_value = SecurePINVerify(reader_index, TxBuffer, TxLength,
RxBuffer, &iBytesReturned);
*pdwBytesReturned = iBytesReturned;
}
if (IOCTL_FEATURE_MODIFY_PIN_DIRECT == dwControlCode)
{
unsigned int iBytesReturned;
iBytesReturned = RxLength;
return_value = SecurePINModify(reader_index, TxBuffer, TxLength,
RxBuffer, &iBytesReturned);
*pdwBytesReturned = iBytesReturned;
}
if (IOCTL_FEATURE_MCT_READERDIRECT == dwControlCode)
{
if ( (TxBuffer[0] != 0x20)
|| ((TxBuffer[1] & 0xF0) != 0x70)
|| ((TxBuffer[1] & 0x0F) > 4)
|| (TxBuffer[2] != 0x00)
|| (TxBuffer[3] != 0x00)
|| (TxBuffer[4] != 0x00)
)
{
DEBUG_INFO("MCT Command refused by driver");
return_value = IFD_COMMUNICATION_ERROR;
}
else
{
unsigned int iBytesReturned;
iBytesReturned = RxLength;
return_value = CmdEscape(reader_index, TxBuffer, TxLength, RxBuffer,
&iBytesReturned);
*pdwBytesReturned = iBytesReturned;
}
}
if (IFD_SUCCESS != return_value)
*pdwBytesReturned = 0;
DEBUG_INFO_XXD("Control RxBuffer: ", RxBuffer, *pdwBytesReturned);
return return_value;
}
EXTERNAL RESPONSECODE IFDHICCPresence(DWORD Lun)
{
unsigned char pcbuffer[SIZE_GET_SLOT_STATUS];
RESPONSECODE return_value = IFD_COMMUNICATION_ERROR;
int oldLogLevel;
int reader_index;
_ccid_descriptor *ccid_descriptor;
unsigned int oldReadTimeout;
if (-1 == (reader_index = LunToReaderIndex(Lun)))
return IFD_COMMUNICATION_ERROR;
DEBUG_PERIODIC3("%s (lun: %X)", CcidSlots[reader_index].readerName, Lun);
ccid_descriptor = get_ccid_descriptor(reader_index);
if (GEMCORESIMPRO == ccid_descriptor->readerID)
{
return_value = ccid_descriptor->dwSlotStatus;
goto end;
}
oldReadTimeout = ccid_descriptor->readTimeout;
ccid_descriptor->readTimeout = DEFAULT_COM_READ_TIMEOUT;
oldLogLevel = LogLevel;
if (! (LogLevel & DEBUG_LEVEL_PERIODIC))
LogLevel &= ~DEBUG_LEVEL_COMM;
return_value = CmdGetSlotStatus(reader_index, pcbuffer);
ccid_descriptor->readTimeout = oldReadTimeout;
LogLevel = oldLogLevel;
if (return_value != IFD_SUCCESS)
return return_value;
return_value = IFD_COMMUNICATION_ERROR;
switch (pcbuffer[7] & CCID_ICC_STATUS_MASK)
{
case CCID_ICC_PRESENT_ACTIVE:
return_value = IFD_ICC_PRESENT;
break;
case CCID_ICC_PRESENT_INACTIVE:
if ((CcidSlots[reader_index].bPowerFlags == POWERFLAGS_RAZ)
|| (CcidSlots[reader_index].bPowerFlags & MASK_POWERFLAGS_PDWN))
return_value = IFD_ICC_PRESENT;
else
{
CcidSlots[reader_index].bPowerFlags = POWERFLAGS_RAZ;
return_value = IFD_ICC_NOT_PRESENT;
}
break;
case CCID_ICC_ABSENT:
CcidSlots[reader_index].nATRLength = 0;
*CcidSlots[reader_index].pcATRBuffer = '\0';
CcidSlots[reader_index].bPowerFlags = POWERFLAGS_RAZ;
return_value = IFD_ICC_NOT_PRESENT;
break;
}
#if 0
if (((SCR331DI == ccid_descriptor->readerID)
|| (SDI010 == ccid_descriptor->readerID)
|| (SCR331DINTTCOM == ccid_descriptor->readerID))
&& (ccid_descriptor->bCurrentSlotIndex > 0))
{
unsigned char cmd[] = { 0x11 };
unsigned char res[10];
unsigned int length_res = sizeof(res);
RESPONSECODE ret;
oldLogLevel = LogLevel;
if (! (LogLevel & DEBUG_LEVEL_PERIODIC))
LogLevel &= ~DEBUG_LEVEL_COMM;
ret = CmdEscape(reader_index, cmd, sizeof(cmd), res, &length_res);
LogLevel = oldLogLevel;
if (ret != IFD_SUCCESS)
{
DEBUG_INFO("CmdEscape failed");
res[0] = 0;
}
if (0x01 == res[0])
return_value = IFD_ICC_PRESENT;
else
{
CcidSlots[reader_index].nATRLength = 0;
*CcidSlots[reader_index].pcATRBuffer = '\0';
CcidSlots[reader_index].bPowerFlags = POWERFLAGS_RAZ;
return_value = IFD_ICC_NOT_PRESENT;
}
}
#endif
end:
DEBUG_PERIODIC2("Card %s",
IFD_ICC_PRESENT == return_value ? "present" : "absent");
return return_value;
}
CcidDesc *get_ccid_slot(unsigned int reader_index)
{
return &CcidSlots[reader_index];
}
void init_driver(void)
{
char keyValue[TOKEN_MAX_VALUE_SIZE];
char infofile[FILENAME_MAX];
char *e;
DEBUG_INFO("Driver version: " VERSION);
(void)snprintf(infofile, sizeof(infofile), "%s/%s/Contents/Info.plist",
PCSCLITE_HP_DROPDIR, BUNDLE);
if (0 == LTPBundleFindValueWithKey(infofile, "ifdLogLevel", keyValue, 0))
{
LogLevel = strtoul(keyValue, NULL, 0);
DEBUG_INFO2("LogLevel: 0x%.4X", LogLevel);
}
e = getenv("LIBCCID_ifdLogLevel");
if (e)
{
LogLevel = strtoul(e, NULL, 0);
DEBUG_INFO2("LogLevel from LIBCCID_ifdLogLevel: 0x%.4X", LogLevel);
}
if (0 == LTPBundleFindValueWithKey(infofile, "ifdDriverOptions", keyValue, 0))
{
DriverOptions = strtoul(keyValue, NULL, 0);
DEBUG_INFO2("DriverOptions: 0x%.4X", DriverOptions);
}
switch ((DriverOptions >> 4) & 0x03)
{
case 0:
PowerOnVoltage = VOLTAGE_5V;
break;
case 1:
PowerOnVoltage = VOLTAGE_3V;
break;
case 2:
PowerOnVoltage = VOLTAGE_1_8V;
break;
case 3:
PowerOnVoltage = VOLTAGE_AUTO;
break;
}
InitReaderIndex();
DebugInitialized = TRUE;
}
void extra_egt(ATR_t *atr, _ccid_descriptor *ccid_desc, DWORD Protocol)
{
unsigned int card_baudrate;
unsigned int default_baudrate;
double f, d;
int i;
if (! atr->ib[0][ATR_INTERFACE_BYTE_TA].present)
return;
(void)ATR_GetParameter(atr, ATR_PARAMETER_D, &d);
(void)ATR_GetParameter(atr, ATR_PARAMETER_F, &f);
if ((0 == f) || (0 == d))
return;
card_baudrate = (unsigned int) (1000 * ccid_desc->dwDefaultClock * d / f);
default_baudrate = (unsigned int) (1000 * ccid_desc->dwDefaultClock
* ATR_DEFAULT_D / ATR_DEFAULT_F);
if (card_baudrate <= default_baudrate)
return;
if (atr->ib[0][ATR_INTERFACE_BYTE_TC].present &&
((0x00 == atr->ib[0][ATR_INTERFACE_BYTE_TC].value) ||
(0xFF == atr->ib[0][ATR_INTERFACE_BYTE_TC].value)))
{
if (SCARD_PROTOCOL_T0 == Protocol)
{
atr->ib[0][ATR_INTERFACE_BYTE_TC].present = TRUE;
atr->ib[0][ATR_INTERFACE_BYTE_TC].value = 2;
DEBUG_INFO("Extra EGT patch applied");
}
if (SCARD_PROTOCOL_T1 == Protocol)
{
for (i=2; i<ATR_MAX_PROTOCOLS; i++)
{
if (atr->ib[i][ATR_INTERFACE_BYTE_TB].present &&
((atr->ib[i][ATR_INTERFACE_BYTE_TB].value & 0x0F) >= 2))
{
atr->ib[0][ATR_INTERFACE_BYTE_TC].present = TRUE;
atr->ib[0][ATR_INTERFACE_BYTE_TC].value = 2;
DEBUG_INFO("Extra EGT patch applied");
break;
}
}
}
}
}
static char find_baud_rate(unsigned int baudrate, unsigned int *list)
{
int i;
DEBUG_COMM2("Card baud rate: %d", baudrate);
for (i=0;; i++)
{
if (0 == list[i])
break;
DEBUG_COMM2("Reader can do: %d", list[i]);
if ((baudrate < list[i] + 2) && (baudrate > list[i] - 2))
return TRUE;
}
return FALSE;
}
static unsigned int T0_card_timeout(double f, double d, int TC1, int TC2,
int clock_frequency)
{
unsigned int timeout = DEFAULT_COM_READ_TIMEOUT;
double EGT, WWT;
unsigned int t;
if ((0 == f) || (0 == d) || (0 == clock_frequency))
return 60;
EGT = 12 * f / d / clock_frequency + (f / d) * TC1 / clock_frequency;
WWT = 960 * TC2 * f / clock_frequency;
t = 261 * EGT + (3 + 3) * WWT;
t = t/1000 +1;
if (timeout < t)
timeout = t;
t = 5 * EGT + (1 + 259) * WWT;
t = t/1000 +1;
if (timeout < t)
timeout = t;
return timeout;
}
static unsigned int T1_card_timeout(double f, double d, int TC1,
int BWI, int CWI, int clock_frequency)
{
double EGT, BWT, CWT, etu;
unsigned int timeout;
if ((0 == f) || (0 == d) || (0 == clock_frequency))
return 60;
etu = f / d / clock_frequency;
EGT = 12 * etu + (f / d) * TC1 / clock_frequency;
BWT = 11 * etu + (1<<BWI) * 960 * 372 / clock_frequency;
CWT = (11 + (1<<CWI)) * etu;
timeout = 260*EGT + BWT + 260*CWT;
timeout = timeout/1000 +1;
return timeout;
}