Skip to content

Commit 4f2e91b

Browse files
authored
windows: change MAX_STRING_WCHARS to 126 (#627)
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).
1 parent 7011fa9 commit 4f2e91b

File tree

1 file changed

+69
-17
lines changed

1 file changed

+69
-17
lines changed

windows/hid.c

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ typedef LONG NTSTATUS;
6969
/* BLUETOOTH_DEVICE_NAME_SIZE from bluetoothapis.h is 256 */
7070
#define MAX_STRING_WCHARS 256
7171

72+
/* For certain USB devices, using a buffer larger or equal to 127 wchars results
73+
in successful completion of HID API functions, but a broken string is stored
74+
in the output buffer. This behaviour persists even if HID API is bypassed and
75+
HID IOCTLs are passed to the HID driver directly. Therefore, for USB devices,
76+
the buffer MUST NOT exceed 126 WCHARs.
77+
*/
78+
79+
#define MAX_STRING_WCHARS_USB 126
80+
7281
static struct hid_api_version api_version = {
7382
.major = HID_API_VERSION_MAJOR,
7483
.minor = HID_API_VERSION_MINOR,
@@ -590,11 +599,22 @@ static void hid_internal_get_ble_info(struct hid_device_info* dev, DEVINST dev_n
590599
}
591600
}
592601

593-
static void hid_internal_get_info(const wchar_t* interface_path, struct hid_device_info* dev)
602+
/* Unfortunately, HID_API_BUS_xxx constants alone aren't enough to distinguish between BLUETOOTH and BLE */
603+
604+
#define HID_API_BUS_FLAG_BLE 0x01
605+
606+
typedef struct hid_internal_detect_bus_type_result_ {
607+
DEVINST dev_node;
608+
hid_bus_type bus_type;
609+
unsigned int bus_flags;
610+
} hid_internal_detect_bus_type_result;
611+
612+
static hid_internal_detect_bus_type_result hid_internal_detect_bus_type(const wchar_t* interface_path)
594613
{
595614
wchar_t *device_id = NULL, *compatible_ids = NULL;
596615
CONFIGRET cr;
597616
DEVINST dev_node;
617+
hid_internal_detect_bus_type_result result = { 0 };
598618

599619
/* Get the device id from interface path */
600620
device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING);
@@ -625,42 +645,45 @@ static void hid_internal_get_info(const wchar_t* interface_path, struct hid_devi
625645
https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support
626646
https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers */
627647
if (wcsstr(compatible_id, L"USB") != NULL) {
628-
dev->bus_type = HID_API_BUS_USB;
629-
hid_internal_get_usb_info(dev, dev_node);
648+
result.bus_type = HID_API_BUS_USB;
630649
break;
631650
}
632651

633652
/* Bluetooth devices
634653
https://docs.microsoft.com/windows-hardware/drivers/bluetooth/installing-a-bluetooth-device */
635654
if (wcsstr(compatible_id, L"BTHENUM") != NULL) {
636-
dev->bus_type = HID_API_BUS_BLUETOOTH;
655+
result.bus_type = HID_API_BUS_BLUETOOTH;
637656
break;
638657
}
639658

640659
/* Bluetooth LE devices */
641660
if (wcsstr(compatible_id, L"BTHLEDEVICE") != NULL) {
642-
dev->bus_type = HID_API_BUS_BLUETOOTH;
643-
hid_internal_get_ble_info(dev, dev_node);
661+
result.bus_type = HID_API_BUS_BLUETOOTH;
662+
result.bus_flags |= HID_API_BUS_FLAG_BLE;
644663
break;
645664
}
646665

647666
/* I2C devices
648667
https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support-and-power-management */
649668
if (wcsstr(compatible_id, L"PNP0C50") != NULL) {
650-
dev->bus_type = HID_API_BUS_I2C;
669+
result.bus_type = HID_API_BUS_I2C;
651670
break;
652671
}
653672

654673
/* SPI devices
655674
https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-for-spi */
656675
if (wcsstr(compatible_id, L"PNP0C51") != NULL) {
657-
dev->bus_type = HID_API_BUS_SPI;
676+
result.bus_type = HID_API_BUS_SPI;
658677
break;
659678
}
660679
}
680+
681+
result.dev_node = dev_node;
682+
661683
end:
662684
free(device_id);
663685
free(compatible_ids);
686+
return result;
664687
}
665688

666689
static char *hid_internal_UTF16toUTF8(const wchar_t *src)
@@ -699,7 +722,10 @@ static struct hid_device_info *hid_internal_get_device_info(const wchar_t *path,
699722
HIDD_ATTRIBUTES attrib;
700723
PHIDP_PREPARSED_DATA pp_data = NULL;
701724
HIDP_CAPS caps;
702-
wchar_t string[MAX_STRING_WCHARS];
725+
wchar_t string[MAX_STRING_WCHARS + 1];
726+
ULONG len;
727+
ULONG size;
728+
hid_internal_detect_bus_type_result detect_bus_type_result;
703729

704730
/* Create the record. */
705731
dev = (struct hid_device_info*)calloc(1, sizeof(struct hid_device_info));
@@ -733,25 +759,46 @@ static struct hid_device_info *hid_internal_get_device_info(const wchar_t *path,
733759
HidD_FreePreparsedData(pp_data);
734760
}
735761

762+
/* detect bus type before reading string descriptors */
763+
detect_bus_type_result = hid_internal_detect_bus_type(path);
764+
dev->bus_type = detect_bus_type_result.bus_type;
765+
766+
len = dev->bus_type == HID_API_BUS_USB ? MAX_STRING_WCHARS_USB : MAX_STRING_WCHARS;
767+
string[len] = L'\0';
768+
size = len * sizeof(wchar_t);
769+
736770
/* Serial Number */
737771
string[0] = L'\0';
738-
HidD_GetSerialNumberString(handle, string, sizeof(string));
739-
string[MAX_STRING_WCHARS - 1] = L'\0';
772+
HidD_GetSerialNumberString(handle, string, size);
740773
dev->serial_number = _wcsdup(string);
741774

742775
/* Manufacturer String */
743776
string[0] = L'\0';
744-
HidD_GetManufacturerString(handle, string, sizeof(string));
745-
string[MAX_STRING_WCHARS - 1] = L'\0';
777+
HidD_GetManufacturerString(handle, string, size);
746778
dev->manufacturer_string = _wcsdup(string);
747779

748780
/* Product String */
749781
string[0] = L'\0';
750-
HidD_GetProductString(handle, string, sizeof(string));
751-
string[MAX_STRING_WCHARS - 1] = L'\0';
782+
HidD_GetProductString(handle, string, size);
752783
dev->product_string = _wcsdup(string);
753784

754-
hid_internal_get_info(path, dev);
785+
/* now, the portion that depends on string descriptors */
786+
switch (dev->bus_type) {
787+
case HID_API_BUS_USB:
788+
hid_internal_get_usb_info(dev, detect_bus_type_result.dev_node);
789+
break;
790+
791+
case HID_API_BUS_BLUETOOTH:
792+
if (detect_bus_type_result.bus_flags & HID_API_BUS_FLAG_BLE)
793+
hid_internal_get_ble_info(dev, detect_bus_type_result.dev_node);
794+
break;
795+
796+
case HID_API_BUS_UNKNOWN:
797+
case HID_API_BUS_SPI:
798+
case HID_API_BUS_I2C:
799+
/* shut down -Wswitch */
800+
break;
801+
}
755802

756803
return dev;
757804
}
@@ -1356,7 +1403,12 @@ int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int
13561403
{
13571404
BOOL res;
13581405

1359-
res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * (DWORD) MIN(maxlen, MAX_STRING_WCHARS));
1406+
if (dev->device_info && dev->device_info->bus_type == HID_API_BUS_USB && maxlen > MAX_STRING_WCHARS_USB) {
1407+
string[MAX_STRING_WCHARS_USB] = L'\0';
1408+
maxlen = MAX_STRING_WCHARS_USB;
1409+
}
1410+
1411+
res = HidD_GetIndexedString(dev->device_handle, string_index, string, (ULONG)maxlen * sizeof(wchar_t));
13601412
if (!res) {
13611413
register_winapi_error(dev, L"HidD_GetIndexedString");
13621414
return -1;

0 commit comments

Comments
 (0)