#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, Boolean toKernel);
static void __IOHIDValueConvertWordToByte(const uint32_t * src, UInt8 * dst, uint32_t bytesToCopy, Boolean fromKernel);
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;
IOHIDElementRef element;
uint64_t timeStamp;
uint32_t length;
uint8_t * bytePtr;
uint8_t bytes[];
} __IOHIDValue, *__IOHIDValueRef;
static const CFRuntimeClass __IOHIDValueClass = {
0, "IOHIDValue", NULL, NULL, __IOHIDValueRelease, NULL, NULL, NULL, NULL
};
static CFTypeID __kIOHIDValueTypeID = _kCFRuntimeNotATypeID;
CFTypeID IOHIDValueGetTypeID(void)
{
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;
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;
ROSETTA_ONLY(
event->timeStamp = OSSwapInt64(event->timeStamp);
);
__IOHIDValueConvertWordToByte((const uint32_t *)&(pElementValue->value[0]), event->bytes, length, TRUE);
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), FALSE);
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;
}
if ( calibrationInfo->satMin != calibrationInfo->satMax ) {
if ( logicalValue <= calibrationInfo->satMin )
return scaledMin;
if ( logicalValue >= calibrationInfo->satMax )
return scaledMax;
logicalMin = calibrationInfo->satMin;
logicalMax = calibrationInfo->satMax;
}
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 { 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, TRUE);
}
#if defined (__i386__) || defined(__x86_64__)
#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, Boolean toKernel)
{
if ( ON_INTEL || (toKernel && _OSRosettaCheck()) )
{
bcopy(src, dst, length);
}
else
{
uint32_t srcOffset = 0;
uint32_t dstOffset = 0;
uint32_t temp = 0;
uint32_t tempShift = 0;
uint32_t bytesToCopy = length;
while ( bytesToCopy >= 4 )
{
dst[dstOffset] = OSSwapInt32(*((UInt32 *)&(src[srcOffset])));
srcOffset += 4;
bytesToCopy -= 4;
dstOffset ++;
}
while ( bytesToCopy )
{
temp |= src[srcOffset++] << tempShift;
tempShift += 8;
bytesToCopy --;
if ( !bytesToCopy )
dst[dstOffset] = temp;
}
}
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, Boolean fromKernel)
{
if ( ON_INTEL || (fromKernel && _OSRosettaCheck()) )
{
bcopy(src, dst, bytesToCopy);
}
else
{
uint32_t dstOffset = 0;
uint32_t srcOffset = 0;
uint32_t temp = 0;
uint32_t tmpOffset = 0;
while ( bytesToCopy )
{
temp = OSSwapInt32(src[srcOffset++]);
if ( bytesToCopy >= 4 )
{
*((UInt32 *)&(dst[dstOffset])) = temp;
bytesToCopy -= 4;
dstOffset += 4;
}
else
{
tmpOffset = 0;
while ( bytesToCopy )
{
dst[dstOffset++] = ((UInt8 *)&temp)[tmpOffset++];
bytesToCopy--;
}
}
}
}
}
void __IOHIDValueConvertByteToLongWord(const UInt8 * src, uint64_t * dst, uint64_t length, Boolean signExtend)
{
if ( ON_INTEL )
{
bcopy(src, dst, length);
}
else
{
uint64_t srcOffset = 0;
uint64_t dstOffset = 0;
uint64_t temp = 0;
uint64_t tempShift = 0;
uint64_t bytesToCopy= length;
while ( bytesToCopy >= sizeof(uint64_t) )
{
dst[dstOffset] = OSSwapInt64(*((uint64_t *)&(src[srcOffset])));
srcOffset += sizeof(uint64_t);
bytesToCopy -= sizeof(uint64_t);
dstOffset ++;
}
while ( bytesToCopy )
{
temp |= src[srcOffset++] << tempShift;
tempShift += 8;
bytesToCopy --;
if ( !bytesToCopy )
dst[dstOffset] = temp;
}
}
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)
{
if ( ON_INTEL )
{
bcopy(src, dst, bytesToCopy);
}
else
{
uint64_t dstOffset = 0;
uint64_t srcOffset = 0;
uint64_t temp = 0;
uint64_t tmpOffset = 0;
while ( bytesToCopy )
{
temp = OSSwapInt64(src[srcOffset++]);
if ( bytesToCopy >= sizeof(uint64_t) )
{
*((uint64_t *)&(dst[dstOffset])) = temp;
bytesToCopy -= sizeof(uint64_t);
dstOffset += sizeof(uint64_t);
}
else
{
tmpOffset = 0;
while ( bytesToCopy )
{
dst[dstOffset++] = ((uint8_t *)&temp)[tmpOffset++];
bytesToCopy--;
}
}
}
}
}