summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHiFiPHile <[email protected]>2026-09-01 03:59:39 +0200
committerHiFiPHile <[email protected]>2026-09-02 10:50:03 +0200
commit393694d842b454ecadd32760851be899a91d69af (patch)
treeafec46b9bcdb9298d06553646cab1c4f2a8644aa
parentdea4d268d909d3063703bf9b431cabf6223479e1 (diff)
fix(dwc2): fail missed isochronous frames
A frame-overrun interrupt means the selected periodic service interval has already been missed. Retrying an isochronous transfer after that point cannot deliver the original packet and can leave the class waiting indefinitely for a terminal result. Enable frame-overrun interrupts for slave periodic channels. Complete isochronous IN and OUT overruns as XFER_RESULT_FAILED in both slave and DMA modes, accounting for bytes already written on OUT. Preserve the existing retry behavior for non-isochronous DMA transfers. This reports the missed packet honestly through the normal HCD completion path: no fabricated success and no class-level abort workaround.
-rw-r--r--src/portable/synopsys/dwc2/hcd_dwc2.c30
1 files changed, 28 insertions, 2 deletions
diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c
index d10f28f6a..55a15b326 100644
--- a/src/portable/synopsys/dwc2/hcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/hcd_dwc2.c
@@ -666,6 +666,9 @@ static bool channel_xfer_start(dwc2_regs_t* dwc2, uint8_t ch_id) {
}
} else {
uint32_t hcintmsk = HCINT_NAK | HCINT_XACT_ERR | HCINT_STALL | HCINT_XFER_COMPLETE | HCINT_DATATOGGLE_ERR;
+ if (is_period) {
+ hcintmsk |= HCINT_FARME_OVERRUN;
+ }
if (hcchar_bm->ep_dir == TUSB_DIR_IN) {
hcintmsk |= HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR | HCINT_ACK;
} else {
@@ -1015,6 +1018,11 @@ static bool handle_channel_in_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t h
} else {
channel_disable(dwc2, channel);
}
+ } else if (hcint & HCINT_FARME_OVERRUN) {
+ if (edpt->hcchar_bm.ep_type == HCCHAR_EPTYPE_ISOCHRONOUS) {
+ xfer->result = XFER_RESULT_FAILED;
+ }
+ channel_disable(dwc2, channel);
} else if (hcint & (HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_STALL)) {
if (hcint & HCINT_STALL) {
xfer->result = XFER_RESULT_STALLED;
@@ -1108,6 +1116,12 @@ static bool handle_channel_out_slave(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t
} else if (hcint & HCINT_STALL) {
xfer->result = XFER_RESULT_STALLED;
channel_disable(dwc2, channel);
+ } else if (hcint & HCINT_FARME_OVERRUN) {
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ if (edpt->hcchar_bm.ep_type == HCCHAR_EPTYPE_ISOCHRONOUS) {
+ xfer->result = XFER_RESULT_FAILED;
+ }
+ channel_disable(dwc2, channel);
} else if (hcint & HCINT_NYET) {
xfer->err_count = 0;
if (hcsplt.split_en == 1u) {
@@ -1273,8 +1287,12 @@ static bool handle_channel_in_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hci
channel_xfer_in_retry(dwc2, ch_id, hcint);
}
} else if (hcint & HCINT_FARME_OVERRUN) {
- // retry start-split in next binterval
- channel_xfer_in_retry(dwc2, ch_id, hcint);
+ if (hcchar.ep_type == HCCHAR_EPTYPE_ISOCHRONOUS) {
+ xfer->result = XFER_RESULT_FAILED;
+ is_done = true;
+ } else {
+ channel_xfer_in_retry(dwc2, ch_id, hcint);
+ }
}
if (xfer->closing == 1) {
@@ -1341,6 +1359,14 @@ static bool handle_channel_out_dma(dwc2_regs_t* dwc2, uint8_t ch_id, uint32_t hc
}
}
}
+ } else if (hcint & HCINT_FARME_OVERRUN) {
+ channel_xfer_out_wrapup(dwc2, ch_id);
+ if (edpt->hcchar_bm.ep_type == HCCHAR_EPTYPE_ISOCHRONOUS) {
+ xfer->result = XFER_RESULT_FAILED;
+ is_done = true;
+ } else {
+ channel_xfer_start(dwc2, ch_id);
+ }
} else if (hcint & HCINT_NYET) {
if (hcsplt.split_en && hcsplt.split_compl) {
// split not yet mean hub has no data, retry complete split