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

This merges mainstream commit
4f2e91bae8
(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).
This commit is contained in:
Ozkan Sezer 2024-03-05 11:11:04 +03:00 committed by Ozkan Sezer
parent 26e3ca7387
commit 98bec6749f

View file

@ -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 @@ end:
}
#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;