Skip to content

Commit 4209208

Browse files
fix CDC umount failure
1 parent 0459bb3 commit 4209208

File tree

1 file changed

+80
-47
lines changed

1 file changed

+80
-47
lines changed

src/portable/raspberrypi/rp2040/hcd_rp2040.c

Lines changed: 80 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ static hw_endpoint_t *epx = &ep_pool[0]; // current active endpoint
6969
// different EP0 sizes (e.g. low-speed MPS=8 vs full-speed MPS=64).
7070
static uint8_t _ep0_mps[CFG_TUH_DEVICE_MAX + CFG_TUH_HUB + 1]; // +1 for addr0
7171

72+
static bool epx_post_error = false;
73+
7274
#ifndef HAS_STOP_EPX_ON_NAK
7375
static volatile bool epx_switch_request = false;
7476
#endif
@@ -197,33 +199,26 @@ TU_ATTR_ALWAYS_INLINE static inline void epx_ctrl_prepare(uint8_t transfer_type)
197199
// IN completed: FULL set to 1 in STATUS phase (was 0 when armed)
198200
// So undo when: AVAIL=1 (never started), or (OUT: FULL=1) or (IN: FULL=0)
199201
static void __tusb_irq_path_func(epx_save_context)(hw_endpoint_t *ep) {
200-
uint32_t buf_ctrl = usbh_dpram->epx_buf_ctrl;
201-
const bool is_out = (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_OUT);
202-
203-
do {
204-
const uint16_t bc16 = (uint16_t)buf_ctrl;
205-
if (bc16) {
206-
const bool avail = (bc16 & USB_BUF_CTRL_AVAIL);
207-
const bool full = (bc16 & USB_BUF_CTRL_FULL);
208-
if (avail || (is_out ? full : !full)) {
209-
const uint16_t buf_len = bc16 & USB_BUF_CTRL_LEN_MASK;
210-
ep->remaining_len += buf_len;
211-
ep->next_pid ^= 1u;
212-
if (is_out) {
213-
ep->user_buf -= buf_len;
214-
}
202+
const uint32_t buf_ctrl = usbh_dpram->epx_buf_ctrl;
203+
const bool is_out = (tu_edpt_dir(ep->ep_addr) == TUSB_DIR_OUT);
204+
const bool is_double = (usbh_dpram->epx_ctrl & EP_CTRL_DOUBLE_BUFFERED_BITS);
205+
206+
for (uint b = 0; b <= (is_double ? 1u : 0u); b++) {
207+
const uint16_t bc16 = (uint16_t)(buf_ctrl >> (b * 16u));
208+
if (!bc16) continue;
209+
const bool avail = (bc16 & USB_BUF_CTRL_AVAIL);
210+
const bool full = (bc16 & USB_BUF_CTRL_FULL);
211+
if (avail || (is_out ? full : !full)) {
212+
const uint16_t buf_len = bc16 & USB_BUF_CTRL_LEN_MASK;
213+
ep->remaining_len += buf_len;
214+
ep->next_pid ^= 1u;
215+
if (is_out) {
216+
ep->user_buf -= buf_len;
215217
}
216218
}
217-
218-
if (usbh_dpram->epx_ctrl & EP_CTRL_DOUBLE_BUFFERED_BITS) {
219-
buf_ctrl >>= 16;
220-
} else {
221-
buf_ctrl = 0;
222-
}
223-
} while (buf_ctrl > 0);
219+
}
224220

225221
usbh_dpram->epx_buf_ctrl = 0;
226-
227222
ep->state = EPSTATE_PENDING;
228223
}
229224

@@ -359,11 +354,12 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) {
359354
usb_hw->int_ep_ctrl = 0;
360355
usb_hw_clear->buf_status = 0xffffffffu;
361356
usbh_dpram->epx_buf_ctrl = 0;
357+
epx_post_error = false;
362358
#ifndef HAS_STOP_EPX_ON_NAK
363359
epx_switch_request = false;
364360
#endif
365361
for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) {
366-
if (ep_pool[i].state == EPSTATE_ACTIVE) rp2usb_reset_transfer(&ep_pool[i]);
362+
if (ep_pool[i].state != EPSTATE_IDLE) rp2usb_reset_transfer(&ep_pool[i]);
367363
}
368364
hcd_event_device_remove(RHPORT_NATIVE, true);
369365
} else {
@@ -405,8 +401,14 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) {
405401

406402
if (epx->state == EPSTATE_ACTIVE) {
407403
xfer_complete_isr(epx, XFER_RESULT_FAILED, false);
408-
} else {
409-
// Spurious timeout (EPX not active) — nothing to complete.
404+
}
405+
406+
// After STOP_TRANS following an error the SIE needs time to settle before
407+
// the next START_TRANS. Defer dispatch to the next SOF (~1ms) instead of
408+
// dispatching immediately, which can silently lose the new transfer.
409+
if (epx->state != EPSTATE_ACTIVE && epx_next_pending(epx) != NULL) {
410+
epx_post_error = true;
411+
usb_hw_set->inte = USB_INTE_HOST_SOF_BITS;
410412
}
411413
}
412414

@@ -447,14 +449,17 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) {
447449
if (epx->state == EPSTATE_ACTIVE) {
448450
xfer_complete_isr(epx, XFER_RESULT_FAILED, false);
449451
}
452+
// Defer to next SOF — same SIE settling issue as RX_TIMEOUT.
453+
if (epx->state != EPSTATE_ACTIVE && epx_next_pending(epx) != NULL) {
454+
epx_post_error = true;
455+
usb_hw_set->inte = USB_INTE_HOST_SOF_BITS;
456+
}
450457
}
451458

452459
if (status & USB_INTS_BUFF_STATUS_BITS) {
453460
handle_buf_status_isr();
454461
}
455462

456-
// SOF-based round-robin MUST run BEFORE BUFF_STATUS to avoid processing
457-
// buf_status on the wrong EPX after a completion+switch in handle_buf_status_isr.
458463
#ifdef HAS_STOP_EPX_ON_NAK
459464
if (status & USB_INTS_EPX_STOPPED_ON_NAK_BITS) {
460465
usb_hw_clear->nak_poll = USB_NAK_POLL_EPX_STOPPED_ON_NAK_BITS;
@@ -467,6 +472,25 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) {
467472
sie_start_xfer(false, TUSB_DIR_IN == tu_edpt_dir(epx->ep_addr), epx->need_pre);
468473
}
469474
}
475+
476+
// Post-error SOF deferral: after STOP_TRANS in error handlers, wait one SOF
477+
// for the SIE to settle before dispatching the next pending transfer.
478+
if (status & USB_INTS_HOST_SOF_BITS) {
479+
(void)usb_hw->sof_rd;
480+
if (epx_post_error) {
481+
// This SOF may be in the same IRQ snapshot as the error. Skip it;
482+
// the next SOF (~1ms) will dispatch.
483+
epx_post_error = false;
484+
} else if (epx->state != EPSTATE_ACTIVE) {
485+
hw_endpoint_t *next_ep = epx_next_pending(epx);
486+
if (next_ep != NULL) {
487+
epx_switch_ep(next_ep);
488+
}
489+
usb_hw_clear->inte = USB_INTE_HOST_SOF_BITS;
490+
} else {
491+
usb_hw_clear->inte = USB_INTE_HOST_SOF_BITS;
492+
}
493+
}
470494
#else
471495
// RP2040: on SOF, switch EPX if another endpoint is pending.
472496
// First SOF sets epx_switch_request. If a transfer completes before next SOF, the flag is
@@ -475,25 +499,31 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) {
475499
// This avoids stopping mid-data-transfer which corrupts double-buffered PID tracking.
476500
if (status & USB_INTS_HOST_SOF_BITS) {
477501
(void)usb_hw->sof_rd; // clear SOF by reading SOF_RD
478-
hw_endpoint_t *next_ep = epx_next_pending(epx);
479-
if (next_ep == NULL) {
480-
usb_hw_clear->inte = USB_INTE_HOST_SOF_BITS;
481-
usb_hw->nak_poll = USB_NAK_POLL_RESET;
482-
epx_switch_request = false;
483-
} else if (epx->state != EPSTATE_ACTIVE) {
484-
// EPX is idle with pending transfers (e.g. after RX_TIMEOUT).
485-
// Start the next pending transfer directly.
486-
epx_switch_request = false;
487-
epx_switch_ep(next_ep);
488-
} else if (epx->state == EPSTATE_ACTIVE) {
489-
if (epx_switch_request) {
490-
// Second SOF with no transfer completion: endpoint is NAK-retrying, safe to switch.
502+
if (epx_post_error) {
503+
// Skip this SOF — may be in the same IRQ snapshot as the error.
504+
// Next SOF (~1ms) will dispatch via the idle-EPX path below.
505+
epx_post_error = false;
506+
} else {
507+
hw_endpoint_t *next_ep = epx_next_pending(epx);
508+
if (next_ep == NULL) {
509+
usb_hw_clear->inte = USB_INTE_HOST_SOF_BITS;
510+
usb_hw->nak_poll = USB_NAK_POLL_RESET;
511+
epx_switch_request = false;
512+
} else if (epx->state != EPSTATE_ACTIVE) {
513+
// EPX is idle with pending transfers (e.g. after RX_TIMEOUT).
514+
// Start the next pending transfer directly.
491515
epx_switch_request = false;
492-
sie_stop_xfer();
493-
epx_save_context(epx);
494516
epx_switch_ep(next_ep);
495-
} else {
496-
epx_switch_request = true;
517+
} else if (epx->state == EPSTATE_ACTIVE) {
518+
if (epx_switch_request) {
519+
// Second SOF with no transfer completion: endpoint is NAK-retrying, safe to switch.
520+
epx_switch_request = false;
521+
sie_stop_xfer();
522+
epx_save_context(epx);
523+
epx_switch_ep(next_ep);
524+
} else {
525+
epx_switch_request = true;
526+
}
497527
}
498528
}
499529
}
@@ -527,6 +557,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) {
527557

528558
// clear epx and interrupt eps
529559
memset(&ep_pool, 0, sizeof(ep_pool));
560+
epx_post_error = false;
530561

531562
// Enable in host mode with SOF / Keep alive on
532563
usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS | USB_MAIN_CTRL_HOST_NDEVICE_BITS;
@@ -865,7 +896,7 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet
865896

866897
usbh_dpram->epx_buf_ctrl = 0; // clear stale buf_ctrl from previous phase
867898
usb_hw->dev_addr_ctrl = ep->dev_addr;
868-
sie_start_xfer(true, tu_edpt_dir(ep->ep_addr) == TUSB_DIR_IN, ep->need_pre);
899+
sie_start_xfer(true, false, ep->need_pre);
869900
}
870901

871902
rp2usb_critical_exit();
@@ -881,7 +912,9 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
881912
if (ep->interrupt_num) {
882913
*dpram_int_ep_buffer_ctrl(ep->interrupt_num) = 0;
883914
} else {
884-
usbh_dpram->epx_buf_ctrl = 0;
915+
if (epx == ep) {
916+
usbh_dpram->epx_buf_ctrl = 0;
917+
}
885918
}
886919
rp2usb_critical_exit();
887920
}

0 commit comments

Comments
 (0)