@@ -412,17 +412,43 @@ static void hid_internal_get_ble_info(struct hid_device_info* dev, DEVINST dev_n
412
412
}
413
413
}
414
414
415
+ /* USB Device Interface Number.
416
+ It can be parsed out of the Hardware ID if a USB device is has multiple interfaces (composite device).
417
+ See https://docs.microsoft.com/windows-hardware/drivers/hid/hidclass-hardware-ids-for-top-level-collections
418
+ and https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers
419
+
420
+ hardware_id is always expected to be uppercase.
421
+ */
422
+ static int hid_internal_get_interface_number (const wchar_t * hardware_id )
423
+ {
424
+ int interface_number ;
425
+ wchar_t * startptr , * endptr ;
426
+ const wchar_t * interface_token = L"&MI_" ;
427
+
428
+ startptr = wcsstr (hardware_id , interface_token );
429
+ if (!startptr )
430
+ return -1 ;
431
+
432
+ startptr += wcslen (interface_token );
433
+ interface_number = wcstol (startptr , & endptr , 16 );
434
+ if (endptr == startptr )
435
+ return -1 ;
436
+
437
+ return interface_number ;
438
+ }
439
+
415
440
static void hid_internal_get_info (struct hid_device_info * dev )
416
441
{
417
442
const char * tmp = NULL ;
418
- wchar_t * interface_path = NULL , * device_id = NULL , * compatible_ids = NULL ;
443
+ wchar_t * interface_path = NULL , * device_id = NULL , * compatible_ids = NULL , * hardware_ids = NULL ;
419
444
mbstate_t state ;
420
445
ULONG len ;
421
446
CONFIGRET cr ;
422
447
DEVPROPTYPE property_type ;
423
448
DEVINST dev_node ;
424
449
425
450
static DEVPROPKEY DEVPKEY_Device_InstanceId = { { 0x78c34fc8 , 0x104a , 0x4aca , 0x9e , 0xa4 , 0x52 , 0x4d , 0x52 , 0x99 , 0x6e , 0x57 }, 256 }; // DEVPROP_TYPE_STRING
451
+ static DEVPROPKEY DEVPKEY_Device_HardwareIds = { { 0xa45c254e , 0xdf1c , 0x4efd , 0x80 , 0x20 , 0x67 , 0xd1 , 0x46 , 0xa8 , 0x50 , 0xe0 }, 3 }; // DEVPROP_TYPE_STRING_LIST
426
452
static DEVPROPKEY DEVPKEY_Device_CompatibleIds = { { 0xa45c254e , 0xdf1c , 0x4efd , 0x80 , 0x20 , 0x67 , 0xd1 , 0x46 , 0xa8 , 0x50 , 0xe0 }, 4 }; // DEVPROP_TYPE_STRING_LIST
427
453
428
454
if (!CM_Get_Device_Interface_PropertyW ||
@@ -455,6 +481,27 @@ static void hid_internal_get_info(struct hid_device_info* dev)
455
481
if (cr != CR_SUCCESS )
456
482
goto end ;
457
483
484
+ /* Get the hardware ids from devnode */
485
+ len = 0 ;
486
+ cr = CM_Get_DevNode_PropertyW (dev_node , & DEVPKEY_Device_HardwareIds , & property_type , NULL , & len , 0 );
487
+ if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING_LIST ) {
488
+ hardware_ids = (wchar_t * )calloc (len , sizeof (BYTE ));
489
+ cr = CM_Get_DevNode_PropertyW (dev_node , & DEVPKEY_Device_HardwareIds , & property_type , (PBYTE )hardware_ids , & len , 0 );
490
+ }
491
+ if (cr != CR_SUCCESS )
492
+ goto end ;
493
+
494
+ // Search for interface number in hardware ids
495
+ for (wchar_t * hardware_id = hardware_ids ; * hardware_id ; hardware_id += wcslen (hardware_id ) + 1 ) {
496
+ /* Normalize to upper case */
497
+ for (wchar_t * p = hardware_id ; * p ; ++ p ) * p = towupper (* p );
498
+
499
+ dev -> interface_number = hid_internal_get_interface_number (hardware_id );
500
+
501
+ if (dev -> interface_number != -1 )
502
+ break ;
503
+ }
504
+
458
505
/* Get devnode parent */
459
506
cr = CM_Get_Parent (& dev_node , dev_node , 0 );
460
507
if (cr != CR_SUCCESS )
@@ -487,6 +534,7 @@ static void hid_internal_get_info(struct hid_device_info* dev)
487
534
end :
488
535
free (interface_path );
489
536
free (device_id );
537
+ free (hardware_ids );
490
538
free (compatible_ids );
491
539
}
492
540
@@ -557,25 +605,6 @@ static struct hid_device_info *hid_get_device_info(const char *path, HANDLE hand
557
605
wstr [WSTR_LEN - 1 ] = L'\0' ;
558
606
dev -> product_string = _wcsdup (wstr );
559
607
560
- /* Interface Number. It can sometimes be parsed out of the path
561
- on Windows if a device has multiple interfaces. See
562
- https://docs.microsoft.com/windows-hardware/drivers/hid/hidclass-hardware-ids-for-top-level-collections
563
- or search for "HIDClass Hardware IDs for Top-Level Collections" at Microsoft Docs. If it's not
564
- in the path, it's set to -1. */
565
- dev -> interface_number = -1 ;
566
- if (dev -> path ) {
567
- char * interface_component = strstr (dev -> path , "&mi_" );
568
- if (interface_component ) {
569
- char * hex_str = interface_component + 4 ;
570
- char * endptr = NULL ;
571
- dev -> interface_number = strtol (hex_str , & endptr , 16 );
572
- if (endptr == hex_str ) {
573
- /* The parsing failed. Set interface_number to -1. */
574
- dev -> interface_number = -1 ;
575
- }
576
- }
577
- }
578
-
579
608
hid_internal_get_info (dev );
580
609
581
610
return dev ;
0 commit comments