SCardControl is the PC/SC function that makes it possible for the application to invoke ‘proprietary’ functions, implemented either in the PC/SC reader itself (CSB6, Prox’N’Roll PC/SC, EasyFinger or CrazyWriter) , or in its driver running on the PC, or in the PC/SC middleware.
The prototype is:
LONG SCardControl( SCARDHANDLE hCard, DWORD dwControlCode, LPCVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned );
(see http://pcsclite.alioth.debian.org/api/group__API.html for the PCSC-Lite documentation, and http://msdn.microsoft.com/en-us/library/windows/desktop/aa379474%28v=vs.85%29.aspx for Microsoft’s version).
The lpInbuffer / nInBufferSize parameters hold the command buffer that will be processed by either target -reader, driver, or PC/SC middleware-.
SpringCard PC/SC Readers do provide a few ‘proprietary’ functions (called ‘Escape commands’ in the USB CCID specification). For instance, an application would send the command 58 1E 01 00
to switch the reader’s red LED ON. A question remains: what must the value of dwControlCode be, when the application wants to send the command right to the reader, bypassing both the PC/SC middleware and the driver? The answer varies with the operating system, which doesn’t help implementing portable code.
Differences between Windows and PCSC-Lite implementations
Windows
In Microsoft’s CCID driver (http://msdn.microsoft.com/en-us/library/windows/hardware/gg487509.aspx), the dwControlCode for the Escape command is defined as follows:
#define IOCTL_CCID_ESCAPE SCARD_CTL_CODE(3500)
SpringCard PC/SC Readers follow the CCID specification. SpringCard’s CCID driver (SDD480) uses the same dwControlCode as Microsoft’s.
Therefore, on Windows, the application would switch the red LED on this way:
#include <windows.h> #include <winscard.h> #define IOCTL_CCID_ESCAPE SCARD_CTL_CODE(3500) (...) const BYTE SET_RED_LED_ON[4] = { 0x58, 0x1E, 0x01, 0x00 }; SCARDCONTEXT hContext; SCARDHANDLE hCard; DWORD dwProtocol; BYTE abResponse[256]; DWORD dwRespLen; LONG rc; (...) /* Instanciate the winscard.dll library */ rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); if (rc != SCARD_S_SUCCESS) { /* TODO: handle error */ } /* Get a direct connection to the reader (we don't need a card to send Escape commands) */ rc = SCardConnect(hContext, szReader, SCARD_SHARE_DIRECT, 0, &hCard, &dwProtocol); if (rc != SCARD_S_SUCCESS) { /* TODO: handle error */ } /* Send the command */ rc = SCardControl(hCard, IOCTL_CCID_ESCAPE, SET_RED_LED_ON, sizeof(SET_RED_LED_ON), abResponse, sizeof(abResponse), &dwRespLen); if (rc != SCARD_S_SUCCESS) { /* TODO: handle error */ } SCardDisconnect(hCard, SCARD_LEAVE_CARD); SCardReleaseContext(hContext);
Important notes:
Working with MS’ CCID driver
With Microsoft’s CCID driver, the Escape feature is disabled by default.
In order to send or receive an Escape command to a reader, the DWORD registry value EscapeCommandEnable must be added and set to a non-zero value under one of the following keys.
HKLM\SYSTEM\CCS\Enum\USB\Vid*Pid*\*\Device Parameters
(prior to Windows 7).
HKLM\SYSTEM\CCS\Enum\USB\Vid*Pid*\*\Device Parameters\WUDFUsbccidDriver
(Windows 7 and later).
This is clearly explained in the Developer’s Manual for every PC/SC reader.
Using SpringCard’s SDD480 CCID driver shall be preferred.
Early versions of SDD480
Branch -Ax of SpringCard’s SDD480 CCID driver uses a different value for the dwControlCode parameter.
#define IOCTL_CCID_ESCAPE SCARD_CTL_CODE(2048)
Switching to the latest version of SpringCard’s SDD480 CCID driver (branch -Bx and onwards) shall be preferred.
Linux, MacOS and other Unix*
In Ludovic Rousseau’s open-source CCID driver (http://pcsclite.alioth.debian.org/ccid.html), the dwControlCode for the Escape command is defined as follows:
#define IOCTL_CCID_ESCAPE SCARD_CTL_CODE(1)
(See http://anonscm.debian.org/viewvc/pcsclite/trunk/Drivers/ccid/SCARDCONTOL.txt?view=markup for details)
Therefore, when working with PCSC-Lite, the application would switch the red LED on this way:
#ifdef __APPLE__ #include <pcsc/winscard.h> #include <pcsc/wintypes.h> #else #include <winscard.h> #endif #define IOCTL_CCID_ESCAPE SCARD_CTL_CODE(1) (...) const BYTE SET_RED_LED_ON[4] = { 0x58, 0x1E, 0x01, 0x00 }; SCARDCONTEXT hContext; SCARDHANDLE hCard; DWORD dwProtocol; BYTE abResponse[256]; DWORD dwRespLen; LONG rc; (...) /* Instanciate the winscard.dll library */ rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); if (rc != SCARD_S_SUCCESS) { /* TODO: handle error */ } /* Get a direct connection to the reader (we don't need a card to send Escape commands) */ rc = SCardConnect(hContext, szReader, SCARD_SHARE_DIRECT, 0, &hCard, &dwProtocol); if (rc != SCARD_S_SUCCESS) { /* TODO: handle error */ } /* Send the command */ rc = SCardControl(hCard, IOCTL_CCID_ESCAPE, SET_RED_LED_ON, sizeof(SET_RED_LED_ON), abResponse, sizeof(abResponse), &dwRespLen); if (rc != SCARD_S_SUCCESS) { /* TODO: handle error */ } SCardDisconnect(hCard, SCARD_LEAVE_CARD); SCardReleaseContext(hContext);
Enabling the Escape commands
With this CCID driver, the Escape feature is also disabled by default.
You’ll have to edit the CCID driver’s Info.plist file to enable this feature:
- Open
/usr/local/lib/pcsc/drivers/ccid/Info.plist
in edit mode withroot
priviledge, - Locate the line <key>ifdDriverOptions</key>,
- The following line is typically <string>0000</string>,
- Define the new value: <string>0001</string>,
- Save the file and restard pcscd.
(More details on http://ludovicrousseau.blogspot.fr/2011/10/featureccidesccommand.html)
Writing portable code
The idea is only to use a #ifdef
to compile the correct value:
#ifdef WIN32 #define IOCTL_CCID_ESCAPE SCARD_CTL_CODE(3500) #else #define IOCTL_CCID_ESCAPE SCARD_CTL_CODE(1) #endif
Java
The javax.smartcardio API provides Java methods that are stricly bound to the underlying PC/SC subsystem. The Card.transmitControlCommand method is the wrapper for SCardControl. The prototype is coherent:
public abstract byte[] transmitControlCommand( int controlCode, byte[] command) throws CardException
Now the same question: what must the value of controlCode be? The answer is short: it depends on the PC/SC stack! SCARD_CTL_CODE(3500)
for Windows, and SCARD_CTL_CODE(1)
for PCSC-Lite. But with another difference: the macro SCARD_CTL_CODE is not computed the same way between both systems!
As a consequence, the Java application must detect the OS, and compute the controlCode parameter accordingly.
Same example to switch the red LED on:
import javax.smartcardio.*; (...) static boolean isWindows() { String os_name = System.getProperty("os.name").toLowerCase(); if (os_name.indexOf("windows") > -1) return true; return false; } static int SCARD_CTL_CODE(int code) { int ioctl; if (isWindows()) { ioctl = (0x31 < < 16 | (code) << 2); } else { ioctl = 0x42000000 + (code); } return ioctl; } static int IOCTL_CCID_ESCAPE() { if (isWindows()) { return SCARD_CTL_CODE(3500); } else { return SCARD_CTL_CODE(1); } } static final byte[] SET_RED_LED_ON = { (byte) 0x58, (byte) 0x1E, (byte) 0x01, (byte) 0x00 }; (...) String readerName; /* Note that the reader's name vary with the OS too!!! */ if (isWindows()) readerName = "SpringCard Prox'N'Roll Contactless 0"; else readerName = "SpringCard Prox'N'Roll (00000000) 00 00"; CardTerminal terminal = CardTerminals.getTerminal(readerName); Card virtualCard = terminal.connect("DIRECT"); virtualCard.transmitControlCommand(IOCTL_CCID_ESCAPE(), SET_RED_LED_ON); virtualCard.disconnect(false);
Of course this code works only if the Escape feature is enable by the underlying CCID driver, as seen above.