diff options
author | Brian Cully <bjc@kublai.com> | 2019-07-27 18:39:14 -0400 |
---|---|---|
committer | Brian Cully <bjc@kublai.com> | 2019-07-27 18:39:14 -0400 |
commit | 2b6941ef833cd9cfbc403d52f388065fc8d065cb (patch) | |
tree | 3177bc112c1d3ef784b3da0d66aab32c27fa18e4 /usbh/src/device.rs | |
parent | 5759c640e06f0223e958ac49793dcf8a4af1c990 (diff) | |
download | samd21-demo-2b6941ef833cd9cfbc403d52f388065fc8d065cb.tar.gz samd21-demo-2b6941ef833cd9cfbc403d52f388065fc8d065cb.zip |
Move post-address device config to Device impl.
Diffstat (limited to 'usbh/src/device.rs')
-rw-r--r-- | usbh/src/device.rs | 185 |
1 files changed, 171 insertions, 14 deletions
diff --git a/usbh/src/device.rs b/usbh/src/device.rs index 3bb0304..ac48c14 100644 --- a/usbh/src/device.rs +++ b/usbh/src/device.rs @@ -1,20 +1,56 @@ +use super::pipe::{DataBuf, PipeErr, PipeTable}; +use super::usbproto::*; + use core::convert::TryInto; +use log::{debug, error, trace}; + +// 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; const MAX_DEVICES: usize = 16; const MAX_ENDPOINTS: usize = 8; -pub struct DeviceTable { +// How long to wait before talking to the device again after setting +// its address. cf ยง9.2.6.3 of USB 2.0 +const SETTLE_DELAY: usize = 300; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub(crate) enum Error { + PipeErr(PipeErr), +} +impl From<PipeErr> for Error { + fn from(e: PipeErr) -> Self { + Self::PipeErr(e) + } +} + +enum FSM { + AddressSet, + WaitForSettle(usize), + GetConfigDescriptor, + SetConfig, + Steady, +} + +pub(crate) struct DeviceTable { devices: [Option<Device>; MAX_DEVICES], } +// TODO: Untie device address from table index. Right now it's wasting +// a table slot because addr 0 isn't used here. And rather than just +// putting in an offset, which can be forgotten, it's better to let +// something else handle address assignment. impl DeviceTable { - pub fn new() -> Self { + pub(crate) fn new() -> Self { + // TODO: should we create addr 0 here for use during + // enumeration? Probably. Self { devices: Default::default(), } } /// Return the device at address `addr`. - pub fn device_for(&mut self, addr: u8) -> Option<&mut Device> { + pub(crate) fn device_for(&mut self, addr: u8) -> Option<&mut Device> { if let Some(ref mut d) = self.devices[addr as usize] { Some(d) } else { @@ -23,12 +59,13 @@ impl DeviceTable { } /// Allocate a device with the next available address. - pub fn next(&mut self) -> Option<&mut Device> { - for i in 0..self.devices.len() { + // 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> { + for i in 1..self.devices.len() { if self.devices[i].is_none() { let a = i.try_into().unwrap(); - let mut d: Device = Default::default(); - d.addr = a; + let d = Device::new(a, millis); self.devices[i] = Some(d); return self.device_for(a); } @@ -37,24 +74,144 @@ impl DeviceTable { } /// Remove the device at address `addr`. - pub fn remove(&mut self, addr: u8) -> Option<Device> { + pub(crate) fn remove(&mut self, addr: u8) -> Option<Device> { let v = core::mem::replace(&mut self.devices[addr as usize], None); v } + + pub(crate) fn run(&mut self, pipe_table: &mut PipeTable, host: &mut usb::HOST) { + for i in 1..self.devices.len() { + // TODO: Woof, this is ugly, but I'm not sure of a better + // way to avoid mutably borrowing self twice. + let mut remove_addr: Option<u8> = None; + if let Some(ref mut d) = self.devices[i] { + if let Err(e) = d.fsm(pipe_table, host) { + error!("Removing device {}: {:?}", d.addr, e); + remove_addr = Some(d.addr); + } else { + remove_addr = None; + } + } + + if let Some(addr) = remove_addr { + self.remove(addr); + } + } + } } pub struct Device { - addr: u8, - max_packet_size: u8, - endpoints: [Endpoint; MAX_ENDPOINTS], + pub addr: u8, + pub max_packet_size: u8, + pub endpoints: [Option<Endpoint>; MAX_ENDPOINTS], + + state: FSM, + millis: &'static dyn Fn() -> usize, } -impl Default for Device { - fn default() -> Self { +impl Device { + fn new(addr: u8, millis: &'static dyn Fn() -> usize) -> Self { Self { - addr: 0, + addr: addr, max_packet_size: 8, endpoints: Default::default(), + + state: FSM::AddressSet, + + // TODO: This doesn't belong here. Ideally the current + // time is passed in to the FSM routine. + millis: millis, + } + } +} + +impl Device { + pub(crate) fn fsm( + &mut self, + pipe_table: &mut PipeTable, + host: &mut usb::HOST, + ) -> Result<(), Error> { + match self.state { + FSM::AddressSet => self.state = FSM::WaitForSettle((self.millis)() + SETTLE_DELAY), + + 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 vol_descr = + ::vcell::VolatileCell::<USBDeviceDescriptor>::new(Default::default()); + pipe.control_req( + BMRequestType::get_descr(), + USBRequest::GetDescriptor, + WValue::from((0, USBDescriptor::Device as u8)), + 0, + Some(DataBuf::from(&mut vol_descr)), + self.millis, + )?; + + let desc = vol_descr.get(); + trace!(" -- devDesc: {:?}", desc); + + self.state = FSM::GetConfigDescriptor + } + } + + 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 vol_descr = + ::vcell::VolatileCell::<USBConfigurationDescriptor>::new(Default::default()); + pipe.control_req( + BMRequestType::get_descr(), + USBRequest::GetDescriptor, + WValue::from((0, USBDescriptor::Configuration as u8)), + 0, + Some(DataBuf::from(&mut vol_descr)), + self.millis, + )?; + let desc = vol_descr.get(); + debug!("config: {:?}", desc); + + // TODO: do real allocation later. + assert!(desc.w_total_length < 64); + let buf: [u8; 64] = [0; 64]; + let mut tmp = &buf[..desc.w_total_length as usize]; + pipe.control_req( + BMRequestType::get_descr(), + USBRequest::GetDescriptor, + WValue::from((0, USBDescriptor::Configuration as u8)), + 0, + Some(DataBuf::from(&mut tmp)), + self.millis, + )?; + + self.state = FSM::SetConfig + } + + FSM::SetConfig => { + let mut pipe = pipe_table.pipe_for(host, self.addr, 0); + + debug!("+++ setting configuration"); + let conf: u8 = 1; + pipe.control_req( + BMRequestType::set(), + USBRequest::SetConfiguration, + WValue::from((conf, 0)), + 0, + None, + self.millis, + )?; + debug!(" -- configuration set"); + + self.state = FSM::Steady + } + + FSM::Steady => { + // Now we should be able to access it normally. + } } + Ok(()) } } |