summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-03-24 16:05:31 +0700
committerhathach <[email protected]>2026-03-24 16:05:31 +0700
commit09197ed27929d03a7f16e3d67dc76fa312948316 (patch)
tree2ac04dfa7867b323cd77c8e7880318882e47acaf /src
parent15eef94df0e759adf14697197a89dddeb8fc3aeb (diff)
handle buf_status in per buffer basic (INTERRUPT_PER_BUFFER), this allows us to sync/move half data payload instead of waiting for pair complete.
Refactor endpoint control and buffer handling functions for clarity and efficiency.
Diffstat (limited to 'src')
-rw-r--r--src/portable/raspberrypi/rp2040/dcd_rp2040.c128
-rw-r--r--src/portable/raspberrypi/rp2040/rp2040_usb.c150
-rw-r--r--src/portable/raspberrypi/rp2040/rp2040_usb.h10
3 files changed, 125 insertions, 163 deletions
diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
index e4d56f69a..ec73cafc1 100644
--- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
@@ -69,20 +69,18 @@ TU_ATTR_ALWAYS_INLINE static inline hw_endpoint_t *hw_endpoint_get_by_addr(uint8
return hw_endpoint_get(num, dir);
}
-TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwep_ctrl_reg_device(struct hw_endpoint *ep) {
- const uint8_t epnum = tu_edpt_number(ep->ep_addr);
- const uint8_t dir = (uint8_t)tu_edpt_dir(ep->ep_addr);
+TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *get_ep_ctrl(const uint8_t epnum, tusb_dir_t dir) {
if (epnum == 0) {
// EP0 has no endpoint control register because the buffer offsets are fixed and always enabled
return NULL;
}
- return (dir == TUSB_DIR_IN) ? &usb_dpram->ep_ctrl[epnum - 1].in : &usb_dpram->ep_ctrl[epnum - 1].out;
+ struct usb_device_dpram_ep_ctrl *ep_ctrl = &usb_dpram->ep_ctrl[epnum - 1];
+ return (dir == TUSB_DIR_IN) ? &ep_ctrl->in : &ep_ctrl->out;
}
-TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *hwbuf_ctrl_reg_device(struct hw_endpoint *ep) {
- const uint8_t epnum = tu_edpt_number(ep->ep_addr);
- const uint8_t dir = (uint8_t)tu_edpt_dir(ep->ep_addr);
- return (dir == TUSB_DIR_IN) ? &usb_dpram->ep_buf_ctrl[epnum].in : &usb_dpram->ep_buf_ctrl[epnum].out;
+TU_ATTR_ALWAYS_INLINE static inline io_rw_32 *get_buf_ctrl(const uint8_t epnum, tusb_dir_t dir) {
+ struct usb_device_dpram_ep_buf_ctrl *buf_ctrl = &usb_dpram->ep_buf_ctrl[epnum];
+ return (dir == TUSB_DIR_IN) ? &buf_ctrl->in : &buf_ctrl->out;
}
// main processing for dcd_edpt_iso_activate
@@ -92,11 +90,12 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPa
ep->max_packet_size = wMaxPacketSize;
// Clear existing buffer control state
- io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep);
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const tusb_dir_t dir = tu_edpt_dir(ep_addr);
+ io_rw_32 *buf_ctrl_reg = get_buf_ctrl(epnum, dir);
*buf_ctrl_reg = 0;
// allocated hw buffer
- const uint8_t epnum = tu_edpt_number(ep_addr);
if (epnum == 0) {
// Buffer offset is fixed (also double buffered)
ep->dpram_buf = (uint8_t *)&usb_dpram->ep0_buf_a[0];
@@ -109,7 +108,7 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPa
size *= 2u;
#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
- if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
+ if (dir == TUSB_DIR_IN) {
ep->e15_bulk_in = true;
}
#endif
@@ -124,13 +123,13 @@ static void hw_endpoint_init(hw_endpoint_t *ep, uint8_t ep_addr, uint16_t wMaxPa
}
}
-static void hw_endpoint_enable(hw_endpoint_t *ep, uint8_t transfer_type) {
- io_rw_32 *ctrl_reg = hwep_ctrl_reg_device(ep);
+static void hw_endpoint_enable(uint8_t epnum, tusb_dir_t dir, uint8_t transfer_type, uint8_t *dpram_buf) {
+ io_rw_32 *ep_reg = get_ep_ctrl(epnum, dir);
// Set endpoint control register to enable (EP0 has no endpoint control register)
- if (ctrl_reg != NULL) {
+ if (ep_reg != NULL) {
const uint32_t ctrl_value =
- EP_CTRL_ENABLE_BITS | ((uint32_t)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(ep->dpram_buf);
- *ctrl_reg = ctrl_value;
+ EP_CTRL_ENABLE_BITS | ((uint32_t)transfer_type << EP_CTRL_BUFFER_TYPE_LSB) | hw_data_offset(dpram_buf);
+ *ep_reg = ctrl_value;
}
}
@@ -141,7 +140,7 @@ static void hw_endpoint_open(uint8_t ep_addr, uint16_t wMaxPacketSize, uint8_t t
hw_endpoint_t *ep = hw_endpoint_get(epnum, dir);
hw_endpoint_init(ep, ep_addr, wMaxPacketSize, transfer_type);
- hw_endpoint_enable(ep, transfer_type);
+ hw_endpoint_enable(epnum, dir, transfer_type, ep->dpram_buf);
}
static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) {
@@ -162,7 +161,7 @@ static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) {
buf_ctrl |= USB_BUF_CTRL_DATA1_PID;
}
- io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep);
+ io_rw_32 *buf_ctrl_reg = get_buf_ctrl(epnum, dir);
hwbuf_ctrl_set(buf_ctrl_reg, buf_ctrl);
hw_endpoint_reset_transfer(ep);
@@ -173,32 +172,41 @@ static void hw_endpoint_abort_xfer(struct hw_endpoint* ep) {
}
static void __tusb_irq_path_func(handle_hw_buff_status)(void) {
- uint32_t remaining_buffers = usb_hw->buf_status;
- pico_trace("buf_status = 0x%08lx\r\n", remaining_buffers);
- uint bit = 1u;
- for (uint8_t i = 0; remaining_buffers && i < USB_MAX_ENDPOINTS * 2; i++) {
- if (remaining_buffers & bit) {
- // clear this in advance
- usb_hw_clear->buf_status = bit;
+ uint32_t buf_status = usb_hw->buf_status;
+ pico_trace("buf_status = 0x%08lx\r\n", buf_status);
+ while (buf_status) {
+ // ctz/clz is faster than loop which has only a few bit set in general
+ const uint8_t i = (uint8_t) __builtin_ctz(buf_status);
+ const uint bit = TU_BIT(i);
- // IN transfer for even i, OUT transfer for odd i
- const uint8_t epnum = i >> 1u;
- const tusb_dir_t dir = (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN;
- hw_endpoint_t *ep = hw_endpoint_get(epnum, dir);
+ // Read which buffer to handle BEFORE clearing buf_status
+ uint8_t buf_id = (usb_hw->buf_cpu_should_handle & bit) ? 1 : 0;
+ usb_hw_clear->buf_status = bit;
- io_rw_32 *ep_reg = hwep_ctrl_reg_device(ep);
- io_rw_32 *buf_reg = hwbuf_ctrl_reg_device(ep);
- const bool done = hw_endpoint_xfer_continue(ep, ep_reg, buf_reg);
+ // IN transfer for even i, OUT transfer for odd i
+ const uint8_t epnum = i >> 1u;
+ const tusb_dir_t dir = (i & 1u) ? TUSB_DIR_OUT : TUSB_DIR_IN;
+ hw_endpoint_t *ep = hw_endpoint_get(epnum, dir);
- if (done) {
- // Notify usbd
- const uint16_t xferred_len = ep->xferred_len;
- hw_endpoint_reset_transfer(ep);
- dcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, true);
- }
- remaining_buffers &= ~bit;
+ io_rw_32 *ep_reg = get_ep_ctrl(epnum, dir);
+ io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir);
+ bool done = hw_endpoint_xfer_continue(ep, ep_reg, buf_reg, buf_id);
+
+ // Double-buffered: if both buffers completed at once, buf_status re-sets
+ // immediately after clearing (datasheet Table 406). Process the second buffer too.
+ if (!done && (usb_hw->buf_status & bit)) {
+ buf_id = (usb_hw->buf_cpu_should_handle & bit) ? 1 : 0;
+ usb_hw_clear->buf_status = bit;
+ done = hw_endpoint_xfer_continue(ep, ep_reg, buf_reg, buf_id);
+ }
+
+ if (done) {
+ const uint16_t xferred_len = ep->xferred_len;
+ hw_endpoint_reset_transfer(ep);
+ dcd_event_xfer_complete(0, ep->ep_addr, xferred_len, XFER_RESULT_SUCCESS, true);
}
- bit <<= 1u;
+
+ buf_status &= ~bit;
}
}
@@ -251,9 +259,9 @@ static void __tusb_irq_path_func(dcd_rp2040_irq)(void) {
hw_endpoint_lock_update(ep, 1);
if (ep->pending) {
ep->pending = 0;
- io_rw_32 *ep_reg = hwep_ctrl_reg_device(ep);
- io_rw_32 *buf_reg = hwbuf_ctrl_reg_device(ep);
- hw_endpoint_start_next_buffer(ep, ep_reg, buf_reg);
+ io_rw_32 *ep_reg = get_ep_ctrl(i, TUSB_DIR_IN);
+ io_rw_32 *buf_reg = get_buf_ctrl(i, TUSB_DIR_IN);
+ hw_endpoint_buffer_xact(ep, ep_reg, buf_reg);
}
hw_endpoint_lock_update(ep, -1);
}
@@ -361,7 +369,7 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
(void) rh_init;
assert(rhport == 0);
- TU_LOG(2, "Chip Version B%u\r\n", rp2040_chip_version());
+ TU_LOG(1, "Chip Version B%u\r\n", rp2040_chip_version());
// Reset hardware to default state
rp2usb_init();
@@ -508,7 +516,7 @@ bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *ep_desc)
}
ep->max_packet_size = ep_desc->wMaxPacketSize;
- hw_endpoint_enable(ep, TUSB_XFER_ISOCHRONOUS);
+ hw_endpoint_enable(epnum, dir, TUSB_XFER_ISOCHRONOUS, ep->dpram_buf);
return true;
}
@@ -521,9 +529,12 @@ void dcd_edpt_close_all(uint8_t rhport) {
bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool is_isr) {
(void)rhport;
(void)is_isr;
- hw_endpoint_t *ep = hw_endpoint_get_by_addr(ep_addr);
- io_rw_32 *ep_reg = hwep_ctrl_reg_device(ep);
- io_rw_32 *buf_reg = hwbuf_ctrl_reg_device(ep);
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const tusb_dir_t dir = tu_edpt_dir(ep_addr);
+
+ hw_endpoint_t *ep = hw_endpoint_get(epnum, dir);
+ io_rw_32 *ep_reg = get_ep_ctrl(epnum, dir);
+ io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir);
hw_endpoint_xfer_start(ep, ep_reg, buf_reg, buffer, NULL, total_bytes);
return true;
}
@@ -532,9 +543,9 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes, bool is_isr) {
(void)rhport;
(void)is_isr;
- hw_endpoint_t *ep = hw_endpoint_get_by_addr(ep_addr);
- io_rw_32 *ep_reg = hwep_ctrl_reg_device(ep);
- io_rw_32 *buf_reg = hwbuf_ctrl_reg_device(ep);
+ hw_endpoint_t *ep = hw_endpoint_get(epnum, dir);
+ io_rw_32 *ep_reg = get_ep_ctrl(epnum, dir);
+ io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir);
hw_endpoint_xfer_start(ep, ep_reg, buf_reg, NULL, ff, total_bytes);
return true;
}
@@ -544,7 +555,6 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
(void)rhport;
const uint8_t epnum = tu_edpt_number(ep_addr);
const tusb_dir_t dir = tu_edpt_dir(ep_addr);
- hw_endpoint_t *ep = hw_endpoint_get(epnum, dir);
if (epnum == 0) {
// A stall on EP0 has to be armed so it can be cleared on the next setup packet
@@ -552,19 +562,19 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
}
// stall and clear current pending buffer, may need to use EP_ABORT
- io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep);
+ io_rw_32 *buf_ctrl_reg = get_buf_ctrl(epnum, dir);
hwbuf_ctrl_set(buf_ctrl_reg, USB_BUF_CTRL_STALL);
}
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
(void) rhport;
+ const uint8_t epnum = tu_edpt_number(ep_addr);
+ const tusb_dir_t dir = tu_edpt_dir(ep_addr);
- if (tu_edpt_number(ep_addr)) {
- struct hw_endpoint* ep = hw_endpoint_get_by_addr(ep_addr);
-
- // clear stall also reset toggle to DATA0, ready for next transfer
- ep->next_pid = 0;
- io_rw_32 *buf_ctrl_reg = hwbuf_ctrl_reg_device(ep);
+ if (epnum != 0) {
+ struct hw_endpoint* ep = hw_endpoint_get(epnum, dir);
+ ep->next_pid = 0; // reset data toggle
+ io_rw_32 *buf_ctrl_reg = get_buf_ctrl(epnum, dir);
hwbuf_ctrl_clear_mask(buf_ctrl_reg, USB_BUF_CTRL_STALL);
}
}
diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c
index 66e579c39..ac1536f5b 100644
--- a/src/portable/raspberrypi/rp2040/rp2040_usb.c
+++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c
@@ -35,8 +35,6 @@
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTOTYPE
//--------------------------------------------------------------------+
-static void sync_xfer(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg);
-
#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
static bool e15_is_critical_frame_period(struct hw_endpoint *ep);
#else
@@ -137,18 +135,18 @@ void __tusb_irq_path_func(hwbuf_ctrl_update)(io_rw_32 *buf_ctrl_reg, uint32_t an
// prepare buffer, move data if tx, return buffer control
static uint32_t __tusb_irq_path_func(hwbuf_prepare)(struct hw_endpoint *ep, uint8_t buf_id, bool is_rx) {
const uint16_t buflen = tu_min16(ep->remaining_len, ep->max_packet_size);
- ep->remaining_len = (uint16_t) (ep->remaining_len - buflen);
+ ep->remaining_len -= buflen;
uint32_t buf_ctrl = buflen | USB_BUF_CTRL_AVAIL;
-
- // PID
- buf_ctrl |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID;
+ if (ep->next_pid) {
+ buf_ctrl |= USB_BUF_CTRL_DATA1_PID;
+ }
ep->next_pid ^= 1u;
if (!is_rx) {
if (buflen) {
// Copy data from user buffer/fifo to hw buffer
- uint8_t *hw_buf = ep->dpram_buf + buf_id * 64;
+ uint8_t *hw_buf = ep->dpram_buf + (buf_id << 6);
#if CFG_TUD_EDPT_DEDICATED_HWFIFO
if (ep->is_xfer_fifo) {
// not in sram, may mess up timing with E15 workaround
@@ -161,7 +159,6 @@ static uint32_t __tusb_irq_path_func(hwbuf_prepare)(struct hw_endpoint *ep, uint
}
}
- // Mark as full
buf_ctrl |= USB_BUF_CTRL_FULL;
}
@@ -172,15 +169,11 @@ static uint32_t __tusb_irq_path_func(hwbuf_prepare)(struct hw_endpoint *ep, uint
buf_ctrl |= USB_BUF_CTRL_LAST;
}
- if (buf_id) {
- buf_ctrl = buf_ctrl << 16;
- }
-
return buf_ctrl;
}
-// Prepare buffer control register value
-void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) {
+// Start transaction on hw buffer
+void __tusb_irq_path_func(hw_endpoint_buffer_xact)(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) {
const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr);
const bool is_host = rp2usb_is_host_mode();
@@ -194,39 +187,34 @@ void __tusb_irq_path_func(hw_endpoint_start_next_buffer)(struct hw_endpoint *ep,
// always compute and start with buffer 0
uint32_t buf_ctrl = hwbuf_prepare(ep, 0, is_rx) | USB_BUF_CTRL_SEL;
- // EP0 has no endpoint control register, also usbd only schedule 1 packet at a time (single buffer)
+ // Device mode EP0 has no endpoint control register
if (ep_reg != NULL) {
- uint32_t ep_ctrl = *ep_reg;
+ // Each buffer completion triggers its own IRQ.
+ // If both complete simultaneously, buf_status re-sets on next clock (datasheet Table 406).
+ uint32_t ep_ctrl = *ep_reg | EP_CTRL_INTERRUPT_PER_BUFFER;
- // For now: skip double buffered for RX e.g OUT endpoint in Device mode, since host could send < 64 bytes and cause
- // short packet on buffer0
- // NOTE: this could happen to Host mode IN endpoint Also, Host mode "interrupt" endpoint hardware is only single
- // buffered,
- // NOTE2: Currently Host bulk is implemented using "interrupt" endpoint
- const bool force_single = (!is_host && is_rx) || (is_host && tu_edpt_number(ep->ep_addr) != 0);
+ // Since short packet on buf0 in double-buffered RX: buf1 may already contain data from the
+ // NEXT transfer (host sent it before CPU processed this IRQ). Cannot safely recover. Avoid by not using double
+ // buffering for rx transfer
+ bool force_single = is_rx;
+ #if CFG_TUH_ENABLED
+ force_single |= (is_host && ep->interrupt_num != 0); // host interrupt is single only
+ #endif
if (ep->remaining_len && !force_single) {
// Use buffer 1 (double buffered) if there is still data
// TODO: Isochronous for buffer1 bit-field is different than CBI (control bulk, interrupt)
-
- buf_ctrl |= hwbuf_prepare(ep, 1, is_rx);
-
- // Set endpoint control double buffered bit if needed
- ep_ctrl &= ~EP_CTRL_INTERRUPT_PER_BUFFER;
- ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER;
+ buf_ctrl |= (hwbuf_prepare(ep, 1, is_rx) << 16);
+ ep_ctrl |= EP_CTRL_DOUBLE_BUFFERED_BITS;
} else {
// Single buffered since 1 is enough
- ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER);
- ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER;
+ ep_ctrl &= ~EP_CTRL_DOUBLE_BUFFERED_BITS;
}
*ep_reg = ep_ctrl;
}
- TU_LOG(3, " Prepare BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl));
-
- // Finally, write to buffer_control which will trigger the transfer
- // the next time the controller polls this dpram address
+ // Finally, write to buffer_control which will trigger the transfer the next time the controller polls this endpoint
hwbuf_ctrl_set(buf_reg, buf_ctrl);
}
@@ -267,7 +255,7 @@ void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *
} else
#endif
{
- hw_endpoint_start_next_buffer(ep, ep_reg, buf_reg);
+ hw_endpoint_buffer_xact(ep, ep_reg, buf_reg);
}
hw_endpoint_lock_update(ep, -1);
@@ -315,91 +303,49 @@ static uint16_t __tusb_irq_path_func(hwbuf_sync)(hw_endpoint_t *ep, io_rw_32 *bu
return xferred_bytes;
}
-// Update hw endpoint struct with info from hardware after a buff status interrupt
-static void __tusb_irq_path_func(sync_xfer)(hw_endpoint_t *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) {
- // const uint8_t ep_num = tu_edpt_number(ep->ep_addr);
- const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr);
- const bool is_host = rp2usb_is_host_mode();
- bool is_rx;
+// Returns true if transfer is complete.
+// buf_id: which buffer completed (from BUFF_CPU_SHOULD_HANDLE, only used for double-buffered).
+bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep, io_rw_32 *ep_reg,
+ io_rw_32 *buf_reg, uint8_t buf_id) {
+ hw_endpoint_lock_update(ep, 1);
- if (is_host) {
- is_rx = (dir == TUSB_DIR_IN);
- } else {
- is_rx = (dir == TUSB_DIR_OUT);
+ if (!ep->active) {
+ panic("Can't continue xfer on inactive ep %02X", ep->ep_addr);
}
- TU_LOG(3, " Sync BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(*buf_reg), tu_u32_high16(*buf_reg));
- uint16_t buf0_bytes = hwbuf_sync(ep, buf_reg, 0, is_rx); // always sync buffer 0
+ const tusb_dir_t dir = tu_edpt_dir(ep->ep_addr);
+ const bool is_host = rp2usb_is_host_mode();
+ const bool is_rx = is_host ? (dir == TUSB_DIR_IN) : (dir == TUSB_DIR_OUT);
+ const bool is_double = ep_reg != NULL && ((*ep_reg) & EP_CTRL_DOUBLE_BUFFERED_BITS);
- // sync buffer 1 if double buffered
- if (ep_reg != NULL && (*ep_reg) & EP_CTRL_DOUBLE_BUFFERED_BITS) {
- if (buf0_bytes == ep->max_packet_size) {
- // sync buffer 1 if not short packet
- hwbuf_sync(ep, buf_reg, 1, is_rx);
- } else {
- // short packet on buffer 0
- // TODO couldn't figure out how to handle this case which happen with net_lwip_webserver example
- // At this time (currently trigger per 2 buffer), the buffer1 is probably filled with data from
- // the next transfer (not current one). For now we disable double buffered for device OUT
- // NOTE this could happen to Host IN
-#if 0
- uint8_t const ep_num = tu_edpt_number(ep->ep_addr);
- uint8_t const dir = (uint8_t) tu_edpt_dir(ep->ep_addr);
- uint8_t const ep_id = 2*ep_num + (dir ? 0 : 1);
-
- // abort queued transfer on buffer 1
- usb_hw->abort |= TU_BIT(ep_id);
-
- while ( !(usb_hw->abort_done & TU_BIT(ep_id)) ) {}
-
- uint32_t ep_ctrl = *ep->endpoint_control;
- ep_ctrl &= ~(EP_CTRL_DOUBLE_BUFFERED_BITS | EP_CTRL_INTERRUPT_PER_DOUBLE_BUFFER);
- ep_ctrl |= EP_CTRL_INTERRUPT_PER_BUFFER;
-
- io_rw_32 *buf_ctrl_reg = is_host ? hwbuf_ctrl_reg_host(ep) : hwbuf_ctrl_reg_device(ep);
- hwbuf_ctrl_set(buf_ctrl_reg, 0);
+ const uint16_t xferred = hwbuf_sync(ep, buf_reg, is_double ? buf_id : 0, is_rx);
+ bool is_done = (ep->remaining_len == 0);
- usb_hw->abort &= ~TU_BIT(ep_id);
-
- TU_LOG(3, "----SHORT PACKET buffer0 on EP %02X:\r\n", ep->ep_addr);
- TU_LOG(3, " BufCtrl: [0] = 0x%04x [1] = 0x%04x\r\n", tu_u32_low16(buf_ctrl), tu_u32_high16(buf_ctrl));
-#endif
+ if (is_double) {
+ if (xferred < ep->max_packet_size) {
+ // Short packet
+ is_done = true;
+ } else if (buf_id == 0) {
+ // buf0 done: wait for buf1, don't start new buffers
+ hw_endpoint_lock_update(ep, -1);
+ return false;
}
+ // buf1 done: is_done determined by remaining_len above
}
-}
-// Returns true if transfer is complete
-bool __tusb_irq_path_func(hw_endpoint_xfer_continue)(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg) {
- hw_endpoint_lock_update(ep, 1);
-
- // Part way through a transfer
- if (!ep->active) {
- panic("Can't continue xfer on inactive ep %02X", ep->ep_addr);
- }
-
- sync_xfer(ep, ep_reg, buf_reg); // Update EP struct from hardware state
-
- // Now we have synced our state with the hardware. Is there more data to transfer?
- // If we are done then notify tinyusb
- if (ep->remaining_len == 0) {
- pico_trace("Completed transfer of %d bytes on ep %02X\r\n", ep->xferred_len, ep->ep_addr);
- // Notify caller we are done so it can notify the tinyusb stack
- hw_endpoint_lock_update(ep, -1);
- return true;
- } else {
+ if (!is_done) {
#if TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX
if (e15_is_critical_frame_period(ep)) {
ep->pending = 1;
} else
#endif
{
- hw_endpoint_start_next_buffer(ep, ep_reg, buf_reg);
+ hw_endpoint_buffer_xact(ep, ep_reg, buf_reg);
}
}
hw_endpoint_lock_update(ep, -1);
- // More work to do
- return false;
+ return is_done;
}
//--------------------------------------------------------------------+
diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h
index 46bd727c8..c4dd0cb98 100644
--- a/src/portable/raspberrypi/rp2040/rp2040_usb.h
+++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h
@@ -26,6 +26,8 @@
#if defined(PICO_RP2040_USB_DEVICE_UFRAME_FIX) && !defined(TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX)
#define TUD_OPT_RP2040_USB_DEVICE_UFRAME_FIX PICO_RP2040_USB_DEVICE_UFRAME_FIX
#endif
+
+ #define CFG_TUSB_RP2040_ERRATA_E4_FIX 1
#endif
#ifndef TUD_OPT_RP2040_USB_DEVICE_ENUMERATION_FIX
@@ -45,6 +47,10 @@
#define PICO_RP2040_USB_FAST_IRQ 0
#endif
+#ifndef CFG_TUSB_RP2040_ERRATA_E4_FIX
+#define CFG_TUSB_RP2040_ERRATA_E4_FIX 0
+#endif
+
#if PICO_RP2040_USB_FAST_IRQ
#define __tusb_irq_path_func(x) __no_inline_not_in_flash_func(x)
#else
@@ -108,8 +114,8 @@ TU_ATTR_ALWAYS_INLINE static inline bool rp2usb_is_host_mode(void) {
//--------------------------------------------------------------------+
void hw_endpoint_xfer_start(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t *buffer, tu_fifo_t *ff,
uint16_t total_len);
-bool hw_endpoint_xfer_continue(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg);
-void hw_endpoint_start_next_buffer(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg);
+bool hw_endpoint_xfer_continue(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg, uint8_t buf_id);
+void hw_endpoint_buffer_xact(struct hw_endpoint *ep, io_rw_32 *ep_reg, io_rw_32 *buf_reg);
void hw_endpoint_reset_transfer(struct hw_endpoint *ep);
TU_ATTR_ALWAYS_INLINE static inline void hw_endpoint_lock_update(__unused struct hw_endpoint * ep, __unused int delta) {