Skip to content

Commit e0a589c

Browse files
committed
dwc2: support ISO IN transfer when bInterval > 1
dwc2 requires manually toggle Even/Odd bit manually for ISO IN transfer, that's poses a problem when bInterval > 1 mainly for audio class, as the moment the transfer is scheduled, we don't know when the host will issue IN token (bInterval vs bRefresh schenanigans). Linux driver use NAK interrupt to detect when the host is sending IN token and toggle the Even/Odd bit accordingly based on the current frame number and bInterval. However on ST's stripped down DWC2 FS controller (e.g STM32F4, STM32F7), NAK interrupt is not supported, even it's marked as always present in DWC2 databook. NAK interrupt is only supported on HS controller with external PHY. Instead I schedule all ISO IN transfer for next frame, if the transfer failed, incomplete isochronous IN transfer interrupt will be triggered and we can relaunch the transfer. Signed-off-by: HiFiPhile <[email protected]>
1 parent 152d25e commit e0a589c

File tree

1 file changed

+39
-2
lines changed

1 file changed

+39
-2
lines changed

src/portable/synopsys/dwc2/dcd_dwc2.c

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ typedef struct {
5151
uint16_t total_len;
5252
uint16_t max_size;
5353
uint8_t interval;
54+
uint8_t iso_retry; // ISO retry counter
5455
} xfer_ctl_t;
5556

5657
// This variable is modified from ISR context, so it must be protected by critical section
@@ -357,7 +358,7 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin
357358
dwc2_depctl_t depctl = {.value = dep->ctl};
358359
depctl.clear_nak = 1;
359360
depctl.enable = 1;
360-
if (depctl.type == DEPCTL_EPTYPE_ISOCHRONOUS && xfer->interval == 1) {
361+
if (depctl.type == DEPCTL_EPTYPE_ISOCHRONOUS) {
361362
const dwc2_dsts_t dsts = {.value = dwc2->dsts};
362363
const uint32_t odd_now = dsts.frame_number & 1u;
363364
if (odd_now) {
@@ -597,6 +598,9 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to
597598
_dcd_data.ep0_pending[dir] = total_bytes;
598599
}
599600

601+
// Reset ISO retry counter to max frame interval value
602+
xfer->iso_retry = 255;
603+
600604
// Schedule packets to be sent within interrupt
601605
edpt_schedule_packets(rhport, epnum, dir);
602606
ret = true;
@@ -629,6 +633,9 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t
629633
xfer->ff = ff;
630634
xfer->total_len = total_bytes;
631635

636+
// Reset ISO retry counter to max frame interval value
637+
xfer->iso_retry = 255;
638+
632639
// Schedule packets to be sent within interrupt
633640
// TODO xfer fifo may only available for slave mode
634641
edpt_schedule_packets(rhport, epnum, dir);
@@ -714,7 +721,7 @@ static void handle_bus_reset(uint8_t rhport) {
714721
dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
715722
}
716723

717-
dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT;
724+
dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT | GINTMSK_IISOIXFRM;
718725
}
719726

720727
static void handle_enum_done(uint8_t rhport) {
@@ -1100,6 +1107,36 @@ void dcd_int_handler(uint8_t rhport) {
11001107
// IEPINT bit read-only, clear using DIEPINTn
11011108
handle_ep_irq(rhport, TUSB_DIR_IN);
11021109
}
1110+
1111+
// Incomplete isochronous IN transfer interrupt handling.
1112+
if (gintsts & GINTSTS_IISOIXFR) {
1113+
dwc2->gintsts = GINTSTS_IISOIXFR;
1114+
// Loop over all IN endpoints
1115+
const uint8_t ep_count = dwc2_ep_count(dwc2);
1116+
for (uint8_t epnum = 0; epnum < ep_count; epnum++) {
1117+
dwc2_dep_t* epin = &dwc2->epin[epnum];
1118+
dwc2_depctl_t depctl = {.value = epin->diepctl};
1119+
// Find enabled ISO endpoints
1120+
if (depctl.enable && depctl.type == DEPCTL_EPTYPE_ISOCHRONOUS) {
1121+
// Disable endpoint, flush fifo and restart transfer
1122+
depctl.set_nak = 1;
1123+
epin->diepctl = depctl.value;
1124+
depctl.disable = 1;
1125+
epin->diepctl = depctl.value;
1126+
while ((epin->diepint & DIEPINT_EPDISD_Msk) == 0) {}
1127+
epin->diepint = DIEPINT_EPDISD;
1128+
dfifo_flush_tx(dwc2, epnum);
1129+
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN);
1130+
if (xfer->iso_retry) {
1131+
xfer->iso_retry--;
1132+
edpt_schedule_packets(rhport, epnum, TUSB_DIR_IN);
1133+
} else {
1134+
// too many retries, give up
1135+
dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, 0, XFER_RESULT_FAILED, true);
1136+
}
1137+
}
1138+
}
1139+
}
11031140
}
11041141

11051142
#if CFG_TUD_TEST_MODE

0 commit comments

Comments
 (0)