//
// report.m
// IOHIDFamily
//
// Created by Matt Dekom on 8/15/17.
//
#import <Foundation/Foundation.h>
#include <IOKit/hid/IOHIDLib.h>
#include <getopt.h>
#include <mach/mach_time.h>
#include "utility.h"
int report(int argc, const char * argv[]);
static const char MAIN_OPTIONS_SHORT[] = "m:g:s:t:h";
static const struct option MAIN_OPTIONS[] =
{
{ "matching", required_argument, NULL, 'm' },
{ "get", required_argument, NULL, 'g' },
{ "set", required_argument, NULL, 's' },
{ "type", required_argument, NULL, 't' },
{ "help", 0, NULL, 'h' },
{ NULL, 0, NULL, 0 }
};
const char reportUsage[] =
"\nMonitor HID device reports\n"
"\nUsage:\n\n"
" hidutil report [--get <reportID> ][ --set <reportID> <bytes> ][ --type <reportType> ][ --matching <matching> ]\n"
"\nFlags:\n\n"
" -g --get...................get report for report ID. Use --type to specify report type\n"
" -s --set...................set report with report ID and data. Use --type to specify report type\n"
" -t --type..................report type (input, output, feature), defaults to feature\n"
MATCHING_HELP
"\nExamples:\n\n"
" hidutil report\n"
" hidutil report --get 01 --type feature\n"
" hidutil report --set 02 0a 0b 0c --type output\n"
" hidutil report --matching '{\"PrimaryUsagePage\":1,\"PrimaryUsage\":6}'\n";
static void reportCallback(void *context __unused, IOReturn result __unused, void *sender, IOHIDReportType type __unused, uint32_t reportID __unused, uint8_t *report, CFIndex reportLength) {
IOHIDDeviceRef device = (IOHIDDeviceRef)sender;
uint64_t absTime = mach_absolute_time();
uint64_t regID;
IORegistryEntryGetRegistryEntryID(IOHIDDeviceGetService(device), ®ID);
printf("timestamp: for (unsigned int index=0; index < (unsigned int)reportLength; index++)
printf(" printf("\n");
}
static void getReport(IOHIDManagerRef manager, uint8_t reportID, IOHIDReportType reportType) {
NSArray *devices = (NSArray *)CFBridgingRelease(IOHIDManagerCopyDevices(manager));
for (id device in devices) {
IOHIDDeviceRef deviceRef = (__bridge IOHIDDeviceRef)device;
NSNumber *reportSize = nil;
IOReturn result = kIOReturnError;
uint8_t *report = NULL;
CFIndex reportLength;
uint64_t regID;
IORegistryEntryGetRegistryEntryID(IOHIDDeviceGetService(deviceRef), ®ID);
if (reportType == kIOHIDReportTypeInput) {
reportSize = (__bridge NSNumber *)IOHIDDeviceGetProperty(deviceRef, CFSTR(kIOHIDMaxInputReportSizeKey));
} else if (reportType == kIOHIDReportTypeOutput) {
reportSize = (__bridge NSNumber *)IOHIDDeviceGetProperty(deviceRef, CFSTR(kIOHIDMaxOutputReportSizeKey));
} else if (reportType == kIOHIDReportTypeFeature) {
reportSize = (__bridge NSNumber *)IOHIDDeviceGetProperty(deviceRef, CFSTR(kIOHIDMaxFeatureReportSizeKey));
}
if (!reportSize) {
continue;
}
IOHIDDeviceOpen(deviceRef, 0);
reportLength = reportSize.unsignedIntValue;
report = malloc(reportLength);
bzero(report, reportLength);
result = IOHIDDeviceGetReport(deviceRef, reportType, reportID, report, &reportLength);
if (result != kIOReturnSuccess) {
printf("GetReport failed for device 0x } else {
printf("GetReport device:0x for (unsigned int index=0; index < (unsigned int)reportLength; index++)
printf(" printf("\n");
}
IOHIDDeviceClose(deviceRef, 0);
free(report);
}
}
static void setReport(IOHIDManagerRef manager, uint8_t reportID, IOHIDReportType reportType, uint8_t *report, uint32_t reportLength) {
NSArray *devices = (NSArray *)CFBridgingRelease(IOHIDManagerCopyDevices(manager));
for (id device in devices) {
IOHIDDeviceRef deviceRef = (__bridge IOHIDDeviceRef)device;
IOReturn result = kIOReturnError;
uint64_t regID;
IORegistryEntryGetRegistryEntryID(IOHIDDeviceGetService(deviceRef), ®ID);
IOHIDDeviceOpen(deviceRef, 0);
result = IOHIDDeviceSetReport(deviceRef, reportType, reportID, report, reportLength);
if (result != kIOReturnSuccess) {
printf("SetReport failed for device 0x } else {
printf("SetReport device:0x for (unsigned int index=0; index < (unsigned int)reportLength; index++)
printf(" printf("\n");
}
IOHIDDeviceClose(deviceRef, 0);
}
}
int report(int argc __unused, const char * argv[] __unused) {
int status = STATUS_SUCCESS;
IOHIDManagerRef manager = NULL;
char *matching = NULL;
bool get = false;
bool set = false;
uint8_t reportID = 0;
uint8_t *reportData = NULL;
uint32_t reportLength = 0;
IOHIDReportType reportType = kIOHIDReportTypeFeature;
int arg, tmpLength;
while ((arg = getopt_long(argc, (char **) argv, MAIN_OPTIONS_SHORT, MAIN_OPTIONS, NULL)) != -1) {
switch (arg) {
// --help
case 'h':
printf(" goto exit;
// --matching
case 'm':
matching = optarg;
break;
// --get
case 'g':
get = true;
reportID = strtol(optarg, NULL, 16);
break;
// --set
case 's':
if (set) {
continue;
}
set = true;
reportID = strtol(optarg, NULL, 16);
tmpLength = optind;
for(;tmpLength < argc && *argv[tmpLength] != '-'; tmpLength++) {
reportLength++;
}
if (!reportLength) {
status = STATUS_ERROR;
goto exit;
}
reportData = malloc(reportLength);
tmpLength = 0;
for(;optind < argc && *argv[optind] != '-'; optind++) {
reportData[tmpLength++] = strtol(argv[optind], NULL, 16);
}
break;
// --type
case 't':
if (strcmp(optarg, "input") == 0) {
reportType = kIOHIDReportTypeInput;
} else if (strcmp(optarg, "output") == 0) {
reportType = kIOHIDReportTypeOutput;
} else if (strcmp(optarg, "feature") == 0) {
reportType = kIOHIDReportTypeFeature;
} else {
printf("Unknown report type: status = STATUS_ERROR;
goto exit;
}
break;
default:
printf(" goto exit;
break;
}
}
manager = IOHIDManagerCreate(kCFAllocatorDefault, 0);
if (!manager) {
goto exit;
}
if (matching) {
if (!setManagerMatching(manager, matching)) {
printf("bad matching string: IOHIDManagerSetDeviceMatching(manager, NULL);
}
} else {
IOHIDManagerSetDeviceMatching(manager, NULL);
}
// get report
if (get) {
getReport(manager, reportID, reportType);
goto exit;
}
// set report
if (set) {
setReport(manager, reportID, reportType, reportData, reportLength);
goto exit;
}
IOHIDManagerRegisterInputReportCallback(manager, reportCallback, NULL);
IOHIDManagerScheduleWithRunLoop(manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
IOHIDManagerOpen(manager, kIOHIDOptionsTypeNone);
CFRunLoopRun();
IOHIDManagerUnscheduleFromRunLoop(manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
IOHIDManagerClose(manager, kIOHIDOptionsTypeNone);
exit:
if (manager) {
CFRelease(manager);
}
if (reportData) {
free(reportData);
}
return status;
}