Blame src/ccid_usb.c

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