-
Notifications
You must be signed in to change notification settings - Fork 466
Known Issues
Platform-specific behaviors and limitations that aren't bugs in HIDAPI itself, but are worth knowing when integrating it.
Symptom. On Bluetooth/BLE HID devices, hid_write() returns success, but the device never receives the report. Common visible effects: rumble, LED color, or other actuator commands have no effect, while the same device works correctly over USB.
Root cause. On Windows, hid_write() is implemented via WriteFile, which the kernel routes as IOCTL_HID_WRITE_REPORT. For Bluetooth/BLE, the BT minidriver maps this to a GATT Write Without Response on the Output Report characteristic — an unacknowledged transport (ATT opcode 0x52). The Bluetooth HID Service 1.0 spec lists Write Without Response as optional on this characteristic, and some adapters/firmwares either don't implement it correctly or never declared it. Because the path is unacknowledged, the failure is silent: the IOCTL returns success while the report is dropped.
Reported affected combinations include Realtek RTL8761B (TP-Link UB400/UB500) and Intel AX-series adapters, and devices such as Sony DualShock 4 (BT) and Google Stadia controller (BT mode).
Workaround. Use hid_send_output_report() (added in v0.15.0). On Windows it calls HidD_SetOutputReport -> IOCTL_HID_SET_OUTPUT_REPORT -> GATT Write Characteristic Value (acknowledged, ATT opcode 0x12), which is mandatory in the BT HID spec and works on the affected devices.
This is intentionally a device-specific workaround rather than a default change inside hid_write. The two functions are not interchangeable: some devices only accept the interrupt-OUT / Write-Without-Response path and will not work via HidD_SetOutputReport, so HIDAPI cannot safely flip the default. Choose the path based on knowledge of your target device (typically VID/PID and transport).
Transport mapping for reference:
| HIDAPI call | Win32 API | IOCTL | USB | Bluetooth (BLE) |
|---|---|---|---|---|
hid_write |
WriteFile |
IOCTL_HID_WRITE_REPORT |
Interrupt OUT (or Set_Report Output if no Interrupt OUT) | GATT Write Without Response (0x52) |
hid_send_output_report |
HidD_SetOutputReport |
IOCTL_HID_SET_OUTPUT_REPORT |
Set_Report (Output) on Control pipe | GATT Write Characteristic Value (0x12) |
References:
- #513 — original analysis and discussion
- signal11/hidapi#378 — earlier report (DS4 LED/rumble over BT)
Windows requires the buffer passed to WriteFile (the kernel-side primitive used by hid_write) to be at least as large as the longest output report. HIDAPI caches that value from WinAPI. If a caller passes a shorter buffer, HIDAPI transparently copies it into an internal buffer of the proper size and zero-pads the remainder before calling WriteFile, so callers do not need to pad themselves.
Two consequences worth knowing:
-
The byte count returned by
hid_writeon success is the value ofbytes_writtenfromWriteFile/GetOverlappedResult— i.e. the size that was submitted to the kernel. When HIDAPI did the padding, that isoutput_report_length, not the originallengthpassed in by the caller. -
Neither value reflects device-side delivery: WinAPI does not expose how many bytes the device actually accepted. On the BT/BLE interrupt-OUT path the report can even be silently dropped (see the previous section); use
hid_send_output_reportif a delivery acknowledgement is needed.
See discussion #255 for background.