#define DEBUG_ASSERT_COMPONENT_NAME_STRING IOHIDDevice
#if 0
#warning ### REMOVE THIS ###
#define DEBUG_ASSERT_PRODUCTION_CODE 0
#endif
#include <pthread.h>
#include <CoreFoundation/CFRuntime.h>
#include <CoreFoundation/CFBase.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOMessage.h>
#include <IOKit/hid/IOHIDKeys.h>
#include <asl.h>
#include <AssertMacros.h>
#include "IOHIDDevicePlugIn.h"
#include "IOHIDDevice.h"
#include "IOHIDQueue.h"
#include "IOHIDElement.h"
#include "IOHIDTransaction.h"
#include "IOHIDLibPrivate.h"
#include "IOHIDManagerPersistentProperties.h"
static IOHIDDeviceRef __IOHIDDeviceCreate(
CFAllocatorRef allocator,
CFAllocatorContext * context __unused);
static void __IOHIDDeviceFree( CFTypeRef object );
static void __IOHIDDeviceRegister(void);
static CFArrayRef __IOHIDDeviceCopyMatchingInputElements(
IOHIDDeviceRef device,
CFArrayRef multiple);
static void __IOHIDDeviceRegisterMatchingInputElements(
IOHIDDeviceRef device,
IOHIDQueueRef queue,
CFArrayRef mutlipleMatch);
static void __IOHIDDeviceInputElementValueCallback(
void * context,
IOReturn result,
void * sender);
static void __IOHIDDeviceNotification(
IOHIDDeviceRef service,
io_service_t ioservice,
natural_t messageType,
void * messageArgument );
static void __IOHIDDeviceValueCallback(
void * context,
IOReturn result,
void * sender,
IOHIDValueRef value);
static void __IOHIDDeviceReportCallbackOnce(
void * context,
IOReturn result,
void * sender,
IOHIDReportType type,
uint32_t reportID,
uint8_t * report,
CFIndex reportLength);
static void __IOHIDDeviceInputReportCallback(
void * context,
IOReturn result,
void * sender,
IOHIDReportType type,
uint32_t reportID,
uint8_t * report,
CFIndex reportLength);
static void __IOHIDDeviceInputReportWithTimeStampCallback(
void * context,
IOReturn result,
void * sender,
IOHIDReportType type,
uint32_t reportID,
uint8_t * report,
CFIndex reportLength,
uint64_t timeStamp);
static void __IOHIDDeviceInputReportApplier(
CFDataRef value,
void *voidContext);
static void __IOHIDDeviceRegisterInputReportCallback(
IOHIDDeviceRef device,
uint8_t * report,
CFIndex reportLength,
IOHIDReportCallback callback,
IOHIDReportWithTimeStampCallback callbackWithTimeStamp,
void * context);
static void __IOHIDDeviceTransactionCallback(
void * context,
IOReturn result,
void * sender);
static Boolean __IOHIDDeviceCallbackBaseDataIsEqual(
const void * value1,
const void * value2);
static CFStringRef __IOHIDDeviceCopyDebugDescription(CFTypeRef cf);
typedef struct {
void *context;
IOReturn result;
void *sender;
IOHIDReportType type;
uint32_t reportID;
uint8_t *report;
CFIndex reportLength;
uint64_t timeStamp;
} IOHIDDeviceInputReportApplierContext;
typedef struct {
void * context;
void * callback;
} IOHIDDeviceCallbackBaseInfo;
typedef struct {
void * context;
void * callback;
IOHIDDeviceRef device;
} IOHIDDeviceCallbackInfo;
typedef struct {
void * context;
void * callback;
IOHIDDeviceRef device;
CFArrayRef elements;
} IOHIDDeviceTransactionCallbackInfo;
typedef struct {
void * context;
IOHIDValueCallback callback;
} IOHIDDeviceInputElementValueCallbackInfo;
typedef struct {
void * context;
IOHIDCallback callback;
} IOHIDDeviceRemovalCallbackInfo;
typedef struct {
void * context;
IOHIDReportCallback callback;
IOHIDReportWithTimeStampCallback callbackWithTimeStamp;
IOHIDDeviceRef device;
} IOHIDDeviceReportCallbackInfo;
typedef struct __IOHIDDevice
{
CFRuntimeBase cfBase;
io_service_t service;
IOHIDDeviceDeviceInterface** deviceInterface;
IOHIDDeviceTimeStampedDeviceInterface** deviceTimeStampedInterface;
IOCFPlugInInterface ** plugInInterface;
CFMutableDictionaryRef properties;
CFMutableSetRef elements;
CFStringRef rootKey;
CFStringRef UUIDKey;
IONotificationPortRef notificationPort;
io_object_t notification;
CFTypeRef asyncEventSource;
CFRunLoopRef runLoop;
CFStringRef runLoopMode;
IOHIDQueueRef queue;
CFArrayRef inputMatchingMultiple;
Boolean loadProperties;
Boolean isDirty;
CFMutableSetRef removalCallbackSet;
CFMutableSetRef inputReportCallbackSet;
CFMutableSetRef inputValueCallbackSet;
} __IOHIDDevice, *__IOHIDDeviceRef;
static const CFRuntimeClass __IOHIDDeviceClass = {
.version = 0,
.className = "IOHIDDevice",
.finalize = __IOHIDDeviceFree,
.copyDebugDesc = __IOHIDDeviceCopyDebugDescription
};
static pthread_once_t __deviceTypeInit = PTHREAD_ONCE_INIT;
static CFTypeID __kIOHIDDeviceTypeID = _kCFRuntimeNotATypeID;
static CFSetCallBacks __callbackBaseSetCallbacks = {};
static CFStringRef __debugKeys[] = {CFSTR(kIOHIDTransportKey), CFSTR(kIOHIDVendorIDKey), CFSTR(kIOHIDVendorIDSourceKey), CFSTR(kIOHIDProductIDKey), CFSTR(kIOHIDManufacturerKey), CFSTR(kIOHIDProductKey), CFSTR(kIOHIDPrimaryUsagePageKey), CFSTR(kIOHIDPrimaryUsageKey), CFSTR(kIOHIDCategoryKey), CFSTR(kIOHIDReportIntervalKey), CFSTR(kIOHIDSampleIntervalKey), CFSTR(kIOHIDBatchIntervalKey)};
CFStringRef __IOHIDDeviceCopyDebugDescription(CFTypeRef cf)
{
IOHIDDeviceRef device = ( IOHIDDeviceRef ) cf;
CFMutableStringRef description = NULL;
CFStringRef subDescription = NULL;
io_name_t name = "";
io_registry_entry_t regEntry = MACH_PORT_NULL;
regEntry = device->service;
if ( regEntry && (IOObjectRetain(regEntry) != KERN_SUCCESS)) {
regEntry = MACH_PORT_NULL;
}
description = CFStringCreateMutable(CFGetAllocator(device), 0);
require(description, exit);
IORegistryEntryGetName(regEntry, name);
if (strlen(name) == 0) {
IOObjectGetClass(regEntry, name);
}
subDescription = CFStringCreateWithFormat(CFGetAllocator(device), NULL, CFSTR("<IOHIDDevice %p [%p] 'ClassName=%s' "),
device,
CFGetAllocator(device),
name);
if ( subDescription ) {
CFStringAppend(description, subDescription);
CFRelease(subDescription);
}
for ( uint32_t index=0; index<(sizeof(__debugKeys)/sizeof(CFStringRef)); index++ ) {
CFTypeRef object;
object = IOHIDDeviceGetProperty(device, __debugKeys[index]);
if ( !object )
continue;
if ( CFGetTypeID(object) == CFNumberGetTypeID() ) {
CFNumberRef number = (CFNumberRef)object;
uint32_t value;
CFNumberGetValue(number, kCFNumberSInt32Type, &value);
subDescription = CFStringCreateWithFormat(CFGetAllocator(device), NULL, CFSTR(" %@=%d"), __debugKeys[index], value);
}
else if ( CFGetTypeID(object) == CFStringGetTypeID() ) {
subDescription = CFStringCreateWithFormat(CFGetAllocator(device), NULL, CFSTR(" %@=%@"), __debugKeys[index], object);
}
if ( !subDescription )
continue;
CFStringAppend(description, subDescription);
CFRelease(subDescription);
}
CFStringAppend(description, CFSTR(">"));
exit:
if ( regEntry )
IOObjectRelease(regEntry);
return description;
}
void __IOHIDDeviceRegister(void)
{
__callbackBaseSetCallbacks = kCFTypeSetCallBacks;
__callbackBaseSetCallbacks.equal = __IOHIDDeviceCallbackBaseDataIsEqual;
__kIOHIDDeviceTypeID = _CFRuntimeRegisterClass(&__IOHIDDeviceClass);
}
IOHIDDeviceRef __IOHIDDeviceCreate(
CFAllocatorRef allocator,
CFAllocatorContext * context __unused)
{
IOHIDDeviceRef device = NULL;
void * offset = NULL;
uint32_t size;
size = sizeof(__IOHIDDevice) - sizeof(CFRuntimeBase);
device = (IOHIDDeviceRef)_CFRuntimeCreateInstance(allocator, IOHIDDeviceGetTypeID(), size, NULL);
if (!device)
return NULL;
offset = device;
bzero(offset + sizeof(CFRuntimeBase), size);
return device;
}
void __IOHIDDeviceFree( CFTypeRef object )
{
IOHIDDeviceRef device = (IOHIDDeviceRef)object;
if ( device->inputMatchingMultiple ) {
CFRelease(device->inputMatchingMultiple);
device->inputMatchingMultiple = NULL;
}
if ( device->queue ) {
CFRelease(device->queue);
device->queue = NULL;
}
CFRELEASE_IF_NOT_NULL(device->properties);
CFRELEASE_IF_NOT_NULL(device->elements);
CFRELEASE_IF_NOT_NULL(device->rootKey);
CFRELEASE_IF_NOT_NULL(device->removalCallbackSet);
CFRELEASE_IF_NOT_NULL(device->inputValueCallbackSet);
CFRELEASE_IF_NOT_NULL(device->inputReportCallbackSet);
if ( device->deviceInterface ) {
(*device->deviceInterface)->Release(device->deviceInterface);
device->deviceInterface = NULL;
}
if ( device->deviceTimeStampedInterface ) {
(*device->deviceTimeStampedInterface)->Release(device->deviceTimeStampedInterface);
device->deviceTimeStampedInterface = NULL;
}
if ( device->plugInInterface ) {
IODestroyPlugInInterface(device->plugInInterface);
device->plugInInterface = NULL;
}
if ( device->service ) {
IOObjectRelease(device->service);
device->service = 0;
}
if ( device->notification ) {
IOObjectRelease(device->notification);
device->notification = 0;
}
if (device->notificationPort) {
IONotificationPortDestroy(device->notificationPort);
device->notificationPort = NULL;
}
}
void __IOHIDDeviceNotification(
IOHIDDeviceRef device,
io_service_t ioservice __unused,
natural_t messageType,
void * messageArgument __unused)
{
CFIndex index, count = 0;
if ( !device || (messageType != kIOMessageServiceIsTerminated) || !device->removalCallbackSet || !(count=CFSetGetCount(device->removalCallbackSet)))
return;
CFRetain(device);
CFDataRef dataValues[count];
bzero(dataValues, sizeof(CFDataRef) * count);
CFSetGetValues(device->removalCallbackSet, (const void **)dataValues);
for ( index=0; index<count; index++ ) {
IOHIDDeviceRemovalCallbackInfo * info;
CFDataRef infoRef;
infoRef = (CFDataRef)dataValues[index];
if ( !infoRef )
continue;
info = (IOHIDDeviceRemovalCallbackInfo *)CFDataGetBytePtr(infoRef);
if ( !info )
continue;
if (!info->callback)
continue;
info->callback(info->context, kIOReturnSuccess, device);
}
CFRelease(device);
}
IOCFPlugInInterface ** _IOHIDDeviceGetIOCFPlugInInterface(
IOHIDDeviceRef device)
{
return device->plugInInterface;
}
CFTypeID IOHIDDeviceGetTypeID(void)
{
if ( _kCFRuntimeNotATypeID == __kIOHIDDeviceTypeID )
pthread_once(&__deviceTypeInit, __IOHIDDeviceRegister);
return __kIOHIDDeviceTypeID;
}
IOHIDDeviceRef IOHIDDeviceCreate(
CFAllocatorRef allocator,
io_service_t service)
{
IOCFPlugInInterface ** plugInInterface = NULL;
IOHIDDeviceDeviceInterface ** deviceInterface = NULL;
IOHIDDeviceTimeStampedDeviceInterface ** deviceTimeStampedInterface = NULL;
IOHIDDeviceRef device = NULL;
SInt32 score = 0;
HRESULT result;
require_noerr(IOObjectRetain(service), retain_fail);
require_noerr(IOCreatePlugInInterfaceForService(service, kIOHIDDeviceTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score), plugin_fail);
require_noerr((*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOHIDDeviceDeviceInterfaceID), (LPVOID)&deviceInterface), query_fail);
result = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOHIDDeviceDeviceInterfaceID2), (LPVOID)&deviceTimeStampedInterface);
if (result != S_OK) {
deviceTimeStampedInterface = NULL;
}
device = __IOHIDDeviceCreate(allocator, NULL);
require(device, create_fail);
device->plugInInterface = plugInInterface;
device->deviceInterface = deviceInterface;
device->deviceTimeStampedInterface = deviceTimeStampedInterface;
device->service = service;
return device;
create_fail:
if (deviceInterface)
(*deviceInterface)->Release(deviceInterface);
if (deviceTimeStampedInterface)
(*deviceTimeStampedInterface)->Release(deviceInterface);
query_fail:
IODestroyPlugInInterface(plugInInterface);
plugin_fail:
IOObjectRelease(service);
retain_fail:
return NULL;
}
io_service_t IOHIDDeviceGetService(
IOHIDDeviceRef device)
{
return device->service;
}
IOReturn IOHIDDeviceOpen(
IOHIDDeviceRef device,
IOOptionBits options)
{
return (*device->deviceInterface)->open(device->deviceInterface, options);
}
IOReturn IOHIDDeviceClose(
IOHIDDeviceRef device,
IOOptionBits options)
{
if ( device->inputValueCallbackSet )
CFSetRemoveAllValues(device->inputValueCallbackSet);
if ( device->inputReportCallbackSet )
CFSetRemoveAllValues(device->inputReportCallbackSet);
return (*device->deviceInterface)->close(device->deviceInterface, options);
}
Boolean IOHIDDeviceConformsTo(
IOHIDDeviceRef device,
uint32_t usagePage,
uint32_t usage)
{
Boolean doesConform = FALSE;
CFMutableDictionaryRef matching = NULL;
matching = CFDictionaryCreateMutable(CFGetAllocator(device), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (matching) {
CFArrayRef elements = NULL;
CFNumberRef number = NULL;
uint32_t value;
value = kIOHIDElementTypeCollection;
number = CFNumberCreate(CFGetAllocator(device), kCFNumberIntType, &value);
CFDictionarySetValue(matching, CFSTR(kIOHIDElementTypeKey), number);
CFRelease(number);
value = usagePage;
number = CFNumberCreate(CFGetAllocator(device), kCFNumberIntType, &value);
CFDictionarySetValue(matching, CFSTR(kIOHIDElementUsagePageKey), number);
CFRelease(number);
value = usage;
number = CFNumberCreate(CFGetAllocator(device), kCFNumberIntType, &value);
CFDictionarySetValue(matching, CFSTR(kIOHIDElementUsageKey), number);
CFRelease(number);
elements = IOHIDDeviceCopyMatchingElements(device, matching, 0);
if ( elements ) {
CFIndex count, index;
IOHIDElementRef element;
count = CFArrayGetCount(elements);
for (index=0; index<count; index++) {
element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, index);
if ( IOHIDElementGetCollectionType(element) == kIOHIDElementCollectionTypePhysical ||
IOHIDElementGetCollectionType(element) == kIOHIDElementCollectionTypeApplication ) {
doesConform = TRUE;
break;
}
}
CFRelease(elements);
}
CFRelease(matching);
}
return doesConform;
}
CFTypeRef IOHIDDeviceGetProperty(
IOHIDDeviceRef device,
CFStringRef key)
{
CFTypeRef property = NULL;
IOReturn ret;
ret = (*device->deviceInterface)->getProperty(
device->deviceInterface,
key,
&property);
if ((ret != kIOReturnSuccess) || !property) {
if (device->properties) {
property = CFDictionaryGetValue(device->properties, key);
}
else {
property = NULL;
}
}
return property;
}
Boolean IOHIDDeviceSetProperty(
IOHIDDeviceRef device,
CFStringRef key,
CFTypeRef property)
{
if (!device->properties) {
device->properties = CFDictionaryCreateMutable(CFGetAllocator(device),
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!device->properties)
return FALSE;
}
device->isDirty = TRUE;
CFDictionarySetValue(device->properties, key, property);
return (*device->deviceInterface)->setProperty(device->deviceInterface,
key,
property) == kIOReturnSuccess;
}
CFArrayRef IOHIDDeviceCopyMatchingElements(
IOHIDDeviceRef device,
CFDictionaryRef matching,
IOOptionBits options)
{
CFArrayRef elements = NULL;
IOReturn ret;
ret = (*device->deviceInterface)->copyMatchingElements(
device->deviceInterface,
matching,
&elements,
options);
if ( ret != kIOReturnSuccess && elements ) {
CFRelease(elements);
elements = NULL;
}
if ( elements ) {
CFIndex count, index;
IOHIDElementRef element;
count = CFArrayGetCount(elements);
for (index=0; index<count; index++) {
element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, index);
_IOHIDElementSetDevice(element, device);
if (device->elements ||
(device->elements = CFSetCreateMutable(CFGetAllocator(device),
0,
&kCFTypeSetCallBacks))) {
if (!CFSetContainsValue(device->elements, element)) {
CFSetSetValue(device->elements, element);
if (device->loadProperties) {
__IOHIDElementLoadProperties(element);
}
}
}
}
}
return elements;
}
void IOHIDDeviceScheduleWithRunLoop(
IOHIDDeviceRef device,
CFRunLoopRef runLoop,
CFStringRef runLoopMode)
{
device->runLoop = runLoop;
device->runLoopMode = runLoopMode;
if ( !device->asyncEventSource) {
IOReturn ret;
ret = (*device->deviceInterface)->getAsyncEventSource(
device->deviceInterface,
&device->asyncEventSource);
if (ret != kIOReturnSuccess || !device->asyncEventSource)
return;
}
if (CFGetTypeID(device->asyncEventSource) == CFRunLoopSourceGetTypeID())
CFRunLoopAddSource( device->runLoop,
(CFRunLoopSourceRef)device->asyncEventSource,
device->runLoopMode);
else if (CFGetTypeID(device->asyncEventSource) == CFRunLoopTimerGetTypeID())
CFRunLoopAddTimer( device->runLoop,
(CFRunLoopTimerRef)device->asyncEventSource,
device->runLoopMode);
if ( device->notificationPort ) {
CFRunLoopSourceRef source = IONotificationPortGetRunLoopSource(device->notificationPort);
if (source) {
CFRunLoopAddSource(device->runLoop, source, device->runLoopMode);
}
}
if ( device->queue ) {
IOHIDQueueScheduleWithRunLoop( device->queue,
device->runLoop,
device->runLoopMode);
IOHIDQueueStart(device->queue);
}
}
void IOHIDDeviceUnscheduleFromRunLoop(
IOHIDDeviceRef device,
CFRunLoopRef runLoop,
CFStringRef runLoopMode)
{
if ( CFEqual(device->runLoop, runLoop) &&
CFEqual(device->runLoopMode, runLoopMode)) {
if ( device->queue ) {
IOHIDQueueStop(device->queue);
IOHIDQueueUnscheduleFromRunLoop(device->queue,
device->runLoop,
device->runLoopMode);
}
if ( device->notificationPort ) {
CFRunLoopSourceRef source = IONotificationPortGetRunLoopSource(device->notificationPort);
if (source) {
CFRunLoopRemoveSource(device->runLoop, source, device->runLoopMode);
}
}
if (device->asyncEventSource) {
if (CFGetTypeID(device->asyncEventSource) == CFRunLoopSourceGetTypeID())
CFRunLoopRemoveSource( device->runLoop,
(CFRunLoopSourceRef)device->asyncEventSource,
device->runLoopMode);
else if (CFGetTypeID(device->asyncEventSource) == CFRunLoopTimerGetTypeID())
CFRunLoopAddTimer( device->runLoop,
(CFRunLoopTimerRef)device->asyncEventSource,
device->runLoopMode);
}
}
}
void IOHIDDeviceRegisterRemovalCallback(
IOHIDDeviceRef device,
IOHIDCallback callback,
void * context)
{
IOHIDDeviceRemovalCallbackInfo info = { context, callback };
CFDataRef infoRef = NULL;
CFRetain(device);
if (!context)
os_log(_IOHIDLog(), "called with a null context");
if (!device->removalCallbackSet) {
device->removalCallbackSet = CFSetCreateMutable(NULL, 0, &__callbackBaseSetCallbacks);
}
require(device->removalCallbackSet, cleanup);
infoRef = CFDataCreate(CFGetAllocator(device), (const UInt8 *) &info, sizeof(info));
require(infoRef, cleanup);
if (callback) {
CFSetAddValue(device->removalCallbackSet, infoRef);
if ( !device->notificationPort ) {
kern_return_t kret = 0;
device->notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
require(device->notificationPort, cleanup);
require(device->service, cleanup);
if ( device->runLoop ) {
CFRunLoopSourceRef source = IONotificationPortGetRunLoopSource(device->notificationPort);
if (source) {
CFRunLoopAddSource(device->runLoop, source, device->runLoopMode);
}
}
kret = IOServiceAddInterestNotification(device->notificationPort, device->service, kIOGeneralInterest, (IOServiceInterestCallback)__IOHIDDeviceNotification, device, &(device->notification) );
require_noerr(kret, cleanup);
}
}
else {
CFSetRemoveValue(device->removalCallbackSet, infoRef);
}
cleanup:
CFRELEASE_IF_NOT_NULL(infoRef);
CFRelease(device);
}
CFArrayRef __IOHIDDeviceCopyMatchingInputElements(IOHIDDeviceRef device, CFArrayRef multiple)
{
CFMutableArrayRef inputElements = NULL;
CFArrayRef elements = NULL;
CFIndex index, count;
if ( multiple ) {
CFRetain(multiple);
} else {
uint32_t inputTypes[] = { kIOHIDElementTypeInput_Misc, kIOHIDElementTypeInput_Button, kIOHIDElementTypeInput_Axis, kIOHIDElementTypeInput_ScanCodes};
count = sizeof(inputTypes) / sizeof(uint32_t);
CFDictionaryRef matching[count];
bzero(matching, sizeof(CFDictionaryRef) * count);
CFStringRef key = CFSTR(kIOHIDElementTypeKey);
for ( index=0; index<count; index++ ) {
CFNumberRef number = CFNumberCreate(CFGetAllocator(device), kCFNumberIntType, &inputTypes[index]);
matching[index] = CFDictionaryCreate(CFGetAllocator(device),
(const void **)&key,
(const void **)&number,
1,
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFRelease(number);
}
multiple = CFArrayCreate(CFGetAllocator(device), (const void **)matching, count, &kCFTypeArrayCallBacks);
for ( index=0; index<count; index++ )
CFRelease(matching[index]);
}
if ( !multiple )
return NULL;
count = CFArrayGetCount( multiple );
for ( index=0; index<count; index++) {
elements = IOHIDDeviceCopyMatchingElements( device,
CFArrayGetValueAtIndex(multiple, index),
0);
if ( !elements )
continue;
if ( !inputElements )
inputElements = CFArrayCreateMutableCopy( CFGetAllocator(device),
0,
elements);
else
CFArrayAppendArray( inputElements,
elements,
CFRangeMake(0, CFArrayGetCount(elements)));
CFRelease(elements);
}
CFRelease(multiple);
return inputElements;
}
void __IOHIDDeviceRegisterMatchingInputElements(
IOHIDDeviceRef device,
IOHIDQueueRef queue,
CFArrayRef mutlipleMatch)
{
CFArrayRef elements = __IOHIDDeviceCopyMatchingInputElements(device, mutlipleMatch);
if ( !elements )
return;
CFIndex i, count;
IOHIDElementRef element;
count = CFArrayGetCount(elements);
for (i=0; i<count; i++) {
element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i);
if ( !element ||
(IOHIDElementGetReportSize(element) > (sizeof(CFIndex) << 3)))
continue;
IOHIDQueueAddElement(queue, element);
}
CFRelease(elements);
}
void IOHIDDeviceRegisterInputValueCallback(
IOHIDDeviceRef device,
IOHIDValueCallback callback,
void * context)
{
CFDataRef infoRef = NULL;
IOHIDDeviceInputElementValueCallbackInfo info = {context, callback};
CFRetain(device);
if (!context)
os_log(_IOHIDLog(), "called with a null context");
if (!device->inputValueCallbackSet) {
device->inputValueCallbackSet = CFSetCreateMutable(NULL, 0, &__callbackBaseSetCallbacks);
}
require(device->inputValueCallbackSet, cleanup);
infoRef = CFDataCreate(CFGetAllocator(device), (const UInt8 *) &info, sizeof(info));
require(infoRef, cleanup);
if (callback) {
if ( !device->queue ) {
device->queue = IOHIDQueueCreate(CFGetAllocator(device), device, 20, 0);
require(device->queue, cleanup);
__IOHIDDeviceRegisterMatchingInputElements(device, device->queue, device->inputMatchingMultiple);
if ( device->runLoop ) {
IOHIDQueueScheduleWithRunLoop(device->queue,
device->runLoop,
device->runLoopMode);
IOHIDQueueStart(device->queue);
}
}
CFSetAddValue(device->inputValueCallbackSet, infoRef);
}
else {
CFSetRemoveValue(device->inputValueCallbackSet, infoRef);
}
if (device && device->queue)
IOHIDQueueRegisterValueAvailableCallback(device->queue,
__IOHIDDeviceInputElementValueCallback,
device);
cleanup:
CFRELEASE_IF_NOT_NULL(infoRef);
CFRelease(device);
}
void IOHIDDeviceSetInputValueMatching(
IOHIDDeviceRef device,
CFDictionaryRef matching)
{
if ( matching ) {
CFArrayRef multiple = CFArrayCreate(CFGetAllocator(device), (const void **)&matching, 1, &kCFTypeArrayCallBacks);
IOHIDDeviceSetInputValueMatchingMultiple(device, multiple);
CFRelease(multiple);
} else {
IOHIDDeviceSetInputValueMatchingMultiple(device, NULL);
}
}
void IOHIDDeviceSetInputValueMatchingMultiple(
IOHIDDeviceRef device,
CFArrayRef multiple)
{
if ( device->queue ) {
IOHIDValueRef value;
CFArrayRef elements;
IOHIDQueueStop(device->queue);
while ( (value = IOHIDQueueCopyNextValue(device->queue)) )
CFRelease(value);
elements = _IOHIDQueueCopyElements(device->queue);
if ( elements ) {
CFIndex index, count;
count = CFArrayGetCount(elements);
for (index=0; index<count; index++)
IOHIDQueueRemoveElement(device->queue, (IOHIDElementRef)CFArrayGetValueAtIndex(elements, index));
CFRelease(elements);
}
__IOHIDDeviceRegisterMatchingInputElements(device, device->queue, multiple);
IOHIDQueueStart(device->queue);
}
if ( device->inputMatchingMultiple )
CFRelease(device->inputMatchingMultiple);
device->inputMatchingMultiple = multiple ? (CFArrayRef)CFRetain(multiple) : NULL;
}
IOReturn IOHIDDeviceSetValue(
IOHIDDeviceRef device,
IOHIDElementRef element,
IOHIDValueRef value)
{
return (*device->deviceInterface)->setValue(
device->deviceInterface,
element,
value,
0,
NULL,
NULL,
0);
}
IOReturn IOHIDDeviceSetValueMultiple(
IOHIDDeviceRef device,
CFDictionaryRef multiple)
{
return IOHIDDeviceSetValueMultipleWithCallback(device, multiple, 0, NULL, NULL);
}
IOReturn IOHIDDeviceSetValueWithCallback(
IOHIDDeviceRef device,
IOHIDElementRef element,
IOHIDValueRef value,
CFTimeInterval timeout,
IOHIDValueCallback callback,
void * context)
{
IOHIDDeviceCallbackInfo * elementInfo =
(IOHIDDeviceCallbackInfo *)malloc(sizeof(IOHIDDeviceCallbackInfo));
if ( !elementInfo )
return kIOReturnNoMemory;
elementInfo->device = device;
elementInfo->callback = callback;
elementInfo->context = context;
uint32_t timeoutMS = timeout * 1000;
IOReturn ret = (*device->deviceInterface)->setValue(
device->deviceInterface,
element,
value,
timeoutMS,
__IOHIDDeviceValueCallback,
elementInfo,
0);
if ( ret != kIOReturnSuccess )
free(elementInfo);
return ret;
}
IOReturn IOHIDDeviceSetValueMultipleWithCallback(
IOHIDDeviceRef device,
CFDictionaryRef multiple,
CFTimeInterval timeout,
IOHIDValueMultipleCallback callback,
void * context)
{
IOReturn ret = kIOReturnError;
IOHIDElementRef * elements = NULL;
IOHIDValueRef * values = NULL;
IOHIDTransactionRef transaction = NULL;
CFIndex index, count;
if ( !multiple || ((count = CFDictionaryGetCount(multiple)) == 0))
return kIOReturnBadArgument;
do {
transaction = IOHIDTransactionCreate( CFGetAllocator(device),
device,
kIOHIDTransactionDirectionTypeOutput,
0);
if ( !transaction ) {
ret = kIOReturnNoMemory;
break;
}
elements = (IOHIDElementRef *)malloc(count * sizeof(IOHIDElementRef));
if ( !elements ) {
ret = kIOReturnNoMemory;
break;
}
values = (IOHIDValueRef *)malloc(count * sizeof(IOHIDValueRef));
if ( !values ) {
ret = kIOReturnNoMemory;
break;
}
CFDictionaryGetKeysAndValues(multiple, (const void **)elements, (const void **)values);
for ( index=0; index<count; index++ ) {
IOHIDTransactionAddElement(transaction, elements[index]);
IOHIDTransactionSetValue(transaction, elements[index], values[index], 0);
}
if ( callback ) { IOHIDDeviceTransactionCallbackInfo * elementInfo =
(IOHIDDeviceTransactionCallbackInfo *)malloc(sizeof(IOHIDDeviceTransactionCallbackInfo));
if ( !elementInfo ) {
ret = kIOReturnNoMemory;
break;
}
elementInfo->device = device;
elementInfo->callback = callback;
elementInfo->context = context;
elementInfo->elements = CFArrayCreate(CFGetAllocator(device), (const void **)elements, count, &kCFTypeArrayCallBacks);
ret = IOHIDTransactionCommitWithCallback(transaction, timeout, __IOHIDDeviceTransactionCallback, elementInfo);
if ( ret != kIOReturnSuccess )
free(elementInfo);
} else { ret = IOHIDTransactionCommit(transaction);
}
} while ( FALSE );
if ( transaction )
CFRelease(transaction);
if ( elements )
free(elements);
if ( values )
free(values);
return ret;
}
IOReturn IOHIDDeviceGetValue(
IOHIDDeviceRef device,
IOHIDElementRef element,
IOHIDValueRef * pValue)
{
return (*device->deviceInterface)->getValue(
device->deviceInterface,
element,
pValue,
0,
NULL,
NULL,
0);
}
IOReturn IOHIDDeviceCopyValueMultiple(
IOHIDDeviceRef device,
CFArrayRef elements,
CFDictionaryRef * pMultiple)
{
return IOHIDDeviceCopyValueMultipleWithCallback(device,
elements,
pMultiple,
0,
NULL,
NULL);
}
IOReturn IOHIDDeviceGetValueWithCallback(
IOHIDDeviceRef device,
IOHIDElementRef element,
IOHIDValueRef * pValue,
CFTimeInterval timeout,
IOHIDValueCallback callback,
void * context)
{
IOHIDDeviceCallbackInfo * elementInfo =
(IOHIDDeviceCallbackInfo *)malloc(sizeof(IOHIDDeviceCallbackInfo));
if ( !elementInfo )
return kIOReturnNoMemory;
elementInfo->device = device;
elementInfo->callback = callback;
elementInfo->context = context;
uint32_t timeoutMS = timeout * 1000;
IOReturn ret = (*device->deviceInterface)->getValue(
device->deviceInterface,
element,
pValue,
timeoutMS,
__IOHIDDeviceValueCallback,
elementInfo,
0);
if ( ret != kIOReturnSuccess )
free(elementInfo);
return ret;
}
IOReturn IOHIDDeviceCopyValueMultipleWithCallback(
IOHIDDeviceRef device,
CFArrayRef elements,
CFDictionaryRef * pMultiple,
CFTimeInterval timeout,
IOHIDValueMultipleCallback callback,
void * context)
{
IOReturn ret = kIOReturnError;
IOHIDTransactionRef transaction = NULL;
IOHIDValueRef value = NULL;
CFIndex index, count;
if ( !pMultiple || !elements || ((count = CFArrayGetCount(elements)) == 0))
return kIOReturnBadArgument;
do {
transaction = IOHIDTransactionCreate( CFGetAllocator(device),
device,
kIOHIDTransactionDirectionTypeInput,
0);
if ( !transaction ) {
ret = kIOReturnNoMemory;
break;
}
for ( index=0; index<count; index++ )
IOHIDTransactionAddElement(transaction, (IOHIDElementRef)CFArrayGetValueAtIndex(elements, index));
if ( callback ) { IOHIDDeviceTransactionCallbackInfo * elementInfo =
(IOHIDDeviceTransactionCallbackInfo *)malloc(sizeof(IOHIDDeviceTransactionCallbackInfo));
if ( !elementInfo ) {
ret = kIOReturnNoMemory;
break;
}
elementInfo->device = device;
elementInfo->callback = callback;
elementInfo->context = context;
elementInfo->elements = CFArrayCreateCopy(CFGetAllocator(device), elements);
ret = IOHIDTransactionCommitWithCallback(transaction, timeout, __IOHIDDeviceTransactionCallback, elementInfo);
if ( ret != kIOReturnSuccess )
free(elementInfo);
} else {
ret = IOHIDTransactionCommit(transaction);
if ( ret != kIOReturnSuccess )
break;
CFMutableDictionaryRef multiple = CFDictionaryCreateMutable(
CFGetAllocator(device),
count,
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if ( !multiple ) {
ret = kIOReturnNoMemory;
break;
}
for ( index = 0; index<count; index++ ) {
IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, index);
value = IOHIDTransactionGetValue(transaction, element, 0);
if ( !value )
continue;
CFDictionarySetValue(multiple, element, value);
}
if ( CFDictionaryGetCount(multiple) == 0 ) {
CFRelease(multiple);
multiple = NULL;
}
*pMultiple = multiple;
}
} while ( FALSE );
if ( transaction )
CFRelease(transaction);
return ret;
}
void __IOHIDDeviceValueCallback(
void * context,
IOReturn result,
void * sender __unused,
IOHIDValueRef value)
{
IOHIDDeviceCallbackInfo * elementInfo = (IOHIDDeviceCallbackInfo *)context;
IOHIDDeviceRef device = elementInfo->device;
if ( elementInfo->callback ) {
IOHIDValueCallback callback = elementInfo->callback;
(*callback)(elementInfo->context,
result,
device,
value);
}
free(elementInfo);
}
void __IOHIDDeviceInputElementValueCallback(
void * context,
IOReturn result,
void * sender __unused)
{
IOHIDDeviceRef device = (IOHIDDeviceRef)context;
IOHIDQueueRef queue = (IOHIDQueueRef)sender;
IOHIDValueRef value = NULL;
if ( (queue != device->queue) || (kIOReturnSuccess != result) )
return;
CFRetain(device);
CFIndex count = CFSetGetCount(device->inputValueCallbackSet);
if ( count ) {
CFDataRef dataValues[count];
CFIndex index = 0;
bzero(dataValues, sizeof(CFDataRef) * count);
CFSetGetValues(device->inputValueCallbackSet, (const void **)dataValues);
while ( (value = IOHIDQueueCopyNextValue(queue)) ) {
for ( index=0; index<count; index++ ) {
CFDataRef infoRef = (CFDataRef)dataValues[index];
IOHIDDeviceInputElementValueCallbackInfo *info;
if ( !infoRef )
continue;
info = (IOHIDDeviceInputElementValueCallbackInfo *)CFDataGetBytePtr(infoRef);
if (info->callback)
info->callback(info->context, kIOReturnSuccess, device, value);
}
CFRelease(value);
}
}
CFRelease(device);
}
void __IOHIDDeviceTransactionCallback(
void * context,
IOReturn result,
void * sender)
{
IOHIDTransactionRef transaction = (IOHIDTransactionRef)sender;
IOHIDDeviceTransactionCallbackInfo *elementInfo = (IOHIDDeviceTransactionCallbackInfo *)context;
IOHIDDeviceRef device = elementInfo->device;
IOHIDValueMultipleCallback callback = elementInfo->callback;
CFMutableDictionaryRef values = NULL;
do {
if ( !transaction )
break;
if ( !callback )
break;
if ( result == kIOReturnSuccess ) {
if ( !elementInfo->elements )
break;
CFIndex count = CFArrayGetCount(elementInfo->elements);
CFIndex index = 0;
IOHIDElementRef element = NULL;
IOHIDValueRef value = NULL;
if ( count )
values = CFDictionaryCreateMutable(
CFGetAllocator(device),
count,
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if ( !values )
break;
for ( index = 0; index<count; index++ ) {
element = (IOHIDElementRef)CFArrayGetValueAtIndex(elementInfo->elements, index);
if ( !element )
continue;
value = IOHIDTransactionGetValue(transaction, element, 0);
if ( !value )
continue;
CFDictionarySetValue(values, element, value);
}
if ( CFDictionaryGetCount(values) == 0 ) {
CFRelease(values);
values = NULL;
}
}
(*callback)(elementInfo->context, result, device, values);
} while ( FALSE );
if ( values )
CFRelease(values);
if ( elementInfo->elements )
CFRelease(elementInfo->elements);
free(elementInfo);
}
void __IOHIDDeviceReportCallbackOnce(void * context,
IOReturn result,
void * sender,
IOHIDReportType type,
uint32_t reportID,
uint8_t * report,
CFIndex reportLength)
{
IOHIDDeviceReportCallbackInfo *info = (IOHIDDeviceReportCallbackInfo *)context;
IOHIDDeviceRef device = info->device;
CFRetain(device);
if ( info->callback && sender == device->deviceInterface ) {
info->callback(info->context,
result,
device,
type,
reportID,
report,
reportLength);
}
else if ( info->callbackWithTimeStamp && sender == device->deviceTimeStampedInterface ) {
info->callbackWithTimeStamp(info->context,
result,
device,
type,
reportID,
report,
reportLength,
0);
}
free(context);
CFRelease(device);
}
void __IOHIDDeviceInputReportApplier(CFDataRef value, void *voidContext)
{
if (value && voidContext) {
IOHIDDeviceReportCallbackInfo *info = (IOHIDDeviceReportCallbackInfo *)CFDataGetBytePtr(value);
IOHIDDeviceInputReportApplierContext *context = (IOHIDDeviceInputReportApplierContext*)voidContext;
if ( info ) {
if (info->callback) {
info->callback(info->context,
context->result,
context->sender,
context->type,
context->reportID,
context->report,
context->reportLength);
}
if (info->callbackWithTimeStamp) {
info->callbackWithTimeStamp(info->context,
context->result,
context->sender,
context->type,
context->reportID,
context->report,
context->reportLength,
context->timeStamp);
}
}
}
}
void __IOHIDDeviceInputReportCallback( void * context,
IOReturn result,
void * sender __unused,
IOHIDReportType type,
uint32_t reportID,
uint8_t * report,
CFIndex reportLength)
{
IOHIDDeviceRef device = (IOHIDDeviceRef)context;
CFIndex count;
if (device && device->inputReportCallbackSet && (count = CFSetGetCount(device->inputReportCallbackSet)) ) {
IOHIDDeviceInputReportApplierContext applierContext = {
context, result, device, type, reportID, report, reportLength, 0
};
CFRetain(device);
CFSetApplyFunction(device->inputReportCallbackSet,
(CFSetApplierFunction)__IOHIDDeviceInputReportApplier,
&applierContext);
CFRelease(device);
}
}
void __IOHIDDeviceInputReportWithTimeStampCallback(void * context,
IOReturn result,
void * sender __unused,
IOHIDReportType type,
uint32_t reportID,
uint8_t * report,
CFIndex reportLength,
uint64_t timeStamp)
{
IOHIDDeviceRef device = (IOHIDDeviceRef)context;
if (device && device->inputReportCallbackSet && CFSetGetCount(device->inputReportCallbackSet) ) {
IOHIDDeviceInputReportApplierContext applierContext = {
context, result, device, type, reportID, report, reportLength, timeStamp
};
CFRetain(device);
CFSetApplyFunction(device->inputReportCallbackSet,
(CFSetApplierFunction)__IOHIDDeviceInputReportApplier,
&applierContext);
CFRelease(device);
}
}
Boolean __IOHIDDeviceCallbackBaseDataIsEqual(const void * value1, const void * value2)
{
IOHIDDeviceCallbackBaseInfo * info1, * info2;
CFDataRef data1 = (CFDataRef)value1;
CFDataRef data2 = (CFDataRef)value2;
Boolean result = false;
require_action_quiet(data1!=data2, exit, result=true);
require_action_quiet(data1&&data2, exit, result=false);
info1 = (IOHIDDeviceCallbackBaseInfo*)CFDataGetBytePtr(data1);
info2 = (IOHIDDeviceCallbackBaseInfo*)CFDataGetBytePtr(data2);
require_action_quiet(info1 && info2, exit, result=false);
result = info1->context == info2->context;
exit:
return result;
}
void __IOHIDDeviceRegisterInputReportCallback(IOHIDDeviceRef device,
uint8_t * report,
CFIndex reportLength,
IOHIDReportCallback callback,
IOHIDReportWithTimeStampCallback callbackWithTimeStamp,
void * context)
{
IOHIDDeviceReportCallbackInfo info = {context, callback, callbackWithTimeStamp, device};
CFDataRef infoRef = NULL;
CFRetain(device);
if (!context)
os_log(_IOHIDLog(), "called with a null context");
if (!device->inputReportCallbackSet) {
device->inputReportCallbackSet = CFSetCreateMutable(NULL, 0, &__callbackBaseSetCallbacks);
}
require(device->inputReportCallbackSet, cleanup);
infoRef = CFDataCreate(CFGetAllocator(device), (const UInt8 *) &info, sizeof(info));
require(infoRef, cleanup);
if (callback || callbackWithTimeStamp) {
CFSetAddValue(device->inputReportCallbackSet, infoRef);
if (device->deviceTimeStampedInterface) {
(*device->deviceTimeStampedInterface)->
setInputReportWithTimeStampCallback(device->deviceInterface,
report,
reportLength,
__IOHIDDeviceInputReportWithTimeStampCallback,
device,
0);
}
else {
(*device->deviceInterface)->setInputReportCallback(device->deviceInterface,
report,
reportLength,
__IOHIDDeviceInputReportCallback,
device,
0);
}
}
else {
CFSetRemoveValue(device->inputReportCallbackSet, infoRef);
}
cleanup:
CFRELEASE_IF_NOT_NULL(infoRef);
CFRelease(device);
}
void IOHIDDeviceRegisterInputReportCallback(IOHIDDeviceRef device,
uint8_t * report,
CFIndex reportLength,
IOHIDReportCallback callback,
void * context)
{
__IOHIDDeviceRegisterInputReportCallback(device, report, reportLength, callback, NULL, context);
}
void IOHIDDeviceRegisterInputReportWithTimeStampCallback(IOHIDDeviceRef device,
uint8_t * report,
CFIndex reportLength,
IOHIDReportWithTimeStampCallback callback,
void * context)
{
__IOHIDDeviceRegisterInputReportCallback(device, report, reportLength, NULL, callback, context);
}
IOReturn IOHIDDeviceSetReport(
IOHIDDeviceRef device,
IOHIDReportType reportType,
CFIndex reportID,
const uint8_t * report,
CFIndex reportLength)
{
return (*device->deviceInterface)->setReport(
device->deviceInterface,
reportType,
reportID,
report,
reportLength,
0,
NULL,
NULL,
0);
}
IOReturn IOHIDDeviceSetReportWithCallback(
IOHIDDeviceRef device,
IOHIDReportType reportType,
CFIndex reportID,
const uint8_t * report,
CFIndex reportLength,
CFTimeInterval timeout,
IOHIDReportCallback callback,
void * context)
{
IOHIDDeviceReportCallbackInfo *info_ptr = (IOHIDDeviceReportCallbackInfo*)malloc(sizeof(IOHIDDeviceReportCallbackInfo));
if ( !info_ptr )
return kIOReturnNoMemory;
info_ptr->context = context;
info_ptr->callback = callback;
info_ptr->device = device;
uint32_t timeoutMS = timeout * 1000;
IOReturn result = (*device->deviceInterface)->setReport(device->deviceInterface,
reportType,
reportID,
report,
reportLength,
timeoutMS,
__IOHIDDeviceReportCallbackOnce,
info_ptr,
0);
if (result)
free(info_ptr);
return result;
}
IOReturn IOHIDDeviceGetReport(
IOHIDDeviceRef device,
IOHIDReportType reportType,
CFIndex reportID,
uint8_t * report,
CFIndex * pReportLength)
{
return (*device->deviceInterface)->getReport(
device->deviceInterface,
reportType,
reportID,
report,
pReportLength,
0,
NULL,
NULL,
0);
}
IOReturn IOHIDDeviceGetReportWithCallback(
IOHIDDeviceRef device,
IOHIDReportType reportType,
CFIndex reportID,
uint8_t * report,
CFIndex * pReportLength,
CFTimeInterval timeout,
IOHIDReportCallback callback,
void * context)
{
IOHIDDeviceReportCallbackInfo info = {
context,
callback,
NULL,
device
};
void *info_ptr = malloc(sizeof(info));
if ( !info_ptr )
return kIOReturnNoMemory;
memcpy(info_ptr, &info, sizeof(info));
uint32_t timeoutMS = timeout * 1000;
IOReturn result = (*device->deviceInterface)->getReport(device->deviceInterface,
reportType,
reportID,
report,
pReportLength,
timeoutMS,
__IOHIDDeviceReportCallbackOnce,
(void *) info_ptr,
0);
if (result)
free(info_ptr);
return result;
}
CFStringRef __IOHIDDeviceGetRootKey(IOHIDDeviceRef device)
{
if (!device->rootKey) {
CFStringRef manager = __IOHIDManagerGetRootKey();
CFStringRef transport = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDTransportKey));
CFNumberRef vendor = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey));
CFNumberRef product = (CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey));
CFStringRef serial = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDSerialNumberKey));
if (!transport || (CFGetTypeID(transport) != CFStringGetTypeID())) {
transport = CFSTR("unknown");
}
if (!vendor || (CFGetTypeID(vendor) != CFNumberGetTypeID())) {
vendor = kCFNumberNaN;
}
if (!product || (CFGetTypeID(product) != CFNumberGetTypeID())) {
product = kCFNumberNaN;
}
if (!serial || (CFGetTypeID(serial) != CFStringGetTypeID())) {
serial = CFSTR("none");
}
device->rootKey = CFStringCreateWithFormat(NULL, NULL,
CFSTR("%@#%@#%@#%@#%@"),
manager,
transport,
vendor,
product,
serial);
}
return device->rootKey;
}
CFStringRef __IOHIDDeviceGetUUIDString(IOHIDDeviceRef device)
{
CFStringRef uuidStr = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDManagerUUIDKey));
if (!uuidStr || (CFGetTypeID(uuidStr) != CFStringGetTypeID())) {
CFUUIDRef uuid = CFUUIDCreate(NULL);
uuidStr = CFUUIDCreateString(NULL, uuid);
IOHIDDeviceSetProperty(device, CFSTR(kIOHIDManagerUUIDKey), uuidStr);
CFRelease(uuid);
CFRelease(uuidStr);
}
return uuidStr;
}
CFStringRef __IOHIDDeviceGetUUIDKey(IOHIDDeviceRef device)
{
if (!device->UUIDKey) {
CFStringRef manager = __IOHIDManagerGetRootKey();
CFStringRef uuidStr = __IOHIDDeviceGetUUIDString(device);
device->UUIDKey = CFStringCreateWithFormat(NULL, NULL,
CFSTR("%@#%@"),
manager,
uuidStr);
}
return device->UUIDKey;
}
void __IOHIDDeviceSaveProperties(IOHIDDeviceRef device, __IOHIDPropertyContext *context)
{
if (device->isDirty && device->properties) {
CFStringRef uuidStr = __IOHIDDeviceGetUUIDString(device);
CFArrayRef uuids = (CFArrayRef)CFPreferencesCopyAppValue(__IOHIDDeviceGetRootKey(device), kCFPreferencesCurrentApplication);
CFArrayRef uuidsToWrite = NULL;
if (uuids && (CFGetTypeID(uuids) == CFArrayGetTypeID())) {
CFRange range = { 0, CFArrayGetCount(uuids) };
if (!CFArrayContainsValue(uuids, range, uuidStr)) {
uuidsToWrite = CFArrayCreateMutableCopy(NULL, CFArrayGetCount(uuids) + 1, uuids);
CFArrayAppendValue((CFMutableArrayRef)uuidsToWrite, uuidStr);
}
}
else {
uuidsToWrite = CFArrayCreate(NULL, (const void**)&uuidStr, 1, &kCFTypeArrayCallBacks);
}
if (uuidsToWrite) {
__IOHIDPropertySaveWithContext(__IOHIDDeviceGetRootKey(device), uuidsToWrite, context);
}
CFRELEASE_IF_NOT_NULL(uuids);
CFRELEASE_IF_NOT_NULL(uuidsToWrite);
__IOHIDPropertySaveToKeyWithSpecialKeys(device->properties,
__IOHIDDeviceGetUUIDKey(device),
NULL,
context);
device->isDirty = FALSE;
}
if (device->elements)
CFSetApplyFunction(device->elements, __IOHIDSaveElementSet, context);
}
void __IOHIDDeviceLoadProperties(IOHIDDeviceRef device)
{
CFStringRef uuidStr = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDManagerUUIDKey));
device->loadProperties = TRUE;
if (!device->UUIDKey) {
if (!uuidStr || (CFGetTypeID(uuidStr) != CFStringGetTypeID())) {
CFArrayRef uuids = (CFArrayRef)CFPreferencesCopyAppValue(__IOHIDDeviceGetRootKey(device), kCFPreferencesCurrentApplication);
if (uuids && (CFGetTypeID(uuids) == CFArrayGetTypeID()) && CFArrayGetCount(uuids)) {
uuidStr = (CFStringRef)CFArrayGetValueAtIndex(uuids, 0);
}
}
if (!uuidStr || (CFGetTypeID(uuidStr) != CFStringGetTypeID())) {
CFUUIDRef uuid = CFUUIDCreate(NULL);
uuidStr = CFUUIDCreateString(NULL, uuid);
CFRelease(uuid);
}
IOHIDDeviceSetProperty(device, CFSTR(kIOHIDManagerUUIDKey), uuidStr);
}
CFMutableDictionaryRef properties = __IOHIDPropertyLoadDictionaryFromKey(__IOHIDDeviceGetUUIDKey(device));
if (properties) {
CFRELEASE_IF_NOT_NULL(device->properties);
device->properties = properties;
device->isDirty = FALSE;
}
IOHIDDeviceSetProperty(device, CFSTR(kIOHIDManagerUUIDKey), uuidStr);
if (device->elements)
CFSetApplyFunction(device->elements, __IOHIDLoadElementSet, NULL);
}
void __IOHIDApplyPropertyToDeviceSet(const void *value, void *context) {
IOHIDDeviceRef device = (IOHIDDeviceRef)value;
__IOHIDApplyPropertyToSetContext *data = (__IOHIDApplyPropertyToSetContext*)context;
if (device && data) {
IOHIDDeviceSetProperty(device, data->key, data->property);
}
}
void __IOHIDApplyPropertiesToDeviceFromDictionary(const void *key, const void *value, void *context) {
IOHIDDeviceRef device = (IOHIDDeviceRef)context;
if (device && key && value) {
IOHIDDeviceSetProperty(device, (CFStringRef)key, (CFTypeRef)value);
}
}
void __IOHIDSaveDeviceSet(const void *value, void *context) {
IOHIDDeviceRef device = (IOHIDDeviceRef)value;
if (device)
__IOHIDDeviceSaveProperties(device, (__IOHIDPropertyContext*)context);
}