From 63d4ce8154eb8f6feb67986e98ea9b5007632460 Mon Sep 17 00:00:00 2001 From: Brian Cully Date: Mon, 24 Jun 2019 21:14:32 -0400 Subject: Initial commit. --- app/src/usb/pipe.rs | 503 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 503 insertions(+) create mode 100644 app/src/usb/pipe.rs (limited to 'app/src/usb/pipe.rs') diff --git a/app/src/usb/pipe.rs b/app/src/usb/pipe.rs new file mode 100644 index 0000000..12ca4cb --- /dev/null +++ b/app/src/usb/pipe.rs @@ -0,0 +1,503 @@ +pub mod addr; +pub mod ctrl_pipe; +pub mod ext_reg; +pub mod pck_size; +pub mod status_bk; +pub mod status_pipe; + +use addr::Addr; +use ctrl_pipe::CtrlPipe; +use ext_reg::ExtReg; +use pck_size::PckSize; +use status_bk::StatusBk; +use status_pipe::StatusPipe; + +use crate::logln_now; +use crate::rtc; + +use core::convert::TryInto; + +// FIXME: this stuff is needed for PipeRegs, and while tied to samd, +// it shouldn't be tied to trinket_m0. Will need to figure out a +// better layout for this. +use trinket_m0::usb::{ + self, + host::{BINTERVAL, PCFG, PINTFLAG, PSTATUS, PSTATUSCLR, PSTATUSSET}, +}; + +// TODO: verify this timeout against §9.2.6.1 of USB 2.0 spec. +const USB_TIMEOUT: usize = 5 * 1024; // 5 Seconds + +// samd21 only supports 8 pipes. +const MAX_PIPES: usize = 8; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub(crate) enum PipeErr { + InvalidPipe, + InvalidToken, + Stall, + TransferFail, + Flow, + HWTimeout, + DataToggle, + SWTimeout, + Other, +} +pub(crate) struct PipeTable { + tbl: [PipeDesc; MAX_PIPES], +} + +impl PipeTable { + pub(crate) fn new() -> Self { + Self { + tbl: [PipeDesc::new(); MAX_PIPES], + } + } + + pub(crate) fn pipe_for<'a, 'b>( + &'a mut self, + host: &'b mut usb::HOST, + addr: u8, + ep: u8, + ) -> Pipe<'a, 'b> { + // Just use two pipes for now. 0 is always for control + // endpoints, 1 for everything else. + // + // TODO: cache in-use pipes and return them without init if + // possible. + let i = if ep == 0 { 0 } else { 1 }; + + let pregs = PipeRegs::from(host, i); + let pdesc = &mut self.tbl[i]; + + logln_now!("setting paddr of pipe {} to {}:{}", i, addr, ep); + logln_now!("cpipe0: {:x}", pdesc.bank0.ctrl_pipe.read().bits()); + pdesc.bank0.ctrl_pipe.write(|w| { + w.pdaddr().set_addr(addr); + w.pepnum().set_epnum(ep) + }); + logln_now!("cpipe1: {:x}", pdesc.bank0.ctrl_pipe.read().bits()); + Pipe { + num: i, + regs: pregs, + desc: pdesc, + } + } +} + +// TODO: hide regs/desc fields. Needed right now for init_pipe0. +pub(crate) struct Pipe<'a, 'b> { + num: usize, + pub(crate) regs: PipeRegs<'b>, + pub(crate) desc: &'a mut PipeDesc, +} +impl Pipe<'_, '_> { + pub(crate) fn send( + &mut self, + token: USBToken, + buf: &DataBuf, + nak_limit: usize, + ) -> Result<(), PipeErr> { + // Data needs to be word aligned. + assert!((buf.ptr as u32) & 0x3 == 0); + // byte_count section of register is 14 bits. + assert!(buf.len < 16_384); + + logln_now!("p{}: sending {:?}", self.num, buf); + + // Equiv to UHD_Pipe_Write(epAddr: 0, sizeof(setup_packet), &setup_packet) + self.desc + .bank0 + .addr + .write(|w| unsafe { w.addr().bits(buf.ptr as u32) }); + // configure packet size PCKSIZE.SIZE + self.desc.bank0.pcksize.write(|w| { + // FIXME: write raw to pcksize, because byte_count offset + // may be off? Doc table shows 6 bits, but text says 14, + // and arduino shows 14. + unsafe { w.bits(buf.len as u32) } + //unsafe { w.byte_count().bits(buf.len as u8) }; + //unsafe { w.multi_packet_size().bits(0) } + }); + + self.dispatch_retries(token, nak_limit) + } + + pub(crate) fn in_transfer(&mut self, buf: &DataBuf, nak_limit: usize) -> Result<(), PipeErr> { + // Data needs to be word aligned. + assert!((buf.ptr as u32) & 0x3 == 0); + // byte_count section of register is 14 bits. + assert!(buf.len < 16_384); + + logln_now!("p{}: Should IN for {}b.", self.num, buf.len); + // TODO: should just pass pipe and pregs in, probably. TODO: + // merge with stuff in `send_to_pipe` that also does this. + self.desc + .bank0 + .addr + .write(|w| unsafe { w.addr().bits(buf.ptr as u32) }); + self.desc.bank0.pcksize.write(|w| { + // FIXME: see note in `send`. + unsafe { w.bits(buf.len as u32) } + //unsafe { w.byte_count().bits(buf.len as u8) }; + //unsafe { w.multi_packet_size().bits(0) } + }); + + // Possibly set PSTATUS.DTGL? Not sure how this works + // yet. Arduino would set it here if dispatchPkt returned USB_ERROR_DATATOGGLE + //pregs.statusset.write(|w| w.dtgl().set_bit()); + //pregs.statusclr.write(|w| unsafe { + // No function for this. FIXME: need to patch the SVD for + // PSTATUSCLR.DTGL at bit0 + // w.bits(1) + //}); + + self.dispatch_retries(USBToken::In, nak_limit) + } + + pub(crate) fn dispatch_retries( + &mut self, + token: USBToken, + retries: usize, + ) -> Result<(), PipeErr> { + assert!(retries > 0); + + logln_now!("initial regs"); + self.log_regs(); + + let until = rtc::millis() + USB_TIMEOUT; + let mut last_result: Result<(), PipeErr> = Err(PipeErr::SWTimeout); + let mut naks = 0; + while naks <= retries { + logln_now!("p{}: dispatch {:?} retry {}", self.num, token, naks); + + self.dispatch_packet(token); + last_result = self.dispatch_result(token, until); + match last_result { + Ok(_) => return Ok(()), + // FIXME: handle datatoggle + Err(PipeErr::DataToggle) => { + if self.regs.status.read().dtgl().bit_is_set() { + self.regs.statusset.write(|w| w.dtgl().set_bit()); + } else { + self.regs.statusclr.write(|w| unsafe { + // No function for this. FIXME: need to patch the SVD for PSTATUSCLR.DTGL at bit0 + w.bits(1) + }); + } + } + Err(PipeErr::SWTimeout) => break, + Err(PipeErr::Stall) => break, + Err(_) => naks += 1, + } + } + + last_result + } + + fn log_regs(&self) { + // Pipe regs + let cfg = self.regs.cfg.read().bits(); + let bin = self.regs.binterval.read().bits(); + let sts = self.regs.status.read().bits(); + let ifl = self.regs.intflag.read().bits(); + logln_now!( + "p{}: cfg: {:x}, bin: {:x}, sts: {:x}, ifl: {:x}", + self.num, + cfg, + bin, + sts, + ifl + ); + + // Pipe RAM regs + let adr = self.desc.bank0.addr.read().bits(); + let pks = self.desc.bank0.pcksize.read().bits(); + let ext = self.desc.bank0.extreg.read().bits(); + let sbk = self.desc.bank0.status_bk.read().bits(); + let hcp = self.desc.bank0.ctrl_pipe.read().bits(); + let spi = self.desc.bank0.status_pipe.read().bits(); + logln_now!( + "p{}: adr: {:x}, pks: {:x}, ext: {:x}, sbk: {:x}, hcp: {:x}, spi: {:x}", + self.num, + adr, + pks, + ext, + sbk, + hcp, + spi + ); + } + + fn dispatch_packet(&mut self, token: USBToken) { + self.regs + .cfg + .modify(|_, w| unsafe { w.ptoken().bits(token as u8) }); + match token { + USBToken::Setup => { + self.regs.intflag.write(|w| w.txstp().set_bit()); + self.regs.statusset.write(|w| w.bk0rdy().set_bit()); + } + USBToken::In => self.regs.statusclr.write(|w| w.bk0rdy().set_bit()), + USBToken::Out => { + self.regs.intflag.write(|w| w.trcpt0().set_bit()); + self.regs.statusset.write(|w| w.bk0rdy().set_bit()); + } + _ => {} + } + self.regs.statusclr.write(|w| w.pfreeze().set_bit()); + } + + fn dispatch_result(&mut self, token: USBToken, until: usize) -> Result<(), PipeErr> { + while rtc::millis() <= until { + if self.is_transfer_complete(token)? { + return Ok(()); + } else if self.regs.intflag.read().stall().bit_is_set() { + logln_now!("stall"); + self.log_regs(); + self.regs.intflag.write(|w| w.stall().set_bit()); + return Err(PipeErr::Stall); + } else if self.regs.intflag.read().trfail().bit_is_set() { + logln_now!("trfail"); + self.log_regs(); + self.regs.intflag.write(|w| w.trfail().set_bit()); + return Err(PipeErr::TransferFail); + } else if self.desc.bank0.status_bk.read().errorflow().bit_is_set() { + logln_now!("errorflow"); + self.log_regs(); + self.desc + .bank0 + .status_bk + .write(|w| w.errorflow().clear_bit()); + return Err(PipeErr::Flow); + } else if self.desc.bank0.status_pipe.read().touter().bit_is_set() { + logln_now!("touter"); + self.log_regs(); + self.desc + .bank0 + .status_pipe + .write(|w| w.touter().clear_bit()); + return Err(PipeErr::HWTimeout); + } else if self.desc.bank0.status_pipe.read().dtgler().bit_is_set() { + logln_now!("dtgler"); + self.log_regs(); + self.desc + .bank0 + .status_pipe + .write(|w| w.dtgler().clear_bit()); + return Err(PipeErr::DataToggle); + } + } + logln_now!("swtimeout"); + self.log_regs(); + Err(PipeErr::SWTimeout) + } + + fn is_transfer_complete(&mut self, token: USBToken) -> Result { + match token { + USBToken::Setup => { + if self.regs.intflag.read().txstp().bit_is_set() { + self.regs.intflag.write(|w| w.txstp().set_bit()); + self.regs.statusset.write(|w| w.pfreeze().set_bit()); + Ok(true) + } else { + Ok(false) + } + } + USBToken::In => { + if self.regs.intflag.read().trcpt0().bit_is_set() { + self.regs.intflag.write(|w| w.trcpt0().set_bit()); + self.regs.statusset.write(|w| w.pfreeze().set_bit()); + Ok(true) + } else { + Ok(false) + } + } + USBToken::Out => { + if self.regs.intflag.read().trcpt0().bit_is_set() { + self.regs.intflag.write(|w| w.trcpt0().set_bit()); + self.regs.statusset.write(|w| w.pfreeze().set_bit()); + Ok(true) + } else { + Ok(false) + } + } + _ => Err(PipeErr::InvalidToken), + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub(crate) enum USBToken { + Setup = 0, + In = 1, + Out = 2, + Reserved = 3, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub(crate) enum USBPipeType { + Disabled = 0x0, + Control = 0x1, + ISO = 0x2, + Bulk = 0x3, + Interrupt = 0x4, + Extended = 0x5, + _Reserved0 = 0x06, + _Reserved1 = 0x07, +} + +pub(crate) struct DataBuf<'a> { + pub(crate) ptr: *const u8, + pub(crate) len: usize, + _marker: core::marker::PhantomData<&'a ()>, +} +impl DataBuf<'_> {} + +impl core::fmt::Debug for DataBuf<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "DataBuf {{ len: {}, ptr: [", self.len)?; + for i in 0..self.len { + write!(f, " {:x}", unsafe { + *self.ptr.offset(i.try_into().unwrap()) + })?; + } + write!(f, " ] }}") + } +} + +impl<'a, T> From<&'a T> for DataBuf<'a> { + fn from(v: &'a T) -> Self { + Self { + ptr: v as *const T as *const u8, + len: core::mem::size_of::(), + _marker: core::marker::PhantomData, + } + } +} + +pub(crate) struct PipeRegs<'a> { + pub(crate) cfg: &'a mut PCFG, + pub(crate) binterval: &'a mut BINTERVAL, + pub(crate) statusclr: &'a mut PSTATUSCLR, + pub(crate) statusset: &'a mut PSTATUSSET, + pub(crate) status: &'a mut PSTATUS, + pub(crate) intflag: &'a mut PINTFLAG, +} +impl<'a> PipeRegs<'a> { + pub(crate) fn from(host: &'a mut usb::HOST, i: usize) -> PipeRegs { + assert!(i < MAX_PIPES); + match i { + 0 => Self { + cfg: &mut host.pcfg0, + binterval: &mut host.binterval0, + statusclr: &mut host.pstatusclr0, + statusset: &mut host.pstatusset0, + status: &mut host.pstatus0, + intflag: &mut host.pintflag0, + }, + 1 => Self { + cfg: &mut host.pcfg1, + binterval: &mut host.binterval1, + statusclr: &mut host.pstatusclr1, + statusset: &mut host.pstatusset1, + status: &mut host.pstatus1, + intflag: &mut host.pintflag1, + }, + 2 => Self { + cfg: &mut host.pcfg2, + binterval: &mut host.binterval2, + statusclr: &mut host.pstatusclr2, + statusset: &mut host.pstatusset2, + status: &mut host.pstatus2, + intflag: &mut host.pintflag2, + }, + 3 => Self { + cfg: &mut host.pcfg3, + binterval: &mut host.binterval3, + statusclr: &mut host.pstatusclr3, + statusset: &mut host.pstatusset3, + status: &mut host.pstatus3, + intflag: &mut host.pintflag3, + }, + 4 => Self { + cfg: &mut host.pcfg4, + binterval: &mut host.binterval4, + statusclr: &mut host.pstatusclr4, + statusset: &mut host.pstatusset4, + status: &mut host.pstatus4, + intflag: &mut host.pintflag4, + }, + 5 => Self { + cfg: &mut host.pcfg5, + binterval: &mut host.binterval5, + statusclr: &mut host.pstatusclr5, + statusset: &mut host.pstatusset5, + status: &mut host.pstatus5, + intflag: &mut host.pintflag5, + }, + 6 => Self { + cfg: &mut host.pcfg6, + binterval: &mut host.binterval6, + statusclr: &mut host.pstatusclr6, + statusset: &mut host.pstatusset6, + status: &mut host.pstatus6, + intflag: &mut host.pintflag6, + }, + 7 => Self { + cfg: &mut host.pcfg7, + binterval: &mut host.binterval7, + statusclr: &mut host.pstatusclr7, + statusset: &mut host.pstatusset7, + status: &mut host.pstatus7, + intflag: &mut host.pintflag7, + }, + _ => unreachable!(), + } + } +} + +// §32.8.7.1 +#[derive(Clone, Copy, Debug)] +pub(crate) struct PipeDesc { + pub bank0: BankDesc, + pub bank1: BankDesc, +} + +// 2 banks: 32 bytes per pipe. +impl PipeDesc { + pub fn new() -> Self { + Self { + bank0: BankDesc::new(), + bank1: BankDesc::new(), + } + } +} + +#[derive(Clone, Copy, Debug)] +#[repr(C, packed)] +// 16 bytes per bank. +pub(crate) struct BankDesc { + pub addr: Addr, + pub pcksize: PckSize, + pub extreg: ExtReg, + pub status_bk: StatusBk, + pub ctrl_pipe: CtrlPipe, + pub status_pipe: StatusPipe, + + _reserved: u8, +} + +impl BankDesc { + fn new() -> Self { + Self { + addr: Addr::from(0), + pcksize: PckSize::from(0), + extreg: ExtReg::from(0), + status_bk: StatusBk::from(0), + ctrl_pipe: CtrlPipe::from(0), + status_pipe: StatusPipe::from(0), + _reserved: 0, + } + } +} -- cgit v1.2.3