1/* 2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * The contents of this file constitute Original Code as defined in and 7 * are subject to the Apple Public Source License Version 1.1 (the 8 * "License"). You may not use this file except in compliance with the 9 * License. Please obtain a copy of the License at 10 * http://www.apple.com/publicsource and read it before using this file. 11 * 12 * This Original Code and all software distributed under the License are 13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the 17 * License for the specific language governing rights and limitations 18 * under the License. 19 * 20 * @APPLE_LICENSE_HEADER_END@ 21 */ 22 23#include <CoreFoundation/CoreFoundation.h> 24#include <IOKit/hid/IOHIDUserDevice.h> 25#include <stdio.h> 26#include <string.h> 27#include <pthread.h> 28#include <AssertMacros.h> 29#include "IOHIDReportDescriptorParser.h" 30 31typedef struct GenericLEDKeyboardDescriptor { 32 //05 01: Usage Page (Generic Desktop) 33 UInt8 devUsagePageOp; 34 UInt8 devUsagePageNum; 35 //09 06: Usage (Keyboard) 36 UInt8 devUsageOp; 37 UInt8 devUsageNum; 38 //A1 01: Collection (Application) 39 UInt8 appCollectionOp; 40 UInt8 appCollectionNum; 41 //05 07: Usage Page (Key Codes) 42 UInt8 modUsagePageOp; 43 UInt8 modUsagePageNum; 44 //19 e0: Usage Minimum...... (224) 45 UInt8 modUsageMinOp; 46 UInt8 modUsageMinNum; 47 //29 e7: Usage Maximum...... (231) 48 UInt8 modUsageMaxOp; 49 UInt8 modUsageMaxNum; 50 //15 00: Logical Minimum.... (0) 51 UInt8 modLogMinOp; 52 UInt8 modLogMinNum; 53 //25 01: Logical Maximum.... (1) 54 UInt8 modLogMaxOp; 55 UInt8 modLogMaxNum; 56 //95 08: Report Count....... (8) 57 UInt8 modRptCountOp; 58 UInt8 modRptCountNum; 59 //75 01: Report Size........ (1) 60 UInt8 modRptSizeOp; 61 UInt8 modRptSizeNum; 62 //81 02: Input (Data) 63 UInt8 modInputOp; 64 UInt8 modInputNum; 65 66 //95 01: Report Count....... (1) 67 UInt8 rsrvCountOp; 68 UInt8 rsrvCountNum; 69 //75 08: Report Size........ (8) 70 UInt8 rsrvSizeOp; 71 UInt8 rsrvSizeNum; 72 //81 01: Input (Constant) 73 UInt8 rsrvInputOp; 74 UInt8 rsrvInputNum; 75 76 77 //95 02: Report Count....... (2) 78 UInt8 ledRptCountOp; 79 UInt8 ledRptCountNum; 80 //75 01: Report Size........ (1) 81 UInt8 ledRptSizeOp; 82 UInt8 ledRptSizeNum; 83 //05 08: Usage Page (LEDs) 84 UInt8 ledUsagePageOp; 85 UInt8 ledUsagePageNum; 86 //19 01: Usage Minimum...... (1) 87 UInt8 ledUsageMinOp; 88 UInt8 ledUsageMinNum; 89 //29 02: Usage Maximum...... (2) 90 UInt8 ledUsageMaxOp; 91 UInt8 ledUsageMaxNum; 92 //91 02: Output (Data) 93 UInt8 ledInputOp; 94 UInt8 ledInputNum; 95 96 //95 01: Report Count....... (1) 97 UInt8 fillRptCountOp; 98 UInt8 fillRptCountNum; 99 //75 03: Report Size........ (3) 100 UInt8 fillRptSizeOp; 101 UInt8 fillRptSizeNum; 102 //91 01: Output (Constant) 103 UInt8 fillInputOp; 104 UInt8 fillInputNum; 105 106 107 //95 06: Report Count....... (6) 108 UInt8 keyRptCountOp; 109 UInt8 keyRptCountNum; 110 //75 08: Report Size........ (8) 111 UInt8 keyRptSizeOp; 112 UInt8 keyRptSizeNum; 113 //15 00: Logical Minimum.... (0) 114 UInt8 keyLogMinOp; 115 UInt8 keyLogMinNum; 116 //26 ff 00: Logical Maximum.... (255) 117 UInt8 keyLogMaxOp; 118 UInt16 keyLogMaxNum; 119 //05 07: Usage Page (Key Codes) 120 UInt8 keyUsagePageOp; 121 UInt8 keyUsagePageNum; 122 //19 00: Usage Minimum...... (0) 123 UInt8 keyUsageMinOp; 124 UInt8 keyUsageMinNum; 125 //29 ff: Usage Maximum...... (255) 126 UInt8 keyUsageMaxOp; 127 UInt8 keyUsageMaxNum; 128 //81 00: Input (Array) 129 UInt8 keyInputOp; 130 UInt8 keyInputNum; 131 132 //C0: End Collection 133 UInt8 appCollectionEnd; 134} GenericLEDKeyboardDescriptor; 135 136typedef struct GenericKeyboardRpt { 137 UInt8 modifiers; 138 UInt8 reserved; 139 UInt8 keys[6]; 140} GenericKeyboardRpt; 141 142static UInt8 gGenLEDKeyboardDesc[] = { 143 0x05, 0x01, 144 0x09, 0x06, 145 0xA1, 0x01, 146 0x05, 0x07, 147 0x19, 0xe0, 148 0x29, 0xe7, 149 0x15, 0x00, 150 0x25, 0x01, 151 0x75, 0x01, 152 0x95, 0x08, 153 0x81, 0x02, 154 0x95, 0x01, 155 0x75, 0x08, 156 0x81, 0x01, 157 158 0x95, 0x02, 159 0x75, 0x01, 160 0x05, 0x08, 161 0x19, 0x01, 162 0x29, 0x02, 163 0x91, 0x02, 164 0x95, 0x01, 165 0x75, 0x06, 166 0x91, 0x01, 167 168 0x95, 0x06, 169 0x75, 0x08, 170 0x15, 0x00, 171 0x26, 0xff, 0x00, 172 0x05, 0x07, 173 0x19, 0x00, 174 0x29, 0xff, 175 0x81, 0x00, 176 0xC0 177}; 178 179static IOHIDUserDeviceRef gDevice = NULL; 180static pthread_mutex_t gMuxtex = PTHREAD_MUTEX_INITIALIZER; 181 182static void printReport(uint8_t * report, CFIndex reportLength, bool rcv) 183{ 184 int index; 185 186 printf("%s report: reportLength=%ld: ", rcv ? "Received" : "Dispatching", reportLength); 187 for (index=0; index<reportLength; index++) 188 printf("%02x ", report[index]); 189 printf("\n"); 190} 191 192#define kKeyboardInterval 0.008 193 194static void dispatchKeyboardEvent(char c) 195{ 196 GenericKeyboardRpt report; 197 static CFAbsoluteTime sDeadline = 0; 198 CFAbsoluteTime delta; 199 200 bzero(&report, sizeof(report)); 201 if ( c < 'a' || c > 'z' ) 202 return; 203 204 printf("dispatching keyboard event for '%c'\n", c); 205 206 pthread_mutex_lock(&gMuxtex); 207 208 delta = sDeadline - CFAbsoluteTimeGetCurrent(); 209 if ( delta > 0 ) 210 usleep(delta*1000000); 211 212 report.keys[0] = 4 + c - 97; 213 printReport((uint8_t*)&report, sizeof(report), 0); 214 IOHIDUserDeviceHandleReport(gDevice, (uint8_t*)&report, sizeof(report)); 215 216 usleep(kKeyboardInterval*1000000); 217 218 report.keys[0] = 0; 219 printReport((uint8_t*)&report, sizeof(report), 0); 220 IOHIDUserDeviceHandleReport(gDevice, (uint8_t*)&report, sizeof(report)); 221 222 sDeadline = CFAbsoluteTimeGetCurrent() + kKeyboardInterval; 223 224 pthread_mutex_unlock(&gMuxtex); 225 226} 227 228static void * getKeyboardCharThread(void *arg) 229{ 230 printf("This virtual keyboard supports dispatching typed characters within the range of 'a' - 'z'\n"); 231 232 while (1) { 233 dispatchKeyboardEvent(getchar()); 234 } 235 return arg; 236} 237 238static void * getReportCharThread(void *arg) 239{ 240 char str[256]; 241 printf("Please enter report data: 00 11 22 33 ...\n"); 242 243 while (1) { 244 bzero(str, sizeof(str)); 245 246 if ( fgets(str, sizeof(str), stdin) ) { 247 248 CFStringRef rawString = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, (uint8_t *)str, sizeof(str), kCFStringEncodingMacRoman, false, kCFAllocatorNull); 249 250 if ( rawString ) { 251 CFArrayRef rawArray = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, rawString, CFSTR("\n")); 252 253 if ( rawArray ) { 254 if ( CFArrayGetCount(rawArray) > 1 ) { 255 CFArrayRef array = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, CFArrayGetValueAtIndex(rawArray, 0), CFSTR(" ")); 256 257 if ( array ) { 258 259 uint8_t report[64] = {}; 260 uint32_t reportIndex = 0; 261 262 for ( int index=0; index<CFArrayGetCount(array) && reportIndex<sizeof(report); index++) { 263 CFStringRef substring = (CFStringRef)CFArrayGetValueAtIndex(array, index); 264 const char * substr; 265 266 if ( !substring ) 267 continue; 268 269 substr = CFStringGetCStringPtr(substring, kCFStringEncodingMacRoman); 270 if ( !substr ) 271 continue; 272 273 report[reportIndex++] = strtoul(substr, NULL, 16); 274 } 275 276 pthread_mutex_lock(&gMuxtex); 277 printReport(report, reportIndex, 0); 278 IOHIDUserDeviceHandleReport(gDevice, report, reportIndex); 279 pthread_mutex_unlock(&gMuxtex); 280 281 CFRelease(array); 282 } 283 } 284 285 CFRelease(rawArray); 286 } 287 288 CFRelease(rawString); 289 } 290 } 291 292 } 293 return arg; 294} 295 296 297static IOReturn setReportCallback(void * refcon __unused, IOHIDReportType type __unused, uint32_t reportID __unused, uint8_t * report, CFIndex reportLength) 298{ 299 printReport(report, reportLength, 1); 300 return kIOReturnSuccess; 301} 302 303typedef void * (*UserInputCallback)(void * refcon); 304 305 306static void startDevice(CFMutableDictionaryRef properties, const uint8_t * descriptor, uint32_t descriptorLength, UserInputCallback userInputCallback, IOHIDUserDeviceReportCallback outputReportCallback) 307{ 308 CFDataRef descriptorData = NULL; 309 310 descriptorData = CFDataCreate(kCFAllocatorDefault, descriptor, descriptorLength); 311 require(descriptorData, finish); 312 313 require(properties, finish); 314 315 CFDictionarySetValue(properties, CFSTR(kIOHIDReportDescriptorKey), descriptorData); 316 317 gDevice = IOHIDUserDeviceCreate(kCFAllocatorDefault, properties); 318 require(gDevice, finish); 319 320 IOHIDUserDeviceScheduleWithRunLoop(gDevice, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 321 322 if ( outputReportCallback ) 323 IOHIDUserDeviceRegisterSetReportCallback(gDevice, outputReportCallback, NULL); 324 325 if ( userInputCallback ) { 326 pthread_t tid; 327 pthread_attr_t attr; 328 329 assert(!pthread_attr_init(&attr)); 330 assert(!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)); 331 assert(!pthread_create( &tid, &attr, userInputCallback, NULL)); 332 assert(!pthread_attr_destroy(&attr)); 333 } 334 335 CFRunLoopRun(); 336 337finish: 338 if ( gDevice ) 339 CFRelease(gDevice); 340 341 if ( descriptorData ) 342 CFRelease(descriptorData); 343} 344 345static void printHelp() 346{ 347 printf("\n"); 348 printf("hidUserDeviceTest usage:\n\n"); 349 printf("\t-d <descriptor data: 00 11 22...>\t: create device with descriptor data\n"); 350 printf("\t-p Parse descriptor data\n"); 351 printf("\t-k Create generic keyboard device\n"); 352 printf("\t--vid <vendor id>\n"); 353 printf("\t--pid <product id>\n"); 354 printf("\n"); 355} 356 357int main (int argc, const char * argv[]) 358{ 359 bool handled = false; 360 bool parse = false; 361 uint8_t * data = NULL; 362 uint32_t dataSize = 0; 363 uint32_t dataIndex = 0; 364 UserInputCallback userInputCallback = NULL; 365 IOHIDUserDeviceReportCallback outputReportCallback = NULL; 366 CFMutableDictionaryRef properties = NULL; 367 368 properties = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 369 370 for ( int argi=1; argi<argc; argi++ ) { 371 // command 372 if ( argv[argi][0] == '-' ) { 373 if ( !strcmp("-k", argv[argi] ) ) { 374 if ( data ) { 375 free(data); 376 } 377 378 dataSize = sizeof(gGenLEDKeyboardDesc); 379 data = malloc(dataSize); 380 userInputCallback = getKeyboardCharThread; 381 outputReportCallback = setReportCallback; 382 handled = true; 383 384 bcopy(gGenLEDKeyboardDesc, data, dataSize); 385 dataIndex = dataSize; 386 } 387 else if ( !strcmp("-d", argv[argi]) ) { 388 if ( !data ) { 389 dataSize = argc-argi+1; 390 data = malloc(dataSize); 391 } 392 userInputCallback = getReportCharThread; 393 outputReportCallback = setReportCallback; 394 handled = true; 395 } 396 else if ( !strcmp("-p", argv[argi]) ) { 397 parse = true; 398 399 if ( !data ) { 400 dataSize = argc-argi+1; 401 data = malloc(argc-argi+1); 402 } 403 } 404 else if ( !strcmp("--vid", argv[argi]) && (argi+1) < argc) { 405 int value = strtol(argv[++argi], NULL, 10); 406 CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &value); 407 if ( number ) { 408 CFDictionarySetValue(properties, CFSTR(kIOHIDVendorIDKey), number); 409 CFRelease(number); 410 } 411 } 412 else if ( !strcmp("--pid", argv[argi]) && (argi+1) < argc) { 413 int value = strtol(argv[++argi], NULL, 10); 414 CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &value); 415 if ( number ) { 416 CFDictionarySetValue(properties, CFSTR(kIOHIDProductIDKey), number); 417 CFRelease(number); 418 } 419 } 420 } 421 // data 422 else if ( data && dataIndex < dataSize ) { 423 data[dataIndex++] = strtoul(argv[argi], NULL, 16); 424 } 425 426 } 427 428 if ( data ) { 429 if ( parse ) 430 PrintHIDReport(data, dataSize); 431 432 if ( handled ) { 433 startDevice(properties, data, dataIndex, userInputCallback, outputReportCallback); 434 } 435 436 free(data); 437 438 } else { 439 printHelp(); 440 } 441 442 if ( properties ) 443 CFRelease(properties); 444 445 return 0; 446} 447