Saturday, December 19, 2015

OS X El Capitan missing feature: SCardGetStatusChange() and "\\?PnP?\Notification"

This is part of the series: "OS X El Capitan and smart cards: known bugs".

SCardGetStatusChange() does not support "\\?PnP?\Notification"

SCardGetStatusChange() does not support the special reader name "\\?PnP?\Notification".

This is not a bug since this feature was never present in any PC/SC from Apple.

From the Doxygen documentation of pcsc-lite:
To wait for a reader event (reader added or removed) you may use the special reader name "\\?PnP?\Notification".
If a reader event occurs the state of this reader will change and the bit SCARD_STATE_CHANGED will be set.
With this feature it is possible to detect that a smart card reader has been added or removed to the system without polling with calls to SCardListReaders() in a loop.

This is used by my tools pcsc_scan from the pcsc-tools package.

See also

Apple bug report #23966225 "PC/SC SCardGetStatusChange() and "\\?PnP?\Notification""

Sample code

#include <stdio.h>
#ifdef __APPLE__
#include <PCSC/winscard.h>
#include <PCSC/wintypes.h>
#else
#include <winscard.h>
#endif

int main(void)
{
    SCARDCONTEXT hContext;
    LPSTR mszReaders;
    DWORD err = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL,
        &hContext);
    if (err != SCARD_S_SUCCESS)
    {
        printf("ScardEstablishedContext: 0x%08x\n",err);
        return -1;
    }

    SCARD_READERSTATE rgReaderStates[1];
    rgReaderStates[0].szReader = "\\\\?PnP?\\Notification";
    rgReaderStates[0].dwCurrentState = SCARD_STATE_UNAWARE;
    rgReaderStates[0].dwEventState = SCARD_STATE_UNKNOWN;

    err = SCardGetStatusChange(hContext, 10, rgReaderStates, 1);
    printf("SCardGetStatusChange: %s (0x%08X)\n", pcsc_stringify_error(err),
        err);
    printf("dwEventState: %X\n", rgReaderStates[0].dwEventState);

    if (rgReaderStates[0].dwEventState & SCARD_STATE_UNKNOWN)
        printf("\\\\?PnP?\\Notification is NOT supported\n");
    else
        printf("\\\\?PnP?\\Notification IS supported\n");

    SCardReleaseContext(hContext);

    return 0;
}

Result (on El Capitan)

$ CFLAGS="-framework PCSC" make main
cc -framework PCSC    main.c   -o main
$ ./main
SCardGetStatusChange: Command successful. (0x00000000)
dwEventState: 6
\\?PnP?\Notification is NOT supported
The returned value for dwEventState is 6 and correspond to SCARD_STATE_CHANGED (2) + SCARD_STATE_UNKNOWN (4). PC/SC indicates that the reader "\\?PnP?\Notification" is unknown.

Note that SCardGetStatusChange() returns SCARD_S_SUCCESS and also waited for the indicated time, here 10 ms but you can increase the value to make the test more explicit.
The command should have returned immediately (or have returned SCARD_E_TIMEOUT). Yes, it is a bug in the PC/SC layer.

Expected result (on Debian)

$ CFLAGS=`pkg-config --cflags libpcsclite` LDFLAGS=`pkg-config --libs libpcsclite` make main
cc -pthread -I/usr/include/PCSC    -lpcsclite    main.c   -o main
$ ./main
SCardGetStatusChange: Command timeout. (0x8010000A)
dwEventState: 0
\\?PnP?\Notification IS supported
Here the command returns after the delay has expired because no reader has been connected or removed. The returned value is SCARD_E_TIMEOUT.
If a reader is connected or removed during the delay (here 10 ms) the command would have indicated SCARD_STATE_CHANGED for this reader.

Known workaround

None known.
Maybe Apple will add support of "\\?PnP?\Notification" one day.