Skip to content

Commit af6c601

Browse files
authored
Parse USB interface number from device Hardware IDs (signal11#360)
Strickly speaking we cannot parse interface string at all and only allowed to parse Hardware ID string.
1 parent b600727 commit af6c601

File tree

1 file changed

+49
-20
lines changed

1 file changed

+49
-20
lines changed

windows/hid.c

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -412,17 +412,43 @@ static void hid_internal_get_ble_info(struct hid_device_info* dev, DEVINST dev_n
412412
}
413413
}
414414

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+
415440
static void hid_internal_get_info(struct hid_device_info* dev)
416441
{
417442
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;
419444
mbstate_t state;
420445
ULONG len;
421446
CONFIGRET cr;
422447
DEVPROPTYPE property_type;
423448
DEVINST dev_node;
424449

425450
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
426452
static DEVPROPKEY DEVPKEY_Device_CompatibleIds = { { 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0}, 4 }; // DEVPROP_TYPE_STRING_LIST
427453

428454
if (!CM_Get_Device_Interface_PropertyW ||
@@ -455,6 +481,27 @@ static void hid_internal_get_info(struct hid_device_info* dev)
455481
if (cr != CR_SUCCESS)
456482
goto end;
457483

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+
458505
/* Get devnode parent */
459506
cr = CM_Get_Parent(&dev_node, dev_node, 0);
460507
if (cr != CR_SUCCESS)
@@ -487,6 +534,7 @@ static void hid_internal_get_info(struct hid_device_info* dev)
487534
end:
488535
free(interface_path);
489536
free(device_id);
537+
free(hardware_ids);
490538
free(compatible_ids);
491539
}
492540

@@ -557,25 +605,6 @@ static struct hid_device_info *hid_get_device_info(const char *path, HANDLE hand
557605
wstr[WSTR_LEN - 1] = L'\0';
558606
dev->product_string = _wcsdup(wstr);
559607

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-
579608
hid_internal_get_info(dev);
580609

581610
return dev;

0 commit comments

Comments
 (0)