summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-08-17 01:01:37 +0700
committerhathach <[email protected]>2026-08-18 20:51:21 +0700
commit073942589355676980ba401cb88c0eb9f065e468 (patch)
treea6447b4ee77f12a8127ea45d78fbfdc09c9e0337 /src
parentaf7d199e73461c25555abbdc72441ab79eb74245 (diff)
usbd: split bus reset into start/end edge events
A driver that can see reset signalling begin has no way to say so: the only event carries the negotiated speed, which does not exist until the reset ends. On ChipIdea that left the stack believing it was still configured for the whole reset window - 3 ms at minimum, tens of milliseconds in practice - while the controller had already torn its endpoints down, so a class driver writing in that window primed a disabled endpoint over a zeroed queue head. Add DCD_EVENT_BUS_RESET_START for the leading edge and rename the existing event to DCD_EVENT_BUS_RESET_END, keeping DCD_EVENT_BUS_RESET as an alias. START is optional and END stays self-sufficient, so every other driver and the unit tests are untouched.
Diffstat (limited to 'src')
-rw-r--r--src/device/dcd.h26
-rw-r--r--src/device/usbd.c12
2 files changed, 27 insertions, 11 deletions
diff --git a/src/device/dcd.h b/src/device/dcd.h
index f005e9620..a4006ae0c 100644
--- a/src/device/dcd.h
+++ b/src/device/dcd.h
@@ -20,19 +20,27 @@
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
+// Bus reset is reported as two edges. BUS_RESET_START is optional: a controller that
+// cannot tell the edges apart emits only BUS_RESET_END, which stays self-sufficient (it
+// performs the full teardown with or without a preceding START). Emit START when reset
+// signaling is detected - the link is unusable and the speed is not negotiated yet - so
+// the stack stops using endpoints immediately instead of at the end of the reset.
typedef enum {
- DCD_EVENT_INVALID = 0, // 0
- DCD_EVENT_BUS_RESET, // 1
- DCD_EVENT_UNPLUGGED, // 2
- DCD_EVENT_SOF, // 3
- DCD_EVENT_SUSPEND, // 4 TODO LPM Sleep L1 support
- DCD_EVENT_RESUME, // 5
- DCD_EVENT_SETUP_RECEIVED, // 6
- DCD_EVENT_XFER_COMPLETE, // 7
- USBD_EVENT_FUNC_CALL, // 8 Not an DCD event, just a convenient way to defer ISR function
+ DCD_EVENT_INVALID = 0, // 0
+ DCD_EVENT_BUS_RESET_START, // 1
+ DCD_EVENT_BUS_RESET_END, // 2 with negotiated speed
+ DCD_EVENT_UNPLUGGED, // 3
+ DCD_EVENT_SOF, // 4
+ DCD_EVENT_SUSPEND, // 5 TODO LPM Sleep L1 support
+ DCD_EVENT_RESUME, // 6
+ DCD_EVENT_SETUP_RECEIVED, // 7
+ DCD_EVENT_XFER_COMPLETE, // 8
+ USBD_EVENT_FUNC_CALL, // 9 Not an DCD event, just a convenient way to defer ISR function
DCD_EVENT_COUNT
} dcd_eventid_t;
+#define DCD_EVENT_BUS_RESET DCD_EVENT_BUS_RESET_END // backward compatibility
+
typedef struct TU_ATTR_ALIGNED(4) {
uint8_t rhport;
uint8_t event_id;
diff --git a/src/device/usbd.c b/src/device/usbd.c
index f5c3046d6..7215a8dc5 100644
--- a/src/device/usbd.c
+++ b/src/device/usbd.c
@@ -456,7 +456,8 @@ TU_ATTR_WEAK bool dcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg
#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
static char const *const _usbd_event_str[DCD_EVENT_COUNT] = {
"Invalid",
- "Bus Reset",
+ "Bus Reset Start",
+ "Bus Reset End",
"Unplugged",
"SOF",
"Suspend",
@@ -697,8 +698,15 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
#endif
switch (event.event_id) {
- case DCD_EVENT_BUS_RESET:
+ case DCD_EVENT_BUS_RESET_START:
+ TU_LOG_USBD("\r\n");
+ usbd_reset(event.rhport);
+ break;
+
+ case DCD_EVENT_BUS_RESET_END:
TU_LOG_USBD(": %s Speed\r\n", tu_str_speed[event.bus_reset.speed]);
+ // TODO a DCD that reports both edges pays for two teardowns: track a per-rhport
+ // "start seen" flag and skip this reset, keeping it for the single-event DCDs.
usbd_reset(event.rhport);
_usbd_dev.speed = event.bus_reset.speed;
break;