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.
Cameron Gutman 2020-03-20 13:44:50 -07:00
parent 106ffd57d8
commit 5ed71f3bc0
1 changed files with 31 additions and 13 deletions

View File

@ -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) {