ccid/src/ccid_usb.c

1770 lines
50 KiB
C

/*
ccid_usb.c: USB access routines using the libusb library
Copyright (C) 2003-2010 Ludovic Rousseau
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define __CCID_USB__
#include <stdio.h>
#include <string.h>
# ifdef S_SPLINT_S
# include <sys/types.h>
# endif
#include <libusb.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>
#include <ifdhandler.h>
#include <config.h>
#include "misc.h"
#include "ccid.h"
#include "debug.h"
#include "defs.h"
#include "utils.h"
#include "parser.h"
#include "ccid_ifdhandler.h"
/* write timeout
* we don't have to wait a long time since the card was doing nothing */
#define USB_WRITE_TIMEOUT (5 * 1000) /* 5 seconds timeout */
/*
* Proprietary USB Class (0xFF) are (or are not) accepted
* A proprietary class is used for devices released before the final CCID
* specifications were ready.
* We should not have problems with non CCID devices because the
* Manufacturer and Product ID are also used to identify the device */
#define ALLOW_PROPRIETARY_CLASS
#define BUS_DEVICE_STRSIZE 32
/* Using the default libusb context */
/* does not work for libusb <= 1.0.8 */
/* #define ctx NULL */
libusb_context *ctx = NULL;
#define CCID_INTERRUPT_SIZE 8
struct usbDevice_MultiSlot_Extension
{
int reader_index;
/* The multi-threaded polling part */
int terminated;
int status;
unsigned char buffer[CCID_INTERRUPT_SIZE];
pthread_t thread_proc;
pthread_mutex_t mutex;
pthread_cond_t condition;
struct libusb_transfer *transfer;
};
typedef struct
{
libusb_device_handle *dev_handle;
uint8_t bus_number;
uint8_t device_address;
int interface;
/*
* Endpoints
*/
int bulk_in;
int bulk_out;
int interrupt;
/* Number of slots using the same device */
int real_nb_opened_slots;
int *nb_opened_slots;
/*
* CCID infos common to USB and serial
*/
_ccid_descriptor ccid;
/* libusb transfer for the polling (or NULL) */
struct libusb_transfer *polling_transfer;
/* pointer to the multislot extension (if any) */
struct usbDevice_MultiSlot_Extension *multislot_extension;
} _usbDevice;
/* The _usbDevice structure must be defined before including ccid_usb.h */
#include "ccid_usb.h"
/* Specific hooks for multislot readers */
static int Multi_InterruptRead(int reader_index, int timeout /* in ms */);
static void Multi_InterruptStop(int reader_index);
static struct usbDevice_MultiSlot_Extension *Multi_CreateFirstSlot(int reader_index);
static struct usbDevice_MultiSlot_Extension *Multi_CreateNextSlot(int physical_reader_index);
static void Multi_PollingTerminate(struct usbDevice_MultiSlot_Extension *msExt);
static int get_end_points(struct libusb_config_descriptor *desc,
_usbDevice *usbdevice, int num);
int ccid_check_firmware(struct libusb_device_descriptor *desc);
static unsigned int *get_data_rates(unsigned int reader_index,
struct libusb_config_descriptor *desc, int num);
/* ne need to initialize to 0 since it is static */
static _usbDevice usbDevice[CCID_DRIVER_MAX_READERS];
#define PCSCLITE_MANUKEY_NAME "ifdVendorID"
#define PCSCLITE_PRODKEY_NAME "ifdProductID"
#define PCSCLITE_NAMEKEY_NAME "ifdFriendlyName"
struct _bogus_firmware
{
int vendor; /* idVendor */
int product; /* idProduct */
int firmware; /* bcdDevice: previous firmwares have bugs */
};
static struct _bogus_firmware Bogus_firmwares[] = {
{ 0x04e6, 0xe001, 0x0516 }, /* SCR 331 */
{ 0x04e6, 0x5111, 0x0620 }, /* SCR 331-DI */
{ 0x04e6, 0xe003, 0x0510 }, /* SPR 532 */
{ 0x0D46, 0x3001, 0x0037 }, /* KAAN Base */
{ 0x0D46, 0x3002, 0x0037 }, /* KAAN Advanced */
{ 0x09C3, 0x0008, 0x0203 }, /* ActivCard V2 */
{ 0x0DC3, 0x1004, 0x0502 }, /* ASE IIIe USBv2 */
{ 0x0DC3, 0x1102, 0x0607 }, /* ASE IIIe KB USB */
{ 0x058F, 0x9520, 0x0102 }, /* Alcor AU9520-G */
{ 0x072F, 0x2200, 0x0206 }, /* ACS ACR122U-WB-R */
{ 0x08C3, 0x0402, 0x5000 }, /* Precise Biometrics Precise 200 MC */
{ 0x08C3, 0x0401, 0x5000 }, /* Precise Biometrics Precise 250 MC */
{ 0x0B0C, 0x0050, 0x0101 }, /* Todos Argos Mini II */
{ 0x0DC3, 0x0900, 0x0200 }, /* Athena IDProtect Key v2 */
{ 0x03F0, 0x0036, 0x0124 }, /* HP USB CCID Smartcard Keyboard */
{ 0x062D, 0x0001, 0x0102 }, /* THRC Smart Card Reader */
{ 0x04E6, 0x5291, 0x0112 }, /* SCM SCL010 Contactless Reader */
/* the firmware version is not correct since I do not have received a
* working reader yet */
#ifndef O2MICRO_OZ776_PATCH
{ 0x0b97, 0x7762, 0x0111 }, /* Oz776S */
#endif
};
/* data rates supported by the secondary slots on the GemCore Pos Pro & SIM Pro */
unsigned int SerialCustomDataRates[] = { GEMPLUS_CUSTOM_DATA_RATES, 0 };
/*****************************************************************************
*
* close_libusb_if_needed
*
****************************************************************************/
static void close_libusb_if_needed(void)
{
int i, to_exit = TRUE;
if (NULL == ctx)
return;
/* if at least 1 reader is still in use we do not exit libusb */
for (i=0; i<CCID_DRIVER_MAX_READERS; i++)
{
if (usbDevice[i].dev_handle != NULL)
to_exit = FALSE;
}
if (to_exit)
{
DEBUG_INFO1("libusb_exit");
libusb_exit(ctx);
ctx = NULL;
}
} /* close_libusb_if_needed */
/*****************************************************************************
*
* OpenUSB
*
****************************************************************************/
status_t OpenUSB(unsigned int reader_index, /*@unused@*/ int Channel)
{
(void)Channel;
return OpenUSBByName(reader_index, NULL);
} /* OpenUSB */
/*****************************************************************************
*
* OpenUSBByName
*
****************************************************************************/
status_t OpenUSBByName(unsigned int reader_index, /*@null@*/ char *device)
{
unsigned int alias;
struct libusb_device_handle *dev_handle;
char infofile[FILENAME_MAX];
#ifndef __APPLE__
unsigned int device_vendor, device_product;
unsigned int device_bus = 0;
unsigned int device_addr = 0;
#else
/* 100 ms delay */
struct timespec sleep_time = { 0, 100 * 1000 * 1000 };
int count_libusb = 10;
#endif
int interface_number = -1;
int i;
static int previous_reader_index = -1;
libusb_device **devs, *dev;
ssize_t cnt;
list_t plist, *values, *ifdVendorID, *ifdProductID, *ifdFriendlyName;
int rv;
int claim_failed = FALSE;
int return_value = STATUS_SUCCESS;
DEBUG_COMM3("Reader index: %X, Device: %s", reader_index, device);
#ifndef __APPLE__
/* device name specified */
if (device)
{
char *dirname;
/* format: usb:%04x/%04x, vendor, product */
if (strncmp("usb:", device, 4) != 0)
{
DEBUG_CRITICAL2("device name does not start with \"usb:\": %s",
device);
return STATUS_UNSUCCESSFUL;
}
if (sscanf(device, "usb:%x/%x", &device_vendor, &device_product) != 2)
{
DEBUG_CRITICAL2("device name can't be parsed: %s", device);
return STATUS_UNSUCCESSFUL;
}
/* format usb:%04x/%04x:libudev:%d:%s
* with %d set to
* 01 (or whatever the interface number is)
* and %s set to
* /dev/bus/usb/008/004
*/
if ((dirname = strstr(device, "libudev:")) != NULL)
{
/* convert the interface number, bus and device ids */
if (sscanf(dirname + 8, "%d:/dev/bus/usb/%d/%d", &interface_number, &device_bus, &device_addr) == 3) {
DEBUG_COMM2("interface_number: %d", interface_number);
DEBUG_COMM3("usb bus/device: %d/%d", device_bus, device_addr);
}
}
else
{
/* format usb:%04x/%04x:libusb-1.0:%d:%d:%d */
if ((dirname = strstr(device, "libusb-1.0:")) != NULL)
{
/* convert the interface number, bus and device ids */
if (sscanf(dirname + 11, "%d:%d:%d",
&device_bus, &device_addr, &interface_number) == 3)
{
DEBUG_COMM2("interface_number: %d", interface_number);
DEBUG_COMM3("usb bus/device: %d/%d", device_bus,
device_addr);
}
}
}
}
#endif
/* is the reader_index already used? */
if (usbDevice[reader_index].dev_handle != NULL)
{
DEBUG_CRITICAL2("USB driver with index %X already in use",
reader_index);
return STATUS_UNSUCCESSFUL;
}
/* Info.plist full patch filename */
(void)snprintf(infofile, sizeof(infofile), "%s/%s/Contents/Info.plist",
PCSCLITE_HP_DROPDIR, BUNDLE);
DEBUG_INFO2("Using: %s", infofile);
rv = bundleParse(infofile, &plist);
if (rv)
return STATUS_UNSUCCESSFUL;
#define GET_KEY(key, values) \
rv = LTPBundleFindValueWithKey(&plist, key, &values); \
if (rv) \
{ \
DEBUG_CRITICAL2("Value/Key not defined for " key " in %s", infofile); \
return_value = STATUS_UNSUCCESSFUL; \
goto end1; \
} \
else \
DEBUG_INFO2(key ": %s", (char *)list_get_at(values, 0));
/* general driver info */
GET_KEY("ifdManufacturerString", values)
GET_KEY("ifdProductString", values)
GET_KEY("Copyright", values)
if (NULL == ctx)
{
rv = libusb_init(&ctx);
if (rv != 0)
{
DEBUG_CRITICAL2("libusb_init failed: %s", libusb_error_name(rv));
return_value = STATUS_UNSUCCESSFUL;
goto end1;
}
}
#define GET_KEYS(key, values) \
rv = LTPBundleFindValueWithKey(&plist, key, values); \
if (rv) \
{ \
DEBUG_CRITICAL2("Value/Key not defined for " key " in %s", infofile); \
return_value = STATUS_UNSUCCESSFUL; \
goto end1; \
}
GET_KEYS("ifdVendorID", &ifdVendorID)
GET_KEYS("ifdProductID", &ifdProductID);
GET_KEYS("ifdFriendlyName", &ifdFriendlyName)
/* The 3 lists do not have the same size */
if ((list_size(ifdVendorID) != list_size(ifdProductID))
|| (list_size(ifdVendorID) != list_size(ifdFriendlyName)))
{
DEBUG_CRITICAL2("Error parsing %s", infofile);
return_value = STATUS_UNSUCCESSFUL;
goto end1;
}
#ifdef __APPLE__
again_libusb:
#endif
cnt = libusb_get_device_list(ctx, &devs);
if (cnt < 0)
{
DEBUG_CRITICAL("libusb_get_device_list() failed\n");
return_value = STATUS_UNSUCCESSFUL;
goto end1;
}
/* for any supported reader */
for (alias=0; alias<list_size(ifdVendorID); alias++)
{
unsigned int vendorID, productID;
char *friendlyName;
vendorID = strtoul(list_get_at(ifdVendorID, alias), NULL, 0);
productID = strtoul(list_get_at(ifdProductID, alias), NULL, 0);
friendlyName = list_get_at(ifdFriendlyName, alias);
#ifndef __APPLE__
/* the device was specified but is not the one we are trying to find */
if (device
&& (vendorID != device_vendor || productID != device_product))
continue;
#else
/* Leopard puts the friendlyname in the device argument */
if (device && strcmp(device, friendlyName))
continue;
#endif
/* for every device */
i = 0;
while ((dev = devs[i++]) != NULL)
{
struct libusb_device_descriptor desc;
struct libusb_config_descriptor *config_desc;
uint8_t bus_number = libusb_get_bus_number(dev);
uint8_t device_address = libusb_get_device_address(dev);
#ifndef __APPLE__
if ((device_bus || device_addr)
&& ((bus_number != device_bus)
|| (device_address != device_addr))) {
/* not USB the device we are looking for */
continue;
}
#endif
DEBUG_COMM3("Try device: %d/%d", bus_number, device_address);
int r = libusb_get_device_descriptor(dev, &desc);
if (r < 0)
{
DEBUG_INFO3("failed to get device descriptor for %d/%d",
bus_number, device_address);
continue;
}
DEBUG_COMM3("vid/pid : %04X/%04X", desc.idVendor, desc.idProduct);
if (desc.idVendor == vendorID && desc.idProduct == productID)
{
int already_used;
const struct libusb_interface *usb_interface = NULL;
int interface;
int num = 0;
const unsigned char *device_descriptor;
int readerID = (vendorID << 16) + productID;
#ifdef USE_COMPOSITE_AS_MULTISLOT
/* use the first CCID interface on first call */
static int static_interface = -1;
int max_interface_number = 2;
/* simulate a composite device as when libudev is used */
if ((GEMALTOPROXDU == readerID)
|| (GEMALTOPROXSU == readerID)
|| (HID_OMNIKEY_5422 == readerID)
|| (FEITIANR502DUAL == readerID))
{
/*
* We can't talk to the two CCID interfaces
* at the same time (the reader enters a
* dead lock). So we simulate a multi slot
* reader. By default multi slot readers
* can't use the slots at the same time. See
* TAG_IFD_SLOT_THREAD_SAFE
*
* One side effect is that the two readers
* are seen by pcscd as one reader so the
* interface name is the same for the two.
*
* So we have:
* 0: Gemalto Prox-DU [Prox-DU Contact_09A00795] (09A00795) 00 00
* 1: Gemalto Prox-DU [Prox-DU Contact_09A00795] (09A00795) 00 01
* instead of
* 0: Gemalto Prox-DU [Prox-DU Contact_09A00795] (09A00795) 00 00
* 1: Gemalto Prox-DU [Prox-DU Contactless_09A00795] (09A00795) 01 00
*/
/* for the Gemalto Prox-DU/SU the interfaces are:
* 0: Prox-DU HID (not used)
* 1: Prox-DU Contactless (CCID)
* 2: Prox-DU Contact (CCID)
*
* For the Feitian R502 the interfaces are:
* 0: R502 Contactless Reader (CCID)
* 1: R502 Contact Reader (CCID)
* 2: R502 SAM1 Reader (CCID)
*
* For the HID Omnikey 5422 the interfaces are:
* 0: OMNIKEY 5422CL Smartcard Reader
* 1: OMNIKEY 5422 Smartcard Reader
*/
interface_number = static_interface;
if (HID_OMNIKEY_5422 == readerID)
/* only 2 interfaces for this device */
max_interface_number = 1;
}
#endif
/* is it already opened? */
already_used = FALSE;
DEBUG_COMM3("Checking device: %d/%d",
bus_number, device_address);
for (r=0; r<CCID_DRIVER_MAX_READERS; r++)
{
if (usbDevice[r].dev_handle)
{
/* same bus, same address */
if (usbDevice[r].bus_number == bus_number
&& usbDevice[r].device_address == device_address)
already_used = TRUE;
}
}
/* this reader is already managed by us */
if (already_used)
{
if ((previous_reader_index != -1)
&& usbDevice[previous_reader_index].dev_handle
&& (usbDevice[previous_reader_index].bus_number == bus_number)
&& (usbDevice[previous_reader_index].device_address == device_address)
&& usbDevice[previous_reader_index].ccid.bCurrentSlotIndex < usbDevice[previous_reader_index].ccid.bMaxSlotIndex)
{
/* we reuse the same device
* and the reader is multi-slot */
usbDevice[reader_index] = usbDevice[previous_reader_index];
/* The other slots of GemCore SIM Pro firmware
* 1.0 do not have the same data rates.
* Firmware 2.0 do not have this limitation */
if ((GEMCOREPOSPRO == readerID)
|| ((GEMCORESIMPRO == readerID)
&& (usbDevice[reader_index].ccid.IFD_bcdDevice < 0x0200)))
{
/* Allocate a memory buffer that will be
* released in CloseUSB() */
void *ptr = malloc(sizeof SerialCustomDataRates);
if (ptr)
{
memcpy(ptr, SerialCustomDataRates,
sizeof SerialCustomDataRates);
}
usbDevice[reader_index].ccid.arrayOfSupportedDataRates = ptr;
usbDevice[reader_index].ccid.dwMaxDataRate = 125000;
}
*usbDevice[reader_index].nb_opened_slots += 1;
usbDevice[reader_index].ccid.bCurrentSlotIndex++;
usbDevice[reader_index].ccid.dwSlotStatus =
IFD_ICC_PRESENT;
DEBUG_INFO2("Opening slot: %d",
usbDevice[reader_index].ccid.bCurrentSlotIndex);
/* This is a multislot reader
* Init the multislot stuff for this next slot */
usbDevice[reader_index].multislot_extension = Multi_CreateNextSlot(previous_reader_index);
goto end;
}
else
{
/* if an interface number is given by HAL we
* continue with this device. */
if (-1 == interface_number)
{
DEBUG_INFO3("USB device %d/%d already in use."
" Checking next one.",
bus_number, device_address);
continue;
}
}
}
DEBUG_COMM3("Trying to open USB bus/device: %d/%d",
bus_number, device_address);
r = libusb_open(dev, &dev_handle);
if (r < 0)
{
DEBUG_CRITICAL4("Can't libusb_open(%d/%d): %s",
bus_number, device_address, libusb_error_name(r));
continue;
}
again:
r = libusb_get_active_config_descriptor(dev, &config_desc);
if (r < 0)
{
#ifdef __APPLE__
/* Some early Gemalto Ezio CB+ readers have
* bDeviceClass, bDeviceSubClass and bDeviceProtocol set
* to 0xFF (proprietary) instead of 0x00.
*
* So on Mac OS X the reader configuration is not done
* by the OS/kernel and we do it ourself.
*/
if ((0xFF == desc.bDeviceClass)
&& (0xFF == desc.bDeviceSubClass)
&& (0xFF == desc.bDeviceProtocol))
{
r = libusb_set_configuration(dev_handle, 1);
if (r < 0)
{
(void)libusb_close(dev_handle);
DEBUG_CRITICAL4("Can't set configuration on %d/%d: %s",
bus_number, device_address,
libusb_error_name(r));
continue;
}
}
/* recall */
r = libusb_get_active_config_descriptor(dev, &config_desc);
if (r < 0)
{
#endif
(void)libusb_close(dev_handle);
DEBUG_CRITICAL4("Can't get config descriptor on %d/%d: %s",
bus_number, device_address, libusb_error_name(r));
continue;
}
#ifdef __APPLE__
}
#endif
usb_interface = get_ccid_usb_interface(config_desc, &num);
if (usb_interface == NULL)
{
libusb_free_config_descriptor(config_desc);
(void)libusb_close(dev_handle);
if (0 == num)
DEBUG_CRITICAL3("Can't find a CCID interface on %d/%d",
bus_number, device_address);
interface_number = -1;
continue;
}
device_descriptor = get_ccid_device_descriptor(usb_interface);
if (NULL == device_descriptor)
{
libusb_free_config_descriptor(config_desc);
(void)libusb_close(dev_handle);
DEBUG_CRITICAL3("Unable to find the device descriptor for %d/%d",
bus_number, device_address);
return_value = STATUS_UNSUCCESSFUL;
goto end2;
}
interface = usb_interface->altsetting->bInterfaceNumber;
if (interface_number >= 0 && interface != interface_number)
{
libusb_free_config_descriptor(config_desc);
/* an interface was specified and it is not the
* current one */
DEBUG_INFO3("Found interface %d but expecting %d",
interface, interface_number);
DEBUG_INFO3("Wrong interface for USB device %d/%d."
" Checking next one.", bus_number, device_address);
/* check for another CCID interface on the same device */
num++;
goto again;
}
r = libusb_claim_interface(dev_handle, interface);
if (r < 0)
{
libusb_free_config_descriptor(config_desc);
(void)libusb_close(dev_handle);
DEBUG_CRITICAL4("Can't claim interface %d/%d: %s",
bus_number, device_address, libusb_error_name(r));
claim_failed = TRUE;
interface_number = -1;
continue;
}
DEBUG_INFO4("Found Vendor/Product: %04X/%04X (%s)",
desc.idVendor, desc.idProduct, friendlyName);
DEBUG_INFO3("Using USB bus/device: %d/%d",
bus_number, device_address);
/* check for firmware bugs */
if (ccid_check_firmware(&desc))
{
libusb_free_config_descriptor(config_desc);
(void)libusb_close(dev_handle);
return_value = STATUS_UNSUCCESSFUL;
goto end2;
}
#ifdef USE_COMPOSITE_AS_MULTISLOT
if ((GEMALTOPROXDU == readerID)
|| (GEMALTOPROXSU == readerID)
|| (HID_OMNIKEY_5422 == readerID)
|| (FEITIANR502DUAL == readerID))
{
/* use the next interface for the next "slot" */
static_interface = interface + 1;
/* reset for a next reader */
/* max interface number for all 3 readers is 2 */
if (static_interface > max_interface_number)
static_interface = -1;
}
#endif
/* Get Endpoints values*/
(void)get_end_points(config_desc, &usbDevice[reader_index], num);
/* store device information */
usbDevice[reader_index].dev_handle = dev_handle;
usbDevice[reader_index].bus_number = bus_number;
usbDevice[reader_index].device_address = device_address;
usbDevice[reader_index].interface = interface;
usbDevice[reader_index].real_nb_opened_slots = 1;
usbDevice[reader_index].nb_opened_slots = &usbDevice[reader_index].real_nb_opened_slots;
usbDevice[reader_index].polling_transfer = NULL;
/* CCID common informations */
usbDevice[reader_index].ccid.real_bSeq = 0;
usbDevice[reader_index].ccid.pbSeq = &usbDevice[reader_index].ccid.real_bSeq;
usbDevice[reader_index].ccid.readerID =
(desc.idVendor << 16) + desc.idProduct;
usbDevice[reader_index].ccid.dwFeatures = dw2i(device_descriptor, 40);
usbDevice[reader_index].ccid.wLcdLayout =
(device_descriptor[51] << 8) + device_descriptor[50];
usbDevice[reader_index].ccid.bPINSupport = device_descriptor[52];
usbDevice[reader_index].ccid.dwMaxCCIDMessageLength = dw2i(device_descriptor, 44);
usbDevice[reader_index].ccid.dwMaxIFSD = dw2i(device_descriptor, 28);
usbDevice[reader_index].ccid.dwDefaultClock = dw2i(device_descriptor, 10);
usbDevice[reader_index].ccid.dwMaxDataRate = dw2i(device_descriptor, 23);
usbDevice[reader_index].ccid.bMaxSlotIndex = device_descriptor[4];
usbDevice[reader_index].ccid.bCurrentSlotIndex = 0;
usbDevice[reader_index].ccid.readTimeout = DEFAULT_COM_READ_TIMEOUT;
if (device_descriptor[27])
usbDevice[reader_index].ccid.arrayOfSupportedDataRates = get_data_rates(reader_index, config_desc, num);
else
{
usbDevice[reader_index].ccid.arrayOfSupportedDataRates = NULL;
DEBUG_INFO1("bNumDataRatesSupported is 0");
}
usbDevice[reader_index].ccid.bInterfaceProtocol = usb_interface->altsetting->bInterfaceProtocol;
usbDevice[reader_index].ccid.bNumEndpoints = usb_interface->altsetting->bNumEndpoints;
usbDevice[reader_index].ccid.dwSlotStatus = IFD_ICC_PRESENT;
usbDevice[reader_index].ccid.bVoltageSupport = device_descriptor[5];
usbDevice[reader_index].ccid.sIFD_serial_number = NULL;
usbDevice[reader_index].ccid.gemalto_firmware_features = NULL;
#ifdef ENABLE_ZLP
usbDevice[reader_index].ccid.zlp = FALSE;
#endif
if (desc.iSerialNumber)
{
unsigned char serial[128];
int ret;
ret = libusb_get_string_descriptor_ascii(dev_handle,
desc.iSerialNumber, serial,
sizeof(serial));
if (ret > 0)
usbDevice[reader_index].ccid.sIFD_serial_number
= strdup((char *)serial);
}
usbDevice[reader_index].ccid.sIFD_iManufacturer = NULL;
if (desc.iManufacturer)
{
unsigned char iManufacturer[128];
int ret;
ret = libusb_get_string_descriptor_ascii(dev_handle,
desc.iManufacturer, iManufacturer,
sizeof(iManufacturer));
if (ret > 0)
usbDevice[reader_index].ccid.sIFD_iManufacturer
= strdup((char *)iManufacturer);
}
usbDevice[reader_index].ccid.IFD_bcdDevice = desc.bcdDevice;
/* If this is a multislot reader, init the multislot stuff */
if (usbDevice[reader_index].ccid.bMaxSlotIndex)
usbDevice[reader_index].multislot_extension = Multi_CreateFirstSlot(reader_index);
else
usbDevice[reader_index].multislot_extension = NULL;
libusb_free_config_descriptor(config_desc);
goto end;
}
}
}
end:
if (usbDevice[reader_index].dev_handle == NULL)
{
/* free the libusb allocated list & devices */
libusb_free_device_list(devs, 1);
#ifdef __APPLE__
/* give some time to libusb to detect the new USB devices on Mac OS X */
if (count_libusb > 0)
{
count_libusb--;
DEBUG_INFO2("Wait after libusb: %d", count_libusb);
nanosleep(&sleep_time, NULL);
goto again_libusb;
}
#endif
/* free bundle list */
bundleRelease(&plist);
/* failed */
close_libusb_if_needed();
if (claim_failed)
return STATUS_COMM_ERROR;
DEBUG_INFO1("Device not found?");
return STATUS_NO_SUCH_DEVICE;
}
/* memorise the current reader_index so we can detect
* a new OpenUSBByName on a multi slot reader */
previous_reader_index = reader_index;
end2:
/* free the libusb allocated list & devices */
libusb_free_device_list(devs, 1);
end1:
/* free bundle list */
bundleRelease(&plist);
if (return_value != STATUS_SUCCESS)
close_libusb_if_needed();
return return_value;
} /* OpenUSBByName */
/*****************************************************************************
*
* WriteUSB
*
****************************************************************************/
status_t WriteUSB(unsigned int reader_index, unsigned int length,
unsigned char *buffer)
{
int rv;
int actual_length;
char debug_header[] = "-> 121234 ";
(void)snprintf(debug_header, sizeof(debug_header), "-> %06X ",
(int)reader_index);
#ifdef ENABLE_ZLP
if (usbDevice[reader_index].ccid.zlp)
{ /* Zero Length Packet */
int dummy_length;
/* try to read a ZLP so transfer length = 0
* timeout of 10 ms */
(void)libusb_bulk_transfer(usbDevice[reader_index].dev_handle,
usbDevice[reader_index].bulk_in, NULL, 0, &dummy_length, 10);
}
#endif
DEBUG_XXD(debug_header, buffer, length);
rv = libusb_bulk_transfer(usbDevice[reader_index].dev_handle,
usbDevice[reader_index].bulk_out, buffer, length,
&actual_length, USB_WRITE_TIMEOUT);
if (rv < 0)
{
DEBUG_CRITICAL5("write failed (%d/%d): %d %s",
usbDevice[reader_index].bus_number,
usbDevice[reader_index].device_address, rv, libusb_error_name(rv));
if (LIBUSB_ERROR_NO_DEVICE == rv)
return STATUS_NO_SUCH_DEVICE;
return STATUS_UNSUCCESSFUL;
}
return STATUS_SUCCESS;
} /* WriteUSB */
/*****************************************************************************
*
* ReadUSB
*
****************************************************************************/
status_t ReadUSB(unsigned int reader_index, unsigned int * length,
unsigned char *buffer)
{
int rv;
int actual_length;
char debug_header[] = "<- 121234 ";
_ccid_descriptor *ccid_descriptor = get_ccid_descriptor(reader_index);
int duplicate_frame = 0;
read_again:
(void)snprintf(debug_header, sizeof(debug_header), "<- %06X ",
(int)reader_index);
rv = libusb_bulk_transfer(usbDevice[reader_index].dev_handle,
usbDevice[reader_index].bulk_in, buffer, *length,
&actual_length, usbDevice[reader_index].ccid.readTimeout);
if (rv < 0)
{
*length = 0;
DEBUG_CRITICAL5("read failed (%d/%d): %d %s",
usbDevice[reader_index].bus_number,
usbDevice[reader_index].device_address, rv, libusb_error_name(rv));
if (LIBUSB_ERROR_NO_DEVICE == rv)
return STATUS_NO_SUCH_DEVICE;
return STATUS_UNSUCCESSFUL;
}
*length = actual_length;
DEBUG_XXD(debug_header, buffer, *length);
#define BSEQ_OFFSET 6
if ((*length >= BSEQ_OFFSET)
&& (buffer[BSEQ_OFFSET] < *ccid_descriptor->pbSeq -1))
{
duplicate_frame++;
if (duplicate_frame > 10)
{
DEBUG_CRITICAL("Too many duplicate frame detected");
return STATUS_UNSUCCESSFUL;
}
DEBUG_INFO1("Duplicate frame detected");
goto read_again;
}
return STATUS_SUCCESS;
} /* ReadUSB */
/*****************************************************************************
*
* CloseUSB
*
****************************************************************************/
status_t CloseUSB(unsigned int reader_index)
{
/* device not opened */
if (usbDevice[reader_index].dev_handle == NULL)
return STATUS_UNSUCCESSFUL;
DEBUG_COMM3("Closing USB device: %d/%d",
usbDevice[reader_index].bus_number,
usbDevice[reader_index].device_address);
/* one slot closed */
(*usbDevice[reader_index].nb_opened_slots)--;
/* release the allocated ressources for the last slot only */
if (0 == *usbDevice[reader_index].nb_opened_slots)
{
struct usbDevice_MultiSlot_Extension *msExt;
DEBUG_COMM("Last slot closed. Release resources");
msExt = usbDevice[reader_index].multislot_extension;
/* If this is a multislot reader, close using the multislot stuff */
if (msExt)
{
/* terminate the interrupt waiter thread */
Multi_PollingTerminate(msExt);
/* wait for the thread to actually terminate */
pthread_join(msExt->thread_proc, NULL);
/* release the shared objects */
pthread_cond_destroy(&msExt->condition);
pthread_mutex_destroy(&msExt->mutex);
/* Deallocate the extension itself */
free(msExt);
/* Stop the slot */
usbDevice[reader_index].multislot_extension = NULL;
}
if (usbDevice[reader_index].ccid.gemalto_firmware_features)
free(usbDevice[reader_index].ccid.gemalto_firmware_features);
if (usbDevice[reader_index].ccid.sIFD_serial_number)
free(usbDevice[reader_index].ccid.sIFD_serial_number);
if (usbDevice[reader_index].ccid.sIFD_iManufacturer)
free(usbDevice[reader_index].ccid.sIFD_iManufacturer);
if (usbDevice[reader_index].ccid.arrayOfSupportedDataRates)
free(usbDevice[reader_index].ccid.arrayOfSupportedDataRates);
(void)libusb_release_interface(usbDevice[reader_index].dev_handle,
usbDevice[reader_index].interface);
(void)libusb_close(usbDevice[reader_index].dev_handle);
}
/* mark the resource unused */
usbDevice[reader_index].dev_handle = NULL;
usbDevice[reader_index].interface = 0;
close_libusb_if_needed();
return STATUS_SUCCESS;
} /* CloseUSB */
/*****************************************************************************
*
* get_ccid_descriptor
*
****************************************************************************/
_ccid_descriptor *get_ccid_descriptor(unsigned int reader_index)
{
return &usbDevice[reader_index].ccid;
} /* get_ccid_descriptor */
/*****************************************************************************
*
* get_ccid_device_descriptor
*
****************************************************************************/
const unsigned char *get_ccid_device_descriptor(const struct libusb_interface *usb_interface)
{
#ifdef O2MICRO_OZ776_PATCH
uint8_t last_endpoint;
#endif
if (54 == usb_interface->altsetting->extra_length)
return usb_interface->altsetting->extra;
if (0 != usb_interface->altsetting->extra_length)
{
/* If extra_length is zero, the descriptor might be at
* the end, but if it's not zero, we have a
* problem. */
DEBUG_CRITICAL2("Extra field has a wrong length: %d",
usb_interface->altsetting->extra_length);
return NULL;
}
#ifdef O2MICRO_OZ776_PATCH
/* Some devices, such as the Oz776, Reiner SCT and bludrive II
* report the device descriptor at the end of the endpoint
* descriptors; to support those, look for it at the end as well.
*/
last_endpoint = usb_interface->altsetting->bNumEndpoints-1;
if (usb_interface->altsetting->endpoint
&& usb_interface->altsetting->endpoint[last_endpoint].extra_length == 54)
return usb_interface->altsetting->endpoint[last_endpoint].extra;
#else
DEBUG_CRITICAL2("Extra field has a wrong length: %d",
usb_interface->altsetting->extra_length);
#endif
return NULL;
} /* get_ccid_device_descriptor */
/*****************************************************************************
*
* get_end_points
*
****************************************************************************/
static int get_end_points(struct libusb_config_descriptor *desc,
_usbDevice *usbdevice, int num)
{
int i;
int bEndpointAddress;
const struct libusb_interface *usb_interface;
usb_interface = get_ccid_usb_interface(desc, &num);
/*
* 3 Endpoints maximum: Interrupt In, Bulk In, Bulk Out
*/
for (i=0; i<usb_interface->altsetting->bNumEndpoints; i++)
{
/* interrupt end point (if available) */
if (usb_interface->altsetting->endpoint[i].bmAttributes
== LIBUSB_TRANSFER_TYPE_INTERRUPT)
{
usbdevice->interrupt =
usb_interface->altsetting->endpoint[i].bEndpointAddress;
continue;
}
if (usb_interface->altsetting->endpoint[i].bmAttributes
!= LIBUSB_TRANSFER_TYPE_BULK)
continue;
bEndpointAddress =
usb_interface->altsetting->endpoint[i].bEndpointAddress;
if ((bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
== LIBUSB_ENDPOINT_IN)
usbdevice->bulk_in = bEndpointAddress;
if ((bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
== LIBUSB_ENDPOINT_OUT)
usbdevice->bulk_out = bEndpointAddress;
}
return 0;
} /* get_end_points */
/*****************************************************************************
*
* get_ccid_usb_interface
*
****************************************************************************/
/*@null@*/ EXTERNAL const struct libusb_interface * get_ccid_usb_interface(
struct libusb_config_descriptor *desc, int *num)
{
const struct libusb_interface *usb_interface = NULL;
int i;
/* if multiple interfaces use the first one with CCID class type */
for (i = *num; i < desc->bNumInterfaces; i++)
{
/* CCID Class? */
if (desc->interface[i].altsetting->bInterfaceClass == 0xb
#ifdef ALLOW_PROPRIETARY_CLASS
|| (desc->interface[i].altsetting->bInterfaceClass == 0xff
&& 54 == desc->interface[i].altsetting->extra_length)
#endif
)
{
usb_interface = &desc->interface[i];
/* store the interface number for further reference */
*num = i;
break;
}
}
return usb_interface;
} /* get_ccid_usb_interface */
/*****************************************************************************
*
* ccid_check_firmware
*
****************************************************************************/
int ccid_check_firmware(struct libusb_device_descriptor *desc)
{
unsigned int i;
for (i=0; i<COUNT_OF(Bogus_firmwares); i++)
{
if (desc->idVendor != Bogus_firmwares[i].vendor)
continue;
if (desc->idProduct != Bogus_firmwares[i].product)
continue;
/* firmware too old and buggy */
if (desc->bcdDevice < Bogus_firmwares[i].firmware)
{
if (DriverOptions & DRIVER_OPTION_USE_BOGUS_FIRMWARE)
{
DEBUG_INFO3("Firmware (%X.%02X) is bogus! but you choosed to use it",
desc->bcdDevice >> 8, desc->bcdDevice & 0xFF);
return FALSE;
}
else
{
DEBUG_CRITICAL3("Firmware (%X.%02X) is bogus! Upgrade the reader firmware or get a new reader.",
desc->bcdDevice >> 8, desc->bcdDevice & 0xFF);
return TRUE;
}
}
}
/* by default the firmware is not bogus */
return FALSE;
} /* ccid_check_firmware */
/*****************************************************************************
*
* get_data_rates
*
****************************************************************************/
static unsigned int *get_data_rates(unsigned int reader_index,
struct libusb_config_descriptor *desc, int num)
{
int n, i, len;
unsigned char buffer[256*sizeof(int)]; /* maximum is 256 records */
unsigned int *uint_array;
/* See CCID 3.7.3 page 25 */
n = ControlUSB(reader_index,
0xA1, /* request type */
0x03, /* GET_DATA_RATES */
0x00, /* value */
buffer, sizeof(buffer));
/* we got an error? */
if (n <= 0)
{
DEBUG_INFO2("IFD does not support GET_DATA_RATES request: %d", n);
return NULL;
}
/* we got a strange value */
if (n % 4)
{
DEBUG_INFO2("Wrong GET DATA RATES size: %d", n);
return NULL;
}
/* allocate the buffer (including the end marker) */
n /= sizeof(int);
/* we do not get the expected number of data rates */
len = get_ccid_device_descriptor(get_ccid_usb_interface(desc, &num))[27]; /* bNumDataRatesSupported */
if ((n != len) && len)
{
DEBUG_INFO3("Got %d data rates but was expecting %d", n, len);
/* we got more data than expected */
if (n > len)
n = len;
}
uint_array = calloc(n+1, sizeof(uint_array[0]));
if (NULL == uint_array)
{
DEBUG_CRITICAL("Memory allocation failed");
return NULL;
}
/* convert in correct endianess */
for (i=0; i<n; i++)
{
uint_array[i] = dw2i(buffer, i*4);
DEBUG_INFO2("declared: %d bps", uint_array[i]);
}
/* end of array marker */
uint_array[i] = 0;
return uint_array;
} /* get_data_rates */
/*****************************************************************************
*
* ControlUSB
*
****************************************************************************/
int ControlUSB(int reader_index, int requesttype, int request, int value,
unsigned char *bytes, unsigned int size)
{
int ret;
DEBUG_COMM2("request: 0x%02X", request);
if (0 == (requesttype & 0x80))
DEBUG_XXD("send: ", bytes, size);
ret = libusb_control_transfer(usbDevice[reader_index].dev_handle,
requesttype, request, value, usbDevice[reader_index].interface,
bytes, size, usbDevice[reader_index].ccid.readTimeout);
if (ret < 0)
{
DEBUG_CRITICAL5("control failed (%d/%d): %d %s",
usbDevice[reader_index].bus_number,
usbDevice[reader_index].device_address, ret, libusb_error_name(ret));
return ret;
}
if (requesttype & 0x80)
DEBUG_XXD("receive: ", bytes, ret);
return ret;
} /* ControlUSB */
/*****************************************************************************
*
* Transfer is complete
*
****************************************************************************/
static void bulk_transfer_cb(struct libusb_transfer *transfer)
{
int *completed = transfer->user_data;
*completed = 1;
/* caller interprets results and frees transfer */
}
/*****************************************************************************
*
* InterruptRead
*
****************************************************************************/
int InterruptRead(int reader_index, int timeout /* in ms */)
{
int ret, actual_length;
int return_value = IFD_SUCCESS;
unsigned char buffer[8];
struct libusb_transfer *transfer;
int completed = 0;
/* Multislot reader: redirect to Multi_InterrupRead */
if (usbDevice[reader_index].multislot_extension != NULL)
return Multi_InterruptRead(reader_index, timeout);
DEBUG_PERIODIC2("before (%d)", reader_index);
transfer = libusb_alloc_transfer(0);
if (NULL == transfer)
return LIBUSB_ERROR_NO_MEM;
libusb_fill_bulk_transfer(transfer,
usbDevice[reader_index].dev_handle,
usbDevice[reader_index].interrupt, buffer, sizeof(buffer),
bulk_transfer_cb, &completed, timeout);
transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT;
ret = libusb_submit_transfer(transfer);
if (ret < 0) {
libusb_free_transfer(transfer);
DEBUG_CRITICAL2("libusb_submit_transfer failed: %s",
libusb_error_name(ret));
return IFD_COMMUNICATION_ERROR;
}
usbDevice[reader_index].polling_transfer = transfer;
while (!completed)
{
ret = libusb_handle_events_completed(ctx, &completed);
if (ret < 0)
{
if (ret == LIBUSB_ERROR_INTERRUPTED)
continue;
libusb_cancel_transfer(transfer);
while (!completed)
if (libusb_handle_events_completed(ctx, &completed) < 0)
break;
libusb_free_transfer(transfer);
DEBUG_CRITICAL2("libusb_handle_events failed: %s",
libusb_error_name(ret));
return IFD_COMMUNICATION_ERROR;
}
}
actual_length = transfer->actual_length;
ret = transfer->status;
usbDevice[reader_index].polling_transfer = NULL;
libusb_free_transfer(transfer);
DEBUG_PERIODIC3("after (%d) (%d)", reader_index, ret);
switch (ret)
{
case LIBUSB_TRANSFER_COMPLETED:
DEBUG_XXD("NotifySlotChange: ", buffer, actual_length);
break;
case LIBUSB_TRANSFER_TIMED_OUT:
break;
default:
/* if libusb_interrupt_transfer() times out we get EILSEQ or EAGAIN */
DEBUG_COMM4("InterruptRead (%d/%d): %d",
usbDevice[reader_index].bus_number,
usbDevice[reader_index].device_address, ret);
return_value = IFD_COMMUNICATION_ERROR;
}
return return_value;
} /* InterruptRead */
/*****************************************************************************
*
* Stop the async loop
*
****************************************************************************/
void InterruptStop(int reader_index)
{
struct libusb_transfer *transfer;
/* Multislot reader: redirect to Multi_InterrupStop */
if (usbDevice[reader_index].multislot_extension != NULL)
{
Multi_InterruptStop(reader_index);
return;
}
transfer = usbDevice[reader_index].polling_transfer;
usbDevice[reader_index].polling_transfer = NULL;
if (transfer)
{
int ret;
ret = libusb_cancel_transfer(transfer);
if (ret < 0)
DEBUG_CRITICAL2("libusb_cancel_transfer failed: %s",
libusb_error_name(ret));
}
} /* InterruptStop */
/*****************************************************************************
*
* Multi_PollingProc
*
****************************************************************************/
static void *Multi_PollingProc(void *p_ext)
{
struct usbDevice_MultiSlot_Extension *msExt = p_ext;
int rv, status, actual_length;
unsigned char buffer[CCID_INTERRUPT_SIZE];
struct libusb_transfer *transfer;
int completed;
DEBUG_COMM3("Multi_PollingProc (%d/%d): thread starting",
usbDevice[msExt->reader_index].bus_number,
usbDevice[msExt->reader_index].device_address);
rv = 0;
while (!msExt->terminated)
{
DEBUG_COMM3("Multi_PollingProc (%d/%d): waiting",
usbDevice[msExt->reader_index].bus_number,
usbDevice[msExt->reader_index].device_address);
transfer = libusb_alloc_transfer(0);
if (NULL == transfer)
{
rv = LIBUSB_ERROR_NO_MEM;
DEBUG_COMM2("libusb_alloc_transfer err %d", rv);
break;
}
libusb_fill_bulk_transfer(transfer,
usbDevice[msExt->reader_index].dev_handle,
usbDevice[msExt->reader_index].interrupt,
buffer, CCID_INTERRUPT_SIZE,
bulk_transfer_cb, &completed, 0); /* No timeout ! */
transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT;
rv = libusb_submit_transfer(transfer);
if (rv)
{
libusb_free_transfer(transfer);
DEBUG_COMM2("libusb_submit_transfer err %d", rv);
break;
}
usbDevice[msExt->reader_index].polling_transfer = transfer;
completed = 0;
while (!completed && !msExt->terminated)
{
rv = libusb_handle_events_completed(ctx, &completed);
if (rv < 0)
{
DEBUG_COMM2("libusb_handle_events err %d", rv);
if (rv == LIBUSB_ERROR_INTERRUPTED)
continue;
libusb_cancel_transfer(transfer);
while (!completed && !msExt->terminated)
{
if (libusb_handle_events_completed(ctx, &completed) < 0)
break;
}
break;
}
}
usbDevice[msExt->reader_index].polling_transfer = NULL;
if (rv < 0)
libusb_free_transfer(transfer);
else
{
int b, slot;
actual_length = transfer->actual_length;
status = transfer->status;
libusb_free_transfer(transfer);
switch (status)
{
case LIBUSB_TRANSFER_COMPLETED:
DEBUG_COMM3("Multi_PollingProc (%d/%d): OK",
usbDevice[msExt->reader_index].bus_number,
usbDevice[msExt->reader_index].device_address);
DEBUG_XXD("NotifySlotChange: ", buffer, actual_length);
/* log the RDR_to_PC_NotifySlotChange data */
slot = 0;
for (b=0; b<actual_length-1; b++)
{
int s;
/* 4 slots per byte */
for (s=0; s<4; s++)
{
/* 2 bits per slot */
int slot_status = ((buffer[1+b] >> (s*2)) & 3);
const char *present, *change;
present = (slot_status & 1) ? "present" : "absent";
change = (slot_status & 2) ? "status changed" : "no change";
DEBUG_COMM3("slot %d status: %d",
s + b*4, slot_status);
DEBUG_COMM3("ICC %s, %s", present, change);
}
slot += 4;
}
break;
case LIBUSB_TRANSFER_TIMED_OUT:
DEBUG_COMM3("Multi_PollingProc (%d/%d): Timeout",
usbDevice[msExt->reader_index].bus_number,
usbDevice[msExt->reader_index].device_address);
break;
default:
/* if libusb_interrupt_transfer() times out
* we get EILSEQ or EAGAIN */
DEBUG_COMM4("Multi_PollingProc (%d/%d): %d",
usbDevice[msExt->reader_index].bus_number,
usbDevice[msExt->reader_index].device_address,
status);
}
/* Tell other slots that there's a new interrupt buffer */
DEBUG_COMM3("Multi_PollingProc (%d/%d): Broadcast to slot(s)",
usbDevice[msExt->reader_index].bus_number,
usbDevice[msExt->reader_index].device_address);
/* Lock the mutex */
pthread_mutex_lock(&msExt->mutex);
/* Set the status and the interrupt buffer */
msExt->status = status;
memset(msExt->buffer, 0, sizeof msExt->buffer);
memcpy(msExt->buffer, buffer, actual_length);
/* Broadcast the condition and unlock */
pthread_cond_broadcast(&msExt->condition);
pthread_mutex_unlock(&msExt->mutex);
}
}
msExt->terminated = TRUE;
if (rv < 0)
{
DEBUG_CRITICAL4("Multi_PollingProc (%d/%d): error %d",
usbDevice[msExt->reader_index].bus_number,
usbDevice[msExt->reader_index].device_address, rv);
}
/* Wake up the slot threads so they will exit as well */
/* Lock the mutex */
pthread_mutex_lock(&msExt->mutex);
/* Set the status and fill-in the interrupt buffer */
msExt->status = 0;
memset(msExt->buffer, 0xFF, sizeof msExt->buffer);
/* Broadcast the condition */
pthread_cond_broadcast(&msExt->condition);
/* Unlock */
pthread_mutex_unlock(&msExt->mutex);
/* Now exit */
DEBUG_COMM3("Multi_PollingProc (%d/%d): Thread terminated",
usbDevice[msExt->reader_index].bus_number,
usbDevice[msExt->reader_index].device_address);
pthread_exit(NULL);
return NULL;
} /* Multi_PollingProc */
/*****************************************************************************
*
* Multi_PollingTerminate
*
****************************************************************************/
static void Multi_PollingTerminate(struct usbDevice_MultiSlot_Extension *msExt)
{
struct libusb_transfer *transfer;
if (msExt && !msExt->terminated)
{
msExt->terminated = TRUE;
transfer = usbDevice[msExt->reader_index].polling_transfer;
if (transfer)
{
int ret;
ret = libusb_cancel_transfer(transfer);
if (ret < 0)
DEBUG_CRITICAL2("libusb_cancel_transfer failed: %d", ret);
}
}
} /* Multi_PollingTerminate */
/*****************************************************************************
*
* Multi_InterruptRead
*
****************************************************************************/
static int Multi_InterruptRead(int reader_index, int timeout /* in ms */)
{
struct usbDevice_MultiSlot_Extension *msExt;
unsigned char buffer[CCID_INTERRUPT_SIZE];
struct timespec cond_wait_until;
struct timeval local_time;
int rv, status, interrupt_byte, interrupt_mask;
msExt = usbDevice[reader_index].multislot_extension;
/* When stopped, return 0 so IFDHPolling will return IFD_NO_SUCH_DEVICE */
if ((msExt == NULL) || msExt->terminated)
return 0;
DEBUG_PERIODIC3("Multi_InterruptRead (%d), timeout: %d ms",
reader_index, timeout);
/* Select the relevant bit in the interrupt buffer */
interrupt_byte = (usbDevice[reader_index].ccid.bCurrentSlotIndex / 4) + 1;
interrupt_mask = 0x02 << (2 * (usbDevice[reader_index].ccid.bCurrentSlotIndex % 4));
/* Wait until the condition is signaled or a timeout occurs */
gettimeofday(&local_time, NULL);
cond_wait_until.tv_sec = local_time.tv_sec;
cond_wait_until.tv_nsec = local_time.tv_usec * 1000;
cond_wait_until.tv_sec += timeout / 1000;
cond_wait_until.tv_nsec += 1000000 * (timeout % 1000);
again:
pthread_mutex_lock(&msExt->mutex);
rv = pthread_cond_timedwait(&msExt->condition, &msExt->mutex,
&cond_wait_until);
if (0 == rv)
{
/* Retrieve interrupt buffer and request result */
memcpy(buffer, msExt->buffer, sizeof buffer);
status = msExt->status;
}
else
if (rv == ETIMEDOUT)
status = LIBUSB_TRANSFER_TIMED_OUT;
else
status = -1;
/* Don't forget to unlock the mutex */
pthread_mutex_unlock(&msExt->mutex);
/* When stopped, return 0 so IFDHPolling will return IFD_NO_SUCH_DEVICE */
if (msExt->terminated)
return 0;
/* Not stopped */
if (status == LIBUSB_TRANSFER_COMPLETED)
{
if (0 == (buffer[interrupt_byte] & interrupt_mask))
{
DEBUG_PERIODIC2("Multi_InterruptRead (%d) -- skipped", reader_index);
goto again;
}
DEBUG_PERIODIC2("Multi_InterruptRead (%d), got an interrupt", reader_index);
}
else
{
DEBUG_PERIODIC3("Multi_InterruptRead (%d), status=%d", reader_index, status);
}
return status;
} /* Multi_InterruptRead */
/*****************************************************************************
*
* Multi_InterruptStop
*
****************************************************************************/
static void Multi_InterruptStop(int reader_index)
{
struct usbDevice_MultiSlot_Extension *msExt;
int interrupt_byte, interrupt_mask;
msExt = usbDevice[reader_index].multislot_extension;
/* Already stopped ? */
if ((NULL == msExt) || msExt->terminated)
return;
DEBUG_PERIODIC2("Stop (%d)", reader_index);
interrupt_byte = (usbDevice[reader_index].ccid.bCurrentSlotIndex / 4) + 1;
interrupt_mask = 0x02 << (2 * (usbDevice[reader_index].ccid.bCurrentSlotIndex % 4));
pthread_mutex_lock(&msExt->mutex);
/* Broacast an interrupt to wake-up the slot's thread */
msExt->buffer[interrupt_byte] |= interrupt_mask;
pthread_cond_broadcast(&msExt->condition);
pthread_mutex_unlock(&msExt->mutex);
} /* Multi_InterruptStop */
/*****************************************************************************
*
* Multi_CreateFirstSlot
*
****************************************************************************/
static struct usbDevice_MultiSlot_Extension *Multi_CreateFirstSlot(int reader_index)
{
struct usbDevice_MultiSlot_Extension *msExt;
/* Allocate a new extension buffer */
msExt = malloc(sizeof(struct usbDevice_MultiSlot_Extension));
if (NULL == msExt)
return NULL;
/* Remember the index */
msExt->reader_index = reader_index;
msExt->terminated = FALSE;
msExt->status = 0;
msExt->transfer = NULL;
/* Create mutex and condition object for the interrupt polling */
pthread_mutex_init(&msExt->mutex, NULL);
pthread_cond_init(&msExt->condition, NULL);
/* create the thread in charge of the interrupt polling */
pthread_create(&msExt->thread_proc, NULL, Multi_PollingProc, msExt);
return msExt;
} /* Multi_CreateFirstSlot */
/*****************************************************************************
*
* Multi_CreateNextSlot
*
****************************************************************************/
static struct usbDevice_MultiSlot_Extension *Multi_CreateNextSlot(int physical_reader_index)
{
/* Take the extension buffer from the main slot */
return usbDevice[physical_reader_index].multislot_extension;
} /* Multi_CreateNextSlot */