IOHIDValue.c   [plain text]


/*
 * Copyright (c) 1999-2008 Apple Computer, Inc.  All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
 
#include <System/libkern/OSCrossEndian.h>
#include <CoreFoundation/CFRuntime.h>
#include <CoreFoundation/CFData.h>
#include <IOKit/hid/IOHIDValue.h>
#include <IOKit/hid/IOHIDElement.h>
#include <IOKit/hid/IOHIDLibUserClient.h>
#include <IOKit/hid/IOHIDLibPrivate.h>

#ifndef min
    #define min(a,b) (a<b?a:b)
#endif

static IOHIDValueRef __IOHIDValueCreatePrivate(CFAllocatorRef allocator, CFAllocatorContext * context __unused, size_t extraBytes);
static void __IOHIDValueRelease( CFTypeRef object );
static void __IOHIDValueConvertByteToWord(const UInt8 * src, uint32_t * dst, uint32_t bytesToCopy, Boolean signExtend);
static void __IOHIDValueConvertWordToByte(const uint32_t * src, UInt8 * dst, uint32_t bytesToCopy);
static void __IOHIDValueConvertByteToLongWord(const UInt8 * src, uint64_t * dst, uint64_t bytesToCopy, Boolean signExtend);
static void __IOHIDValueConvertLongWordToByte(const uint64_t * src, UInt8 * dst, uint64_t bytesToCopy);
typedef struct __IOHIDValue
{
    CFRuntimeBase               cfBase;   // base CFType information
    
    IOHIDElementRef             element;
    uint64_t                    timeStamp;
    uint32_t                    length;
    uint8_t *                   bytePtr;
    uint8_t                     bytes[];
} __IOHIDValue, *__IOHIDValueRef;

static const CFRuntimeClass __IOHIDValueClass = {
    0,                      // version
    "IOHIDValue",           // className
    NULL,                   // init
    NULL,                   // copy
    __IOHIDValueRelease,    // finalize
    NULL,                   // equal
    NULL,                   // hash
    NULL,                   // copyFormattingDesc
    NULL,
    NULL,
    NULL
};

static CFTypeID __kIOHIDValueTypeID = _kCFRuntimeNotATypeID;

CFTypeID IOHIDValueGetTypeID(void)
{
    /* initialize runtime */
    if ( __kIOHIDValueTypeID == _kCFRuntimeNotATypeID )
        __kIOHIDValueTypeID = _CFRuntimeRegisterClass(&__IOHIDValueClass);
        
    return __kIOHIDValueTypeID;
}

IOHIDValueRef __IOHIDValueCreatePrivate(CFAllocatorRef allocator, CFAllocatorContext * context __unused, size_t dataLength)
{
    IOHIDValueRef       event   = NULL;
    void *              offset  = NULL;
    uint32_t            size;
    
    /* allocate session */
    size  = sizeof(__IOHIDValue) - sizeof(CFRuntimeBase) + dataLength;
    event = (IOHIDValueRef)_CFRuntimeCreateInstance(allocator, IOHIDValueGetTypeID(), size, NULL);
    
    if (!event)
        return NULL;

    offset = event;
    bzero(offset + sizeof(CFRuntimeBase), size);
    
    return event;
}

void __IOHIDValueRelease( CFTypeRef object )
{
    IOHIDValueRef event = ( IOHIDValueRef ) object;

    if (event->element) CFRelease(event->element);
}

IOHIDValueRef _IOHIDValueCreateWithElementValuePtr(CFAllocatorRef allocator, IOHIDElementRef element, IOHIDElementValue * pElementValue)
{
    IOHIDValueRef   event   = NULL;
    uint32_t        length  = 0;

    if ( !element || !pElementValue )
        return NULL;
        
    length  = _IOHIDElementGetLength(element);
    event   = __IOHIDValueCreatePrivate(allocator, NULL, length);

    if (!event)
        return NULL;
        
    event->element      = (IOHIDElementRef)CFRetain(element);
    event->timeStamp    = *((uint64_t *)&(pElementValue->timestamp));
    event->length       = length;
    
    __IOHIDValueConvertWordToByte((const uint32_t *)&(pElementValue->value[0]), event->bytes, length);
    
    return event;
}

IOHIDValueRef _IOHIDValueCreateWithStruct(CFAllocatorRef allocator, IOHIDElementRef element, IOHIDEventStruct * pEventStruct)
{
    IOHIDValueRef   event       = NULL;
    Boolean         isLongValue = FALSE;
    uint32_t        length      = 0;

    if ( !element || !pEventStruct )
        return NULL;
        
    isLongValue = (pEventStruct->longValue && pEventStruct->longValueSize);
    length  = _IOHIDElementGetLength(element);
    event   = __IOHIDValueCreatePrivate(allocator, NULL, isLongValue ? 0 : length);

    if (!event)
        return NULL;
        
    event->element      = (IOHIDElementRef)CFRetain(element);
    event->timeStamp    = *((uint64_t *)&(pEventStruct->timestamp));
    event->length       = length;

    if ( isLongValue )
    {
        event->bytePtr  = pEventStruct->longValue;
    }
    else
        __IOHIDValueConvertWordToByte((const uint32_t *)&(pEventStruct->value), event->bytes, min(sizeof(uint32_t), event->length));

    return event;
}

IOHIDValueRef IOHIDValueCreateWithIntegerValue(CFAllocatorRef allocator, IOHIDElementRef element, uint64_t timeStamp, CFIndex value)
{
    IOHIDValueRef   event   = NULL;
    uint32_t        length  = 0;
    uint64_t        tempValue;

    if ( !element )
        return NULL;

    length  = _IOHIDElementGetLength(element);
    event   = __IOHIDValueCreatePrivate(allocator, NULL, length);

    if (!event)
        return NULL;
        
    event->element      = (IOHIDElementRef)CFRetain(element);
    event->timeStamp    = timeStamp;
    event->length       = length;
        
    tempValue = value;
    
    __IOHIDValueConvertLongWordToByte((const uint64_t *)&tempValue, event->bytes, min(length, sizeof(uint32_t)));
    
    return event;
}

IOHIDValueRef IOHIDValueCreateWithBytes(CFAllocatorRef allocator, IOHIDElementRef element, uint64_t timeStamp, const uint8_t * bytes, CFIndex byteLength)
{
    IOHIDValueRef   event   = NULL;
    CFIndex         length  = 0;

    if ( !element || !bytes || !byteLength )
        return NULL;

    length  = _IOHIDElementGetLength(element);
    event   = __IOHIDValueCreatePrivate(allocator, NULL, length);

    if (!event)
        return NULL;
        
    event->element      = (IOHIDElementRef)CFRetain(element);
    event->timeStamp    = timeStamp;
    event->length       = length;
    
    bcopy(bytes, event->bytes, min(length, byteLength));

    return event;
}

IOHIDValueRef IOHIDValueCreateWithBytesNoCopy(CFAllocatorRef allocator, IOHIDElementRef element, uint64_t timeStamp, const uint8_t * bytes, CFIndex length)
{
    IOHIDValueRef   event   = NULL;

    if ( !element || !bytes || !length )
        return NULL;

    event = __IOHIDValueCreatePrivate(allocator, NULL, 0);

    if (!event)
        return NULL;
        
    event->element      = (IOHIDElementRef)CFRetain(element);
    event->timeStamp    = timeStamp;
    event->length       = min(length, _IOHIDElementGetLength(element));
    event->bytePtr      = (uint8_t *)bytes;
    
    return event;
}


IOHIDElementRef IOHIDValueGetElement(IOHIDValueRef event)
{
    return event->element;
}

uint64_t IOHIDValueGetTimeStamp(IOHIDValueRef event)
{
    return event->timeStamp;
}

CFIndex IOHIDValueGetIntegerValue(IOHIDValueRef event)
{    
    uint64_t value = 0;
    IOHIDElementRef element = event->element;
    __IOHIDValueConvertByteToLongWord(IOHIDValueGetBytePtr(event), &value, event->length, IOHIDElementGetLogicalMin(element) < 0 || IOHIDElementGetLogicalMax(element) < 0);
    return (CFIndex)value;
}

double_t IOHIDValueGetScaledValue(IOHIDValueRef event, IOHIDValueScaleType type)
{
    IOHIDElementRef element         = event->element;
    CFIndex         logicalValue    = IOHIDValueGetIntegerValue(event);
    CFIndex         logicalMin      = IOHIDElementGetLogicalMin(element);
    CFIndex         logicalMax      = IOHIDElementGetLogicalMax(element);
    CFIndex         logicalRange    = 0;
    CFIndex         scaledMin       = 0;
    CFIndex         scaledMax       = 0;
    CFIndex         scaledRange     = 0;
    double_t        granularity     = 0.0;
    double_t        returnValue     = 0.0;

    if ( type == kIOHIDValueScaleTypeCalibrated ){
        IOHIDCalibrationInfo * calibrationInfo;

        calibrationInfo = _IOHIDElementGetCalibrationInfo(element);

        if ( calibrationInfo ) {
            if ( calibrationInfo->min != calibrationInfo->max ) {
                scaledMin = calibrationInfo->min;
                scaledMax = calibrationInfo->max;
            } else {
                scaledMin = -1;
                scaledMax = 1;
            }

            // check saturation first
            if ( calibrationInfo->satMin != calibrationInfo->satMax ) {
                if ( logicalValue <= calibrationInfo->satMin )
                    return scaledMin;
                if ( logicalValue >= calibrationInfo->satMax )
                    return scaledMax;

                logicalMin      = calibrationInfo->satMin;
                logicalMax      = calibrationInfo->satMax;
            }

           // now check the dead zone
           if (calibrationInfo->dzMin != calibrationInfo->dzMax) {
                double_t scaledMid = (scaledMin + scaledMax) / 2.0;
                if (logicalValue < calibrationInfo->dzMin) {
                    logicalMax = calibrationInfo->dzMin;
                    scaledMax = scaledMid;
                } else if ( logicalValue > calibrationInfo->dzMax) {
                    logicalMin = calibrationInfo->dzMax;
                    scaledMin = scaledMid;
                } else {
                    return scaledMid;
                }
            }

            granularity = calibrationInfo->gran;
        }
    } else { // kIOHIDValueScaleTypePhysical
        scaledMin = IOHIDElementGetPhysicalMin(element);
        scaledMax = IOHIDElementGetPhysicalMax(element);
    }

    logicalRange    = logicalMax - logicalMin;
    scaledRange     = scaledMax - scaledMin;
    returnValue     = ((double_t)(logicalValue - logicalMin) * (double_t)scaledRange / (double_t)logicalRange) + scaledMin;

    if ( granularity )
        returnValue = granularity * llround(returnValue / granularity);

    return returnValue;
}

CFIndex IOHIDValueGetLength(IOHIDValueRef event)
{    
    return event->length;
}

const uint8_t * IOHIDValueGetBytePtr(IOHIDValueRef event)
{    
    return event->bytePtr ? event->bytePtr : event->bytes;
}

void _IOHIDValueCopyToElementValuePtr(IOHIDValueRef value, IOHIDElementValue * pElementValue)
{
    IOHIDElementRef element = IOHIDValueGetElement(value);
    
    __IOHIDValueConvertByteToWord(IOHIDValueGetBytePtr(value), (uint32_t *)(pElementValue->value), value->length, IOHIDElementGetLogicalMin(element) < 0 || IOHIDElementGetLogicalMax(element) < 0);
}

#if defined (__LITTLE_ENDIAN__) 
    #define ON_INTEL 1
#else
    #define ON_INTEL 0
#endif

#define BIT_MASK(bits)  (((uint64_t)1 << (bits)) - 1)

void __IOHIDValueConvertByteToWord(const UInt8 * src, uint32_t * dst, uint32_t length, Boolean signExtend)
{
    bcopy(src, dst, length);
    
    if ( signExtend && length )
    {
        uint32_t lastOffset = length / sizeof(uint32_t);
        uint32_t wordBitsProcessed = length % sizeof(uint32_t);

        lastOffset += (wordBitsProcessed) ? 0:-1;
        wordBitsProcessed = wordBitsProcessed << 3;
    
        if (wordBitsProcessed && (dst[lastOffset] & (1<<(wordBitsProcessed-1))))
            dst[lastOffset] |= ~(BIT_MASK(wordBitsProcessed));
    }
}

void __IOHIDValueConvertWordToByte(const uint32_t * src, UInt8 * dst, uint32_t bytesToCopy)
{
    bcopy(src, dst, bytesToCopy);
}

void __IOHIDValueConvertByteToLongWord(const UInt8 * src, uint64_t * dst, uint64_t length, Boolean signExtend)
{    
    bcopy(src, dst, length);
    
    if ( signExtend && length )
    {
        uint32_t lastOffset = length / sizeof(uint64_t);
        uint32_t longWordBitsProcessed = length % sizeof(uint64_t);

        lastOffset += (longWordBitsProcessed) ? 0:-1;
        longWordBitsProcessed = longWordBitsProcessed << 3;
    
        if (longWordBitsProcessed && (dst[lastOffset] & (1<<(longWordBitsProcessed-1))))
            dst[lastOffset] |= ~(BIT_MASK(longWordBitsProcessed));
    }
}

void __IOHIDValueConvertLongWordToByte(const uint64_t * src, UInt8 * dst, uint64_t bytesToCopy)
{
    bcopy(src, dst, bytesToCopy);
}