Skip to content

Commit fd4fdde

Browse files
committed
Handle incomplete isochronous IN transfers
Per RM0383's OTG documentation, when we receive an IISOIXFR interrupt we must disable and re-enable the IN endpoint and flush its FIFO before retransmitting.
1 parent 4304902 commit fd4fdde

File tree

1 file changed

+51
-3
lines changed

1 file changed

+51
-3
lines changed

src/bus.rs

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,8 @@ impl<USB: UsbPeripheral> usb_device::bus::UsbBus for UsbBus<USB> {
498498
write_reg!(otg_global, regs.global(), GINTMSK,
499499
USBRST: 1, ENUMDNEM: 1,
500500
USBSUSPM: 1, WUIM: 1,
501-
IEPINT: 1, RXFLVLM: 1
501+
IEPINT: 1, RXFLVLM: 1,
502+
IISOIXFRM: 1
502503
);
503504

504505
// clear pending interrupts
@@ -585,8 +586,8 @@ impl<USB: UsbPeripheral> usb_device::bus::UsbBus for UsbBus<USB> {
585586

586587
let core_id = read_reg!(otg_global, regs.global(), CID);
587588

588-
let (wakeup, suspend, enum_done, reset, iep, rxflvl) = read_reg!(otg_global, regs.global(), GINTSTS,
589-
WKUPINT, USBSUSP, ENUMDNE, USBRST, IEPINT, RXFLVL
589+
let (wakeup, suspend, enum_done, reset, iep, rxflvl, iisoixfr) = read_reg!(otg_global, regs.global(), GINTSTS,
590+
WKUPINT, USBSUSP, ENUMDNE, USBRST, IEPINT, RXFLVL, IISOIXFR
590591
);
591592

592593
if reset != 0 {
@@ -649,6 +650,53 @@ impl<USB: UsbPeripheral> usb_device::bus::UsbBus for UsbBus<USB> {
649650
write_reg!(otg_global, regs.global(), GINTSTS, USBSUSP: 1);
650651

651652
PollResult::Suspend
653+
} else if iisoixfr != 0 {
654+
use crate::ral::endpoint_in;
655+
656+
// Incomplete isochronous IN transfer; see
657+
// RM0383 Rev 3 pp797 "Incomplete isochronous IN data transfers"
658+
write_reg!(otg_global, regs.global(), GINTSTS, IISOIXFR: 1);
659+
660+
let in_endpoints = self.allocator.endpoints_in
661+
.iter()
662+
.flatten()
663+
.map(|ep| ep.address().index());
664+
665+
let mut iep: u16 = 0;
666+
667+
for epnum in in_endpoints {
668+
let ep_regs = regs.endpoint_in(epnum);
669+
670+
// filter out non-isochronous endpoints
671+
if read_reg!(endpoint_in, ep_regs, DIEPCTL, EPTYP) & 0x11 != 0x01 { continue; }
672+
673+
// identify incomplete transfers by the presence of the NAK event
674+
// see RM0383 Rev 3 pp 746 description of NAK:
675+
//
676+
// > In case of isochronous IN endpoints the interrupt gets
677+
// > generated when a zero length packet is transmitted due
678+
// > to unavailability of data in the Tx FIFO.
679+
if read_reg!(endpoint_in, ep_regs, DIEPINT) & 1<<13 == 0 { continue; }
680+
681+
// Set NAK
682+
modify_reg!(endpoint_in, ep_regs, DIEPCTL, SNAK: 1);
683+
while read_reg!(endpoint_in, ep_regs, DIEPINT, INEPNE) == 0 {}
684+
685+
// Disable the endpoint
686+
modify_reg!(endpoint_in, ep_regs, DIEPCTL, SNAK: 1, EPDIS: 1);
687+
while read_reg!(endpoint_in, ep_regs, DIEPINT, EPDISD) == 0 {}
688+
modify_reg!(endpoint_in, ep_regs, DIEPINT, EPDISD: 1);
689+
assert!(read_reg!(endpoint_in, ep_regs, DIEPCTL, EPENA) == 0);
690+
assert!(read_reg!(endpoint_in, ep_regs, DIEPCTL, EPDIS) == 0);
691+
692+
// Flush the TX FIFO
693+
modify_reg!(otg_global, regs.global(), GRSTCTL, TXFNUM: epnum as u32, TXFFLSH: 1);
694+
while read_reg!(otg_global, regs.global(), GRSTCTL, TXFFLSH) == 1 {}
695+
696+
iep |= 1 << epnum;
697+
}
698+
699+
PollResult::Data { ep_out: 0, ep_in_complete: iep, ep_setup: 0 }
652700
} else {
653701
let mut ep_out = 0;
654702
let mut ep_in_complete = 0;

0 commit comments

Comments
 (0)