#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/usb/IOUSBLib.h>
#include <stdlib.h>
#include <string.h>
#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_IFACECLASSKEY_NAME "ifdInterfaceClass"
#define PCSCLITE_HP_IFACESUBCLASSKEY_NAME "ifdInterfaceSubClass"
#define PCSCLITE_HP_IFACEPROTOCOLKEY_NAME "ifdInterfaceProtocol"
#define PCSCLITE_HP_BASE_PORT 0x200000
typedef enum
{
PCSCLITE_HP_Proprietary = 0,
PCSCLITE_HP_InterfaceClass = 1,
} HPDriverType;
typedef struct HPDriver
{
UInt8 m_NotEOV;
UInt8 m_initialized;
HPDriverType m_type;
UInt32 m_vendorId;
UInt32 m_productId;
UInt8 m_class;
UInt8 m_subClass;
UInt8 m_protocol;
char* m_friendlyName;
char* m_libPath;
} HPDriver, *HPDriverVector;
typedef struct HPDevice
{
HPDriver* m_driver;
UInt32 m_address;
struct HPDevice* m_next;
} HPDevice, *HPDeviceList;
static HPDeviceList sDeviceList = NULL;
static IONotificationPortRef sNotificationPort = NULL;
static io_iterator_t sUSBAppearedIter = NULL;
static io_iterator_t sUSBRemovedIter = NULL;
static io_iterator_t sPCCardAppearedIter = NULL;
static io_iterator_t sPCCardRemovedIter = NULL;
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();
}
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();
}
static HPDriverVector
HPDriversGetFromDirectory(const char* driverBundlePath)
{
HPDriverVector bundleVector = NULL;
CFArrayRef bundleArray;
CFStringRef driverBundlePathString;
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;
driverBundle->m_NotEOV = 1;
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()));
if (driverBundle->m_libPath == NULL)
{
DebugLogA("memory allocation failure");
return bundleVector;
}
UInt32 vendorId = 0;
UInt8 gotVendorId = 0;
UInt32 productId = 0;
UInt8 gotProductId = 0;
CFStringRef strValue = (CFStringRef)CFDictionaryGetValue(dict,
CFSTR(PCSCLITE_HP_MANUKEY_NAME));
if (strValue)
{
gotVendorId = 1;
vendorId = strtoul(CFStringGetCStringPtr(strValue,
CFStringGetSystemEncoding()),
NULL, 16);
strValue = (CFStringRef)CFDictionaryGetValue(dict,
CFSTR(PCSCLITE_HP_PRODKEY_NAME));
if (strValue)
{
gotProductId = 1;
productId = strtoul(CFStringGetCStringPtr(strValue,
CFStringGetSystemEncoding()),
NULL, 16);
}
}
if (gotVendorId && gotProductId)
{
driverBundle->m_productId = productId;
driverBundle->m_vendorId = vendorId;
driverBundle->m_type = PCSCLITE_HP_Proprietary;
}
else
{
UInt8 class;
UInt8 subClass;
UInt8 protocol;
strValue = (CFStringRef)CFDictionaryGetValue(dict,
CFSTR(PCSCLITE_HP_IFACECLASSKEY_NAME));
if (strValue)
{
class = (UInt8) strtoul(CFStringGetCStringPtr(strValue,
CFStringGetSystemEncoding()),
NULL, 16);
driverBundle->m_class = class;
}
else
{
DebugLogB("Malformed bundle (class absent) in driver folder: %s. Will be ignored",
driverBundle->m_libPath);
free(driverBundle->m_libPath);
driverBundle->m_libPath = NULL;
continue;
}
strValue = (CFStringRef)CFDictionaryGetValue(dict,
CFSTR(PCSCLITE_HP_IFACESUBCLASSKEY_NAME));
if (strValue)
{
subClass = (UInt8) strtoul(CFStringGetCStringPtr(strValue,
CFStringGetSystemEncoding()),
NULL, 16);
driverBundle->m_subClass = subClass;
}
else
{
DebugLogB("Malformed bundle (subClass absent) in driver folder: %s. Will be ignored",
driverBundle->m_libPath);
free(driverBundle->m_libPath);
driverBundle->m_libPath = NULL;
continue;
}
strValue = (CFStringRef)CFDictionaryGetValue(dict,
CFSTR(PCSCLITE_HP_IFACEPROTOCOLKEY_NAME));
if (strValue)
{
protocol = (UInt8) strtoul(CFStringGetCStringPtr(strValue,
CFStringGetSystemEncoding()),
NULL, 16);
driverBundle->m_protocol = protocol;
}
else
{
DebugLogB("Malformed bundle (protocol absent) in driver folder: %s. Will be ignored",
driverBundle->m_libPath);
free(driverBundle->m_libPath);
driverBundle->m_libPath = NULL;
continue;
}
driverBundle->m_type = PCSCLITE_HP_InterfaceClass;
}
strValue = (CFStringRef)CFDictionaryGetValue(dict,
CFSTR(PCSCLITE_HP_NAMEKEY_NAME));
if (!strValue)
{
DebugLogB("Product friendly name absent in driver folder: %s.",
driverBundle->m_libPath);
driverBundle->m_friendlyName = strdup("unnamed device");
}
else
{
const char* cstr = CFStringGetCStringPtr(strValue,
CFStringGetSystemEncoding());
driverBundle->m_friendlyName = strdup(cstr);
}
driverBundle->m_initialized = 1;
}
CFRelease(bundleArray);
return bundleVector;
}
static HPDriver*
HPDriverCopy(HPDriver* rhs)
{
if (!rhs)
{
return NULL;
}
HPDriver* newDriverBundle = (HPDriver*)calloc(1, sizeof(HPDriver));
if (!newDriverBundle)
{
return NULL;
}
newDriverBundle->m_initialized = rhs->m_initialized;
newDriverBundle->m_type = rhs->m_type;
newDriverBundle->m_vendorId = rhs->m_vendorId;
newDriverBundle->m_productId = rhs->m_productId;
newDriverBundle->m_class = rhs->m_class;
newDriverBundle->m_subClass = rhs->m_subClass;
newDriverBundle->m_friendlyName = strdup(rhs->m_friendlyName);
newDriverBundle->m_libPath = strdup(rhs->m_libPath);
if (newDriverBundle->m_friendlyName == NULL)
{
if (newDriverBundle->m_libPath != NULL)
{
free(newDriverBundle->m_libPath);
}
free(newDriverBundle);
return NULL;
}
if (newDriverBundle->m_libPath == NULL)
{
if (newDriverBundle->m_friendlyName != NULL)
{
free(newDriverBundle->m_friendlyName);
}
free(newDriverBundle);
return NULL;
}
return newDriverBundle;
}
static void
HPDriverRelease(HPDriver* driverBundle)
{
if (driverBundle)
{
free(driverBundle->m_friendlyName);
free(driverBundle->m_libPath);
}
}
static void
HPDriverVectorRelease(HPDriverVector driverBundleVector)
{
if (driverBundleVector)
{
HPDriver* b = driverBundleVector;
for (; b->m_initialized; ++b)
{
HPDriverRelease(b);
}
free(driverBundleVector);
}
}
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;
}
static void
HPDeviceListRelease(HPDeviceList list)
{
HPDevice* p = list;
for (; p; p = p->m_next)
{
HPDriverRelease(p->m_driver);
}
}
static int
HPDeviceEquals(HPDevice* a, HPDevice* b)
{
int res;
if (a->m_driver->m_type == b->m_driver->m_type)
{
if (a->m_driver->m_type == PCSCLITE_HP_Proprietary)
{
res = (a->m_driver->m_vendorId == b->m_driver->m_vendorId)
&& (a->m_driver->m_productId == b->m_driver->m_productId);
}
else
{
res = (a->m_driver->m_subClass == b->m_driver->m_subClass)
&& (a->m_driver->m_class == b->m_driver->m_class);
}
res = res && (a->m_address == b->m_address);
return res;
}
return 0;
}
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;
}
io_object_t usbDevice = 0;
while ((usbDevice = IOIteratorNext(usbIter)))
{
IOCFPlugInInterface** iodev;
SInt32 score;
kret = IOCreatePlugInInterfaceForService(usbDevice,
kIOUSBDeviceUserClientTypeID,
kIOCFPlugInInterfaceID,
&iodev,
&score);
IOObjectRelease(usbDevice);
if (kret != 0)
{
DebugLogA("error getting plugin interface from IOCreatePlugInInterfaceForService()");
continue;
}
IOUSBDeviceInterface245** usbdev;
HRESULT hres = (*iodev)->QueryInterface(iodev,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID245),
(LPVOID*)&usbdev);
if (hres)
{
DebugLogA("error querying interface in QueryInterface()");
IODestroyPlugInInterface ( iodev );
continue;
}
else
{
UInt16 vendorId = 0;
UInt16 productId = 0;
UInt32 usbAddress = 0;
kret = (*usbdev)->GetDeviceVendor(usbdev, &vendorId);
kret = (*usbdev)->GetDeviceProduct(usbdev, &productId);
kret = (*usbdev)->GetLocationID(usbdev, &usbAddress);
HPDriver* driver = driverBundle;
int match = 0;
for (; driver->m_NotEOV; ++driver)
{
if (!driver->m_initialized)
{
continue;
}
if ( (driver->m_type == PCSCLITE_HP_Proprietary)
&& (driver->m_vendorId == vendorId)
&& (driver->m_productId == productId))
{
*readerList = HPDeviceListInsert(*readerList, driver, usbAddress);
match = 1;
}
}
if (!match)
{
driver = driverBundle;
for (; driver->m_NotEOV; ++driver)
{
if (!driver->m_initialized)
{
continue;
}
if ( driver->m_type == PCSCLITE_HP_InterfaceClass)
{
IOUSBFindInterfaceRequest interfaceClassRequest;
io_iterator_t interfaceIterator;
io_service_t interface;
interfaceClassRequest.bInterfaceClass = driver->m_class;
interfaceClassRequest.bInterfaceSubClass = driver->m_subClass;
interfaceClassRequest.bInterfaceProtocol = driver->m_protocol;
interfaceClassRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare;
hres = (*usbdev)->CreateInterfaceIterator(usbdev,
&interfaceClassRequest,
&interfaceIterator);
if (hres)
{
continue;
}
while ( (interface = IOIteratorNext(interfaceIterator)) )
{
*readerList = HPDeviceListInsert(*readerList, driver, usbAddress);
match = 1;
IOObjectRelease ( interface );
}
IOObjectRelease ( interfaceIterator );
}
}
}
(*usbdev)->Release(usbdev);
IODestroyPlugInInterface ( iodev );
}
}
IOObjectRelease(usbIter);
return 0;
}
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;
}
io_object_t pccDevice = 0;
while ((pccDevice = IOIteratorNext(pccIter)))
{
UInt32 vendorId = 0;
UInt32 productId = 0;
UInt32 pccAddress = 0;
CFTypeRef valueRef = IORegistryEntryCreateCFProperty(pccDevice, CFSTR("VendorID"),
kCFAllocatorDefault, 0);
if (!valueRef)
{
DebugLogA("error getting vendor");
}
else
{
CFNumberGetValue((CFNumberRef)valueRef, kCFNumberSInt32Type, &vendorId);
CFRelease ( valueRef );
}
valueRef = IORegistryEntryCreateCFProperty(pccDevice, CFSTR("DeviceID"),
kCFAllocatorDefault, 0);
if (!valueRef)
{
DebugLogA("error getting device");
}
else
{
CFNumberGetValue((CFNumberRef)valueRef, kCFNumberSInt32Type, &productId);
CFRelease ( valueRef );
}
valueRef = IORegistryEntryCreateCFProperty(pccDevice, CFSTR("SocketNumber"),
kCFAllocatorDefault, 0);
if (!valueRef)
{
DebugLogA("error getting PC Card socket");
}
else
{
CFNumberGetValue((CFNumberRef)valueRef, kCFNumberSInt32Type, &pccAddress);
CFRelease ( valueRef );
}
HPDriver* driver = driverBundle;
for (; driver->m_vendorId; ++driver)
{
if ((driver->m_vendorId == vendorId)
&& (driver->m_productId == productId))
{
*readerList = HPDeviceListInsert(*readerList, driver, pccAddress);
}
}
IOObjectRelease ( pccDevice );
}
IOObjectRelease(pccIter);
return 0;
}
static void
HPEstablishUSBNotification()
{
CFMutableDictionaryRef matchingDictionary;
IOReturn kret;
if ( sNotificationPort == NULL )
sNotificationPort = IONotificationPortCreate(kIOMasterPortDefault);
CFRunLoopAddSource(CFRunLoopGetCurrent(),
IONotificationPortGetRunLoopSource(sNotificationPort),
kCFRunLoopDefaultMode);
matchingDictionary = IOServiceMatching("IOUSBDevice");
if (!matchingDictionary)
{
DebugLogB("IOServiceMatching() failed", 0);
}
matchingDictionary = (CFMutableDictionaryRef)CFRetain(matchingDictionary);
kret = IOServiceAddMatchingNotification(sNotificationPort,
kIOMatchedNotification,
matchingDictionary,
HPDeviceAppeared, NULL,
&sUSBAppearedIter);
if (kret)
{
DebugLogB("IOServiceAddMatchingNotification()-1 failed with code %d", kret);
}
HPDeviceAppeared(NULL, sUSBAppearedIter);
kret = IOServiceAddMatchingNotification(sNotificationPort,
kIOTerminatedNotification,
matchingDictionary,
HPDeviceDisappeared, NULL,
&sUSBRemovedIter);
if (kret)
{
DebugLogB("IOServiceAddMatchingNotification()-2 failed with code %d", kret);
}
HPDeviceDisappeared(NULL, sUSBRemovedIter);
}
static void
HPEstablishPCCardNotification()
{
CFMutableDictionaryRef matchingDictionary;
IOReturn kret;
if ( sNotificationPort == NULL )
sNotificationPort = IONotificationPortCreate(kIOMasterPortDefault);
CFRunLoopAddSource(CFRunLoopGetCurrent(),
IONotificationPortGetRunLoopSource(sNotificationPort),
kCFRunLoopDefaultMode);
matchingDictionary = IOServiceMatching("IOPCCard16Device");
if (!matchingDictionary)
{
DebugLogB("IOServiceMatching() failed", 0);
}
matchingDictionary = (CFMutableDictionaryRef)CFRetain(matchingDictionary);
kret = IOServiceAddMatchingNotification(sNotificationPort,
kIOMatchedNotification,
matchingDictionary,
HPDeviceAppeared, NULL,
&sPCCardAppearedIter);
if (kret)
{
DebugLogB("IOServiceAddMatchingNotification()-1 failed with code %d", kret);
}
HPDeviceAppeared(NULL, sPCCardAppearedIter);
kret = IOServiceAddMatchingNotification(sNotificationPort,
kIOTerminatedNotification,
matchingDictionary,
HPDeviceDisappeared, NULL,
&sPCCardRemovedIter);
if (kret)
{
DebugLogB("IOServiceAddMatchingNotification()-2 failed with code %d", kret);
}
HPDeviceDisappeared(NULL, sPCCardRemovedIter);
}
static void
HPDeviceNotificationThread()
{
HPEstablishUSBNotification();
HPEstablishPCCardNotification();
CFRunLoopRun();
}
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;
LONG
HPRegisterForHotplugEvents()
{
LONG sstat;
sstat = SYS_ThreadCreate(&sHotplugWatcherThread,
NULL,
(LPVOID)HPDeviceNotificationThread,
NULL);
return 0;
}