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 with root
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.