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 super::usbproto::*; use atsamd_hal::target_device::usb::{ self, host::{BINTERVAL, PCFG, PINTFLAG, PSTATUS, PSTATUSCLR, PSTATUSSET}, }; use core::convert::TryInto; use log::{debug, trace}; // 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; const NAK_LIMIT: usize = 15; #[derive(Copy, Clone, Debug, PartialEq)] pub(crate) enum PipeErr { ShortPacket, InvalidPipe, InvalidToken, Stall, TransferFail, PipeErr, Flow, HWTimeout, DataToggle, SWTimeout, Other(&'static str), } impl From<&'static str> for PipeErr { fn from(v: &'static str) -> Self { Self::Other(v) } } 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]; trace!("setting paddr of pipe {} to {}:{}", i, addr, ep); pdesc.bank0.ctrl_pipe.write(|w| { w.pdaddr().set_addr(addr); w.pepnum().set_epnum(ep) }); 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 control_req( &mut self, bm_request_type: RequestType, b_request: RequestCode, w_value: WValue, w_index: u16, buf: Option, millis: &dyn Fn() -> usize, ) -> Result<(), PipeErr> { if let Some(ref b) = buf { assert!(b.ptr as usize & 0x3 == 0); assert!(b.len <= 65_535); } // Pipe data toggles for control pipes defined in SAMD21 data // sheet §32.6.3.9 /* * Setup stage. */ let mut setup_packet = SetupPacket { bm_request_type: bm_request_type, b_request: b_request, w_value: w_value, w_index: w_index, w_length: match buf { None => 0, Some(ref b) => b.len as u16, }, }; self.dtgl_clear(); self.send( USBToken::Setup, &DataBuf::from(&mut setup_packet), NAK_LIMIT, millis, )?; /* * Data stage. */ self.dtgl_set(); if let Some(b) = buf { // TODO: data stage, has up to 5,000ms (in 500ms // per-packet chunks) to complete. cf §9.2.6.4 of USB 2.0. match bm_request_type.direction()? { RequestDirection::DeviceToHost => { trace!("buf0: {:?}", &b); self.in_transfer(&b, NAK_LIMIT, millis)?; trace!("buf1: {:?}", &b); } RequestDirection::HostToDevice => { debug!("Should OUT for {}b", b.len); } } } /* * Status stage. */ // TODO: status stage has up to 50ms to complete. cf §9.2.6.4 // of USB 2.0. self.dtgl_set(); self.desc.bank0.pcksize.write(|w| { unsafe { w.byte_count().bits(0) }; unsafe { w.multi_packet_size().bits(0) } }); let token = match bm_request_type.direction()? { RequestDirection::DeviceToHost => USBToken::Out, RequestDirection::HostToDevice => USBToken::In, }; // TODO: should probably make `pipe.send` have optional // `DataBuf`, rather than exposing `dispatch_retries`. trace!("dispatching status stage"); self.dispatch_retries(token, NAK_LIMIT, millis)?; Ok(()) } fn send( &mut self, token: USBToken, buf: &DataBuf, nak_limit: usize, millis: &dyn Fn() -> 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); trace!("p{}: sending {:?}", self.num, buf); 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| { unsafe { w.byte_count().bits(buf.len as u16) }; unsafe { w.multi_packet_size().bits(0) } }); self.dispatch_retries(token, nak_limit, millis) } pub(crate) fn in_transfer( &mut self, buf: &DataBuf, nak_limit: usize, millis: &dyn Fn() -> usize, ) -> Result<(usize), PipeErr> { // TODO: pull this from pipe descriptor for this addr/ep. let packet_size = 8; trace!("p{}: Should IN for {}b.", self.num, buf.len); self.desc.bank0.pcksize.write(|w| { unsafe { w.byte_count().bits(buf.len as u16) }; unsafe { w.multi_packet_size().bits(0) } }); // Read until we get a short packet (indicating that there's // nothing left for us in this transaction) or the buffer is // full. let mut bytes_received = 0; while bytes_received < buf.len { // Move the buffer pointer forward as we get data. self.desc .bank0 .addr .write(|w| unsafe { w.addr().bits(buf.ptr as u32 + bytes_received as u32) }); self.regs.statusclr.write(|w| w.bk0rdy().set_bit()); self.dispatch_retries(USBToken::In, nak_limit, millis)?; let recvd = self.desc.bank0.pcksize.read().byte_count().bits() as usize; bytes_received += recvd; trace!("!! read {} of {}", bytes_received, buf.len); if recvd < packet_size { // If we receive a short packet, we should be done // here. It /may/ be possible to get a short packet // and continue, but only if recvd is a word-sized // multiple. I'm going to assume, for simplicity's // sake, that that's not possible. break; } // Don't allow writing past the buffer. assert!(bytes_received <= buf.len); } //self.dtgl(); self.regs.statusset.write(|w| w.pfreeze().set_bit()); if bytes_received < buf.len { self.log_regs(); // TODO: honestly, this is probably a panic condition, // since whatever's in DataBuf.ptr is totally // invalid. Alternately, this function should be declared // `unsafe`. Err(PipeErr::ShortPacket) } else { Ok(bytes_received) } } fn dtgl_set(&mut self) { self.regs.statusset.write(|w| w.dtgl().set_bit()); } fn dtgl_clear(&mut self) { self.regs.statusclr.write(|w| unsafe { // No function for this. FIXME: need to patch the SVD for // PSTATUSCLR.DTGL at bit0. No? This is in the SVD, but // not the rust output. w.bits(1) }); } fn dtgl(&mut self) { // TODO: this makes no sense to me, and docs are unclear. If // the status bit is set, set it again? if it's clear then // clear it? This is what I get for having to work from // Arduino sources. trace!( "~~~ curbk: {}, dtgl: {}", self.regs.status.read().curbk().bit(), self.regs.status.read().dtgl().bit() ); if self.regs.status.read().dtgl().bit_is_set() { self.dtgl_set(); } else { self.dtgl_clear(); } } // This is the only function that calls `millis`. If we can make // this just take the current timestamp, we can make this // non-blocking. pub(crate) fn dispatch_retries( &mut self, token: USBToken, retries: usize, millis: &dyn Fn() -> usize, ) -> Result<(), PipeErr> { assert!(retries > 0); trace!("initial regs"); self.log_regs(); self.dispatch_packet(token); let until = millis() + USB_TIMEOUT; let mut last_err = PipeErr::SWTimeout; let mut naks = 0; while millis() < until { match self.dispatch_result(token) { Ok(true) => return Ok(()), Ok(false) => continue, Err(e) => { last_err = e; match last_err { PipeErr::DataToggle => self.dtgl(), PipeErr::Stall => break, _ => { naks += 1; if naks > retries { break; } else { self.dispatch_packet(token); } } } } } } trace!("dispatch err: {:?}", last_err); self.log_regs(); Err(last_err) } 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) -> Result { if self.is_transfer_complete(token)? { Ok(true) } else if self.regs.intflag.read().stall().bit_is_set() { self.regs.intflag.write(|w| w.stall().set_bit()); Err(PipeErr::Stall) } else if self.regs.intflag.read().trfail().bit_is_set() { self.regs.intflag.write(|w| w.trfail().set_bit()); Err(PipeErr::TransferFail) } else if self.regs.intflag.read().perr().bit_is_set() { self.regs.intflag.write(|w| w.perr().set_bit()); Err(PipeErr::PipeErr) } else if self.desc.bank0.status_bk.read().errorflow().bit_is_set() { self.desc .bank0 .status_bk .write(|w| w.errorflow().clear_bit()); Err(PipeErr::Flow) } else if self.desc.bank0.status_pipe.read().touter().bit_is_set() { self.desc .bank0 .status_pipe .write(|w| w.touter().clear_bit()); Err(PipeErr::HWTimeout) } else if self.desc.bank0.status_pipe.read().dtgler().bit_is_set() { self.desc .bank0 .status_pipe .write(|w| w.dtgler().clear_bit()); Err(PipeErr::DataToggle) } else { // Nothing wrong, but not done yet. Ok(false) } } 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), } } 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(); trace!( "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(); trace!( "p{}: adr: {:x}, pks: {:x}, ext: {:x}, sbk: {:x}, hcp: {:x}, spi: {:x}", self.num, adr, pks, ext, sbk, hcp, spi ); } } #[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: *mut 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 mut T> for DataBuf<'a> { fn from(v: &'a mut T) -> Self { Self { ptr: v as *mut T as *mut 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, } } }