@@ -329,6 +329,22 @@ static bool try_get_int_property(IOHIDDeviceRef device, CFStringRef key, int32_t
329
329
return result ;
330
330
}
331
331
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
+
332
348
static CFArrayRef get_usage_pairs (IOHIDDeviceRef device )
333
349
{
334
350
return get_array_property (device , CFSTR (kIOHIDDeviceUsagePairsKey ));
@@ -481,6 +497,46 @@ static void process_pending_events(void) {
481
497
} while (res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut );
482
498
}
483
499
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
+
484
540
static struct hid_device_info * create_device_info_with_usage (IOHIDDeviceRef dev , int32_t usage_page , int32_t usage )
485
541
{
486
542
unsigned short dev_vid ;
@@ -490,7 +546,7 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev,
490
546
CFTypeRef transport_prop ;
491
547
492
548
struct hid_device_info * cur_dev ;
493
- io_object_t iokit_dev ;
549
+ io_service_t hid_service ;
494
550
kern_return_t res ;
495
551
uint64_t entry_id = 0 ;
496
552
@@ -514,9 +570,9 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev,
514
570
515
571
/* Fill in the path (as a unique ID of the service entry) */
516
572
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 );
520
576
}
521
577
else {
522
578
res = KERN_INVALID_ARGUMENT ;
@@ -567,11 +623,18 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev,
567
623
if (CFStringCompare ((CFStringRef )transport_prop , CFSTR (kIOHIDTransportUSBValue ), 0 ) == kCFCompareEqualTo ) {
568
624
int32_t interface_number = -1 ;
569
625
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. */
570
629
if (try_get_int_property (dev , CFSTR (kUSBInterfaceNumber ), & interface_number )) {
571
630
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. */
575
638
}
576
639
577
640
/* Match "Bluetooth", "BluetoothLowEnergy" and "Bluetooth Low Energy" strings */
0 commit comments