/* * Copyright (c) 2000-2002 Apple Computer, Inc. All Rights Reserved. * The contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please * obtain a copy of the License at http://www.apple.com/publicsource and * read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please * see the License for the specific language governing rights and * limitations under the License. */ /****************************************************************** MUSCLE SmartCard Development ( http://www.linuxnet.com ) Title : hotplug_macosx.c Package: pcsc lite Author : Stephen M. Webb Date : 03 Dec 2002 License: Copyright (C) 2002 David Corcoran Purpose: This provides a search API for hot pluggble devices. ********************************************************************/ #include #include #include #include #include #include #include "config.h" #include "wintypes.h" #include "pcsclite.h" #include "debuglog.h" #include "hotplug.h" #include "readerfactory.h" #include "thread_generic.h" #define PCSCLITE_HP_DROPDIR "/usr/libexec/SmartCardServices/drivers/" #define PCSCLITE_HP_MANUKEY_NAME "ifdVendorID" #define PCSCLITE_HP_PRODKEY_NAME "ifdProductID" #define PCSCLITE_HP_NAMEKEY_NAME "ifdFriendlyName" #define PCSCLITE_HP_BASE_PORT 0x200000 /* * An aggregation of useful information on a driver bundle in the * drop directory. */ typedef struct HPDriver { UInt32 m_vendorId; /* unique vendor's manufacturer code */ UInt32 m_productId; /* manufacturer's unique product code */ char* m_friendlyName; /* bundle friendly name */ char* m_libPath; /* bundle's plugin library location */ } HPDriver, *HPDriverVector; /* * An aggregation on information on currently active reader drivers. */ typedef struct HPDevice { HPDriver* m_driver; /* driver bundle information */ UInt32 m_address; /* unique system address of device */ struct HPDevice* m_next; /* next device in list */ } HPDevice, *HPDeviceList; /* * Pointer to a list of (currently) known hotplug reader devices (and their * drivers). */ static HPDeviceList sDeviceList = NULL; /* * A callback to handle the asynchronous appearance of new devices that are * candidates for PCSC readers. */ static void HPDeviceAppeared(void* refCon, io_iterator_t iterator) { kern_return_t kret; io_service_t obj; while ((obj = IOIteratorNext(iterator))) { kret = IOObjectRelease(obj); } HPSearchHotPluggables(); } /* * A callback to handle the asynchronous disappearance of devices that are * possibly PCSC readers. */ static void HPDeviceDisappeared(void* refCon, io_iterator_t iterator) { kern_return_t kret; io_service_t obj; while ((obj = IOIteratorNext(iterator))) { kret = IOObjectRelease(obj); } HPSearchHotPluggables(); } /* * Creates a vector of driver bundle info structures from the hot-plug driver * directory. * * Returns NULL on error and a pointer to an allocated HPDriver vector on * success. The caller must free the HPDriver with a call to * HPDriversRelease(). */ static HPDriverVector HPDriversGetFromDirectory(const char* driverBundlePath) { HPDriverVector bundleVector = NULL; CFArrayRef bundleArray; CFStringRef driverBundlePathString = CFStringCreateWithCString(kCFAllocatorDefault, driverBundlePath, kCFStringEncodingMacRoman); CFURLRef pluginUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, driverBundlePathString, kCFURLPOSIXPathStyle, TRUE); CFRelease(driverBundlePathString); if (!pluginUrl) { DebugLogA("error getting plugin directory URL"); return bundleVector; } bundleArray = CFBundleCreateBundlesFromDirectory(kCFAllocatorDefault, pluginUrl, NULL); if (!bundleArray) { DebugLogA("error getting plugin directory bundles"); return bundleVector; } CFRelease(pluginUrl); size_t bundleArraySize = CFArrayGetCount(bundleArray); bundleVector = (HPDriver*)calloc(bundleArraySize + 1, sizeof(HPDriver)); if (!bundleVector) { DebugLogA("memory allocation failure"); return bundleVector; } int i = 0; for (; i < bundleArraySize; ++i) { HPDriver* driverBundle = bundleVector + i; CFBundleRef currBundle = (CFBundleRef)CFArrayGetValueAtIndex(bundleArray, i); CFDictionaryRef dict = CFBundleGetInfoDictionary(currBundle); CFURLRef bundleUrl = CFBundleCopyBundleURL(currBundle); CFStringRef bundlePath = CFURLCopyPath(bundleUrl); driverBundle->m_libPath = strdup(CFStringGetCStringPtr(bundlePath, CFStringGetSystemEncoding())); CFStringRef strValue = (CFStringRef)CFDictionaryGetValue(dict, CFSTR(PCSCLITE_HP_MANUKEY_NAME)); if (!strValue) { DebugLogA("error getting vendor ID from bundle"); return bundleVector; } driverBundle->m_vendorId = strtoul(CFStringGetCStringPtr(strValue, CFStringGetSystemEncoding()), NULL, 16); strValue = (CFStringRef)CFDictionaryGetValue(dict, CFSTR(PCSCLITE_HP_PRODKEY_NAME)); if (!strValue) { DebugLogA("error getting product ID from bundle"); return bundleVector; } driverBundle->m_productId = strtoul(CFStringGetCStringPtr(strValue, CFStringGetSystemEncoding()), NULL, 16); strValue = (CFStringRef)CFDictionaryGetValue(dict, CFSTR(PCSCLITE_HP_NAMEKEY_NAME)); if (!strValue) { DebugLogA("error getting product friendly name from bundle"); driverBundle->m_friendlyName = strdup("unnamed device"); } else { const char* cstr = CFStringGetCStringPtr(strValue, CFStringGetSystemEncoding()); driverBundle->m_friendlyName = strdup(cstr); } } CFRelease(bundleArray); return bundleVector; } /* * Copies a driver bundle instance. */ static HPDriver* HPDriverCopy(HPDriver* rhs) { if (!rhs) { return NULL; } HPDriver* newDriverBundle = (HPDriver*)calloc(1, sizeof(HPDriver)); if (!newDriverBundle) { return NULL; } newDriverBundle->m_vendorId = rhs->m_vendorId; newDriverBundle->m_productId = rhs->m_productId; newDriverBundle->m_friendlyName = strdup(rhs->m_friendlyName); newDriverBundle->m_libPath = strdup(rhs->m_libPath); return newDriverBundle; } /* * Releases resources allocated to a driver bundle vector. */ static void HPDriverRelease(HPDriver* driverBundle) { if (driverBundle) { free(driverBundle->m_friendlyName); free(driverBundle->m_libPath); } } /* * Releases resources allocated to a driver bundle vector. */ static void HPDriverVectorRelease(HPDriverVector driverBundleVector) { if (driverBundleVector) { HPDriver* b = driverBundleVector; for (; b->m_vendorId; ++b) { HPDriverRelease(b); } free(driverBundleVector); } } /* * Inserts a new reader device in the list. */ static HPDeviceList HPDeviceListInsert(HPDeviceList list, HPDriver* bundle, UInt32 address) { HPDevice* newReader = (HPDevice*)calloc(1, sizeof(HPDevice)); if (!newReader) { DebugLogA("memory allocation failure"); return list; } newReader->m_driver = HPDriverCopy(bundle); newReader->m_address = address; newReader->m_next = list; return newReader; } /* * Frees resources allocated to a HPDeviceList. */ static void HPDeviceListRelease(HPDeviceList list) { HPDevice* p = list; for (; p; p = p->m_next) { HPDriverRelease(p->m_driver); } } /* * Compares two driver bundle instances for equality. */ static int HPDeviceEquals(HPDevice* a, HPDevice* b) { return (a->m_driver->m_vendorId == b->m_driver->m_vendorId) && (a->m_driver->m_productId == b->m_driver->m_productId) && (a->m_address == b->m_address); } /* * Finds USB devices currently registered in the system that match any of * the drivers detected in the driver bundle vector. */ static int HPDriversMatchUSBDevices(HPDriverVector driverBundle, HPDeviceList* readerList) { CFDictionaryRef usbMatch = IOServiceMatching("IOUSBDevice"); if (0 == usbMatch) { DebugLogA("error getting USB match from IOServiceMatching()"); return 1; } io_iterator_t usbIter; kern_return_t kret = IOServiceGetMatchingServices(kIOMasterPortDefault, usbMatch, &usbIter); if (kret != 0) { DebugLogA("error getting iterator from IOServiceGetMatchingServices()"); return 1; } IOIteratorReset(usbIter); io_object_t usbDevice = 0; while ((usbDevice = IOIteratorNext(usbIter))) { char namebuf[1024]; kret = IORegistryEntryGetName(usbDevice, namebuf); if (kret != 0) { DebugLogA("error getting device name from IORegistryEntryGetName()"); return 1; } IOCFPlugInInterface** iodev; SInt32 score; kret = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &iodev, &score); if (kret != 0) { DebugLogA("error getting plugin interface from IOCreatePlugInInterfaceForService()"); return 1; } IOObjectRelease(usbDevice); IOUSBDeviceInterface** usbdev; HRESULT hres = (*iodev)->QueryInterface(iodev, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*)&usbdev); (*iodev)->Release(iodev); if (hres) { DebugLogA("error querying interface in QueryInterface()"); return 1; } UInt16 vendorId = 0; UInt16 productId = 0; UInt32 usbAddress = 0; kret = (*usbdev)->GetDeviceVendor(usbdev, &vendorId); kret = (*usbdev)->GetDeviceProduct(usbdev, &productId); kret = (*usbdev)->GetLocationID(usbdev, &usbAddress); (*usbdev)->Release(usbdev); HPDriver* driver = driverBundle; for (; driver->m_vendorId; ++driver) { if ((driver->m_vendorId == vendorId) && (driver->m_productId == productId)) { *readerList = HPDeviceListInsert(*readerList, driver, usbAddress); } } } IOObjectRelease(usbIter); return 0; } /* * Finds PC Card devices currently registered in the system that match any of * the drivers detected in the driver bundle vector. */ static int HPDriversMatchPCCardDevices(HPDriver* driverBundle, HPDeviceList* readerList) { CFDictionaryRef pccMatch = IOServiceMatching("IOPCCard16Device"); if (0 == pccMatch) { DebugLogA("error getting PCCard match from IOServiceMatching()"); return 1; } io_iterator_t pccIter; kern_return_t kret = IOServiceGetMatchingServices(kIOMasterPortDefault, pccMatch, &pccIter); if (kret != 0) { DebugLogA("error getting iterator from IOServiceGetMatchingServices()"); return 1; } IOIteratorReset(pccIter); io_object_t pccDevice = 0; while ((pccDevice = IOIteratorNext(pccIter))) { char namebuf[1024]; kret = IORegistryEntryGetName(pccDevice, namebuf); if (kret != 0) { DebugLogA("error getting plugin interface from IOCreatePlugInInterfaceForService()"); return 1; } UInt32 vendorId = 0; UInt32 productId = 0; UInt32 pccAddress = 0; CFTypeRef valueRef = IORegistryEntryCreateCFProperty(pccDevice, CFSTR("VendorID"), kCFAllocatorDefault, NULL); if (!valueRef) { DebugLogA("error getting vendor"); } else { CFNumberGetValue((CFNumberRef)valueRef, kCFNumberSInt32Type, &vendorId); } valueRef = IORegistryEntryCreateCFProperty(pccDevice, CFSTR("DeviceID"), kCFAllocatorDefault, NULL); if (!valueRef) { DebugLogA("error getting device"); } else { CFNumberGetValue((CFNumberRef)valueRef, kCFNumberSInt32Type, &productId); } valueRef = IORegistryEntryCreateCFProperty(pccDevice, CFSTR("SocketNumber"), kCFAllocatorDefault, NULL); if (!valueRef) { DebugLogA("error getting PC Card socket"); } else { CFNumberGetValue((CFNumberRef)valueRef, kCFNumberSInt32Type, &pccAddress); } HPDriver* driver = driverBundle; for (; driver->m_vendorId; ++driver) { if ((driver->m_vendorId == vendorId) && (driver->m_productId == productId)) { *readerList = HPDeviceListInsert(*readerList, driver, pccAddress); } } } IOObjectRelease(pccIter); return 0; } static void HPEstablishUSBNotification() { io_iterator_t deviceAddedIterator; io_iterator_t deviceRemovedIterator; CFMutableDictionaryRef matchingDictionary; IONotificationPortRef notificationPort; IOReturn kret; notificationPort = IONotificationPortCreate(kIOMasterPortDefault); CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notificationPort), kCFRunLoopDefaultMode); matchingDictionary = IOServiceMatching("IOUSBDevice"); if (!matchingDictionary) { DebugLogB("IOServiceMatching() failed", 0); } matchingDictionary = (CFMutableDictionaryRef)CFRetain(matchingDictionary); kret = IOServiceAddMatchingNotification(notificationPort, kIOMatchedNotification, matchingDictionary, HPDeviceAppeared, NULL, &deviceAddedIterator); if (kret) { DebugLogB("IOServiceAddMatchingNotification()-1 failed with code %d", kret); } HPDeviceAppeared(NULL, deviceAddedIterator); kret = IOServiceAddMatchingNotification(notificationPort, kIOTerminatedNotification, matchingDictionary, HPDeviceDisappeared, NULL, &deviceRemovedIterator); if (kret) { DebugLogB("IOServiceAddMatchingNotification()-2 failed with code %d", kret); } HPDeviceDisappeared(NULL, deviceRemovedIterator); } static void HPEstablishPCCardNotification() { io_iterator_t deviceAddedIterator; io_iterator_t deviceRemovedIterator; CFMutableDictionaryRef matchingDictionary; IONotificationPortRef notificationPort; IOReturn kret; notificationPort = IONotificationPortCreate(kIOMasterPortDefault); CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(notificationPort), kCFRunLoopDefaultMode); matchingDictionary = IOServiceMatching("IOPCCard16Device"); if (!matchingDictionary) { DebugLogB("IOServiceMatching() failed", 0); } matchingDictionary = (CFMutableDictionaryRef)CFRetain(matchingDictionary); kret = IOServiceAddMatchingNotification(notificationPort, kIOMatchedNotification, matchingDictionary, HPDeviceAppeared, NULL, &deviceAddedIterator); if (kret) { DebugLogB("IOServiceAddMatchingNotification()-1 failed with code %d", kret); } HPDeviceAppeared(NULL, deviceAddedIterator); kret = IOServiceAddMatchingNotification(notificationPort, kIOTerminatedNotification, matchingDictionary, HPDeviceDisappeared, NULL, &deviceRemovedIterator); if (kret) { DebugLogB("IOServiceAddMatchingNotification()-2 failed with code %d", kret); } HPDeviceDisappeared(NULL, deviceRemovedIterator); } /* * Thread runner (does not return). */ static void HPDeviceNotificationThread() { HPEstablishUSBNotification(); HPEstablishPCCardNotification(); CFRunLoopRun(); } /* * Scans the hotplug driver directory and looks in the system for matching devices. * Adds or removes matching readers as necessary. */ LONG HPSearchHotPluggables() { HPDriver* drivers = HPDriversGetFromDirectory(PCSCLITE_HP_DROPDIR); if (!drivers) return 1; HPDeviceList devices = NULL; int istat; istat = HPDriversMatchUSBDevices(drivers, &devices); if (istat) { return -1; } istat = HPDriversMatchPCCardDevices(drivers, &devices); if (istat) { return -1; } HPDevice* a = devices; for (; a; a = a->m_next) { int found = 0; HPDevice* b = sDeviceList; for (; b; b = b->m_next) { if (HPDeviceEquals(a, b)) { found = 1; break; } } if (!found) { RFAddReader(a->m_driver->m_friendlyName, PCSCLITE_HP_BASE_PORT + a->m_address, a->m_driver->m_libPath); } } a = sDeviceList; for (; a; a = a->m_next) { int found = 0; HPDevice* b = devices; for (; b; b = b->m_next) { if (HPDeviceEquals(a, b)) { found = 1; break; } } if (!found) { RFRemoveReader(a->m_driver->m_friendlyName, PCSCLITE_HP_BASE_PORT + a->m_address); } } HPDeviceListRelease(sDeviceList); sDeviceList = devices; HPDriverVectorRelease(drivers); return 0; } PCSCLITE_THREAD_T sHotplugWatcherThread; /* * Sets up callbacks for device hotplug events. */ LONG HPRegisterForHotplugEvents() { LONG sstat; sstat = SYS_ThreadCreate(&sHotplugWatcherThread, NULL, (LPVOID)HPDeviceNotificationThread, NULL); return 0; }