IOHIDDeviceClass.cpp [plain text]
#define CFRUNLOOP_NEW_API 1
#include <CoreFoundation/CFMachPort.h>
#include <CoreFoundation/CFPriv.h>
#include "IOHIDDeviceClass.h"
#include "IOHIDQueueClass.h"
#include "IOHIDOutputTransactionClass.h"
#include "IOHIDLibUserClient.h"
#include "IOHIDPrivateKeys.h"
__BEGIN_DECLS
#include <mach/mach.h>
#include <mach/mach_interface.h>
#include <IOKit/iokitmig.h>
#include <IOKit/IOMessage.h>
#include <System/libkern/OSCrossEndian.h>
__END_DECLS
#define connectCheck() do { \
if (!fConnection) \
return kIOReturnNoDevice; \
} while (0)
#define openCheck() do { \
if (!fIsOpen) \
return kIOReturnNotOpen; \
} while (0)
#define terminatedCheck() do { \
if (fIsTerminated) \
return kIOReturnNotAttached;\
} while (0)
#define seizeCheck() do { \
if (fIsSeized) \
return kIOReturnExclusiveAccess; \
} while (0)
#define allChecks() do { \
connectCheck(); \
seizeCheck(); \
openCheck(); \
terminatedCheck(); \
} while (0)
#ifndef max
#define max(a, b) \
((a > b) ? a:b)
#endif
#ifndef min
#define min(a, b) \
((a < b) ? a:b)
#endif
IOCFPlugInInterface ** IOHIDDeviceClass::alloc()
{
IOHIDDeviceClass *me;
me = new IOHIDDeviceClass;
if (me)
return (IOCFPlugInInterface **) &me->iunknown.pseudoVTable;
else
return 0;
}
IOHIDDeviceClass::IOHIDDeviceClass()
: IOHIDIUnknown(&sIOCFPlugInInterfaceV1)
{
fHIDDevice.pseudoVTable = (IUnknownVTbl *) &sHIDDeviceInterfaceV122;
fHIDDevice.obj = this;
fService = MACH_PORT_NULL;
fConnection = MACH_PORT_NULL;
fAsyncPort = MACH_PORT_NULL;
fNotifyPort = MACH_PORT_NULL;
fIsOpen = false;
fIsLUNZero = false;
fIsTerminated = false;
fIsSeized = false;
fAsyncPortSetupDone = false;
fRunLoop = NULL;
fCFSource = NULL;
fNotifyCFSource = NULL;
fQueues = NULL;
fDeviceElements = NULL;
fReportHandlerQueue = NULL;
fRemovalCallback = NULL;
fRemovalTarget = NULL;
fRemovalRefcon = NULL;
fInputReportCallback = NULL;
fInputReportTarget = NULL;
fInputReportRefcon = NULL;
fCachedFlags = 0;
fElementCount = 0;
fElements = NULL;
fCurrentValuesMappedMemory = 0;
fCurrentValuesMappedMemorySize = 0;
fAsyncPrivateDataRef = NULL;
fNotifyPrivateDataRef = NULL;
fReportHandlerElementCount = 0;
fReportHandlerElements = NULL;
}
IOHIDDeviceClass::~IOHIDDeviceClass()
{
if (fConnection) {
IOServiceClose(fConnection);
fConnection = MACH_PORT_NULL;
}
if (fService) {
IOObjectRelease(fService);
fService = MACH_PORT_NULL;
}
if (fReportHandlerQueue){
delete fReportHandlerQueue;
fReportHandlerQueue = 0;
}
if (fReportHandlerElements)
delete[] fReportHandlerElements;
if (fElements)
delete[] fElements;
if (fQueues)
CFRelease(fQueues);
if (fDeviceElements)
CFRelease(fDeviceElements);
if (fNotifyCFSource && fRunLoop)
CFRunLoopRemoveSource(fRunLoop, fNotifyCFSource, kCFRunLoopDefaultMode);
if (fNotifyPort)
IONotificationPortDestroy(fNotifyPort);
if (fAsyncPort)
IONotificationPortDestroy(fAsyncPort);
if (fAsyncPrivateDataRef) {
IOObjectRelease(fAsyncPrivateDataRef->notification);
free(fAsyncPrivateDataRef);
}
if (fNotifyPrivateDataRef) {
IOObjectRelease(fNotifyPrivateDataRef->notification);
free(fNotifyPrivateDataRef);
}
}
HRESULT IOHIDDeviceClass::attachQueue (IOHIDQueueClass * iohidQueue, bool reportHandler)
{
HRESULT res = S_OK;
iohidQueue->setOwningDevice(this);
if ( !reportHandler && ( fQueues ||
( fQueues = CFSetCreateMutable(kCFAllocatorDefault, 0, 0) ) ) )
{
CFSetAddValue(fQueues, (void *)iohidQueue);
}
return res;
}
HRESULT IOHIDDeviceClass::detachQueue (IOHIDQueueClass * iohidQueue)
{
HRESULT res = S_OK;
iohidQueue->setOwningDevice(NULL);
if ( fQueues )
{
CFSetRemoveValue(fQueues, (void *)iohidQueue);
}
return res;
}
HRESULT IOHIDDeviceClass::attachOutputTransaction (IOHIDOutputTransactionClass * iohidOutputTrans)
{
HRESULT res = S_OK;
iohidOutputTrans->setOwningDevice(this);
return res;
}
HRESULT IOHIDDeviceClass::detachOutputTransaction (IOHIDOutputTransactionClass * iohidOutputTrans)
{
HRESULT res = S_OK;
iohidOutputTrans->setOwningDevice(NULL);
return res;
}
IOHIDQueueClass * IOHIDDeviceClass::createQueue(bool reportHandler)
{
IOHIDQueueClass * newQueue = new IOHIDQueueClass;
attachQueue (newQueue, reportHandler);
return newQueue;
}
HRESULT IOHIDDeviceClass::queryInterfaceQueue (void **ppv)
{
HRESULT res = S_OK;
IOHIDQueueClass * newQueue = createQueue();
*ppv = newQueue->getInterfaceMap();
return res;
}
HRESULT IOHIDDeviceClass::queryInterfaceOutputTransaction (void **ppv)
{
HRESULT res = S_OK;
IOHIDOutputTransactionClass * newOutputTrans = new IOHIDOutputTransactionClass;
attachOutputTransaction(newOutputTrans);
*ppv = newOutputTrans->getInterfaceMap();
return res;
}
HRESULT IOHIDDeviceClass::queryInterface(REFIID iid, void **ppv)
{
CFUUIDRef uuid = CFUUIDCreateFromUUIDBytes(NULL, iid);
HRESULT res = S_OK;
if (CFEqual(uuid, kIOHIDQueueInterfaceID))
res = queryInterfaceQueue(ppv);
else if (CFEqual(uuid, kIOHIDOutputTransactionInterfaceID))
res = queryInterfaceOutputTransaction(ppv);
else if (CFEqual(uuid, IUnknownUUID)
|| CFEqual(uuid, kIOCFPlugInInterfaceID))
{
*ppv = &iunknown;
addRef();
}
else if ( CFEqual(uuid, kIOHIDDeviceInterfaceID) ||
CFEqual(uuid, kIOHIDDeviceInterfaceID121) ||
CFEqual(uuid, kIOHIDDeviceInterfaceID122) )
{
*ppv = &fHIDDevice;
addRef();
}
else {
*ppv = 0;
printf ("not found\n");
}
if (!*ppv)
res = E_NOINTERFACE;
CFRelease(uuid);
return res;
}
IOReturn IOHIDDeviceClass::
probe(CFDictionaryRef propertyTable, io_service_t inService, SInt32 *order)
{
if (!inService || !IOObjectConformsTo(inService, "IOHIDDevice"))
return kIOReturnBadArgument;
return kIOReturnSuccess;
}
IOReturn IOHIDDeviceClass::
start(CFDictionaryRef propertyTable, io_service_t inService)
{
IOReturn res;
kern_return_t kr;
mach_port_t masterPort;
CFMutableDictionaryRef properties;
fService = inService;
IOObjectRetain(fService);
res = IOServiceOpen(fService, mach_task_self(), 0, &fConnection);
if (res != kIOReturnSuccess)
return res;
connectCheck();
kr = IOMasterPort(MACH_PORT_NULL, &masterPort);
if (kr || !masterPort)
return kIOReturnError;
fNotifyPort = IONotificationPortCreate(masterPort);
fNotifyCFSource = IONotificationPortGetRunLoopSource(fNotifyPort);
fRunLoop = CFRunLoopGetMain(); CFRunLoopAddSource(fRunLoop, fNotifyCFSource, kCFRunLoopDefaultMode);
fNotifyPrivateDataRef = (MyPrivateData *)malloc(sizeof(MyPrivateData));
bzero(fNotifyPrivateDataRef, sizeof(MyPrivateData));
fNotifyPrivateDataRef->self = this;
kr = IOServiceAddInterestNotification( fNotifyPort,
fService,
kIOGeneralInterest,
IOHIDDeviceClass::_deviceNotification,
fNotifyPrivateDataRef,
&(fNotifyPrivateDataRef->notification));
mach_port_deallocate(mach_task_self(), masterPort);
masterPort = 0;
kr = IORegistryEntryCreateCFProperties (fService,
&properties,
kCFAllocatorDefault,
kNilOptions );
if ( !properties || (kr != kIOReturnSuccess))
return kIOReturnError;
fDeviceElements = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
if ( !fDeviceElements )
return kIOReturnError;
BuildElements((CFDictionaryRef) properties, fDeviceElements);
FindReportHandlers((CFDictionaryRef) properties);
CFRelease(properties);
return kIOReturnSuccess;
}
void IOHIDDeviceClass::_deviceNotification( void *refCon,
io_service_t service,
natural_t messageType,
void *messageArgument )
{
IOHIDDeviceClass *self;
MyPrivateData *privateDataRef = (MyPrivateData *) refCon;
IOOptionBits options = (IOOptionBits)messageArgument;
if (!privateDataRef)
return;
self = privateDataRef->self;
if (!self)
return;
switch(messageType)
{
case kIOMessageServiceIsTerminated:
self->fIsTerminated = true;
if ( privateDataRef != self->fAsyncPrivateDataRef)
break;
if (self->fRemovalCallback)
{
((IOHIDCallbackFunction)self->fRemovalCallback)(
self->fRemovalTarget,
kIOReturnSuccess,
self->fRemovalRefcon,
(void *)&(self->fHIDDevice));
}
IOObjectRelease(privateDataRef->notification);
free(self->fAsyncPrivateDataRef);
self->fAsyncPrivateDataRef = 0;
break;
case kIOMessageServiceIsRequestingClose:
if ( privateDataRef != self->fNotifyPrivateDataRef)
break;
if ((options & kIOHIDOptionsTypeSeizeDevice) &&
(options != self->fCachedFlags))
{
self->stopAllQueues(true);
if (self->fReportHandlerQueue)
self->fReportHandlerQueue->stop();
self->close();
self->fIsSeized = true;
}
break;
case kIOMessageServiceWasClosed:
if ( privateDataRef != self->fNotifyPrivateDataRef)
break;
if (self->fIsSeized &&
(options & kIOHIDOptionsTypeSeizeDevice) &&
(options != self->fCachedFlags))
{
self->fIsSeized = false;
self->open(self->fCachedFlags);
self->startAllQueues(true);
if (self->fReportHandlerQueue)
self->fReportHandlerQueue->start();
}
break;
}
}
IOReturn IOHIDDeviceClass::
createAsyncEventSource(CFRunLoopSourceRef *source)
{
IOReturn ret;
CFMachPortRef cfPort;
CFMachPortContext context;
Boolean shouldFreeInfo;
if (!fAsyncPort) {
ret = createAsyncPort(0);
if (kIOReturnSuccess != ret)
return ret;
}
context.version = 1;
context.info = this;
context.retain = NULL;
context.release = NULL;
context.copyDescription = NULL;
cfPort = CFMachPortCreateWithPort(NULL, IONotificationPortGetMachPort(fAsyncPort),
(CFMachPortCallBack) _cfmachPortCallback,
&context, &shouldFreeInfo);
if (!cfPort)
return kIOReturnNoMemory;
fCFSource = CFMachPortCreateRunLoopSource(NULL, cfPort, 0);
CFRelease(cfPort);
if (!fCFSource)
return kIOReturnNoMemory;
if (source)
*source = fCFSource;
return kIOReturnSuccess;
}
CFRunLoopSourceRef IOHIDDeviceClass::getAsyncEventSource()
{
return fCFSource;
}
IOReturn IOHIDDeviceClass::createAsyncPort(mach_port_t *port)
{
IOReturn ret;
mach_port_t masterPort;
connectCheck();
if (fAsyncPort) {
if (port)
*port = IONotificationPortGetMachPort(fAsyncPort);
return kIOReturnSuccess;
}
ret = IOMasterPort(MACH_PORT_NULL, &masterPort);
if (ret || !masterPort)
return kIOReturnError;
fAsyncPort = IONotificationPortCreate(masterPort);
if (fAsyncPort) {
if (port)
*port = IONotificationPortGetMachPort(fAsyncPort);
if (fIsOpen) {
ret = finishAsyncPortSetup();
}
}
mach_port_deallocate(mach_task_self(), masterPort);
masterPort = 0;
return ret;
}
IOReturn IOHIDDeviceClass::finishAsyncPortSetup()
{
natural_t asyncRef[1];
mach_msg_type_number_t len = 0;
finishReportHandlerQueueSetup();
fAsyncPortSetupDone = true;
return io_async_method_scalarI_scalarO(
fConnection, IONotificationPortGetMachPort(fAsyncPort), asyncRef, 1,
kIOHIDLibUserClientSetAsyncPort, NULL, 0, NULL, &len);
}
mach_port_t IOHIDDeviceClass::getAsyncPort()
{
return IONotificationPortGetMachPort(fAsyncPort);
}
IOReturn IOHIDDeviceClass::open(UInt32 flags)
{
IOReturn ret = kIOReturnSuccess;
int input[1];
connectCheck();
if (fIsOpen)
return kIOReturnSuccess;
input[0] = flags;
fCachedFlags = flags;
mach_msg_type_number_t len = 0;
ret = io_connect_method_scalarI_scalarO(
fConnection, kIOHIDLibUserClientOpen, input, 1, NULL, &len);
if (ret != kIOReturnSuccess) {
fCachedFlags = 0;
return ret;
}
fIsOpen = true;
fIsSeized = false;
if (!fAsyncPortSetupDone && fAsyncPort) {
ret = finishAsyncPortSetup();
if (ret != kIOReturnSuccess) {
close();
return ret;
}
}
if ( !fCurrentValuesMappedMemory )
{
vm_address_t address = nil;
vm_size_t size = 0;
ret = IOConnectMapMemory ( fConnection,
IOHIDLibUserClientElementValuesType,
mach_task_self(),
&address,
&size,
kIOMapAnywhere );
if (ret == kIOReturnSuccess)
{
fCurrentValuesMappedMemory = address;
fCurrentValuesMappedMemorySize = size;
}
}
return ret;
}
IOReturn IOHIDDeviceClass::close()
{
openCheck();
connectCheck();
#if 0
IOCDBCommandClass::
commandDeviceClosing((IOCDBDeviceInterface **) &fCDBDevice);
#endif
if (fCurrentValuesMappedMemory)
{
(void) IOConnectUnmapMemory ( fConnection,
IOHIDLibUserClientElementValuesType,
mach_task_self(),
fCurrentValuesMappedMemory);
fCurrentValuesMappedMemory = 0;
fCurrentValuesMappedMemorySize = 0;
}
mach_msg_type_number_t len = 0;
(void) io_connect_method_scalarI_scalarO(fConnection,
kIOHIDLibUserClientClose, NULL, 0, NULL, &len);
fIsOpen = false;
fIsLUNZero = false;
return kIOReturnSuccess;
}
IOReturn IOHIDDeviceClass::setRemovalCallback(
IOHIDCallbackFunction removalCallback,
void * removalTarget,
void * removalRefcon)
{
IOReturn ret = kIOReturnSuccess;
if (!fAsyncPrivateDataRef)
{
fAsyncPrivateDataRef = (MyPrivateData *)malloc(sizeof(MyPrivateData));
bzero(fAsyncPrivateDataRef, sizeof(MyPrivateData));
fAsyncPrivateDataRef->self = this;
if (!fAsyncPort) {
ret = createAsyncPort(0);
if (kIOReturnSuccess != ret)
return ret;
}
ret = IOServiceAddInterestNotification( fAsyncPort,
fService,
kIOGeneralInterest,
IOHIDDeviceClass::_deviceNotification,
fAsyncPrivateDataRef,
&(fAsyncPrivateDataRef->notification));
}
fRemovalCallback = removalCallback;
fRemovalTarget = removalTarget;
fRemovalRefcon = removalRefcon;
return ret;
}
IOReturn IOHIDDeviceClass::getElementValue(IOHIDElementCookie elementCookie,
IOHIDEventStruct * valueEvent)
{
IOReturn kr;
kr = fillElementValue(elementCookie, valueEvent);
if ( (*(UInt64 *)&valueEvent->timestamp == 0) &&
(kr == kIOReturnSuccess) &&
(valueEvent->type == kIOHIDElementTypeFeature))
{
kr = queryElementValue (elementCookie,
valueEvent,
0,
NULL,
NULL,
NULL);
}
return kr;
}
IOReturn IOHIDDeviceClass::setElementValue(
IOHIDElementCookie elementCookie,
IOHIDEventStruct * valueEvent,
UInt32 timeoutMS,
IOHIDElementCallbackFunction callback,
void * callbackTarget,
void * callbackRefcon,
bool pushToDevice)
{
kern_return_t kr = kIOReturnBadArgument;
IOHIDElementStruct element;
allChecks();
if (!getElement(elementCookie, &element))
return kr;
if ((element.type != kIOHIDElementTypeFeature) &&
(element.type != kIOHIDElementTypeOutput))
return kr;
if (element.valueLocation < fCurrentValuesMappedMemorySize)
{
IOHIDElementValue * elementValue = (IOHIDElementValue *)
(fCurrentValuesMappedMemory + element.valueLocation);
UInt32 totalSize = elementValue->totalSize;
ROSETTA_ONLY(
totalSize = OSSwapInt32(totalSize);
);
if (totalSize == sizeof (IOHIDElementValue))
{
elementValue->value[0] = valueEvent->value;
ROSETTA_ONLY(
elementValue->value[0] = OSSwapInt32(valueEvent->value);
);
}
else if (totalSize > sizeof (IOHIDElementValue))
{
UInt32 longValueSize = valueEvent->longValueSize;
if ((longValueSize > (UInt32)element.bytes) ||
( valueEvent->longValue == NULL))
return kr;
bzero(&(elementValue->value), element.bytes);
convertByteToWord ((const UInt8 *)valueEvent->longValue, elementValue->value, longValueSize);
}
if (!pushToDevice)
return kIOReturnSuccess;
UInt32 input[1];
IOByteCount outputCount = 0;
input[0] = (UInt32)elementCookie;
ROSETTA_ONLY(
input[0] = OSSwapInt32((UInt32)elementCookie);
);
kr = io_connect_method_structureI_structureO(
fConnection, kIOHIDLibUserClientPostElementValue,
(char *)input, sizeof(UInt32), NULL, (mach_msg_type_number_t *)&outputCount);
}
return kr;
}
IOReturn IOHIDDeviceClass::queryElementValue(
IOHIDElementCookie elementCookie,
IOHIDEventStruct * valueEvent,
UInt32 timeoutMS,
IOHIDElementCallbackFunction callback,
void * callbackTarget,
void * callbackRefcon)
{
IOReturn ret = kIOReturnBadArgument;
IOHIDElementStruct element;
int input[1];
mach_msg_type_number_t len = 0;
input[0] = (int) elementCookie;
allChecks();
if (!getElement(elementCookie, &element))
return ret;
ret = io_connect_method_scalarI_scalarO(
fConnection,
kIOHIDLibUserClientUpdateElementValue,
input, 1, NULL, &len);
if (ret == kIOReturnSuccess)
ret = fillElementValue(elementCookie, valueEvent);
return ret;
}
IOReturn IOHIDDeviceClass::fillElementValue(IOHIDElementCookie elementCookie,
IOHIDEventStruct * valueEvent)
{
IOHIDElementStruct element;
allChecks();
if (!getElement(elementCookie, &element) || (element.type == kIOHIDElementTypeCollection))
return kIOReturnBadArgument;
SInt32 value = 0;
void * longValue = 0;
UInt32 longValueSize = 0;
UInt32 totalSize;
UInt64 timestamp = 0;
if (element.valueLocation < fCurrentValuesMappedMemorySize)
{
IOHIDElementValue * elementValue = (IOHIDElementValue *)
(fCurrentValuesMappedMemory + element.valueLocation);
totalSize = elementValue->totalSize;
ROSETTA_ONLY(
totalSize = OSSwapInt32(totalSize);
);
if ( totalSize >= sizeof (IOHIDElementValue))
{
if ( totalSize == sizeof (IOHIDElementValue))
{
value = elementValue->value[0];
}
else
{
longValueSize = element.bytes;
longValue = malloc ( longValueSize );
bzero(longValue, longValueSize);
convertWordToByte((const UInt32 *)elementValue->value, (UInt8 *)longValue, longValueSize);
}
timestamp = *(UInt64 *)&(elementValue->timestamp);
ROSETTA_ONLY(
timestamp = OSSwapInt64(timestamp);
value = OSSwapInt32(value);
);
}
}
valueEvent->type = (IOHIDElementType) element.type;
valueEvent->elementCookie = elementCookie;
valueEvent->value = value;
*(UInt64 *)& valueEvent->timestamp = timestamp;
valueEvent->longValueSize = longValueSize;
valueEvent->longValue = longValue;
return kIOReturnSuccess;
}
struct IOHIDReportRefCon {
IOHIDReportCallbackFunction callback;
void * callbackTarget;
void * callbackRefcon;
void * sender;
};
IOReturn
IOHIDDeviceClass::setReport ( IOHIDReportType reportType,
UInt32 reportID,
void * reportBuffer,
UInt32 reportBufferSize,
UInt32 timeoutMS,
IOHIDReportCallbackFunction callback,
void * callbackTarget,
void * callbackRefcon)
{
int in[5];
mach_msg_type_number_t len = 0;
IOReturn ret;
allChecks();
if (callback)
{
if (!fAsyncPort)
return kIOReturnError;
natural_t asyncRef[kIOAsyncCalloutCount];
IOHIDReportRefCon * hidRefcon = 0;
in[0] = reportType;
in[1] = reportID;
in[2] = (natural_t)reportBuffer;
in[3] = reportBufferSize;
in[4] = timeoutMS;
hidRefcon = (IOHIDReportRefCon *)malloc(sizeof(IOHIDReportRefCon));
if (!hidRefcon)
return kIOReturnError;
hidRefcon->callback = callback;
hidRefcon->callbackTarget = callbackTarget;
hidRefcon->callbackRefcon = callbackRefcon;
hidRefcon->sender = &fHIDDevice;
asyncRef[kIOAsyncCalloutFuncIndex] = (natural_t) _hidReportCallback;
asyncRef[kIOAsyncCalloutRefconIndex] = (natural_t) hidRefcon;
ret = io_async_method_scalarI_scalarO( fConnection, IONotificationPortGetMachPort(fAsyncPort), asyncRef, kIOAsyncCalloutCount, kIOHIDLibUserClientAsyncSetReport, in, 5, NULL, &len);
}
else
{
if(reportBufferSize < sizeof(io_struct_inband_t))
{
in[0] = reportType;
in[1] = reportID;
ret = io_connect_method_scalarI_structureI( fConnection, kIOHIDLibUserClientSetReport, in, 2, (char *)reportBuffer, reportBufferSize);
}
else
{
IOHIDReportReq req;
req.reportType = reportType;
req.reportID = reportID;
req.reportBuffer = reportBuffer;
req.reportBufferSize = reportBufferSize;
ROSETTA_ONLY(
req.reportType = OSSwapInt32(req.reportType);
req.reportID = OSSwapInt32(req.reportID);
req.reportBuffer = (void *)OSSwapInt32((uint32_t)req.reportBuffer);
req.reportBufferSize = OSSwapInt32(req.reportBufferSize);
);
ret = io_connect_method_structureI_structureO( fConnection, kIOHIDLibUserClientSetReportOOL, (char*)&req, sizeof(req), NULL, &len);
}
}
if (ret == MACH_SEND_INVALID_DEST)
{
fIsOpen = false;
fConnection = MACH_PORT_NULL;
ret = kIOReturnNoDevice;
}
return ret;
}
IOReturn
IOHIDDeviceClass::getReport ( IOHIDReportType reportType,
UInt32 reportID,
void * reportBuffer,
UInt32 * reportBufferSize,
UInt32 timeoutMS,
IOHIDReportCallbackFunction callback,
void * callbackTarget,
void * callbackRefcon)
{
int in[5];
mach_msg_type_number_t len = 0;
IOReturn ret;
allChecks();
if (callback)
{
if (!fAsyncPort)
return kIOReturnError;
natural_t asyncRef[kIOAsyncCalloutCount];
IOHIDReportRefCon * hidRefcon = 0;
in[0] = reportType;
in[1] = reportID;
in[2] = (natural_t)reportBuffer;
in[3] = *reportBufferSize;
in[4] = timeoutMS;
hidRefcon = (IOHIDReportRefCon *)malloc(sizeof(IOHIDReportRefCon));
if (!hidRefcon)
return kIOReturnError;
hidRefcon->callback = callback;
hidRefcon->callbackTarget = callbackTarget;
hidRefcon->callbackRefcon = callbackRefcon;
hidRefcon->sender = &fHIDDevice;
asyncRef[kIOAsyncCalloutFuncIndex] = (natural_t) _hidReportCallback;
asyncRef[kIOAsyncCalloutRefconIndex] = (natural_t) hidRefcon;
ret = io_async_method_scalarI_scalarO( fConnection, IONotificationPortGetMachPort(fAsyncPort), asyncRef, kIOAsyncCalloutCount, kIOHIDLibUserClientAsyncGetReport, in, 5, NULL, &len);
}
else
{
if(*reportBufferSize < sizeof(io_struct_inband_t))
{
in[0] = reportType;
in[1] = reportID;
ret = io_connect_method_scalarI_structureO( fConnection, kIOHIDLibUserClientGetReport, in, 2, (char *)reportBuffer, (unsigned int *)reportBufferSize);
}
else
{
IOHIDReportReq req;
len = sizeof(*reportBufferSize);
req.reportType = reportType;
req.reportID = reportID;
req.reportBuffer = reportBuffer;
req.reportBufferSize = *reportBufferSize;
ROSETTA_ONLY(
req.reportType = OSSwapInt32(req.reportType);
req.reportID = OSSwapInt32(req.reportID);
req.reportBuffer = (void *)OSSwapInt32((uint32_t)req.reportBuffer);
req.reportBufferSize = OSSwapInt32(req.reportBufferSize);
);
ret = io_connect_method_structureI_structureO( fConnection, kIOHIDLibUserClientGetReportOOL, (char*)&req, sizeof(req), (char*)reportBufferSize, &len);
ROSETTA_ONLY(
*reportBufferSize = OSSwapInt32(*reportBufferSize);
);
}
}
if (ret == MACH_SEND_INVALID_DEST)
{
fIsOpen = false;
fConnection = MACH_PORT_NULL;
ret = kIOReturnNoDevice;
}
return ret;
}
static bool CompareProperty( CFDictionaryRef element, CFDictionaryRef matching, CFStringRef key )
{
CFTypeRef elementValue;
CFTypeRef matchValue;
bool matches = true;
elementValue = CFDictionaryGetValue(element, key);
matchValue = CFDictionaryGetValue(matching, key);
if( elementValue && matchValue )
matches = CFEqual(elementValue, matchValue);
else if (!elementValue && matchValue)
matches = false;
return matches;
}
IOReturn
IOHIDDeviceClass::copyMatchingElements(CFDictionaryRef matchingDict, CFArrayRef *elements)
{
if (!elements)
return kIOReturnBadArgument;
if ( matchingDict )
{
CFMutableArrayRef tempElements = 0;
CFDictionaryRef element;
if (!(tempElements = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks)))
{
*elements = 0;
return kIOReturnNoMemory;
}
for (int i=0; i<fElementCount; i++)
{
if ( !(element = fElements[i].elementDictionaryRef) )
continue;
if (CompareProperty(element, matchingDict, CFSTR(kIOHIDElementCookieKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementTypeKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementCollectionTypeKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementUsageKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementUsagePageKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementMinKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementMaxKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementScaledMinKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementScaledMaxKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementSizeKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementReportSizeKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementReportCountKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementIsArrayKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementIsRelativeKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementIsWrappingKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementIsNonLinearKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementHasPreferredStateKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementHasNullStateKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementVendorSpecificKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementUnitKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementUnitExponentKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementNameKey))
&& CompareProperty(element, matchingDict, CFSTR(kIOHIDElementDuplicateIndexKey)))
{
CFArrayAppendValue(tempElements, element);
}
}
*elements = tempElements;
}
else if (!(*elements = CFArrayCreateCopy(kCFAllocatorDefault, fDeviceElements)))
return kIOReturnNoMemory;
if (CFArrayGetCount(*elements) == 0)
{
CFRelease(*elements);
*elements = 0;
}
return kIOReturnSuccess;
}
IOReturn IOHIDDeviceClass::setInterruptReportHandlerCallback(
void * reportBuffer,
UInt32 reportBufferSize,
IOHIDReportCallbackFunction callback,
void * callbackTarget,
void * callbackRefcon)
{
IOReturn ret = kIOReturnSuccess;
fInputReportCallback = callback;
fInputReportTarget = callbackTarget;
fInputReportRefcon = callbackRefcon;
fInputReportBuffer = reportBuffer;
fInputReportBufferSize = reportBufferSize;
if ( !fReportHandlerQueue )
{
fReportHandlerQueue = createQueue(true);
ret = fReportHandlerQueue->create(0, 8);
if (ret != kIOReturnSuccess)
goto SET_REPORT_HANDLER_CLEANUP;
for (int i=0; i<fReportHandlerElementCount; i++)
{
ret = fReportHandlerQueue->addElement((IOHIDElementCookie)fReportHandlerElements[i].cookie, 0);
if (ret != kIOReturnSuccess)
goto SET_REPORT_HANDLER_CLEANUP;
}
if ( fAsyncPort && fIsOpen )
{
ret = finishReportHandlerQueueSetup();
if (ret != kIOReturnSuccess)
goto SET_REPORT_HANDLER_CLEANUP;
}
}
return kIOReturnSuccess;
SET_REPORT_HANDLER_CLEANUP:
delete fReportHandlerQueue;
fReportHandlerQueue = 0;
return ret;
}
IOReturn IOHIDDeviceClass::finishReportHandlerQueueSetup()
{
IOReturn ret = kIOReturnError;
if ( fReportHandlerQueue )
{
do {
ret = fReportHandlerQueue->setEventCallout(_hidReportHandlerCallback, this, 0);
if (ret != kIOReturnSuccess) break;
ret = fReportHandlerQueue->setAsyncPort(IONotificationPortGetMachPort(fAsyncPort));
if (ret != kIOReturnSuccess) break;
ret = fReportHandlerQueue->start();
if (ret != kIOReturnSuccess) break;
} while ( false );
}
return ret;
}
void IOHIDDeviceClass::_cfmachPortCallback(CFMachPortRef cfPort, mach_msg_header_t *msg, CFIndex size, void *info) {
mach_msg_header_t * msgh = (mach_msg_header_t *)msg;
IOHIDDeviceClass * self = (IOHIDDeviceClass *) info;
if ( !self )
return;
if( msgh->msgh_id == kOSNotificationMessageID)
IODispatchCalloutFromMessage(cfPort, msg, info);
else if ( self->fReportHandlerQueue )
IOHIDQueueClass::queueEventSourceCallback(cfPort, msg, size, self->fReportHandlerQueue);
}
void IOHIDDeviceClass::_hidReportHandlerCallback(void * target, IOReturn result, void * refcon, void * sender)
{
IOHIDEventStruct event;
IOHIDElementStruct element;
IOHIDDeviceClass * self = (IOHIDDeviceClass *)target;
IOHIDQueueClass * queue = self->fReportHandlerQueue;
AbsoluteTime zeroTime = {0,0};
UInt32 size = 0;
if (!self || !self->fIsOpen)
return;
while ((result = queue->getNextEvent( &event, zeroTime, 0)) == kIOReturnSuccess) {
if ( event.longValueSize == 0)
{
self->getElement(event.elementCookie, &element);
size = element.bytes;
size = min(size, self->fInputReportBufferSize);
bzero(self->fInputReportBuffer, size);
ROSETTA_ONLY(
event.value = OSSwapInt32(event.value);
);
self->convertWordToByte((const UInt32 *)(&(event.value)), (UInt8 *)self->fInputReportBuffer, size);
}
else if (event.longValueSize != 0 && (event.longValue != NULL))
{
size = min(self->fInputReportBufferSize, event.longValueSize);
bcopy(event.longValue, self->fInputReportBuffer, size);
free(event.longValue);
}
if (!self->fInputReportCallback)
return;
(self->fInputReportCallback)( self->fInputReportTarget,
result,
self->fInputReportRefcon,
&(self->fHIDDevice),
size);
}
}
void
IOHIDDeviceClass::_hidReportCallback(void *refcon, IOReturn result, UInt32 bufferSize)
{
IOHIDReportRefCon *hidRefcon = (IOHIDReportRefCon *)refcon;
if (!hidRefcon || !hidRefcon->callback)
return;
((IOHIDReportCallbackFunction)hidRefcon->callback)( hidRefcon->callbackTarget,
result,
hidRefcon->callbackRefcon,
hidRefcon->sender,
bufferSize);
free(hidRefcon);
}
#if __i386__
#define ON_INTEL 1
#else
#define ON_INTEL 0
#endif
void IOHIDDeviceClass::convertByteToWord( const UInt8 * src,
UInt32 * dst,
UInt32 bytesToCopy)
{
if ( ON_INTEL || _OSRosettaCheck() )
{
bcopy(src, dst, bytesToCopy);
}
else
{
UInt32 dstOffset = 0;
UInt32 srcOffset = 0;
UInt32 temp = 0;
UInt32 tempShift = 0;
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;
}
}
}
void IOHIDDeviceClass::convertWordToByte( const UInt32 * src,
UInt8 * dst,
UInt32 bytesToCopy)
{
if ( ON_INTEL || _OSRosettaCheck() )
{
bcopy(src, dst, bytesToCopy);
}
else
{
UInt32 dstOffset = 0;
UInt32 srcOffset = 0;
UInt32 temp = 0;
UInt32 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--;
}
}
}
}
}
IOReturn IOHIDDeviceClass::startAllQueues(bool deviceInitiated)
{
IOReturn ret = kIOReturnSuccess;
if ( fQueues )
{
int queueCount = CFSetGetCount(fQueues);
IOHIDQueueClass ** queues = NULL;
queues = (IOHIDQueueClass **)malloc(sizeof(IOHIDQueueClass *) * queueCount);
CFSetGetValues(fQueues, (const void **)queues);
for (int i=0; queues && i<queueCount; i++)
{
ret = queues[i]->start(deviceInitiated);
}
if (queues)
free(queues);
}
return ret;
}
IOReturn IOHIDDeviceClass::stopAllQueues(bool deviceInitiated)
{
IOReturn ret = kIOReturnSuccess;
if ( fQueues )
{
int queueCount = CFSetGetCount(fQueues);
IOHIDQueueClass ** queues = NULL;
queues = (IOHIDQueueClass **)malloc(sizeof(IOHIDQueueClass *) * queueCount);
CFSetGetValues(fQueues, (const void **)queues);
for (int i=0; queues && i<queueCount && ret==kIOReturnSuccess; i++)
{
ret = queues[i]->stop(deviceInitiated);
}
if (queues)
free(queues);
}
return ret;
}
IOHIDQueueInterface ** IOHIDDeviceClass::allocQueue()
{
IOHIDQueueInterface ** iohidqueue;
HRESULT res;
res = this->queryInterface(CFUUIDGetUUIDBytes(kIOHIDQueueInterfaceID),
(void **) &iohidqueue);
return iohidqueue;
}
IOHIDOutputTransactionInterface ** IOHIDDeviceClass::allocOutputTransaction()
{
IOHIDOutputTransactionInterface ** iohidoutputtransaction;
HRESULT res;
res = this->queryInterface(CFUUIDGetUUIDBytes(kIOHIDOutputTransactionInterfaceID),
(void **) &iohidoutputtransaction);
return iohidoutputtransaction;
}
IOCFPlugInInterface IOHIDDeviceClass::sIOCFPlugInInterfaceV1 =
{
0,
&IOHIDIUnknown::genericQueryInterface,
&IOHIDIUnknown::genericAddRef,
&IOHIDIUnknown::genericRelease,
1, 0, &IOHIDDeviceClass::deviceProbe,
&IOHIDDeviceClass::deviceStart,
&IOHIDDeviceClass::deviceClose
};
IOHIDDeviceInterface122 IOHIDDeviceClass::sHIDDeviceInterfaceV122 =
{
0,
&IOHIDIUnknown::genericQueryInterface,
&IOHIDIUnknown::genericAddRef,
&IOHIDIUnknown::genericRelease,
&IOHIDDeviceClass::deviceCreateAsyncEventSource,
&IOHIDDeviceClass::deviceGetAsyncEventSource,
&IOHIDDeviceClass::deviceCreateAsyncPort,
&IOHIDDeviceClass::deviceGetAsyncPort,
&IOHIDDeviceClass::deviceOpen,
&IOHIDDeviceClass::deviceClose,
&IOHIDDeviceClass::deviceSetRemovalCallback,
&IOHIDDeviceClass::deviceGetElementValue,
&IOHIDDeviceClass::deviceSetElementValue,
&IOHIDDeviceClass::deviceQueryElementValue,
&IOHIDDeviceClass::deviceStartAllQueues,
&IOHIDDeviceClass::deviceStopAllQueues,
&IOHIDDeviceClass::deviceAllocQueue,
&IOHIDDeviceClass::deviceAllocOutputTransaction,
&IOHIDDeviceClass::deviceSetReport,
&IOHIDDeviceClass::deviceGetReport,
&IOHIDDeviceClass::deviceCopyMatchingElements,
&IOHIDDeviceClass::deviceSetInterruptReportHandlerCallback
};
IOReturn IOHIDDeviceClass::
deviceProbe(void *self,
CFDictionaryRef propertyTable,
io_service_t inService, SInt32 *order)
{ return getThis(self)->probe(propertyTable, inService, order); }
IOReturn IOHIDDeviceClass::deviceStart(void *self,
CFDictionaryRef propertyTable,
io_service_t inService)
{ return getThis(self)->start(propertyTable, inService); }
IOReturn IOHIDDeviceClass::deviceStop(void *self)
{ return getThis(self)->close(); }
IOReturn IOHIDDeviceClass::
deviceCreateAsyncEventSource(void *self, CFRunLoopSourceRef *source)
{ return getThis(self)->createAsyncEventSource(source); }
CFRunLoopSourceRef IOHIDDeviceClass::
deviceGetAsyncEventSource(void *self)
{ return getThis(self)->getAsyncEventSource(); }
IOReturn IOHIDDeviceClass::
deviceCreateAsyncPort(void *self, mach_port_t *port)
{ return getThis(self)->createAsyncPort(port); }
mach_port_t IOHIDDeviceClass::
deviceGetAsyncPort(void *self)
{ return getThis(self)->getAsyncPort(); }
IOReturn IOHIDDeviceClass::deviceOpen(void *self, UInt32 flags)
{ return getThis(self)->open(flags); }
IOReturn IOHIDDeviceClass::deviceClose(void *self)
{ return getThis(self)->close(); }
IOReturn IOHIDDeviceClass::deviceSetRemovalCallback(void * self,
IOHIDCallbackFunction removalCallback,
void * removalTarget,
void * removalRefcon)
{ return getThis(self)->setRemovalCallback (removalCallback,
removalTarget,
removalRefcon); }
IOReturn IOHIDDeviceClass::deviceGetElementValue(void * self,
IOHIDElementCookie elementCookie,
IOHIDEventStruct * valueEvent)
{ return getThis(self)->getElementValue (elementCookie, valueEvent); }
IOReturn IOHIDDeviceClass::deviceSetElementValue(void * self,
IOHIDElementCookie elementCookie,
IOHIDEventStruct * valueEvent,
UInt32 timeoutMS,
IOHIDElementCallbackFunction callback,
void * callbackTarget,
void * callbackRefcon)
{ return getThis(self)->setElementValue ( elementCookie,
valueEvent,
timeoutMS,
callback,
callbackTarget,
callbackRefcon,
true); }
IOReturn IOHIDDeviceClass::deviceQueryElementValue(void * self,
IOHIDElementCookie elementCookie,
IOHIDEventStruct * valueEvent,
UInt32 timeoutMS,
IOHIDElementCallbackFunction callback,
void * callbackTarget,
void * callbackRefcon)
{ return getThis(self)-> queryElementValue (elementCookie,
valueEvent,
timeoutMS,
callback,
callbackTarget,
callbackRefcon); }
IOReturn IOHIDDeviceClass::deviceStartAllQueues(void * self)
{ return getThis(self)->startAllQueues (); }
IOReturn IOHIDDeviceClass::deviceStopAllQueues(void * self)
{ return getThis(self)->stopAllQueues (); }
IOHIDQueueInterface ** IOHIDDeviceClass::deviceAllocQueue(void *self)
{ return getThis(self)->allocQueue (); }
IOHIDOutputTransactionInterface **
IOHIDDeviceClass::deviceAllocOutputTransaction (void *self)
{ return getThis(self)->allocOutputTransaction (); }
IOReturn
IOHIDDeviceClass::deviceSetReport (void * self,
IOHIDReportType reportType,
UInt32 reportID,
void * reportBuffer,
UInt32 reportBufferSize,
UInt32 timeoutMS,
IOHIDReportCallbackFunction callback,
void * callbackTarget,
void * callbackRefcon)
{
return getThis(self)->setReport(reportType, reportID, reportBuffer, reportBufferSize, timeoutMS, callback, callbackTarget, callbackRefcon);
}
IOReturn
IOHIDDeviceClass::deviceGetReport (void * self,
IOHIDReportType reportType,
UInt32 reportID,
void * reportBuffer,
UInt32 * reportBufferSize,
UInt32 timeoutMS,
IOHIDReportCallbackFunction callback,
void * callbackTarget,
void * callbackRefcon)
{
return getThis(self)->getReport(reportType, reportID, reportBuffer, reportBufferSize, timeoutMS, callback, callbackTarget, callbackRefcon);
}
IOReturn
IOHIDDeviceClass::deviceCopyMatchingElements(void * self, CFDictionaryRef matchingDict, CFArrayRef *elements)
{
return getThis(self)->copyMatchingElements(matchingDict, elements);
}
IOReturn
IOHIDDeviceClass::deviceSetInterruptReportHandlerCallback(void * self,
void * reportBuffer,
UInt32 reportBufferSize,
IOHIDReportCallbackFunction callback,
void * callbackTarget,
void * callbackRefcon)
{
return getThis(self)->setInterruptReportHandlerCallback(reportBuffer, reportBufferSize, callback, callbackTarget, callbackRefcon);
}
kern_return_t IOHIDDeviceClass::BuildElements (CFDictionaryRef properties, CFMutableArrayRef array)
{
kern_return_t kr = kIOReturnSuccess;
long allocatedElementCount;
fElementCount = this->CountElements(properties, 0, CFSTR(kIOHIDElementKey));
fElements = new IOHIDElementStruct[fElementCount];
allocatedElementCount = 0;
kr = CreateLeafElements (properties, array, 0, &allocatedElementCount, CFSTR(kIOHIDElementKey), fElements);
fElementCount = allocatedElementCount;
#if 0
for (long index = 0; index < fElementCount; index++)
{
printf ("%ld-> (%ld, %ld) %lx:%lx, type %lx\n", fElements[index].cookie, fElements[index].min, fElements[index].max, fElements[index].usage, fElements[index].usagePage, fElements[index].type);
}
#endif
return kr;
}
struct StaticWalkElementsParams
{
IOHIDDeviceClass * iohiddevice;
CFDictionaryRef properties;
CFMutableArrayRef array;
CFStringRef key;
IOHIDElementStruct * elements;
long value;
void * data;
};
typedef struct StaticWalkElementsParams StaticWalkElementsParams;
void IOHIDDeviceClass::StaticCountElements (const void * value, void * parameter)
{
if ( CFGetTypeID(value) != CFDictionaryGetTypeID() )
{
printf ("\nIOHIDDeviceClass:Unexpected device registry structure - non-dict array\n"); return;
}
StaticWalkElementsParams * params = (StaticWalkElementsParams *) parameter;
params->value += params->iohiddevice->CountElements(params->properties, (CFTypeRef) value, params->key);
}
long IOHIDDeviceClass::CountElements (CFDictionaryRef properties, CFTypeRef element, CFStringRef key)
{
long count = 0;
if (element == 0)
{
element = CFDictionaryGetValue (properties, key);
}
CFTypeID type = CFGetTypeID(element);
if (type == CFArrayGetTypeID())
{
StaticWalkElementsParams params;
params.iohiddevice = this;
params.properties = properties;
params.key = key;
params.value = 0;
params.data = NULL;
CFRange range = { 0, CFArrayGetCount((CFArrayRef) element) };
CFArrayApplyFunction((CFArrayRef) element, range, StaticCountElements, ¶ms);
count = params.value;
}
else if (type == CFDictionaryGetTypeID())
{
count = 1;
CFTypeRef subElements = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementKey));
if (subElements)
{
count += this->CountElements ((CFDictionaryRef) element, subElements, key);
}
if (CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementDuplicateValueSizeKey)))
{
CFNumberRef numberRef;
UInt32 duplicateCount;
if ( (numberRef = (CFNumberRef) CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementReportCountKey)))
&& CFNumberGetValue(numberRef, kCFNumberLongType, &duplicateCount))
{
count += duplicateCount;
}
}
}
else
printf ("\nIOHIDDeviceClass:Unexpected device registry structure - strange type\n");
return count;
}
void IOHIDDeviceClass::StaticCreateLeafElements (const void * value, void * parameter)
{
kern_return_t kr = kIOReturnSuccess;
if (CFGetTypeID(value) != CFDictionaryGetTypeID() )
return;
StaticWalkElementsParams * params = (StaticWalkElementsParams *) parameter;
kr = params->iohiddevice->CreateLeafElements(params->properties, params->array, (CFTypeRef) value, (long *) params->data, params->key, params->elements);
if (params->value == kIOReturnSuccess)
params->value = kr;
}
kern_return_t IOHIDDeviceClass::CreateLeafElements (CFDictionaryRef properties, CFMutableArrayRef array,
CFTypeRef element, long * allocatedElementCount, CFStringRef key, IOHIDElementStruct * elements)
{
kern_return_t kr = kIOReturnSuccess;
bool isRootItem = false;
if (element == 0)
{
element = CFDictionaryGetValue (properties, key);
properties = 0;
isRootItem = true;
}
CFTypeID type = CFGetTypeID(element);
if (type == CFArrayGetTypeID())
{
StaticWalkElementsParams params;
params.iohiddevice = this;
params.properties = properties;
params.array = array;
params.key = key;
params.elements = elements;
params.value = kIOReturnSuccess;
params.data = allocatedElementCount;
CFRange range = { 0, CFArrayGetCount((CFArrayRef) element) };
CFArrayApplyFunction((CFArrayRef) element, range, StaticCreateLeafElements, ¶ms);
kr = params.value;
}
else if (type == CFDictionaryGetTypeID())
{
CFMutableDictionaryRef dictionary = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, (CFMutableDictionaryRef) element);
CFDictionaryRef tempElement = 0;
IOHIDElementStruct hidelement;
CFTypeRef object;
long number;
object = CFDictionaryGetValue (dictionary, CFSTR(kIOHIDElementDuplicateIndexKey));
if (object != 0) return kr;
if ( array )
{
if (!isRootItem && properties)
CFDictionarySetValue(dictionary, CFSTR(kIOHIDElementParentCollectionKey), properties);
tempElement = CFDictionaryCreateCopy(kCFAllocatorDefault, dictionary);
CFArrayAppendValue(array, tempElement);
CFRelease(tempElement);
hidelement.elementDictionaryRef = tempElement;
}
object = CFDictionaryGetValue (dictionary, CFSTR(kIOHIDElementCookieKey));
if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
return kIOReturnInternalError;
if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number))
return kIOReturnInternalError;
hidelement.cookie = number;
object = CFDictionaryGetValue (dictionary, CFSTR(kIOHIDElementTypeKey));
if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
return kIOReturnInternalError;
if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number))
return kIOReturnInternalError;
hidelement.type = number;
if ( CFStringCompare( key, CFSTR(kIOHIDElementKey), 0 ) == kCFCompareEqualTo)
{
object = CFDictionaryGetValue (dictionary, CFSTR(kIOHIDElementUsageKey));
if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
return kIOReturnInternalError;
if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number))
return kIOReturnInternalError;
hidelement.usage = number;
object = CFDictionaryGetValue (dictionary, CFSTR(kIOHIDElementUsagePageKey));
if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
return kIOReturnInternalError;
if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number))
return kIOReturnInternalError;
hidelement.usagePage = number;
}
CFTypeRef subElements = CFDictionaryGetValue (dictionary, CFSTR(kIOHIDElementKey));
if (subElements)
{
elements[(*allocatedElementCount)++] = hidelement;
tempElement = CFDictionaryCreateCopy(kCFAllocatorDefault, dictionary);
kr = this->CreateLeafElements (tempElement, array, subElements, allocatedElementCount, key, elements);
CFRelease(tempElement);
}
else
{
object = CFDictionaryGetValue (dictionary, CFSTR(kIOHIDElementSizeKey));
if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
return kIOReturnInternalError;
if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number))
return kIOReturnInternalError;
hidelement.bytes = number >> 3;
hidelement.bytes += (number % 8) ? 1 : 0;
if ( ! CFStringCompare( key, CFSTR(kIOHIDElementKey), 0 ) )
{
object = CFDictionaryGetValue (dictionary, CFSTR(kIOHIDElementMinKey));
if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
return kIOReturnInternalError;
if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number))
return kIOReturnInternalError;
hidelement.min = number;
object = CFDictionaryGetValue (dictionary, CFSTR(kIOHIDElementMaxKey));
if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
return kIOReturnInternalError;
if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number))
return kIOReturnInternalError;
hidelement.max = number;
}
object = CFDictionaryGetValue (dictionary, CFSTR(kIOHIDElementValueLocationKey));
if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
return kIOReturnInternalError;
if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number))
return kIOReturnInternalError;
hidelement.valueLocation = number;
elements[(*allocatedElementCount)++] = hidelement;
do
{
CFTypeRef duplicateReportBitsNumber = 0;
UInt32 duplicateSizeOffset = 0;
UInt32 duplicateCount = 0;
object = CFDictionaryGetValue (dictionary, CFSTR(kIOHIDElementDuplicateValueSizeKey));
if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
break;
if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &duplicateSizeOffset))
break;
object = CFDictionaryGetValue (dictionary, CFSTR(kIOHIDElementReportCountKey));
if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
break;
if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &duplicateCount))
break;
object = CFDictionaryGetValue (dictionary, CFSTR(kIOHIDElementReportSizeKey));
if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
break;
if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number))
break;
hidelement.bytes = number >> 3;
hidelement.bytes += (number % 8) ? 1 : 0;
duplicateReportBitsNumber = object;
for ( unsigned i=0; i<duplicateCount; i++)
{
hidelement.cookie ++;
hidelement.valueLocation -= duplicateSizeOffset;
hidelement.elementDictionaryRef = 0;
if ( array )
{
CFMutableDictionaryRef tempMutableDict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, dictionary);
if ( tempMutableDict )
{
CFDictionaryRemoveValue(tempMutableDict, CFSTR(kIOHIDElementReportSizeKey));
CFDictionaryRemoveValue(tempMutableDict, CFSTR(kIOHIDElementReportCountKey));
CFDictionarySetValue(tempMutableDict, CFSTR(kIOHIDElementSizeKey), duplicateReportBitsNumber);
object = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &hidelement.cookie);
CFDictionarySetValue(tempMutableDict, CFSTR(kIOHIDElementCookieKey), object);
CFRelease( object );
object = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &hidelement.valueLocation);
CFDictionarySetValue(tempMutableDict, CFSTR(kIOHIDElementValueLocationKey), object);
CFRelease( object );
object = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &i);
CFDictionarySetValue(tempMutableDict, CFSTR(kIOHIDElementDuplicateIndexKey), object);
CFRelease( object );
tempElement = CFDictionaryCreateCopy(kCFAllocatorDefault, tempMutableDict);
CFArrayAppendValue(array, tempElement);
CFRelease(tempMutableDict);
CFRelease(tempElement);
hidelement.elementDictionaryRef = tempElement;
}
}
elements[(*allocatedElementCount)++] = hidelement;
}
} while ( 0 );
}
CFRelease(dictionary);
}
else
printf ("\nIOHIDDeviceClass:Unexpected device registry structure - strange type\n");
return kr;
}
kern_return_t IOHIDDeviceClass::FindReportHandlers(CFDictionaryRef properties)
{
kern_return_t kr = kIOReturnSuccess;
long allocatedElementCount;
fReportHandlerElementCount = CountElements(properties, 0, CFSTR(kIOHIDInputReportElementsKey));
fReportHandlerElements = new IOHIDElementStruct[fReportHandlerElementCount];
allocatedElementCount = 0;
kr = CreateLeafElements (properties, 0, 0, &allocatedElementCount, CFSTR(kIOHIDInputReportElementsKey), fReportHandlerElements);
fReportHandlerElementCount = allocatedElementCount;
return kr;
}
IOHIDElementType IOHIDDeviceClass::getElementType(IOHIDElementCookie elementCookie)
{
IOHIDElementStruct element;
IOHIDElementType type = (IOHIDElementType) 0;
if (getElement(elementCookie, &element))
type = (IOHIDElementType) element.type;
return type;
}
UInt32 IOHIDDeviceClass::getElementByteSize(IOHIDElementCookie elementCookie)
{
UInt32 size = 0;
IOHIDElementStruct element;
if (getElement(elementCookie, &element))
size = element.bytes;
return size;
}
bool IOHIDDeviceClass::getElement(IOHIDElementCookie elementCookie, IOHIDElementStruct *element)
{
for (long index = 0; index < fElementCount; index++)
if (fElements[index].cookie == (unsigned long) elementCookie)
{
*element = fElements[index];
return true;
}
for (long index = 0; index < fReportHandlerElementCount; index++)
if (fReportHandlerElements[index].cookie == (unsigned long) elementCookie)
{
*element = fReportHandlerElements[index];
return true;
}
return false;
}