IOHIDEventSystemMonitor.c [plain text]
#include <AssertMacros.h>
#include <pthread.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/hid/IOHIDEventSystemClientPrivate.h>
#include <IOKit/hid/IOHIDEventSystemKeysPrivate.h>
#include <IOKit/hid/IOHIDServiceClient.h>
#include <IOKit/hid/IOHIDEventSystem.h>
#include <IOKit/hid/IOHIDEventSystemPrivate.h>
#include <IOKit/hid/IOHIDService.h>
#include <IOKit/hid/IOHIDServicePrivate.h>
#include <IOKit/hid/IOHIDNotification.h>
#include <IOKit/hid/IOHIDKeys.h>
#include <IOKit/hid/AppleHIDUsageTables.h>
static const char kAdded[] = "ADDED";
static const char kRemoved[] = "REMOVED";
static const char __version[] = "2.00";
static uint32_t __persist = 0;
static uint32_t __dispatchOnly = 0;
static uint64_t __dispatchEventMask = 0;
static uint64_t __eventMask = -1;
static boolean_t __filter = false;
static boolean_t __virtualService = false;
static IOHIDEventSystemClientType __clientType = kIOHIDEventSystemClientTypeMonitor;
static struct {
uint32_t usagePage;
uint32_t usage;
uint32_t builtin;
} __matching[0xff] = {};
static uint32_t __matchingCount = 0;
static uint32_t __matchingInterval = -1;
static uint64_t __eventLastTimestamps[kIOHIDEventTypeCount] = {};
static CFMutableArrayRef __eventIntervals[kIOHIDEventTypeCount] = {};
static uint64_t __eventCounts[kIOHIDEventTypeCount] = {};
static uint64_t __eventCount = 0;
static uint64_t __eventLatencyTotal = 0;
static IOHIDEventSystemClientRef __eventSystemRef = NULL;
static CFAbsoluteTime __startTime = 0;
static CFTimeInterval __timeout = 0;
static mach_timebase_info_data_t __timeBaseinfo = {};
static bool __monitorServices = false;
static bool __monitorClients = false;
IOHIDEventBlock eventBlock = ^(void * target, void * refcon, void * sender, IOHIDEventRef event)
{
IOHIDEventType type = IOHIDEventGetType(event);
uint64_t timestamp = IOHIDEventGetTimeStamp(event);
uint64_t interval = 0;
__eventCount++;
__eventCounts[type]++;
__eventLatencyTotal += IOHIDEventGetLatency(event, kMicrosecondScale);
if ( __eventLastTimestamps[type] ) {
CFNumberRef number = NULL;
interval = timestamp - __eventLastTimestamps[type];
interval *= __timeBaseinfo.numer;
interval /= __timeBaseinfo.denom;
interval /= kMicrosecondScale;
if ( !__eventIntervals[type] )
__eventIntervals[type] = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &interval);
if ( number ) {
CFArrayAppendValue(__eventIntervals[type], number);
CFRelease(number);
}
}
__eventLastTimestamps[type] = timestamp;
if ( ((1<<type) & __eventMask) != 0 ) {
CFStringRef outputString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@"), event);
if ( outputString ) {
if (refcon)
printf("<filter>\n");
printf("*** %lld us since last %s event ***\n", interval, IOHIDEventGetTypeString(IOHIDEventGetType(event)));
printf("%s", CFStringGetCStringPtr(outputString, kCFStringEncodingMacRoman));
if (refcon)
printf("</filter>\n");
printf("\n");
CFRelease(outputString);
}
}
};
IOHIDEventFilterBlock eventFilterBlock = ^ boolean_t (void * target, void * refcon, void * sender, IOHIDEventRef event)
{
eventBlock(target, true, sender, event);
return false;
};
static boolean_t filterEventCallback(void * target, void * refcon, void * sender, IOHIDEventRef event)
{
return eventFilterBlock(target, refcon, sender, event);
}
IOHIDServiceClientBlock serviceClientBlock = ^(void * target, void * refcon, IOHIDServiceClientRef service)
{
CFStringRef string;
if ( refcon == kAdded ) {
IOHIDServiceClientRegisterRemovalBlock(service, serviceClientBlock, NULL, (void*)kRemoved);
if ( __clientType == kIOHIDEventSystemClientTypeRateControlled ) {
CFNumberRef number;
if ( __matchingInterval != -1 ) {
number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &__matchingInterval);
if ( number ) {
IOHIDServiceClientSetProperty(service, CFSTR(kIOHIDServiceReportIntervalKey), number);
CFRelease(number);
}
}
}
}
printf("SERVICE %s:\n", (char *)refcon);
string = CFCopyDescription(service);
if ( string ) {
printf("%s\n", CFStringGetCStringPtr(string, kCFStringEncodingMacRoman));
CFRelease(string);
}
};
static CFMutableDictionaryRef __serviceNotifications = NULL;
void serviceRemovalCallback(void * target, void * refcon, IOHIDServiceRef service)
{
CFStringRef string;
CFDictionaryRemoveValue(__serviceNotifications, service);
printf("SERVICE %s:\n", (char *)kRemoved);
string = CFCopyDescription(service);
if ( string ) {
printf("%s\n", CFStringGetCStringPtr(string, kCFStringEncodingMacRoman));
CFRelease(string);
}
}
static void servicesAddedCallback(void * target, void * refcon, void * sender, CFArrayRef services)
{
CFIndex index, count;
for(index=0, count = CFArrayGetCount(services); index<count; index++) {
IOHIDServiceRef service = (IOHIDServiceRef)CFArrayGetValueAtIndex(services, index);
CFStringRef string;
IOHIDNotificationRef notification = IOHIDServiceCreateRemovalNotification(service, serviceRemovalCallback, NULL, NULL);
if ( notification ) {
CFDictionaryAddValue(__serviceNotifications, service, notification);
CFRelease(notification);
}
printf("SERVICE %s:\n", (char *)kAdded);
string = CFCopyDescription(service);
if ( string ) {
printf("%s\n", CFStringGetCStringPtr(string, kCFStringEncodingMacRoman));
CFRelease(string);
}
}
}
static void connectionAddedCallback(void * target, void * refcon, IOHIDEventSystemConnectionRef connection)
{
CFStringRef string;
printf("CONNECTION %s:\n", (char *)kAdded);
string = CFCopyDescription(connection);
if ( string ) {
printf("%s\n", CFStringGetCStringPtr(string, kCFStringEncodingMacRoman));
CFRelease(string);
}
}
static void connectionRemovedCallback(void * target, void * refcon, IOHIDEventSystemConnectionRef connection)
{
CFStringRef string;
printf("CONNECTION %s:\n", (char *)kRemoved);
string = CFCopyDescription(connection);
if ( string ) {
printf("%s\n", CFStringGetCStringPtr(string, kCFStringEncodingMacRoman));
CFRelease(string);
}
}
static void dispatchClientEvents(IOHIDEventSystemClientRef system, uint64_t mask)
{
do {
for ( uint32_t index=0; index<kIOHIDEventTypeCount; index++ ) {
IOHIDEventRef event;
if ( ((1<<index) & mask) == 0 )
continue;
event = IOHIDEventCreate(kCFAllocatorDefault, index, mach_absolute_time(), 0);
if ( !event )
continue;
IOHIDEventSetSenderID(event, 0xDEFACEDBEEFFECE5);
printf("Dispatch %s event\n", IOHIDEventGetTypeString(index));
IOHIDEventSystemClientDispatchEvent(system, event);
CFRelease(event);
}
if ( !__persist )
continue;
printf("hit return to redispatch\n");
while (getchar() != '\n');
} while (__persist);
}
static void * dispatchClientThread(void * context)
{
IOHIDEventSystemClientRef eventSystem = (IOHIDEventSystemClientRef)context;
dispatchClientEvents(eventSystem, __dispatchEventMask);
return NULL;
}
static boolean_t VirtualOpen(void * target, void * refcon, IOOptionBits options)
{
printf("%s\n", __PRETTY_FUNCTION__);
return true;
}
static void VirtualClose(void * target, void * refcon, IOOptionBits options)
{
printf("%s\n", __PRETTY_FUNCTION__);
}
static CFTypeRef VirtualCopyProperty(void * target, void * refcon, CFStringRef key)
{
printf("%s\n", __PRETTY_FUNCTION__);
if (CFEqual(key, CFSTR(kIOHIDTransportKey))) {
CFStringRef value = CFSTR("Virtual");
CFRetain(value);
return value;
}
return NULL;
}
static boolean_t VirtualSetProperty(void * target, void * refcon, CFStringRef key, CFTypeRef value)
{
printf("%s\n", __PRETTY_FUNCTION__);
return false;
}
static void VirtualSetEventCallback(void * target, void * refcon, IOHIDServiceEventCallback callback, void *callbackTarget, void *callbackRefcon)
{
printf("%s\n", __PRETTY_FUNCTION__);
}
static void VirtualScheduleWithDispatchQueue(void * target, void * refcon, dispatch_queue_t queue)
{
printf("%s\n", __PRETTY_FUNCTION__);
}
static void VirtualUnscheduleFromDispatchQueue(void * target, void * refcon, dispatch_queue_t queue)
{
printf("%s\n", __PRETTY_FUNCTION__);
}
static IOHIDEventRef VirtualCopyEvent(void * target, void * refcon, IOHIDEventType type, IOHIDEventRef matching, IOOptionBits options)
{
printf("%s\n", __PRETTY_FUNCTION__);
return NULL;
}
static IOReturn VirtualSetOutputEvent(void * target, void * refcon, IOHIDEventRef event)
{
printf("%s\n", __PRETTY_FUNCTION__);
return kIOReturnUnsupported;
}
static void runServer()
{
boolean_t result;
IOHIDEventSystemRef eventSystem = NULL;
IOHIDServiceRef virtualService = NULL;
IOHIDServiceVirtualCallbacks virtualCallbacks = {
VirtualOpen,
VirtualClose,
VirtualCopyProperty,
VirtualSetProperty,
VirtualSetEventCallback,
VirtualScheduleWithDispatchQueue,
VirtualUnscheduleFromDispatchQueue,
VirtualCopyEvent,
VirtualSetOutputEvent
};
eventSystem = IOHIDEventSystemCreate(kCFAllocatorDefault);
require(eventSystem, exit);
result = IOHIDEventSystemOpen(eventSystem, filterEventCallback, NULL, NULL, 0);
require(result, exit);
if ( __virtualService ) {
virtualService = _IOHIDServiceCreateVirtual(kCFAllocatorDefault, 0xb0b0000000000000, &virtualCallbacks, NULL, NULL); printf("Virtual Service = %p\n", virtualService);
_IOHIDEventSystemAddService(eventSystem, virtualService);
}
if ( !__serviceNotifications )
__serviceNotifications = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
for ( int index=kIOHIDEventSystemConnectionTypeAdmin; index<kIOHIDEventSystemConnectionTypeCount; index++) {
IOHIDEventSystemRegisterConnectionAdditionCallback(eventSystem, (IOHIDEventSystemConnectionType)index, connectionAddedCallback, NULL, NULL);
IOHIDEventSystemRegisterConnectionRemovalCallback(eventSystem, (IOHIDEventSystemConnectionType)index, connectionRemovedCallback, NULL, NULL);
}
IOHIDEventSystemRegisterServicesCallback(eventSystem, NULL, servicesAddedCallback, NULL, NULL);
CFArrayRef services = IOHIDEventSystemCopyServices(eventSystem, NULL);
if ( services ) {
servicesAddedCallback(NULL, NULL, NULL, services);
CFRelease(services);
}
CFRunLoopRun();
exit:
if ( virtualService )
CFRelease(virtualService);
if ( eventSystem )
CFRelease(eventSystem);
}
static void printIndentation(CFIndex indentationLevel)
{
for ( CFIndex indentationIndex=0; indentationIndex<indentationLevel; indentationIndex++)
printf("\t");
}
static void printBorder(CFIndex indentationLevel)
{
printIndentation(indentationLevel);
if ( indentationLevel )
printf("-----------------------------------------------------------------------\n");
else
printf("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
}
static void printStringValue(CFStringRef key, CFStringRef string, CFIndex indentationLevel, bool newline)
{
if ( !key || !string )
return;
printIndentation(indentationLevel);
printf("%25.25s: %-32.32s ", CFStringGetCStringPtr(key, CFStringGetSystemEncoding()), CFStringGetCStringPtr(string, CFStringGetSystemEncoding()));
if ( newline ) printf("\n");
}
static void printBooleanValue(CFStringRef key, CFBooleanRef value, CFIndex indentationLevel, bool newline)
{
if ( !key || !value )
return;
printIndentation(indentationLevel);
printf("%25.25s: %-3.3s ", CFStringGetCStringPtr(key, CFStringGetSystemEncoding()), value == kCFBooleanTrue ? "YES" : "NO");
if ( newline ) printf("\n");
}
static void printNumberValue(CFStringRef key, CFNumberRef number, CFIndex indentationLevel, bool newline)
{
uint64_t value;
if ( !key || !number )
return;
printIndentation(indentationLevel);
CFNumberGetValue(number, kCFNumberSInt64Type, &value);
if ( CFEqual(key, CFSTR(kIOHIDServiceRegistryIDKey)) )
printf("%25.25s: 0x%016llx ", CFStringGetCStringPtr(key, CFStringGetSystemEncoding()), value);
else if ( CFEqual(key, CFSTR(kIOHIDServiceNextEventTimeStampDeltaKey)) )
printf("%25.25s: %-5lld us ", CFStringGetCStringPtr(key, CFStringGetSystemEncoding()), value);
else
printf("%25.25s: %-5lld ", CFStringGetCStringPtr(key, CFStringGetSystemEncoding()), value);
if ( newline ) printf("\n");
}
static void printDictionaryValue(CFStringRef key, CFDictionaryRef dictionary, CFIndex indentationLevel)
{
CFIndex index, count;
if ( !dictionary )
return;
count = CFDictionaryGetCount(dictionary);
if ( !count )
return;
printf("%20.20s:\n", CFStringGetCStringPtr(key, CFStringGetSystemEncoding()));
CFStringRef keys[count];
CFStringRef values[count];
bzero(keys, sizeof(keys));
CFDictionaryGetKeysAndValues(dictionary, (const void **)keys, (const void **)values);
for (index=0; index<count; index++) {
CFTypeRef value;
CFStringRef key;
key = keys[index];
if ( !keys )
continue;
value = values[index];
if ( !value )
continue;
if ( CFGetTypeID(value) == CFStringGetTypeID() )
printStringValue(key, (CFStringRef)value, indentationLevel+1, true);
else if ( CFGetTypeID(value) == CFNumberGetTypeID() )
printNumberValue(key, (CFNumberRef)value, indentationLevel+1, true);
else if ( CFGetTypeID(value) == CFBooleanGetTypeID() )
printBooleanValue(key, (CFBooleanRef)value, indentationLevel+1, true);
}
}
static void listServices(CFArrayRef services, CFIndex indentationLevel)
{
static CFStringRef sServiceKeys[] = {CFSTR(kIOHIDServiceRegistryIDKey), CFSTR(kIOHIDServiceRegistryNameKey), CFSTR(kIOHIDBuiltInKey), CFSTR(kIOHIDDisplayIntegratedKey), CFSTR(kIOHIDServicePrimaryUsagePageKey), CFSTR(kIOHIDServicePrimaryUsageKey), CFSTR(kIOHIDServiceReportIntervalKey), CFSTR(kIOHIDServiceSampleIntervalKey), CFSTR(kIOHIDServiceNextEventTimeStampDeltaKey), CFSTR(kIOHIDCategoryKey), CFSTR(kIOHIDServiceTransportKey)};
CFIndex index;
for ( index=0; index<CFArrayGetCount(services); index++, printf("\n") ) {
CFDictionaryRef serviceRecord = (CFDictionaryRef)CFArrayGetValueAtIndex(services, index);
if ( !serviceRecord )
continue;
for ( CFIndex keyIndex=0; keyIndex<(sizeof(sServiceKeys)/sizeof(CFStringRef)); keyIndex++ ) {
CFStringRef key;
CFTypeRef value;
key = sServiceKeys[keyIndex];
if ( !key )
continue;
value = CFDictionaryGetValue(serviceRecord, key);
if ( !value )
continue;
if ( CFGetTypeID(value) == CFStringGetTypeID() )
printStringValue(key, (CFStringRef)value, indentationLevel, false);
else if ( CFGetTypeID(value) == CFNumberGetTypeID() )
printNumberValue(key, (CFNumberRef)value, indentationLevel, false);
else if ( CFGetTypeID(value) == CFBooleanGetTypeID() )
printBooleanValue(key, (CFBooleanRef)value, indentationLevel, false);
}
}
}
static void listAllServicesWithSystem(IOHIDEventSystemClientRef eventSystem)
{
CFArrayRef services = NULL;
require(eventSystem, exit);
services = _IOHIDEventSystemClientCopyServiceRecords(eventSystem);
require(services, exit);
listServices(services, 0);
exit:
if ( services )
CFRelease(services);
}
static void listAllServices()
{
IOHIDEventSystemClientRef eventSystem = IOHIDEventSystemClientCreateWithType(kCFAllocatorDefault, kIOHIDEventSystemClientTypeAdmin, NULL);
require(eventSystem, exit);
listAllServicesWithSystem(eventSystem);
exit:
if (eventSystem)
CFRelease(eventSystem);
}
static void listAllClientsWithSystem(IOHIDEventSystemClientRef eventSystem)
{
CFIndex types;
require(eventSystem, exit);
for ( types=kIOHIDEventSystemClientTypeAdmin; types<=kIOHIDEventSystemClientTypeRateControlled; types++ ) {
CFArrayRef clients = _IOHIDEventSystemClientCopyClientRecords(eventSystem, (IOHIDEventSystemClientType)types);
CFIndex index;
if ( !clients )
continue;
for ( index=0, printBorder(0); index<CFArrayGetCount(clients); index++, printBorder(0) ) {
CFDictionaryRef clientRecord = (CFDictionaryRef)CFArrayGetValueAtIndex(clients, index);
CFArrayRef services = NULL;
CFNumberRef number;
number = CFDictionaryGetValue(clientRecord, CFSTR(kIOHIDEventSystemClientTypeKey));
if ( number ) {
uint32_t value;
CFNumberGetValue(number, kCFNumberSInt32Type, &value);
printf("%20.20s: %d (%s)\n", kIOHIDEventSystemClientTypeKey, value, IOHIDEventSystemClientGetTypeString((IOHIDEventSystemClientType)value));
}
printNumberValue(CFSTR(kIOHIDEventSystemClientPIDKey), CFDictionaryGetValue(clientRecord, CFSTR(kIOHIDEventSystemClientPIDKey)), 0, true);
printBooleanValue(CFSTR(kIOHIDEventSystemClientIsInactiveKey), CFDictionaryGetValue(clientRecord, CFSTR(kIOHIDEventSystemClientIsInactiveKey)), 0, true);
printBooleanValue(CFSTR(kIOHIDEventSystemClientProtectedServicesDisabledKey), CFDictionaryGetValue(clientRecord, CFSTR(kIOHIDEventSystemClientProtectedServicesDisabledKey)), 0, true);
printStringValue(CFSTR(kIOHIDEventSystemClientCallerKey), CFDictionaryGetValue(clientRecord, CFSTR(kIOHIDEventSystemClientCallerKey)), 0, true);
printStringValue(CFSTR(kIOHIDEventSystemClientExecutablePathKey), CFDictionaryGetValue(clientRecord, CFSTR(kIOHIDEventSystemClientExecutablePathKey)), 0, true);
printDictionaryValue(CFSTR(kIOHIDEventSystemClientAttributesKey), CFDictionaryGetValue(clientRecord, CFSTR(kIOHIDEventSystemClientAttributesKey)), 0);
services = CFDictionaryGetValue(clientRecord, CFSTR(kIOHIDEventSystemClientServicesKey));
if ( services ) {
printf("%20.20s:\n", kIOHIDEventSystemClientServicesKey);
listServices(services, 1);
}
}
CFRelease(clients);
}
exit:
return;
}
static void listAllClients()
{
IOHIDEventSystemClientRef eventSystem = IOHIDEventSystemClientCreateWithType(kCFAllocatorDefault, kIOHIDEventSystemClientTypeAdmin, NULL);
require(eventSystem, exit);
listAllClientsWithSystem(eventSystem);
exit:
if (eventSystem)
CFRelease(eventSystem);
}
static void serviceRecordsChangedCallback(void * target, IOHIDEventSystemClientRef client, void * context)
{
listAllServicesWithSystem(client);
}
static void clientRecordsChangedCallback(void * target, IOHIDEventSystemClientRef client, void * context)
{
listAllClientsWithSystem(client);
}
static void runClient()
{
IOHIDEventSystemClientRef eventSystem = IOHIDEventSystemClientCreateWithType(kCFAllocatorDefault, __clientType, NULL);
CFMutableArrayRef multiple = NULL;
CFMutableDictionaryRef matching = NULL;
uint32_t index = 0;
require_action(eventSystem, exit, printf("Unable to create client"));
IOHIDEventSystemClientScheduleWithRunLoop(eventSystem, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
if ( __monitorClients || __monitorServices ) {
if ( __monitorServices ) {
printf("Monitoring service records\n");
_IOHIDEventSystemClientRegisterServiceRecordsChangedCallback(eventSystem, serviceRecordsChangedCallback, NULL, NULL);
}
if ( __monitorClients ) {
printf("Monitoring client records\n");
_IOHIDEventSystemClientRegisterClientRecordsChangedCallback(eventSystem, clientRecordsChangedCallback, NULL, NULL);
}
} else {
require_action(!__dispatchOnly, exit, dispatchClientEvents(eventSystem, __dispatchEventMask));
if ( __dispatchEventMask ) {
pthread_attr_t attr;
pthread_t tid;
struct sched_param param;
assert(!pthread_attr_init(&attr));
assert(!pthread_attr_setschedpolicy(&attr, SCHED_RR));
assert(!pthread_attr_getschedparam(&attr, ¶m));
param.sched_priority = 63;
assert(!pthread_attr_setschedparam(&attr, ¶m));
assert(!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE));
assert(!pthread_create(&tid, &attr, &dispatchClientThread, eventSystem));
assert(!pthread_attr_destroy(&attr));
}
if ( __filter )
IOHIDEventSystemClientRegisterEventFilterBlock(eventSystem, eventFilterBlock, NULL, NULL);
else
IOHIDEventSystemClientRegisterEventBlock(eventSystem, eventBlock, NULL, NULL);
if ( __matchingCount ) {
multiple = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
require(multiple, exit);
for (index=0; index<__matchingCount; index++) {
CFNumberRef number = NULL;
matching = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if ( matching ) {
number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &__matching[index].usagePage);
if ( number ) {
CFDictionaryAddValue(matching, CFSTR(kIOHIDServiceDeviceUsagePageKey), number);
CFRelease(number);
}
if ( __matching[index].usage != -1 ) {
number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &__matching[index].usage);
if ( number ) {
CFDictionaryAddValue(matching, CFSTR(kIOHIDServiceDeviceUsageKey), number);
CFRelease(number);
}
}
if ( __matching[index].builtin != -1 ) {
CFDictionaryAddValue(matching, CFSTR(kIOHIDBuiltInKey), kCFBooleanTrue);
}
printf("Matching on UsagePage=%#x Usage=%#x Built-in=%#x\n", __matching[index].usagePage, __matching[index].usage, __matching[index].builtin);
CFArrayAppendValue(multiple, matching);
CFRelease(matching);
}
}
}
IOHIDEventSystemClientSetMatchingMultiple(eventSystem, multiple);
IOHIDEventSystemClientRegisterDeviceMatchingBlock(eventSystem, serviceClientBlock, NULL, (void*)kAdded);
CFArrayRef services = IOHIDEventSystemClientCopyServices(eventSystem);
if ( services ) {
CFIndex index, count;
for(index=0, count = CFArrayGetCount(services); index<count; index++)
serviceClientBlock(NULL, (void*)kAdded, (IOHIDServiceClientRef)CFArrayGetValueAtIndex(services, index));
CFRelease(services);
}
}
__eventSystemRef = eventSystem;
CFRunLoopRun();
exit:
if ( multiple )
CFRelease(multiple);
if ( eventSystem )
CFRelease(eventSystem);
}
static void printStatistics()
{
if ( !__timeout ) {
__timeout = CFAbsoluteTimeGetCurrent() - __startTime;
}
printf("\n");
printf("***************************************************************************\n");
printf("Event Statistics over %10.3f s\n", __timeout);
printf("***************************************************************************\n");
for (uint32_t index=0; index<kIOHIDEventTypeCount; index++) {
uint64_t stdDevInterval = 0;
uint64_t avgInterval = 0;
uint64_t highInterval = 0;
uint64_t lowInterval = 0;
if ( __eventIntervals[index] ) {
CFIndex intervalCount, intervalIndex;
uint64_t intervalTotal=0;
for (intervalIndex=0,intervalCount=CFArrayGetCount(__eventIntervals[index]); intervalIndex<intervalCount; intervalIndex++) {
CFNumberRef number = (CFNumberRef)CFArrayGetValueAtIndex(__eventIntervals[index], intervalIndex);
uint64_t value = 0;
if ( !number )
continue;
CFNumberGetValue(number, kCFNumberSInt64Type, &value);
intervalTotal += value;
if ( value > highInterval )
highInterval = value;
if ( !lowInterval || value < lowInterval )
lowInterval = value;
}
avgInterval = intervalTotal/intervalCount;
for (intervalIndex=0,intervalCount=CFArrayGetCount(__eventIntervals[index]); intervalIndex<intervalCount; intervalIndex++) {
CFNumberRef number = (CFNumberRef)CFArrayGetValueAtIndex(__eventIntervals[index], intervalIndex);
int64_t value = 0;
if ( !number )
continue;
CFNumberGetValue(number, kCFNumberSInt64Type, &value);
value = (value-avgInterval);
value *= value;
intervalTotal += value;
}
stdDevInterval = sqrt(intervalTotal/intervalCount);
}
printf("%-20.20s: EventCount = %10llu AverageInterval = %10llu us StandardDeviation = %10llu us HighInterval = %10llu us LowInterval = %10llu us\n", IOHIDEventGetTypeString(index), __eventCounts[index], avgInterval, stdDevInterval, highInterval, lowInterval);
}
printf("\n");
printf("Average latency: %10llu us\n", __eventLatencyTotal ? __eventLatencyTotal/__eventCount : 0);
}
static void exitTimerCallback(CFRunLoopTimerRef timer, void *info)
{
printStatistics();
exit(0);
}
static void signalHandler(int type)
{
if (type == SIGINT) {
printStatistics();
exit(0);
}
}
static void printHelp()
{
printf("\n");
printf("hidEventSystemMonitor usage:\n\n");
printf("\t-up <usage page>\t: Device usage page\n");
printf("\t-u <usage>\t: Device usage\n");
printf("\t-b <1 | 0>\t: 1=built-in 0=not built-in\n");
printf("\t-m <event type mask ...>\t: monitor all events contained in mask\n");
printf("\t-e <event type number ...>\t: monitor all events of the passed type\n");
printf("\t-nm <event type mask>\t\t: monitor all events except those contained in mask\n");
printf("\t-ne <event type number>\t\t: monitor all events except those of the passed type\n");
printf("\t-d <event type number>\t\t: dispatch event of the passed type\n");
printf("\t-dm <event type number>\t\t: dispatch events of the passed mask\n");
printf("\t-p\t\t\t\t: persist event dispatch\n");
printf("\n");
printf("\t-a\t\t\t\t: Admin (Unfiltered event stream)\n");
printf("\t-r\t\t\t\t: Rate Controlled\n");
printf("\n");
printf("\t-s\t\t\t\t: Instantiate HID event server\n");
printf("\t-ms\t\t\t\t: Monitor services\n");
printf("\t-mc\t\t\t\t: Monitor clients\n");
printf("\n");
printf("\t-lc\t\t\t\t: List clients\n");
printf("\t-ls\t\t\t\t: List services\n");
printf("\t-V\t\t\t\t: Version\n");
printf("\n\tAvailable Event Types:\n");
for (int type = kIOHIDEventTypeNULL; type<kIOHIDEventTypeCount; type++) {
printf("\t\t%2d: %s\n", type, CFStringGetCStringPtr(IOHIDEventTypeGetName(type), kCFStringEncodingMacRoman));
}
}
typedef enum {
kEventRegistrationTypeNone,
kEventRegistrationTypeReplaceMask,
kEventRegistrationTypeRemoveMask,
kEventRegistrationTypeAdd,
kEventRegistrationTypeRemove,
kEventRegistrationTypeDispatch,
kEventRegistrationTypeDispatchMask,
kEventRegistrationTypeUsagePage,
kEventRegistrationTypeUsage,
kEventRegistrationTypeBuiltIn,
kEventRegistrationTypeInterval,
kEventRegistrationTypeTimeout,
} EventRegistrationType;
int main (int argc __unused, const char * argv[] __unused)
{
bool runAsClient = true;
mach_timebase_info(&__timeBaseinfo);
signal(SIGINT, signalHandler);
if ( argc > 1 ) {
EventRegistrationType registrationType=kEventRegistrationTypeNone;
for ( int index=1; index<argc; index++) {
const char * arg = argv[index];
if ( !strcmp("-a", arg ) ) {
__clientType = kIOHIDEventSystemClientTypeAdmin;
}
else if ( !strcmp("-f", arg ) ) {
__filter = true;
}
else if ( !strcmp("-v", arg ) ) {
__virtualService = true;
}
else if ( !strcmp("-r", arg ) ) {
__clientType = kIOHIDEventSystemClientTypeRateControlled;
}
else if ( !strcmp("-up", arg ) ) {
registrationType = kEventRegistrationTypeUsagePage;
}
else if ( !strcmp("-u", arg ) ) {
registrationType = kEventRegistrationTypeUsage;
}
else if ( !strcmp("-b", arg) ) {
registrationType = kEventRegistrationTypeBuiltIn;
}
else if ( !strcmp("-i", arg ) ) {
registrationType = kEventRegistrationTypeInterval;
}
else if ( !strcmp("-s", arg ) ) {
runAsClient = false;
}
else if ( !strcmp("-m", arg ) ) {
registrationType = kEventRegistrationTypeReplaceMask;
}
else if ( !strcmp("-e", arg ) ) {
registrationType = kEventRegistrationTypeAdd;
__eventMask = 0;
}
else if ( !strcmp("-nm", arg ) ) {
registrationType = kEventRegistrationTypeRemoveMask;
}
else if ( !strcmp("-ne", arg ) ) {
registrationType = kEventRegistrationTypeRemove;
}
else if ( !strcmp("-do", arg ) ) {
__dispatchOnly = 1;
}
else if ( !strcmp("-lc", arg ) ) {
listAllClients();
goto exit;
}
else if ( !strcmp("-ls", arg ) ) {
listAllServices();
goto exit;
}
else if ( !strcmp("-ms", arg) ) {
__monitorServices = true;
__clientType = kIOHIDEventSystemClientTypeAdmin;
}
else if ( !strcmp("-mc", arg) ) {
__monitorClients = true;
__clientType = kIOHIDEventSystemClientTypeAdmin;
}
else if ( !strcmp("-d", arg) ) {
registrationType = kEventRegistrationTypeDispatch;
}
else if ( !strcmp("-dm", arg) ) {
registrationType = kEventRegistrationTypeDispatchMask;
}
else if ( !strcmp("-p", arg) ) {
__persist = 1;
}
else if ( !strcmp("-t", arg) ) {
registrationType = kEventRegistrationTypeTimeout;
}
else if ( !strcmp("-V", arg) ) {
printf("Version: %s\n", __version);
}
else if ( registrationType == kEventRegistrationTypeReplaceMask ) {
__eventMask = strtoull(arg, NULL, 16);
}
else if ( registrationType == kEventRegistrationTypeRemoveMask ) {
__eventMask &= ~(strtoull(arg, NULL, 16));
}
else if ( registrationType == kEventRegistrationTypeAdd ) {
__eventMask |= (1<<strtoull(arg, NULL, 10));
}
else if ( registrationType == kEventRegistrationTypeRemove ) {
__eventMask &= ~(1<<strtoull(arg, NULL, 10));
}
else if ( registrationType == kEventRegistrationTypeDispatch ) {
__dispatchEventMask |= (1<<strtoull(arg, NULL, 10));
}
else if ( registrationType == kEventRegistrationTypeDispatchMask ) {
__dispatchEventMask = strtoull(arg, NULL, 16);
}
else if ( registrationType == kEventRegistrationTypeUsagePage ) {
uint32_t index = __matchingCount++;
__matching[index].usagePage = (uint32_t)strtoul(arg, NULL, 10);
__matching[index].usage = -1;
__matching[index].builtin = -1;
}
else if ( registrationType == kEventRegistrationTypeUsage && __matchingCount ) {
__matching[__matchingCount-1].usage = (uint32_t)strtoul(arg, NULL, 10);
}
else if ( registrationType == kEventRegistrationTypeBuiltIn && __matchingCount ) {
__matching[__matchingCount-1].builtin = (uint32_t)strtoul(arg, NULL, 10);
}
else if ( registrationType == kEventRegistrationTypeInterval ) {
__matchingInterval = (uint32_t)strtoul(arg, NULL, 10);
}
else if ( registrationType == kEventRegistrationTypeTimeout ) {
__timeout = (uint32_t)strtoul(arg, NULL, 10);
}
else if ( !strcmp("-h", arg ) ) {
printHelp();
return 0;
}
}
}
if ( __timeout ) {
CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + __timeout, 0, 0, 0, exitTimerCallback, NULL);
if ( timer ) {
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
CFRelease(timer);
}
} else {
__startTime = CFAbsoluteTimeGetCurrent();
}
if ( runAsClient ) {
printf("***************************************************************************\n");
printf("Running as a %s client\n", IOHIDEventSystemClientGetTypeString(__clientType));
printf("***************************************************************************\n");
runClient();
} else {
printf("***************************************************************************\n");
printf("Running as server\n");
printf("***************************************************************************\n");
runServer();
}
exit:
return 0;
}