Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Isochronous transfer support #29

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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']
Expand Down
54 changes: 51 additions & 3 deletions src/bus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,8 @@ impl<USB: UsbPeripheral> usb_device::bus::UsbBus for UsbBus<USB> {
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
Expand Down Expand Up @@ -578,8 +579,8 @@ impl<USB: UsbPeripheral> usb_device::bus::UsbBus for UsbBus<USB> {

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 {
Expand Down Expand Up @@ -642,6 +643,53 @@ impl<USB: UsbPeripheral> usb_device::bus::UsbBus for UsbBus<USB> {
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;
Expand Down
48 changes: 45 additions & 3 deletions src/endpoint.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
Expand Down Expand Up @@ -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
);
}
Expand All @@ -205,6 +227,26 @@ impl EndpointOut {
}

pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
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)
})
Expand Down