From e405c474f5e0e94288191223de7ae0f31ae0b15f Mon Sep 17 00:00:00 2001 From: Brian Cully Date: Sun, 4 Aug 2019 14:19:25 -0400 Subject: Migrate everything over to separate libraries. * `usb-host` is the crate containing the HAL traits. * `bootkbd` is a crate for a bare-bones boot protocol keyboard driver. * `samd21-host` is a crate with an implementation of `usb-host` for a SAMD21 platform, with an example for the trinket-m0 which uses the `bootkbd` driver to print keyboard reports. --- usbh/src/device.rs | 392 ----------------------------------------------------- 1 file changed, 392 deletions(-) delete mode 100644 usbh/src/device.rs (limited to 'usbh/src/device.rs') diff --git a/usbh/src/device.rs b/usbh/src/device.rs deleted file mode 100644 index fd5c1a1..0000000 --- a/usbh/src/device.rs +++ /dev/null @@ -1,392 +0,0 @@ -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) => { - debug!("+++ 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: [u8; 8] = [0; 8]; - match pipe.in_transfer(ep, &mut buf, 15, self.millis) { - Ok(bytes_received) => info!("report {}: {} - {:?}", id, bytes_received, buf), - - Err(PipeErr::Flow) => return, - - 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, - } - } -} -- cgit v1.2.3