IOHIDDeviceClass.cpp [plain text]
#define CFRUNLOOP_NEW_API 1
#include <CoreFoundation/CFMachPort.h>
#include "IOHIDDeviceClass.h"
#include "IOHIDQueueClass.h"
#include "IOHIDLibUserClient.h"
#if IOHID_PSEUDODEVICE
#define _NSBUILDING_APPKIT_DLL 0
#include <CoreFoundation/CFVeryPrivate.h>
#undef _NSBUILDING_APPKIT_DLL
#endif
__BEGIN_DECLS
#include <mach/mach_interface.h>
#include <IOKit/iokitmig.h>
__END_DECLS
#define connectCheck() do { \
if (!fConnection) \
return kIOReturnNoDevice; \
} while (0)
#define openCheck() do { \
if (!fIsOpen) \
return kIOReturnNotOpen; \
} while (0)
#define allChecks() do { \
connectCheck(); \
openCheck(); \
} while (0)
IOCFPlugInInterface ** IOHIDDeviceClass::alloc()
{
IOHIDDeviceClass *me;
me = new IOHIDDeviceClass;
if (me)
return (IOCFPlugInInterface **) &me->iunknown.pseudoVTable;
else
return 0;
}
IOHIDDeviceClass::IOHIDDeviceClass()
: IOHIDIUnknown(&sIOCFPlugInInterfaceV1),
fService(MACH_PORT_NULL),
fConnection(MACH_PORT_NULL),
fAsyncPort(MACH_PORT_NULL),
fIsOpen(false),
fIsLUNZero(false)
{
fHIDDevice.pseudoVTable = (IUnknownVTbl *) &sHIDDeviceInterfaceV1;
fHIDDevice.obj = this;
fElementCount = 0;
fElements = nil;
}
IOHIDDeviceClass::~IOHIDDeviceClass()
{
if (fConnection) {
IOServiceClose(fConnection);
fConnection = MACH_PORT_NULL;
}
if (fService) {
IOObjectRelease(fService);
fService = MACH_PORT_NULL;
}
if (fElements)
delete[] fElements;
}
HRESULT IOHIDDeviceClass::attachQueue (IOHIDQueueClass * iohidQueue)
{
HRESULT res = S_OK;
iohidQueue->setOwningDevice(this);
return res;
}
HRESULT IOHIDDeviceClass::detachQueue (IOHIDQueueClass * iohidQueue)
{
HRESULT res = S_OK;
iohidQueue->setOwningDevice(NULL);
return res;
}
HRESULT IOHIDDeviceClass::queryInterfaceQueue (void **ppv)
{
HRESULT res = S_OK;
IOHIDQueueClass * newQueue = new IOHIDQueueClass;
attachQueue (newQueue);
*ppv = newQueue->getInterfaceMap();
return res;
}
HRESULT IOHIDDeviceClass::queryInterfaceOutputTransaction (void **ppv)
{
HRESULT res = E_NOINTERFACE;
*ppv = 0;
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))
{
*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;
CFMutableDictionaryRef entryProperties = 0;
kern_return_t kr;
fService = inService;
IOObjectRetain(fService);
res = IOServiceOpen(fService, mach_task_self(), 0, &fConnection);
if (res != kIOReturnSuccess)
return res;
connectCheck();
kr = IORegistryEntryCreateCFProperties (fService,
&entryProperties,
kCFAllocatorDefault,
kNilOptions );
if (entryProperties)
{
BuildElements((CFDictionaryRef) entryProperties);
CFRelease(entryProperties);
}
return kIOReturnSuccess;
}
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, fAsyncPort,
(CFMachPortCallBack) IODispatchCalloutFromMessage,
&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;
connectCheck();
ret = IOCreateReceivePort(kOSAsyncCompleteMessageID, &fAsyncPort);
if (kIOReturnSuccess == ret) {
if (port)
*port = fAsyncPort;
if (fIsOpen) {
natural_t asyncRef[1];
mach_msg_type_number_t len = 0;
return io_async_method_structureI_structureO(
fConnection, fAsyncPort, asyncRef, 1,
kIOHIDLibUserClientSetAsyncPort, NULL, 0, NULL, &len);
}
}
return ret;
}
mach_port_t IOHIDDeviceClass::getAsyncPort()
{
return fAsyncPort;
}
IOReturn IOHIDDeviceClass::open(UInt32 flags)
{
IOReturn ret = kIOReturnSuccess;
connectCheck();
if (fIsOpen)
return kIOReturnSuccess;
mach_msg_type_number_t len = 0;
ret = io_connect_method_scalarI_scalarO(
fConnection, kIOHIDLibUserClientOpen, NULL, 0, NULL, &len);
if (ret != kIOReturnSuccess)
return ret;
fIsOpen = true;
if (fAsyncPort) {
natural_t asyncRef[1];
mach_msg_type_number_t len = 0;
ret = io_async_method_scalarI_scalarO(
fConnection, fAsyncPort, asyncRef, 1,
kIOHIDLibUserClientSetAsyncPort, NULL, 0, NULL, &len);
if (ret != kIOReturnSuccess) {
close();
return ret;
}
}
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()
{
allChecks();
#if 0
IOCDBCommandClass::
commandDeviceClosing((IOCDBDeviceInterface **) &fCDBDevice);
#endif
#if 0
if (fCurrentValuesMappedMemory != 0)
{
(void) IOConnectUnmapMemory ( fConnection,
IOHIDLibUserClientElementValuesType,
mach_task_self(),
fCurrentValuesMappedMemory);
fCurrentValuesMappedMemory = nil;
fCurrentValuesMappedMemorySize = 0;
}
#endif
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)
{
return kIOReturnUnsupported;
}
IOReturn IOHIDDeviceClass::getElementValue(IOHIDElementCookie elementCookie,
IOHIDEventStruct * valueEvent)
{
kern_return_t kr = kIOReturnBadArgument;
for (long index = 0; index < fElementCount; index++)
{
if (fElements[index].cookie == (unsigned long) elementCookie)
{
SInt32 value = 0;
UInt64 timestamp = 0;
#if IOHID_PSEUDODEVICE
value = fElements[index].currentValue;
timestamp = __CFReadTSR();
if (fElements[index].pauseCount > 0)
fElements[index].pauseCount--;
else
{
fElements[index].currentValue += fElements[index].increment;
if (fElements[index].currentValue <= fElements[index].min)
fElements[index].increment = 1;
else if (fElements[index].currentValue >= fElements[index].max)
fElements[index].increment = -1;
if (fElements[index].currentValue < fElements[index].min)
fElements[index].currentValue = fElements[index].min;
else if (fElements[index].currentValue > fElements[index].max)
fElements[index].currentValue = fElements[index].max;
if (fElements[index].type == kIOHIDElementTypeInput_Button)
{
if (fElements[index].currentValue == fElements[index].min)
fElements[index].pauseCount = 50 + (random() % 1000);
else
fElements[index].pauseCount = (random() % 5);
}
}
#else
if (fElements[index].valueLocation < fCurrentValuesMappedMemorySize)
{
IOHIDElementValue * elementValue = (IOHIDElementValue *)
(fCurrentValuesMappedMemory + fElements[index].valueLocation);
if (elementValue->totalSize == sizeof (IOHIDElementValue))
{
value = elementValue->value[0];
timestamp = *(UInt64 *)& elementValue->timestamp;
}
}
#endif
valueEvent->type = (IOHIDElementType) fElements[index].type;
valueEvent->elementCookie = elementCookie;
valueEvent->value = value;
*(UInt64 *)& valueEvent->timestamp = timestamp;
valueEvent->longValueSize = 0;
valueEvent->longValue = NULL;
kr = kIOReturnSuccess;
}
}
return kr;
}
IOReturn IOHIDDeviceClass::setElementValue(
IOHIDElementCookie elementCookie,
IOHIDEventStruct * valueEvent,
UInt32 timeoutMS,
IOHIDElementCallbackFunction * callback,
void * callbackTarget,
void * callbackRefcon)
{
return kIOReturnUnsupported;
}
IOReturn IOHIDDeviceClass::queryElementValue(
IOHIDElementCookie elementCookie,
IOHIDEventStruct * valueEvent,
UInt32 timeoutMS,
IOHIDElementCallbackFunction * callback,
void * callbackTarget,
void * callbackRefcon)
{
return kIOReturnUnsupported;
}
IOReturn IOHIDDeviceClass::startAllQueues()
{
return kIOReturnUnsupported;
}
IOReturn IOHIDDeviceClass::stopAllQueues()
{
return kIOReturnUnsupported;
}
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
};
IOHIDDeviceInterface IOHIDDeviceClass::sHIDDeviceInterfaceV1 =
{
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
};
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); }
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 (); }
kern_return_t IOHIDDeviceClass::BuildElements (CFDictionaryRef properties)
{
kern_return_t kr = kIOReturnSuccess;
long allocatedElementCount;
fElementCount = this->CountLeafElements(properties, 0);
fElements = new IOHIDElementStruct[fElementCount];
allocatedElementCount = 0;
kr = this->CreateLeafElements (properties, 0, &allocatedElementCount);
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;
long value;
void * data;
};
typedef struct StaticWalkElementsParams StaticWalkElementsParams;
void IOHIDDeviceClass::StaticCountLeafElements (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->CountLeafElements(params->properties, (CFTypeRef) value);
}
long IOHIDDeviceClass::CountLeafElements (CFDictionaryRef properties, CFTypeRef element)
{
long count = 0;
if (element == 0)
{
element = CFDictionaryGetValue (properties, CFSTR(kIOHIDElementKey));
}
CFTypeID type = CFGetTypeID(element);
if (type == CFArrayGetTypeID())
{
StaticWalkElementsParams params;
params.iohiddevice = this;
params.properties = properties;
params.value = 0;
params.data = NULL;
CFRange range = { 0, CFArrayGetCount((CFArrayRef) element) };
CFArrayApplyFunction((CFArrayRef) element, range, StaticCountLeafElements, ¶ms);
count = params.value;
}
else if (type == CFDictionaryGetTypeID())
{
CFTypeRef subElements = CFDictionaryGetValue ((CFDictionaryRef) element, CFSTR(kIOHIDElementKey));
if (subElements)
{
count = this->CountLeafElements ((CFDictionaryRef) element, subElements);
}
else
{
count = 1;
}
}
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, (CFTypeRef) value, (long *) params->data);
if (params->value == kIOReturnSuccess)
params->value = kr;
}
kern_return_t IOHIDDeviceClass::CreateLeafElements (CFDictionaryRef properties,
CFTypeRef element, long * allocatedElementCount)
{
kern_return_t kr = kIOReturnSuccess;
if (element == 0)
{
element = CFDictionaryGetValue (properties, CFSTR(kIOHIDElementKey));
}
CFTypeID type = CFGetTypeID(element);
if (type == CFArrayGetTypeID())
{
StaticWalkElementsParams params;
params.iohiddevice = this;
params.properties = properties;
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())
{
CFDictionaryRef dictionary = (CFDictionaryRef) element;
CFTypeRef subElements = CFDictionaryGetValue (dictionary, CFSTR(kIOHIDElementKey));
if (subElements)
{
kr = this->CreateLeafElements (dictionary, subElements, allocatedElementCount);
}
else
{
IOHIDElementStruct hidelement;
CFTypeRef object;
long number;
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;
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(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;
#if IOHID_PSEUDODEVICE
hidelement.currentValue = hidelement.min;
hidelement.pauseCount = 0;
hidelement.increment = 1;
#else
object = CFDictionaryGetValue (dictionary, CFSTR(kIOHIDElementValueLocationKey));
if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
return kIOReturnInternalError;
if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number))
return kIOReturnInternalError;
hidelement.valueLocation = number;
#endif
fElements[(*allocatedElementCount)++] = hidelement;
}
}
else
printf ("\nIOHIDDeviceClass:Unexpected device registry structure - strange type\n");
return kr;
}
IOHIDElementType IOHIDDeviceClass::getElementType(IOHIDElementCookie elementCookie)
{
IOHIDElementType type = (IOHIDElementType) 0;
for (long index = 0; index < fElementCount; index++)
if (fElements[index].cookie == (unsigned long) elementCookie)
type = (IOHIDElementType) fElements[index].type;
return type;
}