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; const MAX_DEVICES: usize = 16; // TODO: // This may be wrong. It may be 15 additional input + 15 additional // output! cf §5.3.1.2 of USB 2.0. 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, GetReport(usize), 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(millis: &'static F) -> Self where F: Fn() -> usize + 'static, { let mut devices: [Option; MAX_DEVICES] = { let mut devs: [core::mem::MaybeUninit>; 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`. 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, 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, max_packet_size, 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], pub ep0: Endpoint, state: FSM, millis: &'static dyn Fn() -> usize, } impl Device { // 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; MAX_ENDPOINTS] = { let mut eps: [core::mem::MaybeUninit>; 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: max_packet_size, endpoints: endpoints, ep0: Endpoint::new( addr, 0, TransferType::Control, TransferDirection::In, max_packet_size, ), 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.ep0); let mut vol_descr = ::vcell::VolatileCell::::new(Default::default()); pipe.control_transfer( &mut self.ep0, RequestType::get_descr(), RequestCode::GetDescriptor, WValue::from((0, DescriptorType::Device as u8)), 0, Some(&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.ep0); let mut vol_descr = ::vcell::VolatileCell::::new(Default::default()); pipe.control_transfer( &mut self.ep0, RequestType::get_descr(), RequestCode::GetDescriptor, WValue::from((0, DescriptorType::Configuration as u8)), 0, Some(&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_transfer( &mut self.ep0, RequestType::get_descr(), RequestCode::GetDescriptor, WValue::from((0, DescriptorType::Configuration as u8)), 0, Some(&mut tmp), self.millis, )?; self.state = FSM::SetConfig } FSM::SetConfig => { 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)), 0, NO_DATA_STAGE, self.millis, )?; debug!(" -- configuration set"); debug!("+++ setting idle"); pipe.control_transfer( &mut self.ep0, RequestType::from(( RequestDirection::HostToDevice, RequestKind::Class, RequestRecipient::Interface, )), RequestCode::GetInterface, // This is also idle, but can't have two enums with the same value. WValue::from((0, 0)), 0, NO_DATA_STAGE, self.millis, )?; debug!(" -- idle set"); debug!("+++ setting report"); let mut report: u8 = 0; pipe.control_transfer( &mut self.ep0, RequestType::from(( RequestDirection::HostToDevice, RequestKind::Class, RequestRecipient::Interface, )), RequestCode::SetConfiguration, WValue::from((0, 2)), 0, Some(&mut report), self.millis, )?; debug!(" -- report set"); // 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, FSM::GetReport(count) => { info!("+++ getting report {}", count); // 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. self.read_report(pipe_table, host, 1); // EP 2 is consumer control keys. self.read_report(pipe_table, host, 2); self.state = FSM::GetReport(count) } FSM::Steady => {} } Ok(()) } 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) => trace!("error {}: {:?}", id, e), } } else { error!("endpoint {} doesn't exist!", id) } } } // TransferType (INTERRUPT) // Direction (IN) pub struct Endpoint { // 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, } // 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 { addr: addr, num: num, transfer_type: transfer_type, direction: direction, in_toggle: false, out_toggle: false, max_packet_size: max_packet_size, } } }