diff options
author | Brian Cully <bjc@kublai.com> | 2019-07-25 09:40:31 -0400 |
---|---|---|
committer | Brian Cully <bjc@kublai.com> | 2019-07-25 10:14:46 -0400 |
commit | 7966be1419d3eaa088ed83f95282383182585640 (patch) | |
tree | 3b7b09eb768d47e327035301f53f3bd23b7596f2 /app/src/usb/pipe.rs | |
parent | 5b48a5c2f5526f088187fa341f64368994c9f517 (diff) | |
download | samd21-demo-7966be1419d3eaa088ed83f95282383182585640.tar.gz samd21-demo-7966be1419d3eaa088ed83f95282383182585640.zip |
Move USB host stuff to separate crate.
Diffstat (limited to 'app/src/usb/pipe.rs')
-rw-r--r-- | app/src/usb/pipe.rs | 493 |
1 files changed, 0 insertions, 493 deletions
diff --git a/app/src/usb/pipe.rs b/app/src/usb/pipe.rs deleted file mode 100644 index 2efb29b..0000000 --- a/app/src/usb/pipe.rs +++ /dev/null @@ -1,493 +0,0 @@ -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::rtc; - -use core::convert::TryInto; -use log::info; - -// 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]; - - info!("setting paddr of pipe {} to {}:{}", i, addr, ep); - info!("cpipe0: {:x}", pdesc.bank0.ctrl_pipe.read().bits()); - pdesc.bank0.ctrl_pipe.write(|w| { - w.pdaddr().set_addr(addr); - w.pepnum().set_epnum(ep) - }); - info!("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); - - info!("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); - - info!("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); - - info!("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 { - info!("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(); - info!( - "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(); - info!( - "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() { - info!("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() { - info!("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() { - info!("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() { - info!("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() { - info!("dtgler"); - self.log_regs(); - self.desc - .bank0 - .status_pipe - .write(|w| w.dtgler().clear_bit()); - return Err(PipeErr::DataToggle); - } - } - info!("swtimeout"); - self.log_regs(); - Err(PipeErr::SWTimeout) - } - - fn is_transfer_complete(&mut self, token: USBToken) -> Result<bool, PipeErr> { - 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::<T>(), - _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)] -// 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, - } - } -} |