Skip to content

Commit

Permalink
windows: cleanup code a bit (libusb#305)
Browse files Browse the repository at this point in the history
  • Loading branch information
DJm00n authored Jul 16, 2021
1 parent 9404a95 commit 2a24bf9
Showing 1 changed file with 114 additions and 93 deletions.
207 changes: 114 additions & 93 deletions windows/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ static struct hid_api_version api_version = {
typedef void* PHIDP_PREPARSED_DATA;
#define HIDP_STATUS_SUCCESS 0x110000

typedef void (__stdcall *HidD_GetHidGuid_)(LPGUID hid_guid);
typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib);
typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len);
typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
Expand All @@ -123,6 +124,7 @@ static struct hid_api_version api_version = {
typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps);
typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers);

static HidD_GetHidGuid_ HidD_GetHidGuid;
static HidD_GetAttributes_ HidD_GetAttributes;
static HidD_GetSerialNumberString_ HidD_GetSerialNumberString;
static HidD_GetManufacturerString_ HidD_GetManufacturerString;
Expand Down Expand Up @@ -230,6 +232,7 @@ static int lookup_functions()
# pragma GCC diagnostic ignored "-Wcast-function-type"
#endif
#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1;
RESOLVE(HidD_GetHidGuid);
RESOLVE(HidD_GetAttributes);
RESOLVE(HidD_GetSerialNumberString);
RESOLVE(HidD_GetManufacturerString);
Expand Down Expand Up @@ -306,14 +309,102 @@ int HID_API_EXPORT hid_exit(void)
return 0;
}

static struct hid_device_info *hid_get_device_info(const char *path, HANDLE handle)
{
struct hid_device_info *dev = NULL; /* return object */

BOOL res;
HIDD_ATTRIBUTES attrib;
PHIDP_PREPARSED_DATA pp_data = NULL;
HIDP_CAPS caps;

#define WSTR_LEN 512
wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */

/* Create the record. */
dev = (struct hid_device_info*)calloc(1, sizeof(struct hid_device_info));

/* Fill out the record */
dev->next = NULL;

if (path) {
size_t len = strlen(path);
dev->path = (char*)calloc(len + 1, sizeof(char));
strncpy(dev->path, path, len + 1);
dev->path[len] = '\0';
}
else
dev->path = NULL;

attrib.Size = sizeof(HIDD_ATTRIBUTES);
res = HidD_GetAttributes(handle, &attrib);
if (res) {
/* VID/PID */
dev->vendor_id = attrib.VendorID;
dev->product_id = attrib.ProductID;

/* Release Number */
dev->release_number = attrib.VersionNumber;
}

/* Get the Usage Page and Usage for this device. */
res = HidD_GetPreparsedData(handle, &pp_data);
if (res) {
NTSTATUS nt_res = HidP_GetCaps(pp_data, &caps);
if (nt_res == HIDP_STATUS_SUCCESS) {
dev->usage_page = caps.UsagePage;
dev->usage = caps.Usage;
}

HidD_FreePreparsedData(pp_data);
}

/* Serial Number */
wstr[0] = L'\0';
res = HidD_GetSerialNumberString(handle, wstr, sizeof(wstr));
wstr[WSTR_LEN - 1] = L'\0';
dev->serial_number = _wcsdup(wstr);

/* Manufacturer String */
wstr[0] = L'\0';
res = HidD_GetManufacturerString(handle, wstr, sizeof(wstr));
wstr[WSTR_LEN - 1] = L'\0';
dev->manufacturer_string = _wcsdup(wstr);

/* Product String */
wstr[0] = L'\0';
res = HidD_GetProductString(handle, wstr, sizeof(wstr));
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;
}
}
}

return dev;
}

struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
{
BOOL res;
struct hid_device_info *root = NULL; /* return object */
struct hid_device_info *cur_dev = NULL;

/* Hard-coded GUID retreived by HidD_GetHidGuid */
GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} };
GUID interface_class_guid;

/* Windows objects for interacting with the driver. */
SP_DEVINFO_DATA devinfo_data;
Expand All @@ -326,24 +417,28 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor
if (hid_init() < 0)
return NULL;

/* Retrieve HID Interface Class GUID
https://docs.microsoft.com/windows-hardware/drivers/install/guid-devinterface-hid */
HidD_GetHidGuid(&interface_class_guid);

/* Initialize the Windows objects. */
memset(&devinfo_data, 0x0, sizeof(devinfo_data));
devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA);
device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);

/* Get information for all the devices belonging to the HID class. */
device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
device_info_set = SetupDiGetClassDevsA(&interface_class_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

/* Iterate over each device in the HID class, looking for the right one. */

for (;;) {
HANDLE write_handle = INVALID_HANDLE_VALUE;
HANDLE read_handle = INVALID_HANDLE_VALUE;
DWORD required_size = 0;
HIDD_ATTRIBUTES attrib;

res = SetupDiEnumDeviceInterfaces(device_info_set,
NULL,
&InterfaceClassGuid,
&interface_class_guid,
device_index,
&device_interface_data);

Expand Down Expand Up @@ -398,117 +493,44 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor

//wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath);

/* Open a handle to the device */
write_handle = open_device(device_interface_detail_data->DevicePath, FALSE);
/* Open read-only handle to the device */
read_handle = open_device(device_interface_detail_data->DevicePath, FALSE);

/* Check validity of write_handle. */
if (write_handle == INVALID_HANDLE_VALUE) {
/* Check validity of read_handle. */
if (read_handle == INVALID_HANDLE_VALUE) {
/* Unable to open the device. */
//register_error(dev, "CreateFile");
goto cont_close;
goto cont;
}


/* Get the Vendor ID and Product ID for this device. */
attrib.Size = sizeof(HIDD_ATTRIBUTES);
HidD_GetAttributes(write_handle, &attrib);
HidD_GetAttributes(read_handle, &attrib);
//wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID);

/* 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)) {

#define WSTR_LEN 512
const char *str;
struct hid_device_info *tmp;
PHIDP_PREPARSED_DATA pp_data = NULL;
HIDP_CAPS caps;
NTSTATUS nt_res;
wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */
size_t len;

/* VID/PID match. Create the record. */
tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
struct hid_device_info *tmp = hid_get_device_info(device_interface_detail_data->DevicePath, read_handle);

if (tmp == NULL) {
goto cont_close;
}

if (cur_dev) {
cur_dev->next = tmp;
}
else {
root = tmp;
}
cur_dev = tmp;

/* Get the Usage Page and Usage for this device. */
res = HidD_GetPreparsedData(write_handle, &pp_data);
if (res) {
nt_res = HidP_GetCaps(pp_data, &caps);
if (nt_res == HIDP_STATUS_SUCCESS) {
cur_dev->usage_page = caps.UsagePage;
cur_dev->usage = caps.Usage;
}

HidD_FreePreparsedData(pp_data);
}

/* Fill out the record */
cur_dev->next = NULL;
str = device_interface_detail_data->DevicePath;
if (str) {
len = strlen(str);
cur_dev->path = (char*) calloc(len+1, sizeof(char));
strncpy(cur_dev->path, str, len+1);
cur_dev->path[len] = '\0';
}
else
cur_dev->path = NULL;

/* Serial Number */
wstr[0]= L'\0';
res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr));
wstr[WSTR_LEN-1] = L'\0';
cur_dev->serial_number = _wcsdup(wstr);

/* Manufacturer String */
wstr[0]= L'\0';
res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr));
wstr[WSTR_LEN-1] = L'\0';
cur_dev->manufacturer_string = _wcsdup(wstr);

/* Product String */
wstr[0]= L'\0';
res = HidD_GetProductString(write_handle, wstr, sizeof(wstr));
wstr[WSTR_LEN-1] = L'\0';
cur_dev->product_string = _wcsdup(wstr);

/* VID/PID */
cur_dev->vendor_id = attrib.VendorID;
cur_dev->product_id = attrib.ProductID;

/* Release Number */
cur_dev->release_number = attrib.VersionNumber;

/* Interface Number. It can sometimes be parsed out of the path
on Windows if a device has multiple interfaces. See
http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or
search for "Hardware IDs for HID Devices" at MSDN. If it's not
in the path, it's set to -1. */
cur_dev->interface_number = -1;
if (cur_dev->path) {
char *interface_component = strstr(cur_dev->path, "&mi_");
if (interface_component) {
char *hex_str = interface_component + 4;
char *endptr = NULL;
cur_dev->interface_number = strtol(hex_str, &endptr, 16);
if (endptr == hex_str) {
/* The parsing failed. Set interface_number to -1. */
cur_dev->interface_number = -1;
}
}
}
}

cont_close:
CloseHandle(write_handle);
CloseHandle(read_handle);
cont:
/* We no longer need the detail data. It can be freed */
free(device_interface_detail_data);
Expand All @@ -521,7 +543,6 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor
SetupDiDestroyDeviceInfoList(device_info_set);

return root;

}

void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
Expand Down

0 comments on commit 2a24bf9

Please sign in to comment.