From 5b19dc4bd3e7b57f8bca15d3df40112b4eaadca1 Mon Sep 17 00:00:00 2001 From: pavel Date: Sun, 11 Jan 2015 20:28:58 +0200 Subject: [PATCH] Added OS X, split hiddata.c files --- commandline/hiddata_osx.c | 268 ++++++++++++++++++ .../hidusb-relay-cmd(vc2013).vcxproj.filters | 2 +- 2 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 commandline/hiddata_osx.c diff --git a/commandline/hiddata_osx.c b/commandline/hiddata_osx.c new file mode 100644 index 0000000..803961b --- /dev/null +++ b/commandline/hiddata_osx.c @@ -0,0 +1,268 @@ +/** + * File: hiddata-OSX.c + * Version for OS/X 10.9+ + * 05-jan-2014 pa + * License: ? + */ + +#include "hiddata.h" +#include +#include +#include +#include + +// OS X: Using IOKit HID functionality +#include +#include + + +#if 0 //ifdef DEBUG +#define DEBUG_PRINT(arg) printf arg +#else +#define DEBUG_PRINT(arg) +#endif + +static IOHIDManagerRef g_hidmgr = NULL; +static int g_hidmgr_open_count = 0; + +// Device handle is IOHIDDeviceRef +static inline IOHIDDeviceRef usbh_to_IOHIDDeviceRef(USBDEVHANDLE usbh) +{ + return (IOHIDDeviceRef)(void*)usbh; +} + + +static void _hidmgrDeref(void) +{ + if ( g_hidmgr && (--g_hidmgr_open_count <= 0) ) { + IOHIDManagerClose(g_hidmgr, kIOHIDOptionsTypeNone); + CFRelease(g_hidmgr); + g_hidmgr = NULL; + } +} + + +static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key) +{ + int32_t value = -1; + + CFTypeRef ref = IOHIDDeviceGetProperty(device, key); + if (ref) { + if (CFGetTypeID(ref) == CFNumberGetTypeID()) { + value = 0; + CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &value); + } + } + return value; +} + +static int get_string_property_utf8(IOHIDDeviceRef device, CFStringRef prop, char *buf, ssize_t len) +{ + *buf = 0; + if (len < 2) + return 0; + CFStringRef str = IOHIDDeviceGetProperty(device, prop); + if (!str) + return USBHID_ERR_BAD_ARG; + + CFIndex str_len = CFStringGetLength(str); + CFIndex chars_copied = 0; + if (str_len) { + CFIndex used_buf_len; + len--; + chars_copied = CFStringGetBytes(str, + CFRangeMake(0, CFStringGetLength(str)), + kCFStringEncodingUTF8, + '?', + FALSE, + (void*)buf, + len, + &used_buf_len); + buf[used_buf_len] = 0; + } + return (int)chars_copied; +} + + +// Enumerate devices +int usbhidEnumDevices(int vendor, int product, + void *context, + int (*usbhidEnumFunc)(USBDEVHANDLE usbh, void *ctx)) + +{ + /* Enumerate thru HID devices */ + IOHIDManagerRef mgr; + mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + if (!mgr) + return USBHID_ERR_IO_HID; + // *** TODO: match by vendor/product right here *** + IOHIDManagerSetDeviceMatching(mgr, NULL); + IOReturn ir = IOHIDManagerOpen(mgr, kIOHIDOptionsTypeNone); + if (ir != kIOReturnSuccess) + return USBHID_ERR_IO_HID; + + CFSetRef device_set = IOHIDManagerCopyDevices(mgr); + CFIndex num_devices = CFSetGetCount(device_set); + if (0 == num_devices) + return USBHID_ERR_NOTFOUND; + + g_hidmgr = mgr; + g_hidmgr_open_count = 1; + + IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); + CFSetGetValues(device_set, (const void **) device_array); + + int i; + for (i = 0; i < num_devices; i++) { + IOHIDDeviceRef dev = device_array[i]; + char buf[32]; + uint16_t nvid, npid; + + if (!dev) + continue; /* device gone during enumeration? */ + + /* Skip non-USB devices */ + get_string_property_utf8(dev, CFSTR(kIOHIDTransportKey), buf, sizeof(buf)); + if (0 != strcasecmp(buf, "USB")) + continue; + + nvid = (uint16_t)get_int_property(dev, CFSTR(kIOHIDVendorIDKey)); + npid = (uint16_t)get_int_property(dev, CFSTR(kIOHIDProductIDKey)); + + if (nvid != (uint16_t)vendor || npid != (uint16_t)product) { + continue; + } + //printf("Device: %p\n", dev); + + /* the device handle is owned by the callback, it shoud call usbhidCloseDevice() on it */ + /* No need to open the device only for control pipe io (get/set HID feature) */ + ++g_hidmgr_open_count; + if ( 0 == usbhidEnumFunc((USBDEVHANDLE)dev, context) ) + { + break; /* stop enumeration */ + } + + } + + free(device_array); + _hidmgrDeref(); + return 0; +} + +/* ------------------------------------------------------------------------- */ + +void usbhidCloseDevice(USBDEVHANDLE usbh) +{ + IOHIDDeviceRef dev = usbh_to_IOHIDDeviceRef(usbh); + //printf("close %p\n", dev); + if (dev) { + _hidmgrDeref(); + } +} + +/* ------------------------------------------------------------------------- */ + +int usbhidGetVendorString(USBDEVHANDLE usbh, char *buffer, int len) +{ + IOHIDDeviceRef dev = usbh_to_IOHIDDeviceRef(usbh); + int len2 = get_string_property_utf8(dev, CFSTR(kIOHIDManufacturerKey), buffer, len); + if (len2 < 0) { + fprintf(stderr, "Warning: cannot query vendor for device\n"); + return USBHID_ERR_IO_HID; + } + return 0; +} + +int usbhidGetProductString(USBDEVHANDLE usbh, char *buffer, int len) +{ + IOHIDDeviceRef dev = usbh_to_IOHIDDeviceRef(usbh); + int len2 = get_string_property_utf8(dev, CFSTR(kIOHIDProductKey), buffer, len); + if (len2 < 0) { + fprintf(stderr, "Warning: cannot query product for device\n"); + return USBHID_ERR_IO_HID; + } + return 0; +} + + +static inline bool usesReportIDs(USBDEVHANDLE usbh) { + /* true => the device (or underlying HID layer) returns the report ID + in the 1st byte of Get report buffer, and expects the id in Set request. + The v-usb relay devices do not do this. + */ + return false; +} + + +int usbhidSetReport(USBDEVHANDLE usbh, char *buffer, int len) +{ + IOHIDDeviceRef dev = usbh_to_IOHIDDeviceRef(usbh); + char reportNumber = buffer[0]; + + if ( !usesReportIDs(usbh) ) { + buffer++; /* skip report ID */ + len--; + } + + IOReturn + res = IOHIDDeviceSetReport(dev, kIOHIDReportTypeFeature, (CFIndex)reportNumber, (void*)buffer, (CFIndex)len); + if (res != kIOReturnSuccess) { + fprintf(stderr, "Error sending message: %d\n", (int)res); + return USBHID_ERR_IO_HID; + } + return 0; +} + +/* ------------------------------------------------------------------------- */ + +int usbhidGetReport(USBDEVHANDLE usbh, int reportNumber, char *buffer, int *len) +{ + IOHIDDeviceRef dev = usbh_to_IOHIDDeviceRef(usbh); + int maxLen = *len; + if (maxLen < 2) + return USBHID_ERR_BAD_ARG; + + if (!usesReportIDs(usbh)) { + buffer++; /* make room for dummy report ID */ + maxLen--; + } + + CFIndex rb = maxLen; + IOReturn + res = IOHIDDeviceGetReport(dev, kIOHIDReportTypeFeature, (CFIndex)reportNumber, (void*)buffer, &rb); + + if (res != kIOReturnSuccess || ((unsigned)rb <= 0)) { + fprintf(stderr, "Error get report: %d\n", res); + return USBHID_ERR_IO_HID; + } + + *len = (int)rb; + + if (!usesReportIDs(usbh)) { + buffer[-1] = (char)reportNumber; /* add dummy report ID */ + (*len)++; + } + return 0; +} + + +void usbhidSetUsesReportId(USBDEVHANDLE usbh) +{ + //TODO Implement if some devices prepend report IDs +} + + +int usbhidStrerror_r( int err, char *buf, int len) +{ + const char *s; + switch (err) { + case USBHID_ERR_ACCESS: s = "Access to device denied"; + case USBHID_ERR_NOTFOUND: s = "The specified device was not found"; + case USBHID_ERR_IO: s = "Communication error with device"; + case USBHID_ERR_IO_HID: s = "HID I/O error with device"; + default: + s = ""; + } + + return snprintf(buf, len, "%s", s); +} diff --git a/commandline/hidusb-relay-cmd(vc2013).vcxproj.filters b/commandline/hidusb-relay-cmd(vc2013).vcxproj.filters index 607c33c..8b1cb26 100644 --- a/commandline/hidusb-relay-cmd(vc2013).vcxproj.filters +++ b/commandline/hidusb-relay-cmd(vc2013).vcxproj.filters @@ -26,7 +26,7 @@ - + Source Files -- 2.25.1