static LONG HPReadBundleValues(void) { LONG rv; DIR *hpDir; struct dirent *currFP = NULL; char fullPath[FILENAME_MAX]; char fullLibPath[FILENAME_MAX]; char keyValue[TOKEN_MAX_VALUE_SIZE]; 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; while ((currFP = readdir(hpDir)) != 0) { if (strstr(currFP->d_name, ".bundle") != 0) { int alias = 0; /* * 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'; /* while we find a nth ifdVendorID in Info.plist */ while (LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_MANUKEY_NAME, keyValue, alias) == 0) { driverTracker[listCount].bundleName = strdup(currFP->d_name); /* Get ifdVendorID */ rv = LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_MANUKEY_NAME, keyValue, alias); if (rv == 0) driverTracker[listCount].manuID = strtol(keyValue, NULL, 16); /* get ifdProductID */ rv = LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_PRODKEY_NAME, keyValue, alias); if (rv == 0) driverTracker[listCount].productID = strtol(keyValue, NULL, 16); /* get ifdFriendlyName */ rv = LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_NAMEKEY_NAME, keyValue, alias); if (rv == 0) driverTracker[listCount].readerName = strdup(keyValue); /* get CFBundleExecutable */ rv = LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_LIBRKEY_NAME, keyValue, 0); if (rv == 0) { snprintf(fullLibPath, sizeof(fullLibPath), "%s/%s/Contents/%s/%s", PCSCLITE_HP_DROPDIR, currFP->d_name, PCSC_ARCH, keyValue); fullLibPath[sizeof(fullLibPath) - 1] = '\0'; driverTracker[listCount].libraryPath = strdup(fullLibPath); } /* Get ifdCapabilities */ rv = LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_CPCTKEY_NAME, keyValue, 0); if (rv == 0) driverTracker[listCount].ifdCapabilities = strtol(keyValue, NULL, 16); #ifdef DEBUG_HOTPLUG Log2(PCSC_LOG_INFO, "Found driver for: %s", driverTracker[listCount].readerName); #endif alias++; if (NULL == driverTracker[listCount].readerName) continue; 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; } } } } } 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 LONG HPReadBundleValues(void) { LONG rv; DIR *hpDir; struct dirent *currFP = 0; char fullPath[FILENAME_MAX]; char fullLibPath[FILENAME_MAX]; char keyValue[TOKEN_MAX_VALUE_SIZE]; int listCount = 0; hpDir = opendir(PCSCLITE_HP_DROPDIR); if (hpDir == NULL) { Log1(PCSC_LOG_INFO, "Cannot open PC/SC drivers directory: " PCSCLITE_HP_DROPDIR); Log1(PCSC_LOG_INFO, "Disabling USB support for pcscd."); return -1; } while ((currFP = readdir(hpDir)) != 0) { if (strstr(currFP->d_name, ".bundle") != 0) { int alias = 0; /* * The bundle exists - let's form a full path name and get the * vendor and product ID's for this particular bundle */ snprintf(fullPath, FILENAME_MAX, "%s/%s/Contents/Info.plist", PCSCLITE_HP_DROPDIR, currFP->d_name); fullPath[FILENAME_MAX - 1] = '\0'; /* while we find a nth ifdVendorID in Info.plist */ while (LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_MANUKEY_NAME, keyValue, alias) == 0) { bundleTracker[listCount].bundleName = strdup(currFP->d_name); /* Get ifdVendorID */ rv = LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_MANUKEY_NAME, keyValue, alias); if (rv == 0) bundleTracker[listCount].manuID = strtol(keyValue, 0, 16); /* get ifdProductID */ rv = LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_PRODKEY_NAME, keyValue, alias); if (rv == 0) bundleTracker[listCount].productID = strtol(keyValue, 0, 16); /* get ifdFriendlyName */ rv = LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_NAMEKEY_NAME, keyValue, alias); if (rv == 0) bundleTracker[listCount].readerName = strdup(keyValue); /* get CFBundleExecutable */ rv = LTPBundleFindValueWithKey(fullPath, PCSCLITE_HP_LIBRKEY_NAME, keyValue, 0); if (rv == 0) { snprintf(fullLibPath, sizeof(fullLibPath), "%s/%s/Contents/%s/%s", PCSCLITE_HP_DROPDIR, currFP->d_name, PCSC_ARCH, keyValue); fullLibPath[sizeof(fullLibPath) - 1] = '\0'; bundleTracker[listCount].libraryPath = strdup(fullLibPath); } listCount++; alias++; if (listCount >= sizeof(bundleTracker)/sizeof(bundleTracker[0])) { Log2(PCSC_LOG_CRITICAL, "Too many readers declared. Maximum is %d", sizeof(bundleTracker)/sizeof(bundleTracker[0])); goto end; } } } } end: bundleSize = listCount; if (bundleSize == 0) { Log1(PCSC_LOG_INFO, "No bundle files in pcsc drivers directory: " PCSCLITE_HP_DROPDIR); Log1(PCSC_LOG_INFO, "Disabling USB support for pcscd"); } closedir(hpDir); return 0; }
/***************************************************************************** * * OpenUSBByName * ****************************************************************************/ status_t OpenUSBByName(unsigned int reader_index, /*@null@*/ char *device) { static struct usb_bus *busses = NULL; int alias = 0; struct usb_bus *bus; struct usb_dev_handle *dev_handle; char keyValue[TOKEN_MAX_VALUE_SIZE]; unsigned int vendorID, productID; char infofile[FILENAME_MAX]; #ifndef __APPLE__ unsigned int device_vendor, device_product; #endif char *dirname = NULL, *filename = NULL; int interface_number = -1; static int previous_reader_index = -1; DEBUG_COMM3("Reader index: %X, Device: %s", reader_index, device); #ifndef __APPLE__ /* device name specified */ if (device) { /* format: usb:%04x/%04x, vendor, product */ if (strncmp("usb:", device, 4) != 0) { DEBUG_CRITICAL2("device name does not start with \"usb:\": %s", device); return STATUS_UNSUCCESSFUL; } if (sscanf(device, "usb:%x/%x", &device_vendor, &device_product) != 2) { DEBUG_CRITICAL2("device name can't be parsed: %s", device); return STATUS_UNSUCCESSFUL; } /* format usb:%04x/%04x:libusb:%s * with %s set to %s:%s, dirname, filename */ if ((dirname = strstr(device, "libusb:")) != NULL) { /* dirname points to the first char after libusb: */ dirname += strlen("libusb:"); /* search the : (separation) char */ filename = strchr(dirname, ':'); if (filename) { /* end the dirname string */ *filename = '\0'; /* filename points to the first char after : */ filename++; } else { /* parse failed */ dirname = NULL; DEBUG_CRITICAL2("can't parse using libusb scheme: %s", device); } } /* format usb:%04x/%04x:libhal:%s * with %s set to * /org/freedesktop/Hal/devices/usb_device_VID_PID_SERIAL_ifX * VID is VendorID * PID is ProductID * SERIAL is device serial number * X is the interface number */ if ((dirname = strstr(device, "libhal:")) != NULL) { const char *p; #define HAL_HEADER "usb_device_" /* parse the hal string */ if ( /* search the last '/' char */ (p = strrchr(dirname, '/')) /* if the string starts with "usb_device_" we continue */ && (0 == strncmp(++p, HAL_HEADER, sizeof(HAL_HEADER)-1)) /* skip the HAL header */ && (p += sizeof(HAL_HEADER)-1) /* search the last '_' */ && (p = strrchr(++p, '_')) && (0 == strncmp(++p, "if", 2)) ) { /* convert the interface number */ interface_number = atoi(p+2); } else DEBUG_CRITICAL2("can't parse using libhal scheme: %s", device); /* dirname was just a temporary variable */ dirname = NULL; } } #endif if (busses == NULL) usb_init(); (void)usb_find_busses(); (void)usb_find_devices(); busses = usb_get_busses(); if (busses == NULL) { DEBUG_CRITICAL("No USB busses found"); return STATUS_UNSUCCESSFUL; } /* is the reader_index already used? */ if (usbDevice[reader_index].handle != NULL) { DEBUG_CRITICAL2("USB driver with index %X already in use", reader_index); return STATUS_UNSUCCESSFUL; } /* Info.plist full patch filename */ (void)snprintf(infofile, sizeof(infofile), "%s/%s/Contents/Info.plist", PCSCLITE_HP_DROPDIR, BUNDLE); /* general driver info */ if (!LTPBundleFindValueWithKey(infofile, "ifdManufacturerString", keyValue, 0)) { DEBUG_INFO2("Manufacturer: %s", keyValue); } else { DEBUG_INFO2("LTPBundleFindValueWithKey error. Can't find %s?", infofile); return STATUS_UNSUCCESSFUL; } if (!LTPBundleFindValueWithKey(infofile, "ifdProductString", keyValue, 0)) { DEBUG_INFO2("ProductString: %s", keyValue); } else return STATUS_UNSUCCESSFUL; if (!LTPBundleFindValueWithKey(infofile, "Copyright", keyValue, 0)) { DEBUG_INFO2("Copyright: %s", keyValue); } else return STATUS_UNSUCCESSFUL; vendorID = strlen(keyValue); alias = 0x1C; for (; vendorID--;) alias ^= keyValue[vendorID]; /* for any supported reader */ while (LTPBundleFindValueWithKey(infofile, PCSCLITE_MANUKEY_NAME, keyValue, alias) == 0) { vendorID = strtoul(keyValue, NULL, 0); if (LTPBundleFindValueWithKey(infofile, PCSCLITE_PRODKEY_NAME, keyValue, alias)) goto end; productID = strtoul(keyValue, NULL, 0); if (LTPBundleFindValueWithKey(infofile, PCSCLITE_NAMEKEY_NAME, keyValue, alias)) goto end; /* go to next supported reader for next round */ alias++; #ifndef __APPLE__ /* the device was specified but is not the one we are trying to find */ if (device && (vendorID != device_vendor || productID != device_product)) continue; #else /* Leopard puts the friendlyname in the device argument */ if (device && strcmp(device, keyValue)) continue; #endif /* on any USB buses */ for (bus = busses; bus; bus = bus->next) { struct usb_device *dev; /* any device on this bus */ for (dev = bus->devices; dev; dev = dev->next) { /* device defined by name? */ if (dirname && (strcmp(dirname, bus->dirname) || strcmp(filename, dev->filename))) continue; if (dev->descriptor.idVendor == vendorID && dev->descriptor.idProduct == productID) { int r, already_used; struct usb_interface *usb_interface = NULL; int interface; int num = 0; #ifdef USE_COMPOSITE_AS_MULTISLOT static int static_interface = 1; { /* simulate a composite device as when libhal is * used */ int readerID = (vendorID << 16) + productID; if ((GEMALTOPROXDU == readerID) || (GEMALTOPROXSU == readerID)) { if(interface_number >= 0) { DEBUG_CRITICAL("USE_COMPOSITE_AS_MULTISLOT can't be used with libhal"); continue; } /* the CCID interfaces are 1 and 2 */ interface_number = static_interface; } } #endif /* is it already opened? */ already_used = FALSE; DEBUG_COMM3("Checking device: %s/%s", bus->dirname, dev->filename); for (r=0; r<CCID_DRIVER_MAX_READERS; r++) { if (usbDevice[r].handle) { /* same busname, same filename */ if (strcmp(usbDevice[r].dirname, bus->dirname) == 0 && strcmp(usbDevice[r].filename, dev->filename) == 0) already_used = TRUE; } } /* this reader is already managed by us */ if (already_used) { if ((previous_reader_index != -1) && usbDevice[previous_reader_index].handle && (strcmp(usbDevice[previous_reader_index].dirname, bus->dirname) == 0) && (strcmp(usbDevice[previous_reader_index].filename, dev->filename) == 0) && usbDevice[previous_reader_index].ccid.bCurrentSlotIndex < usbDevice[previous_reader_index].ccid.bMaxSlotIndex) { /* we reuse the same device * and the reader is multi-slot */ usbDevice[reader_index] = usbDevice[previous_reader_index]; /* the other slots do not have the same data rates */ if ((GEMCOREPOSPRO == usbDevice[reader_index].ccid.readerID) || (GEMCORESIMPRO == usbDevice[reader_index].ccid.readerID)) { usbDevice[reader_index].ccid.arrayOfSupportedDataRates = SerialCustomDataRates; usbDevice[reader_index].ccid.dwMaxDataRate = 125000; } *usbDevice[reader_index].nb_opened_slots += 1; usbDevice[reader_index].ccid.bCurrentSlotIndex++; usbDevice[reader_index].ccid.dwSlotStatus = IFD_ICC_PRESENT; DEBUG_INFO2("Opening slot: %d", usbDevice[reader_index].ccid.bCurrentSlotIndex); goto end; } else { /* if an interface number is given by HAL we * continue with this device. */ if (-1 == interface_number) { DEBUG_INFO3("USB device %s/%s already in use." " Checking next one.", bus->dirname, dev->filename); continue; } } } DEBUG_COMM3("Trying to open USB bus/device: %s/%s", bus->dirname, dev->filename); dev_handle = usb_open(dev); if (dev_handle == NULL) { DEBUG_CRITICAL4("Can't usb_open(%s/%s): %s", bus->dirname, dev->filename, strerror(errno)); continue; } /* now we found a free reader and we try to use it */ if (dev->config == NULL) { (void)usb_close(dev_handle); DEBUG_CRITICAL3("No dev->config found for %s/%s", bus->dirname, dev->filename); return STATUS_UNSUCCESSFUL; } again: usb_interface = get_ccid_usb_interface(dev, &num); if (usb_interface == NULL) { (void)usb_close(dev_handle); if (0 == num) DEBUG_CRITICAL3("Can't find a CCID interface on %s/%s", bus->dirname, dev->filename); interface_number = -1; continue; } if (usb_interface->altsetting->extralen != 54) { (void)usb_close(dev_handle); DEBUG_CRITICAL4("Extra field for %s/%s has a wrong length: %d", bus->dirname, dev->filename, usb_interface->altsetting->extralen); return STATUS_UNSUCCESSFUL; } interface = usb_interface->altsetting->bInterfaceNumber; if (interface_number >= 0 && interface != interface_number) { /* an interface was specified and it is not the * current one */ DEBUG_INFO3("Wrong interface for USB device %s/%s." " Checking next one.", bus->dirname, dev->filename); /* check for another CCID interface on the same device */ num++; goto again; } if (usb_claim_interface(dev_handle, interface) < 0) { (void)usb_close(dev_handle); DEBUG_CRITICAL4("Can't claim interface %s/%s: %s", bus->dirname, dev->filename, strerror(errno)); interface_number = -1; continue; } DEBUG_INFO4("Found Vendor/Product: %04X/%04X (%s)", dev->descriptor.idVendor, dev->descriptor.idProduct, keyValue); DEBUG_INFO3("Using USB bus/device: %s/%s", bus->dirname, dev->filename); /* check for firmware bugs */ if (ccid_check_firmware(dev)) { (void)usb_close(dev_handle); return STATUS_UNSUCCESSFUL; } #ifdef USE_COMPOSITE_AS_MULTISLOT /* use the next interface for the next "slot" */ static_interface++; /* reset for a next reader */ if (static_interface > 2) static_interface = 1; #endif /* Get Endpoints values*/ (void)get_end_points(dev, &usbDevice[reader_index], num); /* store device information */ usbDevice[reader_index].handle = dev_handle; usbDevice[reader_index].dirname = strdup(bus->dirname); usbDevice[reader_index].filename = strdup(dev->filename); usbDevice[reader_index].interface = interface; usbDevice[reader_index].real_nb_opened_slots = 1; usbDevice[reader_index].nb_opened_slots = &usbDevice[reader_index].real_nb_opened_slots; /* CCID common informations */ usbDevice[reader_index].ccid.real_bSeq = 0; usbDevice[reader_index].ccid.pbSeq = &usbDevice[reader_index].ccid.real_bSeq; usbDevice[reader_index].ccid.readerID = (dev->descriptor.idVendor << 16) + dev->descriptor.idProduct; usbDevice[reader_index].ccid.dwFeatures = dw2i(usb_interface->altsetting->extra, 40); usbDevice[reader_index].ccid.wLcdLayout = (usb_interface->altsetting->extra[51] << 8) + usb_interface->altsetting->extra[50]; usbDevice[reader_index].ccid.bPINSupport = usb_interface->altsetting->extra[52]; usbDevice[reader_index].ccid.dwMaxCCIDMessageLength = dw2i(usb_interface->altsetting->extra, 44); usbDevice[reader_index].ccid.dwMaxIFSD = dw2i(usb_interface->altsetting->extra, 28); usbDevice[reader_index].ccid.dwDefaultClock = dw2i(usb_interface->altsetting->extra, 10); usbDevice[reader_index].ccid.dwMaxDataRate = dw2i(usb_interface->altsetting->extra, 23); usbDevice[reader_index].ccid.bMaxSlotIndex = usb_interface->altsetting->extra[4]; usbDevice[reader_index].ccid.bCurrentSlotIndex = 0; usbDevice[reader_index].ccid.readTimeout = DEFAULT_COM_READ_TIMEOUT; usbDevice[reader_index].ccid.arrayOfSupportedDataRates = get_data_rates(reader_index, dev, num); usbDevice[reader_index].ccid.bInterfaceProtocol = usb_interface->altsetting->bInterfaceProtocol; usbDevice[reader_index].ccid.bNumEndpoints = usb_interface->altsetting->bNumEndpoints; usbDevice[reader_index].ccid.dwSlotStatus = IFD_ICC_PRESENT; usbDevice[reader_index].ccid.bVoltageSupport = usb_interface->altsetting->extra[5]; goto end; } } } } end: if (usbDevice[reader_index].handle == NULL) return STATUS_NO_SUCH_DEVICE; /* memorise the current reader_index so we can detect * a new OpenUSBByName on a multi slot reader */ previous_reader_index = reader_index; return STATUS_SUCCESS; } /* OpenUSBByName */
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 (NULL == hpDir) { 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 */ driverSize = DRIVER_TRACKER_INITIAL_SIZE; driverTracker = calloc(driverSize, sizeof(*driverTracker)); if (NULL == driverTracker) { Log1(PCSC_LOG_CRITICAL, "Not enough memory"); return -1; } #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 *CFBundleName; char *libraryPath; /* * The bundle exists - let's form a full path name and get the * vendor and product ID's for this particular bundle */ (void)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_KEY(PCSCLITE_HP_MANUKEY_NAME, &manuIDs) GET_KEY(PCSCLITE_HP_PRODKEY_NAME, &productIDs) GET_KEY(PCSCLITE_HP_NAMEKEY_NAME, &readerNames) /* Get CFBundleName */ rv = LTPBundleFindValueWithKey(&plist, PCSCLITE_HP_CFBUNDLE_NAME, &values); if (rv) CFBundleName = NULL; else CFBundleName = strdup(list_get_at(values, 0)); /* 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].CFBundleName = CFBundleName; #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].CFBundleName = NULL; } } } bundleRelease(&plist); } } driverSize = listCount; (void)closedir(hpDir); #ifdef DEBUG_HOTPLUG Log2(PCSC_LOG_INFO, "Found drivers for %d readers", listCount); #endif return 0; } /* HPReadBundleValues */