diff options
author | Brian Cully <bjc@kublai.com> | 2019-10-08 09:37:21 -0400 |
---|---|---|
committer | Brian Cully <bjc@kublai.com> | 2019-10-08 09:37:21 -0400 |
commit | 22cda6d6b362e47fcd5e1f8589297b43fdcb8204 (patch) | |
tree | c81d918547ccdb4361a62c6c2983c02c44dba3a5 | |
parent | 6d6fd2617ab909987e5ddecfb36519f0667ecf57 (diff) | |
download | atsamd-usb-host-22cda6d6b362e47fcd5e1f8589297b43fdcb8204.tar.gz atsamd-usb-host-22cda6d6b362e47fcd5e1f8589297b43fdcb8204.zip |
Handle all known transfer/pipe errors.
* Remove `PipeErr::Other`.
* Add more variants to `PipeErr`.
* Integrate `is_transfer_complete` into `dispatch_result` and
simplify.
* Cache volatile `intflag` on entry to `dispatch_result` to ensure
consistent handling.
* Clear fail/perr `intflag` bits in `dispatch_result`.
-rw-r--r-- | src/lib.rs | 5 | ||||
-rw-r--r-- | src/pipe.rs | 137 |
2 files changed, 81 insertions, 61 deletions
@@ -543,7 +543,10 @@ impl From<PipeErr> for TransferError { PipeErr::PipeErr => Self::Permanent("pipe error"), PipeErr::HWTimeout => Self::Permanent("hardware timeout"), PipeErr::SWTimeout => Self::Permanent("software timeout"), - PipeErr::Other(s) => Self::Permanent(s), + PipeErr::PID => Self::Permanent("pid error"), + PipeErr::DataPID => Self::Permanent("data pid error"), + PipeErr::CRC16 => Self::Permanent("crc16 error"), + //PipeErr::Other(s) => Self::Permanent(s), } } } diff --git a/src/pipe.rs b/src/pipe.rs index 8739efd..8b10dc5 100644 --- a/src/pipe.rs +++ b/src/pipe.rs @@ -53,12 +53,9 @@ pub(crate) enum PipeErr { HWTimeout, DataToggle, SWTimeout, - Other(&'static str), -} -impl From<&'static str> for PipeErr { - fn from(v: &'static str) -> Self { - Self::Other(v) - } + PID, + DataPID, + CRC16, } pub(crate) struct PipeTable { @@ -182,14 +179,16 @@ impl Pipe<'_, '_> { 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 => { + match bm_request_type.direction() { + Ok(RequestDirection::DeviceToHost) => { transfer_len = self.in_transfer(ep, b, NAK_LIMIT, millis)?; } - RequestDirection::HostToDevice => { + Ok(RequestDirection::HostToDevice) => { transfer_len = self.out_transfer(ep, b, NAK_LIMIT, millis)?; } + + Err(_) => Err(PipeErr::PID)?, } } @@ -203,9 +202,10 @@ impl Pipe<'_, '_> { unsafe { w.multi_packet_size().bits(0) } }); - let token = match bm_request_type.direction()? { - RequestDirection::DeviceToHost => PToken::Out, - RequestDirection::HostToDevice => PToken::In, + let token = match bm_request_type.direction() { + Ok(RequestDirection::DeviceToHost) => PToken::Out, + Ok(RequestDirection::HostToDevice) => PToken::In, + Err(_) => Err(PipeErr::PID)?, }; trace!("dispatching status stage"); @@ -436,8 +436,6 @@ impl Pipe<'_, '_> { self.regs .cfg .modify(|_, w| unsafe { w.ptoken().bits(token as u8) }); - self.regs.intflag.modify(|_, w| w.trfail().set_bit()); - self.regs.intflag.modify(|_, w| w.perr().set_bit()); match token { PToken::Setup => { @@ -477,60 +475,79 @@ impl Pipe<'_, '_> { self.regs.statusclr.write(|w| w.pfreeze().set_bit()); } - 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()); + fn dispatch_result(&mut self, _token: PToken) -> Result<bool, PipeErr> { + let flags = self.regs.intflag.read(); + if flags.txstp().bit_is_set() { + self.regs.intflag.write(|w| w.txstp().set_bit()); Ok(true) - } else if self.desc.bank0.status_bk.read().errorflow().bit_is_set() { - trace!("errorflow"); - self.log_regs(); - Err(PipeErr::Flow) - } else if self.desc.bank0.status_pipe.read().touter().bit_is_set() { - trace!("touter"); - self.log_regs(); - Err(PipeErr::HWTimeout) - } else if self.desc.bank0.status_pipe.read().dtgler().bit_is_set() { - trace!("dtgler"); - self.log_regs(); - Err(PipeErr::DataToggle) - } else if self.regs.intflag.read().trfail().bit_is_set() { - self.regs.intflag.write(|w| w.trfail().set_bit()); - trace!("trfail"); + } else if flags.trcpt0().bit_is_set() { + self.regs.intflag.write(|w| w.trcpt0().set_bit()); + Ok(true) + } else if flags.stall().bit_is_set() { + self.regs.intflag.write(|w| w.stall().set_bit()); + trace!("stall"); self.log_regs(); - Err(PipeErr::TransferFail) + Err(PipeErr::Stall) + } else if flags.trfail().bit_is_set() || flags.perr().bit_is_set() { + self.regs.intflag.write(|w| { + w.trfail().set_bit(); + w.perr().set_bit() + }); + + if self.desc.bank0.status_pipe.read().dtgler().bit_is_set() { + self.desc + .bank0 + .status_pipe + .write(|w| w.dtgler().clear_bit()); + trace!("dtgler"); + self.log_regs(); + Err(PipeErr::DataToggle) + } else if self.desc.bank0.status_bk.read().errorflow().bit_is_set() { + self.desc + .bank0 + .status_bk + .write(|w| w.errorflow().clear_bit()); + trace!("errorflow"); + self.log_regs(); + 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()); + trace!("touter"); + self.log_regs(); + Err(PipeErr::HWTimeout) + } else if self.desc.bank0.status_pipe.read().crc16er().bit_is_set() { + self.desc + .bank0 + .status_pipe + .write(|w| w.crc16er().clear_bit()); + trace!("crc16er"); + self.log_regs(); + Err(PipeErr::CRC16) + } else if self.desc.bank0.status_pipe.read().pider().bit_is_set() { + self.desc.bank0.status_pipe.write(|w| w.pider().clear_bit()); + trace!("pider"); + self.log_regs(); + Err(PipeErr::PID) + } else if self.desc.bank0.status_pipe.read().dapider().bit_is_set() { + self.desc + .bank0 + .status_pipe + .write(|w| w.dapider().clear_bit()); + trace!("dapider"); + self.log_regs(); + Err(PipeErr::DataPID) + } else { + Err(PipeErr::TransferFail) + } } else { // Nothing wrong, but not done yet. Ok(false) } } - fn is_transfer_complete(&mut self, token: PToken) -> Result<bool, PipeErr> { - match token { - PToken::Setup => { - if self.regs.intflag.read().txstp().bit_is_set() { - self.regs.intflag.write(|w| w.txstp().set_bit()); - Ok(true) - } else { - Ok(false) - } - } - PToken::In => { - if self.regs.intflag.read().trcpt0().bit_is_set() { - self.regs.intflag.write(|w| w.trcpt0().set_bit()); - Ok(true) - } else { - Ok(false) - } - } - PToken::Out => { - if self.regs.intflag.read().trcpt0().bit_is_set() { - self.regs.intflag.write(|w| w.trcpt0().set_bit()); - Ok(true) - } else { - Ok(false) - } - } - _ => Err(PipeErr::InvalidToken), } } |