summaryrefslogtreecommitdiff
path: root/src/portable/raspberrypi
diff options
context:
space:
mode:
authorHiFiPHile <[email protected]>2026-08-25 09:27:48 +0200
committerHiFiPHile <[email protected]>2026-08-25 09:27:48 +0200
commitdfac26a272fa7bbbca2050fbe9f1ca09008e548e (patch)
treeefbc53f8f2c1e5d9c7f38e5fef6d774a20053cec /src/portable/raspberrypi
parente590b45fcf51f9ddace73178074e4fe6d691e319 (diff)
parent5c0e31cdabaf37f14e1f5e988a020abfc1000495 (diff)
Merge master updates into the UAC1 host branch
Bring the audio work onto the current host core and build files before applying the remaining review fixes. Signed-off-by: HiFiPHile <[email protected]>
Diffstat (limited to 'src/portable/raspberrypi')
-rw-r--r--src/portable/raspberrypi/rp2040/dcd_rp2040.c46
-rw-r--r--src/portable/raspberrypi/rp2040/hcd_rp2040.c4
-rw-r--r--src/portable/raspberrypi/rp2040/rp2040_usb.c13
-rw-r--r--src/portable/raspberrypi/rp2040/rp2040_usb.h1
4 files changed, 59 insertions, 5 deletions
diff --git a/src/portable/raspberrypi/rp2040/dcd_rp2040.c b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
index 63097cd0a..564ded535 100644
--- a/src/portable/raspberrypi/rp2040/dcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/dcd_rp2040.c
@@ -387,6 +387,9 @@ bool dcd_deinit(uint8_t rhport) {
reset_block(RESETS_RESET_USBCTRL_BITS);
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
+ // Release allocated resources
+ rp2usb_deinit();
+
return true;
}
@@ -550,9 +553,46 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
if (epnum != 0) {
struct hw_endpoint* ep = hw_endpoint_get(epnum, dir);
- ep->next_pid = 0; // reset data toggle
- io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir);
- *buf_reg = 0;
+
+ if (ep->state == EPSTATE_ACTIVE) {
+ // Clear-halt on an endpoint with an in-flight transfer is used as a data-toggle reset
+ // (e.g. usbtest case 29) rather than to recover from a real stall (a stall aborts the
+ // transfer, leaving the endpoint IDLE). Abort and re-issue the transfer with the toggle
+ // reset to DATA0 so it still completes and releases the usbd claim, instead of silently
+ // dropping it and starving the endpoint. Save the buffer/length before the abort clears them.
+ uint8_t* user_buf = ep->user_buf;
+ uint16_t remaining = ep->remaining_len;
+ const uint16_t xferred = ep->xferred_len; // bytes already moved on this submission
+ io_rw_32 *ep_reg = get_ep_ctrl(epnum, dir);
+ io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir);
+ // bufctrl_prepare16() subtracts each armed buffer's length from remaining_len when arming,
+ // for BOTH directions, before the host has drained (IN) or filled (OUT) it. The abort below
+ // discards those still-armed buffers, so rewind remaining_len by their lengths or the re-issue
+ // is short by 1-2 packets. IN additionally advances user_buf as packets are copied into DPRAM,
+ // so its pointer must rewind too; OUT copies out only on completion, so its pointer is intact.
+ const uint32_t bc = *buf_reg;
+ uint16_t staged = 0;
+ if (bc & USB_BUF_CTRL_AVAIL) {
+ staged = (uint16_t)(bc & USB_BUF_CTRL_LEN_MASK);
+ }
+ if ((bc >> 16) & USB_BUF_CTRL_AVAIL) {
+ staged = (uint16_t)(staged + ((bc >> 16) & USB_BUF_CTRL_LEN_MASK));
+ }
+ remaining = (uint16_t)(remaining + staged);
+ if (dir == TUSB_DIR_IN) {
+ user_buf -= staged;
+ }
+ hw_endpoint_abort_xfer(ep); // safe abort (handles RP2040-E2), resets ep transfer state
+ ep->next_pid = 0; // DATA0
+ rp2usb_xfer_start(ep, ep_reg, buf_reg, user_buf, NULL, remaining);
+ // rp2usb_xfer_start() zeroes xferred_len; add back what the aborted transfer already moved so
+ // the eventual completion reports the full length, not just the post-clear-halt remainder.
+ ep->xferred_len += xferred;
+ } else {
+ ep->next_pid = 0; // reset data toggle
+ io_rw_32 *buf_reg = get_buf_ctrl(epnum, dir);
+ *buf_reg = 0; // clear the stall response
+ }
}
}
diff --git a/src/portable/raspberrypi/rp2040/hcd_rp2040.c b/src/portable/raspberrypi/rp2040/hcd_rp2040.c
index 28dc7f93c..a04890835 100644
--- a/src/portable/raspberrypi/rp2040/hcd_rp2040.c
+++ b/src/portable/raspberrypi/rp2040/hcd_rp2040.c
@@ -427,6 +427,10 @@ bool hcd_deinit(uint8_t rhport) {
irq_remove_handler(USBCTRL_IRQ, hcd_rp2040_irq);
reset_block(RESETS_RESET_USBCTRL_BITS);
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
+
+ // Release allocated resources
+ rp2usb_deinit();
+
return true;
}
diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c
index e4eb0184e..96b335bd3 100644
--- a/src/portable/raspberrypi/rp2040/rp2040_usb.c
+++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c
@@ -85,6 +85,10 @@ void rp2usb_init(void) {
critical_section_init(&rp2usb_lock);
}
+void rp2usb_deinit(void) {
+ critical_section_deinit(&rp2usb_lock);
+}
+
void __tusb_irq_path_func(rp2usb_reset_transfer)(hw_endpoint_t *ep) {
ep->state = EPSTATE_IDLE;
ep->remaining_len = 0;
@@ -176,10 +180,15 @@ void __tusb_irq_path_func(rp2usb_buffer_start)(hw_endpoint_t *ep, io_rw_32 *ep_r
// Note: device EP0 does not have an endpoint control register
if (ep_reg != NULL) {
uint32_t ep_ctrl = *ep_reg;
+ // Isochronous endpoints get a single DPRAM buffer (hw_endpoint_open only double-sizes BULK), so
+ // they must never be double-buffered here even when a transfer spans multiple packets, or buffer
+ // 1 (at dpram_buf+64) would spill into the next endpoint's DPRAM. (Never true for BULK, so the
+ // double-buffered bulk path is unaffected.)
+ const bool is_iso = (((ep_ctrl >> EP_CTRL_BUFFER_TYPE_LSB) & 0x3u) == TUSB_XFER_ISOCHRONOUS);
#if CFG_TUH_ENABLED
- const bool force_single = (rp2usb_is_host_mode() && ep->interrupt_num > 0);
+ const bool force_single = is_iso || (rp2usb_is_host_mode() && ep->interrupt_num > 0);
#else
- const bool force_single = false;
+ const bool force_single = is_iso;
#endif
if (ep->remaining_len && !force_single) {
diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.h b/src/portable/raspberrypi/rp2040/rp2040_usb.h
index f4e85764d..e5a54007c 100644
--- a/src/portable/raspberrypi/rp2040/rp2040_usb.h
+++ b/src/portable/raspberrypi/rp2040/rp2040_usb.h
@@ -147,6 +147,7 @@ extern volatile uint32_t e15_last_sof;
#endif
void rp2usb_init(void);
+void rp2usb_deinit(void);
// if usb hardware is in host mode
TU_ATTR_ALWAYS_INLINE static inline bool rp2usb_is_host_mode(void) {