hotplug_libhal.c

Go to the documentation of this file.
00001 /*
00002  * MUSCLE SmartCard Development ( http://www.linuxnet.com )
00003  *
00004  * Copyright (C) 2008
00005  *  Ludovic Rousseau <ludovic.rousseau@free.fr>
00006  *
00007  * $Id: hotplug_libhal.c 4249 2009-06-05 08:41:59Z rousseau $
00008  */
00009 
00015 #include "config.h"
00016 #ifdef HAVE_LIBHAL
00017 
00018 #include <string.h>
00019 #include <stdio.h>
00020 #include <dirent.h>
00021 #include <stdlib.h>
00022 #include <libhal.h>
00023 
00024 #include "misc.h"
00025 #include "wintypes.h"
00026 #include "pcscd.h"
00027 #include "debuglog.h"
00028 #include "parser.h"
00029 #include "readerfactory.h"
00030 #include "sys_generic.h"
00031 #include "hotplug.h"
00032 #include "thread_generic.h"
00033 #include "utils.h"
00034 
00035 #undef DEBUG_HOTPLUG
00036 #define ADD_SERIAL_NUMBER
00037 
00038 #define FALSE           0
00039 #define TRUE            1
00040 
00041 #define UDI_BASE "/org/freedesktop/Hal/devices/"
00042 
00043 extern PCSCLITE_MUTEX usbNotifierMutex;
00044 
00045 static PCSCLITE_THREAD_T usbNotifyThread;
00046 static int driverSize = -1;
00047 static char AraKiriHotPlug = FALSE;
00048 
00049 static DBusConnection *conn;
00050 static LibHalContext *hal_ctx;
00051 
00055 static struct _driverTracker
00056 {
00057     unsigned int manuID;
00058     unsigned int productID;
00059 
00060     char *bundleName;
00061     char *libraryPath;
00062     char *readerName;
00063     int ifdCapabilities;
00064     char *CFBundleName;
00065 } *driverTracker = NULL;
00066 #define DRIVER_TRACKER_SIZE_STEP 8
00067 
00071 static struct _readerTracker
00072 {
00073     char *udi;  
00074     char *fullName; 
00075 } readerTracker[PCSCLITE_MAX_READERS_CONTEXTS];
00076 
00077 static LONG HPReadBundleValues(void);
00078 static void HPAddDevice(LibHalContext *ctx, const char *udi);
00079 static void HPRemoveDevice(LibHalContext *ctx, const char *udi);
00080 static void HPEstablishUSBNotifications(void);
00081 
00087 static const char *short_name(const char *udi)
00088 {
00089     return &udi[sizeof(UDI_BASE) - 1];
00090 } /* short_name */
00091 
00092 
00093 static LONG HPReadBundleValues(void)
00094 {
00095     LONG rv;
00096     DIR *hpDir;
00097     struct dirent *currFP = NULL;
00098     char fullPath[FILENAME_MAX];
00099     char fullLibPath[FILENAME_MAX];
00100     char keyValue[TOKEN_MAX_VALUE_SIZE];
00101     int listCount = 0;
00102 
00103     hpDir = opendir(PCSCLITE_HP_DROPDIR);
00104 
00105     if (NULL == hpDir)
00106     {
00107         Log1(PCSC_LOG_ERROR, "Cannot open PC/SC drivers directory: " PCSCLITE_HP_DROPDIR);
00108         Log1(PCSC_LOG_ERROR, "Disabling USB support for pcscd.");
00109         return -1;
00110     }
00111 
00112     /* allocate a first array */
00113     driverTracker = calloc(DRIVER_TRACKER_SIZE_STEP, sizeof(*driverTracker));
00114     if (NULL == driverTracker)
00115     {
00116         Log1(PCSC_LOG_CRITICAL, "Not enough memory");
00117         return -1;
00118     }
00119     driverSize = DRIVER_TRACKER_SIZE_STEP;
00120 
00121     while ((currFP = readdir(hpDir)) != 0)
00122     {
00123         if (strstr(currFP->d_name, ".bundle") != 0)
00124         {
00125             int alias = 0;
00126 
00127             /*
00128              * The bundle exists - let's form a full path name and get the
00129              * vendor and product ID's for this particular bundle
00130              */
00131             (void)snprintf(fullPath, sizeof(fullPath), "%s/%s/Contents/Info.plist",
00132                 PCSCLITE_HP_DROPDIR, currFP->d_name);
00133             fullPath[sizeof(fullPath) - 1] = '\0';
00134 
00135             /* while we find a nth ifdVendorID in Info.plist */
00136             while (LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_MANUKEY_NAME,
00137                 keyValue, alias) == 0)
00138             {
00139                 driverTracker[listCount].bundleName = strdup(currFP->d_name);
00140 
00141                 /* Get ifdVendorID */
00142                 rv = LTPBundleFindValueWithKey(fullPath,
00143                     PCSCLITE_HP_MANUKEY_NAME, keyValue, alias);
00144                 if (0 == rv)
00145                     driverTracker[listCount].manuID = strtol(keyValue, NULL, 16);
00146 
00147                 /* get ifdProductID */
00148                 rv = LTPBundleFindValueWithKey(fullPath,
00149                     PCSCLITE_HP_PRODKEY_NAME, keyValue, alias);
00150                 if (0 == rv)
00151                     driverTracker[listCount].productID =
00152                         strtol(keyValue, NULL, 16);
00153 
00154                 /* get ifdFriendlyName */
00155                 rv = LTPBundleFindValueWithKey(fullPath,
00156                     PCSCLITE_HP_NAMEKEY_NAME, keyValue, alias);
00157                 if (0 == rv)
00158                     driverTracker[listCount].readerName = strdup(keyValue);
00159 
00160                 /* get CFBundleExecutable */
00161                 rv = LTPBundleFindValueWithKey(fullPath,
00162                     PCSCLITE_HP_LIBRKEY_NAME, keyValue, 0);
00163                 if (0 == rv)
00164                 {
00165                     (void)snprintf(fullLibPath, sizeof(fullLibPath),
00166                         "%s/%s/Contents/%s/%s",
00167                         PCSCLITE_HP_DROPDIR, currFP->d_name, PCSC_ARCH,
00168                         keyValue);
00169                     fullLibPath[sizeof(fullLibPath) - 1] = '\0';
00170                     driverTracker[listCount].libraryPath = strdup(fullLibPath);
00171                 }
00172 
00173                 /* Get ifdCapabilities */
00174                 rv = LTPBundleFindValueWithKey(fullPath,
00175                     PCSCLITE_HP_CPCTKEY_NAME, keyValue, 0);
00176                 if (0 == rv)
00177                     driverTracker[listCount].ifdCapabilities = strtol(keyValue,
00178                         NULL, 16);
00179 
00180                 /* Get CFBundleName */
00181                 rv = LTPBundleFindOptionalValueWithKey(fullPath,
00182                     PCSCLITE_HP_CFBUNDLE_NAME, keyValue, 0);
00183                 if (0 == rv)
00184                     driverTracker[listCount].CFBundleName = strdup(keyValue);
00185 
00186 #ifdef DEBUG_HOTPLUG
00187                 Log2(PCSC_LOG_INFO, "Found driver for: %s",
00188                     driverTracker[listCount].readerName);
00189 #endif
00190                 alias++;
00191 
00192                 if (NULL == driverTracker[listCount].readerName)
00193                     continue;
00194 
00195                 listCount++;
00196                 if (listCount >= driverSize)
00197                 {
00198                     int i;
00199 
00200                     /* increase the array size */
00201                     driverSize += DRIVER_TRACKER_SIZE_STEP;
00202 #ifdef DEBUG_HOTPLUG
00203                     Log2(PCSC_LOG_INFO,
00204                         "Increase driverTracker to %d entries", driverSize);
00205 #endif
00206                     driverTracker = realloc(driverTracker,
00207                         driverSize * sizeof(*driverTracker));
00208                     if (NULL == driverTracker)
00209                     {
00210                         Log1(PCSC_LOG_CRITICAL, "Not enough memory");
00211                         driverSize = -1;
00212                         return -1;
00213                     }
00214 
00215                     /* clean the newly allocated entries */
00216                     for (i=driverSize-DRIVER_TRACKER_SIZE_STEP; i<driverSize; i++)
00217                     {
00218                         driverTracker[i].manuID = 0;
00219                         driverTracker[i].productID = 0;
00220                         driverTracker[i].bundleName = NULL;
00221                         driverTracker[i].libraryPath = NULL;
00222                         driverTracker[i].readerName = NULL;
00223                         driverTracker[i].ifdCapabilities = 0;
00224                         driverTracker[i].CFBundleName = NULL;
00225                     }
00226                 }
00227             }
00228         }
00229     }
00230 
00231     driverSize = listCount;
00232     (void)closedir(hpDir);
00233 
00234 #ifdef DEBUG_HOTPLUG
00235     Log2(PCSC_LOG_INFO, "Found drivers for %d readers", listCount);
00236 #endif
00237 
00238     return 0;
00239 } /* HPReadBundleValues */
00240 
00241 
00242 void HPEstablishUSBNotifications(void)
00243 {
00244     while (!AraKiriHotPlug && dbus_connection_read_write_dispatch(conn, -1))
00245     {
00246 #ifdef DEBUG_HOTPLUG
00247         Log0(PCSC_LOG_INFO);
00248 #endif
00249     }
00250 } /* HPEstablishUSBNotifications */
00251 
00252 
00253 /***
00254  * Start a thread waiting for hotplug events
00255  */
00256 LONG HPSearchHotPluggables(void)
00257 {
00258     int i;
00259 
00260     for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
00261     {
00262         readerTracker[i].udi = NULL;
00263         readerTracker[i].fullName = NULL;
00264     }
00265 
00266     return HPReadBundleValues();
00267 } /* HPSearchHotPluggables */
00268 
00269 
00273 LONG HPStopHotPluggables(void)
00274 {
00275     AraKiriHotPlug = TRUE;
00276 
00277     return 0;
00278 } /* HPStopHotPluggables */
00279 
00280 
00281 /*@null@*/ static struct _driverTracker *get_driver(LibHalContext *ctx,
00282     const char *udi)
00283 {
00284     DBusError error;
00285     int i;
00286     unsigned int idVendor, idProduct;
00287     static struct _driverTracker *classdriver, *driver;
00288 
00289     if (!libhal_device_property_exists(ctx, udi, "usb.vendor_id", NULL))
00290         return NULL;
00291 
00292     dbus_error_init(&error);
00293 
00294     /* Vendor ID */
00295     idVendor = libhal_device_get_property_int(ctx, udi,
00296         "usb.vendor_id", &error);
00297     if (dbus_error_is_set(&error))
00298     {
00299         Log3(PCSC_LOG_ERROR, "libhal_device_get_property_int %s: %d",
00300             error.name, error.message);
00301         dbus_error_free(&error);
00302         return NULL;
00303     }
00304 
00305     /* Product ID */
00306     idProduct = libhal_device_get_property_int(ctx, udi,
00307         "usb.product_id", &error);
00308     if (dbus_error_is_set(&error))
00309     {
00310         Log3(PCSC_LOG_ERROR, "libhal_device_get_property_int %s: %d",
00311             error.name, error.message);
00312         dbus_error_free(&error);
00313         return NULL;
00314     }
00315 
00316     Log3(PCSC_LOG_DEBUG, "Looking a driver for VID: 0x%04X, PID: 0x%04X", idVendor, idProduct);
00317 
00318     classdriver = NULL;
00319     driver = NULL;
00320     /* check if the device is supported by one driver */
00321     for (i=0; i<driverSize; i++)
00322     {
00323         if (driverTracker[i].libraryPath != NULL &&
00324             idVendor == driverTracker[i].manuID &&
00325             idProduct == driverTracker[i].productID)
00326         {
00327             if ((driverTracker[i].CFBundleName != NULL)
00328                 && (0 == strcmp(driverTracker[i].CFBundleName, "CCIDCLASSDRIVER")))
00329                 classdriver = &driverTracker[i];
00330             else
00331                 /* it is not a CCID Class driver */
00332                 driver = &driverTracker[i];
00333         }
00334     }
00335 
00336     /* if we found a specific driver */
00337     if (driver)
00338         return driver;
00339 
00340     /* else return the Class driver */
00341     return classdriver;
00342 }
00343 
00344 
00345 static void HPAddDevice(LibHalContext *ctx, const char *udi)
00346 {
00347     int i;
00348     char deviceName[MAX_DEVICENAME];
00349     struct _driverTracker *driver;
00350     LONG ret;
00351 
00352     driver = get_driver(ctx, udi);
00353     if (NULL == driver)
00354     {
00355         /* not a smart card reader */
00356 #ifdef DEBUG_HOTPLUG
00357         Log2(PCSC_LOG_DEBUG, "%s is not a reader", short_name(udi));
00358 #endif
00359         return;
00360     }
00361 
00362     Log2(PCSC_LOG_INFO, "Adding USB device: %s", short_name(udi));
00363 
00364     (void)snprintf(deviceName, sizeof(deviceName), "usb:%04x/%04x:libhal:%s",
00365         driver->manuID, driver->productID, udi);
00366     deviceName[sizeof(deviceName) -1] = '\0';
00367 
00368     /* wait until the device is visible by libusb/etc.  */
00369     (void)SYS_Sleep(1);
00370 
00371     (void)SYS_MutexLock(&usbNotifierMutex);
00372 
00373     /* find a free entry */
00374     for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
00375     {
00376         if (NULL == readerTracker[i].fullName)
00377             break;
00378     }
00379 
00380     if (PCSCLITE_MAX_READERS_CONTEXTS == i)
00381     {
00382         Log2(PCSC_LOG_ERROR,
00383             "Not enough reader entries. Already found %d readers", i);
00384         (void)SYS_MutexUnLock(&usbNotifierMutex);
00385         return;
00386     }
00387 
00388     readerTracker[i].udi = strdup(udi);
00389 
00390 #ifdef ADD_SERIAL_NUMBER
00391     if (libhal_device_property_exists(ctx, udi, "usb.serial", NULL))
00392     {
00393         char fullname[MAX_READERNAME];
00394         char *sSerialNumber;
00395 
00396         sSerialNumber = libhal_device_get_property_string(ctx, udi,
00397             "usb.serial", NULL);
00398 
00399         (void)snprintf(fullname, sizeof(fullname), "%s (%s)",
00400             driver->readerName, sSerialNumber);
00401         readerTracker[i].fullName = strdup(fullname);
00402     }
00403     else
00404 #endif
00405         readerTracker[i].fullName = strdup(driver->readerName);
00406 
00407     ret = RFAddReader(readerTracker[i].fullName, PCSCLITE_HP_BASE_PORT + i,
00408         driver->libraryPath, deviceName);
00409     if (SCARD_S_SUCCESS != ret)
00410     {
00411         char *parent, *device_file;
00412 
00413         /* get the parent descriptor, without the '_if0' */
00414         parent = libhal_device_get_property_string(ctx, udi,
00415             "info.parent", NULL);
00416         if (! parent)
00417             goto error;
00418 
00419         /* get the linux device file: i.e. '/dev/bus/usb/002/012' */
00420         device_file = libhal_device_get_property_string(ctx, parent,
00421             "linux.device_file", NULL);
00422         if (! device_file)
00423             goto error;
00424 
00425         /* check the format looks correct */
00426 #define LIBUSB_HEADER "/dev/bus/usb/"
00427         if (strncmp(device_file, LIBUSB_HEADER, strlen(LIBUSB_HEADER)))
00428             goto error;
00429 
00430         device_file += strlen(LIBUSB_HEADER);
00431 
00432         (void)snprintf(deviceName, sizeof(deviceName),
00433             "usb:%04x/%04x:libusb:%s",
00434             driver->manuID, driver->productID, device_file);
00435         deviceName[sizeof(deviceName) -1] = '\0';
00436 
00437         /* replace the libusb separator '/' by ':' */
00438         if ('/' == deviceName[strlen(deviceName)-3-1])
00439             deviceName[strlen(deviceName)-3-1] = ':';
00440 
00441         Log2(PCSC_LOG_INFO, "trying libusb scheme with: %s", deviceName);
00442         ret = RFAddReader(readerTracker[i].fullName, PCSCLITE_HP_BASE_PORT + i,
00443             driver->libraryPath, deviceName);
00444 
00445         if (SCARD_S_SUCCESS != ret)
00446         {
00447 error:
00448             Log2(PCSC_LOG_ERROR, "Failed adding USB device: %s", short_name(udi));
00449             free(readerTracker[i].fullName);
00450             readerTracker[i].fullName = NULL;
00451             free(readerTracker[i].udi);
00452             readerTracker[i].udi = NULL;
00453 
00454             (void)CheckForOpenCT();
00455         }
00456     }
00457 
00458     (void)SYS_MutexUnLock(&usbNotifierMutex);
00459 } /* HPAddDevice */
00460 
00461 
00462 static void HPRemoveDevice(/*@unused@*/ LibHalContext *ctx, const char *udi)
00463 {
00464     int i;
00465 
00466     (void)ctx;
00467     for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
00468     {
00469         if (readerTracker[i].udi && strcmp(readerTracker[i].udi, udi) == 0)
00470             break;
00471     }
00472     if (PCSCLITE_MAX_READERS_CONTEXTS == i)
00473     {
00474 #ifdef DEBUG_HOTPLUG
00475         Log2(PCSC_LOG_DEBUG, "USB device %s not already used", short_name(udi));
00476 #endif
00477         return;
00478     }
00479     Log3(PCSC_LOG_INFO, "Removing USB device[%d]: %s", i,
00480         short_name(readerTracker[i].udi));
00481 
00482     (void)SYS_MutexLock(&usbNotifierMutex);
00483 
00484     (void)RFRemoveReader(readerTracker[i].fullName, PCSCLITE_HP_BASE_PORT + i);
00485     free(readerTracker[i].fullName);
00486     readerTracker[i].fullName = NULL;
00487     free(readerTracker[i].udi);
00488     readerTracker[i].udi = NULL;
00489 
00490     (void)SYS_MutexUnLock(&usbNotifierMutex);
00491 
00492     return;
00493 } /* HPRemoveDevice */
00494 
00495 
00499 ULONG HPRegisterForHotplugEvents(void)
00500 {
00501     char **device_names;
00502     int i, num_devices;
00503     DBusError error;
00504 
00505     if (driverSize <= 0)
00506     {
00507         Log1(PCSC_LOG_INFO, "No bundle files in pcsc drivers directory: " PCSCLITE_HP_DROPDIR);
00508         Log1(PCSC_LOG_INFO, "Disabling USB support for pcscd");
00509         return 1;
00510     }
00511 
00512     dbus_error_init(&error);
00513     conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
00514     if (conn == NULL)
00515     {
00516         Log3(PCSC_LOG_ERROR, "error: dbus_bus_get: %s: %s",
00517             error.name, error.message);
00518         if (dbus_error_is_set(&error))
00519             dbus_error_free(&error);
00520         return 1;
00521     }
00522 
00523     if ((hal_ctx = libhal_ctx_new()) == NULL)
00524     {
00525         Log1(PCSC_LOG_ERROR, "error: libhal_ctx_new");
00526         return 1;
00527     }
00528     if (!libhal_ctx_set_dbus_connection(hal_ctx, conn))
00529     {
00530         Log1(PCSC_LOG_ERROR, "error: libhal_ctx_set_dbus_connection");
00531         return 1;
00532     }
00533     if (!libhal_ctx_init(hal_ctx, &error))
00534     {
00535         if (dbus_error_is_set(&error))
00536         {
00537             Log3(PCSC_LOG_ERROR, "error: libhal_ctx_init: %s: %s",
00538                 error.name, error.message);
00539             if (dbus_error_is_set(&error))
00540                 dbus_error_free(&error);
00541         }
00542         Log1(PCSC_LOG_ERROR, "Could not initialise connection to hald.");
00543         Log1(PCSC_LOG_ERROR, "Normally this means the HAL daemon (hald) is not running or not ready.");
00544         return 1;
00545     }
00546 
00547     /* callback when device added */
00548     (void)libhal_ctx_set_device_added(hal_ctx, HPAddDevice);
00549 
00550     /* callback when device removed */
00551     (void)libhal_ctx_set_device_removed(hal_ctx, HPRemoveDevice);
00552 
00553     device_names = libhal_get_all_devices(hal_ctx, &num_devices, &error);
00554     if (device_names == NULL)
00555     {
00556         if (dbus_error_is_set(&error))
00557             dbus_error_free(&error);
00558         Log1(PCSC_LOG_ERROR, "Couldn't obtain list of devices");
00559         return 1;
00560     }
00561 
00562     /* try to add every present USB devices */
00563     for (i = 0; i < num_devices; i++)
00564         HPAddDevice(hal_ctx, device_names[i]);
00565 
00566     libhal_free_string_array(device_names);
00567 
00568     (void)SYS_ThreadCreate(&usbNotifyThread, THREAD_ATTR_DETACHED,
00569         (PCSCLITE_THREAD_FUNCTION( )) HPEstablishUSBNotifications, NULL);
00570 
00571     return 0;
00572 } /* HPRegisterForHotplugEvents */
00573 
00574 
00575 void HPReCheckSerialReaders(void)
00576 {
00577     /* nothing to do here */
00578 #ifdef DEBUG_HOTPLUG
00579     Log0(PCSC_LOG_ERROR);
00580 #endif
00581 } /* HPReCheckSerialReaders */
00582 
00583 #endif
00584 

Generated on Wed Jul 22 23:13:21 2009 for pcsc-lite by  doxygen 1.5.8