hotplug_libusb.c

Go to the documentation of this file.
00001 /*
00002  * MUSCLE SmartCard Development ( http://www.linuxnet.com )
00003  *
00004  * Copyright (C) 2001-2004
00005  *  David Corcoran <corcoran@linuxnet.com>
00006  *  Ludovic Rousseau <ludovic.rousseau@free.fr>
00007  *  Toni Andjelkovic <toni@soth.at>
00008  *  Damien Sauveron <damien.sauveron@labri.fr>
00009  *
00010  * $Id: hotplug_libusb.c 2334 2007-01-11 17:58:43Z rousseau $
00011  */
00012 
00018 #include "config.h"
00019 #ifdef HAVE_LIBUSB
00020 
00021 #include <string.h>
00022 #include <sys/types.h>
00023 #include <stdio.h>
00024 #include <dirent.h>
00025 #include <fcntl.h>
00026 #include <time.h>
00027 #include <stdlib.h>
00028 #include <unistd.h>
00029 #include <errno.h>
00030 #include <usb.h>
00031 
00032 #include "misc.h"
00033 #include "pcsclite.h"
00034 #include "debuglog.h"
00035 #include "parser.h"
00036 #include "readerfactory.h"
00037 #include "winscard_msg.h"
00038 #include "sys_generic.h"
00039 #include "hotplug.h"
00040 
00041 #undef DEBUG_HOTPLUG
00042 #define ADD_SERIAL_NUMBER
00043 
00044 #define BUS_DEVICE_STRSIZE  256
00045 
00046 #define READER_ABSENT       0
00047 #define READER_PRESENT      1
00048 #define READER_FAILED       2
00049 
00050 #define FALSE           0
00051 #define TRUE            1
00052 
00053 extern PCSCLITE_MUTEX usbNotifierMutex;
00054 
00055 static PCSCLITE_THREAD_T usbNotifyThread;
00056 static int driverSize = -1;
00057 static char AraKiriHotPlug = FALSE;
00058 extern int HPForceReaderPolling;
00059 
00060 /* values of ifdCapabilities bits */
00061 #define IFD_GENERATE_HOTPLUG 1
00062 
00063 /*
00064  * keep track of drivers in a dynamically allocated array
00065  */
00066 static struct _driverTracker
00067 {
00068     long manuID;
00069     long productID;
00070 
00071     char *bundleName;
00072     char *libraryPath;
00073     char *readerName;
00074     int ifdCapabilities;
00075 } *driverTracker = NULL;
00076 #define DRIVER_TRACKER_SIZE_STEP 8
00077 
00078 /*
00079  * keep track of PCSCLITE_MAX_READERS_CONTEXTS simultaneous readers
00080  */
00081 static struct _readerTracker
00082 {
00083     char status;
00084     char bus_device[BUS_DEVICE_STRSIZE];    /* device name */
00085     char *fullName; /* full reader name (including serial number) */
00086 } readerTracker[PCSCLITE_MAX_READERS_CONTEXTS];
00087 
00088 LONG HPReadBundleValues(void);
00089 LONG HPAddHotPluggable(struct usb_device *dev, const char bus_device[],
00090     struct _driverTracker *driver);
00091 LONG HPRemoveHotPluggable(int index);
00092 static void HPRescanUsbBus(void);
00093 static void HPEstablishUSBNotifications(void);
00094 
00095 LONG HPReadBundleValues(void)
00096 {
00097     LONG rv;
00098     DIR *hpDir;
00099     struct dirent *currFP = 0;
00100     char fullPath[FILENAME_MAX];
00101     char fullLibPath[FILENAME_MAX];
00102     char keyValue[TOKEN_MAX_VALUE_SIZE];
00103     int listCount = 0;
00104 
00105     hpDir = opendir(PCSCLITE_HP_DROPDIR);
00106 
00107     if (hpDir == NULL)
00108     {
00109         Log1(PCSC_LOG_ERROR, "Cannot open PC/SC drivers directory: " PCSCLITE_HP_DROPDIR);
00110         Log1(PCSC_LOG_ERROR, "Disabling USB support for pcscd.");
00111         return -1;
00112     }
00113 
00114     /* allocate a first array */
00115     driverTracker = calloc(DRIVER_TRACKER_SIZE_STEP, sizeof(*driverTracker));
00116     if (NULL == driverTracker)
00117     {
00118         Log1(PCSC_LOG_CRITICAL, "Not enough memory");
00119         return -1;
00120     }
00121     driverSize = DRIVER_TRACKER_SIZE_STEP;
00122 
00123     while ((currFP = readdir(hpDir)) != 0)
00124     {
00125         if (strstr(currFP->d_name, ".bundle") != 0)
00126         {
00127             int alias = 0;
00128 
00129             /*
00130              * The bundle exists - let's form a full path name and get the
00131              * vendor and product ID's for this particular bundle
00132              */
00133             snprintf(fullPath, sizeof(fullPath), "%s/%s/Contents/Info.plist",
00134                 PCSCLITE_HP_DROPDIR, currFP->d_name);
00135             fullPath[sizeof(fullPath) - 1] = '\0';
00136 
00137             /* while we find a nth ifdVendorID in Info.plist */
00138             while (LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_MANUKEY_NAME,
00139                 keyValue, alias) == 0)
00140             {
00141                 driverTracker[listCount].bundleName = strdup(currFP->d_name);
00142 
00143                 /* Get ifdVendorID */
00144                 rv = LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_MANUKEY_NAME,
00145                     keyValue, alias);
00146                 if (rv == 0)
00147                     driverTracker[listCount].manuID = strtol(keyValue, 0, 16);
00148 
00149                 /* get ifdProductID */
00150                 rv = LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_PRODKEY_NAME,
00151                     keyValue, alias);
00152                 if (rv == 0)
00153                     driverTracker[listCount].productID =
00154                         strtol(keyValue, 0, 16);
00155 
00156                 /* get ifdFriendlyName */
00157                 rv = LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_NAMEKEY_NAME,
00158                     keyValue, alias);
00159                 if (rv == 0)
00160                     driverTracker[listCount].readerName = strdup(keyValue);
00161 
00162                 /* get CFBundleExecutable */
00163                 rv = LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_LIBRKEY_NAME,
00164                     keyValue, 0);
00165                 if (rv == 0)
00166                 {
00167                     snprintf(fullLibPath, sizeof(fullLibPath),
00168                         "%s/%s/Contents/%s/%s",
00169                         PCSCLITE_HP_DROPDIR, currFP->d_name, PCSC_ARCH, keyValue);
00170                     fullLibPath[sizeof(fullLibPath) - 1] = '\0';
00171                     driverTracker[listCount].libraryPath = strdup(fullLibPath);
00172                 }
00173 
00174                 /* Get ifdCapabilities */
00175                 rv = LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_CPCTKEY_NAME,
00176                     keyValue, 0);
00177                 if (rv == 0)
00178                     driverTracker[listCount].ifdCapabilities = strtol(keyValue, 0, 16);
00179 
00180 #ifdef DEBUG_HOTPLUG
00181                     Log2(PCSC_LOG_INFO, "Found driver for: %s",
00182                         driverTracker[listCount].readerName);
00183 #endif
00184 
00185                 listCount++;
00186                 alias++;
00187 
00188                 if (listCount >= driverSize)
00189                 {
00190                     int i;
00191 
00192                     /* increase the array size */
00193                     driverSize += DRIVER_TRACKER_SIZE_STEP;
00194 #ifdef DEBUG_HOTPLUG
00195                     Log2(PCSC_LOG_INFO,
00196                         "Increase driverTracker to %d entries", driverSize);
00197 #endif
00198                     driverTracker = realloc(driverTracker,
00199                         driverSize * sizeof(*driverTracker));
00200                     if (NULL == driverTracker)
00201                     {
00202                         Log1(PCSC_LOG_CRITICAL, "Not enough memory");
00203                         driverSize = -1;
00204                         return -1;
00205                     }
00206 
00207                     /* clean the newly allocated entries */
00208                     for (i=driverSize-DRIVER_TRACKER_SIZE_STEP; i<driverSize; i++)
00209                     {
00210                         driverTracker[i].manuID = 0;
00211                         driverTracker[i].productID = 0;
00212                         driverTracker[i].bundleName = NULL;
00213                         driverTracker[i].libraryPath = NULL;
00214                         driverTracker[i].readerName = NULL;
00215                         driverTracker[i].ifdCapabilities = 0;
00216                     }
00217                 }
00218             }
00219         }
00220     }
00221 
00222     driverSize = listCount;
00223     closedir(hpDir);
00224 
00225     rv = TRUE;
00226     if (driverSize == 0)
00227     {
00228         Log1(PCSC_LOG_INFO, "No bundle files in pcsc drivers directory: " PCSCLITE_HP_DROPDIR);
00229         Log1(PCSC_LOG_INFO, "Disabling USB support for pcscd");
00230         rv = FALSE;
00231     }
00232 #ifdef DEBUG_HOTPLUG
00233     else
00234         Log2(PCSC_LOG_INFO, "Found drivers for %d readers", listCount);
00235 #endif
00236 
00237     return rv;
00238 }
00239 
00240 void HPRescanUsbBus(void)
00241 {
00242     int i, j;
00243     struct usb_bus *bus;
00244     struct usb_device *dev;
00245     char bus_device[BUS_DEVICE_STRSIZE];
00246 
00247     usb_find_busses();
00248     usb_find_devices();
00249 
00250     for (i=0; i < PCSCLITE_MAX_READERS_CONTEXTS; i++)
00251         /* clear rollcall */
00252         readerTracker[i].status = READER_ABSENT;
00253 
00254     /* For each USB bus */
00255     for (bus = usb_get_busses(); bus; bus = bus->next)
00256     {
00257         /* For each USB device */
00258         for (dev = bus->devices; dev; dev = dev->next)
00259         {
00260             /* check if the device is supported by one driver */
00261             for (i=0; i<driverSize; i++)
00262             {
00263                 if (driverTracker[i].libraryPath != NULL &&
00264                     dev->descriptor.idVendor == driverTracker[i].manuID &&
00265                     dev->descriptor.idProduct == driverTracker[i].productID)
00266                 {
00267                     int newreader;
00268 
00269                     /* A known device has been found */
00270                     snprintf(bus_device, BUS_DEVICE_STRSIZE, "%s:%s",
00271                         bus->dirname, dev->filename);
00272                     bus_device[BUS_DEVICE_STRSIZE - 1] = '\0';
00273 #ifdef DEBUG_HOTPLUG
00274                     Log2(PCSC_LOG_DEBUG, "Found matching USB device: %s", bus_device);
00275 #endif
00276                     newreader = TRUE;
00277 
00278                     /* Check if the reader is a new one */
00279                     for (j=0; j<PCSCLITE_MAX_READERS_CONTEXTS; j++)
00280                     {
00281                         if (strncmp(readerTracker[j].bus_device,
00282                             bus_device, BUS_DEVICE_STRSIZE) == 0)
00283                         {
00284                             /* The reader is already known */
00285                             readerTracker[j].status = READER_PRESENT;
00286                             newreader = FALSE;
00287 #ifdef DEBUG_HOTPLUG
00288                             Log2(PCSC_LOG_DEBUG, "Refresh USB device: %s", bus_device);
00289 #endif
00290                             break;
00291                         }
00292                     }
00293 
00294                     /* New reader found */
00295                     if (newreader)
00296                         HPAddHotPluggable(dev, bus_device, &driverTracker[i]);
00297                 }
00298             }
00299         } /* End of USB device for..loop */
00300 
00301     } /* End of USB bus for..loop */
00302 
00303     /*
00304      * check if all the previously found readers are still present
00305      */
00306     for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
00307     {
00308 #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)
00309         int fd;
00310         char filename[BUS_DEVICE_STRSIZE];
00311 
00312         /*  BSD workaround:
00313          *  ugenopen() in sys/dev/usb/ugen.c returns EBUSY
00314          *  when the character device file is already open.
00315          *  Because of this, open usb devices will not be
00316          *  detected by usb_find_devices(), so we have to
00317          *  check for this explicitly.
00318          */
00319         if (readerTracker[i].status == READER_PRESENT ||
00320              readerTracker[i].fullName == NULL)
00321             continue;
00322 
00323         sscanf(readerTracker[i].bus_device, "%*[^:]%*[:]%s", filename);
00324         fd = open(filename, O_RDONLY);
00325         if (fd == -1)
00326         {
00327             if (errno == EBUSY)
00328             {
00329                 /* The device is present */
00330 #ifdef DEBUG_HOTPLUG
00331                 Log2(PCSC_LOG_DEBUG, "BSD: EBUSY on %s", filename);
00332 #endif
00333                 readerTracker[i].status = READER_PRESENT;
00334             }
00335 #ifdef DEBUG_HOTPLUG
00336             else
00337                 Log3(PCSC_LOG_DEBUG, "BSD: %s error: %s", filename,
00338                     strerror(errno));
00339 #endif
00340         }
00341         else
00342         {
00343 #ifdef DEBUG_HOTPLUG
00344             Log2(PCSC_LOG_DEBUG, "BSD: %s still present", filename);
00345 #endif
00346             readerTracker[i].status = READER_PRESENT;
00347             close(fd);
00348         }
00349 #endif
00350         if ((readerTracker[i].status == READER_ABSENT) &&
00351             (readerTracker[i].fullName != NULL))
00352             HPRemoveHotPluggable(i);
00353     }
00354 
00355     if (AraKiriHotPlug)
00356     {
00357         int retval;
00358 
00359         for (i=0; i<driverSize; i++)
00360         {
00361             /* free strings allocated by strdup() */
00362             free(driverTracker[i].bundleName);
00363             free(driverTracker[i].libraryPath);
00364             free(driverTracker[i].readerName);
00365         }
00366         free(driverTracker);
00367 
00368         Log1(PCSC_LOG_INFO, "Hotplug stopped");
00369         pthread_exit(&retval);
00370     }
00371 }
00372 
00373 void HPEstablishUSBNotifications(void)
00374 {
00375     int i, do_polling;
00376 
00377     /* libusb default is /dev/bus/usb but the devices are not yet visible there
00378      * when a hotplug is requested */
00379     setenv("USB_DEVFS_PATH", "/proc/bus/usb", 0);
00380 
00381     usb_init();
00382 
00383     /* scan the USB bus for devices at startup */
00384     HPRescanUsbBus();
00385 
00386     /* if at least one driver do not have IFD_GENERATE_HOTPLUG */
00387     do_polling = FALSE;
00388     for (i=0; i<driverSize; i++)
00389         if (driverTracker[i].libraryPath)
00390             if ((driverTracker[i].ifdCapabilities & IFD_GENERATE_HOTPLUG) == 0)
00391             {
00392                 Log2(PCSC_LOG_INFO,
00393                     "Driver %s does not support IFD_GENERATE_HOTPLUG",
00394                     driverTracker[i].bundleName);
00395                 do_polling = TRUE;
00396                 break;
00397             }
00398 
00399     if (HPForceReaderPolling)
00400     {
00401         Log2(PCSC_LOG_INFO,
00402                 "Polling forced every %d second(s)", HPForceReaderPolling);
00403         do_polling = TRUE;
00404     }
00405 
00406     while (do_polling)
00407     {
00408         SYS_Sleep(HPForceReaderPolling);
00409         HPRescanUsbBus();
00410     }
00411 }
00412 
00413 LONG HPSearchHotPluggables(void)
00414 {
00415     int i;
00416 
00417     for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
00418     {
00419         readerTracker[i].status = READER_ABSENT;
00420         readerTracker[i].bus_device[0] = '\0';
00421         readerTracker[i].fullName = NULL;
00422     }
00423 
00424     if (HPReadBundleValues())
00425         SYS_ThreadCreate(&usbNotifyThread, THREAD_ATTR_DETACHED,
00426             (PCSCLITE_THREAD_FUNCTION( )) HPEstablishUSBNotifications, 0);
00427 
00428     return 0;
00429 }
00430 
00431 LONG HPStopHotPluggables(void)
00432 {
00433     AraKiriHotPlug = TRUE;
00434 
00435     return 0;
00436 }
00437 
00438 LONG HPAddHotPluggable(struct usb_device *dev, const char bus_device[],
00439     struct _driverTracker *driver)
00440 {
00441     int i;
00442     char deviceName[MAX_DEVICENAME];
00443 
00444     SYS_MutexLock(&usbNotifierMutex);
00445 
00446     Log2(PCSC_LOG_INFO, "Adding USB device: %s", bus_device);
00447 
00448     snprintf(deviceName, sizeof(deviceName), "usb:%04x/%04x:libusb:%s",
00449         dev->descriptor.idVendor, dev->descriptor.idProduct, bus_device);
00450     deviceName[sizeof(deviceName) -1] = '\0';
00451 
00452     /* find a free entry */
00453     for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
00454     {
00455         if (readerTracker[i].fullName == NULL)
00456             break;
00457     }
00458 
00459     if (i==PCSCLITE_MAX_READERS_CONTEXTS)
00460     {
00461         Log2(PCSC_LOG_ERROR,
00462             "Not enough reader entries. Already found %d readers", i);
00463         return 0;
00464     }
00465 
00466     strncpy(readerTracker[i].bus_device, bus_device,
00467         sizeof(readerTracker[i].bus_device));
00468     readerTracker[i].bus_device[sizeof(readerTracker[i].bus_device) - 1] = '\0';
00469 
00470 #ifdef ADD_SERIAL_NUMBER
00471     if (dev->descriptor.iSerialNumber)
00472     {
00473         usb_dev_handle *device;
00474         char serialNumber[MAX_READERNAME];
00475         char fullname[MAX_READERNAME];
00476 
00477         device = usb_open(dev);
00478         usb_get_string_simple(device, dev->descriptor.iSerialNumber,
00479             serialNumber, MAX_READERNAME);
00480         usb_close(device);
00481 
00482         snprintf(fullname, sizeof(fullname), "%s (%s)",
00483             driver->readerName, serialNumber);
00484         readerTracker[i].fullName = strdup(fullname);
00485     }
00486     else
00487 #endif
00488         readerTracker[i].fullName = strdup(driver->readerName);
00489 
00490     if (RFAddReader(readerTracker[i].fullName, PCSCLITE_HP_BASE_PORT + i,
00491         driver->libraryPath, deviceName) == SCARD_S_SUCCESS)
00492         readerTracker[i].status = READER_PRESENT;
00493     else
00494         readerTracker[i].status = READER_FAILED;
00495 
00496     SYS_MutexUnLock(&usbNotifierMutex);
00497 
00498     return 1;
00499 }   /* End of function */
00500 
00501 LONG HPRemoveHotPluggable(int index)
00502 {
00503     SYS_MutexLock(&usbNotifierMutex);
00504 
00505     Log3(PCSC_LOG_INFO, "Removing USB device[%d]: %s", index,
00506         readerTracker[index].bus_device);
00507 
00508     RFRemoveReader(readerTracker[index].fullName,
00509         PCSCLITE_HP_BASE_PORT + index);
00510     free(readerTracker[index].fullName);
00511     readerTracker[index].status = READER_ABSENT;
00512     readerTracker[index].bus_device[0] = '\0';
00513     readerTracker[index].fullName = NULL;
00514 
00515     SYS_MutexUnLock(&usbNotifierMutex);
00516 
00517     return 1;
00518 }   /* End of function */
00519 
00520 /*
00521  * Sets up callbacks for device hotplug events.
00522  */
00523 ULONG HPRegisterForHotplugEvents(void)
00524 {
00525     return 0;
00526 }
00527 
00528 void HPReCheckSerialReaders(void)
00529 {
00530     HPRescanUsbBus();
00531     RFReCheckReaderConf();
00532 }
00533 
00534 #endif
00535 

Generated on Tue Aug 14 13:59:18 2007 for pcsc-lite by  doxygen 1.5.2