Skip to content

Commit e77555d

Browse files
committed
Add code to determine HID device bus type
1 parent 8ffd3d6 commit e77555d

6 files changed

Lines changed: 180 additions & 0 deletions

File tree

hidapi/hidapi.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,30 @@ extern "C" {
7878
struct hid_device_;
7979
typedef struct hid_device_ hid_device; /**< opaque hidapi structure */
8080

81+
/** @brief HID underlying bus types.
82+
83+
@ingroup API
84+
*/
85+
typedef enum {
86+
/* Universal Serial Bus */
87+
HID_BUS_USB = 0x01,
88+
89+
/* Bluetooth or Bluetooth LE */
90+
HID_BUS_BLUETOOTH = 0x02,
91+
92+
/* Inter-Integrated Circuit */
93+
HID_BUS_I2C = 0x03,
94+
95+
/* Serial Peripheral Interface */
96+
HID_BUS_SPI = 0x04,
97+
98+
/* Virtual HID Bus */
99+
HID_BUS_VIRTUAL = 0x05,
100+
101+
/* Unknown Bus Type */
102+
HID_BUS_UNKNOWN = 0xff,
103+
} hid_bus_type;
104+
81105
/** hidapi info structure */
82106
struct hid_device_info {
83107
/** Platform-specific device path */
@@ -110,6 +134,9 @@ extern "C" {
110134
* Valid on the Mac implementation if and only if the device
111135
is a USB HID device. */
112136
int interface_number;
137+
/** Underlying bus type
138+
(Windows/libusb/hidraw only) */
139+
hid_bus_type bus_type;
113140

114141
/** Pointer to the next device */
115142
struct hid_device_info *next;

hidtest/test.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ int device_callback(
4545
printf(" Release: %hx\n", device->release_number);
4646
printf(" Interface: %d\n", device->interface_number);
4747
printf(" Usage (page): 0x%hx (0x%hx)\n", device->usage, device->usage_page);
48+
printf(" Bus type: %d\n", device->bus_type);
4849
printf("\n");
4950

5051
//if (device->product_id == 0x0ce6)
@@ -89,6 +90,7 @@ int main(int argc, char* argv[])
8990
printf(" Release: %hx\n", cur_dev->release_number);
9091
printf(" Interface: %d\n", cur_dev->interface_number);
9192
printf(" Usage (page): 0x%hx (0x%hx)\n", cur_dev->usage, cur_dev->usage_page);
93+
printf(" Bus type: %d\n", cur_dev->bus_type);
9294
printf("\n");
9395
cur_dev = cur_dev->next;
9496
}

libusb/hid.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,9 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
707707

708708
/* Interface Number */
709709
cur_dev->interface_number = interface_num;
710+
711+
/* Bus Type */
712+
cur_dev->bus_type = HID_BUS_USB;
710713
}
711714
}
712715
} /* altsettings */

linux/hid.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,9 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
740740
/* Interface Number */
741741
cur_dev->interface_number = -1;
742742

743+
/* Bus Type */
744+
cur_dev->bus_type = HID_BUS_UNKNOWN;
745+
743746
switch (bus_type) {
744747
case BUS_USB:
745748
/* The device pointed to by raw_dev contains information about
@@ -753,13 +756,19 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
753756
"usb",
754757
"usb_device");
755758

759+
/* Bus Type */
760+
cur_dev->bus_type = HID_BUS_USB;
761+
756762
/* uhid USB devices
757763
Since this is a virtual hid interface, no USB information will
758764
be available. */
759765
if (!usb_dev) {
760766
/* Manufacturer and Product strings */
761767
cur_dev->manufacturer_string = wcsdup(L"");
762768
cur_dev->product_string = utf8_to_wchar_t(product_name_utf8);
769+
770+
/* Bus Type */
771+
cur_dev->bus_type = HID_BUS_VIRTUAL;
763772
break;
764773
}
765774

@@ -784,11 +793,22 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
784793
break;
785794

786795
case BUS_BLUETOOTH:
796+
/* Manufacturer and Product strings */
797+
cur_dev->manufacturer_string = wcsdup(L"");
798+
cur_dev->product_string = utf8_to_wchar_t(product_name_utf8);
799+
800+
/* Bus Type */
801+
cur_dev->bus_type = HID_BUS_BLUETOOTH;
802+
803+
break;
787804
case BUS_I2C:
788805
/* Manufacturer and Product strings */
789806
cur_dev->manufacturer_string = wcsdup(L"");
790807
cur_dev->product_string = utf8_to_wchar_t(product_name_utf8);
791808

809+
/* Bus Type */
810+
cur_dev->bus_type = HID_BUS_I2C;
811+
792812
break;
793813

794814
default:

mac/hid.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,9 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev,
438438
cur_dev->interface_number = -1;
439439
}
440440

441+
/* Bus Type */
442+
cur_dev->bus_type = HID_BUS_UNKNOWN;
443+
441444
return cur_dev;
442445
}
443446

windows/hid.c

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,26 @@ static struct hid_api_version api_version = {
143143

144144
typedef DWORD RETURN_TYPE;
145145
typedef RETURN_TYPE CONFIGRET;
146+
typedef DWORD DEVNODE, DEVINST;
147+
typedef DEVNODE* PDEVNODE, * PDEVINST;
148+
typedef WCHAR* DEVNODEID_W, * DEVINSTID_W;
146149

147150
#define CR_SUCCESS (0x00000000)
151+
#define CR_BUFFER_SMALL (0x0000001A)
152+
153+
#define CM_LOCATE_DEVNODE_NORMAL 0x00000000
154+
155+
#define DEVPROP_TYPE_STRING 0x00000012
156+
157+
typedef CONFIGRET(__stdcall* CM_Locate_DevNodeW_)(PDEVINST pdnDevInst, DEVINSTID_W pDeviceID, ULONG ulFlags);
158+
typedef CONFIGRET(__stdcall* CM_Get_Parent_)(PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags);
159+
typedef CONFIGRET(__stdcall* CM_Get_DevNode_PropertyW_)(DEVINST dnDevInst, CONST DEVPROPKEY* PropertyKey, DEVPROPTYPE* PropertyType, PBYTE PropertyBuffer, PULONG PropertyBufferSize, ULONG ulFlags);
160+
typedef CONFIGRET(__stdcall* CM_Get_Device_Interface_PropertyW_)(LPCWSTR pszDeviceInterface, CONST DEVPROPKEY* PropertyKey, DEVPROPTYPE* PropertyType, PBYTE PropertyBuffer, PULONG PropertyBufferSize, ULONG ulFlags);
161+
162+
static CM_Locate_DevNodeW_ CM_Locate_DevNodeW;
163+
static CM_Get_Parent_ CM_Get_Parent;
164+
static CM_Get_DevNode_PropertyW_ CM_Get_DevNode_PropertyW;
165+
static CM_Get_Device_Interface_PropertyW_ CM_Get_Device_Interface_PropertyW;
148166

149167
DECLARE_HANDLE(HCMNOTIFICATION);
150168
typedef HCMNOTIFICATION* PHCMNOTIFICATION;
@@ -337,6 +355,10 @@ static int lookup_functions()
337355
# pragma GCC diagnostic ignored "-Wcast-function-type"
338356
#endif
339357
#define RESOLVE(x) x = (x##_)GetProcAddress(cfgmgr32_lib_handle, #x); if (!x) return -1;
358+
RESOLVE(CM_Locate_DevNodeW);
359+
RESOLVE(CM_Get_Parent);
360+
RESOLVE(CM_Get_DevNode_PropertyW);
361+
RESOLVE(CM_Get_Device_Interface_PropertyW);
340362
RESOLVE(CM_Register_Notification);
341363
RESOLVE(CM_Unregister_Notification);
342364
#undef RESOLVE
@@ -404,6 +426,107 @@ int HID_API_EXPORT hid_exit(void)
404426
return 0;
405427
}
406428

429+
static void hid_internal_get_info(struct hid_device_info* dev)
430+
{
431+
dev->bus_type = HID_BUS_UNKNOWN;
432+
433+
wchar_t *device_id, *interface_path;
434+
ULONG len;
435+
CONFIGRET cr;
436+
DEVPROPTYPE property_type;
437+
DEVINST dev_node;
438+
439+
static DEVPROPKEY DEVPKEY_Device_InstanceId = { { 0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57 }, 256 }; // DEVPROP_TYPE_STRING
440+
441+
if (!CM_Get_Device_Interface_PropertyW ||
442+
!CM_Locate_DevNodeW ||
443+
!CM_Get_Parent ||
444+
!CM_Get_DevNode_PropertyW)
445+
goto close;
446+
447+
len = (ULONG)strlen(dev->path);
448+
interface_path = (wchar_t*)calloc(len + 1, sizeof(wchar_t));
449+
450+
if (mbstowcs(interface_path, dev->path, len) == (size_t)-1)
451+
goto close;
452+
453+
/* Get the device id from interface path */
454+
len = 0;
455+
cr = CM_Get_Device_Interface_PropertyW(interface_path, &DEVPKEY_Device_InstanceId, &property_type, NULL, &len, 0);
456+
if (cr == CR_BUFFER_SMALL || property_type == DEVPROP_TYPE_STRING) {
457+
device_id = (wchar_t*)calloc(len, sizeof(BYTE));
458+
cr = CM_Get_Device_Interface_PropertyW(interface_path, &DEVPKEY_Device_InstanceId, &property_type, (PBYTE)device_id, &len, 0);
459+
}
460+
461+
if (cr != CR_SUCCESS)
462+
goto close;
463+
464+
/* Open devnode from device id */
465+
cr = CM_Locate_DevNodeW(&dev_node, (DEVINSTID_W)device_id, CM_LOCATE_DEVNODE_NORMAL);
466+
if (cr != CR_SUCCESS)
467+
goto close;
468+
469+
/* Get devnode parent */
470+
cr = CM_Get_Parent(&dev_node, dev_node, 0);
471+
if (cr != CR_SUCCESS)
472+
goto close;
473+
474+
/* Get the device id from parent devnode */
475+
len = 0;
476+
cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_Device_InstanceId, &property_type, NULL, &len, 0);
477+
if (cr == CR_BUFFER_SMALL || property_type == DEVPROP_TYPE_STRING) {
478+
free(device_id);
479+
device_id = (wchar_t*)calloc(len, sizeof(BYTE));
480+
cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_Device_InstanceId, &property_type, (PBYTE)device_id, &len, 0);
481+
}
482+
if (cr != CR_SUCCESS)
483+
goto close;
484+
485+
/* Normalize the device id */
486+
for (wchar_t* p = device_id; *p; ++p) *p = towupper(*p);
487+
488+
/* Now we can parse parent's device ID to find out the device bus type */
489+
490+
/* USB device ids always have a special prefix
491+
https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support
492+
https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers */
493+
if (wcsstr(device_id, L"USB\\") != 0) {
494+
dev->bus_type = HID_BUS_USB;
495+
goto close;
496+
}
497+
498+
/* Bluetooth devices ids have a special prefix
499+
https://docs.microsoft.com/windows-hardware/drivers/bluetooth/installing-a-bluetooth-device */
500+
if (wcsstr(device_id, L"BTHENUM\\") != 0) {
501+
dev->bus_type = HID_BUS_BLUETOOTH;
502+
goto close;
503+
}
504+
505+
/* Bluetooth LE HID device */
506+
if (wcsstr(device_id, L"BTHLEDEVICE\\") != 0) {
507+
dev->bus_type = HID_BUS_BLUETOOTH;
508+
goto close;
509+
}
510+
511+
/* I2C HID devices have a special ACPI ID
512+
https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support-and-power-management */
513+
if (wcsstr(device_id, L"ACPI\\PNP0C50") != 0) {
514+
dev->bus_type = HID_BUS_I2C;
515+
goto close;
516+
}
517+
518+
/* SPI HID devices have a special ACPI ID
519+
https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-for-spi */
520+
if (wcsstr(device_id, L"ACPI\\PNP0C51") != 0) {
521+
dev->bus_type = HID_BUS_SPI;
522+
goto close;
523+
}
524+
525+
close:
526+
free(device_id);
527+
free(interface_path);
528+
}
529+
407530
static struct hid_device_info *hid_get_device_info(const char *path, HANDLE handle)
408531
{
409532
struct hid_device_info *dev = NULL; /* return object */
@@ -491,6 +614,8 @@ static struct hid_device_info *hid_get_device_info(const char *path, HANDLE hand
491614
}
492615
}
493616

617+
hid_internal_get_info(dev);
618+
494619
return dev;
495620
}
496621

0 commit comments

Comments
 (0)