Skip to content

Commit

Permalink
Add code to determine HID device bus type
Browse files Browse the repository at this point in the history
  • Loading branch information
DJm00n committed Jul 22, 2021
1 parent 8ffd3d6 commit a2df078
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 0 deletions.
27 changes: 27 additions & 0 deletions hidapi/hidapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,30 @@ extern "C" {
struct hid_device_;
typedef struct hid_device_ hid_device; /**< opaque hidapi structure */

/** @brief HID underlying bus types.
@ingroup API
*/
typedef enum {
/* Universal Serial Bus */
HID_BUS_USB = 0x01,

/* Bluetooth or Bluetooth LE */
HID_BUS_BLUETOOTH = 0x02,

/* Inter-Integrated Circuit */
HID_BUS_I2C = 0x03,

/* Serial Peripheral Interface */
HID_BUS_SPI = 0x04,

/* Virtual HID Bus */
HID_BUS_VIRTUAL = 0x05,

/* Unknown Bus Type */
HID_BUS_UNKNOWN = 0xff,
} hid_bus_type;

/** hidapi info structure */
struct hid_device_info {
/** Platform-specific device path */
Expand Down Expand Up @@ -110,6 +134,9 @@ extern "C" {
* Valid on the Mac implementation if and only if the device
is a USB HID device. */
int interface_number;
/** Underlying bus type
(Windows/libusb/hidraw only) */
hid_bus_type bus_type;

/** Pointer to the next device */
struct hid_device_info *next;
Expand Down
2 changes: 2 additions & 0 deletions hidtest/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ int device_callback(
printf(" Release: %hx\n", device->release_number);
printf(" Interface: %d\n", device->interface_number);
printf(" Usage (page): 0x%hx (0x%hx)\n", device->usage, device->usage_page);
printf(" Bus type: %d\n", device->bus_type);
printf("\n");

//if (device->product_id == 0x0ce6)
Expand Down Expand Up @@ -89,6 +90,7 @@ int main(int argc, char* argv[])
printf(" Release: %hx\n", cur_dev->release_number);
printf(" Interface: %d\n", cur_dev->interface_number);
printf(" Usage (page): 0x%hx (0x%hx)\n", cur_dev->usage, cur_dev->usage_page);
printf(" Bus type: %d\n", cur_dev->bus_type);
printf("\n");
cur_dev = cur_dev->next;
}
Expand Down
3 changes: 3 additions & 0 deletions libusb/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,9 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,

/* Interface Number */
cur_dev->interface_number = interface_num;

/* Bus Type */
cur_dev->bus_type = HID_BUS_USB;
}
}
} /* altsettings */
Expand Down
20 changes: 20 additions & 0 deletions linux/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,9 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
/* Interface Number */
cur_dev->interface_number = -1;

/* Bus Type */
cur_dev->bus_type = HID_BUS_UNKNOWN;

switch (bus_type) {
case BUS_USB:
/* The device pointed to by raw_dev contains information about
Expand All @@ -753,13 +756,19 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
"usb",
"usb_device");

/* Bus Type */
cur_dev->bus_type = HID_BUS_USB;

/* uhid USB devices
Since this is a virtual hid interface, no USB information will
be available. */
if (!usb_dev) {
/* Manufacturer and Product strings */
cur_dev->manufacturer_string = wcsdup(L"");
cur_dev->product_string = utf8_to_wchar_t(product_name_utf8);

/* Bus Type */
cur_dev->bus_type = HID_BUS_VIRTUAL;
break;
}

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

case BUS_BLUETOOTH:
/* Manufacturer and Product strings */
cur_dev->manufacturer_string = wcsdup(L"");
cur_dev->product_string = utf8_to_wchar_t(product_name_utf8);

/* Bus Type */
cur_dev->bus_type = HID_BUS_BLUETOOTH;

break;
case BUS_I2C:
/* Manufacturer and Product strings */
cur_dev->manufacturer_string = wcsdup(L"");
cur_dev->product_string = utf8_to_wchar_t(product_name_utf8);

/* Bus Type */
cur_dev->bus_type = HID_BUS_I2C;

break;

default:
Expand Down
3 changes: 3 additions & 0 deletions mac/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,9 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev,
cur_dev->interface_number = -1;
}

/* Bus Type */
cur_dev->bus_type = HID_BUS_UNKNOWN;

return cur_dev;
}

Expand Down
124 changes: 124 additions & 0 deletions windows/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,26 @@ static struct hid_api_version api_version = {

typedef DWORD RETURN_TYPE;
typedef RETURN_TYPE CONFIGRET;
typedef DWORD DEVNODE, DEVINST;
typedef DEVNODE* PDEVNODE, * PDEVINST;
typedef WCHAR* DEVNODEID_W, * DEVINSTID_W;

#define CR_SUCCESS (0x00000000)
#define CR_BUFFER_SMALL (0x0000001A)

#define CM_LOCATE_DEVNODE_NORMAL 0x00000000

#define DEVPROP_TYPE_STRING 0x00000012

typedef CONFIGRET(__stdcall* CM_Locate_DevNodeW_)(PDEVINST pdnDevInst, DEVINSTID_W pDeviceID, ULONG ulFlags);
typedef CONFIGRET(__stdcall* CM_Get_Parent_)(PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags);
typedef CONFIGRET(__stdcall* CM_Get_DevNode_PropertyW_)(DEVINST dnDevInst, CONST DEVPROPKEY* PropertyKey, DEVPROPTYPE* PropertyType, PBYTE PropertyBuffer, PULONG PropertyBufferSize, ULONG ulFlags);
typedef CONFIGRET(__stdcall* CM_Get_Device_Interface_PropertyW_)(LPCWSTR pszDeviceInterface, CONST DEVPROPKEY* PropertyKey, DEVPROPTYPE* PropertyType, PBYTE PropertyBuffer, PULONG PropertyBufferSize, ULONG ulFlags);

static CM_Locate_DevNodeW_ CM_Locate_DevNodeW;
static CM_Get_Parent_ CM_Get_Parent;
static CM_Get_DevNode_PropertyW_ CM_Get_DevNode_PropertyW;
static CM_Get_Device_Interface_PropertyW_ CM_Get_Device_Interface_PropertyW;

DECLARE_HANDLE(HCMNOTIFICATION);
typedef HCMNOTIFICATION* PHCMNOTIFICATION;
Expand Down Expand Up @@ -337,6 +355,10 @@ static int lookup_functions()
# pragma GCC diagnostic ignored "-Wcast-function-type"
#endif
#define RESOLVE(x) x = (x##_)GetProcAddress(cfgmgr32_lib_handle, #x); if (!x) return -1;
RESOLVE(CM_Locate_DevNodeW);
RESOLVE(CM_Get_Parent);
RESOLVE(CM_Get_DevNode_PropertyW);
RESOLVE(CM_Get_Device_Interface_PropertyW);
RESOLVE(CM_Register_Notification);
RESOLVE(CM_Unregister_Notification);
#undef RESOLVE
Expand Down Expand Up @@ -404,6 +426,106 @@ int HID_API_EXPORT hid_exit(void)
return 0;
}

static void hid_internal_get_info(struct hid_device_info* dev)
{
dev->bus_type = HID_BUS_UNKNOWN;

wchar_t *device_id, *interface_path;
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

if (!CM_Get_Device_Interface_PropertyW ||
!CM_Locate_DevNodeW ||
!CM_Get_Parent ||
!CM_Get_DevNode_PropertyW)
goto close;

len = (ULONG)strlen(dev->path);
interface_path = (wchar_t*)calloc(len + 1, sizeof(wchar_t));

if (mbstowcs(interface_path, dev->path, len) == (size_t)-1)
goto close;

/* Get the device id from interface path */
len = 0;
cr = CM_Get_Device_Interface_PropertyW(interface_path, &DEVPKEY_Device_InstanceId, &property_type, NULL, &len, 0);
if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING) {
device_id = (wchar_t*)calloc(len, sizeof(BYTE));
cr = CM_Get_Device_Interface_PropertyW(interface_path, &DEVPKEY_Device_InstanceId, &property_type, (PBYTE)device_id, &len, 0);
}
if (cr != CR_SUCCESS)
goto close;

/* Open devnode from device id */
cr = CM_Locate_DevNodeW(&dev_node, (DEVINSTID_W)device_id, CM_LOCATE_DEVNODE_NORMAL);
if (cr != CR_SUCCESS)
goto close;

/* Get devnode parent */
cr = CM_Get_Parent(&dev_node, dev_node, 0);
if (cr != CR_SUCCESS)
goto close;

/* Get the device id from parent devnode */
len = 0;
cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_Device_InstanceId, &property_type, NULL, &len, 0);
if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING) {
free(device_id);
device_id = (wchar_t*)calloc(len, sizeof(BYTE));
cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_Device_InstanceId, &property_type, (PBYTE)device_id, &len, 0);
}
if (cr != CR_SUCCESS)
goto close;

/* Normalize the device id */
for (wchar_t* p = device_id; *p; ++p) *p = towupper(*p);

/* Now we can parse parent's device ID to find out the device bus type */

/* USB device ids always have a special prefix
https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support
https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers */
if (wcsstr(device_id, L"USB\\") != 0) {
dev->bus_type = HID_BUS_USB;
goto close;
}

/* Bluetooth devices ids have a special prefix
https://docs.microsoft.com/windows-hardware/drivers/bluetooth/installing-a-bluetooth-device */
if (wcsstr(device_id, L"BTHENUM\\") != 0) {
dev->bus_type = HID_BUS_BLUETOOTH;
goto close;
}

/* Bluetooth LE device */
if (wcsstr(device_id, L"BTHLEDEVICE\\") != 0) {
dev->bus_type = HID_BUS_BLUETOOTH;
goto close;
}

/* I2C HID devices have a special ACPI ID
https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support-and-power-management */
if (wcsstr(device_id, L"ACPI\\PNP0C50") != 0) {
dev->bus_type = HID_BUS_I2C;
goto close;
}

/* SPI HID devices have a special ACPI ID
https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-for-spi */
if (wcsstr(device_id, L"ACPI\\PNP0C51") != 0) {
dev->bus_type = HID_BUS_SPI;
goto close;
}

close:
free(device_id);
free(interface_path);
}

static struct hid_device_info *hid_get_device_info(const char *path, HANDLE handle)
{
struct hid_device_info *dev = NULL; /* return object */
Expand Down Expand Up @@ -491,6 +613,8 @@ static struct hid_device_info *hid_get_device_info(const char *path, HANDLE hand
}
}

hid_internal_get_info(dev);

return dev;
}

Expand Down

0 comments on commit a2df078

Please sign in to comment.