From 5ed71f3bc0a4215cd00709ef4084b9fce36c0886 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Fri, 20 Mar 2020 13:44:50 -0700 Subject: [PATCH] Only enumerate HID devices on Windows that have gamepad HID usages There are a number of poorly behaved HID devices that time out on attempts to read various strings. Rather than end up on an endless treadmill of blacklisting broken devices, reduce our risk by only querying devices that are gamepads. SDL_hidapijoystick.c already checks these same usages, so we shouldn't exclude any working HID devices (caveat below). This also makes HidP_GetPreparsedData() and HidP_GetCaps() failure skip the device entirely, but that seems desired. If a device can't even return basic top-level collection data properly, we want nothing to do with that broken device. If we do find devices that work with HIDAPI joystick and fail these calls, we can add an exception via VID+PID matching. --- src/hidapi/windows/hid.c | 44 ++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/src/hidapi/windows/hid.c b/src/hidapi/windows/hid.c index 5cea24f5c614e..831e0032022fd 100644 --- a/src/hidapi/windows/hid.c +++ b/src/hidapi/windows/hid.c @@ -68,6 +68,12 @@ typedef LONG NTSTATUS; report that we've seen is ~200-250ms so let's double that */ #define HID_WRITE_TIMEOUT_MILLISECONDS 500 +/* We will only enumerate devices that match these usages */ +#define USAGE_PAGE_GENERIC_DESKTOP 0x0001 +#define USAGE_JOYSTICK 0x0004 +#define USAGE_GAMEPAD 0x0005 +#define USAGE_MULTIAXISCONTROLLER 0x0008 + #ifdef __cplusplus extern "C" { #endif @@ -450,7 +456,7 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor if (write_handle == INVALID_HANDLE_VALUE) { /* Unable to open the device. */ //register_error(dev, "CreateFile"); - goto cont_close; + goto cont; } @@ -475,6 +481,28 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */ size_t len; + /* Get the Usage Page and Usage for this device. */ + hidp_res = HidD_GetPreparsedData(write_handle, &pp_data); + if (hidp_res) { + nt_res = HidP_GetCaps(pp_data, &caps); + HidD_FreePreparsedData(pp_data); + if (nt_res != HIDP_STATUS_SUCCESS) { + goto cont_close; + } + } + else { + goto cont_close; + } + + /* SDL Modification: Ignore the device if it's not a gamepad. This limits compatibility + risk from devices that may respond poorly to our string queries below. */ + if (caps.UsagePage != USAGE_PAGE_GENERIC_DESKTOP) { + goto cont_close; + } + if (caps.Usage != USAGE_JOYSTICK && caps.Usage != USAGE_GAMEPAD && caps.Usage != USAGE_MULTIAXISCONTROLLER) { + goto cont_close; + } + /* VID/PID match. Create the record. */ tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); if (cur_dev) { @@ -484,20 +512,10 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor root = tmp; } cur_dev = tmp; - - /* Get the Usage Page and Usage for this device. */ - hidp_res = HidD_GetPreparsedData(write_handle, &pp_data); - if (hidp_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->usage_page = caps.UsagePage; + cur_dev->usage = caps.Usage; cur_dev->next = NULL; str = device_interface_detail_data->DevicePath; if (str) {