@@ -329,6 +329,22 @@ static bool try_get_int_property(IOHIDDeviceRef device, CFStringRef key, int32_t
329329 return result ;
330330}
331331
332+ static bool try_get_ioregistry_int_property (io_service_t service , CFStringRef property , int32_t * out_val )
333+ {
334+ bool result = false;
335+ CFTypeRef ref = IORegistryEntryCreateCFProperty (service , property , kCFAllocatorDefault , 0 );
336+
337+ if (ref ) {
338+ if (CFGetTypeID (ref ) == CFNumberGetTypeID ()) {
339+ result = CFNumberGetValue (ref , kCFNumberSInt32Type , out_val );
340+ }
341+
342+ CFRelease (ref );
343+ }
344+
345+ return result ;
346+ }
347+
332348static CFArrayRef get_usage_pairs (IOHIDDeviceRef device )
333349{
334350 return get_array_property (device , CFSTR (kIOHIDDeviceUsagePairsKey ));
@@ -481,6 +497,46 @@ static void process_pending_events(void) {
481497 } while (res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut );
482498}
483499
500+ static int read_usb_interface_from_hid_service_parent (io_service_t hid_service )
501+ {
502+ int32_t result = -1 ;
503+ bool success = false;
504+ io_registry_entry_t current = IO_OBJECT_NULL ;
505+ kern_return_t res ;
506+ int parent_number = 0 ;
507+
508+ res = IORegistryEntryGetParentEntry (hid_service , kIOServicePlane , & current );
509+ while (KERN_SUCCESS == res
510+ /* Only search up to 3 parent entries.
511+ * With the default driver - the parent-of-interest supposed to be the first one,
512+ * but lets assume some custom drivers or so, with deeper tree. */
513+ && parent_number < 3 ) {
514+ io_registry_entry_t parent = IO_OBJECT_NULL ;
515+ int32_t interface_number = -1 ;
516+ parent_number ++ ;
517+
518+ success = try_get_ioregistry_int_property (current , CFSTR (kUSBInterfaceNumber ), & interface_number );
519+ if (success ) {
520+ result = interface_number ;
521+ break ;
522+ }
523+
524+ res = IORegistryEntryGetParentEntry (current , kIOServicePlane , & parent );
525+ if (parent ) {
526+ IOObjectRelease (current );
527+ current = parent ;
528+ }
529+
530+ }
531+
532+ if (current ) {
533+ IOObjectRelease (current );
534+ current = IO_OBJECT_NULL ;
535+ }
536+
537+ return result ;
538+ }
539+
484540static struct hid_device_info * create_device_info_with_usage (IOHIDDeviceRef dev , int32_t usage_page , int32_t usage )
485541{
486542 unsigned short dev_vid ;
@@ -490,7 +546,7 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev,
490546 CFTypeRef transport_prop ;
491547
492548 struct hid_device_info * cur_dev ;
493- io_object_t iokit_dev ;
549+ io_service_t hid_service ;
494550 kern_return_t res ;
495551 uint64_t entry_id = 0 ;
496552
@@ -514,9 +570,9 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev,
514570
515571 /* Fill in the path (as a unique ID of the service entry) */
516572 cur_dev -> path = NULL ;
517- iokit_dev = IOHIDDeviceGetService (dev );
518- if (iokit_dev != MACH_PORT_NULL ) {
519- res = IORegistryEntryGetRegistryEntryID (iokit_dev , & entry_id );
573+ hid_service = IOHIDDeviceGetService (dev );
574+ if (hid_service != MACH_PORT_NULL ) {
575+ res = IORegistryEntryGetRegistryEntryID (hid_service , & entry_id );
520576 }
521577 else {
522578 res = KERN_INVALID_ARGUMENT ;
@@ -567,11 +623,18 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev,
567623 if (CFStringCompare ((CFStringRef )transport_prop , CFSTR (kIOHIDTransportUSBValue ), 0 ) == kCFCompareEqualTo ) {
568624 int32_t interface_number = -1 ;
569625 cur_dev -> bus_type = HID_API_BUS_USB ;
626+
627+ /* A IOHIDDeviceRef used to have this simple property,
628+ * until macOS 13.3 - we will try to use it. */
570629 if (try_get_int_property (dev , CFSTR (kUSBInterfaceNumber ), & interface_number )) {
571630 cur_dev -> interface_number = interface_number ;
572- }
573- else {
574- // TODO: use a different approach - this is a USB device after all
631+ } else {
632+ /* Otherwise fallback to io_service_t property.
633+ * (of one of the parent services). */
634+ cur_dev -> interface_number = read_usb_interface_from_hid_service_parent (hid_service );
635+
636+ /* If the above doesn't work -
637+ * no (known) fallback exists at this point. */
575638 }
576639
577640 /* Match "Bluetooth", "BluetoothLowEnergy" and "Bluetooth Low Energy" strings */
0 commit comments