HID output report race condition
shuffle2 opened this issue · 1 comments
Operating System
Others
Board
raspberry pi pico W
Firmware
using pico-sdk 1.5.1
What happened ?
I'm writing firmware for the pico which talks to pre-existing software on a usb host (read: I cannot change or easily inspect the host software/stack).
The host regularly sends HID output reports to the pico. At the same time, it also sends SET_ and GET_ FEATURE_REPORT reports. What I observe is that occasionally while I am processing a SET_FEATURE, the underlying buffer gets overwritten by one of the output reports the host is periodically sending. (And so, the report_id no longer corresponds to the buffer contents).
I note that the callers of tud_hid_set_report_cb
use _hidd_itf[0]->epout_buf
in both cases (output reports and HID_REQ_CONTROL_SET_REPORT
). Am I missing something here (like required locking, time bound requirements, etc)? Is there a way to use a different buffer for each of output reports and SET_REPORTs ?
I've implemented a simple patch which, amazingly, seems to fix the problem I'm encountering:
diff --git a/src/class/hid/hid_device.c b/src/class/hid/hid_device.c
index 2d46d760f..c939c0117 100644
--- a/src/class/hid/hid_device.c
+++ b/src/class/hid/hid_device.c
@@ -52,6 +52,7 @@ typedef struct
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_EP_BUFSIZE];
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_EP_BUFSIZE];
+ CFG_TUSB_MEM_ALIGN uint8_t set_report_buf[CFG_TUD_HID_EP_BUFSIZE];
// TODO save hid descriptor since host can specifically request this after enumeration
// Note: HID descriptor may be not available from application after enumeration
@@ -307,15 +308,15 @@ bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t
case HID_REQ_CONTROL_SET_REPORT:
if ( stage == CONTROL_STAGE_SETUP )
{
- TU_VERIFY(request->wLength <= sizeof(p_hid->epout_buf));
- tud_control_xfer(rhport, request, p_hid->epout_buf, request->wLength);
+ TU_VERIFY(request->wLength <= sizeof(p_hid->set_report_buf));
+ tud_control_xfer(rhport, request, p_hid->set_report_buf, request->wLength);
}
else if ( stage == CONTROL_STAGE_ACK )
{
uint8_t const report_type = tu_u16_high(request->wValue);
uint8_t const report_id = tu_u16_low(request->wValue);
- uint8_t const* report_buf = p_hid->epout_buf;
+ uint8_t const* report_buf = p_hid->set_report_buf;
uint16_t report_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE);
// If host request a specific Report ID, extract report ID in buffer before invoking callback
I'm not sure if this is a good solution. Any comments / suggestions?
Should a similar thing be done for HID_REQ_CONTROL_GET_REPORT
?
How to reproduce ?
I unfortunately don't have good repro steps.
Debug Log as txt file (LOG/CFG_TUSB_DEBUG=2)
Example of the set_report, sandwiched between two output reports. It appears the second output report has overwritten contents of the buffer between first and second time it is parsed (the SET_FEATURE_REPORT_F0(8d, d7, 0): crc mismatch
line prints out the first 3 bytes of the buffer, which are not the expected values of `01, 01, 001)
USBD Setup Received 21 09 F0 03 00 00 40 00
HID control request
Queue EP 00 with 64 bytes ...
USBD Xfer Complete on EP 03 with 48 bytes
HID xfer callback
output report
02 8d d7 00 00 00 00 00 00 00 80 05 00 00 00 00
00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 04 03 00 00 01 00 00 00 00 ff
Queue EP 03 with 64 bytes ...
USBD Xfer Complete on EP 00 with 64 bytes
0000: F0 01 01 00 01 00 00 00 F7 14 F9 0D 7F F4 96 54 |...............T|
0010: D0 38 21 28 22 6B 13 A8 10 00 01 00 00 00 00 00 |.8!("k..........|
0020: 00 00 00 00 00 00 00 00 30 30 30 31 30 30 30 31 |........00010001|
0030: 30 30 30 30 30 30 30 30 BB 3D 50 84 F2 3C 04 59 |00000000.=P..<.Y|
HID control complete
Queue EP 80 with 0 bytes ...
Queue EP 84 with 64 bytes ...
USBD Xfer Complete on EP 84 with 64 bytes
HID xfer callback
USBD Xfer Complete on EP 80 with 0 bytes
set_feature_report(f0, 20002EF9, 3f)
01 01 00 01 00 00 00 f7 14 f9 0d 7f f4 96 54 d0
38 21 28 22 6b 13 a8 10 00 01 00 00 00 00 00 00
00 00 00 00 00 04 03 00 00 01 00 00 00 00 ff 30
30 30 30 30 30 30 30 bb 3d 50 84 f2 3c 04 59
SET_FEATURE_REPORT_F0(8d, d7, 0): crc mismatch
USBD Xfer Complete on EP 03 with 48 bytes
HID xfer callback
output report
02 8d d7 00 00 00 00 00 00 00 80 05 00 00 00 00
00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 04 03 00 00 01 00 00 00 00 ff
Queue EP 03 with 64 bytes ...
Queue EP 84 with 64 bytes ...
USBD Xfer Complete on EP 84 with 64 bytes
HID xfer callback
Screenshots
No response
I have checked existing issues, dicussion and documentation
- I confirm I have checked existing issues, dicussion and documentation.
Thanks @shuffle2, it was the solution in my case also.