diff --git a/Cargo.toml b/Cargo.toml index 4e85563..4bb71cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,10 @@ riscv = { version = "0.6.0", optional = true } cortex-m = { version = "0.7.0", optional = true } embedded-hal = "0.2.4" vcell = "0.1.0" -usb-device = "0.2.3" +usb-device = "0.2.9" + +[patch.crates-io] +usb-device = { git = "https://github.com/ianrrees/usb-device", branch = "isochronous" } [package.metadata.docs.rs] features = ['cortex-m', 'fs'] diff --git a/src/bus.rs b/src/bus.rs index 8b510ba..8fd520f 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -491,7 +491,8 @@ impl usb_device::bus::UsbBus for UsbBus { write_reg!(otg_global, regs.global(), GINTMSK, USBRST: 1, ENUMDNEM: 1, USBSUSPM: 1, WUIM: 1, - IEPINT: 1, RXFLVLM: 1 + IEPINT: 1, RXFLVLM: 1, + IISOIXFRM: 1 ); // clear pending interrupts @@ -578,8 +579,8 @@ impl usb_device::bus::UsbBus for UsbBus { let core_id = read_reg!(otg_global, regs.global(), CID); - let (wakeup, suspend, enum_done, reset, iep, rxflvl) = read_reg!(otg_global, regs.global(), GINTSTS, - WKUPINT, USBSUSP, ENUMDNE, USBRST, IEPINT, RXFLVL + let (wakeup, suspend, enum_done, reset, iep, rxflvl, iisoixfr) = read_reg!(otg_global, regs.global(), GINTSTS, + WKUPINT, USBSUSP, ENUMDNE, USBRST, IEPINT, RXFLVL, IISOIXFR ); if reset != 0 { @@ -642,6 +643,53 @@ impl usb_device::bus::UsbBus for UsbBus { write_reg!(otg_global, regs.global(), GINTSTS, USBSUSP: 1); PollResult::Suspend + } else if iisoixfr != 0 { + use crate::ral::endpoint_in; + + // Incomplete isochronous IN transfer; see + // RM0383 Rev 3 pp797 "Incomplete isochronous IN data transfers" + write_reg!(otg_global, regs.global(), GINTSTS, IISOIXFR: 1); + + let in_endpoints = self.allocator.endpoints_in + .iter() + .flatten() + .map(|ep| ep.address().index()); + + let mut iep: u16 = 0; + + for epnum in in_endpoints { + let ep_regs = regs.endpoint_in(epnum); + + // filter out non-isochronous endpoints + if read_reg!(endpoint_in, ep_regs, DIEPCTL, EPTYP) & 0x11 != 0x01 { continue; } + + // identify incomplete transfers by the presence of the NAK event + // see RM0383 Rev 3 pp 746 description of NAK: + // + // > In case of isochronous IN endpoints the interrupt gets + // > generated when a zero length packet is transmitted due + // > to unavailability of data in the Tx FIFO. + if read_reg!(endpoint_in, ep_regs, DIEPINT) & 1<<13 == 0 { continue; } + + // Set NAK + modify_reg!(endpoint_in, ep_regs, DIEPCTL, SNAK: 1); + while read_reg!(endpoint_in, ep_regs, DIEPINT, INEPNE) == 0 {} + + // Disable the endpoint + modify_reg!(endpoint_in, ep_regs, DIEPCTL, SNAK: 1, EPDIS: 1); + while read_reg!(endpoint_in, ep_regs, DIEPINT, EPDISD) == 0 {} + modify_reg!(endpoint_in, ep_regs, DIEPINT, EPDISD: 1); + assert!(read_reg!(endpoint_in, ep_regs, DIEPCTL, EPENA) == 0); + assert!(read_reg!(endpoint_in, ep_regs, DIEPCTL, EPDIS) == 0); + + // Flush the TX FIFO + modify_reg!(otg_global, regs.global(), GRSTCTL, TXFNUM: epnum as u32, TXFFLSH: 1); + while read_reg!(otg_global, regs.global(), GRSTCTL, TXFFLSH) == 1 {} + + iep |= 1 << epnum; + } + + PollResult::Data { ep_out: 0, ep_in_complete: iep, ep_setup: 0 } } else { let mut ep_out = 0; let mut ep_in_complete = 0; diff --git a/src/endpoint.rs b/src/endpoint.rs index 2860b33..64b7cb8 100644 --- a/src/endpoint.rs +++ b/src/endpoint.rs @@ -1,7 +1,8 @@ +use usb_device::class_prelude::EndpointType; use usb_device::{Result, UsbError, UsbDirection}; use usb_device::endpoint::EndpointAddress; use crate::endpoint_memory::{EndpointBuffer, EndpointBufferState}; -use crate::ral::{read_reg, write_reg, modify_reg, endpoint_in, endpoint_out, endpoint0_out}; +use crate::ral::{read_reg, write_reg, modify_reg, endpoint_in, endpoint_out, endpoint0_out, otg_device}; use crate::target::{fifo_write, UsbRegisters}; use crate::target::interrupt::{self, CriticalSection, Mutex}; use core::ops::{Deref, DerefMut}; @@ -92,7 +93,7 @@ impl EndpointIn { write_reg!(endpoint_in, regs, DIEPCTL, SNAK: 1, USBAEP: 1, - EPTYP: self.descriptor.ep_type as u32, + EPTYP: self.descriptor.ep_type.to_bm_attributes() as u32, SD0PID_SEVNFRM: 1, TXFNUM: self.index() as u32, MPSIZ: self.descriptor.max_packet_size as u32 @@ -121,6 +122,7 @@ impl EndpointIn { pub fn write(&self, buf: &[u8]) -> Result<()> { let ep = self.usb.endpoint_in(self.index() as usize); + let device = self.usb.device(); if self.index() != 0 && read_reg!(endpoint_in, ep, DIEPCTL, EPENA) != 0{ return Err(UsbError::WouldBlock); } @@ -142,6 +144,26 @@ impl EndpointIn { #[cfg(feature = "hs")] write_reg!(endpoint_in, ep, DIEPTSIZ, MCNT: 1, PKTCNT: 1, XFRSIZ: buf.len() as u32); + match self.descriptor.ep_type { + // Isochronous endpoints must set the correct even/odd frame bit to + // correspond with the next frame's number. + EndpointType::Isochronous{..} => { + // Previous frame number is OTG_DSTS.FNSOF + let frame_number = read_reg!(otg_device, device, DSTS, FNSOF); + if frame_number & 0x1 == 1 { + // Previous frame number is odd, so upcoming frame is even + modify_reg!(endpoint_in, ep, DIEPCTL, SD0PID_SEVNFRM: 1); + } else { + // Previous frame number is even, so upcoming frame is odd + #[cfg(feature = "fs")] + modify_reg!(endpoint_in, ep, DIEPCTL, SODDFRM_SD1PID: 1); + #[cfg(feature = "hs")] + modify_reg!(endpoint_in, ep, DIEPCTL, SODDFRM: 1); + } + }, + _ => {} + } + modify_reg!(endpoint_in, ep, DIEPCTL, CNAK: 1, EPENA: 1); fifo_write(self.usb, self.index(), buf); @@ -183,7 +205,7 @@ impl EndpointOut { CNAK: 1, EPENA: 1, USBAEP: 1, - EPTYP: self.descriptor.ep_type as u32, + EPTYP: self.descriptor.ep_type.to_bm_attributes() as u32, MPSIZ: self.descriptor.max_packet_size as u32 ); } @@ -205,6 +227,26 @@ impl EndpointOut { } pub fn read(&self, buf: &mut [u8]) -> Result { + let ep = self.usb.endpoint_out(self.index() as usize); + let device = self.usb.device(); + + match self.descriptor.ep_type { + // Isochronous endpoints must set the correct even/odd frame bit to + // correspond with the next frame's number. + EndpointType::Isochronous{..} => { + // Previous frame number is OTG_DSTS.FNSOF + let frame_number = read_reg!(otg_device, device, DSTS, FNSOF); + if frame_number & 0x1 == 1 { + // Previous frame number is odd, so upcoming frame is even + modify_reg!(endpoint_out, ep, DOEPCTL, SD0PID_SEVNFRM: 1); + } else { + // Previous frame number is even, so upcoming frame is odd + modify_reg!(endpoint_out, ep, DOEPCTL, SODDFRM: 1); + } + }, + _ => {} + } + interrupt::free(|cs| { self.buffer.borrow(cs).borrow_mut().read_packet(buf) })