summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-03-27 19:01:57 +0700
committerhathach <[email protected]>2026-03-27 19:01:57 +0700
commita9eb36b0fb5ad5322cb711954c98bf172faa1e06 (patch)
tree370b593fb1277e9dee72093b8689122c3cb6df6b
parentd9d28b7e94aac295e08db5fff8649e1467a37399 (diff)
refactor
-rw-r--r--src/portable/raspberrypi/rp2040/hcd_rp2040.c266
1 files changed, 133 insertions, 133 deletions
diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c
index 379ba075d..8145cfdcc 100644
--- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c
@@ -116,12 +116,119 @@ TU_ATTR_ALWAYS_INLINE static inline bool need_pre(uint8_t dev_addr) {
return hcd_port_speed_get(0) != tuh_speed_get(dev_addr);
}
-// forward declaration
-static void __tusb_irq_path_func(edpt_schedule_next)(void);
-TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(uint32_t value);
+//--------------------------------------------------------------------+
+// EPX
+//--------------------------------------------------------------------+
+
+static void __tusb_irq_path_func(epx_schedule_next)(void);
static void epx_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len);
-static void __tusb_irq_path_func(hw_xfer_complete)(hw_endpoint_t *ep, xfer_result_t xfer_result) {
+TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(bool send_setup, tusb_dir_t ep_dir, bool need_pre) {
+ uint32_t value = usb_hw->sie_ctrl & SIE_CTRL_BASE_MASK; // preserve base bits
+ if (send_setup) {
+ value |= USB_SIE_CTRL_SEND_SETUP_BITS;
+ } else {
+ value |= (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS);
+ }
+ if (need_pre) {
+ value |= USB_SIE_CTRL_PREAMBLE_EN_BITS;
+ }
+
+ // START_TRANS bit on SIE_CTRL has the same behavior as the AVAILABLE bit
+ // described in RP2040 Datasheet, release 2.1, section "4.1.2.5.1. Concurrent access".
+ // We write everything except the START_TRANS bit first, then wait some cycles.
+ usb_hw->sie_ctrl = value;
+ busy_wait_at_least_cycles(12);
+ usb_hw->sie_ctrl = value | USB_SIE_CTRL_START_TRANS_BITS;
+}
+
+// All non-interrupt endpoints use shared EPX.
+// Save current EPX context, mark pending, switch to next_ep
+static void __tusb_irq_path_func(epx_switch_ep)(hw_endpoint_t *next_ep) {
+ const uint32_t buf_ctrl = usbh_dpram->epx_buf_ctrl;
+ const uint16_t buf0_len = buf_ctrl & USB_BUF_CTRL_LEN_MASK;
+ epx->remaining_len = (uint16_t)(epx->remaining_len + buf0_len);
+ epx->next_pid = (buf_ctrl & USB_BUF_CTRL_DATA1_PID) ? 1 : 0;
+ if (tu_edpt_dir(epx->ep_addr) == TUSB_DIR_OUT) {
+ epx->user_buf -= buf0_len;
+ }
+ epx->pending = 1;
+ epx->active = false;
+ usbh_dpram->epx_buf_ctrl = 0;
+
+ if (next_ep->pending == 2) {
+ next_ep->ep_addr = 0;
+ next_ep->remaining_len = 8;
+ next_ep->xferred_len = 0;
+ next_ep->active = true;
+ next_ep->pending = 0;
+ epx = next_ep;
+ usb_hw->dev_addr_ctrl = next_ep->dev_addr;
+ sie_start_xfer(true, TUSB_DIR_OUT, next_ep->need_pre);
+ } else {
+ uint16_t prev_xferred = next_ep->xferred_len;
+ next_ep->pending = 0;
+ epx_xfer(next_ep, next_ep->user_buf, NULL, next_ep->remaining_len);
+ epx->xferred_len += prev_xferred;
+ }
+}
+
+// Round-robin find next pending ep after current epx
+static hw_endpoint_t *__tusb_irq_path_func(epx_next_pending)(hw_endpoint_t *cur_ep) {
+ const uint cur_idx = (uint)(cur_ep - &ep_pool[0]);
+ for (uint i = cur_idx + 1; i < TU_ARRAY_SIZE(ep_pool); i++) {
+ if (ep_pool[i].pending) {
+ return &ep_pool[i];
+ }
+ }
+ for (uint i = 0; i < cur_idx; i++) {
+ if (ep_pool[i].pending) {
+ return &ep_pool[i];
+ }
+ }
+ return NULL;
+}
+
+// Schedule next pending EPX transfer from ISR context
+static void __tusb_irq_path_func(epx_schedule_next)(void) {
+ // EPX may already be active if the completion callback started a new transfer
+ // if (epx->active) {
+ // return;
+ // }
+
+ for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) {
+ hw_endpoint_t *ep = &ep_pool[i];
+ if (ep->pending == 0) {
+ continue;
+ }
+
+ if (ep->pending == 2) {
+ // Pending setup: DPRAM already has the setup packet
+ ep->ep_addr = 0;
+ ep->remaining_len = 8;
+ ep->xferred_len = 0;
+ ep->active = true;
+ ep->pending = 0;
+
+ epx = ep;
+ usb_hw->dev_addr_ctrl = ep->dev_addr;
+
+ sie_start_xfer(true, TUSB_DIR_OUT, ep->need_pre);
+ } else {
+ // Pending data transfer: preserve partial progress from preemption
+ uint16_t prev_xferred = ep->xferred_len;
+ ep->pending = 0;
+ epx_xfer(ep, ep->user_buf, NULL, ep->remaining_len);
+ epx->xferred_len += prev_xferred; // restore partial progress
+ }
+ return; // start only one transfer
+ }
+}
+
+//--------------------------------------------------------------------+
+// Interrupt handlers
+//--------------------------------------------------------------------+
+static void __tusb_irq_path_func(xfer_complete_isr)(hw_endpoint_t *ep, xfer_result_t xfer_result) {
// Mark transfer as done before we tell the tinyusb stack
uint8_t dev_addr = ep->dev_addr;
uint8_t ep_addr = ep->ep_addr;
@@ -131,17 +238,21 @@ static void __tusb_irq_path_func(hw_xfer_complete)(hw_endpoint_t *ep, xfer_resul
// Schedule next pending EPX transfer (only for non-interrupt endpoints)
if (ep == epx) {
- edpt_schedule_next();
+ epx_schedule_next();
+ // hw_endpoint_t *next_ep = epx_next_pending(epx);
+ // if (next_ep != NULL) {
+ // epx_switch_ep(next_ep);
+ // }
}
}
-static void __tusb_irq_path_func(handle_hwbuf_status)(void) {
+static void __tusb_irq_path_func(handle_buf_status_isr)(void) {
pico_trace("buf_status 0x%08lx\n", buf_status);
enum {
BUF_STATUS_EPX = 1u
};
- // Check EPX first (bit 0). EPX is currently single-buffered, always use buf_id=0.
+ // Check EPX first (bit 0). EPX is currently single-buffered, always use buf_id=0.3
// Double-buffered: if both buffers completed at once, buf_status re-sets
// immediately after clearing (datasheet Table 406). Process the second buffer too.
while (usb_hw->buf_status & BUF_STATUS_EPX) {
@@ -151,7 +262,7 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) {
io_rw_32 *ep_reg = &usbh_dpram->epx_ctrl;
io_rw_32 *buf_reg = &usbh_dpram->epx_buf_ctrl;
if (rp2usb_xfer_continue(epx, ep_reg, buf_reg, buf_id)) {
- hw_xfer_complete(epx, XFER_RESULT_SUCCESS);
+ xfer_complete_isr(epx, XFER_RESULT_SUCCESS);
}
}
@@ -176,7 +287,7 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) {
io_rw_32 *buf_reg = dpram_int_ep_buffer_ctrl(ep->interrupt_num);
const bool done = rp2usb_xfer_continue(ep, ep_reg, buf_reg, 0);
if (done) {
- hw_xfer_complete(ep, XFER_RESULT_SUCCESS);
+ xfer_complete_isr(ep, XFER_RESULT_SUCCESS);
}
break;
}
@@ -184,56 +295,6 @@ static void __tusb_irq_path_func(handle_hwbuf_status)(void) {
}
}
-// All non-interrupt endpoints use shared EPX.
-// Forward declared above hw_xfer_complete, defined after epx_xfer below.
-// Save current EPX context, mark pending, switch to next_ep
-static void __tusb_irq_path_func(epx_switch_ep)(hw_endpoint_t *next_ep) {
- const uint32_t buf_ctrl = usbh_dpram->epx_buf_ctrl;
- const uint16_t buf0_len = buf_ctrl & USB_BUF_CTRL_LEN_MASK;
- epx->remaining_len = (uint16_t)(epx->remaining_len + buf0_len);
- epx->next_pid = (buf_ctrl & USB_BUF_CTRL_DATA1_PID) ? 1 : 0;
- if (tu_edpt_dir(epx->ep_addr) == TUSB_DIR_OUT) {
- epx->user_buf -= buf0_len;
- }
- epx->pending = 1;
- epx->active = false;
- usbh_dpram->epx_buf_ctrl = 0;
-
- if (next_ep->pending == 2) {
- next_ep->ep_addr = 0;
- next_ep->remaining_len = 8;
- next_ep->xferred_len = 0;
- next_ep->active = true;
- next_ep->pending = 0;
- epx = next_ep;
- usb_hw->dev_addr_ctrl = next_ep->dev_addr;
- const uint32_t sc = USB_SIE_CTRL_SEND_SETUP_BITS |
- (next_ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0);
- sie_start_xfer(sc);
- } else {
- uint16_t prev_xferred = next_ep->xferred_len;
- next_ep->pending = 0;
- epx_xfer(next_ep, next_ep->user_buf, NULL, next_ep->remaining_len);
- epx->xferred_len += prev_xferred;
- }
-}
-
-// Round-robin find next pending ep after current epx
-static hw_endpoint_t *__tusb_irq_path_func(epx_find_pending)(void) {
- const uint start = (uint)(epx - &ep_pool[0]) + 1;
- for (uint i = start; i < TU_ARRAY_SIZE(ep_pool); i++) {
- if (ep_pool[i].pending) {
- return &ep_pool[i];
- }
- }
- for (uint i = 0; i < start - 1; i++) {
- if (ep_pool[i].pending) {
- return &ep_pool[i];
- }
- }
- return NULL;
-}
-
static void __tusb_irq_path_func(hcd_rp2040_irq)(void) {
const uint32_t status = usb_hw->ints;
@@ -258,11 +319,11 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) {
// AND TRANS_COMPLETE as the stall is an alternative response
// to one of those events
usb_hw_clear->sie_status = USB_SIE_STATUS_STALL_REC_BITS;
- hw_xfer_complete(epx, XFER_RESULT_STALLED);
+ xfer_complete_isr(epx, XFER_RESULT_STALLED);
}
if (status & USB_INTS_BUFF_STATUS_BITS) {
- handle_hwbuf_status();
+ handle_buf_status_isr();
}
if (status & USB_INTS_TRANS_COMPLETE_BITS) {
@@ -271,32 +332,23 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) {
// only handle a setup packet
if (usb_hw->sie_ctrl & USB_SIE_CTRL_SEND_SETUP_BITS) {
epx->xferred_len = 8;
- hw_xfer_complete(epx, XFER_RESULT_SUCCESS);
+ xfer_complete_isr(epx, XFER_RESULT_SUCCESS);
} else {
// Don't care. Will handle this in buff status
}
}
#ifdef HAS_STOP_EPX_ON_NAK
- // RP2350: hardware stops EPX on NAK automatically
if (status & USB_INTS_EPX_STOPPED_ON_NAK_BITS) {
usb_hw_clear->nak_poll = USB_NAK_POLL_EPX_STOPPED_ON_NAK_BITS;
- hw_endpoint_t *next_ep = NULL;
- if (epx->active && tu_edpt_number(epx->ep_addr) != 0) {
- next_ep = epx_find_pending();
- }
- if (next_ep) {
+ hw_endpoint_t *next_ep = epx_next_pending(epx);
+ if (next_ep != NULL) {
epx_switch_ep(next_ep);
} else {
- // No preemption: disable stop-on-NAK, restart current transfer
+ // No switch: disable stop-on-NAK, restart current transfer
usb_hw_clear->nak_poll = USB_NAK_POLL_STOP_EPX_ON_NAK_BITS;
- if (epx->active) {
- const tusb_dir_t ep_dir = tu_edpt_dir(epx->ep_addr);
- const uint32_t sie_ctrl = (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) |
- (epx->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0);
- sie_start_xfer(sie_ctrl);
- }
+ sie_start_xfer(false, tu_edpt_dir(epx->ep_addr), epx->need_pre);
}
}
#else
@@ -304,22 +356,22 @@ static void __tusb_irq_path_func(hcd_rp2040_irq)(void) {
if (status & USB_INTS_HOST_SOF_BITS) {
(void) usb_hw->sof_rd; // clear SOF by reading SOF_RD
if (epx->active && tu_edpt_number(epx->ep_addr) != 0) {
- hw_endpoint_t *next_ep = epx_find_pending();
+ hw_endpoint_t *next_ep = epx_next_pending(epx);
if (next_ep) {
usb_hw_set->sie_ctrl = USB_SIE_CTRL_STOP_TRANS_BITS;
while (usb_hw->sie_ctrl & USB_SIE_CTRL_STOP_TRANS_BITS) {}
busy_wait_at_least_cycles(12);
if (usb_hw->buf_status & 1u) {
usb_hw->nak_poll = USB_NAK_POLL_RESET;
- handle_hwbuf_status();
+ handle_buf_status_isr();
} else {
epx_switch_ep(next_ep);
}
} else {
usb_hw_clear->inte = USB_INTE_HOST_SOF_BITS;
- usb_hw->nak_poll = USB_NAK_POLL_RESET;
+ usb_hw->nak_poll = USB_NAK_POLL_RESET;
}
- } else if (!epx_find_pending()) {
+ } else if (!epx_next_pending(epx)) {
// EPX is on control endpoint or inactive — disable SOF if nothing pending
usb_hw_clear->inte = USB_INTE_HOST_SOF_BITS;
usb_hw->nak_poll = USB_NAK_POLL_RESET;
@@ -536,17 +588,6 @@ bool hcd_edpt_close(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
return false; // TODO not implemented yet
}
-TU_ATTR_ALWAYS_INLINE static inline void sie_start_xfer(uint32_t value) {
- value |= (usb_hw->sie_ctrl & SIE_CTRL_BASE_MASK); // preserve base bits
-
- // START_TRANS bit on SIE_CTRL has the same behavior as the AVAILABLE bit
- // described in RP2040 Datasheet, release 2.1, section "4.1.2.5.1. Concurrent access".
- // We write everything except the START_TRANS bit first, then wait some cycles.
- usb_hw->sie_ctrl = value;
- busy_wait_at_least_cycles(12);
- usb_hw->sie_ctrl = value | USB_SIE_CTRL_START_TRANS_BITS;
-}
-
// start a transfer on epx endpoint
static void epx_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t total_len) {
const uint8_t ep_num = tu_edpt_number(ep->ep_addr);
@@ -575,47 +616,7 @@ static void epx_xfer(hw_endpoint_t *ep, uint8_t *buffer, tu_fifo_t *ff, uint16_t
epx = ep;
// start transfer
- const uint32_t sie_ctrl = (ep_dir ? USB_SIE_CTRL_RECEIVE_DATA_BITS : USB_SIE_CTRL_SEND_DATA_BITS) |
- (ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0);
- sie_start_xfer(sie_ctrl);
-}
-
-// Schedule next pending EPX transfer from ISR context
-static void __tusb_irq_path_func(edpt_schedule_next)(void) {
- // EPX may already be active if the completion callback started a new transfer
- if (epx->active) {
- return;
- }
-
- for (uint i = 0; i < TU_ARRAY_SIZE(ep_pool); i++) {
- hw_endpoint_t *ep = &ep_pool[i];
- if (ep->pending == 0) {
- continue;
- }
-
- if (ep->pending == 2) {
- // Pending setup: DPRAM already has the setup packet
- ep->ep_addr = 0;
- ep->remaining_len = 8;
- ep->xferred_len = 0;
- ep->active = true;
- ep->pending = 0;
-
- epx = ep;
- usb_hw->dev_addr_ctrl = ep->dev_addr;
-
- const uint32_t sie_ctrl = USB_SIE_CTRL_SEND_SETUP_BITS |
- (ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0);
- sie_start_xfer(sie_ctrl);
- } else {
- // Pending data transfer: preserve partial progress from preemption
- uint16_t prev_xferred = ep->xferred_len;
- ep->pending = 0;
- epx_xfer(ep, ep->user_buf, NULL, ep->remaining_len);
- epx->xferred_len += prev_xferred; // restore partial progress
- }
- return; // start only one transfer
- }
+ sie_start_xfer(false, ep_dir, ep->need_pre);
}
bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) {
@@ -705,8 +706,7 @@ bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet
usb_hw->dev_addr_ctrl = dev_addr; // Set device address
// Set pre if we are a low speed device on full speed hub
- const uint32_t sie_ctrl = USB_SIE_CTRL_SEND_SETUP_BITS | (ep->need_pre ? USB_SIE_CTRL_PREAMBLE_EN_BITS : 0);
- sie_start_xfer(sie_ctrl);
+ sie_start_xfer(true, TUSB_DIR_OUT, ep->need_pre);
return true;
}