aboutsummaryrefslogtreecommitdiffstats
path: root/usbh/src/pipe.rs
diff options
context:
space:
mode:
Diffstat (limited to 'usbh/src/pipe.rs')
-rw-r--r--usbh/src/pipe.rs90
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,