Skip to content

Commit 18c9db6

Browse files
committed
Add code to determine HID device bus type
1 parent 2a24bf9 commit 18c9db6

File tree

6 files changed

+201
-0
lines changed

6 files changed

+201
-0
lines changed

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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ int main(int argc, char* argv[])
6262
printf(" Release: %hx\n", cur_dev->release_number);
6363
printf(" Interface: %d\n", cur_dev->interface_number);
6464
printf(" Usage (page): 0x%hx (0x%hx)\n", cur_dev->usage, cur_dev->usage_page);
65+
printf(" Bus type: %d\n", cur_dev->bus_type);
6566
printf("\n");
6667
cur_dev = cur_dev->next;
6768
}

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: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,31 @@ static struct hid_api_version api_version = {
140140

141141
static HMODULE lib_handle = NULL;
142142
static BOOLEAN initialized = FALSE;
143+
144+
typedef DWORD RETURN_TYPE;
145+
typedef RETURN_TYPE CONFIGRET;
146+
typedef DWORD DEVNODE, DEVINST;
147+
typedef DEVNODE* PDEVNODE, * PDEVINST;
148+
typedef WCHAR* DEVNODEID_W, * DEVINSTID_W;
149+
150+
#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;
166+
167+
static HMODULE cfgmgr32_lib_handle = NULL;
143168
#endif /* HIDAPI_USE_DDK */
144169

145170
struct hid_device_ {
@@ -253,6 +278,23 @@ static int lookup_functions()
253278
else
254279
return -1;
255280

281+
cfgmgr32_lib_handle = LoadLibraryA("cfgmgr32.dll");
282+
if (cfgmgr32_lib_handle) {
283+
#if defined(__GNUC__)
284+
# pragma GCC diagnostic push
285+
# pragma GCC diagnostic ignored "-Wcast-function-type"
286+
#endif
287+
#define RESOLVE(x) x = (x##_)GetProcAddress(cfgmgr32_lib_handle, #x); if (!x) return -1;
288+
RESOLVE(CM_Locate_DevNodeW);
289+
RESOLVE(CM_Get_Parent);
290+
RESOLVE(CM_Get_DevNode_PropertyW);
291+
RESOLVE(CM_Get_Device_Interface_PropertyW);
292+
#undef RESOLVE
293+
#if defined(__GNUC__)
294+
# pragma GCC diagnostic pop
295+
#endif
296+
}
297+
256298
return 0;
257299
}
258300
#endif
@@ -304,11 +346,114 @@ int HID_API_EXPORT hid_exit(void)
304346
if (lib_handle)
305347
FreeLibrary(lib_handle);
306348
lib_handle = NULL;
349+
if (cfgmgr32_lib_handle)
350+
FreeLibrary(cfgmgr32_lib_handle);
351+
cfgmgr32_lib_handle = NULL;
307352
initialized = FALSE;
308353
#endif
309354
return 0;
310355
}
311356

357+
static void hid_internal_get_info(struct hid_device_info* dev)
358+
{
359+
dev->bus_type = HID_BUS_UNKNOWN;
360+
361+
wchar_t *device_id, *interface_path;
362+
ULONG len;
363+
CONFIGRET cr;
364+
DEVPROPTYPE property_type;
365+
DEVINST dev_node;
366+
367+
static DEVPROPKEY DEVPKEY_Device_InstanceId = { { 0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57 }, 256 }; // DEVPROP_TYPE_STRING
368+
369+
if (!CM_Get_Device_Interface_PropertyW ||
370+
!CM_Locate_DevNodeW ||
371+
!CM_Get_Parent ||
372+
!CM_Get_DevNode_PropertyW)
373+
goto close;
374+
375+
len = (ULONG)strlen(dev->path);
376+
interface_path = (wchar_t*)calloc(len + 1, sizeof(wchar_t));
377+
378+
if (mbstowcs(interface_path, dev->path, len) == (size_t)-1)
379+
goto close;
380+
381+
/* Get the device id from interface path */
382+
len = 0;
383+
cr = CM_Get_Device_Interface_PropertyW(interface_path, &DEVPKEY_Device_InstanceId, &property_type, NULL, &len, 0);
384+
if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING) {
385+
device_id = (wchar_t*)calloc(len, sizeof(BYTE));
386+
cr = CM_Get_Device_Interface_PropertyW(interface_path, &DEVPKEY_Device_InstanceId, &property_type, (PBYTE)device_id, &len, 0);
387+
}
388+
if (cr != CR_SUCCESS)
389+
goto close;
390+
391+
/* Open devnode from device id */
392+
cr = CM_Locate_DevNodeW(&dev_node, (DEVINSTID_W)device_id, CM_LOCATE_DEVNODE_NORMAL);
393+
if (cr != CR_SUCCESS)
394+
goto close;
395+
396+
/* Get devnode parent */
397+
cr = CM_Get_Parent(&dev_node, dev_node, 0);
398+
if (cr != CR_SUCCESS)
399+
goto close;
400+
401+
/* Get the device id from parent devnode */
402+
len = 0;
403+
cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_Device_InstanceId, &property_type, NULL, &len, 0);
404+
if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING) {
405+
free(device_id);
406+
device_id = (wchar_t*)calloc(len, sizeof(BYTE));
407+
cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_Device_InstanceId, &property_type, (PBYTE)device_id, &len, 0);
408+
}
409+
if (cr != CR_SUCCESS)
410+
goto close;
411+
412+
/* Normalize the device id */
413+
for (wchar_t* p = device_id; *p; ++p) *p = towupper(*p);
414+
415+
/* Now we can parse parent's device ID to find out the device bus type */
416+
417+
/* USB device ids always have a special prefix
418+
https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support
419+
https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers */
420+
if (wcsstr(device_id, L"USB\\") != 0) {
421+
dev->bus_type = HID_BUS_USB;
422+
goto close;
423+
}
424+
425+
/* Bluetooth devices ids have a special prefix
426+
https://docs.microsoft.com/windows-hardware/drivers/bluetooth/installing-a-bluetooth-device */
427+
if (wcsstr(device_id, L"BTHENUM\\") != 0) {
428+
dev->bus_type = HID_BUS_BLUETOOTH;
429+
goto close;
430+
}
431+
432+
/* Bluetooth LE device */
433+
if (wcsstr(device_id, L"BTHLEDEVICE\\") != 0) {
434+
dev->bus_type = HID_BUS_BLUETOOTH;
435+
goto close;
436+
}
437+
438+
/* I2C HID devices have a special ACPI ID
439+
https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support-and-power-management */
440+
if (wcsstr(device_id, L"ACPI\\PNP0C50") != 0) {
441+
dev->bus_type = HID_BUS_I2C;
442+
goto close;
443+
}
444+
445+
/* SPI HID devices have a special ACPI ID
446+
https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-for-spi */
447+
if (wcsstr(device_id, L"ACPI\\PNP0C51") != 0) {
448+
dev->bus_type = HID_BUS_SPI;
449+
goto close;
450+
}
451+
452+
close:
453+
free(device_id);
454+
free(interface_path);
455+
}
456+
312457
static struct hid_device_info *hid_get_device_info(const char *path, HANDLE handle)
313458
{
314459
struct hid_device_info *dev = NULL; /* return object */
@@ -396,6 +541,8 @@ static struct hid_device_info *hid_get_device_info(const char *path, HANDLE hand
396541
}
397542
}
398543

544+
hid_internal_get_info(dev);
545+
399546
return dev;
400547
}
401548

0 commit comments

Comments
 (0)