diff options
author | Brian Cully <bjc@kublai.com> | 2019-08-01 21:16:07 -0400 |
---|---|---|
committer | Brian Cully <bjc@kublai.com> | 2019-08-01 21:16:07 -0400 |
commit | c2119f219eb538fa3ebcb942e99f615a7b1be266 (patch) | |
tree | c693ab90991c434f51dd8f35e2ce967759ec11e2 /usbh/src/device.rs | |
parent | 7f3601e081f71776d3b5f9fc58d5e1adda7772a3 (diff) | |
download | samd21-demo-c2119f219eb538fa3ebcb942e99f615a7b1be266.tar.gz samd21-demo-c2119f219eb538fa3ebcb942e99f615a7b1be266.zip |
Refactor so Endpoint controls pipe characteristics.
Endpoints know their own address, number, max packet size, and data
toggle states. When a pipe is needed, pass in the endpoint its needed
for so that configuration can happen according to that endpoint.
Diffstat (limited to 'usbh/src/device.rs')
-rw-r--r-- | usbh/src/device.rs | 183 |
1 files changed, 134 insertions, 49 deletions
diff --git a/usbh/src/device.rs b/usbh/src/device.rs index 6efc395..aba3919 100644 --- a/usbh/src/device.rs +++ b/usbh/src/device.rs @@ -1,9 +1,12 @@ -use super::pipe::{Pipe, PipeErr, PipeTable, NO_DATA_STAGE}; +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; @@ -46,12 +49,20 @@ pub(crate) struct DeviceTable { // 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(), - } + pub(crate) fn new<F>(millis: &'static F) -> Self + where + F: Fn() -> usize + 'static, + { + let mut devices: [Option<Device>; MAX_DEVICES] = { + let mut devs: [core::mem::MaybeUninit<Option<Device>>; 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`. @@ -66,11 +77,15 @@ impl DeviceTable { /// 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> { + 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, millis); + let d = Device::new(a, max_packet_size, millis); self.devices[i] = Some(d); return self.device_for(a); } @@ -109,16 +124,35 @@ pub struct Device { pub addr: u8, pub max_packet_size: u8, pub endpoints: [Option<Endpoint>; MAX_ENDPOINTS], + pub ep0: Endpoint, state: FSM, millis: &'static dyn Fn() -> usize, } impl Device { - fn new(addr: u8, millis: &'static dyn Fn() -> usize) -> Self { + // 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<Endpoint>; MAX_ENDPOINTS] = { + let mut eps: [core::mem::MaybeUninit<Option<Endpoint>>; 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: 8, - endpoints: Default::default(), + max_packet_size: max_packet_size, + endpoints: endpoints, + ep0: Endpoint::new( + addr, + 0, + TransferType::Control, + TransferDirection::In, + max_packet_size, + ), state: FSM::AddressSet, @@ -141,11 +175,12 @@ impl Device { 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 pipe = pipe_table.pipe_for(host, &self.ep0); let mut vol_descr = ::vcell::VolatileCell::<DeviceDescriptor>::new(Default::default()); pipe.control_transfer( + &mut self.ep0, RequestType::get_descr(), RequestCode::GetDescriptor, WValue::from((0, DescriptorType::Device as u8)), @@ -163,11 +198,12 @@ impl Device { 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 pipe = pipe_table.pipe_for(host, &self.ep0); let mut vol_descr = ::vcell::VolatileCell::<ConfigurationDescriptor>::new(Default::default()); pipe.control_transfer( + &mut self.ep0, RequestType::get_descr(), RequestCode::GetDescriptor, WValue::from((0, DescriptorType::Configuration as u8)), @@ -183,6 +219,7 @@ impl Device { 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)), @@ -195,11 +232,12 @@ impl Device { } FSM::SetConfig => { - let mut pipe = pipe_table.pipe_for(host, self.addr, 0); + 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)), @@ -211,6 +249,7 @@ impl Device { debug!("+++ setting idle"); pipe.control_transfer( + &mut self.ep0, RequestType::from(( RequestDirection::HostToDevice, RequestKind::Class, @@ -227,6 +266,7 @@ impl Device { debug!("+++ setting report"); let mut report: u8 = 0; pipe.control_transfer( + &mut self.ep0, RequestType::from(( RequestDirection::HostToDevice, RequestKind::Class, @@ -240,7 +280,23 @@ impl Device { )?; debug!(" -- report set"); - self.state = FSM::GetReport(3) + // 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, @@ -252,22 +308,12 @@ impl Device { // 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); + self.read_report(pipe_table, host, 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); + self.read_report(pipe_table, host, 2); - self.state = FSM::GetReport(count - 1) + self.state = FSM::GetReport(count) } FSM::Steady => {} @@ -275,34 +321,73 @@ impl Device { Ok(()) } - fn read_report(&mut self, pipe: &mut Pipe, id: u8) { - let mut buf: core::mem::MaybeUninit<[u8; 8]> = core::mem::MaybeUninit::uninit(); - match pipe.in_transfer(&mut buf, 15, self.millis) { - Ok(bytes_received) => { - let tmp = unsafe { &(buf.assume_init())[..bytes_received] }; - info!("report {}: {:?}", id, tmp); - } + 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) => info!("error {}: {:?}", id, e), + Err(e) => trace!("error {}: {:?}", id, e), + } + } else { + error!("endpoint {} doesn't exist!", id) } } } +// TransferType (INTERRUPT) +// Direction (IN) pub struct Endpoint { - num: u8, - typ: EndpointType, + // 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, } -impl Default for Endpoint { - fn default() -> Self { + +// 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 { - num: 0, - typ: EndpointType::Control, + addr: addr, + num: num, + transfer_type: transfer_type, + direction: direction, + in_toggle: false, + out_toggle: false, + max_packet_size: max_packet_size, } } } - -enum EndpointType { - Control, - In, - Out, -} |