--- /dev/null
+/**
+ * File: hiddata-OSX.c
+ * Version for OS/X 10.9+
+ * 05-jan-2014 pa
+ * License: ?
+ */
+
+#include "hiddata.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+// OS X: Using IOKit HID functionality
+#include <IOKit/IOKitLib.h>
+#include <IOKit/hid/IOHIDDevice.h>
+
+
+#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);
+}