diff options
Diffstat (limited to 'usbh/src/pipe.rs')
-rw-r--r-- | usbh/src/pipe.rs | 249 |
1 files changed, 176 insertions, 73 deletions
diff --git a/usbh/src/pipe.rs b/usbh/src/pipe.rs index d7b7981..08602a3 100644 --- a/usbh/src/pipe.rs +++ b/usbh/src/pipe.rs @@ -12,6 +12,8 @@ use pck_size::PckSize; use status_bk::StatusBk; use status_pipe::StatusPipe; +use super::device::{Endpoint, TransferType}; + use super::usbproto::*; use atsamd_hal::target_device::usb::{ @@ -60,31 +62,47 @@ pub(crate) struct PipeTable { impl PipeTable { pub(crate) fn new() -> Self { - Self { - tbl: [PipeDesc::new(); MAX_PIPES], - } + let tbl = { + let mut tbl: [core::mem::MaybeUninit<PipeDesc>; MAX_PIPES] = + unsafe { core::mem::MaybeUninit::uninit().assume_init() }; + + for e in &mut tbl[..] { + unsafe { core::ptr::write(e.as_mut_ptr(), PipeDesc::new()) } + } + + unsafe { core::mem::transmute(tbl) } + }; + Self { tbl: tbl } } pub(crate) fn pipe_for<'a, 'b>( &'a mut self, host: &'b mut usb::HOST, - addr: u8, - ep: u8, + endpoint: &Endpoint, ) -> 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 i = if endpoint.num == 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); + pregs.cfg.write(|w| { + let ptype = PType::from(endpoint.transfer_type) as u8; + unsafe { w.ptype().bits(ptype) } + }); + trace!( + "setting paddr of pipe {} to {}:{}", + i, + endpoint.addr, + endpoint.num + ); pdesc.bank0.ctrl_pipe.write(|w| { - w.pdaddr().set_addr(addr); - w.pepnum().set_epnum(ep) + w.pdaddr().set_addr(endpoint.addr); + w.pepnum().set_epnum(endpoint.num) }); Pipe { num: i, @@ -97,12 +115,14 @@ impl PipeTable { // 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_transfer<T>( &mut self, + ep: &mut Endpoint, bm_request_type: RequestType, b_request: RequestCode, w_value: WValue, @@ -110,9 +130,6 @@ impl Pipe<'_, '_> { buf: Option<&mut T>, millis: &dyn Fn() -> usize, ) -> Result<(), PipeErr> { - // Pipe data toggles for control pipes defined in SAMD21 data - // sheet §32.6.3.9 - let len = match buf { None => 0, _ => core::mem::size_of::<T>(), @@ -128,8 +145,8 @@ impl Pipe<'_, '_> { w_index: w_index, w_length: len as u16, }; - self.dtgl_clear(); self.send( + ep, PToken::Setup, &DataBuf::from(&mut setup_packet), NAK_LIMIT, @@ -139,17 +156,16 @@ impl Pipe<'_, '_> { /* * 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 => { - self.in_transfer(b, NAK_LIMIT, millis)?; + self.in_transfer(ep, b, NAK_LIMIT, millis)?; } RequestDirection::HostToDevice => { - self.out_transfer(b, NAK_LIMIT, millis)?; + self.out_transfer(ep, b, NAK_LIMIT, millis)?; } } } @@ -159,7 +175,6 @@ impl Pipe<'_, '_> { */ // 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) } @@ -171,12 +186,14 @@ impl Pipe<'_, '_> { }; trace!("dispatching status stage"); - self.dispatch_retries(token, NAK_LIMIT, millis)?; + self.dispatch_retries(ep, token, NAK_LIMIT, millis)?; + Ok(()) } fn send( &mut self, + ep: &mut Endpoint, token: PToken, buf: &DataBuf, nak_limit: usize, @@ -194,41 +211,12 @@ impl Pipe<'_, '_> { unsafe { w.multi_packet_size().bits(0) } }); - self.dispatch_retries(token, nak_limit, millis) - } - - pub(crate) fn out_transfer<T>( - &mut self, - buf: &mut T, - nak_limit: usize, - millis: &dyn Fn() -> usize, - ) -> Result<usize, PipeErr> { - let db: DataBuf = buf.into(); - - trace!("p{}: Should OUT for {}b.", self.num, db.len); - self.desc.bank0.pcksize.write(|w| { - unsafe { w.byte_count().bits(db.len as u16) }; - unsafe { w.multi_packet_size().bits(0) } - }); - - let mut bytes_sent = 0; - while bytes_sent < db.len { - self.desc - .bank0 - .addr - .write(|w| unsafe { w.addr().bits(db.ptr as u32 + bytes_sent as u32) }); - self.dispatch_retries(PToken::Out, nak_limit, millis)?; - - let sent = self.desc.bank0.pcksize.read().byte_count().bits() as usize; - bytes_sent += sent; - trace!("!! wrote {} of {}", bytes_sent, db.len); - } - - Ok(bytes_sent) + self.dispatch_retries(ep, token, nak_limit, millis) } pub(crate) fn in_transfer<T>( &mut self, + ep: &mut Endpoint, buf: &mut T, nak_limit: usize, millis: &dyn Fn() -> usize, @@ -259,8 +247,10 @@ impl Pipe<'_, '_> { .addr .write(|w| unsafe { w.addr().bits(db.ptr as u32 + bytes_received as u32) }); self.regs.statusclr.write(|w| w.bk0rdy().set_bit()); + trace!("--- !!! regs-pre-dispatch !!! ---"); + self.log_regs(); - self.dispatch_retries(PToken::In, nak_limit, millis)?; + self.dispatch_retries(ep, PToken::In, nak_limit, millis)?; let recvd = self.desc.bank0.pcksize.read().byte_count().bits() as usize; bytes_received += recvd; trace!("!! read {} of {}", bytes_received, db.len); @@ -271,7 +261,6 @@ impl Pipe<'_, '_> { // Don't allow writing past the buffer. assert!(bytes_received <= db.len); } - //self.dtgl(); self.regs.statusset.write(|w| w.pfreeze().set_bit()); if bytes_received < db.len { @@ -288,11 +277,42 @@ impl Pipe<'_, '_> { } } - fn dtgl_set(&mut self) { + pub(crate) fn out_transfer<T>( + &mut self, + ep: &mut Endpoint, + buf: &mut T, + nak_limit: usize, + millis: &dyn Fn() -> usize, + ) -> Result<usize, PipeErr> { + let db: DataBuf = buf.into(); + + trace!("p{}: Should OUT for {}b.", self.num, db.len); + self.desc.bank0.pcksize.write(|w| { + unsafe { w.byte_count().bits(db.len as u16) }; + unsafe { w.multi_packet_size().bits(0) } + }); + + let mut bytes_sent = 0; + while bytes_sent < db.len { + self.desc + .bank0 + .addr + .write(|w| unsafe { w.addr().bits(db.ptr as u32 + bytes_sent as u32) }); + self.dispatch_retries(ep, PToken::Out, nak_limit, millis)?; + + let sent = self.desc.bank0.pcksize.read().byte_count().bits() as usize; + bytes_sent += sent; + trace!("!! wrote {} of {}", bytes_sent, db.len); + } + + Ok(bytes_sent) + } + + pub(crate) fn dtgl_set(&mut self) { self.regs.statusset.write(|w| w.dtgl().set_bit()); } - fn dtgl_clear(&mut self) { + pub(crate) 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 @@ -301,20 +321,27 @@ impl Pipe<'_, '_> { }); } - fn dtgl(&mut self) { + pub(crate) fn dtgl(&mut self, ep: &mut Endpoint, token: PToken) { // 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() + "~~~ tok: {:?}, dtgl: {}, i: {}, o: {}", + token, + self.regs.status.read().dtgl().bit(), + ep.in_toggle, + ep.out_toggle, ); if self.regs.status.read().dtgl().bit_is_set() { + self.dtgl_clear(); + } else { self.dtgl_set(); + } + if token == PToken::In { + ep.in_toggle = self.regs.status.read().dtgl().bit_is_set() } else { - self.dtgl_clear(); + ep.out_toggle = self.regs.status.read().dtgl().bit_is_set() } } @@ -323,31 +350,40 @@ impl Pipe<'_, '_> { // non-blocking. fn dispatch_retries( &mut self, + ep: &mut Endpoint, token: PToken, retries: usize, millis: &dyn Fn() -> usize, ) -> Result<(), PipeErr> { - self.dispatch_packet(token); + self.dispatch_packet(ep, 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(()), + let res = self.dispatch_result(token); + match res { + Ok(true) => { + if token == PToken::In { + ep.in_toggle = self.regs.status.read().dtgl().bit_is_set(); + } else if token == PToken::Out { + ep.out_toggle = self.regs.status.read().dtgl().bit_is_set(); + } + return Ok(()); + } Ok(false) => continue, Err(e) => { last_err = e; match last_err { - PipeErr::DataToggle => self.dtgl(), + PipeErr::DataToggle => self.dtgl(ep, token), PipeErr::Stall => break, _ => { naks += 1; if naks > retries { break; } else { - self.dispatch_packet(token); + self.dispatch_packet(ep, token); } } } @@ -358,46 +394,105 @@ impl Pipe<'_, '_> { Err(last_err) } - fn dispatch_packet(&mut self, token: PToken) { + fn dispatch_packet(&mut self, ep: &mut Endpoint, token: PToken) { self.regs .cfg .modify(|_, w| unsafe { w.ptoken().bits(token as u8) }); - - trace!("initial regs"); - self.log_regs(); + self.regs.intflag.modify(|_, w| w.trfail().set_bit()); + self.regs.intflag.modify(|_, w| w.perr().set_bit()); match token { PToken::Setup => { self.regs.intflag.write(|w| w.txstp().set_bit()); self.regs.statusset.write(|w| w.bk0rdy().set_bit()); + + // Toggles should be 1 for host and function's + // sequence at end of setup transaction. cf §8.6.1 of + // USB 2.0. + self.dtgl_clear(); + ep.in_toggle = true; + ep.out_toggle = true; + } + PToken::In => { + self.regs.statusclr.write(|w| w.bk0rdy().set_bit()); + if ep.in_toggle { + self.dtgl_set(); + } else { + self.dtgl_clear(); + } } - PToken::In => self.regs.statusclr.write(|w| w.bk0rdy().set_bit()), PToken::Out => { self.regs.intflag.write(|w| w.trcpt0().set_bit()); self.regs.statusset.write(|w| w.bk0rdy().set_bit()); + if ep.out_toggle { + self.dtgl_set(); + } else { + self.dtgl_clear(); + } } _ => {} } + + trace!("initial regs"); + self.log_regs(); + self.regs.statusclr.write(|w| w.pfreeze().set_bit()); } + #[cfg(not(feature = "foo"))] fn dispatch_result(&mut self, token: PToken) -> Result<bool, PipeErr> { if self.is_transfer_complete(token)? { + self.regs.statusset.write(|w| w.pfreeze().set_bit()); + Ok(true) + } else if self.regs.intflag.read().trfail().bit_is_set() { + self.regs.intflag.write(|w| w.trfail().set_bit()); + trace!("trfail"); + self.regs.statusset.write(|w| w.pfreeze().set_bit()); + self.log_regs(); + Err(PipeErr::TransferFail) + } else if self.desc.bank0.status_bk.read().errorflow().bit_is_set() { + trace!("errorflow"); + self.regs.statusset.write(|w| w.pfreeze().set_bit()); + self.log_regs(); + Err(PipeErr::Flow) + } else if self.desc.bank0.status_pipe.read().touter().bit_is_set() { + trace!("touter"); + self.regs.statusset.write(|w| w.pfreeze().set_bit()); + self.log_regs(); + Err(PipeErr::HWTimeout) + } else if self.desc.bank0.status_pipe.read().dtgler().bit_is_set() { + trace!("dtgler"); + self.regs.statusset.write(|w| w.pfreeze().set_bit()); + self.log_regs(); + Err(PipeErr::DataToggle) + } else { + // Nothing wrong, but not done yet. + Ok(false) + } + } + + #[cfg(feature = "foo")] + fn dispatch_result(&mut self, token: PToken) -> Result<bool, PipeErr> { + if self.is_transfer_complete(token)? { + self.regs.statusset.write(|w| w.pfreeze().set_bit()); Ok(true) } else if self.regs.intflag.read().stall().bit_is_set() { warn!("stall"); self.log_regs(); self.regs.intflag.write(|w| w.stall().set_bit()); + self.regs.statusset.write(|w| w.pfreeze().set_bit()); Err(PipeErr::Stall) } else if self.regs.intflag.read().trfail().bit_is_set() { warn!("trfail"); self.log_regs(); self.regs.intflag.write(|w| w.trfail().set_bit()); + self.regs.statusset.write(|w| w.pfreeze().set_bit()); Err(PipeErr::TransferFail) } else if self.regs.intflag.read().perr().bit_is_set() { warn!("perr"); self.log_regs(); self.regs.intflag.write(|w| w.perr().set_bit()); + self.regs.statusset.write(|w| w.pfreeze().set_bit()); Err(PipeErr::PipeErr) } else if self.desc.bank0.status_bk.read().errorflow().bit_is_set() { warn!("errorflow"); @@ -406,6 +501,7 @@ impl Pipe<'_, '_> { .bank0 .status_bk .write(|w| w.errorflow().clear_bit()); + self.regs.statusset.write(|w| w.pfreeze().set_bit()); Err(PipeErr::Flow) } else if self.desc.bank0.status_pipe.read().touter().bit_is_set() { warn!("touter"); @@ -414,6 +510,7 @@ impl Pipe<'_, '_> { .bank0 .status_pipe .write(|w| w.touter().clear_bit()); + self.regs.statusset.write(|w| w.pfreeze().set_bit()); Err(PipeErr::HWTimeout) } else if self.desc.bank0.status_pipe.read().dtgler().bit_is_set() { warn!("dtgler"); @@ -422,6 +519,7 @@ impl Pipe<'_, '_> { .bank0 .status_pipe .write(|w| w.dtgler().clear_bit()); + self.regs.statusset.write(|w| w.pfreeze().set_bit()); Err(PipeErr::DataToggle) } else { // Nothing wrong, but not done yet. @@ -434,7 +532,6 @@ impl Pipe<'_, '_> { PToken::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) @@ -443,7 +540,6 @@ impl Pipe<'_, '_> { PToken::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) @@ -452,7 +548,6 @@ impl Pipe<'_, '_> { PToken::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) @@ -518,6 +613,16 @@ pub(crate) enum PType { _Reserved0 = 0x06, _Reserved1 = 0x07, } +impl From<TransferType> for PType { + fn from(v: TransferType) -> Self { + match v { + TransferType::Control => Self::Control, + TransferType::Isochronous => Self::ISO, + TransferType::Bulk => Self::Bulk, + TransferType::Interrupt => Self::Interrupt, + } + } +} struct DataBuf<'a> { ptr: *mut u8, @@ -630,7 +735,6 @@ impl<'a> PipeRegs<'a> { } // §32.8.7.1 -#[derive(Clone, Copy, Debug)] pub(crate) struct PipeDesc { pub bank0: BankDesc, pub bank1: BankDesc, @@ -646,7 +750,6 @@ impl PipeDesc { } } -#[derive(Clone, Copy, Debug)] #[repr(C, packed)] // 16 bytes per bank. pub(crate) struct BankDesc { |