1#include <CoreFoundation/CoreFoundation.h> 2#include <IOKit/hid/IOHIDLib.h> 3#include "IOHIDReportDescriptorParser.h" 4 5static CFTimeInterval gPollInterval = 0.0; 6static bool gReport = TRUE; 7static bool gValue = FALSE; 8static bool gSend = FALSE; 9static bool gSendTransaction = FALSE; 10static bool gPrintDescriptor = FALSE; 11 12static CFMutableDictionaryRef gOutputElements = NULL; 13 14static char * getReportTypeString(IOHIDReportType type) 15{ 16 switch ( type ) { 17 case kIOHIDReportTypeInput: 18 return "INPUT"; 19 case kIOHIDReportTypeOutput: 20 return "OUTPUT"; 21 case kIOHIDReportTypeFeature: 22 return "FEATURE"; 23 default: 24 return "DUH"; 25 } 26} 27 28static void __deviceReportCallback(void * context, IOReturn result, void * sender, IOHIDReportType type, uint32_t reportID, uint8_t * report, CFIndex reportLength) 29{ 30 int index; 31 32 printf("IOHIDDeviceRef[%p]: result=0x%08x reportType=%s reportID=%d reportLength=%ld: ", sender, result, getReportTypeString(type), reportID, reportLength); 33 for (index=0; result==kIOReturnSuccess && index<reportLength; index++) 34 printf("%02x ", report[index]); 35 printf("\n"); 36 37 // toggle a report 38 if ( gSend || gSendTransaction ) { 39 CFArrayRef outputElements = NULL; 40 41 outputElements = CFDictionaryGetValue(gOutputElements, sender); 42 43 if ( outputElements ) { 44 static uint8_t value = 0; 45 IOHIDTransactionRef transaction = NULL; 46 47 transaction = IOHIDTransactionCreate(kCFAllocatorDefault, (IOHIDDeviceRef)sender, kIOHIDTransactionDirectionTypeOutput, 0); 48 if ( transaction ) { 49 IOReturn ret; 50 CFIndex index, count; 51 52 for ( index=0, count = CFArrayGetCount(outputElements); index<count; index++) { 53 54 IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(outputElements, index); 55 IOHIDValueRef hidValue = 0; 56 57 if ( !element ) 58 continue; 59 60 IOHIDTransactionAddElement(transaction, element); 61 62 hidValue = IOHIDValueCreateWithIntegerValue(kCFAllocatorDefault, element, 0, value); 63 if ( !hidValue ) 64 continue; 65 66 if ( gSendTransaction ) { 67 IOHIDTransactionSetValue(transaction, element, hidValue, 0); 68 } else { 69 ret = IOHIDDeviceSetValue((IOHIDDeviceRef)sender, element, hidValue); 70 printf("Attempt to send value. Ret = 0x%08x\n", ret); 71 } 72 CFRelease(hidValue); 73 74 } 75 76 if ( gSendTransaction ) { 77 ret = IOHIDTransactionCommit(transaction); 78 printf("Attempt to send transaction. Ret = 0x%08x\n", ret); 79 } 80 value = value+1 % 2; 81 82 CFRelease(transaction); 83 } 84 } 85 } 86} 87 88void __deviceValueCallback (void * context, IOReturn result, void * sender, IOHIDValueRef value) 89{ 90 IOHIDElementRef element = IOHIDValueGetElement(value); 91 92 printf("IOHIDDeviceRef[%p]: value=%p timestamp=%lld cookie=%d usagePage=0x%02X usage=0x%02X intValue=%ld\n", sender, value, IOHIDValueGetTimeStamp(value), (uint32_t)IOHIDElementGetCookie(element), IOHIDElementGetUsagePage(element), IOHIDElementGetUsage(element), IOHIDValueGetIntegerValue(value)); 93} 94 95static void __timerCallback(CFRunLoopTimerRef timer, void *info) 96{ 97 IOHIDDeviceRef device = (IOHIDDeviceRef)info; 98 99 CFNumberRef number = NULL; 100 CFIndex reportSize = 0; 101 IOReturn result; 102 103 number = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDMaxInputReportSizeKey)); 104 if ( !number ) 105 return; 106 107 CFNumberGetValue(number, kCFNumberCFIndexType, &reportSize); 108 109 uint8_t report[reportSize]; 110 111 bzero(report, reportSize); 112 113 result = IOHIDDeviceGetReport(device, kIOHIDReportTypeInput, 0, report, &reportSize); 114 115 __deviceReportCallback(NULL, result, device, kIOHIDReportTypeInput, 0, report, reportSize); 116} 117 118static void __deviceCallback(void * context, IOReturn result, void * sender, IOHIDDeviceRef device) 119{ 120 121 boolean_t terminated = context == 0; 122 CFStringRef debugString = CFCopyDescription(device); 123 124 static CFMutableDictionaryRef s_timers = NULL; 125 126 if ( !s_timers ) 127 s_timers = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 128 129 printf("%-10.10s: %s\n", terminated ? "terminated" : "matched", debugString ? CFStringGetCStringPtr(debugString, CFStringGetSystemEncoding()) : ""); 130 131 if ( debugString ) 132 CFRelease(debugString); 133 134 if ( terminated ) { 135 CFDictionaryRemoveValue(gOutputElements, device); 136 137 138 CFRunLoopTimerRef timer = (CFRunLoopTimerRef)CFDictionaryGetValue(s_timers, device); 139 if ( timer ) { 140 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes); 141 } 142 CFDictionaryRemoveValue(s_timers, device); 143 144 } else { 145 CFArrayRef outputElements = NULL; 146 CFMutableDictionaryRef matching = NULL; 147 148 if ( gPrintDescriptor ) { 149 CFDataRef descriptor = NULL; 150 151 descriptor = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDReportDescriptorKey)); 152 if ( descriptor ) { 153 PrintHIDDescriptor(CFDataGetBytePtr(descriptor), CFDataGetLength(descriptor)); 154 } 155 } 156 157 if ( gPollInterval != 0.0 ) { 158 CFRunLoopTimerContext context = {.info=device}; 159 CFRunLoopTimerRef timer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent(), gPollInterval, 0, 0, __timerCallback, &context); 160 161 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes); 162 163 CFDictionaryAddValue(s_timers, device, timer); 164 CFRelease(timer); 165 166 printf("Adding polling timer @ %4.6f s\n", gPollInterval); 167 } 168 169 matching = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 170 if ( matching ) { 171 uint32_t value = kIOHIDElementTypeOutput; 172 CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); 173 174 if ( number ) { 175 CFDictionarySetValue(matching, CFSTR(kIOHIDElementTypeKey), number); 176 177 outputElements = IOHIDDeviceCopyMatchingElements(device, matching, 0); 178 if ( outputElements ) { 179 CFDictionarySetValue(gOutputElements, device, outputElements); 180 CFRelease(outputElements); 181 } 182 183 CFRelease(number); 184 } 185 CFRelease(matching); 186 } 187 } 188 189 190} 191 192static void printHelp() 193{ 194 printf("\n"); 195 printf("hidReportTest usage:\n\n"); 196 printf("\t-p Parse descriptor data\n"); 197 printf("\t-i Manually poll at a given interval (s)"); 198 printf("\t--usage <usage>\n"); 199 printf("\t--usagepage <usage page>\n"); 200 printf("\t--transport <transport string value\n>"); 201 printf("\t--vid <vendor id>\n"); 202 printf("\t--pid <product id>\n"); 203 printf("\t--transport <transport string value>\n"); 204 printf("\n"); 205} 206 207int main (int argc, const char * argv[]) { 208 209 IOHIDManagerRef manager = IOHIDManagerCreate(kCFAllocatorDefault, 0); 210 CFMutableDictionaryRef matching = NULL; 211 212 int argi; 213 for (argi=1; argi<argc; argi++) { 214 if ( 0 == strcmp("-v", argv[argi]) ) { 215 gValue = TRUE; 216 } 217 else if ( 0 == strcmp("-p", argv[argi]) ) { 218 gPrintDescriptor = TRUE; 219 } 220 else if ( 0 == strcmp("-s", argv[argi]) ) { 221 gSend = TRUE; 222 } 223 else if ( 0 == strcmp("-st", argv[argi]) ) { 224 gSendTransaction = TRUE; 225 } 226 else if ( 0 == strcmp("-nr", argv[argi]) ) { 227 gReport = FALSE; 228 } 229 else if ( !strcmp("-i", argv[argi]) && (argi+1) < argc) { 230 gPollInterval = atof(argv[++argi]); 231 printf("gPollInterval = %f seconds\n", gPollInterval); 232 } 233 else if ( !strcmp("--usage", argv[argi]) && (argi+1) < argc) { 234 long value = atol(argv[++argi]); 235 CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &value); 236 if ( number ) { 237 if ( !matching ) 238 matching = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 239 CFDictionarySetValue(matching, CFSTR(kIOHIDDeviceUsageKey), number); 240 CFRelease(number); 241 } 242 } 243 else if ( !strcmp("--usagepage", argv[argi]) && (argi+1) < argc) { 244 long value = atol(argv[++argi]); 245 CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &value); 246 if ( number ) { 247 if ( !matching ) 248 matching = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 249 CFDictionarySetValue(matching, CFSTR(kIOHIDDeviceUsagePageKey), number); 250 CFRelease(number); 251 } 252 } 253 else if ( !strcmp("--vid", argv[argi]) && (argi+1) < argc) { 254 long value = atol(argv[++argi]); 255 CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &value); 256 if ( number ) { 257 if ( !matching ) 258 matching = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 259 CFDictionarySetValue(matching, CFSTR(kIOHIDVendorIDKey), number); 260 CFRelease(number); 261 } 262 } 263 else if ( !strcmp("--pid", argv[argi]) && (argi+1) < argc) { 264 long value = atol(argv[++argi]); 265 CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &value); 266 if ( number ) { 267 if ( !matching ) 268 matching = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 269 CFDictionarySetValue(matching, CFSTR(kIOHIDProductIDKey), number); 270 CFRelease(number); 271 } 272 } 273 else if ( !strcmp("--transport", argv[argi]) && (argi+1) < argc) { 274 CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, argv[++argi], CFStringGetSystemEncoding()); 275 if ( string ) { 276 if ( !matching ) 277 matching = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 278 CFDictionarySetValue(matching, CFSTR(kIOHIDTransportKey), string); 279 CFRelease(string); 280 } 281 } 282 else { 283 printHelp(); 284 return 0; 285 } 286 } 287 288 gOutputElements = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 289 290 IOHIDManagerRegisterDeviceMatchingCallback(manager, __deviceCallback, (void*)TRUE); 291 IOHIDManagerRegisterDeviceRemovalCallback(manager, __deviceCallback, (void*)FALSE); 292 293 if ( gReport ) { 294 295 if ( gPollInterval == 0.0 ) { 296 IOHIDManagerRegisterInputReportCallback(manager, __deviceReportCallback, NULL); 297 } 298 299 } 300 if ( gValue ) 301 IOHIDManagerRegisterInputValueCallback(manager, __deviceValueCallback, NULL); 302 303 IOHIDManagerScheduleWithRunLoop(manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 304 IOHIDManagerSetDeviceMatching(manager, matching); 305 IOHIDManagerOpen(manager, 0); 306 307 CFRunLoopRun(); 308 309 if ( matching ) 310 CFRelease(matching); 311 312 return 0; 313} 314