use super::pipe::{DataBuf, Pipe, PipeErr, PipeTable}; use super::usbproto::*; use core::convert::TryInto; use log::{debug, error, info, 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 = 16; // 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 = 2; #[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( RequestType::get_descr(), RequestCode::GetDescriptor, WValue::from((0, DescriptorType::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( RequestType::get_descr(), RequestCode::GetDescriptor, WValue::from((0, DescriptorType::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( RequestType::get_descr(), RequestCode::GetDescriptor, WValue::from((0, DescriptorType::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( RequestType::set(), RequestCode::SetConfiguration, WValue::from((conf, 0)), 0, None, self.millis, )?; debug!(" -- configuration set"); debug!("+++ setting idle"); pipe.control_req( RequestType::from(( USBSetupDirection::HostToDevice, USBSetupType::Class, USBSetupRecipient::Interface, )), RequestCode::GetInterface, // This is also idle, but can't have two enums with the same value. WValue::from((0, 0)), 0, None, self.millis, )?; debug!(" -- idle set"); debug!("+++ setting report"); let mut rep_res: u8 = 0; pipe.control_req( RequestType::from(( USBSetupDirection::HostToDevice, USBSetupType::Class, USBSetupRecipient::Interface, )), RequestCode::SetConfiguration, WValue::from((0, 2)), 0, Some(DataBuf::from(&mut rep_res)), self.millis, )?; debug!(" -- report set: {}", rep_res); self.state = FSM::Steady } FSM::Steady => { // For now, just do an IN transfer to see if we can // 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); // 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); } } Ok(()) } fn read_report(&mut self, pipe: &mut Pipe, id: u8) { let mut buf: core::mem::MaybeUninit<[u8; 64]> = core::mem::MaybeUninit::uninit(); let mut db = DataBuf::from(&mut buf); match pipe.in_transfer(&mut db, 15, self.millis) { Ok(bytes_received) => { let tmp = unsafe { &(buf.assume_init())[..bytes_received] }; info!("report {}: {:?}", id, tmp); } Err(e) => info!("error {}: {:?}", id, e), } } } 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, }