New CCID 1.4.0 and card movement notification mechanism
Card movement detection in the ancient times
Before the mechanism I will describe in this blog post was implemented and used, the only way pcsc-lite detected a card movement (card insertion or removal) was to poll the driver every 200ms to ask if a card was present or not in the reader and to compare with the previous state.
AdvantagesEvery driver (i.e. the old ones) implements the IFDHICCPresence function:
This function returns the status of the card inserted in the reader/slot specified by Lun.
The driver is then continuously asking the reader. This is host CPU "intensive" and will prevent the CPU to go in sleep state to limit its power consumption. You can use the powertop tool to see that. This is BAD.
PerformancesAnother problem is that the pcscd is notified up to 200ms after the card status has changed. So if your application is waiting for a card insertion you can waste up to 200ms between the card insertion and the application being notified.
libccid 1.4.04 days ago I released a new version 1.4.0 of my CCID driver (libccid).
Changes1.4.0 - 4 August 2010, Ludovic Rousseau
- add support of Kingtrust Multi-Reader, Dectel CI692, Todos CX00,
C3PO LTC36, ACS AET65, Broadcom 5880, Tianyu Smart Card Reader,
Gemalto Hybrid Smartcard Reader
- Add support of the SCM SDI 010 again. At least the contact
interface can be used.
- Use libusb-1.0 instead of libusb-0.1
- add support of
TAG_IFD_STOP_POLLING_THREADand use of the asynchronous libusb API to be able to stop a transfer.
- Request pcsc-lite 1.6.2 minimum (instead of 1.6.0) to have
- The O2MICRO OZ776 patch (for OZ776, OZ776_7772, REINER_SCT and BLUDRIVEII_CCID) is no more supported with libusb-1.0
- correctly get the IFSC from the ATR (ATR parsing was not always correct)
- some minor bugs removed
The change that will interest us here is the support of
TAG_IFD_STOP_POLLING_THREAD. This is used so that pcscd can ask the driver to stop its polling function.
But the support of
TAG_IFD_STOP_POLLING_THREADhas only be included in pcsc-lite 1.6.2. This is why you need pcsc-lite 1.6.2 to compile libccid 1.4.0.
pcsc-lite 1.6.2The same day of the libcid 1.4.0 release I also released pcsc-lite 1.6.2.
Changespcsc-lite-1.6.2: Ludovic Rousseau
4 August 2010
- implement a "Forced suicide" mechanism. After 3 Ctrl-C without much reaction from pcscd (in fact the drivers) we force the suicide. Sometimes libusb is blocked in a kind of dead-lock and kill -9 was the only option.
- Add support of
TAG_IFD_STOP_POLLING_THREADto request the stop of the driver polling function.
- Avoid a division by 0. Closes [#312555] "simclist bug in pcsc-lite"
pcscdis started by
libpcsclitethen close all file handles except stdin, stdout and stderr so that pcscd does not confiscate resources allocated by the application
- in case of auto exit create a new session so that Ctrl-C on the application will not also quit pcscd
src/hotplug_libusb.c: port from libusb-0.1 to libusb-1.0
- default configuration is now
- fix crash with empty config dir
src/PCSC/winscard.h: Remove definitions of
- some other minor improvements and bug corrections
Card movement notificationHow does the card movement notification works with the CCID protocol?
USB interrupt end pointSome readers (but not all) provides an interrupt endpoint at the USB level. This endpoint is used to signal a card movement to the host even if the host does not send a CCID command to be notified. The host just has to read the interrupt endpoint.
From the CCID specification 1.1 § 5.2.3 Interrupt-IN Endpoint:
The interrupt pipe is mandatory for a CCID that supports ICC insertion/removal. It is optional for a CCID with ICCs that are always inserted and are not removable. If there is an Interrupt-In endpoint, then the
RDR_to_PC_NotifySlotChangemessage is required and all other messages are optional.
RDR_to_PC_NotifySlotChange messageThe first byte is
bMessageTypeand its value is 0x50 indicating
The next bytes are
This field is reported on byte granularity.
The size is ( 2 bits * number of slots ) rounded up to the nearest byte.
Each slot has 2 bits. The least significant bit reports the current state of the slot (0b = no ICC present, 1b = ICC present). The most significant bit reports whether the slot has changed state since the last
RDR_to_PC_NotifySlotChangemessage was sent (0b = no change, 1b = change).
If no slot exists for a given location, the field returns 00b in those 2 bits.
Example: A 3 slot CCID reports a single byte with the following format:
Bit 0 = Slot 0 current state
Bit 1 = Slot 0 changed status
Bit 2 = Slot 1 current state
Bit 3 = Slot 1 changed status
Bit 4 = Slot 2 current state
Bit 5 = Slot 2 changed status
Bit 6 = 0b
Bit 7 = 0b
In short, and if the reader has only one slot, you will receive
0x50 0x03when a card is removed and
0x50 0x02when a card is inserted.
libusb-0.1The use of an interrupt end point is already supported in libusb-0.1 but I had many issues when using this feature. The main issue is that libusb-0.1 calls are synchronous and it is not possible to (cleanly) interrupt a usb_interrupt_read call. It is then impossible to cleanly stop the driver when pcscd exits.
One hack I used is to make the usb_interrupt_read call timeout after 2 seconds. But it is still a kind of polling, even if the period is now 2 seconds instead of 200 ms.
libusb-1.0libusb-1.0 has a new API and, in particular, an asynchronous API. Using the asynchronous API it is possible to cancel an ongoing call. This is exactly what is done when
TAG_IFD_STOP_POLLING_THREADis requested by pcscd to the driver.
But libusb-1.0 is relatively new and has bugs. The latest stable version 1.0.8 is not yet bug free and I found some bugs in particular cases. Bugs have been reported to the libusb project and most are already corrected.