Thursday, May 20, 2010

What is in a PC/SC reader name?

USB serial number


In a recent discussion [Muscle] pcsc-lite and reader serial number? it was asked if it is possible to get the USB serial number of a USB reader using PC/SC. The PC/SC API does not provide such a service but pcsc-lite includes the USB serial number in the PC/SC name of the reader.

Example with a SCM SCR331 reader:
  • On GNU/Linux
    SCM SCR 331 (80000F3A) 00 00
  • On Mac OS X (Snow Leopard)
    SCM SCR 331 00 00
  • On Windows XP
    SCM Microsystems Inc. SCR33x USB Smart Card Reader 0
You can see that the reader name is not standardised.

You can also see that on GNU/Linux you have a "(80000F3A)". This is the serial number of the USB device. You can also see the device serial number in the file /proc/bus/usb/devices
T:  Bus=02 Lev=02 Prnt=41 Port=03 Cnt=02 Dev#= 47 Spd=12  MxCh= 0
D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=16 #Cfgs=  1
P:  Vendor=04e6 ProdID=e001 Rev= 5.25
S:  Manufacturer=SCM Microsystems Inc.
S:  Product=SCRx31 USB Smart Card Reader
S:  SerialNumber=80000F3A
C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=100mA
I:* If#= 0 Alt= 0 #EPs= 3 Cls=0b(scard) Sub=00 Prot=00 Driver=usbfs
E:  Ad=01(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=83(I) Atr=03(Int.) MxPS=  16 Ivl=16ms

Interface name


The Gemalto Prox-DU reader is a composite USB device with two CCID readers inside (and a HID interface but we don't use it). One reader is for contact cards and the other reader is for contactless cards.

  • On GNU/Linux
    Gemalto Prox-DU [Prox-DU Contact_09A00795] (09A00795) 00 00
    Gemalto Prox-DU [Prox-DU Contactless_09A00795] (09A00795) 01 00
  • On Mac OS X (Snow Leopard)
    Gemalto Prox-DU 00 00
    Gemalto Prox-DU 00 01
    Gemalto Prox-DU 00 01
    We have two identical lines because of a bug in pcsc-lite on Mac OS X. This bug is already fixed on the smartcardservices.macosforge.org project but not yet deployed in a Mac OS X upgrade.
  • On Windows XP
    Gemalto Prox-DU Contact_09A00795 0
    Gemalto Prox-DU Contactless_09A00795 1

File /proc/bus/usb/devices:
T:  Bus=02 Lev=02 Prnt=41 Port=03 Cnt=02 Dev#= 49 Spd=12  MxCh= 0
D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=32 #Cfgs=  1
P:  Vendor=08e6 ProdID=5503 Rev= 1.00
S:  Manufacturer=Gemalto
S:  Product=Prox Dual USB PC LinkReader
S:  SerialNumber=09A00795
C:* #Ifs= 3 Cfg#= 1 Atr=80 MxPwr=200mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=03(HID  ) Sub=00 Prot=00 Driver=usbhid
E:  Ad=83(I) Atr=03(Int.) MxPS=   8 Ivl=254ms
I:* If#= 1 Alt= 0 #EPs= 3 Cls=0b(scard) Sub=00 Prot=00 Driver=(none)
E:  Ad=01(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=86(I) Atr=03(Int.) MxPS=   8 Ivl=24ms
I:* If#= 2 Alt= 0 #EPs= 3 Cls=0b(scard) Sub=00 Prot=00 Driver=(none)
E:  Ad=04(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=85(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=87(I) Atr=03(Int.) MxPS=   8 Ivl=24ms

The interface name can be found in the /sys/bus/usb/devices subsystem. But it is not easy to identify the correct USB device (the 2-1.4:1.* in the example).

$ ls /sys/bus/usb/devices/2-1.4:1.*
/sys/bus/usb/devices/2-1.4:1.0:
bAlternateSetting   bInterfaceSubClass  ep_83      subsystem
bInterfaceClass     bNumEndpoints       interface  uevent
bInterfaceNumber    bus                 modalias   usb_endpoint:usbdev2.49_ep83
bInterfaceProtocol  driver              power      usb:hiddev0

/sys/bus/usb/devices/2-1.4:1.1:
bAlternateSetting   bNumEndpoints  interface  usb_endpoint:usbdev2.49_ep01
bInterfaceClass     bus            modalias   usb_endpoint:usbdev2.49_ep82
bInterfaceNumber    ep_01          power      usb_endpoint:usbdev2.49_ep86
bInterfaceProtocol  ep_82          subsystem
bInterfaceSubClass  ep_86          uevent

/sys/bus/usb/devices/2-1.4:1.2:
bAlternateSetting   bNumEndpoints  interface  usb_endpoint:usbdev2.49_ep04
bInterfaceClass     bus            modalias   usb_endpoint:usbdev2.49_ep85
bInterfaceNumber    ep_04          power      usb_endpoint:usbdev2.49_ep87
bInterfaceProtocol  ep_85          subsystem
bInterfaceSubClass  ep_87          uevent

$ cat /sys/bus/usb/devices/2-1.4:1.*/interface
Prox-DU HID_09A00795
Prox-DU Contactless_09A00795
Prox-DU Contact_09A00795

It is easier to use the hal-device command:
0: udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795_usbraw'
  linux.sysfs_path = '/sys/class/usb_device/usbdev2.51'  (string)
  info.subsystem = 'usb_device'  (string)
  info.parent = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795'  (string)
  info.product = 'USB Raw Device Access'  (string)
  info.udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795_usbraw'  (string)
  usbraw.device = '/dev/bus/usb/002/051'  (string)
  info.category = 'usbraw'  (string)
  info.capabilities = { 'usbraw' } (string list)
  linux.hotplug_type = 2  (0x2)  (int)
  linux.subsystem = 'usb_device'  (string)
  linux.device_file = '/dev/bus/usb/002/051'  (string)

1: udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795_if2'
  usb.speed = 12  (double)
  usb.device_protocol = 0  (0x0)  (int)
  usb.version = 2  (double)
  usb.vendor_id = 2278  (0x8e6)  (int)
  usb.is_self_powered = false  (bool)
  usb.product_id = 21763  (0x5503)  (int)
  info.subsystem = 'usb'  (string)
  linux.hotplug_type = 2  (0x2)  (int)
  info.product = 'USB Chip/Smartcard Interface'  (string)
  usb.can_wake_up = false  (bool)
  linux.subsystem = 'usb'  (string)
  info.udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795_if2'  (string)
  usb.vendor = 'Gemplus'  (string)
  usb.bus_number = 2  (0x2)  (int)
  usb.product = 'USB Chip/Smartcard Interface'  (string)
  usb.linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1.3/2-1.3:1.2'  (string)
  usb.device_revision_bcd = 256  (0x100)  (int)
  usb.configuration_value = 1  (0x1)  (int)
  usb.max_power = 200  (0xc8)  (int)
  usb.interface.number = 2  (0x2)  (int)
  usb.interface.class = 11  (0xb)  (int)
  usb.interface.subclass = 0  (0x0)  (int)
  usb.interface.protocol = 0  (0x0)  (int)
  linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1.3/2-1.3:1.2'  (string)
  info.parent = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795'  (string)
  usb.num_configurations = 1  (0x1)  (int)
  usb.interface.description = 'Prox-DU Contact_09A00795'  (string)
  usb.num_ports = 0  (0x0)  (int)
  usb.num_interfaces = 3  (0x3)  (int)
  usb.linux.device_number = 51  (0x33)  (int)
  usb.device_class = 0  (0x0)  (int)
  usb.serial = '09A00795'  (string)
  usb.device_subclass = 0  (0x0)  (int)

2: udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795_if1'
  usb.speed = 12  (double)
  usb.device_protocol = 0  (0x0)  (int)
  usb.version = 2  (double)
  usb.vendor_id = 2278  (0x8e6)  (int)
  usb.is_self_powered = false  (bool)
  usb.product_id = 21763  (0x5503)  (int)
  info.subsystem = 'usb'  (string)
  linux.hotplug_type = 2  (0x2)  (int)
  info.product = 'USB Chip/Smartcard Interface'  (string)
  usb.can_wake_up = false  (bool)
  linux.subsystem = 'usb'  (string)
  info.udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795_if1'  (string)
  usb.vendor = 'Gemplus'  (string)
  usb.bus_number = 2  (0x2)  (int)
  usb.product = 'USB Chip/Smartcard Interface'  (string)
  usb.linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1.3/2-1.3:1.1'  (string)
  usb.device_revision_bcd = 256  (0x100)  (int)
  usb.configuration_value = 1  (0x1)  (int)
  usb.max_power = 200  (0xc8)  (int)
  usb.interface.number = 1  (0x1)  (int)
  usb.interface.class = 11  (0xb)  (int)
  usb.interface.subclass = 0  (0x0)  (int)
  usb.interface.protocol = 0  (0x0)  (int)
  linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1.3/2-1.3:1.1'  (string)
  info.parent = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795'  (string)
  usb.num_configurations = 1  (0x1)  (int)
  usb.interface.description = 'Prox-DU Contactless_09A00795'  (string)
  usb.num_ports = 0  (0x0)  (int)
  usb.num_interfaces = 3  (0x3)  (int)
  usb.linux.device_number = 51  (0x33)  (int)
  usb.device_class = 0  (0x0)  (int)
  usb.serial = '09A00795'  (string)
  usb.device_subclass = 0  (0x0)  (int)

3: udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795_if0'
  usb.speed = 12  (double)
  usb.device_protocol = 0  (0x0)  (int)
  usb.version = 2  (double)  usb.vendor_id = 2278  (0x8e6)  (int)
  usb.is_self_powered = false  (bool)
  usb.product_id = 21763  (0x5503)  (int)  info.subsystem = 'usb'  (string)
  linux.hotplug_type = 2  (0x2)  (int)
  info.product = 'USB HID Interface'  (string)
  usb.can_wake_up = false  (bool)
  linux.subsystem = 'usb'  (string)
  info.udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795_if0'  (s
tring)  usb.vendor = 'Gemplus'  (string)
  usb.bus_number = 2  (0x2)  (int)
  usb.product = 'USB HID Interface'  (string)
  usb.linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1.3/2-
1.3:1.0'  (string)
  usb.device_revision_bcd = 256  (0x100)  (int)
  usb.configuration_value = 1  (0x1)  (int)  usb.max_power = 200  (0xc8)  (int)
  usb.interface.number = 0  (0x0)  (int)
  usb.interface.class = 3  (0x3)  (int)  usb.interface.subclass = 0  (0x0)  (int)
  usb.interface.protocol = 0  (0x0)  (int)
  linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1.3/2-1.3:
1.0'  (string)
  info.parent = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795'  (st
ring)
  usb.num_configurations = 1  (0x1)  (int)
  info.linux.driver = 'usbhid'  (string)
  usb.num_ports = 0  (0x0)  (int)
  usb.num_interfaces = 3  (0x3)  (int)
  usb.linux.device_number = 51  (0x33)  (int)
  usb.device_class = 0  (0x0)  (int)
  usb.serial = '09A00795'  (string)
  usb.device_subclass = 0  (0x0)  (int)

4: udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795'
  usb_device.num_configurations = 1  (0x1)  (int)
  usb_device.num_ports = 0  (0x0)  (int)
  usb_device.num_interfaces = 3  (0x3)  (int)
  usb_device.linux.device_number = 51  (0x33)  (int)
  linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1.3'  (string)
  info.subsystem = 'usb_device'  (string)
  usb_device.device_class = 0  (0x0)  (int)
  usb_device.serial = '09A00795'  (string)
  info.parent = '/org/freedesktop/Hal/devices/usb_device_4cc_1122_noserial'  (string)
  usb_device.device_subclass = 0  (0x0)  (int)
  info.vendor = 'Gemplus'  (string)
  info.product = 'Prox Dual USB PC LinkReader'  (string)
  usb_device.speed = 12  (double)
  usb_device.device_protocol = 0  (0x0)  (int)
  usb_device.version = 2  (double)
  info.udi = '/org/freedesktop/Hal/devices/usb_device_8e6_5503_09A00795'  (string)
  info.linux.driver = 'usb'  (string)
  usb_device.vendor_id = 2278  (0x8e6)  (int)
  usb_device.is_self_powered = false  (bool)
  usb_device.product_id = 21763  (0x5503)  (int)
  usb_device.can_wake_up = false  (bool)
  usb_device.vendor = 'Gemplus'  (string)
  usb_device.bus_number = 2  (0x2)  (int)
  linux.hotplug_type = 2  (0x2)  (int)
  usb_device.product = 'Prox Dual USB PC LinkReader'  (string)
  linux.subsystem = 'usb'  (string)
  linux.device_file = '/dev/bus/usb/002/051'  (string)
  usb_device.linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1.3'  (string)
  usb_device.device_revision_bcd = 256  (0x100)  (int)
  usb_device.configuration_value = 1  (0x1)  (int)
  usb_device.max_power = 200  (0xc8)  (int)

Remarks


Gemalto decided to include the serial number in the interface name. It is to be able to differentiate (on Windows) two identical readers connected. The serial number is also available on a sticker on the back of the reader so it is easy to tell which PC/SC reader is which physical reader.

The SCM SCR331 reader also has a serial number on a sticker on the back but the written serial number has nothing to do with the USB serial number.

Mac OS X does not include the serial number nor the interface name in the PC/SC name. Maybe one day I will implement these features in pcsc-lite on Mac OS X.

pcsc-lite reader name format

name [interface] (serial) index slot
This format is used by pcsc-lite on Linux if you use libhal (default with pcsc-lite 1.6.0). If you still use libusb the interface name is not included.

The name part comes from the driver Info.plist file for USB readers or the reader.conf file for serial readers.

The interface part comes from the USB layer (usb.interface.description HAL entry)

The serial part comes from the USB layer (usb.serial HAL entry)

The index part is an internal index in PC/SC. It is used to differentiate two identical readers at the PC/SC layer for readers without serial numbers (the vast majority).

The slot part is specific to pcsc-lite and is the slot index in multi-slot readers. Multi-slot readers are not common at all and this information is not really useful for the PC/SC application. I think we still have this number for historical reasons.

Remarks


On Linux the GemProx DU is seen as two CCID readers. So the PC/SC reader names differentiate on the index part.

On Mac OS X pcsc-lite does not supports composite devices. So the CCID driver emulates a single reader with 2 slots. That is why the PC/SC reader names differentiate on the slot part and have the same index part.

Conclusion


PC/SC reader names are very different from an OS to another. It is very dangerous to hard code a reader name in an application and just pass it to SCardConnect(). You should always use SCardListReaders() first.


Flattr this