SDL: hidapi, windows: sync with mainstream: change MAX_STRING_WCHARS to 126.

From 98bec6749f202176238416ca650c11b5ab0eb1d2 Mon Sep 17 00:00:00 2001
From: Ozkan Sezer <[EMAIL REDACTED]>
Date: Tue, 5 Mar 2024 11:11:04 +0300
Subject: [PATCH] hidapi, windows: sync with mainstream: change
 MAX_STRING_WCHARS to 126.

This merges mainstream commit
https://github.com/libusb/hidapi/commit/4f2e91bae80cc48e567a80bd9ae3dc53dc5b73c6
(authored by Vladimir Gladkov) into ours. From the original commit log:

Win32 HID API doc says: For USB devices, the maximum string length is
126 wide characters (not including the terminating NULL character).

For certain USB devices, using a buffer larger or equal to 127 wchars
results in successful completion of HID API functions, but a broken
string is stored in the output buffer. This behaviour persists even if
HID API is bypassed and HID IOCTLs are passed to the HID driver directly
(IOCTL_HID_GET_MANUFACTURER_STRING, IOCTL_HID_GET_PRODUCT_STRING, etc).

So, the buffer MUST NOT exceed 126 wchars.

windows: refactor ULONGLONG hid_internal_get_info(...) ->
 hid_internal_detect_bus_type_result hid_internal_detect_bus_type(...)

hid_internal_detect_bus_type is now only responsible for detection of
the bus type; rename it accordingly. Also, mixing an internal flag and
DEV_INST into an ULONGLONG retval feels kinda hackish; use a cleaner
approach instead (add an internal flag to help distinguishing between
BLUETOOTH and BLE devices, then clear it once we are done).
---
 src/hidapi/windows/hid.c | 86 ++++++++++++++++++++++++++++++++--------
 1 file changed, 69 insertions(+), 17 deletions(-)

diff --git a/src/hidapi/windows/hid.c b/src/hidapi/windows/hid.c
index b602fb7c2ee94..660c20cefae05 100644
--- a/src/hidapi/windows/hid.c
+++ b/src/hidapi/windows/hid.c
@@ -72,6 +72,15 @@ typedef LONG NTSTATUS;
 /* BLUETOOTH_DEVICE_NAME_SIZE from bluetoothapis.h is 256 */
 #define MAX_STRING_WCHARS 256
 
+/* For certain USB devices, using a buffer larger or equal to 127 wchars results
+   in successful completion of HID API functions, but a broken string is stored
+   in the output buffer. This behaviour persists even if HID API is bypassed and
+   HID IOCTLs are passed to the HID driver directly. Therefore, for USB devices,
+   the buffer MUST NOT exceed 126 WCHARs.
+*/
+
+#define MAX_STRING_WCHARS_USB 126
+
 static struct hid_api_version api_version = {
 	.major = HID_API_VERSION_MAJOR,
 	.minor = HID_API_VERSION_MINOR,
@@ -719,11 +728,22 @@ static hid_bus_type get_bus_type(const wchar_t* interface_path)
 }
 #endif /* HIDAPI_IGNORE_DEVICE */
 
-static void hid_internal_get_info(const wchar_t* interface_path, struct hid_device_info* dev)
+/* Unfortunately, HID_API_BUS_xxx constants alone aren't enough to distinguish between BLUETOOTH and BLE */
+
+#define HID_API_BUS_FLAG_BLE 0x01
+
+typedef struct hid_internal_detect_bus_type_result_ {
+	DEVINST dev_node;
+	hid_bus_type bus_type;
+	unsigned int bus_flags;
+} hid_internal_detect_bus_type_result;
+
+static hid_internal_detect_bus_type_result hid_internal_detect_bus_type(const wchar_t* interface_path)
 {
 	wchar_t *device_id = NULL, *compatible_ids = NULL;
 	CONFIGRET cr;
 	DEVINST dev_node;
+	hid_internal_detect_bus_type_result result = { 0 };
 
 	/* Get the device id from interface path */
 	device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING);
@@ -754,42 +774,45 @@ static void hid_internal_get_info(const wchar_t* interface_path, struct hid_devi
 		   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(compatible_id, L"USB") != NULL) {
-			dev->bus_type = HID_API_BUS_USB;
-			hid_internal_get_usb_info(dev, dev_node);
+			result.bus_type = HID_API_BUS_USB;
 			break;
 		}
 
 		/* Bluetooth devices
 		   https://docs.microsoft.com/windows-hardware/drivers/bluetooth/installing-a-bluetooth-device */
 		if (wcsstr(compatible_id, L"BTHENUM") != NULL) {
-			dev->bus_type = HID_API_BUS_BLUETOOTH;
+			result.bus_type = HID_API_BUS_BLUETOOTH;
 			break;
 		}
 
 		/* Bluetooth LE devices */
 		if (wcsstr(compatible_id, L"BTHLEDEVICE") != NULL) {
-			dev->bus_type = HID_API_BUS_BLUETOOTH;
-			hid_internal_get_ble_info(dev, dev_node);
+			result.bus_type = HID_API_BUS_BLUETOOTH;
+			result.bus_flags |= HID_API_BUS_FLAG_BLE;
 			break;
 		}
 
 		/* I2C devices
 		   https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support-and-power-management */
 		if (wcsstr(compatible_id, L"PNP0C50") != NULL) {
-			dev->bus_type = HID_API_BUS_I2C;
+			result.bus_type = HID_API_BUS_I2C;
 			break;
 		}
 
 		/* SPI devices
 		   https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-for-spi */
 		if (wcsstr(compatible_id, L"PNP0C51") != NULL) {
-			dev->bus_type = HID_API_BUS_SPI;
+			result.bus_type = HID_API_BUS_SPI;
 			break;
 		}
 	}
+
+	result.dev_node = dev_node;
+
 end:
 	free(device_id);
 	free(compatible_ids);
+	return result;
 }
 
 static char *hid_internal_UTF16toUTF8(const wchar_t *src)
@@ -836,7 +859,10 @@ static struct hid_device_info *hid_internal_get_device_info(const wchar_t *path,
 	HIDD_ATTRIBUTES attrib;
 	PHIDP_PREPARSED_DATA pp_data = NULL;
 	HIDP_CAPS caps;
-	wchar_t string[MAX_STRING_WCHARS];
+	wchar_t string[MAX_STRING_WCHARS + 1];
+	ULONG len;
+	ULONG size;
+	hid_internal_detect_bus_type_result detect_bus_type_result;
 
 	/* Create the record. */
 	dev = (struct hid_device_info*)calloc(1, sizeof(struct hid_device_info));
@@ -870,25 +896,46 @@ static struct hid_device_info *hid_internal_get_device_info(const wchar_t *path,
 		HidD_FreePreparsedData(pp_data);
 	}
 
+	/* detect bus type before reading string descriptors */
+	detect_bus_type_result = hid_internal_detect_bus_type(path);
+	dev->bus_type = detect_bus_type_result.bus_type;
+
+	len = dev->bus_type == HID_API_BUS_USB ? MAX_STRING_WCHARS_USB : MAX_STRING_WCHARS;
+	string[len] = L'\0';
+	size = len * sizeof(wchar_t);
+
 	/* Serial Number */
 	string[0] = L'\0';
-	HidD_GetSerialNumberString(handle, string, sizeof(string));
-	string[MAX_STRING_WCHARS - 1] = L'\0';
+	HidD_GetSerialNumberString(handle, string, size);
 	dev->serial_number = _wcsdup(string);
 
 	/* Manufacturer String */
 	string[0] = L'\0';
-	HidD_GetManufacturerString(handle, string, sizeof(string));
-	string[MAX_STRING_WCHARS - 1] = L'\0';
+	HidD_GetManufacturerString(handle, string, size);
 	dev->manufacturer_string = _wcsdup(string);
 
 	/* Product String */
 	string[0] = L'\0';
-	HidD_GetProductString(handle, string, sizeof(string));
-	string[MAX_STRING_WCHARS - 1] = L'\0';
+	HidD_GetProductString(handle, string, size);
 	dev->product_string = _wcsdup(string);
 
-	hid_internal_get_info(path, dev);
+	/* now, the portion that depends on string descriptors */
+	switch (dev->bus_type) {
+	case HID_API_BUS_USB:
+		hid_internal_get_usb_info(dev, detect_bus_type_result.dev_node);
+		break;
+
+	case HID_API_BUS_BLUETOOTH:
+		if (detect_bus_type_result.bus_flags & HID_API_BUS_FLAG_BLE)
+			hid_internal_get_ble_info(dev, detect_bus_type_result.dev_node);
+		break;
+
+	case HID_API_BUS_UNKNOWN:
+	case HID_API_BUS_SPI:
+	case HID_API_BUS_I2C:
+		/* shut down -Wswitch */
+		break;
+	}
 
 	return dev;
 }
@@ -1572,7 +1619,12 @@ int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int
 {
 	BOOL res;
 
-	res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * (DWORD) MIN(maxlen, MAX_STRING_WCHARS));
+	if (dev->device_info && dev->device_info->bus_type == HID_API_BUS_USB && maxlen > MAX_STRING_WCHARS_USB) {
+		string[MAX_STRING_WCHARS_USB] = L'\0';
+		maxlen = MAX_STRING_WCHARS_USB;
+	}
+
+	res = HidD_GetIndexedString(dev->device_handle, string_index, string, (ULONG)maxlen * sizeof(wchar_t));
 	if (!res) {
 		register_winapi_error(dev, L"HidD_GetIndexedString");
 		return -1;