#include <pthread.h>
#include <CoreFoundation/CFRuntime.h>
#include <IOKit/IOKitLib.h>
#include "IOHIDLibPrivate.h"
#include "IOHIDDevice.h"
#include "IOHIDLib.h"
static IOHIDManagerRef __IOHIDManagerCreate(
CFAllocatorRef allocator,
CFAllocatorContext * context __unused);
static void __IOHIDManagerRelease( CFTypeRef object );
static void __IOHIDManagerSetDeviceMatching(
IOHIDManagerRef manager,
CFDictionaryRef matching);
static void __IOHIDManagerDeviceAdded(
void * context,
io_iterator_t iterator);
static void __IOHIDManagerDeviceRemoved(
void * context,
IOReturn result,
void * sender);
static void __IOHIDManagerDeviceApplier(
const void * value,
void * context);
static void __IOHIDManagerInitialEnumCallback(
void * info);
static void __MergeDictionaries(
CFDictionaryRef srcDict,
CFMutableDictionaryRef * pDstDict);
enum {
kDeviceApplierOpen = 1 << 0,
kDeviceApplierClose = 1 << 1,
kDeviceApplierInitEnumCallback = 1 << 2,
kDeviceApplierSetInputMatching = 1 << 3,
kDeviceApplierSetInputCallback = 1 << 4,
kDeviceApplierSetInputReportCallback = 1 << 5,
kDeviceApplierScheduleRunLoop = 1 << 6,
kDeviceApplierUnscheduleRunLoop = 1 << 7
};
typedef struct __DeviceApplierArgs {
IOHIDManagerRef manager;
IOOptionBits options;
IOReturn retVal;
}DeviceApplierArgs;
typedef struct __IOHIDManager
{
CFRuntimeBase cfBase;
CFMutableSetRef devices;
CFMutableSetRef iterators;
CFMutableDictionaryRef properties;
CFMutableDictionaryRef deviceInputBuffers;
IONotificationPortRef notifyPort;
CFRunLoopRef runLoop;
CFStringRef runLoopMode;
CFRunLoopSourceRef initEnumRunLoopSource;
CFMutableDictionaryRef initRetVals;
Boolean isOpen;
IOOptionBits options;
void * inputContext;
IOHIDValueCallback inputCallback;
void * reportContext;
IOHIDReportCallback reportCallback;
void * matchContext;
IOHIDDeviceCallback matchCallback;
void * removalContext;
IOHIDDeviceCallback removalCallback;
CFArrayRef inputMatchingMultiple;
} __IOHIDManager, *__IOHIDManagerRef;
static const CFRuntimeClass __IOHIDManagerClass = {
0, "IOHIDManager", NULL, NULL, __IOHIDManagerRelease, NULL, NULL, NULL, NULL,
NULL
};
static pthread_once_t __sessionTypeInit = PTHREAD_ONCE_INIT;
static CFTypeID __kIOHIDManagerTypeID = _kCFRuntimeNotATypeID;
void __IOHIDManagerRegister(void)
{
__kIOHIDManagerTypeID = _CFRuntimeRegisterClass(&__IOHIDManagerClass);
}
IOHIDManagerRef __IOHIDManagerCreate(
CFAllocatorRef allocator,
CFAllocatorContext * context __unused)
{
IOHIDManagerRef manager = NULL;
void * offset = NULL;
uint32_t size;
size = sizeof(__IOHIDManager) - sizeof(CFRuntimeBase);
manager = (IOHIDManagerRef)_CFRuntimeCreateInstance(
allocator,
IOHIDManagerGetTypeID(),
size,
NULL);
if (!manager)
return NULL;
offset = manager;
bzero(offset + sizeof(CFRuntimeBase), size);
return manager;
}
void __IOHIDManagerRelease( CFTypeRef object )
{
IOHIDManagerRef manager = (IOHIDManagerRef)object;
if ( manager->isOpen )
IOHIDManagerClose(manager, manager->options);
if ( manager->notifyPort ) {
IONotificationPortDestroy(manager->notifyPort);
manager->notifyPort = NULL;
}
if ( manager->devices ) {
CFRelease(manager->devices);
manager->devices = NULL;
}
if ( manager->deviceInputBuffers ) {
CFRelease(manager->deviceInputBuffers);
manager->deviceInputBuffers = NULL;
}
if ( manager->iterators ) {
CFRelease(manager->iterators);
manager->iterators = NULL;
}
if ( manager->properties ) {
CFRelease(manager->properties);
manager->properties = NULL;
}
if ( manager->initEnumRunLoopSource ) {
CFRelease(manager->initEnumRunLoopSource);
manager->initEnumRunLoopSource = NULL;
}
if ( manager->initRetVals ) {
CFRelease(manager->initRetVals);
manager->initRetVals = NULL;
}
if ( manager->inputMatchingMultiple ) {
CFRelease(manager->inputMatchingMultiple);
manager->inputMatchingMultiple = NULL;
}
}
void __IOHIDManagerSetDeviceMatching(
IOHIDManagerRef manager,
CFDictionaryRef matching)
{
CFMutableDictionaryRef matchingDict = NULL;
io_iterator_t iterator;
IOReturn kr;
if (!manager->notifyPort) {
manager->notifyPort = IONotificationPortCreate(kIOMasterPortDefault);
if (manager->runLoop)
CFRunLoopAddSource(
manager->runLoop,
IONotificationPortGetRunLoopSource(manager->notifyPort),
manager->runLoopMode);
}
matchingDict = IOServiceMatching(kIOHIDDeviceKey);
if ( !matchingDict )
return;
__MergeDictionaries(matching, &matchingDict);
kr = IOServiceAddMatchingNotification(manager->notifyPort,
kIOFirstMatchNotification,
matchingDict,
__IOHIDManagerDeviceAdded,
manager,
&iterator
);
if ( kr != kIOReturnSuccess )
return;
if ( !manager->iterators ) {
CFSetCallBacks callbacks;
bzero(&callbacks, sizeof(CFSetCallBacks));
callbacks.retain = _IOObjectCFRetain;
callbacks.release = _IOObjectCFRelease;
manager->iterators = CFSetCreateMutable(
kCFAllocatorDefault,
0,
&callbacks);
if ( !manager->iterators )
return;
}
CFSetAddValue(manager->iterators, (void *)iterator);
__IOHIDManagerDeviceAdded(manager, iterator);
}
void __IOHIDManagerDeviceAdded( void * refcon,
io_iterator_t iterator)
{
IOHIDManagerRef manager = (IOHIDManagerRef)refcon;
IOHIDDeviceRef device;
IOReturn retVal;
io_service_t service;
Boolean initial = FALSE;
while (( service = IOIteratorNext(iterator) )) {
device = IOHIDDeviceCreate(kCFAllocatorDefault, service);
if ( device ) {
if ( !manager->devices ) {
manager->devices = CFSetCreateMutable(
kCFAllocatorDefault,
0,
&kCFTypeSetCallBacks);
initial = TRUE;
if ( manager->isOpen )
manager->initRetVals = CFDictionaryCreateMutable(
kCFAllocatorDefault,
0,
&kCFTypeDictionaryKeyCallBacks,
NULL);
}
if ( manager->devices )
CFSetAddValue(manager->devices, device);
CFRelease(device);
DeviceApplierArgs args;
args.manager = manager;
args.options = 0;
IOHIDDeviceRegisterRemovalCallback(
device,
__IOHIDManagerDeviceRemoved,
manager);
retVal = kIOReturnSuccess;
if ( manager->isOpen )
args.options |= kDeviceApplierOpen;
if ( manager->inputMatchingMultiple )
args.options |= kDeviceApplierSetInputMatching;
if ( manager->inputCallback )
args.options |= kDeviceApplierSetInputCallback;
if ( manager->reportCallback )
args.options |= kDeviceApplierSetInputReportCallback;
if ( manager->runLoop ) {
args.options |= kDeviceApplierScheduleRunLoop;
if ( !initial && manager->matchCallback )
args.options |= kDeviceApplierInitEnumCallback;
}
__IOHIDManagerDeviceApplier((const void *)device, &args);
}
IOObjectRelease(service);
}
if ( initial ) {
CFRunLoopSourceContext context;
bzero(&context, sizeof(CFRunLoopSourceContext));
context.info = manager;
context.perform = __IOHIDManagerInitialEnumCallback;
manager->initEnumRunLoopSource = CFRunLoopSourceCreate(
kCFAllocatorDefault,
0,
&context);
if ( manager->runLoop && manager->initEnumRunLoopSource ) {
CFRunLoopAddSource(
manager->runLoop,
manager->initEnumRunLoopSource,
manager->runLoopMode);
CFRunLoopSourceSignal(manager->initEnumRunLoopSource);
}
}
}
void __IOHIDManagerDeviceRemoved( void * context,
IOReturn result __unused,
void * sender)
{
IOHIDManagerRef manager = (IOHIDManagerRef)context;
if ( manager->deviceInputBuffers )
CFDictionaryRemoveValue(manager->deviceInputBuffers, sender);
CFSetRemoveValue(manager->devices, sender);
if ( manager->removalCallback )
(*manager->removalCallback)(manager->removalContext,
kIOReturnSuccess,
manager,
sender);
}
void __IOHIDManagerInitialEnumCallback(void * info)
{
IOHIDManagerRef manager = (IOHIDManagerRef)info;
if ( manager->matchCallback && manager->devices ) {
DeviceApplierArgs args;
args.manager = manager;
args.options = kDeviceApplierInitEnumCallback;
CFSetApplyFunction( manager->devices,
__IOHIDManagerDeviceApplier,
&args);
}
CFRunLoopRemoveSource( manager->runLoop,
manager->initEnumRunLoopSource,
manager->runLoopMode);
CFRelease(manager->initEnumRunLoopSource);
manager->initEnumRunLoopSource = NULL;
if ( manager->initRetVals ) {
CFRelease(manager->initRetVals);
manager->initRetVals = NULL;
}
}
void __IOHIDManagerDeviceApplier(
const void * value,
void * context)
{
DeviceApplierArgs * args = (DeviceApplierArgs*)context;
IOHIDDeviceRef device = (IOHIDDeviceRef)value;
IOReturn retVal = kIOReturnSuccess;
if ( args->options & kDeviceApplierOpen ) {
retVal = IOHIDDeviceOpen( device,
args->manager->options);
if ( args->manager->initRetVals )
CFDictionarySetValue(args->manager->initRetVals, device, (void*)retVal);
}
if ( args->options & kDeviceApplierClose )
retVal = IOHIDDeviceClose( device,
args->manager->options);
if ( args->options & kDeviceApplierInitEnumCallback ) {
if ( args->manager->initRetVals )
retVal = (IOReturn)CFDictionaryGetValue(
args->manager->initRetVals,
device);
(*args->manager->matchCallback)( args->manager->matchContext,
retVal,
args->manager,
device);
}
if ( args->options & kDeviceApplierSetInputMatching )
IOHIDDeviceSetInputValueMatchingMultiple(
device,
args->manager->inputMatchingMultiple);
if ( args->options & kDeviceApplierSetInputCallback )
IOHIDDeviceRegisterInputValueCallback(
device,
args->manager->inputCallback,
args->manager->inputContext);
if ( args->options & kDeviceApplierSetInputReportCallback ) {
CFMutableDataRef dataRef = NULL;
if ( !args->manager->deviceInputBuffers ) {
args->manager->deviceInputBuffers = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
dataRef = (CFMutableDataRef)CFDictionaryGetValue(args->manager->deviceInputBuffers, device);
if ( !dataRef ) {
CFNumberRef number = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDMaxInputReportSizeKey));
CFIndex length = 64;
if ( number ) {
CFNumberGetValue(number, kCFNumberCFIndexType, &length);
}
dataRef = CFDataCreateMutable(kCFAllocatorDefault, length);
if ( dataRef ) {
CFDataSetLength(dataRef, length);
CFDictionarySetValue(args->manager->deviceInputBuffers, device, dataRef);
CFRelease(dataRef);
}
}
IOHIDDeviceRegisterInputReportCallback(
device,
(uint8_t *)CFDataGetMutableBytePtr(dataRef),
CFDataGetLength(dataRef),
args->manager->reportCallback,
args->manager->reportContext);
}
if ( args->options & kDeviceApplierScheduleRunLoop )
IOHIDDeviceScheduleWithRunLoop( device,
args->manager->runLoop,
args->manager->runLoopMode);
if ( args->options & kDeviceApplierUnscheduleRunLoop )
IOHIDDeviceUnscheduleFromRunLoop( device,
args->manager->runLoop,
args->manager->runLoopMode);
if ( args->retVal == kIOReturnSuccess && retVal != kIOReturnSuccess )
args->retVal = retVal;
}
CFTypeID IOHIDManagerGetTypeID(void)
{
if ( _kCFRuntimeNotATypeID == __kIOHIDManagerTypeID )
pthread_once(&__sessionTypeInit, __IOHIDManagerRegister);
return __kIOHIDManagerTypeID;
}
IOHIDManagerRef IOHIDManagerCreate(
CFAllocatorRef allocator,
IOOptionBits options __unused)
{
IOHIDManagerRef manager;
manager = __IOHIDManagerCreate(allocator, NULL);
if (!manager)
return NULL;
return manager;
}
IOReturn IOHIDManagerOpen(
IOHIDManagerRef manager,
IOOptionBits options)
{
IOReturn retVal = kIOReturnSuccess;
if ( !manager->isOpen ) {
manager->isOpen = TRUE;
manager->options = options;
if ( manager->devices ) {
DeviceApplierArgs args;
args.manager = manager;
args.options = kDeviceApplierOpen;
args.retVal = kIOReturnSuccess;
if ( manager->inputMatchingMultiple )
args.options |= kDeviceApplierSetInputMatching;
if ( manager->inputCallback )
args.options |= kDeviceApplierSetInputCallback;
if ( manager->reportCallback )
args.options |= kDeviceApplierSetInputReportCallback;
CFSetApplyFunction( manager->devices,
__IOHIDManagerDeviceApplier,
&args);
retVal = args.retVal;
}
}
return retVal;
}
IOReturn IOHIDManagerClose(
IOHIDManagerRef manager,
IOOptionBits options)
{
IOReturn retVal = kIOReturnSuccess;
if ( manager->isOpen ) {
manager->isOpen = FALSE;
manager->options = options;
if ( manager->devices ) {
DeviceApplierArgs args;
args.manager = manager;
args.options = kDeviceApplierClose;
args.retVal = kIOReturnSuccess;
CFSetApplyFunction( manager->devices,
__IOHIDManagerDeviceApplier,
&args);
retVal = args.retVal;
}
}
return retVal;
}
CFTypeRef IOHIDManagerGetProperty(
IOHIDManagerRef manager,
CFStringRef key)
{
if ( !manager->properties )
return NULL;
return CFDictionaryGetValue(manager->properties, key);
}
Boolean IOHIDManagerSetProperty(
IOHIDManagerRef manager,
CFStringRef key,
CFTypeRef value)
{
if (!manager->properties ||
!(manager->properties = CFDictionaryCreateMutable(
kCFAllocatorDefault,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks)))
return FALSE;
CFDictionarySetValue(manager->properties, key, value);
return TRUE;
}
void IOHIDManagerSetDeviceMatching(
IOHIDManagerRef manager,
CFDictionaryRef matching)
{
CFArrayRef multiple = NULL;
if ( matching )
multiple = CFArrayCreate(kCFAllocatorDefault,
(const void **)&matching,
1,
&kCFTypeArrayCallBacks);
IOHIDManagerSetDeviceMatchingMultiple(manager, multiple);
if ( multiple )
CFRelease(multiple);
}
void IOHIDManagerSetDeviceMatchingMultiple(
IOHIDManagerRef manager,
CFArrayRef multiple)
{
CFIndex i, count;
CFTypeRef value;
if ( manager->devices )
CFSetRemoveAllValues(manager->devices);
if ( manager->iterators )
CFSetRemoveAllValues(manager->iterators);
if ( manager->deviceInputBuffers )
CFDictionaryRemoveAllValues(manager->deviceInputBuffers);
if ( multiple ) {
count = CFArrayGetCount(multiple);
for ( i=0; i<count; i++ ) {
value = CFArrayGetValueAtIndex(multiple, i);
if (CFDictionaryGetTypeID() == CFGetTypeID(value))
__IOHIDManagerSetDeviceMatching(manager, (CFDictionaryRef)value);
}
}
else
__IOHIDManagerSetDeviceMatching(manager, NULL);
}
CFSetRef IOHIDManagerCopyDevices(
IOHIDManagerRef manager)
{
return manager->devices ?
CFSetCreateCopy(kCFAllocatorDefault, manager->devices) : NULL;
}
void IOHIDManagerRegisterDeviceMatchingCallback(
IOHIDManagerRef manager,
IOHIDDeviceCallback callback,
void * context)
{
manager->matchCallback = callback;
manager->matchContext = context;
}
void IOHIDManagerRegisterDeviceRemovalCallback(
IOHIDManagerRef manager,
IOHIDDeviceCallback callback,
void * context)
{
manager->removalCallback = callback;
manager->removalContext = context;
}
void IOHIDManagerRegisterInputReportCallback(
IOHIDManagerRef manager,
IOHIDReportCallback callback,
void * context)
{
manager->reportCallback = callback;
manager->reportContext = context;
if ( manager->isOpen && manager->devices ) {
DeviceApplierArgs args;
args.manager = manager;
args.options = kDeviceApplierSetInputReportCallback;
CFSetApplyFunction( manager->devices,
__IOHIDManagerDeviceApplier,
&args);
}
}
void IOHIDManagerRegisterInputValueCallback(
IOHIDManagerRef manager,
IOHIDValueCallback callback,
void * context)
{
manager->inputCallback = callback;
manager->inputContext = context;
if ( manager->isOpen && manager->devices ) {
DeviceApplierArgs args;
args.manager = manager;
args.options = kDeviceApplierSetInputCallback;
CFSetApplyFunction( manager->devices,
__IOHIDManagerDeviceApplier,
&args);
}
}
void IOHIDManagerSetInputValueMatching(
IOHIDManagerRef manager,
CFDictionaryRef matching)
{
if ( matching ) {
CFArrayRef multiple = CFArrayCreate(kCFAllocatorDefault, (const void **)&matching, 1, &kCFTypeArrayCallBacks);
IOHIDManagerSetInputValueMatchingMultiple(manager, multiple);
CFRelease(multiple);
} else {
IOHIDManagerSetInputValueMatchingMultiple(manager, NULL);
}
}
void IOHIDManagerSetInputValueMatchingMultiple(
IOHIDManagerRef manager,
CFArrayRef multiple)
{
if ( manager->inputMatchingMultiple )
CFRelease(manager->inputMatchingMultiple);
if ( multiple )
CFRetain(multiple);
manager->inputMatchingMultiple = multiple;
if ( manager->isOpen && manager->devices ) {
DeviceApplierArgs args;
args.manager = manager;
args.options = kDeviceApplierSetInputMatching;
CFSetApplyFunction( manager->devices,
__IOHIDManagerDeviceApplier,
&args);
}
}
void IOHIDManagerScheduleWithRunLoop(
IOHIDManagerRef manager,
CFRunLoopRef runLoop,
CFStringRef runLoopMode)
{
manager->runLoop = runLoop;
manager->runLoopMode = runLoopMode;
if ( manager->runLoop ) {
if (manager->notifyPort)
CFRunLoopAddSource(
manager->runLoop,
IONotificationPortGetRunLoopSource(manager->notifyPort),
manager->runLoopMode);
if ( manager->initEnumRunLoopSource ) {
CFRunLoopAddSource(
manager->runLoop,
manager->initEnumRunLoopSource,
manager->runLoopMode);
CFRunLoopSourceSignal(manager->initEnumRunLoopSource);
}
if ( manager->devices ) {
DeviceApplierArgs args;
args.manager = manager;
args.options = kDeviceApplierScheduleRunLoop;
CFSetApplyFunction( manager->devices,
__IOHIDManagerDeviceApplier,
&args);
}
}
}
void IOHIDManagerUnscheduleFromRunLoop(
IOHIDManagerRef manager,
CFRunLoopRef runLoop,
CFStringRef runLoopMode)
{
if (!CFEqual(manager->runLoop, runLoop) ||
!CFEqual(manager->runLoopMode, runLoopMode))
return;
if ( manager->devices ) {
DeviceApplierArgs args;
args.manager = manager;
args.options = kDeviceApplierUnscheduleRunLoop;
CFSetApplyFunction( manager->devices,
__IOHIDManagerDeviceApplier,
&args);
}
manager->runLoop = NULL;
manager->runLoopMode = NULL;
}
void __MergeDictionaries( CFDictionaryRef srcDict,
CFMutableDictionaryRef * pDstDict)
{
uint32_t count;
CFTypeRef * values;
CFStringRef * keys;
if ( !pDstDict || !srcDict || !(count = CFDictionaryGetCount(srcDict)))
return;
if ( !*pDstDict )
return;
values = (CFTypeRef *)malloc(sizeof(CFTypeRef) * count);
keys = (CFStringRef *)malloc(sizeof(CFStringRef) * count);
if ( values && keys ) {
CFDictionaryGetKeysAndValues(srcDict, (const void **)keys, (const void **)values);
for ( uint32_t i=0; i<count; i++)
CFDictionarySetValue(*pDstDict, keys[i], values[i]);
}
if ( values )
free(values);
if ( keys )
free(keys);
}