Skip to content

Commit

Permalink
Parse USB interface number from device Hardware IDs.
Browse files Browse the repository at this point in the history
Strickly speaking we cannot parse interface string at all and only allowed to parse Hardware ID string.
  • Loading branch information
DJm00n committed Dec 20, 2021
1 parent d67b5c9 commit 12a9bca
Showing 1 changed file with 49 additions and 22 deletions.
71 changes: 49 additions & 22 deletions windows/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -412,17 +412,41 @@ static void hid_internal_get_ble_info(struct hid_device_info* dev, DEVINST dev_n
}
}

/* USB Device Interface Number.
It can be parsed out of the Hardware ID if a USB device is has multiple interfaces (composite device).
See https://docs.microsoft.com/windows-hardware/drivers/hid/hidclass-hardware-ids-for-top-level-collections
and https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers
*/
static int hid_internal_get_interface_number(const wchar_t* hardware_id)
{
int interface_number;
wchar_t *startptr, *endptr;
const wchar_t *interface_token = L"&MI_";

startptr = wcsstr(hardware_id, interface_token);
if (!startptr)
return -1;

startptr += wcslen(interface_token);
interface_number = wcstol(startptr, &endptr, 16);
if (endptr == startptr)
return -1;

return interface_number;
}

static void hid_internal_get_info(struct hid_device_info* dev)
{
const char *tmp = NULL;
wchar_t *interface_path = NULL, *device_id = NULL, *compatible_ids = NULL;
wchar_t *interface_path = NULL, *device_id = NULL, *compatible_ids = NULL, *hardware_ids = NULL;
mbstate_t state;
ULONG len;
CONFIGRET cr;
DEVPROPTYPE property_type;
DEVINST dev_node;

static DEVPROPKEY DEVPKEY_Device_InstanceId = { { 0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57 }, 256 }; // DEVPROP_TYPE_STRING
static DEVPROPKEY DEVPKEY_Device_HardwareIds = { { 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0}, 3 }; // DEVPROP_TYPE_STRING_LIST
static DEVPROPKEY DEVPKEY_Device_CompatibleIds = { { 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0}, 4 }; // DEVPROP_TYPE_STRING_LIST

if (!CM_Get_Device_Interface_PropertyW ||
Expand Down Expand Up @@ -455,6 +479,27 @@ static void hid_internal_get_info(struct hid_device_info* dev)
if (cr != CR_SUCCESS)
goto end;

/* Get the hardware ids from devnode */
len = 0;
cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_Device_HardwareIds, &property_type, NULL, &len, 0);
if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING_LIST) {
hardware_ids = (wchar_t*)calloc(len, sizeof(BYTE));
cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_Device_HardwareIds, &property_type, (PBYTE)hardware_ids, &len, 0);
}
if (cr != CR_SUCCESS)
goto end;

// Search for interface number in hardware ids
for (wchar_t* hardware_id = hardware_ids; *hardware_id; hardware_id += wcslen(hardware_id) + 1) {
/* Normalize to upper case */
for (wchar_t* p = hardware_id; *p; ++p) *p = towupper(*p);

dev->interface_number = hid_internal_get_interface_number(hardware_id);

if (dev->interface_number != -1)
break;
}

/* Get devnode parent */
cr = CM_Get_Parent(&dev_node, dev_node, 0);
if (cr != CR_SUCCESS)
Expand Down Expand Up @@ -487,6 +532,7 @@ static void hid_internal_get_info(struct hid_device_info* dev)
end:
free(interface_path);
free(device_id);
free(hardware_ids);
free(compatible_ids);
}

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

/* Interface Number. It can sometimes be parsed out of the path
on Windows if a device has multiple interfaces. See
https://docs.microsoft.com/windows-hardware/drivers/hid/hidclass-hardware-ids-for-top-level-collections
or search for "HIDClass Hardware IDs for Top-Level Collections" at Microsoft Docs. If it's not
in the path, it's set to -1. */
dev->interface_number = -1;
if (dev->path) {
char* interface_component = strstr(dev->path, "&mi_");
if (interface_component) {
char* hex_str = interface_component + 4;
char* endptr = NULL;
dev->interface_number = strtol(hex_str, &endptr, 16);
if (endptr == hex_str) {
/* The parsing failed. Set interface_number to -1. */
dev->interface_number = -1;
}
}
}

hid_internal_get_info(dev);

return dev;
Expand Down Expand Up @@ -693,7 +720,7 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor
/* Check the VID/PID to see if we should add this
device to the enumeration list. */
if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) &&
(product_id == 0x0 || attrib.ProductID == product_id)) {
(product_id == 0x0 || attrib.ProductID == product_id)) {

/* VID/PID match. Create the record. */
struct hid_device_info *tmp = hid_get_device_info(device_interface_detail_data->DevicePath, read_handle);
Expand Down Expand Up @@ -754,7 +781,7 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsi
cur_dev = devs;
while (cur_dev) {
if (cur_dev->vendor_id == vendor_id &&
cur_dev->product_id == product_id) {
cur_dev->product_id == product_id) {
if (serial_number) {
if (cur_dev->serial_number && wcscmp(serial_number, cur_dev->serial_number) == 0) {
path_to_open = cur_dev->path;
Expand Down

0 comments on commit 12a9bca

Please sign in to comment.