@@ -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).
7070static 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
7375static 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)
199201static 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