2017-12-09 17:15:20 +00:00
|
|
|
/*
|
2018-10-12 09:16:36 +00:00
|
|
|
* MUSCLE SmartCard Development ( https://pcsclite.apdu.fr/ )
|
2017-12-09 17:15:20 +00:00
|
|
|
*
|
|
|
|
* Copyright (C) 2002-2004
|
|
|
|
* Stephen M. Webb <stephenw@cryptocard.com>
|
|
|
|
* Copyright (C) 2002-2011
|
|
|
|
* Ludovic Rousseau <ludovic.rousseau@free.fr>
|
|
|
|
* Copyright (C) 2002
|
|
|
|
* David Corcoran <corcoran@musclecard.com>
|
|
|
|
* Copyright (C) 2003
|
|
|
|
* Antti Tapaninen
|
|
|
|
*
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
1. Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in the
|
|
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
3. The name of the author may not be used to endorse or promote products
|
|
|
|
derived from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @file
|
|
|
|
* @brief This provides a search API for hot pluggble devices.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "misc.h"
|
|
|
|
#include "pcscd.h"
|
|
|
|
|
|
|
|
#if defined(__APPLE__) && !defined(HAVE_LIBUSB)
|
|
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
|
|
#include <IOKit/IOCFPlugIn.h>
|
|
|
|
#include <IOKit/IOKitLib.h>
|
|
|
|
#include <IOKit/usb/IOUSBLib.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "debuglog.h"
|
|
|
|
#include "parser.h"
|
|
|
|
#include "readerfactory.h"
|
|
|
|
#include "winscard_msg.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "hotplug.h"
|
|
|
|
|
|
|
|
#undef DEBUG_HOTPLUG
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2020-06-14 13:55:09 +00:00
|
|
|
static int HPScan(void);
|
|
|
|
static HPDriver *Drivers = NULL;
|
|
|
|
|
2017-12-09 17:15:20 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
(void)refCon;
|
|
|
|
|
|
|
|
while ((obj = IOIteratorNext(iterator)))
|
|
|
|
kret = IOObjectRelease(obj);
|
|
|
|
|
2020-06-14 13:55:09 +00:00
|
|
|
HPScan();
|
2017-12-09 17:15:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
(void)refCon;
|
|
|
|
|
|
|
|
while ((obj = IOIteratorNext(iterator)))
|
|
|
|
kret = IOObjectRelease(obj);
|
|
|
|
|
2020-06-14 13:55:09 +00:00
|
|
|
HPScan();
|
2017-12-09 17:15:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_HOTPLUG
|
|
|
|
Log2(PCSC_LOG_DEBUG, "Entering HPDriversGetFromDirectory: %s",
|
|
|
|
driverBundlePath);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int readersNumber = 0;
|
|
|
|
HPDriverVector bundleVector = NULL;
|
|
|
|
CFArrayRef bundleArray;
|
|
|
|
CFStringRef driverBundlePathString =
|
|
|
|
CFStringCreateWithCString(kCFAllocatorDefault,
|
|
|
|
driverBundlePath,
|
|
|
|
kCFStringEncodingMacRoman);
|
|
|
|
CFURLRef pluginUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
|
|
|
|
driverBundlePathString,
|
|
|
|
kCFURLPOSIXPathStyle, TRUE);
|
|
|
|
|
|
|
|
CFRelease(driverBundlePathString);
|
|
|
|
if (!pluginUrl)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR, "error getting plugin directory URL");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
bundleArray = CFBundleCreateBundlesFromDirectory(kCFAllocatorDefault,
|
|
|
|
pluginUrl, NULL);
|
|
|
|
if (!bundleArray)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR, "error getting plugin directory bundles");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
CFRelease(pluginUrl);
|
|
|
|
|
|
|
|
size_t bundleArraySize = CFArrayGetCount(bundleArray);
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
/* get the number of readers (including aliases) */
|
|
|
|
for (i = 0; i < bundleArraySize; i++)
|
|
|
|
{
|
|
|
|
CFBundleRef currBundle =
|
|
|
|
(CFBundleRef) CFArrayGetValueAtIndex(bundleArray, i);
|
|
|
|
CFDictionaryRef dict = CFBundleGetInfoDictionary(currBundle);
|
|
|
|
|
|
|
|
const void * blobValue = CFDictionaryGetValue(dict,
|
|
|
|
CFSTR(PCSCLITE_HP_MANUKEY_NAME));
|
|
|
|
|
|
|
|
if (!blobValue)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR, "error getting vendor ID from bundle");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CFGetTypeID(blobValue) == CFArrayGetTypeID())
|
|
|
|
{
|
|
|
|
/* alias found, each reader count as 1 */
|
|
|
|
CFArrayRef propertyArray = blobValue;
|
|
|
|
readersNumber += CFArrayGetCount(propertyArray);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
/* No alias, only one reader supported */
|
|
|
|
readersNumber++;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_HOTPLUG
|
|
|
|
Log2(PCSC_LOG_DEBUG, "Total of %d readers supported", readersNumber);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* The last entry is an end marker (m_vendorId = 0)
|
|
|
|
* see checks in HPDriversMatchUSBDevices:503
|
|
|
|
* and HPDriverVectorRelease:376 */
|
|
|
|
readersNumber++;
|
|
|
|
|
|
|
|
bundleVector = calloc(readersNumber, sizeof(HPDriver));
|
|
|
|
if (!bundleVector)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR, "memory allocation failure");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
HPDriver *driverBundle = bundleVector;
|
|
|
|
for (i = 0; i < bundleArraySize; 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()));
|
|
|
|
|
|
|
|
const void * blobValue = CFDictionaryGetValue(dict,
|
|
|
|
CFSTR(PCSCLITE_HP_MANUKEY_NAME));
|
|
|
|
|
|
|
|
if (!blobValue)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR, "error getting vendor ID from bundle");
|
|
|
|
return bundleVector;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CFGetTypeID(blobValue) == CFArrayGetTypeID())
|
|
|
|
{
|
|
|
|
CFArrayRef vendorArray = blobValue;
|
|
|
|
CFArrayRef productArray;
|
|
|
|
CFArrayRef friendlyNameArray;
|
|
|
|
char *libPath = driverBundle->m_libPath;
|
|
|
|
|
|
|
|
#ifdef DEBUG_HOTPLUG
|
|
|
|
Log2(PCSC_LOG_DEBUG, "Driver with aliases: %s", libPath);
|
|
|
|
#endif
|
|
|
|
/* get list of ProductID */
|
|
|
|
productArray = CFDictionaryGetValue(dict,
|
|
|
|
CFSTR(PCSCLITE_HP_PRODKEY_NAME));
|
|
|
|
if (!productArray)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR, "error getting product ID from bundle");
|
|
|
|
return bundleVector;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get list of FriendlyName */
|
|
|
|
friendlyNameArray = CFDictionaryGetValue(dict,
|
|
|
|
CFSTR(PCSCLITE_HP_NAMEKEY_NAME));
|
|
|
|
if (!friendlyNameArray)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR, "error getting product ID from bundle");
|
|
|
|
return bundleVector;
|
|
|
|
}
|
|
|
|
|
|
|
|
int reader_nb = CFArrayGetCount(vendorArray);
|
|
|
|
|
|
|
|
if (reader_nb != CFArrayGetCount(productArray))
|
|
|
|
{
|
|
|
|
Log3(PCSC_LOG_ERROR,
|
|
|
|
"Malformed Info.plist: %d vendors and %ld products",
|
|
|
|
reader_nb, CFArrayGetCount(productArray));
|
|
|
|
return bundleVector;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reader_nb != CFArrayGetCount(friendlyNameArray))
|
|
|
|
{
|
|
|
|
Log3(PCSC_LOG_ERROR,
|
|
|
|
"Malformed Info.plist: %d vendors and %ld friendlynames",
|
|
|
|
reader_nb, CFArrayGetCount(friendlyNameArray));
|
|
|
|
return bundleVector;
|
|
|
|
}
|
|
|
|
|
|
|
|
int j;
|
|
|
|
for (j=0; j<reader_nb; j++)
|
|
|
|
{
|
|
|
|
CFStringRef strValue = CFArrayGetValueAtIndex(vendorArray, j);
|
|
|
|
|
|
|
|
driverBundle->m_vendorId = strtoul(CFStringGetCStringPtr(strValue,
|
|
|
|
CFStringGetSystemEncoding()), NULL, 16);
|
|
|
|
|
|
|
|
strValue = CFArrayGetValueAtIndex(productArray, j);
|
|
|
|
driverBundle->m_productId = strtoul(CFStringGetCStringPtr(strValue,
|
|
|
|
CFStringGetSystemEncoding()), NULL, 16);
|
|
|
|
|
|
|
|
strValue = CFArrayGetValueAtIndex(friendlyNameArray, j);
|
|
|
|
const char *cstr = CFStringGetCStringPtr(strValue,
|
|
|
|
CFStringGetSystemEncoding());
|
2020-06-14 13:55:09 +00:00
|
|
|
if (NULL == cstr)
|
|
|
|
{
|
|
|
|
char utf8_str[200];
|
|
|
|
if (CFStringGetCString(strValue, utf8_str, sizeof utf8_str,
|
|
|
|
kCFStringEncodingUTF8))
|
|
|
|
driverBundle->m_friendlyName = strdup(utf8_str);
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
driverBundle->m_friendlyName = strdup(cstr);
|
2017-12-09 17:15:20 +00:00
|
|
|
|
|
|
|
if (!driverBundle->m_libPath)
|
|
|
|
driverBundle->m_libPath = strdup(libPath);
|
|
|
|
|
|
|
|
#ifdef DEBUG_HOTPLUG
|
|
|
|
Log2(PCSC_LOG_DEBUG, "VendorID: 0x%04X",
|
|
|
|
driverBundle->m_vendorId);
|
|
|
|
Log2(PCSC_LOG_DEBUG, "ProductID: 0x%04X",
|
|
|
|
driverBundle->m_productId);
|
|
|
|
Log2(PCSC_LOG_DEBUG, "Friendly name: %s",
|
|
|
|
driverBundle->m_friendlyName);
|
|
|
|
Log2(PCSC_LOG_DEBUG, "Driver: %s", driverBundle->m_libPath);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* go to next bundle in the vector */
|
|
|
|
driverBundle++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CFStringRef strValue = blobValue;
|
|
|
|
|
|
|
|
#ifdef DEBUG_HOTPLUG
|
|
|
|
Log3(PCSC_LOG_DEBUG, "Driver without alias: %s %s",
|
|
|
|
driverBundle->m_friendlyName, driverBundle->m_libPath);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
driverBundle->m_vendorId = strtoul(CFStringGetCStringPtr(strValue,
|
|
|
|
CFStringGetSystemEncoding()), NULL, 16);
|
|
|
|
|
|
|
|
strValue = (CFStringRef) CFDictionaryGetValue(dict,
|
|
|
|
CFSTR(PCSCLITE_HP_PRODKEY_NAME));
|
|
|
|
if (!strValue)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR, "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)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR, "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);
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_HOTPLUG
|
|
|
|
Log2(PCSC_LOG_DEBUG, "VendorID: 0x%04X", driverBundle->m_vendorId);
|
|
|
|
Log2(PCSC_LOG_DEBUG, "ProductID: 0x%04X", driverBundle->m_productId);
|
|
|
|
Log2(PCSC_LOG_DEBUG, "Friendly name: %s", driverBundle->m_friendlyName);
|
|
|
|
Log2(PCSC_LOG_DEBUG, "Driver: %s", driverBundle->m_libPath);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* go to next bundle in the vector */
|
|
|
|
driverBundle++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CFRelease(bundleArray);
|
|
|
|
return bundleVector;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copies a driver bundle instance.
|
|
|
|
*/
|
|
|
|
static HPDriver *HPDriverCopy(HPDriver * rhs)
|
|
|
|
{
|
|
|
|
if (!rhs)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
HPDriver *newDriverBundle = 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Inserts a new reader device in the list.
|
|
|
|
*/
|
|
|
|
static HPDeviceList
|
|
|
|
HPDeviceListInsert(HPDeviceList list, HPDriver * bundle, UInt32 address)
|
|
|
|
{
|
|
|
|
HPDevice *newReader = calloc(1, sizeof(HPDevice));
|
|
|
|
|
|
|
|
if (!newReader)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR, "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;
|
|
|
|
|
|
|
|
for (p = list; 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)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR,
|
|
|
|
"error getting USB match from IOServiceMatching()");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
io_iterator_t usbIter;
|
|
|
|
kern_return_t kret = IOServiceGetMatchingServices(kIOMasterPortDefault,
|
|
|
|
usbMatch, &usbIter);
|
|
|
|
|
|
|
|
if (kret != 0)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR,
|
|
|
|
"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)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR,
|
|
|
|
"error getting device name from IORegistryEntryGetName()");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
IOCFPlugInInterface **iodev;
|
|
|
|
SInt32 score;
|
|
|
|
|
|
|
|
kret = IOCreatePlugInInterfaceForService(usbDevice,
|
|
|
|
kIOUSBDeviceUserClientTypeID,
|
|
|
|
kIOCFPlugInInterfaceID, &iodev, &score);
|
|
|
|
if (kret != 0)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR, "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)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR,
|
|
|
|
"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);
|
|
|
|
|
|
|
|
#ifdef DEBUG_HOTPLUG
|
|
|
|
Log4(PCSC_LOG_DEBUG, "Found USB device 0x%04X:0x%04X at 0x%X",
|
|
|
|
vendorId, productId, usbAddress);
|
|
|
|
#endif
|
|
|
|
HPDriver *driver;
|
|
|
|
for (driver = driverBundle; driver->m_vendorId; ++driver)
|
|
|
|
{
|
|
|
|
if ((driver->m_vendorId == vendorId)
|
|
|
|
&& (driver->m_productId == productId))
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_HOTPLUG
|
|
|
|
Log4(PCSC_LOG_DEBUG, "Adding USB device %04X:%04X at 0x%X",
|
|
|
|
vendorId, productId, usbAddress);
|
|
|
|
#endif
|
|
|
|
*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 (pccMatch == NULL)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR,
|
|
|
|
"error getting PCCard match from IOServiceMatching()");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
io_iterator_t pccIter;
|
|
|
|
kern_return_t kret =
|
|
|
|
IOServiceGetMatchingServices(kIOMasterPortDefault, pccMatch,
|
|
|
|
&pccIter);
|
|
|
|
if (kret != 0)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR,
|
|
|
|
"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)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR, "error getting plugin interface from IOCreatePlugInInterfaceForService()");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
UInt32 vendorId = 0;
|
|
|
|
UInt32 productId = 0;
|
|
|
|
UInt32 pccAddress = 0;
|
|
|
|
CFTypeRef valueRef =
|
|
|
|
IORegistryEntryCreateCFProperty(pccDevice, CFSTR("VendorID"),
|
|
|
|
kCFAllocatorDefault, 0);
|
|
|
|
|
|
|
|
if (!valueRef)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR, "error getting vendor");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CFNumberGetValue((CFNumberRef) valueRef, kCFNumberSInt32Type,
|
|
|
|
&vendorId);
|
|
|
|
}
|
|
|
|
valueRef =
|
|
|
|
IORegistryEntryCreateCFProperty(pccDevice, CFSTR("DeviceID"),
|
|
|
|
kCFAllocatorDefault, 0);
|
|
|
|
if (!valueRef)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR, "error getting device");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CFNumberGetValue((CFNumberRef) valueRef, kCFNumberSInt32Type,
|
|
|
|
&productId);
|
|
|
|
}
|
|
|
|
valueRef =
|
|
|
|
IORegistryEntryCreateCFProperty(pccDevice, CFSTR("SocketNumber"),
|
|
|
|
kCFAllocatorDefault, 0);
|
|
|
|
if (!valueRef)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR, "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(void)
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR, "IOServiceMatching() failed");
|
|
|
|
}
|
|
|
|
matchingDictionary =
|
|
|
|
(CFMutableDictionaryRef) CFRetain(matchingDictionary);
|
|
|
|
|
|
|
|
kret = IOServiceAddMatchingNotification(notificationPort,
|
|
|
|
kIOMatchedNotification,
|
|
|
|
matchingDictionary, HPDeviceAppeared, NULL, &deviceAddedIterator);
|
|
|
|
if (kret)
|
|
|
|
{
|
|
|
|
Log2(PCSC_LOG_ERROR,
|
|
|
|
"IOServiceAddMatchingNotification()-1 failed with code %d", kret);
|
|
|
|
}
|
|
|
|
HPDeviceAppeared(NULL, deviceAddedIterator);
|
|
|
|
|
|
|
|
kret = IOServiceAddMatchingNotification(notificationPort,
|
|
|
|
kIOTerminatedNotification,
|
|
|
|
matchingDictionary,
|
|
|
|
HPDeviceDisappeared, NULL, &deviceRemovedIterator);
|
|
|
|
if (kret)
|
|
|
|
{
|
|
|
|
Log2(PCSC_LOG_ERROR,
|
|
|
|
"IOServiceAddMatchingNotification()-2 failed with code %d", kret);
|
|
|
|
}
|
|
|
|
HPDeviceDisappeared(NULL, deviceRemovedIterator);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void HPEstablishPCCardNotification(void)
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
Log1(PCSC_LOG_ERROR, "IOServiceMatching() failed");
|
|
|
|
}
|
|
|
|
matchingDictionary =
|
|
|
|
(CFMutableDictionaryRef) CFRetain(matchingDictionary);
|
|
|
|
|
|
|
|
kret = IOServiceAddMatchingNotification(notificationPort,
|
|
|
|
kIOMatchedNotification,
|
|
|
|
matchingDictionary, HPDeviceAppeared, NULL, &deviceAddedIterator);
|
|
|
|
if (kret)
|
|
|
|
{
|
|
|
|
Log2(PCSC_LOG_ERROR,
|
|
|
|
"IOServiceAddMatchingNotification()-1 failed with code %d", kret);
|
|
|
|
}
|
|
|
|
HPDeviceAppeared(NULL, deviceAddedIterator);
|
|
|
|
|
|
|
|
kret = IOServiceAddMatchingNotification(notificationPort,
|
|
|
|
kIOTerminatedNotification,
|
|
|
|
matchingDictionary,
|
|
|
|
HPDeviceDisappeared, NULL, &deviceRemovedIterator);
|
|
|
|
if (kret)
|
|
|
|
{
|
|
|
|
Log2(PCSC_LOG_ERROR,
|
|
|
|
"IOServiceAddMatchingNotification()-2 failed with code %d", kret);
|
|
|
|
}
|
|
|
|
HPDeviceDisappeared(NULL, deviceRemovedIterator);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Thread runner (does not return).
|
|
|
|
*/
|
|
|
|
static void HPDeviceNotificationThread(void)
|
|
|
|
{
|
|
|
|
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(void)
|
|
|
|
{
|
2020-06-14 13:55:09 +00:00
|
|
|
Drivers = HPDriversGetFromDirectory(PCSCLITE_HP_DROPDIR);
|
2017-12-09 17:15:20 +00:00
|
|
|
|
2020-06-14 13:55:09 +00:00
|
|
|
if (!Drivers)
|
2017-12-09 17:15:20 +00:00
|
|
|
return 1;
|
|
|
|
|
2020-06-14 13:55:09 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int HPScan(void)
|
|
|
|
{
|
2017-12-09 17:15:20 +00:00
|
|
|
HPDeviceList devices = NULL;
|
|
|
|
|
2020-06-14 13:55:09 +00:00
|
|
|
if (HPDriversMatchUSBDevices(Drivers, &devices))
|
2017-12-09 17:15:20 +00:00
|
|
|
return -1;
|
|
|
|
|
2020-06-14 13:55:09 +00:00
|
|
|
if (HPDriversMatchPCCardDevices(Drivers, &devices))
|
2017-12-09 17:15:20 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
HPDevice *a;
|
|
|
|
|
|
|
|
for (a = devices; a; a = a->m_next)
|
|
|
|
{
|
|
|
|
int found = FALSE;
|
|
|
|
HPDevice *b;
|
|
|
|
|
|
|
|
for (b = sDeviceList; b; b = b->m_next)
|
|
|
|
{
|
|
|
|
if (HPDeviceEquals(a, b))
|
|
|
|
{
|
|
|
|
found = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found)
|
|
|
|
{
|
2022-05-11 15:31:16 +00:00
|
|
|
char *deviceName;
|
2017-12-09 17:15:20 +00:00
|
|
|
|
|
|
|
/* the format should be "usb:%04x/%04x" but Apple uses the
|
|
|
|
* friendly name instead */
|
2022-05-11 15:31:16 +00:00
|
|
|
asprintf(&deviceName, "%s", a->m_driver->m_friendlyName);
|
2017-12-09 17:15:20 +00:00
|
|
|
|
|
|
|
RFAddReader(a->m_driver->m_friendlyName,
|
|
|
|
PCSCLITE_HP_BASE_PORT + a->m_address, a->m_driver->m_libPath,
|
|
|
|
deviceName);
|
2022-05-11 15:31:16 +00:00
|
|
|
free(deviceName);
|
2017-12-09 17:15:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (a = sDeviceList; a; a = a->m_next)
|
|
|
|
{
|
|
|
|
int found = FALSE;
|
|
|
|
HPDevice *b;
|
|
|
|
|
|
|
|
for (b = devices; b; b = b->m_next)
|
|
|
|
{
|
|
|
|
if (HPDeviceEquals(a, b))
|
|
|
|
{
|
|
|
|
found = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
RFRemoveReader(a->m_driver->m_friendlyName,
|
2021-12-04 12:36:24 +00:00
|
|
|
PCSCLITE_HP_BASE_PORT + a->m_address,
|
|
|
|
REMOVE_READER_FLAG_REMOVED);
|
2017-12-09 17:15:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
HPDeviceListRelease(sDeviceList);
|
|
|
|
sDeviceList = devices;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pthread_t sHotplugWatcherThread;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Sets up callbacks for device hotplug events.
|
|
|
|
*/
|
|
|
|
ULONG HPRegisterForHotplugEvents(void)
|
|
|
|
{
|
|
|
|
ThreadCreate(&sHotplugWatcherThread,
|
|
|
|
THREAD_ATTR_DEFAULT,
|
|
|
|
(PCSCLITE_THREAD_FUNCTION( )) HPDeviceNotificationThread, NULL);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
LONG HPStopHotPluggables(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void HPReCheckSerialReaders(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* __APPLE__ */
|
|
|
|
|