Skip to content

Commit 4d63a50

Browse files
authored
macOS: use IORegistryEntryGetRegistryEntryID to resolve paths (#322)
With some device connection configurations, the device paths become over 512 bytes (io_string_t max length) which makes them unusable with current implementation. Rather than using ServiceRegistry string path, use its ID, which is uint64_t and easily serializable/deserializable into a string. Implementation idea by [email protected] flirc/hidapi@8d251c3 Fixes: #127, #236.
1 parent 7c0dc53 commit 4d63a50

File tree

1 file changed

+51
-12
lines changed

1 file changed

+51
-12
lines changed

mac/hid.c

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev,
381381
struct hid_device_info *cur_dev;
382382
io_object_t iokit_dev;
383383
kern_return_t res;
384-
io_string_t path;
384+
uint64_t entry_id;
385385

386386
if (dev == NULL) {
387387
return NULL;
@@ -401,13 +401,30 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev,
401401
/* Fill out the record */
402402
cur_dev->next = NULL;
403403

404-
/* Fill in the path (IOService plane) */
404+
/* Fill in the path (as a unique ID of the service entry) */
405+
cur_dev->path = NULL;
405406
iokit_dev = IOHIDDeviceGetService(dev);
406-
res = IORegistryEntryGetPath(iokit_dev, kIOServicePlane, path);
407-
if (res == KERN_SUCCESS)
408-
cur_dev->path = strdup(path);
409-
else
407+
if (iokit_dev != MACH_PORT_NULL) {
408+
res = IORegistryEntryGetRegistryEntryID(iokit_dev, &entry_id);
409+
}
410+
else {
411+
res = KERN_INVALID_ARGUMENT;
412+
}
413+
414+
if (res == KERN_SUCCESS) {
415+
/* max value of entry_id(uint64_t) is 18446744073709551615 which is 20 characters long,
416+
so for (max) "path" string 'DevSrvsID:18446744073709551615' we would need
417+
9+1+20+1=31 bytes byffer, but allocate 32 for simple alignment */
418+
cur_dev->path = calloc(1, 32);
419+
if (cur_dev->path != NULL) {
420+
sprintf(cur_dev->path, "DevSrvsID:%llu", entry_id);
421+
}
422+
}
423+
424+
if (cur_dev->path == NULL) {
425+
/* for whatever reason, trying to keep it a non-NULL string */
410426
cur_dev->path = strdup("");
427+
}
411428

412429
/* Serial Number */
413430
get_serial_number(dev, buf, BUF_LEN);
@@ -759,11 +776,33 @@ static void *read_thread(void *param)
759776
return NULL;
760777
}
761778

762-
/* hid_open_path()
763-
*
764-
* path must be a valid path to an IOHIDDevice in the IOService plane
765-
* Example: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver"
766-
*/
779+
/* \p path must be one of:
780+
- in format 'DevSrvsID:<RegistryEntryID>' (as returned by hid_enumerate);
781+
- a valid path to an IOHIDDevice in the IOService plane (as returned by IORegistryEntryGetPath,
782+
e.g.: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver");
783+
Second format is for compatibility with paths accepted by older versions of HIDAPI.
784+
*/
785+
static io_registry_entry_t hid_open_service_registry_from_path(const char *path)
786+
{
787+
if (path == NULL)
788+
return MACH_PORT_NULL;
789+
790+
/* Get the IORegistry entry for the given path */
791+
if (strncmp("DevSrvsID:", path, 10) == 0) {
792+
char *endptr;
793+
uint64_t entry_id = strtoull(path + 10, &endptr, 10);
794+
if (*endptr == '\0') {
795+
return IOServiceGetMatchingService(kIOMasterPortDefault, IORegistryEntryIDMatching(entry_id));
796+
}
797+
}
798+
else {
799+
/* Fallback to older format of the path */
800+
return IORegistryEntryFromPath(kIOMasterPortDefault, path);
801+
}
802+
803+
return MACH_PORT_NULL;
804+
}
805+
767806
hid_device * HID_API_EXPORT hid_open_path(const char *path)
768807
{
769808
hid_device *dev = NULL;
@@ -777,7 +816,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
777816
dev = new_hid_device();
778817

779818
/* Get the IORegistry entry for the given path */
780-
entry = IORegistryEntryFromPath(kIOMasterPortDefault, path);
819+
entry = hid_open_service_registry_from_path(path);
781820
if (entry == MACH_PORT_NULL) {
782821
/* Path wasn't valid (maybe device was removed?) */
783822
goto return_error;

0 commit comments

Comments
 (0)