1/*
2 * Copyright (c) 1999-2008 Apple Computer, Inc.  All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23#include <pthread.h>
24#include <CoreFoundation/CFRuntime.h>
25#include "IOHIDManager.h"
26#include <IOKit/IOKitLib.h>
27#include "IOHIDLibPrivate.h"
28#include "IOHIDDevice.h"
29#include "IOHIDLib.h"
30#include "IOHIDManagerPersistentProperties.h"
31
32static IOHIDManagerRef  __IOHIDManagerCreate(
33                                    CFAllocatorRef          allocator,
34                                    CFAllocatorContext *    context __unused);
35static void             __IOHIDManagerRelease( CFTypeRef object );
36static void             __IOHIDManagerSetDeviceMatching(
37                                IOHIDManagerRef                 manager,
38                                CFDictionaryRef                 matching);
39static void             __IOHIDManagerDeviceAdded(
40                                    void *                      context,
41                                    io_iterator_t               iterator);
42static void             __IOHIDManagerDeviceRemoved(
43                                    void *                      context,
44                                    IOReturn                    result,
45                                    void *                      sender);
46static void             __IOHIDManagerDeviceApplier(
47                                    const void *                value,
48                                    void *                      context);
49static void             __IOHIDManagerInitialEnumCallback(
50                                    void *                      info);
51static void             __IOHIDManagerMergeDictionaries(
52                                    CFDictionaryRef             srcDict,
53                                    CFMutableDictionaryRef      dstDict);
54
55enum {
56    kDeviceApplierOpen                      = 1 << 0,
57    kDeviceApplierClose                     = 1 << 1,
58    kDeviceApplierInitEnumCallback          = 1 << 2,
59    kDeviceApplierSetInputMatching          = 1 << 3,
60    kDeviceApplierSetInputCallback          = 1 << 4,
61    kDeviceApplierSetInputReportCallback    = 1 << 5,
62    kDeviceApplierScheduleRunLoop           = 1 << 6,
63    kDeviceApplierUnscheduleRunLoop         = 1 << 7
64};
65
66typedef struct __DeviceApplierArgs {
67    IOHIDManagerRef     manager;
68    IOOptionBits        options;
69    IOReturn            retVal;
70}   DeviceApplierArgs;
71
72typedef struct __IOHIDManager
73{
74    CFRuntimeBase                   cfBase;   // base CFType information
75
76    CFMutableSetRef                 devices;
77    CFMutableSetRef                 iterators;
78    CFMutableDictionaryRef          properties;
79    CFMutableDictionaryRef          deviceInputBuffers;
80
81    IONotificationPortRef           notifyPort;
82    CFRunLoopRef                    runLoop;
83    CFStringRef                     runLoopMode;
84
85    CFRunLoopSourceRef              initEnumRunLoopSource;
86    CFMutableDictionaryRef          initRetVals;
87
88    Boolean                         isOpen;
89    IOOptionBits                    openOptions;
90    IOOptionBits                    createOptions;
91
92    void *                          inputContext;
93    IOHIDValueCallback              inputCallback;
94
95    void *                          reportContext;
96    IOHIDReportCallback             reportCallback;
97
98    void *                          matchContext;
99    IOHIDDeviceCallback             matchCallback;
100
101    void *                          removalContext;
102    IOHIDDeviceCallback             removalCallback;
103
104    CFArrayRef                      inputMatchingMultiple;
105    Boolean                         isDirty;
106
107} __IOHIDManager, *__IOHIDManagerRef;
108
109static const CFRuntimeClass __IOHIDManagerClass = {
110    0,                      // version
111    "IOHIDManager",         // className
112    NULL,                   // init
113    NULL,                   // copy
114    __IOHIDManagerRelease,   // finalize
115    NULL,                   // equal
116    NULL,                   // hash
117    NULL,                   // copyFormattingDesc
118    NULL,
119    NULL,
120    NULL
121};
122
123static pthread_once_t __sessionTypeInit = PTHREAD_ONCE_INIT;
124static CFTypeID __kIOHIDManagerTypeID = _kCFRuntimeNotATypeID;
125
126//------------------------------------------------------------------------------
127// __IOHIDManagerRegister
128//------------------------------------------------------------------------------
129void __IOHIDManagerRegister(void)
130{
131    __kIOHIDManagerTypeID = _CFRuntimeRegisterClass(&__IOHIDManagerClass);
132}
133
134//------------------------------------------------------------------------------
135// __IOHIDManagerCreate
136//------------------------------------------------------------------------------
137IOHIDManagerRef __IOHIDManagerCreate(
138                                CFAllocatorRef              allocator,
139                                CFAllocatorContext *        context __unused)
140{
141    IOHIDManagerRef manager = NULL;
142    void *          offset  = NULL;
143    uint32_t        size;
144
145    /* allocate service */
146    size    = sizeof(__IOHIDManager) - sizeof(CFRuntimeBase);
147    manager = (IOHIDManagerRef)_CFRuntimeCreateInstance(
148                                                allocator,
149                                                IOHIDManagerGetTypeID(),
150                                                size,
151                                                NULL);
152
153    if (!manager)
154        return NULL;
155
156    offset = manager;
157    bzero(offset + sizeof(CFRuntimeBase), size);
158
159    return manager;
160}
161
162//------------------------------------------------------------------------------
163// __IOHIDManagerRelease
164//------------------------------------------------------------------------------
165void __IOHIDManagerRelease( CFTypeRef object )
166{
167    IOHIDManagerRef manager = (IOHIDManagerRef)object;
168
169    if ( (manager->createOptions & kIOHIDManagerOptionUsePersistentProperties) &&
170        !(manager->createOptions & kIOHIDManagerOptionDoNotSaveProperties)) {
171        __IOHIDManagerSaveProperties(manager, NULL);
172    }
173
174    if ( manager->isOpen ) {
175        // This will unschedule the manager if it was opened
176        IOHIDManagerClose(manager, manager->openOptions);
177    }
178
179    if ( manager->runLoop ) {
180        // This will unschedule the manager if it wasn't
181        IOHIDManagerUnscheduleFromRunLoop(manager, manager->runLoop, manager->runLoopMode);
182    }
183
184    // Destroy the notification
185    if (manager->notifyPort) {
186        CFRunLoopSourceInvalidate(IONotificationPortGetRunLoopSource(manager->notifyPort));
187        if (manager->runLoop)
188            CFRunLoopRemoveSource(manager->runLoop,
189                                  IONotificationPortGetRunLoopSource(manager->notifyPort),
190                                  manager->runLoopMode);
191        IONotificationPortDestroy(manager->notifyPort);
192        manager->notifyPort = NULL;
193    }
194
195    if ( manager->devices ) {
196        CFRelease(manager->devices);
197        manager->devices = NULL;
198    }
199
200    if ( manager->deviceInputBuffers ) {
201        CFRelease(manager->deviceInputBuffers);
202        manager->deviceInputBuffers = NULL;
203    }
204
205    if ( manager->iterators ) {
206        CFRelease(manager->iterators);
207        manager->iterators = NULL;
208    }
209
210    if ( manager->properties ) {
211        CFRelease(manager->properties);
212        manager->properties = NULL;
213    }
214
215    if ( manager->initRetVals ) {
216        CFRelease(manager->initRetVals);
217        manager->initRetVals = NULL;
218    }
219
220    if ( manager->inputMatchingMultiple ) {
221        CFRelease(manager->inputMatchingMultiple);
222        manager->inputMatchingMultiple = NULL;
223    }
224}
225
226//------------------------------------------------------------------------------
227// __IOHIDManagerSetDeviceMatching
228//------------------------------------------------------------------------------
229void __IOHIDManagerSetDeviceMatching(
230                                IOHIDManagerRef                 manager,
231                                CFDictionaryRef                 matching)
232{
233    CFMutableDictionaryRef  matchingDict = NULL;
234    io_iterator_t           iterator;
235    IOReturn                kr;
236
237    if (!manager->notifyPort) {
238        manager->notifyPort = IONotificationPortCreate(kIOMasterPortDefault);
239
240        if (manager->runLoop)
241            CFRunLoopAddSource(
242                        manager->runLoop,
243                        IONotificationPortGetRunLoopSource(manager->notifyPort),
244                        manager->runLoopMode);
245    }
246
247    matchingDict = IOServiceMatching(kIOHIDDeviceKey);
248
249    if ( !matchingDict )
250        return;
251
252    __IOHIDManagerMergeDictionaries(matching, matchingDict);
253
254    // Now set up a notification to be called when a device is first
255    // matched by I/O Kit.  Note that this will not catch any devices that were
256    // already plugged in so we take care of those later.
257    kr = IOServiceAddMatchingNotification(manager->notifyPort,
258                                          kIOFirstMatchNotification,
259                                          matchingDict,
260                                          __IOHIDManagerDeviceAdded,
261                                          manager,
262                                          &iterator
263                                          );
264
265    if ( kr != kIOReturnSuccess )
266        return;
267
268    // Add iterator to set for later destruction
269    if ( !manager->iterators ) {
270        CFSetCallBacks callbacks;
271
272        bzero(&callbacks, sizeof(CFSetCallBacks));
273
274        callbacks.retain    = _IOObjectCFRetain;
275        callbacks.release   = _IOObjectCFRelease;
276
277        manager->iterators  = CFSetCreateMutable(
278                                            CFGetAllocator(manager),
279                                            0,
280                                            &callbacks);
281        if ( !manager->iterators )
282            return;
283    }
284
285    intptr_t temp = iterator;
286    CFSetAddValue(manager->iterators, (void *)temp);
287    IOObjectRelease(iterator);
288
289    __IOHIDManagerDeviceAdded(manager, iterator);
290}
291
292//------------------------------------------------------------------------------
293// __IOHIDManagerDeviceAdded
294//------------------------------------------------------------------------------
295void __IOHIDManagerDeviceAdded(     void *                      refcon,
296                                    io_iterator_t               iterator)
297{
298    IOHIDManagerRef manager = (IOHIDManagerRef)refcon;
299    IOHIDDeviceRef  device;
300    IOReturn        retVal;
301    io_service_t    service;
302    Boolean         initial = FALSE;
303
304    while (( service = IOIteratorNext(iterator) )) {
305
306        device = IOHIDDeviceCreate(CFGetAllocator(manager), service);
307
308        if ( device ) {
309            if ( !manager->devices ) {
310                manager->devices = CFSetCreateMutable(
311                                                        CFGetAllocator(manager),
312                                                        0,
313                                                        &kCFTypeSetCallBacks);
314                initial = TRUE;
315
316                if ( manager->isOpen )
317                    manager->initRetVals = CFDictionaryCreateMutable(
318                                            CFGetAllocator(manager),
319                                            0,
320                                            &kCFTypeDictionaryKeyCallBacks,
321                                            NULL);
322            }
323
324            if ( manager->devices ) {
325                CFSetAddValue(manager->devices, device);
326            }
327
328            CFRelease(device);
329
330            DeviceApplierArgs args;
331
332            args.manager = manager;
333            args.options = 0;
334
335            IOHIDDeviceRegisterRemovalCallback(
336                                            device,
337                                            __IOHIDManagerDeviceRemoved,
338                                            manager);
339
340            retVal = kIOReturnSuccess;
341
342            if ( manager->isOpen )
343                args.options |= kDeviceApplierOpen;
344
345            if ( manager->inputMatchingMultiple )
346                args.options |= kDeviceApplierSetInputMatching;
347
348            if ( manager->inputCallback )
349                args.options |= kDeviceApplierSetInputCallback;
350
351            if ( manager->reportCallback )
352                args.options |= kDeviceApplierSetInputReportCallback;
353
354            if ( manager->runLoop ) {
355                args.options |= kDeviceApplierScheduleRunLoop;
356
357                // If this this is called using the iterator returned in
358                // IOServiceAddMatchingNotification, pend performing the
359                // callback on the runLoop
360                if ( !initial && manager->matchCallback )
361                    args.options |= kDeviceApplierInitEnumCallback;
362            }
363
364            __IOHIDManagerDeviceApplier((const void *)device, &args);
365
366            if ( (manager->createOptions & kIOHIDManagerOptionUsePersistentProperties) &&
367                !(manager->createOptions & kIOHIDManagerOptionDoNotLoadProperties)) {
368                __IOHIDDeviceLoadProperties(device);
369            }
370            if (manager->properties) {
371                CFDictionaryApplyFunction(manager->properties,
372                                          __IOHIDApplyPropertiesToDeviceFromDictionary,
373                                          device);
374            }
375        }
376
377        IOObjectRelease(service);
378    }
379
380    // Dispatch initial enumeration callback on runLoop
381    if ( initial ) {
382
383        CFRunLoopSourceContext  context;
384
385        bzero(&context, sizeof(CFRunLoopSourceContext));
386
387        context.info    = manager;
388        context.perform = __IOHIDManagerInitialEnumCallback;
389
390        manager->initEnumRunLoopSource = CFRunLoopSourceCreate(
391                                                        CFGetAllocator(manager),
392                                                        0,
393                                                        &context);
394
395        if ( manager->runLoop && manager->initEnumRunLoopSource ) {
396            CFRunLoopAddSource(
397                        manager->runLoop,
398                        manager->initEnumRunLoopSource,
399                        manager->runLoopMode);
400
401            CFRunLoopSourceSignal(manager->initEnumRunLoopSource);
402        }
403    }
404}
405
406//------------------------------------------------------------------------------
407// __IOHIDManagerDeviceRemoved
408//------------------------------------------------------------------------------
409void __IOHIDManagerDeviceRemoved(   void *                      context,
410                                    IOReturn                    result __unused,
411                                    void *                      sender)
412{
413    IOHIDManagerRef manager = (IOHIDManagerRef)context;
414
415    if ( manager->deviceInputBuffers )
416        CFDictionaryRemoveValue(manager->deviceInputBuffers, sender);
417
418    if ( (manager->createOptions & kIOHIDManagerOptionUsePersistentProperties) &&
419        !(manager->createOptions & kIOHIDManagerOptionDoNotSaveProperties)) {
420        if (CFSetContainsValue(manager->devices, sender))
421            __IOHIDDeviceSaveProperties((IOHIDDeviceRef)sender, NULL);
422    }
423
424    CFSetRemoveValue(manager->devices, sender);
425
426    if ( manager->removalCallback )
427        (*manager->removalCallback)(manager->removalContext,
428                                    kIOReturnSuccess,
429                                    manager,
430                                    sender);
431}
432
433//------------------------------------------------------------------------------
434// __IOHIDManagerInitialEnumCallback
435//------------------------------------------------------------------------------
436void __IOHIDManagerInitialEnumCallback(void * info)
437{
438    IOHIDManagerRef manager = (IOHIDManagerRef)info;
439
440    if ( manager->matchCallback && manager->devices ) {
441        DeviceApplierArgs args;
442
443        args.manager = manager;
444        args.options = kDeviceApplierInitEnumCallback;
445
446        CFSetApplyFunction( manager->devices,
447                            __IOHIDManagerDeviceApplier,
448                            &args);
449    }
450
451    // After we have dispatched all of the enum callbacks, kill the source
452    if (manager->runLoop)
453        CFRunLoopRemoveSource(manager->runLoop,
454                              manager->initEnumRunLoopSource,
455                              manager->runLoopMode);
456
457    CFRelease(manager->initEnumRunLoopSource);
458    manager->initEnumRunLoopSource = NULL;
459
460    if ( manager->initRetVals ) {
461        CFRelease(manager->initRetVals);
462        manager->initRetVals = NULL;
463    }
464}
465
466//------------------------------------------------------------------------------
467// __IOHIDManagerDeviceApplier
468//------------------------------------------------------------------------------
469void __IOHIDManagerDeviceApplier(
470                                    const void *                value,
471                                    void *                      context)
472{
473    DeviceApplierArgs * args    = (DeviceApplierArgs*)context;
474    IOHIDDeviceRef      device  = (IOHIDDeviceRef)value;
475    intptr_t            retVal  = kIOReturnSuccess;
476
477    if ( args->options & kDeviceApplierOpen ) {
478        retVal = IOHIDDeviceOpen(           device,
479                                            args->manager->openOptions);
480        if ( args->manager->initRetVals )
481            CFDictionarySetValue(args->manager->initRetVals, device, (void*)retVal);
482    }
483
484    if ( args->options & kDeviceApplierClose )
485        retVal = IOHIDDeviceClose(          device,
486                                            args->manager->openOptions);
487
488    if ( args->options & kDeviceApplierInitEnumCallback ) {
489        if ( args->manager->initRetVals )
490            retVal = (intptr_t)CFDictionaryGetValue(
491                                            args->manager->initRetVals,
492                                            device);
493
494        (*args->manager->matchCallback)(    args->manager->matchContext,
495                                            retVal,
496                                            args->manager,
497                                            device);
498    }
499
500    if ( args->options & kDeviceApplierSetInputMatching )
501        IOHIDDeviceSetInputValueMatchingMultiple(
502                                            device,
503                                            args->manager->inputMatchingMultiple);
504
505    if ( args->options & kDeviceApplierSetInputCallback )
506        IOHIDDeviceRegisterInputValueCallback(
507                                            device,
508                                            args->manager->inputCallback,
509                                            args->manager->inputContext);
510
511    if ( args->options & kDeviceApplierSetInputReportCallback ) {
512        CFMutableDataRef dataRef = NULL;
513        if ( !args->manager->deviceInputBuffers ) {
514            args->manager->deviceInputBuffers = CFDictionaryCreateMutable(CFGetAllocator(args->manager), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
515        }
516
517        dataRef = (CFMutableDataRef)CFDictionaryGetValue(args->manager->deviceInputBuffers, device);
518        if ( !dataRef ) {
519            CFNumberRef number = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDMaxInputReportSizeKey));
520            CFIndex     length = 64;
521
522            if ( number ) {
523                CFNumberGetValue(number, kCFNumberCFIndexType, &length);
524            }
525
526            dataRef = CFDataCreateMutable(CFGetAllocator(args->manager), length);
527            if ( dataRef ) {
528                CFDataSetLength(dataRef, length);
529                CFDictionarySetValue(args->manager->deviceInputBuffers, device, dataRef);
530                CFRelease(dataRef);
531            }
532        }
533
534        IOHIDDeviceRegisterInputReportCallback(
535                                            device,
536                                            (uint8_t *)CFDataGetMutableBytePtr(dataRef),
537                                            CFDataGetLength(dataRef),
538                                            args->manager->reportCallback,
539                                            args->manager->reportContext);
540    }
541
542    if ( args->options & kDeviceApplierScheduleRunLoop )
543        IOHIDDeviceScheduleWithRunLoop(     device,
544                                            args->manager->runLoop,
545                                            args->manager->runLoopMode);
546
547    if ( args->options & kDeviceApplierUnscheduleRunLoop )
548        IOHIDDeviceUnscheduleFromRunLoop(   device,
549                                            args->manager->runLoop,
550                                            args->manager->runLoopMode);
551
552    if ( args->retVal == kIOReturnSuccess && retVal != kIOReturnSuccess )
553        args->retVal = retVal;
554}
555
556//------------------------------------------------------------------------------
557// IOHIDManagerGetTypeID
558//------------------------------------------------------------------------------
559CFTypeID IOHIDManagerGetTypeID(void)
560{
561    if ( _kCFRuntimeNotATypeID == __kIOHIDManagerTypeID )
562        pthread_once(&__sessionTypeInit, __IOHIDManagerRegister);
563
564    return __kIOHIDManagerTypeID;
565}
566
567//------------------------------------------------------------------------------
568// IOHIDManagerCreate
569//------------------------------------------------------------------------------
570IOHIDManagerRef IOHIDManagerCreate(
571                                    CFAllocatorRef          allocator,
572                                    IOOptionBits            options)
573{
574    IOHIDManagerRef manager;
575
576    manager = __IOHIDManagerCreate(allocator, NULL);
577
578    if (!manager)
579        return NULL;
580
581    manager->createOptions = options;
582
583    if ( (manager->createOptions & kIOHIDManagerOptionUsePersistentProperties) &&
584        !(manager->createOptions & kIOHIDManagerOptionDoNotLoadProperties))
585    {
586        __IOHIDManagerLoadProperties(manager);
587    }
588
589    return manager;
590}
591
592//------------------------------------------------------------------------------
593// IOHIDManagerOpen
594//------------------------------------------------------------------------------
595IOReturn IOHIDManagerOpen(
596                                IOHIDManagerRef                 manager,
597                                IOOptionBits                    options)
598{
599    IOReturn retVal = kIOReturnSuccess;
600
601    if ( !manager->isOpen ) {
602        manager->isOpen     = TRUE;
603        manager->openOptions    = options;
604
605        if ( manager->devices ) {
606            DeviceApplierArgs args;
607
608            args.manager        = manager;
609            args.options        = kDeviceApplierOpen;
610            args.retVal         = kIOReturnSuccess;
611
612            if ( manager->inputMatchingMultiple )
613                args.options |= kDeviceApplierSetInputMatching;
614
615            if ( manager->inputCallback )
616                args.options |= kDeviceApplierSetInputCallback;
617
618            if ( manager->reportCallback )
619                args.options |= kDeviceApplierSetInputReportCallback;
620
621            CFSetApplyFunction( manager->devices,
622                                __IOHIDManagerDeviceApplier,
623                                &args);
624
625            retVal = args.retVal;
626        }
627    }
628
629    return retVal;
630}
631
632//------------------------------------------------------------------------------
633// IOHIDManagerClose
634//------------------------------------------------------------------------------
635IOReturn IOHIDManagerClose(
636                                IOHIDManagerRef                 manager,
637                                IOOptionBits                    options)
638{
639    IOReturn retVal = kIOReturnSuccess;
640
641    if (manager->runLoop) {
642        IOHIDManagerUnscheduleFromRunLoop(manager, manager->runLoop, manager->runLoopMode);
643    }
644
645    if ( manager->isOpen ) {
646        manager->isOpen     = FALSE;
647        manager->openOptions    = options;
648
649        if ( manager->devices ) {
650            DeviceApplierArgs args;
651
652            args.manager        = manager;
653            args.options        = kDeviceApplierClose;
654            args.retVal         = kIOReturnSuccess;
655
656            CFSetApplyFunction( manager->devices,
657                                __IOHIDManagerDeviceApplier,
658                                &args);
659
660            retVal = args.retVal;
661        }
662    }
663
664    if ( (manager->createOptions & kIOHIDManagerOptionUsePersistentProperties) &&
665        !(manager->createOptions & kIOHIDManagerOptionDoNotSaveProperties)) {
666        __IOHIDManagerSaveProperties(manager, NULL);
667    }
668
669    return retVal;
670}
671
672//------------------------------------------------------------------------------
673// IOHIDManagerGetProperty
674//------------------------------------------------------------------------------
675CFTypeRef IOHIDManagerGetProperty(
676                                IOHIDManagerRef                 manager,
677                                CFStringRef                     key)
678{
679    if ( !manager->properties )
680        return NULL;
681
682    return CFDictionaryGetValue(manager->properties, key);
683}
684
685//------------------------------------------------------------------------------
686// IOHIDManagerSetProperty
687//------------------------------------------------------------------------------
688Boolean IOHIDManagerSetProperty(
689                                IOHIDManagerRef                 manager,
690                                CFStringRef                     key,
691                                CFTypeRef                       value)
692{
693    __IOHIDApplyPropertyToSetContext context = {
694        key, value
695    };
696
697    if (!manager->properties) {
698        manager->properties = CFDictionaryCreateMutable(CFGetAllocator(manager),
699                                                        0,
700                                                        &kCFTypeDictionaryKeyCallBacks,
701                                                        &kCFTypeDictionaryValueCallBacks);
702        if (!manager->properties)
703            return FALSE;
704    }
705
706    manager->isDirty = TRUE;
707    CFDictionarySetValue(manager->properties, key, value);
708    if (manager->devices)
709        CFSetApplyFunction(manager->devices, __IOHIDApplyPropertyToDeviceSet, &context);
710
711    return TRUE;
712}
713
714//------------------------------------------------------------------------------
715// IOHIDManagerSetDeviceMatching
716//------------------------------------------------------------------------------
717void IOHIDManagerSetDeviceMatching(
718                                IOHIDManagerRef                 manager,
719                                CFDictionaryRef                 matching)
720{
721    CFArrayRef multiple = NULL;
722
723    if ( matching )
724        multiple = CFArrayCreate(CFGetAllocator(manager),
725                                (const void **)&matching,
726                                1,
727                                &kCFTypeArrayCallBacks);
728
729    IOHIDManagerSetDeviceMatchingMultiple(manager, multiple);
730
731    if ( multiple )
732        CFRelease(multiple);
733}
734
735//------------------------------------------------------------------------------
736// IOHIDManagerSetDeviceMatchingMultiple
737//------------------------------------------------------------------------------
738void IOHIDManagerSetDeviceMatchingMultiple(
739                                IOHIDManagerRef                 manager,
740                                CFArrayRef                      multiple)
741{
742    CFIndex         i, count;
743    CFTypeRef       value;
744
745    if ( manager->devices ) {
746        if ( (manager->createOptions & kIOHIDManagerOptionUsePersistentProperties) &&
747            !(manager->createOptions & kIOHIDManagerOptionDoNotSaveProperties)) {
748            __IOHIDManagerSaveProperties(manager, NULL);
749        }
750        CFSetRemoveAllValues(manager->devices);
751    }
752
753    if ( manager->iterators )
754        CFSetRemoveAllValues(manager->iterators);
755
756    if ( manager->deviceInputBuffers )
757        CFDictionaryRemoveAllValues(manager->deviceInputBuffers);
758
759    if ( multiple ) {
760        count = CFArrayGetCount(multiple);
761        for ( i=0; i<count; i++ ) {
762            value = CFArrayGetValueAtIndex(multiple, i);
763            if (CFDictionaryGetTypeID() == CFGetTypeID(value))
764                __IOHIDManagerSetDeviceMatching(manager, (CFDictionaryRef)value);
765        }
766    }
767    else
768        __IOHIDManagerSetDeviceMatching(manager, NULL);
769
770}
771
772//------------------------------------------------------------------------------
773// IOHIDManagerCopyDevices
774//------------------------------------------------------------------------------
775CFSetRef IOHIDManagerCopyDevices(
776                                IOHIDManagerRef                 manager)
777{
778    return manager->devices ?
779                CFSetCreateCopy(CFGetAllocator(manager), manager->devices) : NULL;
780}
781
782//------------------------------------------------------------------------------
783// IOHIDManagerRegisterDeviceMatchingCallback
784//------------------------------------------------------------------------------
785void IOHIDManagerRegisterDeviceMatchingCallback(
786                                IOHIDManagerRef                 manager,
787                                IOHIDDeviceCallback             callback,
788                                void *                          context)
789{
790    manager->matchCallback  = callback;
791    manager->matchContext   = context;
792}
793
794//------------------------------------------------------------------------------
795// IOHIDManagerRegisterDeviceRemovalCallback
796//------------------------------------------------------------------------------
797void IOHIDManagerRegisterDeviceRemovalCallback(
798                                IOHIDManagerRef                 manager,
799                                IOHIDDeviceCallback             callback,
800                                void *                          context)
801{
802    manager->removalCallback    = callback;
803    manager->removalContext     = context;
804
805}
806
807//------------------------------------------------------------------------------
808// IOHIDManagerRegisterInputReportCallback
809//------------------------------------------------------------------------------
810void IOHIDManagerRegisterInputReportCallback(
811                                    IOHIDManagerRef             manager,
812                                    IOHIDReportCallback         callback,
813                                    void *                      context)
814{
815    manager->reportCallback  = callback;
816    manager->reportContext   = context;
817
818    if ( manager->isOpen && manager->devices ) {
819        DeviceApplierArgs args;
820
821        args.manager = manager;
822        args.options = kDeviceApplierSetInputReportCallback;
823
824        CFSetApplyFunction( manager->devices,
825                            __IOHIDManagerDeviceApplier,
826                            &args);
827    }
828}
829
830//------------------------------------------------------------------------------
831// IOHIDManagerRegisterInputValueCallback
832//------------------------------------------------------------------------------
833void IOHIDManagerRegisterInputValueCallback(
834                                    IOHIDManagerRef             manager,
835                                    IOHIDValueCallback          callback,
836                                    void *                      context)
837{
838    manager->inputCallback  = callback;
839    manager->inputContext   = context;
840
841    if ( manager->isOpen && manager->devices ) {
842        DeviceApplierArgs args;
843
844        args.manager = manager;
845        args.options = kDeviceApplierSetInputCallback;
846
847        CFSetApplyFunction( manager->devices,
848                            __IOHIDManagerDeviceApplier,
849                            &args);
850    }
851}
852
853//------------------------------------------------------------------------------
854// IOHIDManagerSetInputValueMatching
855//------------------------------------------------------------------------------
856void IOHIDManagerSetInputValueMatching(
857                                    IOHIDManagerRef             manager,
858                                    CFDictionaryRef             matching)
859{
860    if ( matching ) {
861        CFArrayRef multiple = CFArrayCreate(CFGetAllocator(manager), (const void **)&matching, 1, &kCFTypeArrayCallBacks);
862
863        IOHIDManagerSetInputValueMatchingMultiple(manager, multiple);
864
865        CFRelease(multiple);
866    } else {
867        IOHIDManagerSetInputValueMatchingMultiple(manager, NULL);
868    }
869}
870
871//------------------------------------------------------------------------------
872// IOHIDManagerSetInputValueMatchingMultiple
873//------------------------------------------------------------------------------
874void IOHIDManagerSetInputValueMatchingMultiple(
875                                    IOHIDManagerRef             manager,
876                                    CFArrayRef                  multiple)
877{
878    if ( manager->inputMatchingMultiple )
879        CFRelease(manager->inputMatchingMultiple);
880
881    if ( multiple )
882        CFRetain(multiple);
883
884    manager->inputMatchingMultiple = multiple;
885
886    if ( manager->isOpen && manager->devices ) {
887        DeviceApplierArgs args;
888
889        args.manager = manager;
890        args.options = kDeviceApplierSetInputMatching;
891
892        CFSetApplyFunction( manager->devices,
893                            __IOHIDManagerDeviceApplier,
894                            &args);
895    }
896}
897
898//------------------------------------------------------------------------------
899// IOHIDManagerScheduleWithRunLoop
900//------------------------------------------------------------------------------
901void IOHIDManagerScheduleWithRunLoop(
902                                        IOHIDManagerRef         manager,
903                                        CFRunLoopRef            runLoop,
904                                        CFStringRef             runLoopMode)
905{
906    manager->runLoop        = runLoop;
907    manager->runLoopMode    = runLoopMode;
908
909    if ( manager->runLoop ) {
910        // Schedule the notifyPort
911        if (manager->notifyPort)
912            CFRunLoopAddSource(
913                        manager->runLoop,
914                        IONotificationPortGetRunLoopSource(manager->notifyPort),
915                        manager->runLoopMode);
916
917        // schedule the initial enumeration routine
918        if ( manager->initEnumRunLoopSource ) {
919            CFRunLoopAddSource(
920                        manager->runLoop,
921                        manager->initEnumRunLoopSource,
922                        manager->runLoopMode);
923
924            CFRunLoopSourceSignal(manager->initEnumRunLoopSource);
925        }
926
927        // Schedule the devices
928        if ( manager->devices ) {
929            DeviceApplierArgs args;
930
931            args.manager = manager;
932            args.options = kDeviceApplierScheduleRunLoop;
933
934            CFSetApplyFunction( manager->devices,
935                                __IOHIDManagerDeviceApplier,
936                                &args);
937        }
938    }
939}
940
941//------------------------------------------------------------------------------
942// IOHIDManagerUnscheduleFromRunLoop
943//------------------------------------------------------------------------------
944void IOHIDManagerUnscheduleFromRunLoop(
945                                        IOHIDManagerRef         manager,
946                                        CFRunLoopRef            runLoop,
947                                        CFStringRef             runLoopMode)
948{
949    if (!manager->runLoop)
950        return;
951
952    if (!CFEqual(manager->runLoop, runLoop) ||
953        !CFEqual(manager->runLoopMode, runLoopMode))
954        return;
955
956    if ( manager->devices ) {
957        // Unschedule the devices
958        DeviceApplierArgs args;
959
960        args.manager = manager;
961        args.options = kDeviceApplierUnscheduleRunLoop;
962
963        CFSetApplyFunction( manager->devices,
964                            __IOHIDManagerDeviceApplier,
965                            &args);
966
967        // Unschedule the initial enumeration routine
968        if (manager->initEnumRunLoopSource) {
969            CFRunLoopSourceInvalidate(manager->initEnumRunLoopSource);
970            CFRunLoopRemoveSource(manager->runLoop,
971                                  manager->initEnumRunLoopSource,
972                                  manager->runLoopMode);
973            CFRelease(manager->initEnumRunLoopSource);
974            manager->initEnumRunLoopSource = NULL;
975        }
976    }
977
978    manager->runLoop        = NULL;
979    manager->runLoopMode    = NULL;
980}
981
982//------------------------------------------------------------------------------
983// IOHIDManagerSaveToPropertyDomain
984//------------------------------------------------------------------------------
985void IOHIDManagerSaveToPropertyDomain(IOHIDManagerRef                 manager,
986                                      CFStringRef                     applicationID,
987                                      CFStringRef                     userName,
988                                      CFStringRef                     hostName,
989                                      IOOptionBits                    options)
990{
991    __IOHIDPropertyContext context = {
992        applicationID, userName, hostName, options
993    };
994
995    if (manager && applicationID && userName && hostName) {
996        __IOHIDManagerSaveProperties(manager, &context);
997    }
998    else {
999        // LOG AN ERROR?
1000    }
1001}
1002
1003//------------------------------------------------------------------------------
1004CFStringRef __IOHIDManagerGetRootKey()
1005{
1006    return CFSTR(kIOHIDManagerKey);
1007}
1008
1009//------------------------------------------------------------------------------
1010void __IOHIDManagerSaveProperties(IOHIDManagerRef manager, __IOHIDPropertyContext *context)
1011{
1012    if (manager->isDirty && manager->properties) {
1013        __IOHIDPropertySaveToKeyWithSpecialKeys(manager->properties,
1014                                               __IOHIDManagerGetRootKey(),
1015                                               NULL,
1016                                               context);
1017        manager->isDirty = FALSE;
1018    }
1019
1020    if (manager->devices)
1021        CFSetApplyFunction(manager->devices, __IOHIDSaveDeviceSet, context);
1022}
1023
1024//------------------------------------------------------------------------------
1025void __IOHIDManagerLoadProperties(IOHIDManagerRef manager)
1026{
1027    // Convert to __IOHIDPropertyLoadFromKeyWithSpecialKeys if we identify special keys
1028    CFMutableDictionaryRef properties = __IOHIDPropertyLoadDictionaryFromKey(__IOHIDManagerGetRootKey());
1029
1030    if (properties) {
1031        CFRELEASE_IF_NOT_NULL(manager->properties);
1032        manager->properties = properties;
1033        manager->isDirty = FALSE;
1034    }
1035
1036    // We do not load device properties here, since the devices are not present when this is called.
1037}
1038
1039//------------------------------------------------------------------------------
1040void __IOHIDPropertySaveWithContext(CFStringRef key, CFPropertyListRef value, __IOHIDPropertyContext *context)
1041{
1042    if (key && value) {
1043        if (context && context->applicationID && context->userName && context->hostName) {
1044            CFPreferencesSetValue(key, value, context->applicationID, context->userName, context->hostName);
1045        }
1046        else {
1047            CFPreferencesSetAppValue(key, value, kCFPreferencesCurrentApplication);
1048        }
1049    }
1050}
1051
1052//------------------------------------------------------------------------------
1053void __IOHIDPropertySaveToKeyWithSpecialKeys(CFDictionaryRef dictionary, CFStringRef key, CFStringRef *specialKeys, __IOHIDPropertyContext *context)
1054{
1055    CFMutableDictionaryRef temp = CFDictionaryCreateMutableCopy(NULL, 0, dictionary);
1056
1057    if (specialKeys) {
1058        while (*specialKeys) {
1059            CFTypeRef value = CFDictionaryGetValue(temp, *specialKeys);
1060            if (value) {
1061                CFStringRef subKey = CFStringCreateWithFormat(NULL, NULL,
1062                                                              CFSTR("%@#%@"),
1063                                                              key,
1064                                                              *specialKeys);
1065                __IOHIDPropertySaveWithContext(subKey, value, context);
1066                CFDictionaryRemoveValue(temp, *specialKeys);
1067                CFRelease(subKey);
1068            }
1069            specialKeys++;
1070        }
1071    }
1072
1073    CFAbsoluteTime time = CFAbsoluteTimeGetCurrent();
1074    CFNumberRef timeNum = CFNumberCreate(NULL, kCFNumberDoubleType, &time);
1075    CFDictionaryAddValue(temp, CFSTR("time of last save"), timeNum);
1076    CFRelease(timeNum);
1077    __IOHIDPropertySaveWithContext(key, temp, context);
1078    CFRelease(temp);
1079}
1080
1081//------------------------------------------------------------------------------
1082CFMutableDictionaryRef __IOHIDPropertyLoadDictionaryFromKey(CFStringRef key)
1083{
1084    CFMutableDictionaryRef result = NULL;
1085    CFDictionaryRef baseProperties = CFPreferencesCopyAppValue(key, kCFPreferencesCurrentApplication);
1086    if (baseProperties && (CFGetTypeID(baseProperties) == CFDictionaryGetTypeID())) {
1087        result = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1088
1089        CFDictionaryRef properties = CFPreferencesCopyValue(key, kCFPreferencesAnyApplication, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
1090        if (properties && (CFGetTypeID(properties) == CFDictionaryGetTypeID()))
1091            __IOHIDManagerMergeDictionaries(properties, result);
1092        if (properties)
1093            CFRelease(properties);
1094
1095        properties = CFPreferencesCopyValue(key, kCFPreferencesAnyApplication, kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
1096        if (properties && (CFGetTypeID(properties) == CFDictionaryGetTypeID()))
1097            __IOHIDManagerMergeDictionaries(properties, result);
1098        if (properties)
1099            CFRelease(properties);
1100
1101        properties = CFPreferencesCopyValue(key, kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
1102        if (properties && (CFGetTypeID(properties) == CFDictionaryGetTypeID()))
1103            __IOHIDManagerMergeDictionaries(properties, result);
1104        if (properties)
1105            CFRelease(properties);
1106
1107        properties = CFPreferencesCopyValue(key, kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
1108        if (properties && (CFGetTypeID(properties) == CFDictionaryGetTypeID()))
1109            __IOHIDManagerMergeDictionaries(properties, result);
1110        if (properties)
1111            CFRelease(properties);
1112
1113        properties = CFPreferencesCopyValue(key, kCFPreferencesCurrentApplication, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
1114        if (properties && (CFGetTypeID(properties) == CFDictionaryGetTypeID()))
1115            __IOHIDManagerMergeDictionaries(properties, result);
1116        if (properties)
1117            CFRelease(properties);
1118
1119        properties = CFPreferencesCopyValue(key, kCFPreferencesCurrentApplication, kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
1120        if (properties && (CFGetTypeID(properties) == CFDictionaryGetTypeID()))
1121            __IOHIDManagerMergeDictionaries(properties, result);
1122        if (properties)
1123            CFRelease(properties);
1124
1125        properties = CFPreferencesCopyValue(key, kCFPreferencesCurrentApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
1126        if (properties && (CFGetTypeID(properties) == CFDictionaryGetTypeID()))
1127            __IOHIDManagerMergeDictionaries(properties, result);
1128        if (properties)
1129            CFRelease(properties);
1130
1131        properties = CFPreferencesCopyValue(key, kCFPreferencesCurrentApplication, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
1132        if (properties && (CFGetTypeID(properties) == CFDictionaryGetTypeID()))
1133            __IOHIDManagerMergeDictionaries(properties, result);
1134        if (properties)
1135            CFRelease(properties);
1136
1137        __IOHIDManagerMergeDictionaries(baseProperties, result);
1138    }
1139    if (baseProperties)
1140        CFRelease(baseProperties);
1141    return result;
1142}
1143
1144//------------------------------------------------------------------------------
1145CFMutableDictionaryRef __IOHIDPropertyLoadFromKeyWithSpecialKeys(CFStringRef key, CFStringRef *specialKeys)
1146{
1147    CFMutableDictionaryRef result = __IOHIDPropertyLoadDictionaryFromKey(key);
1148
1149    if (!result)
1150        result = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1151
1152    while (*specialKeys) {
1153        CFStringRef subKey = CFStringCreateWithFormat(NULL, NULL,
1154                                                      CFSTR("%@#%@"),
1155                                                      key,
1156                                                      *specialKeys);
1157        CFPropertyListRef value = CFPreferencesCopyAppValue(subKey, kCFPreferencesCurrentApplication);
1158        if (value) {
1159            CFDictionarySetValue(result, *specialKeys, value);
1160            CFRelease(value);
1161        }
1162
1163        CFRelease(subKey);
1164        specialKeys++;
1165    }
1166
1167    return result;
1168}
1169
1170//===========================================================================
1171// Static Helper Definitions
1172//===========================================================================
1173//------------------------------------------------------------------------------
1174// __IOHIDManagerMergeDictionaries
1175//------------------------------------------------------------------------------
1176void __IOHIDManagerMergeDictionaries(CFDictionaryRef        srcDict,
1177                                     CFMutableDictionaryRef dstDict)
1178{
1179    uint32_t        count;
1180    CFTypeRef *     values;
1181    CFStringRef *   keys;
1182
1183    if ( !dstDict || !srcDict || !(count = CFDictionaryGetCount(srcDict)))
1184        return;
1185
1186    values  = (CFTypeRef *)malloc(sizeof(CFTypeRef) * count);
1187    keys    = (CFStringRef *)malloc(sizeof(CFStringRef) * count);
1188
1189    if ( values && keys ) {
1190        CFDictionaryGetKeysAndValues(srcDict, (const void **)keys, (const void **)values);
1191
1192        for ( uint32_t i=0; i<count; i++)
1193            CFDictionarySetValue(dstDict, keys[i], values[i]);
1194    }
1195
1196    if ( values )
1197        free(values);
1198
1199    if ( keys )
1200        free(keys);
1201}
1202
1203