aboutsummaryrefslogtreecommitdiffstats
path: root/usbh
diff options
context:
space:
mode:
authorBrian Cully <bjc@kublai.com>2019-07-27 14:52:27 -0400
committerBrian Cully <bjc@kublai.com>2019-07-27 14:52:27 -0400
commite0a9f75354564b22ef41b4a3a58e5ff321a3fc25 (patch)
treef6a1c4b5d9000c5ddc657e44ca2ec60a1aea3d9a /usbh
parent4421c02a3e98d8b9e3af8db207dfeb9d276e2962 (diff)
downloadsamd21-demo-e0a9f75354564b22ef41b4a3a58e5ff321a3fc25.tar.gz
samd21-demo-e0a9f75354564b22ef41b4a3a58e5ff321a3fc25.zip
WIP: we have lights on the keyboard!
Diffstat (limited to 'usbh')
-rw-r--r--usbh/src/device.rs78
-rwxr-xr-xusbh/src/lib.rs87
-rw-r--r--usbh/src/pipe.rs91
-rw-r--r--usbh/src/pipe/addr.rs6
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
}