1/*
2 * Copyright (c) 2000-2002 Apple Computer, Inc. All Rights Reserved.
3 * The contents of this file constitute Original Code as defined in and are
4 * subject to the Apple Public Source License Version 1.2 (the 'License').
5 * You may not use this file except in compliance with the License. Please
6 * obtain a copy of the License at http://www.apple.com/publicsource and
7 * read it before using this file.
8 *
9 * This Original Code and all software distributed under the License are
10 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
11 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
12 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
13 * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please
14 * see the License for the specific language governing rights and
15 * limitations under the License.
16 */
17
18/******************************************************************
19
20	MUSCLE SmartCard Development ( http://www.linuxnet.com )
21	    Title  : hotplug_macosx.c
22	    Package: pcsc lite
23      Author : Stephen M. Webb <stephenw@cryptocard.com>
24      Date   : 03 Dec 2002
25	    License: Copyright (C) 2002 David Corcoran
26	             <corcoran@linuxnet.com>
27            Purpose: This provides a search API for hot pluggble
28	             devices.
29
30********************************************************************/
31
32#include <CoreFoundation/CoreFoundation.h>
33#include <IOKit/IOCFPlugIn.h>
34#include <IOKit/IOKitLib.h>
35#include <IOKit/usb/IOUSBLib.h>
36#include <stdlib.h>
37#include <string.h>
38
39#include "config.h"
40#include "wintypes.h"
41#include "pcsclite.h"
42#include "debuglog.h"
43#include "hotplug.h"
44#include "readerfactory.h"
45#include "thread_generic.h"
46
47#define PCSCLITE_HP_DROPDIR          "/usr/libexec/SmartCardServices/drivers/"
48#define PCSCLITE_HP_MANUKEY_NAME     "ifdVendorID"
49#define PCSCLITE_HP_PRODKEY_NAME     "ifdProductID"
50#define PCSCLITE_HP_NAMEKEY_NAME     "ifdFriendlyName"
51#define PCSCLITE_HP_IFACECLASSKEY_NAME    "ifdInterfaceClass"
52#define PCSCLITE_HP_IFACESUBCLASSKEY_NAME "ifdInterfaceSubClass"
53#define PCSCLITE_HP_IFACEPROTOCOLKEY_NAME "ifdInterfaceProtocol"
54#define PCSCLITE_HP_BASE_PORT       0x200000
55
56
57/*
58 * Defines the type of driver in the driver vector
59 */
60typedef enum
61{
62    PCSCLITE_HP_Proprietary      = 0,
63    PCSCLITE_HP_InterfaceClass   = 1,
64    // * Could accomodate more types */
65} HPDriverType;
66
67
68
69/*
70 * An aggregation of useful information on a driver bundle in the
71 * drop directory.
72 */
73typedef struct HPDriver
74{
75    UInt8        m_NotEOV;           /* set to 1 for any driver before the end */
76    UInt8        m_initialized;      /* set to 1 on successful intialization */
77    HPDriverType m_type;             /* type of the driver in this element */
78    UInt32       m_vendorId;         /* unique vendor's manufacturer code */
79    UInt32       m_productId;        /* manufacturer's unique product code */
80    UInt8        m_class;            /* class of a non product specific driver */
81    UInt8        m_subClass;         /* subClass of a non product specific driver */
82    UInt8        m_protocol;         /* protocol of a non product specific driver */
83    char*        m_friendlyName;     /* bundle friendly name */
84    char*        m_libPath;          /* bundle's plugin library location */
85} HPDriver, *HPDriverVector;
86
87/*
88 * An aggregation on information on currently active reader drivers.
89 */
90typedef struct HPDevice
91{
92    HPDriver*         m_driver;   /* driver bundle information */
93    UInt32            m_address;  /* unique system address of device */
94    struct HPDevice*  m_next;     /* next device in list */
95} HPDevice, *HPDeviceList;
96
97/*
98 * Pointer to a list of (currently) known hotplug reader devices (and their
99                                                                  * drivers).
100 */
101static HPDeviceList				sDeviceList			= NULL;
102static IONotificationPortRef	sNotificationPort	= NULL;
103static io_iterator_t			sUSBAppearedIter	= NULL;
104static io_iterator_t			sUSBRemovedIter		= NULL;
105static io_iterator_t			sPCCardAppearedIter	= NULL;
106static io_iterator_t			sPCCardRemovedIter	= NULL;
107
108/*
109 * A callback to handle the asynchronous appearance of new devices that are
110 * candidates for PCSC readers.
111 */
112static void
113HPDeviceAppeared(void* refCon, io_iterator_t iterator)
114{
115    kern_return_t kret;
116    io_service_t  obj;
117    while ((obj = IOIteratorNext(iterator)))
118    {
119        kret = IOObjectRelease(obj);
120    }
121
122    HPSearchHotPluggables();
123}
124
125/*
126 * A callback to handle the asynchronous disappearance of devices that are
127 * possibly PCSC readers.
128 */
129static void
130HPDeviceDisappeared(void* refCon, io_iterator_t iterator)
131{
132    kern_return_t kret;
133    io_service_t  obj;
134    while ((obj = IOIteratorNext(iterator)))
135    {
136        kret = IOObjectRelease(obj);
137    }
138    HPSearchHotPluggables();
139}
140
141
142/*
143 * Creates a vector of driver bundle info structures from the hot-plug driver
144 * directory.
145 *
146 * Returns NULL on error and a pointer to an allocated HPDriver vector on
147 * success.  The caller must free the HPDriver with a call to
148 * HPDriversRelease().
149 */
150static HPDriverVector
151HPDriversGetFromDirectory(const char* driverBundlePath)
152{
153    HPDriverVector bundleVector = NULL;
154    CFArrayRef bundleArray;
155    CFStringRef driverBundlePathString;
156    driverBundlePathString = CFStringCreateWithCString(kCFAllocatorDefault,
157                                                       driverBundlePath,
158                                                       kCFStringEncodingMacRoman);
159    CFURLRef pluginUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
160                                                       driverBundlePathString,
161                                                       kCFURLPOSIXPathStyle, TRUE);
162    CFRelease(driverBundlePathString);
163    if (!pluginUrl)
164    {
165        DebugLogA("error getting plugin directory URL");
166        return bundleVector;
167    }
168    bundleArray = CFBundleCreateBundlesFromDirectory(kCFAllocatorDefault,
169                                                     pluginUrl,
170                                                     NULL);
171    if (!bundleArray)
172    {
173        DebugLogA("error getting plugin directory bundles");
174        return bundleVector;
175    }
176    CFRelease(pluginUrl);
177
178    size_t bundleArraySize = CFArrayGetCount(bundleArray);
179    // bundleArraySize + 1 <- because the last vector element is
180    // blank and is used to determine the length (m_NotEOV == 0)
181    bundleVector = (HPDriver*)calloc(bundleArraySize + 1, sizeof(HPDriver));
182    if (!bundleVector)
183    {
184        DebugLogA("memory allocation failure");
185        return bundleVector;
186    }
187
188    int i = 0;
189    for (; i < bundleArraySize; ++i)
190    {
191        HPDriver* driverBundle = bundleVector + i;
192        // This is not the last
193        driverBundle->m_NotEOV = 1;
194        CFBundleRef currBundle = (CFBundleRef)CFArrayGetValueAtIndex(bundleArray, i);
195        CFDictionaryRef dict   = CFBundleGetInfoDictionary(currBundle);
196
197        CFURLRef bundleUrl      = CFBundleCopyBundleURL(currBundle);
198        CFStringRef bundlePath  = CFURLCopyPath(bundleUrl);
199        driverBundle->m_libPath = strdup(CFStringGetCStringPtr(bundlePath,
200                                                               CFStringGetSystemEncoding()));
201        if (driverBundle->m_libPath == NULL)
202        {
203            DebugLogA("memory allocation failure");
204            return bundleVector;
205        }
206        UInt32 vendorId     = 0;
207        UInt8  gotVendorId  = 0;
208        UInt32 productId    = 0;
209        UInt8  gotProductId = 0;
210
211        CFStringRef strValue   = (CFStringRef)CFDictionaryGetValue(dict,
212                                                                   CFSTR(PCSCLITE_HP_MANUKEY_NAME));
213        if (strValue)
214        {
215            gotVendorId = 1;
216            vendorId = strtoul(CFStringGetCStringPtr(strValue,
217                                                     CFStringGetSystemEncoding()),
218                                                     NULL, 16);
219
220            strValue = (CFStringRef)CFDictionaryGetValue(dict,
221                                                         CFSTR(PCSCLITE_HP_PRODKEY_NAME));
222            if (strValue)
223            {
224                gotProductId = 1;
225                productId = strtoul(CFStringGetCStringPtr(strValue,
226                                                          CFStringGetSystemEncoding()),
227                                                          NULL, 16);
228            }
229        }
230        if (gotVendorId && gotProductId)
231        {
232            /* This is a product-specific driver */
233            driverBundle->m_productId   = productId;
234            driverBundle->m_vendorId    = vendorId;
235            driverBundle->m_type        = PCSCLITE_HP_Proprietary;
236        }
237        else
238        {
239            /* If not a product-specific driver, it must be */
240            /* an interface class-specifc driver            */
241            UInt8 class;
242            UInt8 subClass;
243            UInt8 protocol;
244
245            strValue = (CFStringRef)CFDictionaryGetValue(dict,
246                                                         CFSTR(PCSCLITE_HP_IFACECLASSKEY_NAME));
247            if (strValue)
248            {
249                class = (UInt8) strtoul(CFStringGetCStringPtr(strValue,
250                                                              CFStringGetSystemEncoding()),
251                                        NULL, 16);
252                driverBundle->m_class     = class;
253            }
254            else
255            {
256                DebugLogB("Malformed bundle (class absent) in driver folder: %s. Will be ignored",
257                          driverBundle->m_libPath);
258                free(driverBundle->m_libPath);
259                driverBundle->m_libPath = NULL;
260                continue;
261            }
262            strValue = (CFStringRef)CFDictionaryGetValue(dict,
263                                                         CFSTR(PCSCLITE_HP_IFACESUBCLASSKEY_NAME));
264            if (strValue)
265            {
266                subClass = (UInt8) strtoul(CFStringGetCStringPtr(strValue,
267                                                                 CFStringGetSystemEncoding()),
268                                           NULL, 16);
269                driverBundle->m_subClass  = subClass;
270            }
271            else
272            {
273                DebugLogB("Malformed bundle (subClass absent) in driver folder: %s. Will be ignored",
274                          driverBundle->m_libPath);
275                free(driverBundle->m_libPath);
276                driverBundle->m_libPath = NULL;
277                continue;
278            }
279            strValue = (CFStringRef)CFDictionaryGetValue(dict,
280                                                         CFSTR(PCSCLITE_HP_IFACEPROTOCOLKEY_NAME));
281            if (strValue)
282            {
283                protocol = (UInt8) strtoul(CFStringGetCStringPtr(strValue,
284                                                                 CFStringGetSystemEncoding()),
285                                           NULL, 16);
286                driverBundle->m_protocol  = protocol;
287            }
288            else
289            {
290                DebugLogB("Malformed bundle (protocol absent) in driver folder: %s. Will be ignored",
291                          driverBundle->m_libPath);
292                free(driverBundle->m_libPath);
293                driverBundle->m_libPath = NULL;
294                continue;
295            }
296            driverBundle->m_type = PCSCLITE_HP_InterfaceClass;
297        }
298        strValue = (CFStringRef)CFDictionaryGetValue(dict,
299                                                     CFSTR(PCSCLITE_HP_NAMEKEY_NAME));
300        if (!strValue)
301        {
302            DebugLogB("Product friendly name absent in driver folder: %s.",
303				driverBundle->m_libPath);
304            driverBundle->m_friendlyName = strdup("unnamed device");
305        }
306        else
307        {
308            const char* cstr = CFStringGetCStringPtr(strValue,
309                                                     CFStringGetSystemEncoding());
310            driverBundle->m_friendlyName = strdup(cstr);
311        }
312        driverBundle->m_initialized = 1;
313    }
314    CFRelease(bundleArray);
315    return bundleVector;
316}
317
318/*
319 * Copies a driver bundle instance.
320 */
321static HPDriver*
322HPDriverCopy(HPDriver* rhs)
323{
324    if (!rhs)
325    {
326        return NULL;
327    }
328    HPDriver* newDriverBundle = (HPDriver*)calloc(1, sizeof(HPDriver));
329    if (!newDriverBundle)
330    {
331        return NULL;
332    }
333
334    newDriverBundle->m_initialized  = rhs->m_initialized;
335    newDriverBundle->m_type         = rhs->m_type;
336    newDriverBundle->m_vendorId     = rhs->m_vendorId;
337    newDriverBundle->m_productId    = rhs->m_productId;
338
339    newDriverBundle->m_class        = rhs->m_class;
340    newDriverBundle->m_subClass     = rhs->m_subClass;
341    newDriverBundle->m_friendlyName = strdup(rhs->m_friendlyName);
342    newDriverBundle->m_libPath      = strdup(rhs->m_libPath);
343    if (newDriverBundle->m_friendlyName == NULL)
344    {
345        if (newDriverBundle->m_libPath != NULL)
346        {
347            free(newDriverBundle->m_libPath);
348        }
349        free(newDriverBundle);
350        return NULL;
351    }
352
353    if (newDriverBundle->m_libPath == NULL)
354    {
355        if (newDriverBundle->m_friendlyName != NULL)
356        {
357            free(newDriverBundle->m_friendlyName);
358        }
359        free(newDriverBundle);
360        return NULL;
361    }
362    return newDriverBundle;
363}
364
365/*
366 * Releases resources allocated to a driver bundle vector.
367 */
368static void
369HPDriverRelease(HPDriver* driverBundle)
370{
371    if (driverBundle)
372    {
373        free(driverBundle->m_friendlyName);
374        free(driverBundle->m_libPath);
375    }
376}
377
378/*
379 * Releases resources allocated to a driver bundle vector.
380 */
381static void
382HPDriverVectorRelease(HPDriverVector driverBundleVector)
383{
384    if (driverBundleVector)
385    {
386        HPDriver* b = driverBundleVector;
387        for (; b->m_initialized; ++b)
388        {
389            HPDriverRelease(b);
390        }
391        free(driverBundleVector);
392    }
393}
394
395/*
396 * Inserts a new reader device in the list.
397 */
398static HPDeviceList
399HPDeviceListInsert(HPDeviceList list, HPDriver* bundle, UInt32 address)
400{
401    HPDevice* newReader = (HPDevice*)calloc(1, sizeof(HPDevice));
402    if (!newReader)
403    {
404        DebugLogA("memory allocation failure");
405        return list;
406    }
407    newReader->m_driver  = HPDriverCopy(bundle);
408    newReader->m_address = address;
409    newReader->m_next    = list;
410    return newReader;
411}
412
413/*
414 * Frees resources allocated to a HPDeviceList.
415 */
416static void
417HPDeviceListRelease(HPDeviceList list)
418{
419    HPDevice* p = list;
420    for (; p; p = p->m_next)
421    {
422        HPDriverRelease(p->m_driver);
423    }
424}
425
426/*
427 * Compares two driver bundle instances for equality.
428 */
429static int
430HPDeviceEquals(HPDevice* a, HPDevice* b)
431{
432    int res;
433    if (a->m_driver->m_type == b->m_driver->m_type)
434    {
435        if (a->m_driver->m_type == PCSCLITE_HP_Proprietary)
436        {
437            // a and b have same vendor and product id
438            res = (a->m_driver->m_vendorId == b->m_driver->m_vendorId)
439                && (a->m_driver->m_productId == b->m_driver->m_productId);
440        }
441        else
442        {
443            // a and b have same class
444            res = (a->m_driver->m_subClass == b->m_driver->m_subClass)
445                && (a->m_driver->m_class == b->m_driver->m_class);
446        }
447        // AND have the same address
448        res = res && (a->m_address == b->m_address);
449
450        return res;
451    }
452    return 0;
453}
454
455/*
456 * Finds USB devices currently registered in the system that match any of
457 * the drivers detected in the driver bundle vector.
458 */
459static int
460HPDriversMatchUSBDevices(HPDriverVector driverBundle, HPDeviceList* readerList)
461{
462    CFDictionaryRef usbMatch = IOServiceMatching("IOUSBDevice");
463    if (0 == usbMatch)
464    {
465        DebugLogA("error getting USB match from IOServiceMatching()");
466        return 1;
467    }
468
469    io_iterator_t usbIter;
470    kern_return_t kret = IOServiceGetMatchingServices(kIOMasterPortDefault,
471                                                      usbMatch,
472                                                      &usbIter);
473    if (kret != 0)
474    {
475        DebugLogA("error getting iterator from IOServiceGetMatchingServices()");
476        return 1;
477    }
478
479    io_object_t usbDevice = 0;
480    while ((usbDevice = IOIteratorNext(usbIter)))
481    {
482        IOCFPlugInInterface** iodev;
483        SInt32                score;
484        kret = IOCreatePlugInInterfaceForService(usbDevice,
485                                                 kIOUSBDeviceUserClientTypeID,
486                                                 kIOCFPlugInInterfaceID,
487                                                 &iodev,
488                                                 &score);
489        IOObjectRelease(usbDevice);
490        if (kret != 0)
491        {
492            DebugLogA("error getting plugin interface from IOCreatePlugInInterfaceForService()");
493            continue;
494        }
495
496        IOUSBDeviceInterface245** usbdev;
497        HRESULT hres = (*iodev)->QueryInterface(iodev,
498                                                CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID245),
499                                                (LPVOID*)&usbdev);
500        if (hres)
501        {
502            DebugLogA("error querying interface in QueryInterface()");
503            IODestroyPlugInInterface ( iodev );
504            continue;
505        }
506
507        else
508		{
509
510			UInt16 vendorId  = 0;
511			UInt16 productId = 0;
512			UInt32 usbAddress = 0;
513			kret = (*usbdev)->GetDeviceVendor(usbdev, &vendorId);
514			kret = (*usbdev)->GetDeviceProduct(usbdev, &productId);
515			kret = (*usbdev)->GetLocationID(usbdev, &usbAddress);
516
517			HPDriver* driver = driverBundle;
518			int match = 0;
519			for (; driver->m_NotEOV; ++driver)
520			{
521				if (!driver->m_initialized)
522				{
523					// Malformed driver, skip
524					continue;
525				}
526				if ( (driver->m_type == PCSCLITE_HP_Proprietary)
527					&& (driver->m_vendorId == vendorId)
528					&& (driver->m_productId == productId))
529				{
530					*readerList = HPDeviceListInsert(*readerList, driver, usbAddress);
531					match = 1;
532				}
533			}
534			if (!match)
535			{
536				// Now try to locate Interfaces with supported classes
537				// We create an interface iterator for each of the
538				// classes supported by drivers of PCSCLITE_HP_InterfaceClass
539				// type.
540
541				// Using IOServiceMatching(kIOUSBInterfaceClassName)
542				// does not seem feasible as there does not seem to be a
543				// way to limit the search to the device we are currently
544				// analysing
545
546				// Another option would be to iterate on all interfaces
547				// and get the class of each of them. This is probably
548				// not interesting as the list of PCSCLITE_HP_InterfaceClass
549				// type of readers should only have one element (CCID)
550
551				// Restart scan at the begining of the array
552				driver = driverBundle;
553				// Iterate on PCSCLITE_HP_InterfaceClass driver types
554				for (; driver->m_NotEOV; ++driver)
555				{
556					if (!driver->m_initialized)
557					{
558						// Malformed driver, skip
559						continue;
560					}
561					if ( driver->m_type == PCSCLITE_HP_InterfaceClass)
562					{
563						// Iterate on interfaces of the current device
564						IOUSBFindInterfaceRequest interfaceClassRequest;
565						io_iterator_t			  interfaceIterator;
566						io_service_t			  interface;
567
568						interfaceClassRequest.bInterfaceClass = driver->m_class;
569						interfaceClassRequest.bInterfaceSubClass = driver->m_subClass;
570						interfaceClassRequest.bInterfaceProtocol = driver->m_protocol;
571						interfaceClassRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare;
572						hres = (*usbdev)->CreateInterfaceIterator(usbdev,
573																  &interfaceClassRequest,
574																  &interfaceIterator);
575						if (hres)
576						{
577							// Continue to next driver class
578							continue;
579						}
580
581						while ( (interface = IOIteratorNext(interfaceIterator)) )
582						{
583							// Found a matching device
584							*readerList = HPDeviceListInsert(*readerList, driver, usbAddress);
585							match = 1;
586							IOObjectRelease ( interface );
587						}
588
589						IOObjectRelease ( interfaceIterator );
590
591					}
592				}
593				// Add another if (!match) for other driver types
594			}
595			(*usbdev)->Release(usbdev);
596			IODestroyPlugInInterface ( iodev );
597		}
598    }
599
600    IOObjectRelease(usbIter);
601    return 0;
602}
603
604/*
605 * Finds PC Card devices currently registered in the system that match any of
606 * the drivers detected in the driver bundle vector.
607 */
608static int
609HPDriversMatchPCCardDevices(HPDriver* driverBundle, HPDeviceList* readerList)
610{
611    CFDictionaryRef pccMatch = IOServiceMatching("IOPCCard16Device");
612    if (0 == pccMatch)
613    {
614        DebugLogA("error getting PCCard match from IOServiceMatching()");
615        return 1;
616    }
617
618    io_iterator_t pccIter;
619    kern_return_t kret = IOServiceGetMatchingServices(kIOMasterPortDefault, pccMatch, &pccIter);
620    if (kret != 0)
621    {
622        DebugLogA("error getting iterator from IOServiceGetMatchingServices()");
623        return 1;
624    }
625
626    io_object_t pccDevice = 0;
627    while ((pccDevice = IOIteratorNext(pccIter)))
628    {
629
630        UInt32 vendorId   = 0;
631        UInt32 productId  = 0;
632        UInt32 pccAddress = 0;
633        CFTypeRef valueRef = IORegistryEntryCreateCFProperty(pccDevice, CFSTR("VendorID"),
634                                                             kCFAllocatorDefault, 0);
635        if (!valueRef)
636        {
637            DebugLogA("error getting vendor");
638        }
639        else
640        {
641            CFNumberGetValue((CFNumberRef)valueRef, kCFNumberSInt32Type, &vendorId);
642            CFRelease ( valueRef );
643        }
644        valueRef = IORegistryEntryCreateCFProperty(pccDevice, CFSTR("DeviceID"),
645                                                   kCFAllocatorDefault, 0);
646        if (!valueRef)
647        {
648            DebugLogA("error getting device");
649        }
650        else
651        {
652            CFNumberGetValue((CFNumberRef)valueRef, kCFNumberSInt32Type, &productId);
653            CFRelease ( valueRef );
654        }
655        valueRef = IORegistryEntryCreateCFProperty(pccDevice, CFSTR("SocketNumber"),
656                                                   kCFAllocatorDefault, 0);
657        if (!valueRef)
658        {
659            DebugLogA("error getting PC Card socket");
660        }
661        else
662        {
663            CFNumberGetValue((CFNumberRef)valueRef, kCFNumberSInt32Type, &pccAddress);
664            CFRelease ( valueRef );
665        }
666        HPDriver* driver = driverBundle;
667        for (; driver->m_vendorId; ++driver)
668        {
669            if ((driver->m_vendorId == vendorId)
670                && (driver->m_productId == productId))
671            {
672                *readerList = HPDeviceListInsert(*readerList, driver, pccAddress);
673            }
674        }
675
676        IOObjectRelease ( pccDevice );
677
678    }
679    IOObjectRelease(pccIter);
680    return 0;
681}
682
683
684static void
685HPEstablishUSBNotification()
686{
687
688    CFMutableDictionaryRef  matchingDictionary;
689    IOReturn                kret;
690
691    if ( sNotificationPort == NULL )
692		sNotificationPort = IONotificationPortCreate(kIOMasterPortDefault);
693
694    CFRunLoopAddSource(CFRunLoopGetCurrent(),
695                       IONotificationPortGetRunLoopSource(sNotificationPort),
696                       kCFRunLoopDefaultMode);
697
698    matchingDictionary = IOServiceMatching("IOUSBDevice");
699    if (!matchingDictionary)
700    {
701        DebugLogB("IOServiceMatching() failed", 0);
702    }
703    matchingDictionary = (CFMutableDictionaryRef)CFRetain(matchingDictionary);
704
705    kret = IOServiceAddMatchingNotification(sNotificationPort,
706                                            kIOMatchedNotification,
707                                            matchingDictionary,
708                                            HPDeviceAppeared, NULL,
709                                            &sUSBAppearedIter);
710    if (kret)
711    {
712        DebugLogB("IOServiceAddMatchingNotification()-1 failed with code %d", kret);
713    }
714
715    HPDeviceAppeared(NULL, sUSBAppearedIter);
716
717    kret = IOServiceAddMatchingNotification(sNotificationPort,
718                                            kIOTerminatedNotification,
719                                            matchingDictionary,
720                                            HPDeviceDisappeared, NULL,
721                                            &sUSBRemovedIter);
722    if (kret)
723    {
724        DebugLogB("IOServiceAddMatchingNotification()-2 failed with code %d", kret);
725    }
726    HPDeviceDisappeared(NULL, sUSBRemovedIter);
727}
728
729static void
730HPEstablishPCCardNotification()
731{
732
733	CFMutableDictionaryRef  matchingDictionary;
734    IOReturn                kret;
735
736	if ( sNotificationPort == NULL )
737		sNotificationPort = IONotificationPortCreate(kIOMasterPortDefault);
738
739    CFRunLoopAddSource(CFRunLoopGetCurrent(),
740                       IONotificationPortGetRunLoopSource(sNotificationPort),
741                       kCFRunLoopDefaultMode);
742
743    matchingDictionary = IOServiceMatching("IOPCCard16Device");
744    if (!matchingDictionary)
745    {
746        DebugLogB("IOServiceMatching() failed", 0);
747    }
748    matchingDictionary = (CFMutableDictionaryRef)CFRetain(matchingDictionary);
749
750    kret = IOServiceAddMatchingNotification(sNotificationPort,
751                                            kIOMatchedNotification,
752                                            matchingDictionary,
753                                            HPDeviceAppeared, NULL,
754                                            &sPCCardAppearedIter);
755    if (kret)
756    {
757        DebugLogB("IOServiceAddMatchingNotification()-1 failed with code %d", kret);
758    }
759    HPDeviceAppeared(NULL, sPCCardAppearedIter);
760
761    kret = IOServiceAddMatchingNotification(sNotificationPort,
762                                            kIOTerminatedNotification,
763                                            matchingDictionary,
764                                            HPDeviceDisappeared, NULL,
765                                            &sPCCardRemovedIter);
766    if (kret)
767    {
768        DebugLogB("IOServiceAddMatchingNotification()-2 failed with code %d", kret);
769    }
770    HPDeviceDisappeared(NULL, sPCCardRemovedIter);
771}
772
773/*
774 * Thread runner (does not return).
775 */
776static void
777HPDeviceNotificationThread()
778{
779    HPEstablishUSBNotification();
780    HPEstablishPCCardNotification();
781    CFRunLoopRun();
782}
783
784/*
785 * Scans the hotplug driver directory and looks in the system for matching devices.
786 * Adds or removes matching readers as necessary.
787 */
788LONG
789HPSearchHotPluggables()
790{
791    HPDriver* drivers = HPDriversGetFromDirectory(PCSCLITE_HP_DROPDIR);
792    if (!drivers) return 1;
793
794    HPDeviceList devices = NULL;
795    int istat;
796    istat = HPDriversMatchUSBDevices(drivers, &devices);
797    if (istat)
798    {
799        return -1;
800    }
801    istat = HPDriversMatchPCCardDevices(drivers, &devices);
802    if (istat)
803    {
804        return -1;
805    }
806
807    HPDevice* a = devices;
808    for (; a; a = a->m_next)
809    {
810        int found = 0;
811        HPDevice* b = sDeviceList;
812        for (; b; b = b->m_next)
813        {
814            if (HPDeviceEquals(a, b))
815            {
816                found = 1;
817                break;
818            }
819        }
820        if (!found)
821        {
822            RFAddReader(a->m_driver->m_friendlyName,
823                        PCSCLITE_HP_BASE_PORT + a->m_address,
824                        a->m_driver->m_libPath);
825        }
826    }
827
828    a = sDeviceList;
829    for (; a; a = a->m_next)
830    {
831        int found = 0;
832        HPDevice* b = devices;
833        for (; b; b = b->m_next)
834        {
835            if (HPDeviceEquals(a, b))
836            {
837                found = 1;
838                break;
839            }
840        }
841        if (!found)
842        {
843            RFRemoveReader(a->m_driver->m_friendlyName,
844                           PCSCLITE_HP_BASE_PORT + a->m_address);
845        }
846    }
847
848    HPDeviceListRelease(sDeviceList);
849    sDeviceList = devices;
850    HPDriverVectorRelease(drivers);
851    return 0;
852}
853
854
855PCSCLITE_THREAD_T sHotplugWatcherThread;
856
857/*
858 * Sets up callbacks for device hotplug events.
859 */
860LONG
861HPRegisterForHotplugEvents()
862{
863    LONG sstat;
864    sstat = SYS_ThreadCreate(&sHotplugWatcherThread,
865                             NULL,
866                             (LPVOID)HPDeviceNotificationThread,
867                             NULL);
868    return 0;
869}
870