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 4297 2009-07-03 12:55:47Z 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 #include "strlcpycat.h"
00035 
00036 #undef DEBUG_HOTPLUG
00037 #define ADD_SERIAL_NUMBER
00038 #define ADD_INTERFACE_NAME
00039 
00040 #define FALSE           0
00041 #define TRUE            1
00042 
00043 #define UDI_BASE "/org/freedesktop/Hal/devices/"
00044 
00045 extern PCSCLITE_MUTEX usbNotifierMutex;
00046 
00047 static PCSCLITE_THREAD_T usbNotifyThread;
00048 static int driverSize = -1;
00049 static char AraKiriHotPlug = FALSE;
00050 
00051 static DBusConnection *conn;
00052 static LibHalContext *hal_ctx;
00053 
00057 static struct _driverTracker
00058 {
00059     unsigned int manuID;
00060     unsigned int productID;
00061 
00062     char *bundleName;
00063     char *libraryPath;
00064     char *readerName;
00065     int ifdCapabilities;
00066     char *CFBundleName;
00067 } *driverTracker = NULL;
00068 #define DRIVER_TRACKER_SIZE_STEP 8
00069 
00073 static struct _readerTracker
00074 {
00075     char *udi;  
00076     char *fullName; 
00077 } readerTracker[PCSCLITE_MAX_READERS_CONTEXTS];
00078 
00079 static LONG HPReadBundleValues(void);
00080 static void HPAddDevice(LibHalContext *ctx, const char *udi);
00081 static void HPRemoveDevice(LibHalContext *ctx, const char *udi);
00082 static void HPEstablishUSBNotifications(void);
00083 
00089 static const char *short_name(const char *udi)
00090 {
00091     return &udi[sizeof(UDI_BASE) - 1];
00092 } /* short_name */
00093 
00094 
00095 static LONG HPReadBundleValues(void)
00096 {
00097     LONG rv;
00098     DIR *hpDir;
00099     struct dirent *currFP = NULL;
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 (NULL == hpDir)
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             (void)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,
00145                     PCSCLITE_HP_MANUKEY_NAME, keyValue, alias);
00146                 if (0 == rv)
00147                     driverTracker[listCount].manuID = strtol(keyValue, NULL, 16);
00148 
00149                 /* get ifdProductID */
00150                 rv = LTPBundleFindValueWithKey(fullPath,
00151                     PCSCLITE_HP_PRODKEY_NAME, keyValue, alias);
00152                 if (0 == rv)
00153                     driverTracker[listCount].productID =
00154                         strtol(keyValue, NULL, 16);
00155 
00156                 /* get ifdFriendlyName */
00157                 rv = LTPBundleFindValueWithKey(fullPath,
00158                     PCSCLITE_HP_NAMEKEY_NAME, keyValue, alias);
00159                 if (0 == rv)
00160                     driverTracker[listCount].readerName = strdup(keyValue);
00161 
00162                 /* get CFBundleExecutable */
00163                 rv = LTPBundleFindValueWithKey(fullPath,
00164                     PCSCLITE_HP_LIBRKEY_NAME, keyValue, 0);
00165                 if (0 == rv)
00166                 {
00167                     (void)snprintf(fullLibPath, sizeof(fullLibPath),
00168                         "%s/%s/Contents/%s/%s",
00169                         PCSCLITE_HP_DROPDIR, currFP->d_name, PCSC_ARCH,
00170                         keyValue);
00171                     fullLibPath[sizeof(fullLibPath) - 1] = '\0';
00172                     driverTracker[listCount].libraryPath = strdup(fullLibPath);
00173                 }
00174 
00175                 /* Get ifdCapabilities */
00176                 rv = LTPBundleFindValueWithKey(fullPath,
00177                     PCSCLITE_HP_CPCTKEY_NAME, keyValue, 0);
00178                 if (0 == rv)
00179                     driverTracker[listCount].ifdCapabilities = strtol(keyValue,
00180                         NULL, 16);
00181 
00182                 /* Get CFBundleName */
00183                 rv = LTPBundleFindOptionalValueWithKey(fullPath,
00184                     PCSCLITE_HP_CFBUNDLE_NAME, keyValue, 0);
00185                 if (0 == rv)
00186                     driverTracker[listCount].CFBundleName = strdup(keyValue);
00187 
00188 #ifdef DEBUG_HOTPLUG
00189                 Log2(PCSC_LOG_INFO, "Found driver for: %s",
00190                     driverTracker[listCount].readerName);
00191 #endif
00192                 alias++;
00193 
00194                 if (NULL == driverTracker[listCount].readerName)
00195                     continue;
00196 
00197                 listCount++;
00198                 if (listCount >= driverSize)
00199                 {
00200                     int i;
00201 
00202                     /* increase the array size */
00203                     driverSize += DRIVER_TRACKER_SIZE_STEP;
00204 #ifdef DEBUG_HOTPLUG
00205                     Log2(PCSC_LOG_INFO,
00206                         "Increase driverTracker to %d entries", driverSize);
00207 #endif
00208                     driverTracker = realloc(driverTracker,
00209                         driverSize * sizeof(*driverTracker));
00210                     if (NULL == driverTracker)
00211                     {
00212                         Log1(PCSC_LOG_CRITICAL, "Not enough memory");
00213                         driverSize = -1;
00214                         return -1;
00215                     }
00216 
00217                     /* clean the newly allocated entries */
00218                     for (i=driverSize-DRIVER_TRACKER_SIZE_STEP; i<driverSize; i++)
00219                     {
00220                         driverTracker[i].manuID = 0;
00221                         driverTracker[i].productID = 0;
00222                         driverTracker[i].bundleName = NULL;
00223                         driverTracker[i].libraryPath = NULL;
00224                         driverTracker[i].readerName = NULL;
00225                         driverTracker[i].ifdCapabilities = 0;
00226                         driverTracker[i].CFBundleName = NULL;
00227                     }
00228                 }
00229             }
00230         }
00231     }
00232 
00233     driverSize = listCount;
00234     (void)closedir(hpDir);
00235 
00236 #ifdef DEBUG_HOTPLUG
00237     Log2(PCSC_LOG_INFO, "Found drivers for %d readers", listCount);
00238 #endif
00239 
00240     return 0;
00241 } /* HPReadBundleValues */
00242 
00243 
00244 void HPEstablishUSBNotifications(void)
00245 {
00246     while (!AraKiriHotPlug && dbus_connection_read_write_dispatch(conn, -1))
00247     {
00248 #ifdef DEBUG_HOTPLUG
00249         Log0(PCSC_LOG_INFO);
00250 #endif
00251     }
00252 } /* HPEstablishUSBNotifications */
00253 
00254 
00255 /***
00256  * Start a thread waiting for hotplug events
00257  */
00258 LONG HPSearchHotPluggables(void)
00259 {
00260     int i;
00261 
00262     for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
00263     {
00264         readerTracker[i].udi = NULL;
00265         readerTracker[i].fullName = NULL;
00266     }
00267 
00268     return HPReadBundleValues();
00269 } /* HPSearchHotPluggables */
00270 
00271 
00275 LONG HPStopHotPluggables(void)
00276 {
00277     AraKiriHotPlug = TRUE;
00278 
00279     return 0;
00280 } /* HPStopHotPluggables */
00281 
00282 
00283 /*@null@*/ static struct _driverTracker *get_driver(LibHalContext *ctx,
00284     const char *udi)
00285 {
00286     DBusError error;
00287     int i;
00288     unsigned int idVendor, idProduct;
00289     static struct _driverTracker *classdriver, *driver;
00290 
00291     if (!libhal_device_property_exists(ctx, udi, "usb.vendor_id", NULL))
00292         return NULL;
00293 
00294     dbus_error_init(&error);
00295 
00296     /* Vendor ID */
00297     idVendor = libhal_device_get_property_int(ctx, udi,
00298         "usb.vendor_id", &error);
00299     if (dbus_error_is_set(&error))
00300     {
00301         Log3(PCSC_LOG_ERROR, "libhal_device_get_property_int %s: %d",
00302             error.name, error.message);
00303         dbus_error_free(&error);
00304         return NULL;
00305     }
00306 
00307     /* Product ID */
00308     idProduct = libhal_device_get_property_int(ctx, udi,
00309         "usb.product_id", &error);
00310     if (dbus_error_is_set(&error))
00311     {
00312         Log3(PCSC_LOG_ERROR, "libhal_device_get_property_int %s: %d",
00313             error.name, error.message);
00314         dbus_error_free(&error);
00315         return NULL;
00316     }
00317 
00318     Log3(PCSC_LOG_DEBUG, "Looking a driver for VID: 0x%04X, PID: 0x%04X", idVendor, idProduct);
00319 
00320     classdriver = NULL;
00321     driver = NULL;
00322     /* check if the device is supported by one driver */
00323     for (i=0; i<driverSize; i++)
00324     {
00325         if (driverTracker[i].libraryPath != NULL &&
00326             idVendor == driverTracker[i].manuID &&
00327             idProduct == driverTracker[i].productID)
00328         {
00329             if ((driverTracker[i].CFBundleName != NULL)
00330                 && (0 == strcmp(driverTracker[i].CFBundleName, "CCIDCLASSDRIVER")))
00331                 classdriver = &driverTracker[i];
00332             else
00333                 /* it is not a CCID Class driver */
00334                 driver = &driverTracker[i];
00335         }
00336     }
00337 
00338     /* if we found a specific driver */
00339     if (driver)
00340         return driver;
00341 
00342     /* else return the Class driver */
00343     return classdriver;
00344 }
00345 
00346 
00347 static void HPAddDevice(LibHalContext *ctx, const char *udi)
00348 {
00349     int i;
00350     char deviceName[MAX_DEVICENAME];
00351     struct _driverTracker *driver;
00352     char *sSerialNumber = NULL, *sInterfaceName = NULL;
00353     char fullname[MAX_READERNAME];
00354     LONG ret;
00355 
00356     driver = get_driver(ctx, udi);
00357     if (NULL == driver)
00358     {
00359         /* not a smart card reader */
00360 #ifdef DEBUG_HOTPLUG
00361         Log2(PCSC_LOG_DEBUG, "%s is not a reader", short_name(udi));
00362 #endif
00363         return;
00364     }
00365 
00366     Log2(PCSC_LOG_INFO, "Adding USB device: %s", short_name(udi));
00367 
00368     (void)snprintf(deviceName, sizeof(deviceName), "usb:%04x/%04x:libhal:%s",
00369         driver->manuID, driver->productID, udi);
00370     deviceName[sizeof(deviceName) -1] = '\0';
00371 
00372     /* wait until the device is visible by libusb/etc.  */
00373     (void)SYS_Sleep(1);
00374 
00375     (void)SYS_MutexLock(&usbNotifierMutex);
00376 
00377     /* find a free entry */
00378     for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
00379     {
00380         if (NULL == readerTracker[i].fullName)
00381             break;
00382     }
00383 
00384     if (PCSCLITE_MAX_READERS_CONTEXTS == i)
00385     {
00386         Log2(PCSC_LOG_ERROR,
00387             "Not enough reader entries. Already found %d readers", i);
00388         (void)SYS_MutexUnLock(&usbNotifierMutex);
00389         return;
00390     }
00391 
00392     readerTracker[i].udi = strdup(udi);
00393 
00394 #ifdef ADD_INTERFACE_NAME
00395     if (libhal_device_property_exists(ctx, udi, "usb.interface.description", NULL))
00396         sInterfaceName = libhal_device_get_property_string(ctx, udi,
00397             "usb.interface.description", NULL);
00398 #endif
00399 
00400 #ifdef ADD_SERIAL_NUMBER
00401     if (libhal_device_property_exists(ctx, udi, "usb.serial", NULL))
00402         sSerialNumber = libhal_device_get_property_string(ctx, udi,
00403             "usb.serial", NULL);
00404 #endif
00405     
00406     /* name from the Info.plist file */
00407     strlcpy(fullname, driver->readerName, sizeof(fullname));
00408 
00409     /* interface name from the device (if any) */
00410     if (sInterfaceName)
00411     {
00412         strlcat(fullname, " [", sizeof(fullname));
00413         strlcat(fullname, sInterfaceName, sizeof(fullname));
00414         strlcat(fullname, "]", sizeof(fullname));
00415     }
00416 
00417     /* serial number from the device (if any) */
00418     if (sSerialNumber)
00419     {
00420         strlcat(fullname, " (", sizeof(fullname));
00421         strlcat(fullname, sSerialNumber, sizeof(fullname));
00422         strlcat(fullname, ")", sizeof(fullname));
00423     }
00424 
00425     readerTracker[i].fullName = strdup(fullname);
00426 
00427     ret = RFAddReader(readerTracker[i].fullName, PCSCLITE_HP_BASE_PORT + i,
00428         driver->libraryPath, deviceName);
00429     if ((SCARD_S_SUCCESS != ret) && (SCARD_E_UNKNOWN_READER != ret))
00430     {
00431         char *parent, *device_file;
00432 
00433         /* get the parent descriptor, without the '_if0' */
00434         parent = libhal_device_get_property_string(ctx, udi,
00435             "info.parent", NULL);
00436         if (! parent)
00437             goto error;
00438 
00439         /* get the linux device file: i.e. '/dev/bus/usb/002/012' */
00440         device_file = libhal_device_get_property_string(ctx, parent,
00441             "linux.device_file", NULL);
00442         if (! device_file)
00443             goto error;
00444 
00445         /* check the format looks correct */
00446 #define LIBUSB_HEADER "/dev/bus/usb/"
00447         if (strncmp(device_file, LIBUSB_HEADER, strlen(LIBUSB_HEADER)))
00448             goto error;
00449 
00450         device_file += strlen(LIBUSB_HEADER);
00451 
00452         (void)snprintf(deviceName, sizeof(deviceName),
00453             "usb:%04x/%04x:libusb:%s",
00454             driver->manuID, driver->productID, device_file);
00455         deviceName[sizeof(deviceName) -1] = '\0';
00456 
00457         /* replace the libusb separator '/' by ':' */
00458         if ('/' == deviceName[strlen(deviceName)-3-1])
00459             deviceName[strlen(deviceName)-3-1] = ':';
00460 
00461         Log2(PCSC_LOG_INFO, "trying libusb scheme with: %s", deviceName);
00462         ret = RFAddReader(readerTracker[i].fullName, PCSCLITE_HP_BASE_PORT + i,
00463             driver->libraryPath, deviceName);
00464 
00465         if (SCARD_S_SUCCESS != ret)
00466         {
00467 error:
00468             Log2(PCSC_LOG_ERROR, "Failed adding USB device: %s", short_name(udi));
00469             free(readerTracker[i].fullName);
00470             readerTracker[i].fullName = NULL;
00471             free(readerTracker[i].udi);
00472             readerTracker[i].udi = NULL;
00473 
00474             (void)CheckForOpenCT();
00475         }
00476     }
00477 
00478     (void)SYS_MutexUnLock(&usbNotifierMutex);
00479 } /* HPAddDevice */
00480 
00481 
00482 static void HPRemoveDevice(/*@unused@*/ LibHalContext *ctx, const char *udi)
00483 {
00484     int i;
00485 
00486     (void)ctx;
00487     for (i=0; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
00488     {
00489         if (readerTracker[i].udi && strcmp(readerTracker[i].udi, udi) == 0)
00490             break;
00491     }
00492     if (PCSCLITE_MAX_READERS_CONTEXTS == i)
00493     {
00494 #ifdef DEBUG_HOTPLUG
00495         Log2(PCSC_LOG_DEBUG, "USB device %s not already used", short_name(udi));
00496 #endif
00497         return;
00498     }
00499     Log3(PCSC_LOG_INFO, "Removing USB device[%d]: %s", i,
00500         short_name(readerTracker[i].udi));
00501 
00502     (void)SYS_MutexLock(&usbNotifierMutex);
00503 
00504     (void)RFRemoveReader(readerTracker[i].fullName, PCSCLITE_HP_BASE_PORT + i);
00505     free(readerTracker[i].fullName);
00506     readerTracker[i].fullName = NULL;
00507     free(readerTracker[i].udi);
00508     readerTracker[i].udi = NULL;
00509 
00510     (void)SYS_MutexUnLock(&usbNotifierMutex);
00511 
00512     return;
00513 } /* HPRemoveDevice */
00514 
00515 
00519 ULONG HPRegisterForHotplugEvents(void)
00520 {
00521     char **device_names;
00522     int i, num_devices;
00523     DBusError error;
00524 
00525     if (driverSize <= 0)
00526     {
00527         Log1(PCSC_LOG_INFO, "No bundle files in pcsc drivers directory: " PCSCLITE_HP_DROPDIR);
00528         Log1(PCSC_LOG_INFO, "Disabling USB support for pcscd");
00529         return 1;
00530     }
00531 
00532     dbus_error_init(&error);
00533     conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
00534     if (conn == NULL)
00535     {
00536         Log3(PCSC_LOG_ERROR, "error: dbus_bus_get: %s: %s",
00537             error.name, error.message);
00538         if (dbus_error_is_set(&error))
00539             dbus_error_free(&error);
00540         return 1;
00541     }
00542 
00543     if ((hal_ctx = libhal_ctx_new()) == NULL)
00544     {
00545         Log1(PCSC_LOG_ERROR, "error: libhal_ctx_new");
00546         return 1;
00547     }
00548     if (!libhal_ctx_set_dbus_connection(hal_ctx, conn))
00549     {
00550         Log1(PCSC_LOG_ERROR, "error: libhal_ctx_set_dbus_connection");
00551         return 1;
00552     }
00553     if (!libhal_ctx_init(hal_ctx, &error))
00554     {
00555         if (dbus_error_is_set(&error))
00556         {
00557             Log3(PCSC_LOG_ERROR, "error: libhal_ctx_init: %s: %s",
00558                 error.name, error.message);
00559             if (dbus_error_is_set(&error))
00560                 dbus_error_free(&error);
00561         }
00562         Log1(PCSC_LOG_ERROR, "Could not initialise connection to hald.");
00563         Log1(PCSC_LOG_ERROR, "Normally this means the HAL daemon (hald) is not running or not ready.");
00564         return 1;
00565     }
00566 
00567     /* callback when device added */
00568     (void)libhal_ctx_set_device_added(hal_ctx, HPAddDevice);
00569 
00570     /* callback when device removed */
00571     (void)libhal_ctx_set_device_removed(hal_ctx, HPRemoveDevice);
00572 
00573     device_names = libhal_get_all_devices(hal_ctx, &num_devices, &error);
00574     if (device_names == NULL)
00575     {
00576         if (dbus_error_is_set(&error))
00577             dbus_error_free(&error);
00578         Log1(PCSC_LOG_ERROR, "Couldn't obtain list of devices");
00579         return 1;
00580     }
00581 
00582     /* try to add every present USB devices */
00583     for (i = 0; i < num_devices; i++)
00584         HPAddDevice(hal_ctx, device_names[i]);
00585 
00586     libhal_free_string_array(device_names);
00587 
00588     (void)SYS_ThreadCreate(&usbNotifyThread, THREAD_ATTR_DETACHED,
00589         (PCSCLITE_THREAD_FUNCTION( )) HPEstablishUSBNotifications, NULL);
00590 
00591     return 0;
00592 } /* HPRegisterForHotplugEvents */
00593 
00594 
00595 void HPReCheckSerialReaders(void)
00596 {
00597     /* nothing to do here */
00598 #ifdef DEBUG_HOTPLUG
00599     Log0(PCSC_LOG_ERROR);
00600 #endif
00601 } /* HPReCheckSerialReaders */
00602 
00603 #endif
00604 
Generated by  doxygen 1.6.2-20100208