IOHIDUserDeviceTest.c [plain text]
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/hid/IOHIDUserDevice.h>
#include <IOKit/hid/IOHIDUsageTables.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <AssertMacros.h>
#include "IOHIDReportDescriptorParser.h"
typedef struct __attribute__((packed)) {
UInt8 modifiers;
UInt8 reserved;
UInt8 keys[6];
} KeyboardInputReport;
typedef struct __attribute__((packed)) {
UInt8 leds;
} KeyboardOutputReport;
static UInt8 gKeyboardDesc[] = {
0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0, 0x29, 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x02, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x02, 0x91, 0x02, 0x95, 0x01, 0x75, 0x06, 0x91, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x05, 0x07, 0x19, 0x00, 0x29, 0xFF, 0x81, 0x00, 0xC0, };
static UInt8 gMediaButtonsDesc[] = {
0x05, 0x0C, 0x09, 0x01, 0xA1, 0x01, 0x05, 0x0C, 0x09, 0xB5, 0x09, 0xB6, 0x09, 0xCD, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x04, 0x81, 0x02, 0x75, 0x04, 0x95, 0x01, 0x81, 0x01, 0xC0, };
static UInt8 gTelephonyButtonsDesc[] = {
0x05, 0x0B, 0x09, 0x01, 0xA1, 0x01, 0x05, 0x0B, 0x09, 0x21, 0x09, 0xB0, 0x09, 0xB1, 0x09, 0xB2, 0x09, 0xB3, 0x09, 0xB4, 0x09, 0xB5, 0x09, 0xB6, 0x09, 0xB7, 0x09, 0xB8, 0x09, 0xB9, 0x09, 0xBA, 0x09, 0xBB, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x0D, 0x81, 0x02, 0x75, 0x03, 0x95, 0x01, 0x81, 0x01, 0xC0, };
typedef struct __attribute__((packed)) {
UInt16 usage;
} UnicodeReport;
static UInt8 gUnicodeDesc[] = {
0x05, 0x10, 0x09, 0x00, 0xA1, 0x01, 0x05, 0x10, 0x19, 0x00, 0x2A, 0xFF, 0xFF, 0x75, 0x10, 0x95, 0x01, 0x81, 0x00, 0xC0, };
typedef struct __attribute__((packed)) {
uint8_t state;
uint16_t x;
uint16_t y;
} SingleTouchDigitizerReport;
static UInt8 gSingleTouchDigitizerDesc[] = {
0x05, 0x0D, 0x09, 0x04, 0xA1, 0x01, 0x05, 0x0D, 0x09, 0x22, 0xA1, 0x02, 0x05, 0x0D, 0x09, 0x33, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0x81, 0x02, 0x75, 0x07, 0x95, 0x01, 0x81, 0x01, 0x05, 0x01, 0x09, 0x30, 0x15, 0x00, 0x26, 0x20, 0x03, 0x75, 0x10, 0x95, 0x01, 0x81, 0x02, 0x09, 0x31, 0x15, 0x00, 0x26, 0xE0, 0x01, 0x75, 0x10, 0x95, 0x01, 0x81, 0x02, 0xC0, 0xC0, };
typedef struct __attribute__((packed)) {
struct {
uint8_t state;
uint8_t id;
uint16_t x;
uint16_t y;
} tranducers[2];
} MultiTouchDigitizerReport;
static UInt8 gMultiTouchDigitizerDesc[] = {
0x05, 0x0D, 0x09, 0x04, 0xA1, 0x01, 0x05, 0x0D, 0x09, 0x22, 0xA1, 0x02, 0x05, 0x0D, 0x09, 0x38, 0x75, 0x08, 0x95, 0x01, 0x81, 0x02, 0x09, 0x33, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0x81, 0x02, 0x75, 0x07, 0x95, 0x01, 0x81, 0x01, 0x05, 0x01, 0x09, 0x30, 0x15, 0x00, 0x26, 0x20, 0x03, 0x75, 0x10, 0x95, 0x01, 0x81, 0x02, 0x09, 0x31, 0x15, 0x00, 0x26, 0xE0, 0x01, 0x75, 0x10, 0x95, 0x01, 0x81, 0x02, 0xC0, 0x05, 0x0D, 0x09, 0x22, 0xA1, 0x02, 0x05, 0x0D, 0x09, 0x38, 0x75, 0x08, 0x95, 0x01, 0x81, 0x02, 0x09, 0x33, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0x81, 0x02, 0x75, 0x07, 0x95, 0x01, 0x81, 0x01, 0x05, 0x01, 0x09, 0x30, 0x15, 0x00, 0x26, 0x20, 0x03, 0x75, 0x10, 0x95, 0x01, 0x81, 0x02, 0x09, 0x31, 0x15, 0x00, 0x26, 0xE0, 0x01, 0x75, 0x10, 0x95, 0x01, 0x81, 0x02, 0xC0, 0xC0, };
typedef struct __attribute__((packed)) {
SingleTouchDigitizerReport touch;
struct {
UInt8 string[4];
UInt8 length;
UInt8 encoding;
UInt8 quality;
} chars[2];
} DigitizerUnicodeInputReport;
typedef struct __attribute__((packed)) {
UInt8 enable;
} DigitizerUnicodeOutputReport;
static UInt8 gDigitizerUnicodeDesc[] = {
0x05, 0x0D, 0x09, 0x05, 0xA1, 0x01, 0x05, 0x0D, 0x09, 0x22, 0xA1, 0x02, 0x05, 0x0D, 0x09, 0x33, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0x81, 0x02, 0x75, 0x07, 0x95, 0x01, 0x81, 0x01, 0x05, 0x01, 0x09, 0x30, 0x15, 0x00, 0x26, 0x00, 0x04, 0x75, 0x10, 0x95, 0x01, 0x81, 0x02, 0x09, 0x31, 0x15, 0x00, 0x26, 0x00, 0x04, 0x75, 0x10, 0x95, 0x01, 0x81, 0x02, 0xC0, 0x05, 0x0D, 0x09, 0x23, 0xA1, 0x02, 0x05, 0x0D, 0x09, 0x60, 0x15, 0x00, 0x25, 0x01, 0x95, 0x01, 0x75, 0x01, 0xB1, 0x02, 0x95, 0x01, 0x75, 0x07, 0xB1, 0x01, 0xC0, 0x05, 0x0D, 0x09, 0x24, 0xA1, 0x02, 0x05, 0x0D, 0x09, 0x63, 0x75, 0x20, 0x95, 0x01, 0x82, 0x02, 0x01, 0x09, 0x62, 0x09, 0x66, 0x75, 0x08, 0x95, 0x02, 0x81, 0x02, 0x09, 0x61, 0x15, 0x00, 0x25, 0x64, 0x75, 0x08, 0x95, 0x01, 0x81, 0x02, 0xC0, 0x05, 0x0D, 0x09, 0x24, 0xA1, 0x02, 0x05, 0x0D, 0x09, 0x63, 0x75, 0x20, 0x95, 0x01, 0x82, 0x02, 0x01, 0x09, 0x62, 0x09, 0x66, 0x75, 0x08, 0x95, 0x02, 0x81, 0x02, 0x09, 0x61, 0x15, 0x00, 0x25, 0x64, 0x75, 0x08, 0x95, 0x01, 0x81, 0x02, 0xC0, 0xC0, };
typedef struct __attribute__((packed)) {
uint8_t buttonMask;
uint8_t x;
uint8_t y;
uint8_t scroll;
} MultiAxisDeviceInputReport;
static UInt8 gMultiAxisInputDesc[] = {
0x05, 0x01, 0x09, 0x08, 0xA1, 0x01, 0x05, 0x09, 0x09, 0x01, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0x81, 0x02, 0x05, 0x0C, 0x0A, 0x23, 0x02, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0x81, 0x02, 0x05, 0x0C, 0x0A, 0x24, 0x02, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0x81, 0x02, 0x75, 0x05, 0x95, 0x01, 0x81, 0x01, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, 0x81, 0x25, 0x7F, 0x75, 0x08, 0x95, 0x02, 0x81, 0x02, 0xC0, };
static IOHIDUserDeviceRef gDevice = NULL;
static pthread_mutex_t gMuxtex = PTHREAD_MUTEX_INITIALIZER;
static uint32_t gGetReportDelay = 0;
static void printReport(uint8_t * report, CFIndex reportLength, bool rcv)
{
int index;
printf("%s report: reportLength=%ld: ", rcv ? "Received" : "Dispatched", reportLength);
for (index=0; index<reportLength; index++)
printf("%02x ", report[index]);
printf("\n");
}
#define kKeyboardInterval 0.008
static void dispatchKeyboardEvent(char c)
{
KeyboardInputReport report;
static CFAbsoluteTime sDeadline = 0;
CFAbsoluteTime delta;
bzero(&report, sizeof(report));
if ( c < 'a' || c > 'z' )
return;
printf("dispatching keyboard event for '%c'\n", c);
pthread_mutex_lock(&gMuxtex);
delta = sDeadline - CFAbsoluteTimeGetCurrent();
if ( delta > 0 )
usleep(delta*1000000);
report.keys[0] = 4 + c - 97;
printReport((uint8_t*)&report, sizeof(report), 0);
IOHIDUserDeviceHandleReport(gDevice, (uint8_t*)&report, sizeof(report));
usleep(kKeyboardInterval*1000000);
report.keys[0] = 0;
printReport((uint8_t*)&report, sizeof(report), 0);
IOHIDUserDeviceHandleReport(gDevice, (uint8_t*)&report, sizeof(report));
sDeadline = CFAbsoluteTimeGetCurrent() + kKeyboardInterval;
pthread_mutex_unlock(&gMuxtex);
}
static void * getKeyboardCharThread(void *arg)
{
printf("This virtual keyboard supports dispatching typed characters within the range of 'a' - 'z'\n");
while (1) {
dispatchKeyboardEvent(getchar());
}
return arg;
}
static void dispatchUnicodeEvent(char c)
{
UnicodeReport report;
static CFAbsoluteTime sDeadline = 0;
CFAbsoluteTime delta;
bzero(&report, sizeof(report));
if ( c < 'a' || c > 'z' )
return;
printf("dispatching unicode event for '%c'\n", c);
pthread_mutex_lock(&gMuxtex);
delta = sDeadline - CFAbsoluteTimeGetCurrent();
if ( delta > 0 )
usleep(delta*1000000);
report.usage = c;
OSSwapHostToLittleInt16(report.usage);
printReport((uint8_t*)&report, sizeof(report), 0);
IOHIDUserDeviceHandleReport(gDevice, (uint8_t*)&report, sizeof(report));
sDeadline = CFAbsoluteTimeGetCurrent() + kKeyboardInterval;
pthread_mutex_unlock(&gMuxtex);
}
static void * getUnicodeCharThread(void *arg)
{
printf("This virtual unicode supports dispatching typed character within the range of 'a' - 'z'\n");
while (1) {
dispatchUnicodeEvent(getchar());
}
return arg;
}
static void dispatchDigitizerUnicodeEvent(char c)
{
DigitizerUnicodeInputReport report;
static CFAbsoluteTime sDeadline = 0;
CFAbsoluteTime delta;
if ( c < 'a' || c > 'z' )
return;
printf("dispatching digitizer unicode event for '%c'\n", c);
bzero(&report, sizeof(report));
report.chars[0].encoding = 1;
report.chars[0].quality = 50;
report.chars[0].length = 2;
report.chars[0].string[0] = c;
report.chars[1].encoding = 1;
report.chars[1].quality = 50;
report.chars[1].length = 2;
report.chars[1].string[0] = c-32;
pthread_mutex_lock(&gMuxtex);
delta = sDeadline - CFAbsoluteTimeGetCurrent();
if ( delta > 0 )
usleep(delta*1000000);
printReport((uint8_t*)&report, sizeof(report), 0);
IOHIDUserDeviceHandleReport(gDevice, (uint8_t*)&report, sizeof(report));
sDeadline = CFAbsoluteTimeGetCurrent() + kKeyboardInterval;
bzero(&report, sizeof(report));
printReport((uint8_t*)&report, sizeof(report), 0);
IOHIDUserDeviceHandleReport(gDevice, (uint8_t*)&report, sizeof(report));
pthread_mutex_unlock(&gMuxtex);
}
static void * getDigitizerUnicodeCharThread(void *arg)
{
printf("This virtual digitizer unicode supports dispatching typed character within the range of 'a' - 'z'\n");
while (1) {
dispatchDigitizerUnicodeEvent(getchar());
}
return arg;
}
static uint8_t __report[256] = {};
IOReturn getReportCallback(void * refcon, IOHIDReportType type, uint32_t reportID, uint8_t * report, CFIndex reportLength);
IOReturn getReportCallback(void * refcon __unused, IOHIDReportType type __unused, uint32_t reportID __unused, uint8_t * report, CFIndex reportLength)
{
if ( !report || !reportLength )
return kIOReturnBadArgument;
if (gGetReportDelay)
usleep(gGetReportDelay);
bcopy(__report, report, reportLength);
return kIOReturnSuccess;
}
static void * getReportCharThread(void *arg)
{
printf("Please enter report data: 00 11 22 33 ...\n");
while (1) {
char str[1024];
bzero(str, sizeof(str));
if ( fgets(str, sizeof(str), stdin) ) {
CFStringRef rawString = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault, (uint8_t *)str, sizeof(str), kCFStringEncodingMacRoman, false, kCFAllocatorNull);
if ( rawString ) {
CFArrayRef rawArray = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, rawString, CFSTR("\n"));
if ( rawArray ) {
if ( CFArrayGetCount(rawArray) > 1 ) {
CFArrayRef array = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, CFArrayGetValueAtIndex(rawArray, 0), CFSTR(" "));
if ( array ) {
uint32_t reportIndex = 0;
bzero(__report, sizeof(__report));
for ( int index=0; index<CFArrayGetCount(array) && reportIndex<sizeof(__report); index++) {
CFStringRef substring = (CFStringRef)CFArrayGetValueAtIndex(array, index);
const char * substr;
if ( !substring )
continue;
substr = CFStringGetCStringPtr(substring, kCFStringEncodingMacRoman);
if ( !substr )
continue;
__report[reportIndex++] = strtoul(substr, NULL, 16);
}
pthread_mutex_lock(&gMuxtex);
printReport(__report, reportIndex, 0);
IOHIDUserDeviceHandleReport(gDevice, __report, reportIndex);
pthread_mutex_unlock(&gMuxtex);
CFRelease(array);
}
}
CFRelease(rawArray);
}
CFRelease(rawString);
}
}
}
return arg;
}
static IOReturn setReportCallback(void * refcon __unused, IOHIDReportType type __unused, uint32_t reportID __unused, uint8_t * report, CFIndex reportLength)
{
printReport(report, reportLength, 1);
return kIOReturnSuccess;
}
typedef void * (*UserInputCallback)(void * refcon);
static void startDevice(CFMutableDictionaryRef properties, uint32_t reportinterval, const uint8_t * descriptor, uint32_t descriptorLength, UserInputCallback userInputCallback, IOHIDUserDeviceReportCallback outputReportCallback, IOHIDUserDeviceReportCallback inputReportCallback)
{
CFDataRef descriptorData = NULL;
CFNumberRef timeoutNumber = NULL;
CFNumberRef intervalNumber = NULL;
uint32_t value = 5000000;
descriptorData = CFDataCreate(kCFAllocatorDefault, descriptor, descriptorLength);
require(descriptorData, finish);
timeoutNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value);
require(timeoutNumber, finish);
intervalNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reportinterval);
require(intervalNumber, finish);
require(properties, finish);
CFDictionarySetValue(properties, CFSTR(kIOHIDReportDescriptorKey), descriptorData);
CFDictionarySetValue(properties, CFSTR(kIOHIDRequestTimeoutKey), timeoutNumber);
CFDictionarySetValue(properties, CFSTR(kIOHIDReportIntervalKey), intervalNumber);
gDevice = IOHIDUserDeviceCreate(kCFAllocatorDefault, properties);
require(gDevice, finish);
IOHIDUserDeviceScheduleWithRunLoop(gDevice, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
if ( outputReportCallback )
IOHIDUserDeviceRegisterSetReportCallback(gDevice, outputReportCallback, NULL);
if ( inputReportCallback )
IOHIDUserDeviceRegisterGetReportCallback(gDevice, inputReportCallback, NULL);
if ( userInputCallback ) {
pthread_t tid;
pthread_attr_t attr;
assert(!pthread_attr_init(&attr));
assert(!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
assert(!pthread_create( &tid, &attr, userInputCallback, NULL));
assert(!pthread_attr_destroy(&attr));
}
CFRunLoopRun();
finish:
if ( gDevice )
CFRelease(gDevice);
if ( descriptorData )
CFRelease(descriptorData);
if ( timeoutNumber )
CFRelease(timeoutNumber);
if ( intervalNumber )
CFRelease(intervalNumber);
}
static void printHelp()
{
printf("\n");
printf("hidUserDeviceTest usage:\n\n");
printf("\t-d <descriptor data: 00 11 22...>\t: create device with descriptor data\n");
printf("\t-p Parse descriptor data\n");
printf("\t-k Create generic keyboard device with user generated char input\n");
printf("\t-kr Create generic keyboard device with user generated reports\n");
printf("\t--vid <vendor id>\n");
printf("\t--pid <product id>\n");
printf("\t--ri <report interval us>\n");
printf("\t--transport <transport string value>\n");
printf("\t--rdelay <getReport delay in uS>");
printf("\n");
}
int main (int argc, const char * argv[])
{
bool handled = false;
bool parse = false;
bool dataString = false;
uint8_t * data = NULL;
uint32_t dataSize = 0;
uint32_t dataIndex = 0;
uint32_t reportInterval = 16000;
UserInputCallback userInputCallback = NULL;
IOHIDUserDeviceReportCallback outputReportCallback = NULL;
IOHIDUserDeviceReportCallback inputReportCallback = getReportCallback;
CFMutableDictionaryRef properties = NULL;
properties = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
for ( int argi=1; argi<argc; argi++ ) {
if ( argv[argi][0] == '-' ) {
if ( !strcmp("-k", argv[argi]) || !strcmp("-kr", argv[argi]) ) {
if ( data ) {
free(data);
}
dataSize = sizeof(gKeyboardDesc);
data = malloc(dataSize);
userInputCallback = !strcmp("-k", argv[argi]) ? getKeyboardCharThread : getReportCharThread;
outputReportCallback = setReportCallback;
handled = true;
bcopy(gKeyboardDesc, data, dataSize);
dataIndex = dataSize;
}
if ( !strcmp("-u", argv[argi]) || !strcmp("-ur", argv[argi]) ) {
if ( data ) {
free(data);
}
dataSize = sizeof(gUnicodeDesc);
data = malloc(dataSize);
userInputCallback = !strcmp("-u", argv[argi]) ? getUnicodeCharThread : getReportCharThread;
handled = true;
bcopy(gUnicodeDesc, data, dataSize);
dataIndex = dataSize;
}
if ( !strcmp("-mb", argv[argi]) ) {
if ( data ) {
free(data);
}
dataSize = sizeof(gMediaButtonsDesc);
data = malloc(dataSize);
userInputCallback = getReportCharThread;
outputReportCallback = setReportCallback;
handled = true;
bcopy(gMediaButtonsDesc, data, dataSize);
dataIndex = dataSize;
}
if ( !strcmp("-tb", argv[argi]) ) {
if ( data ) {
free(data);
}
dataSize = sizeof(gTelephonyButtonsDesc);
data = malloc(dataSize);
userInputCallback = getReportCharThread;
outputReportCallback = setReportCallback;
handled = true;
bcopy(gTelephonyButtonsDesc, data, dataSize);
dataIndex = dataSize;
}
if ( !strcmp("-std", argv[argi]) ) {
if ( data ) {
free(data);
}
dataSize = sizeof(gSingleTouchDigitizerDesc);
data = malloc(dataSize);
userInputCallback = getReportCharThread;
outputReportCallback = setReportCallback;
handled = true;
bcopy(gSingleTouchDigitizerDesc, data, dataSize);
dataIndex = dataSize;
}
if ( !strcmp("-mac", argv[argi]) ) {
if ( data ) {
free(data);
}
dataSize = sizeof(gMultiAxisInputDesc);
data = malloc(dataSize);
userInputCallback = getReportCharThread;
outputReportCallback = setReportCallback;
handled = true;
bcopy(gMultiAxisInputDesc, data, dataSize);
dataIndex = dataSize;
}
if ( !strcmp("-mtd", argv[argi]) ) {
if ( data ) {
free(data);
}
dataSize = sizeof(gMultiTouchDigitizerDesc);
data = malloc(dataSize);
userInputCallback = getReportCharThread;
outputReportCallback = setReportCallback;
handled = true;
bcopy(gMultiTouchDigitizerDesc, data, dataSize);
dataIndex = dataSize;
}
if ( !strcmp("-du", argv[argi]) || !strcmp("-dur", argv[argi]) ) {
if ( data ) {
free(data);
}
dataSize = sizeof(gDigitizerUnicodeDesc);
data = malloc(dataSize);
userInputCallback = !strcmp("-du", argv[argi]) ? getDigitizerUnicodeCharThread : getReportCharThread;
outputReportCallback = setReportCallback;
handled = true;
bcopy(gDigitizerUnicodeDesc, data, dataSize);
dataIndex = dataSize;
}
else if ( !strcmp("-d", argv[argi]) ) {
if ( !data ) {
dataSize = argc-argi+1;
data = malloc(dataSize);
bzero(data, dataSize);
}
userInputCallback = getReportCharThread;
outputReportCallback = setReportCallback;
handled = true;
}
else if ( !strcmp("-ds", argv[argi]) ) {
userInputCallback = getReportCharThread;
outputReportCallback = setReportCallback;
handled = true;
dataString = true;
if ( data ) {
free(data);
data = NULL;
dataSize = 0;
}
}
else if ( !strcmp("-p", argv[argi]) ) {
parse = true;
if ( !data ) {
dataSize = argc-argi+1;
data = malloc(argc-argi+1);
}
}
else if ( !strcmp("--vid", argv[argi]) && (argi+1) < argc) {
long value = strtol(argv[++argi], NULL, 10);
CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &value);
if ( number ) {
CFDictionarySetValue(properties, CFSTR(kIOHIDVendorIDKey), number);
CFRelease(number);
}
}
else if ( !strcmp("--pid", argv[argi]) && (argi+1) < argc) {
long value = strtol(argv[++argi], NULL, 10);
CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &value);
if ( number ) {
CFDictionarySetValue(properties, CFSTR(kIOHIDProductIDKey), number);
CFRelease(number);
}
}
else if ( !strcmp("--transport", argv[argi]) && (argi+1) < argc) {
CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, argv[++argi], CFStringGetSystemEncoding());
if ( string ) {
CFDictionarySetValue(properties, CFSTR(kIOHIDTransportKey), string);
CFRelease(string);
}
}
else if ( !strcmp("--rdelay", argv[argi]) && (argi+1) < argc) {
gGetReportDelay = (uint32_t)strtol(argv[++argi], NULL, 10);
}
else if ( !strcmp("--ri", argv[argi]) && (argi+1) < argc) {
reportInterval = (uint32_t)strtol(argv[++argi], NULL, 10);
}
else if ( !strcmp("--auth", argv[argi]) && (argi+1) < argc) {
int authenticated = (uint32_t)strtol(argv[++argi], NULL, 10);
if (authenticated) {
CFDictionarySetValue(properties, CFSTR("Authenticated"), kCFBooleanTrue);
} else {
CFDictionarySetValue(properties, CFSTR("Authenticated"), kCFBooleanFalse);
}
}
}
else if ( !dataString && data && dataIndex < dataSize ) {
data[dataIndex++] = strtoul(argv[argi], NULL, 16);
}
else if ( dataString && !data) {
printf("parsing data string\n");
uint32_t stringLength = (uint32_t)strlen(argv[argi]);
uint32_t stringIndex;
if ( !data ) {
dataSize = stringLength;
data = malloc(dataSize);
bzero(data, dataSize);
}
for ( stringIndex=0; stringIndex<stringLength; stringIndex+=2) {
char byteStr[3] = {};
bcopy(&(argv[argi][stringIndex]), byteStr, 2);
data[dataIndex++] = strtoul(byteStr, NULL, 16);
}
}
}
if ( data ) {
if ( dataSize > dataIndex )
dataSize = dataIndex;
if ( parse )
PrintHIDDescriptor(data, dataSize);
if ( handled ) {
startDevice(properties, reportInterval, data, dataIndex, userInputCallback, outputReportCallback, inputReportCallback);
}
free(data);
} else {
printHelp();
}
if ( properties )
CFRelease(properties);
return 0;
}