Logo Search packages:      
Sourcecode: pcsc-lite version File versions  Download package

hotplug_libusb.c

Go to the documentation of this file.
/*
 * MUSCLE SmartCard Development ( http://www.linuxnet.com )
 *
 * Copyright (C) 2001-2004
 *  David Corcoran <corcoran@linuxnet.com>
 * Copyright (C) 2003-2010
 *  Ludovic Rousseau <ludovic.rousseau@free.fr>
 * Copyright (C) 2003
 *  Toni Andjelkovic <toni@soth.at>
 * Copyright (C) 2003-2004
 *  Damien Sauveron <damien.sauveron@labri.fr>
 *
 * $Id: hotplug_libusb.c 5621 2011-02-21 15:05:38Z rousseau $
 */

/**
 * @file
 * @brief This provides a search API for hot pluggble devices.
 */

#include "config.h"
#ifdef HAVE_LIBUSB

#include <string.h>
#include <sys/types.h>
#include <stdio.h>
#include <dirent.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <libusb.h>
#include <pthread.h>

#include "misc.h"
#include "wintypes.h"
#include "pcscd.h"
#include "debuglog.h"
#include "parser.h"
#include "readerfactory.h"
#include "winscard_msg.h"
#include "sys_generic.h"
#include "hotplug.h"
#include "utils.h"

#undef DEBUG_HOTPLUG
#define ADD_SERIAL_NUMBER

/* format is "%d:%d:%d", bus_number, device_address, interface */
#define BUS_DEVICE_STRSIZE    10+1+10+1+10+1

#define READER_ABSENT         0
#define READER_PRESENT        1
#define READER_FAILED         2

#define FALSE                 0
#define TRUE                  1

/* we use the default libusb context */
#define ctx NULL

pthread_mutex_t usbNotifierMutex;

static pthread_t usbNotifyThread;
static int driverSize = -1;
static char AraKiriHotPlug = FALSE;
static int rescan_pipe[] = { -1, -1 };
extern int HPForceReaderPolling;

/* values of ifdCapabilities bits */
#define IFD_GENERATE_HOTPLUG 1

/**
 * keep track of drivers in a dynamically allocated array
 */
static struct _driverTracker
{
      unsigned int manuID;
      unsigned int productID;

      char *bundleName;
      char *libraryPath;
      char *readerName;
      int ifdCapabilities;
} *driverTracker = NULL;
#define DRIVER_TRACKER_SIZE_STEP 8

/**
 * keep track of PCSCLITE_MAX_READERS_CONTEXTS simultaneous readers
 */
static struct _readerTracker
{
      char status;
      char bus_device[BUS_DEVICE_STRSIZE];      /**< device name */
      char *fullName;   /**< full reader name (including serial number) */
} readerTracker[PCSCLITE_MAX_READERS_CONTEXTS];

static LONG HPAddHotPluggable(struct libusb_device *dev,
      struct libusb_device_descriptor desc,
      const char bus_device[], int interface,
      struct _driverTracker *driver);
static LONG HPRemoveHotPluggable(int reader_index);

static LONG HPReadBundleValues(void)
{
      LONG rv;
      DIR *hpDir;
      struct dirent *currFP = NULL;
      char fullPath[FILENAME_MAX];
      char fullLibPath[FILENAME_MAX];
      int listCount = 0;

      hpDir = opendir(PCSCLITE_HP_DROPDIR);

      if (hpDir == NULL)
      {
            Log1(PCSC_LOG_ERROR, "Cannot open PC/SC drivers directory: " PCSCLITE_HP_DROPDIR);
            Log1(PCSC_LOG_ERROR, "Disabling USB support for pcscd.");
            return -1;
      }

      /* allocate a first array */
      driverTracker = calloc(DRIVER_TRACKER_SIZE_STEP, sizeof(*driverTracker));
      if (NULL == driverTracker)
      {
            Log1(PCSC_LOG_CRITICAL, "Not enough memory");
            return -1;
      }
      driverSize = DRIVER_TRACKER_SIZE_STEP;

#define GET_KEY(key, values) \
      rv = LTPBundleFindValueWithKey(&plist, key, values); \
      if (rv) \
      { \
            Log2(PCSC_LOG_ERROR, "Value/Key not defined for " key " in %s", \
                  fullPath); \
            continue; \
      }

      while ((currFP = readdir(hpDir)) != 0)
      {
            if (strstr(currFP->d_name, ".bundle") != 0)
            {
                  unsigned int alias;
                  list_t plist, *values;
                  list_t *manuIDs, *productIDs, *readerNames;
                  char *libraryPath;
                  int ifdCapabilities;

                  /*
                   * The bundle exists - let's form a full path name and get the
                   * vendor and product ID's for this particular bundle
                   */
                  snprintf(fullPath, sizeof(fullPath), "%s/%s/Contents/Info.plist",
                        PCSCLITE_HP_DROPDIR, currFP->d_name);
                  fullPath[sizeof(fullPath) - 1] = '\0';

                  rv = bundleParse(fullPath, &plist);
                  if (rv)
                        continue;

                  /* get CFBundleExecutable */
                  GET_KEY(PCSCLITE_HP_LIBRKEY_NAME, &values)
                  libraryPath = list_get_at(values, 0);
                  (void)snprintf(fullLibPath, sizeof(fullLibPath),
                        "%s/%s/Contents/%s/%s",
                        PCSCLITE_HP_DROPDIR, currFP->d_name, PCSC_ARCH,
                        libraryPath);
                  fullLibPath[sizeof(fullLibPath) - 1] = '\0';

                  /* Get ifdCapabilities */
                  GET_KEY(PCSCLITE_HP_CPCTKEY_NAME, &values)
                  ifdCapabilities = strtol(list_get_at(values, 0), NULL, 16);

                  GET_KEY(PCSCLITE_HP_MANUKEY_NAME, &manuIDs)
                  GET_KEY(PCSCLITE_HP_PRODKEY_NAME, &productIDs)
                  GET_KEY(PCSCLITE_HP_NAMEKEY_NAME, &readerNames)

                  /* while we find a nth ifdVendorID in Info.plist */
                  for (alias=0; alias<list_size(manuIDs); alias++)
                  {
                        char *value;

                        /* variables entries */
                        value = list_get_at(manuIDs, alias);
                        driverTracker[listCount].manuID = strtol(value, NULL, 16);

                        value = list_get_at(productIDs, alias);
                        driverTracker[listCount].productID = strtol(value, NULL, 16);

                        driverTracker[listCount].readerName = strdup(list_get_at(readerNames, alias));

                        /* constant entries for a same driver */
                        driverTracker[listCount].bundleName = strdup(currFP->d_name);
                        driverTracker[listCount].libraryPath = strdup(fullLibPath);
                        driverTracker[listCount].ifdCapabilities = ifdCapabilities;

#ifdef DEBUG_HOTPLUG
                        Log2(PCSC_LOG_INFO, "Found driver for: %s",
                              driverTracker[listCount].readerName);
#endif
                        listCount++;
                        if (listCount >= driverSize)
                        {
                              int i;

                              /* increase the array size */
                              driverSize += DRIVER_TRACKER_SIZE_STEP;
#ifdef DEBUG_HOTPLUG
                              Log2(PCSC_LOG_INFO,
                                    "Increase driverTracker to %d entries", driverSize);
#endif
                              driverTracker = realloc(driverTracker,
                                    driverSize * sizeof(*driverTracker));
                              if (NULL == driverTracker)
                              {
                                    Log1(PCSC_LOG_CRITICAL, "Not enough memory");
                                    driverSize = -1;
                                    return -1;
                              }

                              /* clean the newly allocated entries */
                              for (i=driverSize-DRIVER_TRACKER_SIZE_STEP; i<driverSize; i++)
                              {
                                    driverTracker[i].manuID = 0;
                                    driverTracker[i].productID = 0;
                                    driverTracker[i].bundleName = NULL;
                                    driverTracker[i].libraryPath = NULL;
                                    driverTracker[i].readerName = NULL;
                                    driverTracker[i].ifdCapabilities = 0;
                              }
                        }
                  }
                  bundleRelease(&plist);
            }
      }

      driverSize = listCount;
      closedir(hpDir);

      rv = TRUE;
      if (driverSize == 0)
      {
            Log1(PCSC_LOG_INFO, "No bundle files in pcsc drivers directory: " PCSCLITE_HP_DROPDIR);
            Log1(PCSC_LOG_INFO, "Disabling USB support for pcscd");
            rv = FALSE;
      }
#ifdef DEBUG_HOTPLUG
      else
            Log2(PCSC_LOG_INFO, "Found drivers for %d readers", listCount);
#endif

      return rv;
}

static void HPRescanUsbBus(void)
{
      int i, j;
      char bus_device[BUS_DEVICE_STRSIZE];
      libusb_device **devs, *dev;
      ssize_t cnt;

      for (i=0; i < PCSCLITE_MAX_READERS_CONTEXTS; i++)
            /* clear rollcall */
            readerTracker[i].status = READER_ABSENT;

      cnt = libusb_get_device_list(ctx, &devs);
      if (cnt < 0)
      {
            Log1(PCSC_LOG_CRITICAL, "libusb_get_device_list() failed\n");
            return;
      }

      /* For each USB device */
      cnt = 0;
      while ((dev = devs[cnt++]) != NULL)
      {
            struct libusb_device_descriptor desc;
            struct libusb_config_descriptor *config_desc;
            uint8_t bus_number = libusb_get_bus_number(dev);
            uint8_t device_address = libusb_get_device_address(dev);

            int r = libusb_get_device_descriptor(dev, &desc);
            if (r < 0)
            {
                  Log3(PCSC_LOG_ERROR, "failed to get device descriptor for %d/%d",
                        bus_number, device_address);
                  continue;
            }

            r = libusb_get_active_config_descriptor(dev, &config_desc);
            if (r < 0)
            {
                  Log3(PCSC_LOG_ERROR, "failed to get device config for %d/%d",
                        bus_number, device_address);
                  continue;
            }

            /* check if the device is supported by one driver */
            for (i=0; i<driverSize; i++)
            {
                  if (driverTracker[i].libraryPath != NULL &&
                        desc.idVendor == driverTracker[i].manuID &&
                        desc.idProduct == driverTracker[i].productID)
                  {
                        int interface;

#ifdef DEBUG_HOTPLUG
                        Log3(PCSC_LOG_DEBUG, "Found matching USB device: %d:%d",
                              bus_number, device_address);
#endif

                        for (interface = 0; interface < config_desc->bNumInterfaces;
                              interface++)
                        {
                              int newreader;

                              /* A known device has been found */
                              snprintf(bus_device, BUS_DEVICE_STRSIZE, "%d:%d:%d",
                                     bus_number, device_address, interface);
                              bus_device[BUS_DEVICE_STRSIZE - 1] = '\0';
                              newreader = TRUE;

                              /* Check if the reader is a new one */
                              for (j=0; j<PCSCLITE_MAX_READERS_CONTEXTS; j++)
                              {
                                    if (strncmp(readerTracker[j].bus_device,
                                          bus_device, BUS_DEVICE_STRSIZE) == 0)
                                    {
                                          /* The reader is already known */
                                          readerTracker[j].status = READER_PRESENT;
                                          newreader = FALSE;
#ifdef DEBUG_HOTPLUG
                                          Log2(PCSC_LOG_DEBUG, "Refresh USB device: %s",
                                                bus_device);
#endif
                                          break;
                                    }
                              }

                              /* New reader found */
                              if (newreader)
                              {
                                    printf("POUET %d\n", config_desc->bNumInterfaces);
                                    if (config_desc->bNumInterfaces > 1)
                                          HPAddHotPluggable(dev, desc, bus_device,
                                                interface, &driverTracker[i]);
                                          else
                                          HPAddHotPluggable(dev, desc, bus_device,
                                                -1, &driverTracker[i]);
                              }
                        }
                  }
            }
      }

      /*
       * check if all the previously found readers are still present
       */
      for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
      {
            if ((readerTracker[i].status == READER_ABSENT) &&
                  (readerTracker[i].fullName != NULL))
                  HPRemoveHotPluggable(i);
      }

      if (AraKiriHotPlug)
      {
            int retval;

            for (i=0; i<driverSize; i++)
            {
                  /* free strings allocated by strdup() */
                  free(driverTracker[i].bundleName);
                  free(driverTracker[i].libraryPath);
                  free(driverTracker[i].readerName);
            }
            free(driverTracker);

            Log1(PCSC_LOG_INFO, "Hotplug stopped");
            pthread_exit(&retval);
      }

      /* free the libusb allocated list & devices */
      libusb_free_device_list(devs, 1);
}

static void HPEstablishUSBNotifications(int pipefd[2])
{
      int i, do_polling;
      char c = 42;      /* magic value */

      libusb_init(ctx);

      /* scan the USB bus for devices at startup */
      HPRescanUsbBus();

      /* signal that the initially connected readers are now visible */
      write(pipefd[1], &c, 1);
      close(pipefd[1]);

      /* if at least one driver do not have IFD_GENERATE_HOTPLUG */
      do_polling = FALSE;
      for (i=0; i<driverSize; i++)
            if (driverTracker[i].libraryPath)
                  if ((driverTracker[i].ifdCapabilities & IFD_GENERATE_HOTPLUG) == 0)
                  {
                        Log2(PCSC_LOG_INFO,
                              "Driver %s does not support IFD_GENERATE_HOTPLUG. Using active polling instead.",
                              driverTracker[i].bundleName);
                        if (HPForceReaderPolling < 1)
                              HPForceReaderPolling = 1;
                        break;
                  }

      if (HPForceReaderPolling)
      {
            Log2(PCSC_LOG_INFO,
                        "Polling forced every %d second(s)", HPForceReaderPolling);
            do_polling = TRUE;
      }

      if (do_polling)
      {
            while (!AraKiriHotPlug)
            {
                  SYS_Sleep(HPForceReaderPolling);
                  HPRescanUsbBus();
            }
      }
      else
      {
            char dummy;

            pipe(rescan_pipe);
            while (read(rescan_pipe[0], &dummy, sizeof(dummy)) > 0)
            {
                  Log1(PCSC_LOG_INFO, "Reload serial configuration");
                  HPRescanUsbBus();
#ifdef USE_SERIAL
                  RFReCheckReaderConf();
#endif
                  Log1(PCSC_LOG_INFO, "End reload serial configuration");
            }
            close(rescan_pipe[0]);
            rescan_pipe[0] = -1;
      }
}

LONG HPSearchHotPluggables(void)
{
      int i;

      for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
      {
            readerTracker[i].status = READER_ABSENT;
            readerTracker[i].bus_device[0] = '\0';
            readerTracker[i].fullName = NULL;
      }

      if (HPReadBundleValues())
      {
            int pipefd[2];
            char c;

            if (pipe(pipefd) == -1)
            {
                  Log2(PCSC_LOG_ERROR, "pipe: %s", strerror(errno));
                  return -1;
            }

            ThreadCreate(&usbNotifyThread, THREAD_ATTR_DETACHED,
                  (PCSCLITE_THREAD_FUNCTION( )) HPEstablishUSBNotifications, pipefd);

            /* Wait for initial readers to setup */
            read(pipefd[0], &c, 1);
            close(pipefd[0]);
      }

      return 0;
}

LONG HPStopHotPluggables(void)
{
      AraKiriHotPlug = TRUE;
      if (rescan_pipe[1] >= 0)
      {
            close(rescan_pipe[1]);
            rescan_pipe[1] = -1;
      }

      return 0;
}

static LONG HPAddHotPluggable(struct libusb_device *dev,
      struct libusb_device_descriptor desc,
      const char bus_device[], int interface,
      struct _driverTracker *driver)
{
      int i;
      char deviceName[MAX_DEVICENAME];

      Log2(PCSC_LOG_INFO, "Adding USB device: %s", bus_device);

      if (interface >= 0)
            snprintf(deviceName, sizeof(deviceName), "usb:%04x/%04x:libhal:/org/freedesktop/Hal/devices/usb_device_%04x_%04x_serialnotneeded_if%d",
                   desc.idVendor, desc.idProduct, desc.idVendor, desc.idProduct,
                   interface);
      else
            snprintf(deviceName, sizeof(deviceName), "usb:%04x/%04x:libusb-1.0:%s",
                  desc.idVendor, desc.idProduct, bus_device);

      deviceName[sizeof(deviceName) -1] = '\0';

      pthread_mutex_lock(&usbNotifierMutex);

      /* find a free entry */
      for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
      {
            if (readerTracker[i].fullName == NULL)
                  break;
      }

      if (i==PCSCLITE_MAX_READERS_CONTEXTS)
      {
            Log2(PCSC_LOG_ERROR,
                  "Not enough reader entries. Already found %d readers", i);
            pthread_mutex_unlock(&usbNotifierMutex);
            return 0;
      }

      strncpy(readerTracker[i].bus_device, bus_device,
            sizeof(readerTracker[i].bus_device));
      readerTracker[i].bus_device[sizeof(readerTracker[i].bus_device) - 1] = '\0';

#ifdef ADD_SERIAL_NUMBER
      if (desc.iSerialNumber)
      {
            libusb_device_handle *device;
            unsigned char serialNumber[MAX_READERNAME];
            char fullname[MAX_READERNAME];
            int ret;

            ret = libusb_open(dev, &device);
            if (ret < 0)
            {
                  Log2(PCSC_LOG_ERROR, "libusb_open failed: %d", ret);
            }
            else
            {
                  ret = libusb_get_string_descriptor_ascii(device, desc.iSerialNumber,
                        serialNumber, MAX_READERNAME);
                  libusb_close(device);

                  if (ret < 0)
                  {
                        Log2(PCSC_LOG_ERROR,
                              "libusb_get_string_descriptor_ascii failed: %d", ret);
                        readerTracker[i].fullName = strdup(driver->readerName);
                  }
                  else
                  {
                        snprintf(fullname, sizeof(fullname), "%s (%s)",
                              driver->readerName, serialNumber);
                        readerTracker[i].fullName = strdup(fullname);
                  }
            }
      }
      else
#endif
            readerTracker[i].fullName = strdup(driver->readerName);

      if (RFAddReader(readerTracker[i].fullName, PCSCLITE_HP_BASE_PORT + i,
            driver->libraryPath, deviceName) == SCARD_S_SUCCESS)
            readerTracker[i].status = READER_PRESENT;
      else
      {
            readerTracker[i].status = READER_FAILED;

            (void)CheckForOpenCT();
      }

      pthread_mutex_unlock(&usbNotifierMutex);

      return 1;
}     /* End of function */

static LONG HPRemoveHotPluggable(int reader_index)
{
      pthread_mutex_lock(&usbNotifierMutex);

      Log3(PCSC_LOG_INFO, "Removing USB device[%d]: %s", reader_index,
            readerTracker[reader_index].bus_device);

      RFRemoveReader(readerTracker[reader_index].fullName,
            PCSCLITE_HP_BASE_PORT + reader_index);
      free(readerTracker[reader_index].fullName);
      readerTracker[reader_index].status = READER_ABSENT;
      readerTracker[reader_index].bus_device[0] = '\0';
      readerTracker[reader_index].fullName = NULL;

      pthread_mutex_unlock(&usbNotifierMutex);

      return 1;
}     /* End of function */

/**
 * Sets up callbacks for device hotplug events.
 */
ULONG HPRegisterForHotplugEvents(void)
{
      (void)pthread_mutex_init(&usbNotifierMutex, NULL);
      return 0;
}

void HPReCheckSerialReaders(void)
{
      if (rescan_pipe[1] >= 0)
      {
            char dummy = 0;
            write(rescan_pipe[1], &dummy, sizeof(dummy));
      }
}

#endif


Generated by  Doxygen 1.6.0   Back to index