@@ -108,6 +108,8 @@ TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr) {
108108
109109// forward declaration
110110static void __tusb_irq_path_func (edpt_schedule_next )(void );
111+ TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer (uint32_t value );
112+ static void edpt_xfer (hw_endpoint_t * ep , uint8_t * buffer , tu_fifo_t * ff , uint16_t total_len );
111113
112114static void __tusb_irq_path_func (hw_xfer_complete )(hw_endpoint_t * ep , xfer_result_t xfer_result ) {
113115 // Mark transfer as done before we tell the tinyusb stack
@@ -222,29 +224,79 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) {
222224 }
223225 }
224226
225- // if (status & USB_INTS_EP_STALL_NAK_BITS) {
226- // const uint32_t ep_stall_nak = usb_hw->ep_status_stall_nak;
227- // usb_hw->ep_status_stall_nak = ep_stall_nak; // clear by writing back (WC)
228- //
229- // // Preempt non-control EPX bulk transfer when a different ep is pending
230- // if (epx->active && tu_edpt_number(epx->ep_addr) != 0) {
231- // for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) {
232- // hw_endpoint_t *ep = &ep_pool[i];
233- // if (ep->pending && ep != epx) {
234- // // Stop current transaction
235- // usb_hw_set->sie_ctrl = USB_SIE_CTRL_STOP_TRANS_BITS;
236- //
237- // // Mark current EPX as pending to resume later
238- // epx->pending = 1;
239- // epx->active = false;
240- //
241- // // Start the next pending transfer
242- // edpt_schedule_next();
243- // break;
244- // }
245- // }
246- // }
247- // }
227+ #if defined(PICO_RP2350 ) && PICO_RP2350 == 1
228+ if (status & USB_INTS_EPX_STOPPED_ON_NAK_BITS ) {
229+ // RP2350: EPX transfer stopped due to NAK from device.
230+ // Clear EPX_STOPPED_ON_NAK status (WC)
231+ usb_hw -> nak_poll |= USB_NAK_POLL_EPX_STOPPED_ON_NAK_BITS ;
232+
233+ bool preempted = false;
234+
235+ // Only preempt non-control endpoints
236+ if (epx -> active && tu_edpt_number (epx -> ep_addr ) != 0 ) {
237+ // Find the next pending transfer (different from the current epx)
238+ for (uint i = 0 ; i < TU_ARRAY_SIZE (ep_pool ); i ++ ) {
239+ hw_endpoint_t * ep = & ep_pool [i ];
240+ if (ep -> pending && ep != epx ) {
241+ // NAK means no data transferred. Restore remaining_len from buffer control
242+ // so edpt_schedule_next can properly resume this transfer later.
243+ const uint16_t buf0_len = usbh_dpram -> epx_buf_ctrl & USB_BUF_CTRL_LEN_MASK ;
244+ epx -> remaining_len = (uint16_t )(epx -> remaining_len + buf0_len );
245+ epx -> next_pid ^= 1u ; // undo PID toggle from hwbuf_prepare
246+ if (tu_edpt_dir (epx -> ep_addr ) == TUSB_DIR_OUT ) {
247+ epx -> user_buf -= buf0_len ; // undo buffer advance for OUT
248+ }
249+
250+ // Mark current EPX as pending to resume later
251+ epx -> pending = 1 ;
252+ epx -> active = false;
253+
254+ // Clear EPX buffer control - AVAILABLE is still set from the NAK'd transfer
255+ usbh_dpram -> epx_buf_ctrl = 0 ;
256+
257+ // Start the found pending transfer directly
258+ if (ep -> pending == 2 ) {
259+ // Pending setup: DPRAM already has the setup packet
260+ ep -> ep_addr = 0 ;
261+ ep -> remaining_len = 8 ;
262+ ep -> xferred_len = 0 ;
263+ ep -> active = true;
264+ ep -> pending = 0 ;
265+
266+ epx = ep ;
267+ usb_hw -> dev_addr_ctrl = ep -> dev_addr ;
268+
269+ const uint32_t sc = USB_SIE_CTRL_SEND_SETUP_BITS |
270+ (ep -> need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0 );
271+ sie_start_xfer (sc );
272+ } else {
273+ // Pending data transfer: preserve partial progress
274+ uint16_t prev_xferred = ep -> xferred_len ;
275+ ep -> pending = 0 ;
276+ edpt_xfer (ep , ep -> user_buf , NULL , ep -> remaining_len );
277+ epx -> xferred_len += prev_xferred ;
278+ }
279+
280+ preempted = true;
281+ break ;
282+ }
283+ }
284+ }
285+
286+ if (!preempted && epx -> active ) {
287+ // No preemption needed: disable stop-on-NAK and restart the transaction.
288+ // Buffer control still has AVAILABLE set, just re-trigger START_TRANS.
289+ uint32_t nak_poll = usb_hw -> nak_poll ;
290+ nak_poll &= ~USB_NAK_POLL_STOP_EPX_ON_NAK_BITS ;
291+ usb_hw -> nak_poll = nak_poll ;
292+
293+ const tusb_dir_t ep_dir = tu_edpt_dir (epx -> ep_addr );
294+ const uint32_t sie_ctrl = (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS ) |
295+ (epx -> need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0 );
296+ sie_start_xfer (sie_ctrl );
297+ }
298+ }
299+ #endif
248300
249301 if (status & USB_INTS_ERROR_RX_TIMEOUT_BITS ) {
250302 usb_hw_clear -> sie_status = USB_SIE_STATUS_RX_TIMEOUT_BITS ;
@@ -344,10 +396,14 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
344396 USB_INTE_HOST_RESUME_BITS |
345397 USB_INTE_STALL_BITS |
346398 USB_INTE_TRANS_COMPLETE_BITS |
347- // USB_INTE_EP_STALL_NAK_BITS |
348399 USB_INTE_ERROR_RX_TIMEOUT_BITS |
349400 USB_INTE_ERROR_DATA_SEQ_BITS ;
350401
402+ #if defined(PICO_RP2350 ) && PICO_RP2350 == 1
403+ // RP2350: Enable EPX stopped-on-NAK interrupt (feature is enabled dynamically when transfers are pending)
404+ usb_hw_set -> inte = USB_INTE_EPX_STOPPED_ON_NAK_BITS ;
405+ #endif
406+
351407 return true;
352408}
353409
@@ -557,6 +613,10 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b
557613 ep -> user_buf = buffer ;
558614 ep -> remaining_len = buflen ;
559615 ep -> pending = 1 ;
616+ #if defined(PICO_RP2350 ) && PICO_RP2350 == 1
617+ // RP2350: Enable stop-on-NAK so current EPX transfer can be preempted
618+ usb_hw_set -> nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS ;
619+ #endif
560620 return true;
561621 }
562622
@@ -589,6 +649,10 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet
589649 // If EPX is busy, mark as pending setup (DPRAM already has the packet)
590650 if (epx -> active ) {
591651 ep -> pending = 2 ;
652+ #if defined(PICO_RP2350 ) && PICO_RP2350 == 1
653+ // RP2350: Enable stop-on-NAK so current EPX transfer can be preempted
654+ usb_hw_set -> nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS ;
655+ #endif
592656 return true;
593657 }
594658
0 commit comments