diff options
Diffstat (limited to 'usbh/src/pipe.rs')
-rw-r--r-- | usbh/src/pipe.rs | 90 |
1 files changed, 87 insertions, 3 deletions
diff --git a/usbh/src/pipe.rs b/usbh/src/pipe.rs index af2bc33..be4ca57 100644 --- a/usbh/src/pipe.rs +++ b/usbh/src/pipe.rs @@ -12,13 +12,14 @@ use pck_size::PckSize; use status_bk::StatusBk; use status_pipe::StatusPipe; -use core::convert::TryInto; -use log::trace; +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 @@ -26,6 +27,8 @@ 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, @@ -86,7 +89,88 @@ pub(crate) struct Pipe<'a, 'b> { pub(crate) desc: &'a mut PipeDesc, } impl Pipe<'_, '_> { - pub(crate) fn send( + pub(crate) fn control_req( + &mut self, + bm_request_type: BMRequestType, + b_request: USBRequest, + w_value: WValue, + w_index: u16, + buf: Option<DataBuf>, + 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 = USBSetupPacket { + 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 { + match bm_request_type.direction() { + USBSetupDirection::DeviceToHost => { + trace!("buf0: {:?}", &b); + self.in_transfer(&b, NAK_LIMIT, millis)?; + trace!("buf1: {:?}", &b); + } + + USBSetupDirection::HostToDevice => { + debug!("Should OUT for {}b", b.len); + } + } + } + + /* + * Status stage. + */ + self.dtgl_set(); + self.desc.bank0.pcksize.write(|w| { + unsafe { w.byte_count().bits(0) }; + unsafe { w.multi_packet_size().bits(0) } + }); + + // PSTATUSSET.DTGL set -- TODO: figure out if this is + // necessary. + self.regs.statusset.write(|w| w.dtgl().set_bit()); + + let token = match bm_request_type.direction() { + USBSetupDirection::DeviceToHost => USBToken::Out, + USBSetupDirection::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, |