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