summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHiFiPHile <[email protected]>2026-08-19 05:06:04 +0200
committerHiFiPHile <[email protected]>2026-08-19 05:06:04 +0200
commitf065f280284a6f1b5b4c4d2849db4bff4a3ce70c (patch)
tree7ca5519f9001b263dac05aab8b13228573f0c803 /src
parent18bb2d650404432d0c6c61c26e98c7074f46ec6b (diff)
ohci: defer descriptor reclaim until next frame
Diffstat (limited to 'src')
-rw-r--r--src/portable/ohci/ohci.c11
-rw-r--r--src/portable/ohci/ohci.h1
2 files changed, 9 insertions, 3 deletions
diff --git a/src/portable/ohci/ohci.c b/src/portable/ohci/ohci.c
index 7ede1ac72..2a174e46c 100644
--- a/src/portable/ohci/ohci.c
+++ b/src/portable/ohci/ohci.c
@@ -390,6 +390,9 @@ static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr) {
// Control endpoints (EP number 0) are statically allocated with the device which are only reused
// after connection of another device long after HC has finished with them now, these can be freed immediately.
if (ed->w0.ep_number != 0) {
+ // Wait until the next frame before reclaiming the ED and its TDs. Set the deadline before
+ // publishing is_reclaiming so a pending SOF IRQ cannot use an older deadline for this ED.
+ ohci_data.reclaim_frame = (uint16_t)(OHCI_REG->frame_number + 1);
ed->w0.is_reclaiming = 1;
// 5.2.7.1.2 Removing. Disable list processing for bulk
@@ -397,7 +400,8 @@ static void ed_list_remove_by_addr(ohci_ed_t * p_head, uint8_t dev_addr) {
OHCI_REG->control &= ~OHCI_CONTROL_LIST_BULK_ENABLE_MASK;
}
- // Temporarily enable SOF IRQ. ED and TD Memory will be reclaimed in the SOF IRQ.
+ // Temporarily enable SOF IRQ. Clear any pending SOF first to wait for the next frame.
+ OHCI_REG->interrupt_status = OHCI_INT_SOF_MASK;
OHCI_REG->interrupt_enable = OHCI_INT_SOF_MASK;
} else {
ed->w0.used = 0;
@@ -665,8 +669,9 @@ void hcd_int_handler(uint8_t hostid, bool in_isr) {
// Disable MIE as per OHCI spec 5.3
OHCI_REG->interrupt_disable = OHCI_INT_MASTER_ENABLE_MASK;
- // Start of frame (SOF)
- if (int_status & OHCI_INT_SOF_MASK) {
+ // Start of frame (SOF). Signed subtraction handles frame number rollover and delayed interrupts.
+ if ((int_status & OHCI_INT_SOF_MASK) &&
+ ((int16_t)((uint16_t)OHCI_REG->frame_number - ohci_data.reclaim_frame) >= 0)) {
OHCI_REG->interrupt_disable = OHCI_INT_SOF_MASK;
bool re_enable_lists = false;
diff --git a/src/portable/ohci/ohci.h b/src/portable/ohci/ohci.h
index c66954502..e28c6404f 100644
--- a/src/portable/ohci/ohci.h
+++ b/src/portable/ohci/ohci.h
@@ -183,6 +183,7 @@ typedef struct TU_ATTR_ALIGNED(256) {
gtd_extra_data_t gtd_extra[GTD_MAX];
volatile uint16_t frame_number_hi;
+ volatile uint16_t reclaim_frame;
} ohci_data_t;
//--------------------------------------------------------------------+