Skip to content

Commit

Permalink
macOS: use IORegistryEntryGetRegistryEntryID to resolve paths
Browse files Browse the repository at this point in the history
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 felix.schwarz@iospirit.com
flirc/hidapi@8d251c3

Fixes: #127, #236.
  • Loading branch information
Youw committed Aug 14, 2021
1 parent 8741697 commit 180d331
Showing 1 changed file with 51 additions and 12 deletions.
63 changes: 51 additions & 12 deletions mac/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev,
struct hid_device_info *cur_dev;
io_object_t iokit_dev;
kern_return_t res;
io_string_t path;
uint64_t entry_id;

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

/* Fill in the path (IOService plane) */
/* Fill in the path (as a unique ID of the service entry) */
cur_dev->path = NULL;
iokit_dev = IOHIDDeviceGetService(dev);
res = IORegistryEntryGetPath(iokit_dev, kIOServicePlane, path);
if (res == KERN_SUCCESS)
cur_dev->path = strdup(path);
else
if (iokit_dev != MACH_PORT_NULL) {
res = IORegistryEntryGetRegistryEntryID(iokit_dev, &entry_id);
}
else {
res = KERN_INVALID_ARGUMENT;
}

if (res == KERN_SUCCESS) {
/* max value of entry_id(uint64_t) is 18446744073709551615 which is 20 characters long,
so for (max) "path" string 'DevSrvsID:18446744073709551615' we would need
9+1+20+1=31 bytes byffer, but allocate 32 for simple alignment */
cur_dev->path = calloc(1, 32);
if (cur_dev->path != NULL) {
sprintf(cur_dev->path, "DevSrvsID:%llu", entry_id);
}
}

if (cur_dev->path == NULL) {
/* for whatever reason, trying to keep it a non-NULL string */
cur_dev->path = strdup("");
}

/* Serial Number */
get_serial_number(dev, buf, BUF_LEN);
Expand Down Expand Up @@ -759,11 +776,33 @@ static void *read_thread(void *param)
return NULL;
}

/* hid_open_path()
*
* path must be a valid path to an IOHIDDevice in the IOService plane
* Example: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver"
*/
/* \p path must be one of:
- in format 'DevSrvsID:<RegistryEntryID>' (as returned by hid_enumerate);
- a valid path to an IOHIDDevice in the IOService plane (as returned by IORegistryEntryGetPath,
e.g.: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver");
Second format is for compatibility with paths accepted by older versions of HIDAPI.
*/
static io_registry_entry_t hid_open_service_registry_from_path(const char *path)
{
if (path == NULL)
return MACH_PORT_NULL;

/* Get the IORegistry entry for the given path */
if (strncmp("DevSrvsID:", path, 10) == 0) {
char *endptr;
uint64_t entry_id = strtoull(path + 10, &endptr, 10);
if (*endptr == '\0') {
return IOServiceGetMatchingService(kIOMasterPortDefault, IORegistryEntryIDMatching(entry_id));
}
}
else {
/* Fallback to older format of the path */
return IORegistryEntryFromPath(kIOMasterPortDefault, path);
}

return MACH_PORT_NULL;
}

hid_device * HID_API_EXPORT hid_open_path(const char *path)
{
hid_device *dev = NULL;
Expand All @@ -777,7 +816,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
dev = new_hid_device();

/* Get the IORegistry entry for the given path */
entry = IORegistryEntryFromPath(kIOMasterPortDefault, path);
entry = hid_open_service_registry_from_path(path);
if (entry == MACH_PORT_NULL) {
/* Path wasn't valid (maybe device was removed?) */
goto return_error;
Expand Down

0 comments on commit 180d331

Please sign in to comment.