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 { 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)] // 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, } } }