WritingDrivers – OpenCT

# Writing a simple card reader driver for OpenCT

Using an editor like anjuta, kdevelop or similar is handy since it allows you to quickly inspect data types definitions wherever they are defined in a project. It is beyond the intent of this document to show you how to do it, but I remember that using the import functionalities of the editor allowed me to get up to speed quickly.

Download an openct release, openct-0.6.6 in the rest of this document, and unpack it:

    tar -xzvf openct-0.6.6.tar.gz

Set a shell environment variable to be the directory. In my case:

    export OPENCT_SRC = $HOME/src/openct-0.6.6

Go to the directory where all other card reader drivers are:

    cd $OPENCT_SRC/src/ifd

Create the file that will hold the driver source code with your editor of choice:

    emacs ifd-wbeiuu.c

And add what follows:

    #include "internal.h"

    static int wbeiuu_open(ifd_reader_t * reader, const char *device_name)
    {
       ifd_device_t *dev;
       ifd_debug(1, "%s:%d wbeiuu_open()", __FILE__, __LINE__);

       reader->name = "Test driver. For illustration purposes only";
       reader->nslots = 1;
       if (!(dev = ifd_device_open(device_name)))
    	return -1;

       if (ifd_device_type(dev) != IFD_DEVICE_TYPE_USB) {
    	ct_error("test driver: device %s is not a USB device", device_name);
    	ifd_device_close(dev);
    	return -1;
       }

       reader->device = dev;
       dev->timeout = 1000;

       ifd_debug(1, "%s:%d Checkpoint", __FILE__, __LINE__);
       return 0;

    }

    static int wbeiuu_close(ifd_reader_t * reader)
    { ifd_debug(1, "%s:%d wbeiuu_close()", __FILE__, __LINE__); return 0; }

    static int wbeiuu_activate(ifd_reader_t * reader)
    { ifd_debug(1, "%s:%d wbeiuu_activate()", __FILE__, __LINE__); return 0; }

    static int wbeiuu_deactivate(ifd_reader_t * reader)
    { ifd_debug(1, "%s:%d wbeiuu_deactivate()", __FILE__, __LINE__); return 0; }

    static int wbeiuu_change_parity(ifd_reader_t * reader, int parity)
    { ifd_debug(1, "%s:%d wbeiuu_change_parity()", __FILE__, __LINE__); return 0; }

    static int wbeiuu_change_speed(ifd_reader_t * reader,
                                   unsigned int speed)
    { ifd_debug(1, "%s:%d wbeiuu_change_speed()", __FILE__, __LINE__); return 0; }

    static int wbeiuu_card_reset(ifd_reader_t * reader, int slot, void *atr,
                                 size_t atr_len)
    { ifd_debug(1, "%s:%d wbeiuu_card_reset()", __FILE__, __LINE__); return 0; }

    static int wbeiuu_card_status(ifd_reader_t * reader, int slot,
                                  int *status)
    { ifd_debug(1, "%s:%d wbeiuu_card_status()", __FILE__, __LINE__); return 0; }

    static int wbeiuu_send(ifd_reader_t * reader, unsigned int dad,
                           const unsigned char *buffer, size_t len)
    { ifd_debug(1, "%s:%d wbeiuu_send()", __FILE__, __LINE__); return 0; }

    static int wbeiuu_recv(ifd_reader_t * reader, unsigned int dad,
                           unsigned char *buffer, size_t len, long timeout)
    { ifd_debug(1, "%s:%d wbeiuu_recv()", __FILE__, __LINE__); return 0; }

    static struct ifd_driver_ops wbeiuu_driver;

    void ifd_wbeiuu_register(void)
    {
            wbeiuu_driver.open = wbeiuu_open;
            wbeiuu_driver.close = wbeiuu_close;
            wbeiuu_driver.activate = wbeiuu_activate;
            wbeiuu_driver.deactivate = wbeiuu_deactivate;
            wbeiuu_driver.card_reset = wbeiuu_card_reset;
            wbeiuu_driver.card_status = wbeiuu_card_status;
            wbeiuu_driver.change_parity = wbeiuu_change_parity;
            wbeiuu_driver.change_speed = wbeiuu_change_speed;
            wbeiuu_driver.send = wbeiuu_send;
            wbeiuu_driver.recv = wbeiuu_recv;
            ifd_driver_register("wbeiuu", &wbeiuu_driver);
    }

Basically a driver is a structure whose members are the functions you need to implement for your card reader. In this case we just added some tracing code that will help us see how the functions are being executed. We will see later how to code a function that does actually something.

Add the driver registration call to $OPENCT_SRC/src/ifd/init.c

            ifd_towitoko_register();
            ifd_wbeiuu_register();
            /* ccid last */
            ifd_ccid_register();

Add the function prototype in $OPENCT_SRC/src/ifd/internal.h in order to avoid compiler warnings and errors:

           extern void ifd_towitoko_register(void);
           extern void ifd_wbeiuu_register(void);

Unless you add the file to be compiled in the proper Makefile.in it will be ignored whe building openct. Therefore you must edit $OPENCT_SRC/src/ifd/Makefile.am and modify it accordingly:

    libifd_la_SOURCES = \
            apdu.c atr.c checksum.c conf.c ctbcs.c device.c driver.c \
            init.c locks.c manager.c modules.c pcmcia.c pcmcia-block.c process.c protocol.c \
            reader.c serial.c usb.c usb-descriptors.c utils.c \
            \
            ifd-acr30u.c ifd-cardman.c ifd-ccid.c ifd-cm4000.c ifd-egate.c \
            ifd-etoken.c ifd-etoken64.c ifd-eutron.c ifd-gempc.c ifd-ikey2k.c \
            ifd-ikey3k.c ifd-kaan.c ifd-pertosmart1030.c ifd-smartboard.c \
            ifd-towitoko.c cardman.h \
            ifd-wbeiuu.c\
            proto-gbp.c proto-sync.c proto-t0.c proto-t1.c \
            proto-trans.c \
            \
            sys-sunray.c sys-solaris.c sys-bsd.c sys-linux.c sys-null.c \
            \
            ria.c

If your card reader is a usb device like the wbeiuu is then you need the USB Vendor and Product ID (in my case 0x104f and 0x0004 respectively). The lsusb -v command comes handy here to get that information. This is required in order to add some configuration information that will tell openct to react upon plugin the card reader in the system. In my case I did so adding the following lines to the end of $OPENCT_SRC/etc/openct.conf.in:

    driver pertosmart1030 {
            ids = {
                    usb:072f/0001,
                    usb:072f/8009,
            };
    };
    driver wbeiuu {
           ids = {
                   usb:104f/0004,
           };
    };

Notice that here the string "wbeiuu" must be the same that the first parameter of ifd_driver_register() in ifd-wbeiuu.c

$OPENCT_SRC/etc/openct.usermap

     # pertosmart1038
     openct               0x0003      0x072f   0x9000    0x0000       0x0000       0x00         0x00            0x00            0x0b            0x00               0x00               0x00000000
     openct               0x0003      0x072f   0x90d0    0x0000       0x0000       0x00         0x00            0x00            0x0b            0x00               0x00               0x00000000
    # wbeiuu
    openct               0x0003      0x104f   0x0004    0x0000       0x0000       0x00         0x00            0x00            0x0b         0x00               0x00               0x00000000

And $OPENCT_SRC/etc/openct.udev

    SYSFS{idVendor}=="072f", SYSFS{idProduct}=="9000", RUN+="/etc/hotplug/usb/openct
    SYSFS{idVendor}=="072f", SYSFS{idProduct}=="90d0", RUN+="/etc/hotplug/usb/openct
    +# wbeiuu
    +SYSFS{idVendor}=="104f", SYSFS{idProduct}=="0004", RUN+="/etc/hotplug/usb/openct

Go back to the base directory, update makefiles, rebuild and install (for the latter you will very likely need to be root):

    cd $OPENCT_SRC
    aclocal
    autoconf
    automake
    ./configure --prefix=/usr/local/openct-0.6.6
    make
    make install

It is useful to follow the QuickStart guidelines for installing openct

Let's see if it works. Run:

    /etc/init.d/openct start
    /usr/local/openct-0.6.6/sbin/ifdhandler -d -d -d -d -d -H wbeiuu /proc/bus/usb/001/005

Customized for your current usb setup. My configuration above was usb bus 1, device 5 but you should definitely check /proc/bus/usb/devices and the output of lsusb.

Which should print something like:

    Debug: ifd_open: trying to open wbeiuu@/proc/bus/usb/001/005
    Debug: wbeiuu_open: ifd-wbeiuu.c:6
    Segmentation fault

An wonderfully successful but absolutely useless card reader driver. That is, the driver actually works but it lack the logic to perform the communication between the usb device and the host (hence the crash).

To make it do something useful one must actually implement the communication mechanisms between the host computer and the card reader. It is quite easy if you know what functions provided by openct to use when accessing USB devices.

    ifd_device_open(); // grabs USB devices and handles for you.

    ifd_usb_control(); // used to send USB control messages to the card reader.

    ifd_sysdep_usb_bulk(); // is used for both sending and receiving byte streams to
                           // and from the USB devices. The USB endpoint used defines
                           // the direction in the communication.

Feel free to take a look at the code of the files $OPENCT_SRC/src/ifd/ifd-*.c for further inspiration.