[Pcsclite-cvs-commit] r6678 - trunk/Drivers/ccid/src
ludovic.rousseau at free.fr
ludovic.rousseau at free.fr
Sun Jul 28 09:30:20 UTC 2013
Author: rousseau
Date: 2013-06-30 14:42:36 +0000 (Sun, 30 Jun 2013)
New Revision: 6678
Modified:
trunk/Drivers/ccid/src/ccid_usb.c
trunk/Drivers/ccid/src/ifdhandler.c
Log:
Add support of multi-slot readers
Thanks to Johann Dantant for the initial patch
Modified: trunk/Drivers/ccid/src/ccid_usb.c
===================================================================
--- trunk/Drivers/ccid/src/ccid_usb.c 2013-06-30 14:30:09 UTC (rev 6677)
+++ trunk/Drivers/ccid/src/ccid_usb.c 2013-06-30 14:42:36 UTC (rev 6678)
@@ -31,6 +31,8 @@
# endif
#include <libusb.h>
#include <stdlib.h>
+#include <pthread.h>
+#include <sys/time.h>
#include <ifdhandler.h>
#include "config.h"
@@ -62,6 +64,22 @@
/* #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;
@@ -88,11 +106,21 @@
/* 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);
@@ -435,6 +463,10 @@
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
@@ -628,6 +660,13 @@
}
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;
+
goto end;
}
}
@@ -789,8 +828,28 @@
/* 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);
+ }
+
if (usbDevice[reader_index].ccid.gemalto_firmware_features)
free(usbDevice[reader_index].ccid.gemalto_firmware_features);
@@ -1119,6 +1178,10 @@
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);
@@ -1193,6 +1256,13 @@
{
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)
@@ -1205,3 +1275,369 @@
}
} /* 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)
+ {
+ 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(ctx);
+ 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(ctx) < 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 */
+ pthread_mutex_lock(&msExt->mutex);
+ 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:
+ 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);
+
+ /* Stop the slot */
+ usbDevice[reader_index].multislot_extension = NULL;
+
+ 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 */
+
Modified: trunk/Drivers/ccid/src/ifdhandler.c
===================================================================
--- trunk/Drivers/ccid/src/ifdhandler.c 2013-06-30 14:30:09 UTC (rev 6677)
+++ trunk/Drivers/ccid/src/ifdhandler.c 2013-06-30 14:42:36 UTC (rev 6678)
@@ -506,10 +506,6 @@
ccid_desc = get_ccid_descriptor(reader_index);
- /* more than one slot is not supported */
- if (ccid_desc -> bMaxSlotIndex > 0)
- break;
-
/* CCID and not ICCD */
if ((PROTOCOL_CCID == ccid_desc -> bInterfaceProtocol)
/* 3 end points */
More information about the Pcsclite-cvs-commit
mailing list