SpringProxEX API - Working with more than one CSB at a time

Abstract

SpringCard API makes it easy to work with a CSB or other SpringProx family reader, using simple function calls.

One of the key aspects of SpringProx API is in the lack of pointer or handle to identify the device, as the API assumes that the application works with only one device at once. It some cases, this limitation must be overcomed, typically when one single PC-based application is in charge of numerous devicess.

This documents introduces « SpringProxEX » API, which make it possible to do so.

Audience

This document assumes that the reader has a good knowledge of SpringProx API. Always refer to SpringProx API documentation for details regarding function parameters, and usage.

Step-by-step guide to migrate native applications

In this chapter we'll see how a typicall application (C or C++) can switch from SpringProx API to SpringProxEX API.

Replace springprox header by springprox_ex

SpringProxEX API functions are protyped in springprox_ex.h. Replace C or C++ lines #include <sprinprox.h> by #include <sprinprox_ex.h> in your source code

Note that springprox_ex.h refers to springprox.h (e.g. includes it) for compatibility and convenience.

Create an instance for each device

Each CSB (or SpringProx compliant reader) will be associated to a distinct SPROX_INSTANCE object. Use SPROXx_CreateInstance function to instantiate the objects, as follow :

  /* Allocate 3 instances, to work with 3 CSB readers */
  SPROX_INSTANCE arInst[3];
  int i;
  
  for (i=0; i<3; i++)
  {
    arInst[i] = SPROXx_CreateInstance();
    if (arInst[i] == NULL)
    {
      /* Fatal error, raise an exception or leave the procedure here */
      return FALSE;
    }  
  }  

Get access to the devices

Just as you call SPROX_ReaderOpen to get access to your CSB, call SPROXx_ReaderOpen to get access (note the plural !) to the 2 or more readers.

There're three different ways to select the reader to be opened.

No explicit selection

In this case we use the « automatic detection » feature, where the library tries to connect to the first available reader. Detection is done in this order :

Here's the sample source code :

  SWORD rc;
  BOOL reader_found[3] = { FALSE };
  
  for (i=0; i<3; i++)
  {
    rc = SPROXx_ReaderOpen(arInst[i], NULL);
    if (rc == MI_OK)
    {
      /* OK, reader connected to this instance */
      reader_found[i] = TRUE;
    }  
  }  

The major advantage of this method is its ease of use : there's no need to store any configuration data related to a list of readers.

One disadvantage is the time taken by the discovery loop : for every existing serial communication device (COM1 to COM9) with no reader attached, SPROXx_ReaderOpen function will block for 2 to 4 seconds until returning an error.

Another disadvantage is that there's no guarantee regarding the order of the USB discovery loop  in other words, if you have 2 (or more) CSB readers connected to USB ports of your computer, you have no way to know which SpringProxEX instance is associated to which physical device.

Explicit selection through serial port

In this case the application has to provide a list of ports, the CSB are connected to. They can either be real serial ports or virtual ports (USB or PCMCIA/CompactFlash bridge).

Here's the sample source code :

  SWORD rc;
  BOOL reader_found[3] = { FALSE };
  
  /* To be provided by application */
  const char *reader_ports[3] = {
    "COM2",            // First device is connected to COM2
    "COM7",            // Second device is connected to COM7
    "\\\\.\\COM12"     // Third device is connected to COM12.
                       // Note that we MUST write \\.\COM12 (and double the anti-slash in C or C++)
                       // as Windows expects serial devices above 10 to be named this way.
  };
  
  for (i=0; i<3; i++)
  {
    rc = SPROXx_ReaderOpen(arInst[i], reader_ports[i]);
    if (rc == MI_OK)
    {
      /* OK, reader connected to this instance */
      reader_found[i] = TRUE;
    }  
  }  

The major advantage is that we know for sure the relationship between physical device and SpringProx EX instance.

For sure ? Well, it depends... Generally speaking, yes, but with USB devices it is possible to play with driver parameters (in device manager) and have the devices taking different virtual comm port numbers than expected.

Explicit selection through USB identifiers

This method is applicable only to

In this case the application has to provide the list of CSB identifiers (use ref_usbenum.exe from the SDK to get this list, or read the numbers from product's identification label).

Source code ref_usbenum.c is provided in the SDK, making it easy to add the enumeration process in your own application.

Here's the sample source code :

  SWORD rc;
  BOOL reader_found[3] = { FALSE };
  
  /* To be provided by application */
  const char *reader_idents[3] = {
    "USB:PA001234",    // Serial number of first device, prefixed with "USB:"
    "USB:PA005678",    // Serial number of second device, prefixed with "USB:"
    "USB:PA010155"     // Serial number of third device, prefixed with "USB:"
  };
  
  for (i=0; i<3; i++)
  {
    rc = SPROXx_ReaderOpen(arInst[i], reader_idents[i]);
    if (rc == MI_OK)
    {
      /* OK, reader connected to this instance */
      reader_found[i] = TRUE;
    }  
  }  

The major advantage is that we know for sure the relationship between physical device and SpringProx EX instance.

For sure ? Yes, for sure !

Work with the devices

Just as SpringProx API functions are called SPROX_..., SpringProxEX API functions are called SPROXx_.... Function prototypes are generally speaking identical, only first parameter SPROX_INSTANCE rInst must be provided to every function call.

Here's a few examples :

SpringProx SpringProxEX
      // LEDs : red is on, green is blinking
      SPROX_ControlLed(1, 2);    
    
      // LEDs : red is on, green is blinking
      SPROXx_ControlLed(rInst, 1, 2);            
    
      // Select an ISO 14443-A card
      SPROX_A_SelectAny(atq, snr, &snrlen, sak);
    
      // Select an ISO 14443-A card
      SPROXx_A_SelectAny(rInst, atq, snr, &snrlen, sak);
    
      // Halt the selected card
      SPROX_A_Halt();
    
      // Halt the selected card
      SPROXx_A_Halt(rInst);
    

As you can see, existing function SpringProx calls must be rewritten as SpringProxEX function calls. At the end of the migration process, every call to SPROX_... must have been replaced by its SPROXx_... equivalent.

Cleanup everyhing when exiting

Just as you created an instance for each device, then got access to it, the application must close access and free the instance before exiting.

Use SPROXx_ReaderClose and SPROXx_DestroyInstance to do so.

  /* Terminate 3 instances */
  for (i=0; i<3; i++)
  {  
    if (reader_active[i])
      SPROXx_ReaderClose(arInst[i]);
    SPROXx_DestroyInstance(arInst[i]);
    arInst[i] = NULL; // Safety line...
  }

Link with SpringProxEX API

Now that your source code has been modified to use SpringProxEX API, it's time to replace springprox.lib (that contains the entry points for springprox.dll) by springprox_ex.lib (that contains the entry points for springprox_ex.dll).

Don't forget to distribute springprox_ex.dll with your application, instead of springprox.dll.

A few hints and advices

Multi-threading and performance issue

SpringProxEX API is a thread safe library (a thread associated to instance arInst[0] will never block waiting for instance arInst[1] or arInst[2]). As with any multi-threaded application working with hardware, we can come to a bottleneck when the USB subsystem is heavily loaded. When performing active polling (loop over SPROXx_A_SelectAny typically), allow other threads to run by adding a delay in the loop :

  /* Instance is waiting for a card */
  SWORD rc;
  
  for (;;)
  {
    rc = SPROXx_A_SelectAny(rInst, atq, snr, &snrlen, sak);
    if (rc == MI_OK)
      break;   // Card found !!!

    Sleep(30); // Going to sleep mode explicitly gives other threads with the same priority
               // more chances to perform their "important" job quickly, where this thread is
               // still doing nothing important.
  }

The SPROX_INSTANCE type

SPROX_INSTANCE is an abstract data type, i.e. a pointer to an incomplete type typedef struct _SPROX_CTX_ST *SPROX_INSTANCE.

If your compiler doesn't allow this syntax, you may replace it by a #define SPROX_INSTANCE void*.

For other languages (VB, Delphi) or other environment (.NET typically), SPROX_INSTANCE must be a native pointer type (pointer for Delphi, System.IntPtr for .NET).