aboutsummaryrefslogtreecommitdiffstats
path: root/usbh/src/device.rs
diff options
context:
space:
mode:
authorBrian Cully <bjc@kublai.com>2019-08-01 21:16:07 -0400
committerBrian Cully <bjc@kublai.com>2019-08-01 21:16:07 -0400
commitc2119f219eb538fa3ebcb942e99f615a7b1be266 (patch)
treec693ab90991c434f51dd8f35e2ce967759ec11e2 /usbh/src/device.rs
parent7f3601e081f71776d3b5f9fc58d5e1adda7772a3 (diff)
downloadsamd21-demo-c2119f219eb538fa3ebcb942e99f615a7b1be266.tar.gz
samd21-demo-c2119f219eb538fa3ebcb942e99f615a7b1be266.zip
Refactor so Endpoint controls pipe characteristics.
Endpoints know their own address, number, max packet size, and data toggle states. When a pipe is needed, pass in the endpoint its needed for so that configuration can happen according to that endpoint.
Diffstat (limited to 'usbh/src/device.rs')
-rw-r--r--usbh/src/device.rs183
1 files changed, 134 insertions, 49 deletions
diff --git a/usbh/src/device.rs b/usbh/src/device.rs
index 6efc395..aba3919 100644
--- a/usbh/src/device.rs
+++ b/usbh/src/device.rs
@@ -1,9 +1,12 @@
-use super::pipe::{Pipe, PipeErr, PipeTable, NO_DATA_STAGE};
+use super::pipe::{PipeErr, PipeTable, NO_DATA_STAGE};
use super::usbproto::*;
use core::convert::TryInto;
use log::{debug, error, info, trace};
+// TODO: impl Drop for Device/Endpoint cleanup if any ends up being
+// required.
+
// FIXME: once again, this doesn't belong here. The issue is that
// we're using `pipe_for`, which requires it.
use atsamd_hal::target_device::usb;
@@ -46,12 +49,20 @@ pub(crate) struct DeviceTable {
// putting in an offset, which can be forgotten, it's better to let
// something else handle address assignment.
impl DeviceTable {
- pub(crate) fn new() -> Self {
- // TODO: should we create addr 0 here for use during
- // enumeration? Probably.
- Self {
- devices: Default::default(),
- }
+ pub(crate) fn new<F>(millis: &'static F) -> Self
+ where
+ F: Fn() -> usize + 'static,
+ {
+ let mut devices: [Option<Device>; MAX_DEVICES] = {
+ let mut devs: [core::mem::MaybeUninit<Option<Device>>; MAX_DEVICES] =
+ unsafe { core::mem::MaybeUninit::uninit().assume_init() };
+ for d in &mut devs[..] {
+ unsafe { core::ptr::write(d.as_mut_ptr(), None) }
+ }
+ unsafe { core::mem::transmute(devs) }
+ };
+ devices[0] = Some(Device::new(0, 8, millis));
+ Self { devices: devices }
}
/// Return the device at address `addr`.
@@ -66,11 +77,15 @@ impl DeviceTable {
/// Allocate a device with the next available address.
// TODO: get rid of the millis argument somehow, but the device
// does need a way of tracking time for Settle reasons.
- pub(crate) fn next(&mut self, millis: &'static dyn Fn() -> usize) -> Option<&mut Device> {
+ pub(crate) fn next(
+ &mut self,
+ max_packet_size: u8,
+ millis: &'static dyn Fn() -> usize,
+ ) -> Option<&mut Device> {
for i in 1..self.devices.len() {
if self.devices[i].is_none() {
let a = i.try_into().unwrap();
- let d = Device::new(a, millis);
+ let d = Device::new(a, max_packet_size, millis);
self.devices[i] = Some(d);
return self.device_for(a);
}
@@ -109,16 +124,35 @@ pub struct Device {
pub addr: u8,
pub max_packet_size: u8,
pub endpoints: [Option<Endpoint>; MAX_ENDPOINTS],
+ pub ep0: Endpoint,
state: FSM,
millis: &'static dyn Fn() -> usize,
}
impl Device {
- fn new(addr: u8, millis: &'static dyn Fn() -> usize) -> Self {
+ // TODO: get max packet size from device descriptor.
+ pub fn new(addr: u8, max_packet_size: u8, millis: &'static dyn Fn() -> usize) -> Self {
+ // Set up endpoints array with 0 as default control endpoint.
+ let endpoints: [Option<Endpoint>; MAX_ENDPOINTS] = {
+ let mut eps: [core::mem::MaybeUninit<Option<Endpoint>>; MAX_ENDPOINTS] =
+ unsafe { core::mem::MaybeUninit::uninit().assume_init() };
+ for ep in &mut eps[..] {
+ unsafe { core::ptr::write(ep.as_mut_ptr(), None) }
+ }
+ unsafe { core::mem::transmute(eps) }
+ };
+
Self {
addr: addr,
- max_packet_size: 8,
- endpoints: Default::default(),
+ max_packet_size: max_packet_size,
+ endpoints: endpoints,
+ ep0: Endpoint::new(
+ addr,
+ 0,
+ TransferType::Control,
+ TransferDirection::In,
+ max_packet_size,
+ ),
state: FSM::AddressSet,
@@ -141,11 +175,12 @@ impl Device {
FSM::WaitForSettle(until) => {
if (self.millis)() >= until {
// Dunno why we get the device descriptor a second time.
- let mut pipe = pipe_table.pipe_for(host, self.addr, 0);
+ let mut pipe = pipe_table.pipe_for(host, &self.ep0);
let mut vol_descr =
::vcell::VolatileCell::<DeviceDescriptor>::new(Default::default());
pipe.control_transfer(
+ &mut self.ep0,
RequestType::get_descr(),
RequestCode::GetDescriptor,
WValue::from((0, DescriptorType::Device as u8)),
@@ -163,11 +198,12 @@ impl Device {
FSM::GetConfigDescriptor => {
// Get config descriptor with minimal data, to see how much we need to allocate for the full descriptor.
- let mut pipe = pipe_table.pipe_for(host, self.addr, 0);
+ let mut pipe = pipe_table.pipe_for(host, &self.ep0);
let mut vol_descr =
::vcell::VolatileCell::<ConfigurationDescriptor>::new(Default::default());
pipe.control_transfer(
+ &mut self.ep0,
RequestType::get_descr(),
RequestCode::GetDescriptor,
WValue::from((0, DescriptorType::Configuration as u8)),
@@ -183,6 +219,7 @@ impl Device {
let buf: [u8; 64] = [0; 64];
let mut tmp = &buf[..desc.w_total_length as usize];
pipe.control_transfer(
+ &mut self.ep0,
RequestType::get_descr(),
RequestCode::GetDescriptor,
WValue::from((0, DescriptorType::Configuration as u8)),
@@ -195,11 +232,12 @@ impl Device {
}
FSM::SetConfig => {
- let mut pipe = pipe_table.pipe_for(host, self.addr, 0);
+ let mut pipe = pipe_table.pipe_for(host, &self.ep0);
debug!("+++ setting configuration");
let conf: u8 = 1;
pipe.control_transfer(
+ &mut self.ep0,
RequestType::set(),
RequestCode::SetConfiguration,
WValue::from((conf, 0)),
@@ -211,6 +249,7 @@ impl Device {
debug!("+++ setting idle");
pipe.control_transfer(
+ &mut self.ep0,
RequestType::from((
RequestDirection::HostToDevice,
RequestKind::Class,
@@ -227,6 +266,7 @@ impl Device {
debug!("+++ setting report");
let mut report: u8 = 0;
pipe.control_transfer(
+ &mut self.ep0,
RequestType::from((
RequestDirection::HostToDevice,
RequestKind::Class,
@@ -240,7 +280,23 @@ impl Device {
)?;
debug!(" -- report set");
- self.state = FSM::GetReport(3)
+ // Stub in some endpoints until we can parse the
+ // configuration descriptor.
+ self.endpoints[1] = Some(Endpoint::new(
+ self.addr,
+ 1,
+ TransferType::Interrupt,
+ TransferDirection::In,
+ 8,
+ ));
+ self.endpoints[2] = Some(Endpoint::new(
+ self.addr,
+ 2,
+ TransferType::Interrupt,
+ TransferDirection::In,
+ 8,
+ ));
+ self.state = FSM::GetReport(2)
}
FSM::GetReport(0) => self.state = FSM::Steady,
@@ -252,22 +308,12 @@ impl Device {
// get some keyboard reports without further setup.
// EP 1 is boot proto keyboard.
- let mut pipe = pipe_table.pipe_for(host, self.addr, 1);
- pipe.regs.cfg.write(|w| unsafe { w.ptype().bits(0x4) });
- pipe.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)
- });
- self.read_report(&mut pipe, 1);
+ self.read_report(pipe_table, host, 1);
// EP 2 is consumer control keys.
- //let mut pipe = pipe_table.pipe_for(host, self.addr, 2);
- //pipe.regs.cfg.write(|w| unsafe { w.ptype().bits(0x4) });
- //self.read_report(&mut pipe, 2);
+ self.read_report(pipe_table, host, 2);
- self.state = FSM::GetReport(count - 1)
+ self.state = FSM::GetReport(count)
}
FSM::Steady => {}
@@ -275,34 +321,73 @@ impl Device {
Ok(())
}
- fn read_report(&mut self, pipe: &mut Pipe, id: u8) {
- let mut buf: core::mem::MaybeUninit<[u8; 8]> = core::mem::MaybeUninit::uninit();
- match pipe.in_transfer(&mut buf, 15, self.millis) {
- Ok(bytes_received) => {
- let tmp = unsafe { &(buf.assume_init())[..bytes_received] };
- info!("report {}: {:?}", id, tmp);
- }
+ fn read_report(&mut self, pipe_table: &mut PipeTable, host: &mut usb::HOST, id: u8) {
+ if let Some(ref mut ep) = self.endpoints[id as usize] {
+ let mut pipe = pipe_table.pipe_for(host, ep);
+ let mut buf: core::mem::MaybeUninit<[u8; 8]> = core::mem::MaybeUninit::uninit();
+ match pipe.in_transfer(ep, &mut buf, 15, self.millis) {
+ Ok(bytes_received) => {
+ let tmp = unsafe { &(buf.assume_init())[..bytes_received] };
+ info!("report {}: {:?}", id, tmp);
+ }
- Err(e) => info!("error {}: {:?}", id, e),
+ Err(e) => trace!("error {}: {:?}", id, e),
+ }
+ } else {
+ error!("endpoint {} doesn't exist!", id)
}
}
}
+// TransferType (INTERRUPT)
+// Direction (IN)
pub struct Endpoint {
- num: u8,
- typ: EndpointType,
+ // This just points back to the address because we need to know it
+ // for all endpoint operations, but we don't want to pass the
+ // address struct (which contains all endpoints) around.
+ pub addr: u8,
+ pub num: u8,
+ pub transfer_type: TransferType,
+ pub direction: TransferDirection,
+ pub in_toggle: bool,
+ pub out_toggle: bool,
+ pub max_packet_size: u8,
}
-impl Default for Endpoint {
- fn default() -> Self {
+
+// cf ยง9.6.6 of USB 2.0
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum TransferDirection {
+ Out,
+ In,
+}
+
+// ibid
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum TransferType {
+ Control = 0,
+ Isochronous = 1,
+ Bulk = 2,
+ Interrupt = 3,
+}
+
+impl Endpoint {
+ // TODO: direction is ignored on control endpoints. Try to remove
+ // it from the API in those cases as well.
+ pub fn new(
+ addr: u8,
+ num: u8,
+ transfer_type: TransferType,
+ direction: TransferDirection,
+ max_packet_size: u8,
+ ) -> Self {
Self {
- num: 0,
- typ: EndpointType::Control,
+ addr: addr,
+ num: num,
+ transfer_type: transfer_type,
+ direction: direction,
+ in_toggle: false,
+ out_toggle: false,
+ max_packet_size: max_packet_size,
}
}
}
-
-enum EndpointType {
- Control,
- In,
- Out,
-}