diff options
author | Brian Cully <bjc@kublai.com> | 2019-07-27 14:52:27 -0400 |
---|---|---|
committer | Brian Cully <bjc@kublai.com> | 2019-07-27 14:52:27 -0400 |
commit | e0a9f75354564b22ef41b4a3a58e5ff321a3fc25 (patch) | |
tree | f6a1c4b5d9000c5ddc657e44ca2ec60a1aea3d9a | |
parent | 4421c02a3e98d8b9e3af8db207dfeb9d276e2962 (diff) | |
download | samd21-demo-e0a9f75354564b22ef41b4a3a58e5ff321a3fc25.tar.gz samd21-demo-e0a9f75354564b22ef41b4a3a58e5ff321a3fc25.zip |
WIP: we have lights on the keyboard!
-rw-r--r-- | usbh/src/device.rs | 78 | ||||
-rwxr-xr-x | usbh/src/lib.rs | 87 | ||||
-rw-r--r-- | usbh/src/pipe.rs | 91 | ||||
-rw-r--r-- | usbh/src/pipe/addr.rs | 6 |
4 files changed, 215 insertions, 47 deletions
diff --git a/usbh/src/device.rs b/usbh/src/device.rs new file mode 100644 index 0000000..3bb0304 --- /dev/null +++ b/usbh/src/device.rs @@ -0,0 +1,78 @@ +use core::convert::TryInto; + +const MAX_DEVICES: usize = 16; +const MAX_ENDPOINTS: usize = 8; + +pub struct DeviceTable { + devices: [Option<Device>; MAX_DEVICES], +} +impl DeviceTable { + pub fn new() -> Self { + Self { + devices: Default::default(), + } + } + + /// Return the device at address `addr`. + pub 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. + pub fn next(&mut self) -> Option<&mut Device> { + for i in 0..self.devices.len() { + if self.devices[i].is_none() { + let a = i.try_into().unwrap(); + let mut d: Device = Default::default(); + d.addr = a; + self.devices[i] = Some(d); + return self.device_for(a); + } + } + None + } + + /// Remove the device at address `addr`. + pub fn remove(&mut self, addr: u8) -> Option<Device> { + let v = core::mem::replace(&mut self.devices[addr as usize], None); + v + } +} + +pub struct Device { + addr: u8, + max_packet_size: u8, + endpoints: [Endpoint; MAX_ENDPOINTS], +} +impl Default for Device { + fn default() -> Self { + Self { + addr: 0, + max_packet_size: 8, + endpoints: Default::default(), + } + } +} + +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, +} diff --git a/usbh/src/lib.rs b/usbh/src/lib.rs index 8c19a3c..63b5581 100755 --- a/usbh/src/lib.rs +++ b/usbh/src/lib.rs @@ -1,9 +1,11 @@ #![no_std] #![allow(dead_code)] +mod device; mod pipe; mod usbproto; +use device::DeviceTable; use pipe::{DataBuf, PipeErr, PipeTable, USBPipeType, USBToken}; use rb::{Reader, RingBuffer, Writer}; use usbproto::*; @@ -55,7 +57,6 @@ enum TaskState { Steady(SteadyState), } -const MAX_DEVICES: usize = 16; const SETTLE_DELAY: usize = 205; // Delay in sec/1024 const NAK_LIMIT: usize = 15; @@ -78,6 +79,8 @@ where // register. pipe_table: PipeTable, + devices: DeviceTable, + // need sof 1kHz pad? _sof_pad: gpio::Pa23<gpio::PfG>, _dm_pad: gpio::Pa24<gpio::PfG>, @@ -104,13 +107,18 @@ where millis: &'static F, ) -> (Self, impl FnMut()) { let (eventr, mut eventw) = unsafe { EVENTS.split() }; + let mut rc = Self { usb: usb, + events: eventr, task_state: TaskState::Detached(DetachedState::Initialize), delay: 0, + pipe_table: PipeTable::new(), + devices: DeviceTable::new(), + _sof_pad: sof_pin.into_function_g(port), _dm_pad: dm_pin.into_function_g(port), _dp_pad: dp_pin.into_function_g(port), @@ -265,9 +273,7 @@ where } } - fn poll_devices(&mut self) { - for _ in 0..MAX_DEVICES {} - } + fn poll_devices(&mut self) {} fn fsm(&mut self) { // respond to events from interrupt. @@ -353,10 +359,7 @@ where } fn configure_dev(&mut self, _parent: u32, _port: u32, _low_speed: u32) -> Result<(), PipeErr> { - // addr: 0x20007774 - let tmp: USBDeviceDescriptor = Default::default(); - // addr: 0x20007788 - let mut vol_descr = ::vcell::VolatileCell::new(tmp); + let mut vol_descr = ::vcell::VolatileCell::<USBDeviceDescriptor>::new(Default::default()); self.control_req( 0, 0, @@ -373,7 +376,7 @@ where // Assign address to this device and: // - Stash bMaxPacketSize // Then SET_ADDRESS(newAddr) - let new_address: u8 = 1; + let new_address: u8 = 5; debug!("Setting address to {}.", new_address); self.control_req( 0, @@ -388,26 +391,62 @@ where let until = (self.millis)() + 300; while (self.millis)() < until {} - debug!("getting config with array"); - //let tmp: USBConfigurationDescriptor = Default::default(); - let mut tmp: [u8; 9] = [0; 9]; - //let vol_descr = ::vcell::VolatileCell::new(tmp); + // Everything after this point is what linux does. So do that? + + // Dunno why we get the device descriptor a second time. + trace!( + " -- getting devDesc2 from {}:{}: {:?}", + new_address, + 0, + desc + ); + let tmp: USBDeviceDescriptor = Default::default(); + let mut vol_descr = ::vcell::VolatileCell::new(tmp); self.control_req( new_address, 0, BMRequestType::get_descr(), - USBRequest::GetConfiguration, - WValue::from((0, 0)), + USBRequest::GetDescriptor, + WValue::from((0, USBDescriptor::Device as u8)), 0, - Some(DataBuf::from(&mut tmp)), + Some(DataBuf::from(&mut vol_descr)), + )?; + + let desc = vol_descr.get(); + trace!(" -- devDesc2: {:?}", desc); + + // Get config descriptor with minimal data, to see how much we need to allocate for the full descriptor. + let mut vol_descr = + ::vcell::VolatileCell::<USBConfigurationDescriptor>::new(Default::default()); + self.control_req( + new_address, + 0, + BMRequestType::get_descr(), + USBRequest::GetDescriptor, + WValue::from((0, USBDescriptor::Configuration as u8)), + 0, + Some(DataBuf::from(&mut vol_descr)), )?; + let desc = vol_descr.get(); + debug!("config: {:?}", desc); - //let desc = vol_descr.get(); - debug!("cdesc.len: {}, type: {}", tmp[0], tmp[1]); + // 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]; + self.control_req( + new_address, + 0, + BMRequestType::get_descr(), + USBRequest::GetDescriptor, + WValue::from((0, USBDescriptor::Configuration as u8)), + 0, + Some(DataBuf::from(&mut tmp)), + )?; // Once addressed, SET_CONFIGURATION(0) debug!("+++ setting configuration"); - let conf: u8 = 0; + let conf: u8 = 1; self.control_req( new_address, 0, @@ -439,6 +478,9 @@ where assert!(b.len <= 65_535); } + // Pipe data toggles for control pipes defined in SAMD21 data + // sheet §32.6.3.9 + /* * Setup stage. */ @@ -453,6 +495,7 @@ where }, }; let mut pipe = self.pipe_table.pipe_for(self.usb.host_mut(), addr, ep); + pipe.dtgl_clear(); pipe.send( USBToken::Setup, &DataBuf::from(&mut setup_packet), @@ -463,6 +506,7 @@ where /* * Data stage. */ + pipe.dtgl_set(); if let Some(b) = buf { match bm_request_type.direction() { USBSetupDirection::DeviceToHost => { @@ -480,9 +524,10 @@ where /* * Status stage. */ + pipe.dtgl_set(); pipe.desc.bank0.pcksize.write(|w| { - // FIXME: see note in `Pipe.send`. - unsafe { w.bits(0) } + unsafe { w.byte_count().bits(0) }; + unsafe { w.multi_packet_size().bits(0) } }); // PSTATUSSET.DTGL set -- TODO: figure out if this is diff --git a/usbh/src/pipe.rs b/usbh/src/pipe.rs index 833ddea..af2bc33 100644 --- a/usbh/src/pipe.rs +++ b/usbh/src/pipe.rs @@ -28,6 +28,7 @@ const MAX_PIPES: usize = 8; #[derive(Copy, Clone, Debug, PartialEq)] pub(crate) enum PipeErr { + ShortPacket, InvalidPipe, InvalidToken, Stall, @@ -123,18 +124,21 @@ impl Pipe<'_, '_> { // byte_count section of register is 14 bits. assert!(buf.len < 16_384); + // TODO: pull this from pipe descriptor for this addr/ep. + let packet_size = 8; + trace!("p{}: Should IN for {}b.", self.num, buf.len); self.desc.bank0.pcksize.write(|w| { unsafe { w.byte_count().bits(buf.len as u16) }; unsafe { w.multi_packet_size().bits(0) } }); + // Read until we get a short packet (indicating that there's + // nothing left for us in this transaction) or the buffer is + // full. let mut bytes_received = 0; while bytes_received < buf.len { - // Update buffer pointer. - // - // FIXME: This only works when the packet size is a word - // multiple, since `addr` requires word-aligned pointers. + // Move the buffer pointer forward as we get data. self.desc .bank0 .addr @@ -142,21 +146,70 @@ impl Pipe<'_, '_> { self.regs.statusclr.write(|w| w.bk0rdy().set_bit()); self.dispatch_retries(USBToken::In, nak_limit, millis)?; - bytes_received += self.desc.bank0.pcksize.read().byte_count().bits() as usize; - assert!(bytes_received <= buf.len); + let recvd = self.desc.bank0.pcksize.read().byte_count().bits() as usize; + bytes_received += recvd; trace!("!! read {} of {}", bytes_received, buf.len); + if recvd < packet_size { + // If we receive a short packet, we should be done + // here. It /may/ be possible to get a short packet + // and continue, but only if recvd is a word-sized + // multiple. I'm going to assume, for simplicity's + // sake, that that's not possible. + break; + } + + // Don't allow writing past the buffer. + assert!(bytes_received <= buf.len); + } + //self.dtgl(); + + if bytes_received < buf.len { + self.log_regs(); + // TODO: honestly, this is probably a panic condition, + // since whatever's in DataBuf.ptr is totally + // invalid. Alternately, this function should be declared + // `unsafe`. + self.regs.statusset.write(|w| w.pfreeze().set_bit()); + Err(PipeErr::ShortPacket) + } else { + self.regs.statusset.write(|w| w.pfreeze().set_bit()); + Ok(()) } - self.regs.statusset.write(|w| w.pfreeze().set_bit()); + } + // TODO: these two functions shouldn't be exposed, since they're + // pretty hardware-dependent. Instead, move USBHost.control_req() + // into this module (where it will sit with its peers + // `in_transfer` and `out_transfer`) and combine it with `send` + // (which is its only use). + pub(crate) fn dtgl_set(&mut self) { + self.regs.statusset.write(|w| w.dtgl().set_bit()); + } + + pub(crate) fn dtgl_clear(&mut self) { + self.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) + }); + } + + fn dtgl(&mut self) { + // TODO: this makes no sense to me, and docs are unclear. If + // the status bit is set, set it again? if it's clear then + // clear it? This is what I get for having to work from + // Arduino sources. + trace!( + "~~~ curbk: {}, dtgl: {}", + self.regs.status.read().curbk().bit(), + self.regs.status.read().dtgl().bit() + ); if self.regs.status.read().dtgl().bit_is_set() { - self.regs.statusset.write(|w| w.dtgl().set_bit()); + self.dtgl_set(); } else { - self.regs.statusclr.write(|w| unsafe { - // No function for this. FIXME: need to patch the SVD for PSTATUSCLR.DTGL at bit0 - w.bits(1) - }); + self.dtgl_clear(); } - Ok(()) } pub(crate) fn dispatch_retries( @@ -180,17 +233,7 @@ impl Pipe<'_, '_> { last_result = self.dispatch_result(token, until, millis); match last_result { Ok(_) => return Ok(()), - // FIXME: handle datatoggle - Err(PipeErr::DataToggle) => { - if self.regs.status.read().dtgl().bit_is_set() { - self.regs.statusset.write(|w| w.dtgl().set_bit()); - } else { - self.regs.statusclr.write(|w| unsafe { - // No function for this. FIXME: need to patch the SVD for PSTATUSCLR.DTGL at bit0 - w.bits(1) - }); - } - } + Err(PipeErr::DataToggle) => self.dtgl(), Err(PipeErr::SWTimeout) => break, Err(PipeErr::Stall) => break, Err(_) => naks += 1, diff --git a/usbh/src/pipe/addr.rs b/usbh/src/pipe/addr.rs index 3ff2d2d..98199d1 100644 --- a/usbh/src/pipe/addr.rs +++ b/usbh/src/pipe/addr.rs @@ -87,8 +87,10 @@ pub(crate) struct AddrW<'a> { } impl<'a> AddrW<'a> { pub unsafe fn bits(self, v: u32) -> &'a mut W { - // Address must be 32-bit aligned. - assert!((v & 0x3) == 0); + // Address must be 32-bit aligned. cf §32.8.7.2 of SAMD21 data + // sheet. + + //assert!((v & 0x3) == 0); self.w.bits = v; self.w } |