Hello,

As requested by "many", here is a copy of my latest blog message on
http://ludovicrousseau.blogspot.com/
The formatting is much nicer in the blog.

Here is the PCSC sample in C language I promised in PC/SC sample in
different languages.

C Source code

#ifdef WIN32
#undef UNICODE
#endif

#include
#include

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

#endif

#ifdef WIN32
static char *pcsc_stringify_error(LONG rv)
{

 static char out[20];
 sprintf_s(out, sizeof(out), "0x%08X", rv);

 return out;
}
#endif

#define CHECK(f, rv) \
 if (SCARD_S_SUCCESS != rv) \
 { \
  printf(f ": %s\n", pcsc_stringify_error(rv)); \
  return -1; \
 }

int main(void)
{
 LONG rv;

 SCARDCONTEXT hContext;
 LPTSTR mszReaders;
 SCARDHANDLE hCard;
 DWORD dwReaders, dwActiveProtocol, dwRecvLength;

 SCARD_IO_REQUEST pioSendPci;
 BYTE pbRecvBuffer[256];
 BYTE cmd1[] = { 0x00, 0xA4, 0x04, 0x00, 0x0A, 0xA0,
  0x00, 0x00, 0x00, 0x62, 0x03, 0x01, 0x0C, 0x06, 0x01 };
 BYTE cmd2[] = { 0x00, 0x00, 0x00, 0x00 };

 unsigned int i;

 rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext);
 CHECK("SCardEstablishContext", rv)

#ifdef SCARD_AUTOALLOCATE
 dwReaders = SCARD_AUTOALLOCATE;

 rv = SCardListReaders(hContext, NULL, (LPTSTR)&mszReaders, &dwReaders);
 CHECK("SCardListReaders", rv)
#else
 rv = SCardListReaders(hContext, NULL, NULL, &dwReaders);
 CHECK("SCardListReaders", rv)

 mszReaders = calloc(dwReaders, sizeof(char));
 rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders);
 CHECK("SCardListReaders", rv)
#endif
 printf("reader name: %s\n", mszReaders);

 rv = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED,
  SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol);
 CHECK("SCardConnect", rv)

 switch(dwActiveProtocol)
 {
  case SCARD_PROTOCOL_T0:
   pioSendPci = *SCARD_PCI_T0;
   break;

  case SCARD_PROTOCOL_T1:
   pioSendPci = *SCARD_PCI_T1;
   break;
 }
 dwRecvLength = sizeof(pbRecvBuffer);
 rv = SCardTransmit(hCard, &pioSendPci, cmd1, sizeof(cmd1),
  NULL, pbRecvBuffer, &dwRecvLength);
 CHECK("SCardTransmit", rv)

 printf("response: ");
 for(i=0; i<dwRecvLength; i++)
  printf("%02X ", pbRecvBuffer[i]);
 printf("\n");

 dwRecvLength = sizeof(pbRecvBuffer);
 rv = SCardTransmit(hCard, &pioSendPci, cmd2, sizeof(cmd2),
  NULL, pbRecvBuffer, &dwRecvLength);
 CHECK("SCardTransmit", rv)

 printf("response: ");
 for(i=0; i<dwRecvLength; i++)
  printf("%02X ", pbRecvBuffer[i]);
 printf("\n");

 rv = SCardDisconnect(hCard, SCARD_LEAVE_CARD);
 CHECK("SCardDisconnect", rv)

#ifdef SCARD_AUTOALLOCATE
 rv = SCardFreeMemory(hContext, mszReaders);
 CHECK("SCardFreeMemory", rv)

#else
 free(mszReaders);
#endif

 rv = SCardReleaseContext(hContext);

 CHECK("SCardReleaseContext", rv)

 return 0;
}


Makefile

# Linux
PCSC_CFLAGS := $(shell pkg-config --cflags libpcsclite)
LDFLAGS := $(shell pkg-config --libs libpcsclite)

# Mac OS X
#PCSC_CFLAGS := -framework PCSC

CFLAGS += $(PCSC_CFLAGS)

sample: sample.c

clean:
 rm -f sample


Outputs
The output is "Hello world!" which is in hex:

$ echo -n 'Hello world!' | xxd -g 1
0000000: 48 65 6c 6c 6f 20 77 6f 72 6c 64 21   Hello world!


Linux and Mac OS X


reader name: Gemplus GemPC Twin 00 00
response: 90 00
response: 48 65 6C 6C 6F 20 77 6F 72 6C 64 21 90 00


Windows

D:\Documents and Settings\lroussea\My Documents\Visual Studio 2008\Projects\
sample\Debug> sample.exe
reader name: Gemplus USB Smart Card Reader 0
response: 90 00
response: 48 65 6C 6C 6F 20 77 6F 72 6C 64 21 90 00


Lessons learned

Reader name

The reader names are different on different plate forms. Linux and Mac
OS X use the same PC/SC driver and the same (mostly) pcsc-lite so the
name of an identical reader is the same. But Windows uses a different
driver with a different PC/SC reader name.

Do not use the PC/SC reader name to identify a specific reader if you
want to be portable.

SCardTransmit

pioRecvPci argument may be NULL (as in the sample) but shall not be
left uninitialised on Windows. Otherwise SCardTransmit returns an
error 0xE. This error code does not look like a WinSCard error code
and is not documented in the SCardTransmit() API AFAIK.

UNICODE

If UNICODE is defined under Windows then the reader name is in UTF-16
and not easy to manipulate in a portable way. But I am not a Windows
expert.

Unix uses UTF-8 so the name is the same in ASCII and UTF-8.

SCARD_AUTOALLOCATE

Mac OS X does not (yet) support memory (auto) allocation in the PC/SC
layer. So you have to use a double call mechanism. One first call to
get the size to allocate and one second call with a buffer allocated
at the correct size.

In the example the memory auto allocation is used to get the list of
connected readers. We see here that the C language is very low level
regarding memory allocation.

Memory allocated by PC/SC has to be free-ed by PC/SC using SCardFreeMemory()

PCSC framework

Mac OS X uses Frameworks instead of classical Unix libraries. The
difference (for our example) is the header files are not stored in
/usr/include/ but in
/System/Library/Frameworks/PCSC.framework/Headers/. You do not specify
the header path to the compiler using -Ipath> but use -framework PCSC
instead. The compiler (gcc) takes care to find and use the correct
framework.


pcsc_stringify_error()

pcsc_stringify_error() is a function specific to pcsc-lite. So we have
to either not use it at all in a portable source code or re-implement
a version on Windows. I decided to rewrite a limited version on
Windows.


wintypes.h

Mac OS X does not use the exact same API definition than Windows or
Linux for the WinSCard functions. Only standards types are used
instead of Windows ones. For example:


int32_t SCardConnect(SCARDCONTEXT hContext,
        const char *szReader,
        uint32_t dwShareMode,
        uint32_t dwPreferredProtocols,
        LPSCARDHANDLE phCard,
        uint32_t *pdwActiveProtocol);


instead of


LONG SCardConnect(SCARDCONTEXT hContext,
 LPCSTR szReader,
 DWORD dwShareMode,
 DWORD dwPreferredProtocols,
 LPSCARDHANDLE phCard,
 LPDWORD pdwActiveProtocol);


The advantage is that the types used on Mac OS X are clearly defined
by inttypes.h.

For example ULONG is defined as

typedef unsigned long ULONG;

but the long type from the C-language is different on Windows and Linux.

Linux (and Mac OS X) are LLP64 systems but Windows is a LP64 system.
See 64-bit. So on Linux a long is 64-bits on a 64-bits CPU and on
Windows a long is 32-bits on a 64-bits CPU.

To avoid problems Apple decided to explicitly fix the size using uint32_t.

-- 
 Dr. Ludovic Rousseau
_______________________________________________
Muscle mailing list
[email protected]
http://lists.drizzle.com/mailman/listinfo/muscle

Reply via email to