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
24#include <CoreFoundation/CFRuntime.h>
25#include <CoreFoundation/CFArray.h>
26#include <IOKit/hid/IOHIDElement.h>
27#include <IOKit/hid/IOHIDLibUserClient.h>
28#include <IOKit/hid/IOHIDPrivateKeys.h>
29#include <IOKit/hid/IOHIDDevicePlugIn.h>
30#include <IOKit/hid/IOHIDLibPrivate.h>
31#include "IOHIDManagerPersistentProperties.h"
32
33static IOHIDElementRef      __IOHIDElementCreate(
34                                    CFAllocatorRef          allocator,
35                                    CFAllocatorContext *    context);
36static Boolean              __IOHIDElementEqual(
37                                    CFTypeRef               cf1,
38                                    CFTypeRef               cf2);
39static CFHashCode           __IOHIDElementHash(CFTypeRef cf);
40static void                 __IOHIDElementRelease(
41                                    CFTypeRef               object );
42static IOHIDElementStruct * __IOHIDElementGetElementStruct(
43                                    IOHIDElementRef         element);
44static void                 _IOHIDElementAttach(
45                                    IOHIDElementRef         element,
46                                    IOHIDElementRef         toAttach,
47                                    Boolean                 propagate);
48static void                 _IOHIDElementDetach(
49                                    IOHIDElementRef         element,
50                                    IOHIDElementRef         toAttach,
51                                    Boolean                 propagate);
52static void                 __IOHIDElementApplyCalibration(
53                                    IOHIDElementRef element);
54
55typedef struct __IOHIDElement
56{
57    CFRuntimeBase                   cfBase;   // base CFType information
58
59    IOHIDDeviceDeviceInterface**    deviceInterface;
60    IOHIDDeviceRef                  device;
61    IOHIDValueRef                   value;
62
63    IOHIDElementStruct *            elementStructPtr;
64    uint32_t                        index;
65    CFDataRef                       data;
66    CFMutableArrayRef               attachedElements;
67    CFArrayRef                      childElements;
68    IOHIDElementRef                 parentElement;
69    IOHIDElementRef                 originalElement;
70    IOHIDCalibrationInfo *          calibrationPtr;
71    CFMutableDictionaryRef          properties;
72    CFStringRef                     rootKey;
73    Boolean                         isDirty;
74} __IOHIDElement, *__IOHIDElementRef;
75
76static const CFRuntimeClass __IOHIDElementClass = {
77    0,                      // version
78    "IOHIDElement",         // className
79    NULL,                   // init
80    NULL,                   // copy
81    __IOHIDElementRelease,  // finalize
82    __IOHIDElementEqual,    // equal
83    __IOHIDElementHash,     // hash
84    NULL,                   // copyFormattingDesc
85    NULL,
86    NULL,
87    NULL
88};
89
90static CFTypeID __kIOHIDElementTypeID = _kCFRuntimeNotATypeID;
91static CFStringRef __KIOHIDElementSpecialKeys[] = {
92    CFSTR(kIOHIDElementCalibrationMinKey),
93    CFSTR(kIOHIDElementCalibrationMaxKey),
94    CFSTR(kIOHIDElementCalibrationSaturationMinKey),
95    CFSTR(kIOHIDElementCalibrationSaturationMaxKey),
96    CFSTR(kIOHIDElementCalibrationMaxKey),
97    CFSTR(kIOHIDElementCalibrationMaxKey),
98    CFSTR(kIOHIDElementCalibrationMaxKey),
99    NULL
100};
101
102
103
104//------------------------------------------------------------------------------
105// __IOHIDElementCreate
106//------------------------------------------------------------------------------
107IOHIDElementRef __IOHIDElementCreate(
108                                    CFAllocatorRef          allocator,
109                                    CFAllocatorContext *    context __unused)
110{
111    IOHIDElementRef     element = NULL;
112    void *              offset  = NULL;
113    uint32_t            size;
114
115    /* allocate session */
116    size  = sizeof(__IOHIDElement) - sizeof(CFRuntimeBase);
117    element = (IOHIDElementRef)_CFRuntimeCreateInstance(allocator, IOHIDElementGetTypeID(), size, NULL);
118
119    if (!element)
120        return NULL;
121
122    offset = element;
123    bzero(offset + sizeof(CFRuntimeBase), size);
124
125    return element;
126}
127
128//------------------------------------------------------------------------------
129// __IOHIDElementRelease
130//------------------------------------------------------------------------------
131void __IOHIDElementRelease( CFTypeRef object )
132{
133    IOHIDElementRef element = ( IOHIDElementRef ) object;
134
135    CFRELEASE_IF_NOT_NULL(element->attachedElements);
136    CFRELEASE_IF_NOT_NULL(element->childElements);
137    CFRELEASE_IF_NOT_NULL(element->parentElement);
138    CFRELEASE_IF_NOT_NULL(element->data);
139    CFRELEASE_IF_NOT_NULL(element->originalElement);
140    CFRELEASE_IF_NOT_NULL(element->properties);
141    CFRELEASE_IF_NOT_NULL(element->rootKey);
142
143    if (element->calibrationPtr)    free(element->calibrationPtr);
144    element->calibrationPtr = NULL;
145}
146
147//------------------------------------------------------------------------------
148// __IOHIDElementEqual
149//------------------------------------------------------------------------------
150Boolean __IOHIDElementEqual(CFTypeRef cf1, CFTypeRef cf2)
151{
152    if ((CFGetTypeID(cf1) != IOHIDElementGetTypeID()) ||
153        (CFGetTypeID(cf2) != IOHIDElementGetTypeID()) ||
154        (IOHIDElementGetCookie((IOHIDElementRef)cf1) != IOHIDElementGetCookie((IOHIDElementRef)cf2)))
155        return FALSE;
156
157    return TRUE;
158}
159
160//------------------------------------------------------------------------------
161// __IOHIDElementHash
162//------------------------------------------------------------------------------
163CFHashCode __IOHIDElementHash(CFTypeRef cf)
164{
165    if (CFGetTypeID(cf) == IOHIDElementGetTypeID())
166        return (CFHashCode)IOHIDElementGetCookie((IOHIDElementRef)cf);
167
168    return  0;
169}
170
171//------------------------------------------------------------------------------
172// __IOHIDElementGetElementStruct
173//------------------------------------------------------------------------------
174IOHIDElementStruct * __IOHIDElementGetElementStruct(IOHIDElementRef element)
175{
176    return element->elementStructPtr;
177}
178
179//------------------------------------------------------------------------------
180// IOHIDElementGetTypeID
181//------------------------------------------------------------------------------
182CFTypeID IOHIDElementGetTypeID(void)
183{
184    /* initialize runtime */
185    if ( __kIOHIDElementTypeID == _kCFRuntimeNotATypeID )
186        __kIOHIDElementTypeID = _CFRuntimeRegisterClass(&__IOHIDElementClass);
187
188    return __kIOHIDElementTypeID;
189}
190
191//------------------------------------------------------------------------------
192// _IOHIDElementCreateWithParentAndData
193//------------------------------------------------------------------------------
194IOHIDElementRef _IOHIDElementCreateWithParentAndData(
195                                        CFAllocatorRef          allocator,
196                                        IOHIDElementRef         parent,
197                                        CFDataRef               data,
198                                        IOHIDElementStruct *    elementStruct,
199                                        uint32_t                index)
200{
201    IOHIDElementRef element = NULL;
202
203    if (!elementStruct)
204        return NULL;
205
206    element = __IOHIDElementCreate(allocator, NULL);
207
208    if (!element)
209        return NULL;
210
211    element->data               = (CFDataRef)CFRetain(data);
212    element->elementStructPtr   = elementStruct;
213    element->index              = index;
214    element->parentElement      = (parent) ? (IOHIDElementRef)CFRetain(parent) : 0;
215
216    return element;
217}
218
219//------------------------------------------------------------------------------
220// _IOHIDElementCreateWithElement
221//------------------------------------------------------------------------------
222IOHIDElementRef _IOHIDElementCreateWithElement(
223                                        CFAllocatorRef          allocator,
224                                        IOHIDElementRef         original,
225                                        uint32_t                usagePage,
226                                        uint32_t                usage)
227{
228    IOHIDElementRef element = NULL;
229
230    if ( !original )
231        return NULL;
232
233    element = __IOHIDElementCreate(allocator, NULL);
234
235    if (!element)
236        return NULL;
237
238    element->index              = 0;
239    element->originalElement     = (IOHIDElementRef)CFRetain(original);
240
241    // Unlike normal IOHIDElements, this element does not reference IOHIDDeviceClass memory
242    element->data               = CFDataCreateMutable(allocator, sizeof(IOHIDElementStruct));
243    element->elementStructPtr   = (IOHIDElementStruct *)CFDataGetMutableBytePtr((CFMutableDataRef)element->data);
244
245    bcopy(__IOHIDElementGetElementStruct(original), element->elementStructPtr, sizeof(IOHIDElementStruct));
246
247   // To denote a mapped virual element, a simple approach would be to use
248    // the negative cookie value of the original
249    intptr_t cookie = (intptr_t)IOHIDElementGetCookie(original);
250    element->elementStructPtr->cookieMin            = -cookie;
251    element->elementStructPtr->cookieMax            = -cookie;
252
253    element->elementStructPtr->usagePage            = usagePage;
254    element->elementStructPtr->usageMin             = usage;
255    element->elementStructPtr->usageMax             = usage;
256    element->elementStructPtr->duplicateIndex       = 0;
257    element->elementStructPtr->duplicateValueSize   = 0;
258
259    return element;
260}
261
262//------------------------------------------------------------------------------
263// _IOHIDElementSetDevice
264//------------------------------------------------------------------------------
265void _IOHIDElementSetDevice(IOHIDElementRef element, IOHIDDeviceRef device)
266{
267    element->device = device;
268}
269
270//------------------------------------------------------------------------------
271// _IOHIDElementSetDeviceInterface
272//------------------------------------------------------------------------------
273void _IOHIDElementSetDeviceInterface(IOHIDElementRef element, IOHIDDeviceDeviceInterface ** interface)
274{
275    element->deviceInterface = interface;
276}
277
278//------------------------------------------------------------------------------
279// _IOHIDElementGetLength
280//------------------------------------------------------------------------------
281CFIndex _IOHIDElementGetLength(IOHIDElementRef element)
282{
283    CFIndex bits = element->elementStructPtr->size;
284
285    if ( element->elementStructPtr->duplicateValueSize && (element->index != 0))
286        bits = element->elementStructPtr->reportSize;
287
288    return (bits + 7) / 8;
289}
290
291//------------------------------------------------------------------------------
292// IOHIDElementGetCookie
293//------------------------------------------------------------------------------
294IOHIDElementCookie IOHIDElementGetCookie(IOHIDElementRef element)
295{
296    return (IOHIDElementCookie)
297                        (element->elementStructPtr->cookieMin + element->index);
298}
299
300//------------------------------------------------------------------------------
301// IOHIDElementGetType
302//------------------------------------------------------------------------------
303IOHIDElementType IOHIDElementGetType(IOHIDElementRef element)
304{
305    return element->elementStructPtr->type;
306}
307
308//------------------------------------------------------------------------------
309// IOHIDElementCreateWithDictionary
310//------------------------------------------------------------------------------
311IOHIDElementRef IOHIDElementCreateWithDictionary(
312                                        CFAllocatorRef          allocator,
313                                        CFDictionaryRef         dictionary)
314{
315    IOHIDElementRef element = NULL;
316
317    if ( !dictionary )
318        return NULL;
319
320    element = __IOHIDElementCreate(allocator, NULL);
321
322    if (!element)
323        return NULL;
324
325    element->index              = 0;
326
327    // Unlike normal IOHIDElements, this element
328    // does not reference IOHIDDeviceClass memory
329    element->data = CFDataCreateMutable( allocator, sizeof(IOHIDElementStruct));
330
331    if ( !element->data ) {
332        CFRelease(element);
333        return NULL;
334    }
335
336    element->elementStructPtr = (IOHIDElementStruct *)CFDataGetMutableBytePtr(
337                                            (CFMutableDataRef)element->data);
338
339    // fill in element->elementStructPtr
340
341    return element;
342}
343
344
345//------------------------------------------------------------------------------
346// IOHIDElementGetCollectionType
347//------------------------------------------------------------------------------
348IOHIDElementCollectionType IOHIDElementGetCollectionType(IOHIDElementRef element)
349{
350    return element->elementStructPtr->collectionType;
351}
352
353//------------------------------------------------------------------------------
354// IOHIDElementGetUsagePage
355//------------------------------------------------------------------------------
356uint32_t IOHIDElementGetUsagePage(IOHIDElementRef element)
357{
358    return element->elementStructPtr->usagePage;
359}
360
361//------------------------------------------------------------------------------
362// IOHIDElementGetUsagePage
363//------------------------------------------------------------------------------
364uint32_t IOHIDElementGetUsage(IOHIDElementRef element)
365{
366    return element->elementStructPtr->usageMin + ((element->elementStructPtr->usageMin != element->elementStructPtr->usageMax) ? element->index : 0);
367}
368
369//------------------------------------------------------------------------------
370// _IOHIDElementGetFlags
371//------------------------------------------------------------------------------
372uint32_t _IOHIDElementGetFlags(IOHIDElementRef element)
373{
374    return element->elementStructPtr->flags;
375}
376
377Boolean IOHIDElementIsVirtual(IOHIDElementRef element)
378{
379    return ( element->originalElement != NULL );
380}
381
382//------------------------------------------------------------------------------
383// IOHIDElementIsRelative
384//------------------------------------------------------------------------------
385Boolean IOHIDElementIsRelative(IOHIDElementRef element)
386{
387    return ((element->elementStructPtr->flags & kIOHIDElementFlagsRelativeMask) != 0);
388}
389
390//------------------------------------------------------------------------------
391// IOHIDElementIsWrapping
392//------------------------------------------------------------------------------
393Boolean IOHIDElementIsWrapping(IOHIDElementRef element)
394{
395    return ((element->elementStructPtr->flags & kIOHIDElementFlagsWrapMask) != 0);
396}
397
398//------------------------------------------------------------------------------
399// IOHIDElementIsArray
400//------------------------------------------------------------------------------
401Boolean IOHIDElementIsArray(IOHIDElementRef element)
402{
403    return ((element->elementStructPtr->flags & kIOHIDElementFlagsVariableMask) == 0);
404}
405
406//------------------------------------------------------------------------------
407// IOHIDElementIsNonLinear
408//------------------------------------------------------------------------------
409Boolean IOHIDElementIsNonLinear(IOHIDElementRef element)
410{
411    return ((element->elementStructPtr->flags & kIOHIDElementFlagsNonLinearMask) != 0);
412}
413
414//------------------------------------------------------------------------------
415// IOHIDElementHasPreferredState
416//------------------------------------------------------------------------------
417Boolean IOHIDElementHasPreferredState(IOHIDElementRef element)
418{
419    return ((element->elementStructPtr->flags & kIOHIDElementFlagsNoPreferredMask) == 0);
420}
421
422//------------------------------------------------------------------------------
423// IOHIDElementHasNullState
424//------------------------------------------------------------------------------
425Boolean IOHIDElementHasNullState(IOHIDElementRef element)
426{
427    return (element->elementStructPtr->flags & kIOHIDElementFlagsNullStateMask);
428}
429
430//------------------------------------------------------------------------------
431// IOHIDElementGetName
432//------------------------------------------------------------------------------
433CFStringRef IOHIDElementGetName(IOHIDElementRef element)
434{
435    CFTypeRef type = IOHIDElementGetProperty(   element,
436                                                CFSTR(kIOHIDElementNameKey));
437
438    return (type && (CFGetTypeID(type) == CFStringGetTypeID())) ?
439                                                (CFStringRef)type : NULL;
440}
441
442//------------------------------------------------------------------------------
443// IOHIDElementGetReportID
444//------------------------------------------------------------------------------
445uint32_t IOHIDElementGetReportID(IOHIDElementRef element)
446{
447    return element->elementStructPtr->reportID;
448}
449
450//------------------------------------------------------------------------------
451// IOHIDElementGetReportSize
452//------------------------------------------------------------------------------
453uint32_t IOHIDElementGetReportSize(IOHIDElementRef element)
454{
455    return element->elementStructPtr->reportSize;
456}
457
458//------------------------------------------------------------------------------
459// IOHIDElementGetReportCount
460//------------------------------------------------------------------------------
461uint32_t IOHIDElementGetReportCount(IOHIDElementRef element)
462{
463    uint32_t reportCount = element->elementStructPtr->reportCount;
464
465    if ( element->elementStructPtr->duplicateValueSize && (element->index != 0))
466        reportCount = 1;
467
468    return reportCount;
469}
470
471//------------------------------------------------------------------------------
472// IOHIDElementGetUnit
473//------------------------------------------------------------------------------
474uint32_t IOHIDElementGetUnit(IOHIDElementRef element)
475{
476    return element->elementStructPtr->unit;
477}
478
479//------------------------------------------------------------------------------
480// IOHIDElementGetUnitExponent
481//------------------------------------------------------------------------------
482uint32_t IOHIDElementGetUnitExponent(IOHIDElementRef element)
483{
484    return element->elementStructPtr->unitExponent;
485}
486
487//------------------------------------------------------------------------------
488// IOHIDElementGetLogicalMin
489//------------------------------------------------------------------------------
490CFIndex IOHIDElementGetLogicalMin(IOHIDElementRef element)
491{
492    return element->elementStructPtr->min;
493}
494
495//------------------------------------------------------------------------------
496// IOHIDElementGetLogicalMax
497//------------------------------------------------------------------------------
498CFIndex IOHIDElementGetLogicalMax(IOHIDElementRef element)
499{
500    return element->elementStructPtr->max;
501}
502
503//------------------------------------------------------------------------------
504// IOHIDElementGetPhysicalMin
505//------------------------------------------------------------------------------
506CFIndex IOHIDElementGetPhysicalMin(IOHIDElementRef element)
507{
508    return element->elementStructPtr->scaledMin;
509}
510
511//------------------------------------------------------------------------------
512// IOHIDElementGetPhysicalMax
513//------------------------------------------------------------------------------
514CFIndex IOHIDElementGetPhysicalMax(IOHIDElementRef element)
515{
516    return element->elementStructPtr->scaledMax;
517}
518
519//------------------------------------------------------------------------------
520// IOHIDElementGetDuplicateIndex
521//------------------------------------------------------------------------------
522uint32_t IOHIDElementGetDuplicateIndex(IOHIDElementRef element)
523{
524    uint32_t dupIndex = 0;
525
526    if ( element->elementStructPtr->duplicateValueSize && (element->index != 0))
527        dupIndex = element->index - 1;
528
529    return dupIndex;
530}
531
532//------------------------------------------------------------------------------
533// IOHIDElementGetDevice
534//------------------------------------------------------------------------------
535IOHIDDeviceRef IOHIDElementGetDevice(IOHIDElementRef element)
536{
537    return element->device;
538}
539
540//------------------------------------------------------------------------------
541// IOHIDElementGetParent
542//------------------------------------------------------------------------------
543IOHIDElementRef IOHIDElementGetParent(IOHIDElementRef element)
544{
545    if (!element->parentElement && element->deviceInterface) {
546        CFMutableDictionaryRef  matchingDict;
547        CFArrayRef              elementArray;
548
549        matchingDict = CFDictionaryCreateMutable(
550                                            CFGetAllocator(element),
551                                            1,
552                                            &kCFTypeDictionaryKeyCallBacks,
553                                            &kCFTypeDictionaryValueCallBacks);
554
555        if ( matchingDict ) {
556            uint32_t cookie = (uint32_t)element->elementStructPtr->parentCookie;
557            CFNumberRef cookieNumber = CFNumberCreate(
558                                            CFGetAllocator(element),
559                                            kCFNumberIntType,
560                                            &cookie);
561
562            CFDictionarySetValue(           matchingDict,
563                                            CFSTR(kIOHIDElementCookieKey),
564                                            cookieNumber);
565            CFRelease(cookieNumber);
566
567            (*(element->deviceInterface))->copyMatchingElements(
568                                            element->deviceInterface,
569                                            matchingDict,
570                                            &elementArray,
571                                            0);
572
573            if (elementArray) {
574                element->parentElement = (IOHIDElementRef)CFRetain(
575                                    CFArrayGetValueAtIndex(elementArray, 0));
576                CFRelease(elementArray);
577            }
578
579            CFRelease(matchingDict);
580        }
581    }
582
583    return element->parentElement;
584}
585
586//------------------------------------------------------------------------------
587// IOHIDElementGetChildren
588//------------------------------------------------------------------------------
589CFArrayRef IOHIDElementGetChildren(IOHIDElementRef element)
590{
591    CFArrayRef childrenArray = NULL;
592
593    if (!element->childElements && element->deviceInterface) {
594        CFMutableDictionaryRef matchingDict;
595
596        matchingDict = CFDictionaryCreateMutable(
597                                            CFGetAllocator(element),
598                                            1,
599                                            &kCFTypeDictionaryKeyCallBacks,
600                                            &kCFTypeDictionaryValueCallBacks);
601
602        if ( matchingDict ) {
603            uint32_t cookie = (uint32_t)IOHIDElementGetCookie(element);
604
605            CFNumberRef cookieNumber = CFNumberCreate(
606                                            CFGetAllocator(element),
607                                            kCFNumberIntType, &cookie);
608
609            CFDictionarySetValue(   matchingDict,
610                                    CFSTR(kIOHIDElementCollectionCookieKey),
611                                    cookieNumber);
612
613            CFRelease(cookieNumber);
614
615            (*(element->deviceInterface))->copyMatchingElements(
616                                            element->deviceInterface,
617                                            matchingDict, &childrenArray, 0);
618
619            if (childrenArray)
620                element->childElements = childrenArray;
621
622            CFRelease(matchingDict);
623        }
624    } else
625        childrenArray = element->childElements;
626
627    return childrenArray;
628}
629
630//------------------------------------------------------------------------------
631// IOHIDElementAttach
632//------------------------------------------------------------------------------
633void IOHIDElementAttach(IOHIDElementRef element, IOHIDElementRef toAttach)
634{
635    _IOHIDElementAttach(element, toAttach, TRUE);
636}
637
638//------------------------------------------------------------------------------
639// IOHIDElementDetach
640//------------------------------------------------------------------------------
641void IOHIDElementDetach(IOHIDElementRef element, IOHIDElementRef toDetach)
642{
643    _IOHIDElementDetach(element, toDetach, TRUE);
644}
645
646//------------------------------------------------------------------------------
647// _IOHIDElementAttach
648//------------------------------------------------------------------------------
649void _IOHIDElementAttach(IOHIDElementRef element, IOHIDElementRef toAttach, Boolean propagate)
650{
651    if ( !element->attachedElements )
652        element->attachedElements = CFArrayCreateMutable(
653                                        CFGetAllocator(element),
654                                        0,
655                                        &kCFTypeArrayCallBacks);
656
657    if ( !element->attachedElements )
658        return;
659
660    CFIndex index = CFArrayGetFirstIndexOfValue(
661                        element->attachedElements,
662                        CFRangeMake(0, CFArrayGetCount(element->attachedElements)),
663                        toAttach);
664
665    if ( index != kCFNotFound )
666        return;
667
668    CFArrayAppendValue(element->attachedElements, toAttach);
669
670    if ( propagate )
671        _IOHIDElementAttach(toAttach, element, FALSE);
672}
673
674//------------------------------------------------------------------------------
675// _IOHIDElementDetach
676//------------------------------------------------------------------------------
677void _IOHIDElementDetach(IOHIDElementRef element, IOHIDElementRef toDetach, Boolean propagate)
678{
679    if ( !element->attachedElements )
680        return;
681
682    CFIndex index = CFArrayGetFirstIndexOfValue(
683                        element->attachedElements,
684                        CFRangeMake(0, CFArrayGetCount(element->attachedElements)),
685                        toDetach);
686
687    if ( index == kCFNotFound )
688        return;
689
690    CFArrayRemoveValueAtIndex(element->attachedElements, index);
691
692    if ( propagate )
693        _IOHIDElementDetach(toDetach, element, FALSE);
694}
695
696//------------------------------------------------------------------------------
697// IOHIDElementCopyAttached
698//------------------------------------------------------------------------------
699CFArrayRef IOHIDElementCopyAttached(IOHIDElementRef element)
700{
701    return element->attachedElements ?
702            CFArrayCreateCopy(CFGetAllocator(element), element->attachedElements) :
703            NULL;
704}
705
706
707//------------------------------------------------------------------------------
708// _IOHIDElementGetValue
709//------------------------------------------------------------------------------
710IOHIDValueRef _IOHIDElementGetValue(IOHIDElementRef element)
711{
712    return element->value;
713}
714
715//------------------------------------------------------------------------------
716// _IOHIDElementSetValue
717//------------------------------------------------------------------------------
718void _IOHIDElementSetValue(IOHIDElementRef element, IOHIDValueRef value)
719{
720    if (element->value)
721        CFRelease(element->value);
722
723    element->value = value ? (IOHIDValueRef)CFRetain(value) : NULL;
724}
725
726//------------------------------------------------------------------------------
727// _IOHIDElementGetCalibrationInfo
728//------------------------------------------------------------------------------
729IOHIDCalibrationInfo * _IOHIDElementGetCalibrationInfo(IOHIDElementRef element)
730{
731    return element->calibrationPtr;
732}
733
734//------------------------------------------------------------------------------
735// IOHIDElementGetProperty
736//------------------------------------------------------------------------------
737CFTypeRef IOHIDElementGetProperty(IOHIDElementRef element, CFStringRef key)
738{
739    if ( !element->properties )
740        return NULL;
741
742    return CFDictionaryGetValue(element->properties, key);
743}
744
745//------------------------------------------------------------------------------
746// IOHIDElementSetProperty
747//------------------------------------------------------------------------------
748CF_EXPORT
749Boolean IOHIDElementSetProperty(            IOHIDElementRef         element,
750                                            CFStringRef             key,
751                                            CFTypeRef               property)
752{
753    if ( !element->properties ) {
754        element->properties = CFDictionaryCreateMutable(CFGetAllocator(element),
755                                                        0,
756                                                        &kCFTypeDictionaryKeyCallBacks,
757                                                        &kCFTypeDictionaryValueCallBacks);
758
759        if ( !element->properties )
760            return FALSE;
761    }
762
763    boolean_t   isCalMin = FALSE;
764    boolean_t   isCalMax = FALSE;
765    boolean_t   isSatMin = FALSE;
766    boolean_t   isSatMax = FALSE;
767    boolean_t   isDZMin  = FALSE;
768    boolean_t   isDZMax  = FALSE;
769    boolean_t   isGran   = FALSE;
770
771    element->isDirty = TRUE;
772
773    if ((CFGetTypeID(property) == CFNumberGetTypeID()) && (
774        (isCalMin   = CFEqual(key, CFSTR(kIOHIDElementCalibrationMinKey))) ||
775        (isCalMax   = CFEqual(key, CFSTR(kIOHIDElementCalibrationMaxKey))) ||
776        (isSatMin   = CFEqual(key, CFSTR(kIOHIDElementCalibrationSaturationMinKey))) ||
777        (isSatMax   = CFEqual(key, CFSTR(kIOHIDElementCalibrationSaturationMaxKey))) ||
778        (isDZMin    = CFEqual(key, CFSTR(kIOHIDElementCalibrationDeadZoneMinKey))) ||
779        (isDZMax    = CFEqual(key, CFSTR(kIOHIDElementCalibrationDeadZoneMaxKey))) ||
780        (isGran     = CFEqual(key, CFSTR(kIOHIDElementCalibrationGranularityKey))))) {
781
782        if ( !element->calibrationPtr ) {
783            element->calibrationPtr =
784            (IOHIDCalibrationInfo *)malloc(sizeof(IOHIDCalibrationInfo));
785
786            bzero(element->calibrationPtr, sizeof(IOHIDCalibrationInfo));
787        }
788
789        if ( element->calibrationPtr ) {
790
791            CFIndex value = 0;
792            CFNumberGetValue(property, kCFNumberCFIndexType, &value);
793
794            if ( isCalMin )
795                element->calibrationPtr->min = value;
796            else if ( isCalMax )
797                element->calibrationPtr->max = value;
798            else if ( isSatMin )
799                element->calibrationPtr->satMin = value;
800            else if ( isSatMax )
801                element->calibrationPtr->satMax = value;
802            else if ( isDZMin )
803                element->calibrationPtr->dzMin  = value;
804            else if ( isDZMax )
805                element->calibrationPtr->dzMax  = value;
806            else if ( isGran )
807                CFNumberGetValue(property, kCFNumberFloat64Type, &element->calibrationPtr->gran);
808        }
809
810    }
811
812    CFDictionarySetValue(element->properties, key, property);
813
814    return TRUE;
815}
816
817//------------------------------------------------------------------------------
818CFStringRef __IOHIDElementGetRootKey(IOHIDElementRef element)
819{
820    if (!element->rootKey) {
821        // Device Root Key
822        // All *required* matching information
823        CFStringRef device = __IOHIDDeviceGetUUIDKey(element->device);
824        long int usagePage = (long int)IOHIDElementGetUsagePage(element);
825        long int usage = (long int)IOHIDElementGetUsage(element);
826        long int cookie = (long int)IOHIDElementGetCookie(element);
827        long int type = (long int)IOHIDElementGetType(element);
828
829        element->rootKey = CFStringCreateWithFormat(NULL, NULL,
830                                                    CFSTR("%@#%04lx#%04lx#%016lx#%ld"),
831                                                    device,
832                                                    usagePage,
833                                                    usage,
834                                                    cookie,
835                                                    type);
836    }
837
838    return element->rootKey;
839}
840
841//------------------------------------------------------------------------------
842void __IOHIDElementSaveProperties(IOHIDElementRef element, __IOHIDPropertyContext *context)
843{
844    if (element->isDirty && element->properties) {
845        __IOHIDPropertySaveToKeyWithSpecialKeys(element->properties, __IOHIDElementGetRootKey(element), __KIOHIDElementSpecialKeys, context);
846        element->isDirty = FALSE;
847    }
848}
849
850//------------------------------------------------------------------------------
851void __IOHIDElementLoadProperties(IOHIDElementRef element)
852{
853    CFMutableDictionaryRef properties = __IOHIDPropertyLoadFromKeyWithSpecialKeys(__IOHIDElementGetRootKey(element), __KIOHIDElementSpecialKeys);
854
855    if (properties) {
856        CFRELEASE_IF_NOT_NULL(element->properties);
857        element->properties = properties;
858        __IOHIDElementApplyCalibration(element);
859        element->isDirty = FALSE;
860    }
861}
862
863//------------------------------------------------------------------------------
864void __IOHIDElementApplyCalibration(IOHIDElementRef element)
865{
866    if (element->properties) {
867        CFNumberRef property;
868
869        property = CFDictionaryGetValue(element->properties, CFSTR(kIOHIDElementCalibrationMinKey));
870        if (property && (CFGetTypeID(property) == CFNumberGetTypeID())) {
871            CFNumberGetValue(property, kCFNumberCFIndexType, &element->calibrationPtr->min);
872        }
873
874        property = CFDictionaryGetValue(element->properties, CFSTR(kIOHIDElementCalibrationMaxKey));
875        if (property && (CFGetTypeID(property) == CFNumberGetTypeID())) {
876            CFNumberGetValue(property, kCFNumberCFIndexType, &element->calibrationPtr->max);
877        }
878
879        property = CFDictionaryGetValue(element->properties, CFSTR(kIOHIDElementCalibrationSaturationMinKey));
880        if (property && (CFGetTypeID(property) == CFNumberGetTypeID())) {
881            CFNumberGetValue(property, kCFNumberCFIndexType, &element->calibrationPtr->satMin);
882        }
883
884        property = CFDictionaryGetValue(element->properties, CFSTR(kIOHIDElementCalibrationSaturationMaxKey));
885        if (property && (CFGetTypeID(property) == CFNumberGetTypeID())) {
886            CFNumberGetValue(property, kCFNumberCFIndexType, &element->calibrationPtr->satMax);
887        }
888
889        property = CFDictionaryGetValue(element->properties, CFSTR(kIOHIDElementCalibrationDeadZoneMinKey));
890        if (property && (CFGetTypeID(property) == CFNumberGetTypeID())) {
891            CFNumberGetValue(property, kCFNumberCFIndexType, &element->calibrationPtr->dzMin);
892        }
893
894        property = CFDictionaryGetValue(element->properties, CFSTR(kIOHIDElementCalibrationDeadZoneMaxKey));
895        if (property && (CFGetTypeID(property) == CFNumberGetTypeID())) {
896            CFNumberGetValue(property, kCFNumberCFIndexType, &element->calibrationPtr->dzMax);
897        }
898
899        property = CFDictionaryGetValue(element->properties, CFSTR(kIOHIDElementCalibrationGranularityKey));
900        if (property && (CFGetTypeID(property) == CFNumberGetTypeID())) {
901            CFNumberGetValue(property, kCFNumberFloat64Type, &element->calibrationPtr->gran);
902        }
903    }
904}
905
906//------------------------------------------------------------------------------
907void __IOHIDSaveElementSet(const void *value, void *context) {
908    IOHIDElementRef element = (IOHIDElementRef)value;
909    if (element)
910        __IOHIDElementSaveProperties(element, (__IOHIDPropertyContext*)context);
911}
912
913//------------------------------------------------------------------------------
914void __IOHIDLoadElementSet(const void *value, void *context __unused) {
915    IOHIDElementRef element = (IOHIDElementRef)value;
916    if (element)
917        __IOHIDElementLoadProperties(element);
918}
919
920//------------------------------------------------------------------------------
921