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 <System/libkern/OSCrossEndian.h>
26#include <CoreFoundation/CFRuntime.h>
27#include <CoreFoundation/CFData.h>
28#include <IOKit/hid/IOHIDValue.h>
29#include <IOKit/hid/IOHIDElement.h>
30#include <IOKit/hid/IOHIDLibUserClient.h>
31#include <IOKit/hid/IOHIDLibPrivate.h>
32
33#ifndef min
34    #define min(a,b) (a<b?a:b)
35#endif
36
37static IOHIDValueRef __IOHIDValueCreatePrivate(CFAllocatorRef allocator, CFAllocatorContext * context __unused, size_t extraBytes);
38static void __IOHIDValueRelease( CFTypeRef object );
39static void __IOHIDValueConvertByteToWord(const UInt8 * src, uint32_t * dst, uint32_t bytesToCopy, Boolean signExtend);
40static void __IOHIDValueConvertWordToByte(const uint32_t * src, UInt8 * dst, uint32_t bytesToCopy);
41static void __IOHIDValueConvertByteToLongWord(const UInt8 * src, uint64_t * dst, uint64_t bytesToCopy, Boolean signExtend);
42static void __IOHIDValueConvertLongWordToByte(const uint64_t * src, UInt8 * dst, uint64_t bytesToCopy);
43typedef struct __IOHIDValue
44{
45    CFRuntimeBase               cfBase;   // base CFType information
46
47    IOHIDElementRef             element;
48    uint64_t                    timeStamp;
49    uint32_t                    length;
50    uint8_t *                   bytePtr;
51    uint8_t                     bytes[];
52} __IOHIDValue, *__IOHIDValueRef;
53
54static const CFRuntimeClass __IOHIDValueClass = {
55    0,                      // version
56    "IOHIDValue",           // className
57    NULL,                   // init
58    NULL,                   // copy
59    __IOHIDValueRelease,    // finalize
60    NULL,                   // equal
61    NULL,                   // hash
62    NULL,                   // copyFormattingDesc
63    NULL,
64    NULL,
65    NULL
66};
67
68static CFTypeID         __valueTypeID   = _kCFRuntimeNotATypeID;
69static pthread_once_t   __valueTypeInit = PTHREAD_ONCE_INIT;
70
71void __IOHIDValueRegister(void)
72{
73    __valueTypeID = _CFRuntimeRegisterClass(&__IOHIDValueClass);
74}
75
76CFTypeID IOHIDValueGetTypeID(void)
77{
78    /* initialize runtime */
79    if ( __valueTypeID == _kCFRuntimeNotATypeID )
80        pthread_once(&__valueTypeInit, __IOHIDValueRegister);
81
82    return __valueTypeID;
83}
84
85IOHIDValueRef __IOHIDValueCreatePrivate(CFAllocatorRef allocator, CFAllocatorContext * context __unused, size_t dataLength)
86{
87    IOHIDValueRef       event   = NULL;
88    void *              offset  = NULL;
89    uint32_t            size;
90
91    /* allocate session */
92    size  = sizeof(__IOHIDValue) - sizeof(CFRuntimeBase) + dataLength;
93    event = (IOHIDValueRef)_CFRuntimeCreateInstance(allocator, IOHIDValueGetTypeID(), size, NULL);
94
95    if (!event)
96        return NULL;
97
98    offset = event;
99    bzero(offset + sizeof(CFRuntimeBase), size);
100
101    return event;
102}
103
104void __IOHIDValueRelease( CFTypeRef object )
105{
106    IOHIDValueRef event = ( IOHIDValueRef ) object;
107
108    if (event->element) CFRelease(event->element);
109}
110
111IOHIDValueRef _IOHIDValueCreateWithElementValuePtr(CFAllocatorRef allocator, IOHIDElementRef element, IOHIDElementValue * pElementValue)
112{
113    IOHIDValueRef   event   = NULL;
114    uint32_t        length  = 0;
115
116    if ( !element || !pElementValue )
117        return NULL;
118
119    length  = _IOHIDElementGetLength(element);
120    event   = __IOHIDValueCreatePrivate(allocator, NULL, length);
121
122    if (!event)
123        return NULL;
124
125    event->element      = (IOHIDElementRef)CFRetain(element);
126    event->timeStamp    = *((uint64_t *)&(pElementValue->timestamp));
127    event->length       = length;
128
129    __IOHIDValueConvertWordToByte((const uint32_t *)&(pElementValue->value[0]), event->bytes, length);
130
131    return event;
132}
133
134IOHIDValueRef _IOHIDValueCreateWithStruct(CFAllocatorRef allocator, IOHIDElementRef element, IOHIDEventStruct * pEventStruct)
135{
136    IOHIDValueRef   event       = NULL;
137    Boolean         isLongValue = FALSE;
138    uint32_t        length      = 0;
139
140    if ( !element || !pEventStruct )
141        return NULL;
142
143    isLongValue = (pEventStruct->longValue && pEventStruct->longValueSize);
144    length  = _IOHIDElementGetLength(element);
145    event   = __IOHIDValueCreatePrivate(allocator, NULL, isLongValue ? 0 : length);
146
147    if (!event)
148        return NULL;
149
150    event->element      = (IOHIDElementRef)CFRetain(element);
151    event->timeStamp    = *((uint64_t *)&(pEventStruct->timestamp));
152    event->length       = length;
153
154    if ( isLongValue )
155    {
156        event->bytePtr  = pEventStruct->longValue;
157    }
158    else
159        __IOHIDValueConvertWordToByte((const uint32_t *)&(pEventStruct->value), event->bytes, min(sizeof(uint32_t), event->length));
160
161    return event;
162}
163
164IOHIDValueRef IOHIDValueCreateWithIntegerValue(CFAllocatorRef allocator, IOHIDElementRef element, uint64_t timeStamp, CFIndex value)
165{
166    IOHIDValueRef   event   = NULL;
167    uint32_t        length  = 0;
168    uint64_t        tempValue;
169
170    if ( !element )
171        return NULL;
172
173    length  = _IOHIDElementGetLength(element);
174    event   = __IOHIDValueCreatePrivate(allocator, NULL, length);
175
176    if (!event)
177        return NULL;
178
179    event->element      = (IOHIDElementRef)CFRetain(element);
180    event->timeStamp    = timeStamp;
181    event->length       = length;
182
183    tempValue = value;
184
185    __IOHIDValueConvertLongWordToByte((const uint64_t *)&tempValue, event->bytes, min(length, sizeof(uint32_t)));
186
187    return event;
188}
189
190IOHIDValueRef IOHIDValueCreateWithBytes(CFAllocatorRef allocator, IOHIDElementRef element, uint64_t timeStamp, const uint8_t * bytes, CFIndex byteLength)
191{
192    IOHIDValueRef   event   = NULL;
193    CFIndex         length  = 0;
194
195    if ( !element || !bytes || !byteLength )
196        return NULL;
197
198    length  = _IOHIDElementGetLength(element);
199    event   = __IOHIDValueCreatePrivate(allocator, NULL, length);
200
201    if (!event)
202        return NULL;
203
204    event->element      = (IOHIDElementRef)CFRetain(element);
205    event->timeStamp    = timeStamp;
206    event->length       = length;
207
208    bcopy(bytes, event->bytes, min(length, byteLength));
209
210    return event;
211}
212
213IOHIDValueRef IOHIDValueCreateWithBytesNoCopy(CFAllocatorRef allocator, IOHIDElementRef element, uint64_t timeStamp, const uint8_t * bytes, CFIndex length)
214{
215    IOHIDValueRef   event   = NULL;
216
217    if ( !element || !bytes || !length )
218        return NULL;
219
220    event = __IOHIDValueCreatePrivate(allocator, NULL, 0);
221
222    if (!event)
223        return NULL;
224
225    event->element      = (IOHIDElementRef)CFRetain(element);
226    event->timeStamp    = timeStamp;
227    event->length       = min(length, _IOHIDElementGetLength(element));
228    event->bytePtr      = (uint8_t *)bytes;
229
230    return event;
231}
232
233
234IOHIDElementRef IOHIDValueGetElement(IOHIDValueRef event)
235{
236    return event->element;
237}
238
239uint64_t IOHIDValueGetTimeStamp(IOHIDValueRef event)
240{
241    return event->timeStamp;
242}
243
244CFIndex IOHIDValueGetIntegerValue(IOHIDValueRef event)
245{
246    uint64_t value = 0;
247    IOHIDElementRef element = event->element;
248    __IOHIDValueConvertByteToLongWord(IOHIDValueGetBytePtr(event), &value, event->length, IOHIDElementGetLogicalMin(element) < 0 || IOHIDElementGetLogicalMax(element) < 0);
249    return (CFIndex)value;
250}
251
252double_t IOHIDValueGetScaledValue(IOHIDValueRef event, IOHIDValueScaleType type)
253{
254    IOHIDElementRef element         = event->element;
255    CFIndex         logicalValue    = IOHIDValueGetIntegerValue(event);
256    CFIndex         logicalMin      = IOHIDElementGetLogicalMin(element);
257    CFIndex         logicalMax      = IOHIDElementGetLogicalMax(element);
258    CFIndex         logicalRange    = 0;
259    CFIndex         scaledMin       = 0;
260    CFIndex         scaledMax       = 0;
261    CFIndex         scaledRange     = 0;
262    double_t        granularity     = 0.0;
263    double_t        returnValue     = 0.0;
264
265    if ( type == kIOHIDValueScaleTypeCalibrated ){
266        IOHIDCalibrationInfo * calibrationInfo;
267
268        calibrationInfo = _IOHIDElementGetCalibrationInfo(element);
269
270        if ( calibrationInfo ) {
271            if ( calibrationInfo->min != calibrationInfo->max ) {
272                scaledMin = calibrationInfo->min;
273                scaledMax = calibrationInfo->max;
274            } else {
275                scaledMin = -1;
276                scaledMax = 1;
277            }
278
279            // check saturation first
280            if ( calibrationInfo->satMin != calibrationInfo->satMax ) {
281                if ( logicalValue <= calibrationInfo->satMin )
282                    return scaledMin;
283                if ( logicalValue >= calibrationInfo->satMax )
284                    return scaledMax;
285
286                logicalMin      = calibrationInfo->satMin;
287                logicalMax      = calibrationInfo->satMax;
288            }
289
290           // now check the dead zone
291           if (calibrationInfo->dzMin != calibrationInfo->dzMax) {
292                double_t scaledMid = scaledMin + ((scaledMax - scaledMin) / 2.0);
293                if (logicalValue < calibrationInfo->dzMin) {
294                    logicalMax = calibrationInfo->dzMin;
295                    scaledMax = scaledMid;
296                } else if ( logicalValue > calibrationInfo->dzMax) {
297                    logicalMin = calibrationInfo->dzMax;
298                    scaledMin = scaledMid;
299                } else {
300                    return scaledMid;
301                }
302            }
303
304            granularity = calibrationInfo->gran;
305        }
306    } else { // kIOHIDValueScaleTypePhysical
307        scaledMin = IOHIDElementGetPhysicalMin(element);
308        scaledMax = IOHIDElementGetPhysicalMax(element);
309    }
310
311    logicalRange    = logicalMax - logicalMin;
312    scaledRange     = scaledMax - scaledMin;
313    returnValue     = ((double_t)(logicalValue - logicalMin) * (double_t)scaledRange / (double_t)logicalRange) + scaledMin;
314
315    if ( granularity )
316        returnValue = granularity * llround(returnValue / granularity);
317
318    return returnValue;
319}
320
321CFIndex IOHIDValueGetLength(IOHIDValueRef event)
322{
323    return event->length;
324}
325
326const uint8_t * IOHIDValueGetBytePtr(IOHIDValueRef event)
327{
328    return event->bytePtr ? event->bytePtr : event->bytes;
329}
330
331void _IOHIDValueCopyToElementValuePtr(IOHIDValueRef value, IOHIDElementValue * pElementValue)
332{
333    IOHIDElementRef element = IOHIDValueGetElement(value);
334
335    __IOHIDValueConvertByteToWord(IOHIDValueGetBytePtr(value), (uint32_t *)(pElementValue->value), value->length, IOHIDElementGetLogicalMin(element) < 0 || IOHIDElementGetLogicalMax(element) < 0);
336}
337
338#if defined (__LITTLE_ENDIAN__)
339    #define ON_INTEL 1
340#else
341    #define ON_INTEL 0
342#endif
343
344#define BIT_MASK(bits)  (((uint64_t)1 << (bits)) - 1)
345
346void __IOHIDValueConvertByteToWord(const UInt8 * src, uint32_t * dst, uint32_t length, Boolean signExtend)
347{
348    bcopy(src, dst, length);
349
350    if ( signExtend && length )
351    {
352        uint32_t lastOffset = length / sizeof(uint32_t);
353        uint32_t wordBitsProcessed = length % sizeof(uint32_t);
354
355        lastOffset += (wordBitsProcessed) ? 0:-1;
356        wordBitsProcessed = wordBitsProcessed << 3;
357
358        if (wordBitsProcessed && (dst[lastOffset] & (1<<(wordBitsProcessed-1))))
359            dst[lastOffset] |= ~(BIT_MASK(wordBitsProcessed));
360    }
361}
362
363void __IOHIDValueConvertWordToByte(const uint32_t * src, UInt8 * dst, uint32_t bytesToCopy)
364{
365    bcopy(src, dst, bytesToCopy);
366}
367
368void __IOHIDValueConvertByteToLongWord(const UInt8 * src, uint64_t * dst, uint64_t length, Boolean signExtend)
369{
370    bcopy(src, dst, length);
371
372    if ( signExtend && length )
373    {
374        uint32_t lastOffset = length / sizeof(uint64_t);
375        uint32_t longWordBitsProcessed = length % sizeof(uint64_t);
376
377        lastOffset += (longWordBitsProcessed) ? 0:-1;
378        longWordBitsProcessed = longWordBitsProcessed << 3;
379
380        if (longWordBitsProcessed && (dst[lastOffset] & (1<<(longWordBitsProcessed-1))))
381            dst[lastOffset] |= ~(BIT_MASK(longWordBitsProcessed));
382    }
383}
384
385void __IOHIDValueConvertLongWordToByte(const uint64_t * src, UInt8 * dst, uint64_t bytesToCopy)
386{
387    bcopy(src, dst, bytesToCopy);
388}
389