diff --git a/hidtest/test.c b/hidtest/test.c index 5a0841213..857300a58 100644 --- a/hidtest/test.c +++ b/hidtest/test.c @@ -55,6 +55,7 @@ int main(int argc, char* argv[]) printf(" Product: %ls\n", cur_dev->product_string); printf(" Release: %hx\n", cur_dev->release_number); printf(" Interface: %d\n", cur_dev->interface_number); + printf(" Usage (page): 0x%hx (0x%hx)\n", cur_dev->usage, cur_dev->usage_page); printf("\n"); cur_dev = cur_dev->next; } diff --git a/mac/hid.c b/mac/hid.c index 6d7b372db..11f4ebde5 100644 --- a/mac/hid.c +++ b/mac/hid.c @@ -189,6 +189,15 @@ static void register_error(hid_device *dev, const char *op) } #endif +static CFArrayRef get_array_property(IOHIDDeviceRef device, CFStringRef key) +{ + CFTypeRef ref = IOHIDDeviceGetProperty(device, key); + if (ref != NULL && CFGetTypeID(ref) == CFArrayGetTypeID()) { + return (CFArrayRef)ref; + } else { + return NULL; + } +} static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key) { @@ -205,6 +214,11 @@ static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key) return 0; } +static CFArrayRef get_usage_pairs(IOHIDDeviceRef device) +{ + return get_array_property(device, CFSTR(kIOHIDDeviceUsagePairsKey)); +} + static unsigned short get_vendor_id(IOHIDDeviceRef device) { return get_int_property(device, CFSTR(kIOHIDVendorIDKey)); @@ -392,6 +406,125 @@ static void process_pending_events(void) { } while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut); } +static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, int32_t usage_page, int32_t usage) +{ + unsigned short dev_vid; + unsigned short dev_pid; + int BUF_LEN = 256; + wchar_t buf[BUF_LEN]; + + struct hid_device_info *cur_dev; + io_object_t iokit_dev; + kern_return_t res; + io_string_t path; + + if (dev == NULL) { + return NULL; + } + + cur_dev = (struct hid_device_info *)calloc(1, sizeof(struct hid_device_info)); + if (cur_dev == NULL) { + return NULL; + } + + dev_vid = get_vendor_id(dev); + dev_pid = get_product_id(dev); + + cur_dev->usage_page = usage_page; + cur_dev->usage = usage; + + /* Fill out the record */ + cur_dev->next = NULL; + + /* Fill in the path (IOService plane) */ + iokit_dev = hidapi_IOHIDDeviceGetService(dev); + res = IORegistryEntryGetPath(iokit_dev, kIOServicePlane, path); + if (res == KERN_SUCCESS) + cur_dev->path = strdup(path); + else + cur_dev->path = strdup(""); + + /* Serial Number */ + get_serial_number(dev, buf, BUF_LEN); + cur_dev->serial_number = dup_wcs(buf); + + /* Manufacturer and Product strings */ + get_manufacturer_string(dev, buf, BUF_LEN); + cur_dev->manufacturer_string = dup_wcs(buf); + get_product_string(dev, buf, BUF_LEN); + cur_dev->product_string = dup_wcs(buf); + + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Release Number */ + cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey)); + + /* Interface Number */ + /* We can only retrieve the interface number for USB HID devices. + * IOKit always seems to return 0 when querying a standard USB device + * for its interface. */ + bool is_usb_hid = get_int_property(dev, CFSTR(kUSBInterfaceClass)) == kUSBHIDClass; + if (is_usb_hid) { + /* Get the interface number */ + cur_dev->interface_number = get_int_property(dev, CFSTR(kUSBInterfaceNumber)); + } else { + cur_dev->interface_number = -1; + } + + return cur_dev; +} + +static struct hid_device_info *create_device_info(IOHIDDeviceRef device) +{ + struct hid_device_info *root = NULL; + CFArrayRef usage_pairs = get_usage_pairs(device); + + if (usage_pairs != NULL) { + struct hid_device_info *cur = NULL; + struct hid_device_info *next = NULL; + for (CFIndex i = 0; i < CFArrayGetCount(usage_pairs); i++) { + CFTypeRef dict = CFArrayGetValueAtIndex(usage_pairs, i); + if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) { + continue; + } + + CFTypeRef usage_page_ref, usage_ref; + int32_t usage_page, usage; + + if (!CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsagePageKey), &usage_page_ref) || + !CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsageKey), &usage_ref) || + CFGetTypeID(usage_page_ref) != CFNumberGetTypeID() || + CFGetTypeID(usage_ref) != CFNumberGetTypeID() || + !CFNumberGetValue((CFNumberRef)usage_page_ref, kCFNumberSInt32Type, &usage_page) || + !CFNumberGetValue((CFNumberRef)usage_ref, kCFNumberSInt32Type, &usage)) { + continue; + } + next = create_device_info_with_usage(device, usage_page, usage); + if (cur == NULL) { + root = next; + } + else { + cur->next = next; + } + if (next != NULL) { + cur = next; + } + } + } + + if (root == NULL) { + /* error when generating or parsing usage pairs */ + int32_t usage_page = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey)); + int32_t usage = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey)); + + root = create_device_info_with_usage(device, usage_page, usage); + } + + return root; +} + struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) { struct hid_device_info *root = NULL; /* return object */ @@ -407,7 +540,27 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, process_pending_events(); /* Get a list of the Devices */ - IOHIDManagerSetDeviceMatching(hid_mgr, NULL); + CFMutableDictionaryRef matching = NULL; + if (vendor_id != 0 || product_id != 0) { + matching = CFDictionaryCreateMutable(kCFAllocatorDefault, kIOHIDOptionsTypeNone, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + if (matching && vendor_id != 0) { + CFNumberRef v = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &vendor_id); + CFDictionarySetValue(matching, CFSTR(kIOHIDVendorIDKey), v); + CFRelease(v); + } + + if (matching && product_id != 0) { + CFNumberRef p = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &product_id); + CFDictionarySetValue(matching, CFSTR(kIOHIDProductIDKey), p); + CFRelease(p); + } + } + IOHIDManagerSetDeviceMatching(hid_mgr, matching); + if (matching != NULL) { + CFRelease(matching); + } + CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); /* Convert the list into a C array so we can iterate easily. */ @@ -417,81 +570,28 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, /* Iterate over each device, making an entry for it. */ for (i = 0; i < num_devices; i++) { - unsigned short dev_vid; - unsigned short dev_pid; - #define BUF_LEN 256 - wchar_t buf[BUF_LEN]; IOHIDDeviceRef dev = device_array[i]; + if (!dev) { + continue; + } - if (!dev) { - continue; - } - dev_vid = get_vendor_id(dev); - dev_pid = get_product_id(dev); - - /* Check the VID/PID against the arguments */ - if ((vendor_id == 0x0 || vendor_id == dev_vid) && - (product_id == 0x0 || product_id == dev_pid)) { - struct hid_device_info *tmp; - bool is_usb_hid; /* Is this an actual HID usb device */ - io_object_t iokit_dev; - kern_return_t res; - io_string_t path; - - /* VID/PID match. Create the record. */ - tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); - if (cur_dev) { - cur_dev->next = tmp; - } - else { - root = tmp; - } - cur_dev = tmp; - - is_usb_hid = get_int_property(dev, CFSTR(kUSBInterfaceClass)) == kUSBHIDClass; - - /* Get the Usage Page and Usage for this device. */ - cur_dev->usage_page = get_int_property(dev, CFSTR(kIOHIDPrimaryUsagePageKey)); - cur_dev->usage = get_int_property(dev, CFSTR(kIOHIDPrimaryUsageKey)); - - /* Fill out the record */ - cur_dev->next = NULL; - - /* Fill in the path (IOService plane) */ - iokit_dev = hidapi_IOHIDDeviceGetService(dev); - res = IORegistryEntryGetPath(iokit_dev, kIOServicePlane, path); - if (res == KERN_SUCCESS) - cur_dev->path = strdup(path); - else - cur_dev->path = strdup(""); - - /* Serial Number */ - get_serial_number(dev, buf, BUF_LEN); - cur_dev->serial_number = dup_wcs(buf); - - /* Manufacturer and Product strings */ - get_manufacturer_string(dev, buf, BUF_LEN); - cur_dev->manufacturer_string = dup_wcs(buf); - get_product_string(dev, buf, BUF_LEN); - cur_dev->product_string = dup_wcs(buf); - - /* VID/PID */ - cur_dev->vendor_id = dev_vid; - cur_dev->product_id = dev_pid; - - /* Release Number */ - cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey)); - - /* We can only retrieve the interface number for USB HID devices. - * IOKit always seems to return 0 when querying a standard USB device - * for its interface. */ - if (is_usb_hid) { - /* Get the interface number */ - cur_dev->interface_number = get_int_property(dev, CFSTR(kUSBInterfaceNumber)); - } else { - cur_dev->interface_number = -1; - } + struct hid_device_info *tmp = create_device_info(dev); + if (tmp == NULL) { + continue; + } + + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + /* move the pointer to the tail of returnd list */ + while (cur_dev->next != NULL) { + cur_dev = cur_dev->next; } }