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; // 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 for Error { fn from(e: PipeErr) -> Self { Self::PipeErr(e) } } enum FSM { AddressSet, WaitForSettle(usize), GetConfigDescriptor, SetConfig, Steady, } pub(crate) struct DeviceTable { devices: [Option; 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(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(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 { None } } /// 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> { 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); self.devices[i] = Some(d); return self.device_for(a); } } None } /// Remove the device at address `addr`. pub(crate) fn remove(&mut self, addr: u8) -> Option { 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 = 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 { pub addr: u8, pub max_packet_size: u8, pub endpoints: [Option; MAX_ENDPOINTS], state: FSM, millis: &'static dyn Fn() -> usize, } impl Device { fn new(addr: u8, millis: &'static dyn Fn() -> usize) -> Self { Self { 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::::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::::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(()) } } pub struct Endpoint { num: u8, typ: EndpointType, } impl Default for Endpoint { fn default() -> Self { Self { num: 0, typ: EndpointType::Control, } } } enum EndpointType { Control, In, Out, }