2017-12-08 18:39:30 +00:00
|
|
|
/*
|
|
|
|
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)
|
|
|
|
{
|
2018-09-19 07:43:58 +00:00
|
|
|
libusb_free_config_descriptor(config_desc);
|
2017-12-08 18:39:30 +00:00
|
|
|
(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)
|
|
|
|
{
|
2018-09-19 07:43:58 +00:00
|
|
|
libusb_free_config_descriptor(config_desc);
|
2017-12-08 18:39:30 +00:00
|
|
|
(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)
|
|
|
|
{
|
2018-09-19 07:43:58 +00:00
|
|
|
libusb_free_config_descriptor(config_desc);
|
2017-12-08 18:39:30 +00:00
|
|
|
/* 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)
|
|
|
|
{
|
2018-09-19 07:43:58 +00:00
|
|
|
libusb_free_config_descriptor(config_desc);
|
2017-12-08 18:39:30 +00:00
|
|
|
(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))
|
|
|
|
{
|
2018-09-19 07:43:58 +00:00
|
|
|
libusb_free_config_descriptor(config_desc);
|
2017-12-08 18:39:30 +00:00
|
|
|
(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;
|
|
|
|
|
2018-09-19 07:43:58 +00:00
|
|
|
libusb_free_config_descriptor(config_desc);
|
2017-12-08 18:39:30 +00:00
|
|
|
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
|
2018-09-19 07:43:58 +00:00
|
|
|
|
|
|
|
/* free bundle list */
|
|
|
|
bundleRelease(&plist);
|
|
|
|
|
2017-12-08 18:39:30 +00:00
|
|
|
/* 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 */
|
2018-09-19 07:43:58 +00:00
|
|
|
DEBUG_COMM4("InterruptRead (%d/%d): %d",
|
2017-12-08 18:39:30 +00:00
|
|
|
usbDevice[reader_index].bus_number,
|
2018-09-19 07:43:58 +00:00
|
|
|
usbDevice[reader_index].device_address, ret);
|
2017-12-08 18:39:30 +00:00
|
|
|
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)
|
2018-09-19 07:43:58 +00:00
|
|
|
DEBUG_CRITICAL2("libusb_cancel_transfer failed: %s",
|
|
|
|
libusb_error_name(ret));
|
2017-12-08 18:39:30 +00:00
|
|
|
}
|
|
|
|
} /* 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 */
|
|
|
|
|