Only enumerate HID devices on Windows that have gamepad HID usages
authorCameron Gutman <aicommander@gmail.com>
Fri, 20 Mar 2020 13:44:50 -0700
changeset 1366025927a14e406
parent 13659 6814f6645eb2
child 13661 8a949642b6eb
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
     1.1 --- a/src/hidapi/windows/hid.c	Fri Mar 20 20:45:30 2020 -0700
     1.2 +++ b/src/hidapi/windows/hid.c	Fri Mar 20 13:44:50 2020 -0700
     1.3 @@ -68,6 +68,12 @@
     1.4     report that we've seen is ~200-250ms so let's double that */
     1.5  #define HID_WRITE_TIMEOUT_MILLISECONDS 500
     1.6  
     1.7 +/* We will only enumerate devices that match these usages */
     1.8 +#define USAGE_PAGE_GENERIC_DESKTOP 0x0001
     1.9 +#define USAGE_JOYSTICK 0x0004
    1.10 +#define USAGE_GAMEPAD 0x0005
    1.11 +#define USAGE_MULTIAXISCONTROLLER 0x0008
    1.12 +
    1.13  #ifdef __cplusplus
    1.14  extern "C" {
    1.15  #endif
    1.16 @@ -450,7 +456,7 @@
    1.17  		if (write_handle == INVALID_HANDLE_VALUE) {
    1.18  			/* Unable to open the device. */
    1.19  			//register_error(dev, "CreateFile");
    1.20 -			goto cont_close;
    1.21 +			goto cont;
    1.22  		}		
    1.23  
    1.24  
    1.25 @@ -475,6 +481,28 @@
    1.26  			wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */
    1.27  			size_t len;
    1.28  
    1.29 +			/* Get the Usage Page and Usage for this device. */
    1.30 +			hidp_res = HidD_GetPreparsedData(write_handle, &pp_data);
    1.31 +			if (hidp_res) {
    1.32 +				nt_res = HidP_GetCaps(pp_data, &caps);
    1.33 +				HidD_FreePreparsedData(pp_data);
    1.34 +				if (nt_res != HIDP_STATUS_SUCCESS) {
    1.35 +					goto cont_close;
    1.36 +				}
    1.37 +			}
    1.38 +			else {
    1.39 +				goto cont_close;
    1.40 +			}
    1.41 +
    1.42 +			/* SDL Modification: Ignore the device if it's not a gamepad. This limits compatibility
    1.43 +			   risk from devices that may respond poorly to our string queries below. */
    1.44 +			if (caps.UsagePage != USAGE_PAGE_GENERIC_DESKTOP) {
    1.45 +				goto cont_close;
    1.46 +			}
    1.47 +			if (caps.Usage != USAGE_JOYSTICK && caps.Usage != USAGE_GAMEPAD && caps.Usage != USAGE_MULTIAXISCONTROLLER) {
    1.48 +				goto cont_close;
    1.49 +			}
    1.50 +
    1.51  			/* VID/PID match. Create the record. */
    1.52  			tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
    1.53  			if (cur_dev) {
    1.54 @@ -484,20 +512,10 @@
    1.55  				root = tmp;
    1.56  			}
    1.57  			cur_dev = tmp;
    1.58 -
    1.59 -			/* Get the Usage Page and Usage for this device. */
    1.60 -			hidp_res = HidD_GetPreparsedData(write_handle, &pp_data);
    1.61 -			if (hidp_res) {
    1.62 -				nt_res = HidP_GetCaps(pp_data, &caps);
    1.63 -				if (nt_res == HIDP_STATUS_SUCCESS) {
    1.64 -					cur_dev->usage_page = caps.UsagePage;
    1.65 -					cur_dev->usage = caps.Usage;
    1.66 -				}
    1.67 -
    1.68 -				HidD_FreePreparsedData(pp_data);
    1.69 -			}
    1.70  			
    1.71  			/* Fill out the record */
    1.72 +			cur_dev->usage_page = caps.UsagePage;
    1.73 +			cur_dev->usage = caps.Usage;
    1.74  			cur_dev->next = NULL;
    1.75  			str = device_interface_detail_data->DevicePath;
    1.76  			if (str) {