[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