diff options
author | Brian Cully <bjc@kublai.com> | 2019-08-04 14:19:25 -0400 |
---|---|---|
committer | Brian Cully <bjc@kublai.com> | 2019-08-04 14:19:25 -0400 |
commit | e405c474f5e0e94288191223de7ae0f31ae0b15f (patch) | |
tree | 4ca89a92f0c868eb8feae272513c1e924b834adc | |
parent | abd478d9425dd2d4acd5b58202b1a95b73ff217b (diff) | |
download | samd21-demo-e405c474f5e0e94288191223de7ae0f31ae0b15f.tar.gz samd21-demo-e405c474f5e0e94288191223de7ae0f31ae0b15f.zip |
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.
-rw-r--r-- | app/logitech-kb-start.pcapng | bin | 8616 -> 0 bytes | |||
-rw-r--r-- | app/src/dotstar.rs | 42 | ||||
-rw-r--r-- | bootkbd/Cargo.toml | 1 | ||||
-rwxr-xr-x[-rw-r--r--] | bootkbd/src/lib.rs | 40 | ||||
-rw-r--r-- | samd21-host/.cargo/config (renamed from app/.cargo/config) | 0 | ||||
-rw-r--r-- | samd21-host/Cargo.lock (renamed from app/Cargo.lock) | 113 | ||||
-rw-r--r-- | samd21-host/Cargo.toml (renamed from app/Cargo.toml) | 23 | ||||
-rw-r--r-- | samd21-host/Makefile (renamed from app/Makefile) | 12 | ||||
-rw-r--r-- | samd21-host/examples/simple/logger.rs (renamed from app/src/logger.rs) | 0 | ||||
-rw-r--r-- | samd21-host/examples/simple/macros.rs (renamed from app/src/macros.rs) | 0 | ||||
-rwxr-xr-x | samd21-host/examples/simple/main.rs (renamed from app/src/main.rs) | 51 | ||||
-rw-r--r-- | samd21-host/examples/simple/rtc.rs (renamed from app/src/rtc.rs) | 0 | ||||
-rwxr-xr-x | samd21-host/find-serial-port (renamed from app/find-serial-port) | 0 | ||||
-rwxr-xr-x | samd21-host/simple.bin | bin | 0 -> 123568 bytes | |||
-rw-r--r-- | samd21-host/simple.uf2 | bin | 0 -> 247296 bytes | |||
-rwxr-xr-x | samd21-host/src/lib.rs (renamed from usbh/src/lib.rs) | 309 | ||||
-rw-r--r-- | samd21-host/src/pipe.rs (renamed from usbh/src/pipe.rs) | 157 | ||||
-rw-r--r-- | samd21-host/src/pipe/addr.rs (renamed from usbh/src/pipe/addr.rs) | 10 | ||||
-rw-r--r-- | samd21-host/src/pipe/ctrl_pipe.rs (renamed from usbh/src/pipe/ctrl_pipe.rs) | 18 | ||||
-rw-r--r-- | samd21-host/src/pipe/ext_reg.rs (renamed from usbh/src/pipe/ext_reg.rs) | 0 | ||||
-rw-r--r-- | samd21-host/src/pipe/pck_size.rs (renamed from usbh/src/pipe/pck_size.rs) | 0 | ||||
-rw-r--r-- | samd21-host/src/pipe/status_bk.rs (renamed from usbh/src/pipe/status_bk.rs) | 0 | ||||
-rw-r--r-- | samd21-host/src/pipe/status_pipe.rs (renamed from usbh/src/pipe/status_pipe.rs) | 0 | ||||
-rwxr-xr-x[-rw-r--r--] | usb-host/src/lib.rs | 38 | ||||
-rw-r--r-- | usbh/Cargo.lock | 269 | ||||
-rw-r--r-- | usbh/Cargo.toml | 16 | ||||
-rw-r--r-- | usbh/src/device.rs | 392 | ||||
-rw-r--r-- | usbh/src/usbproto.rs | 438 |
28 files changed, 444 insertions, 1485 deletions
diff --git a/app/logitech-kb-start.pcapng b/app/logitech-kb-start.pcapng Binary files differdeleted file mode 100644 index ab22fd0..0000000 --- a/app/logitech-kb-start.pcapng +++ /dev/null diff --git a/app/src/dotstar.rs b/app/src/dotstar.rs deleted file mode 100644 index 8e930d0..0000000 --- a/app/src/dotstar.rs +++ /dev/null @@ -1,42 +0,0 @@ -use trinket_m0::{ - clock::GenericClockController, - gpio::{self, Floating, Input}, - prelude::*, - sercom::{self, PadPin, SPIMaster1}, - PM, SERCOM1, -}; - -use apa102_spi::Apa102; - -pub fn new( - sercom: SERCOM1, - miso: gpio::Pa31<Input<Floating>>, - mosi: gpio::Pa0<Input<Floating>>, - sck: gpio::Pa1<Input<Floating>>, - port: &mut gpio::Port, - pm: &mut PM, - clocks: &mut GenericClockController, -) -> Apa102< - SPIMaster1< - sercom::Sercom1Pad3<gpio::Pa31<gpio::PfD>>, - sercom::Sercom1Pad0<gpio::Pa0<gpio::PfD>>, - sercom::Sercom1Pad1<gpio::Pa1<gpio::PfD>>, - >, -> { - let gclk = clocks.gclk0(); - let miso = miso.into_pad(port); - let mosi = mosi.into_pad(port); - let sck = sck.into_pad(port); - let spi = SPIMaster1::new( - &clocks - .sercom1_core(&gclk) - .expect("setting up sercom1 clock"), - 3.mhz(), - apa102_spi::MODE, - sercom, - pm, - (miso, mosi, sck), - ); - - Apa102::new(spi) -} diff --git a/bootkbd/Cargo.toml b/bootkbd/Cargo.toml index 7b1b02d..5ae7bd4 100644 --- a/bootkbd/Cargo.toml +++ b/bootkbd/Cargo.toml @@ -8,4 +8,3 @@ license = "GPL-3.0-or-later" [dependencies] usb-host = { path = "../usb-host" } log = "~0.4" - diff --git a/bootkbd/src/lib.rs b/bootkbd/src/lib.rs index c143512..dde9e71 100644..100755 --- a/bootkbd/src/lib.rs +++ b/bootkbd/src/lib.rs @@ -1,8 +1,10 @@ +#![no_std] + use log::{debug, error, info}; use usb_host::{ - ConfigurationDescriptor, DescriptorType, DeviceDescriptor, Direction, Driver, Endpoint, - Error as USBError, RequestCode, RequestDirection, RequestKind, RequestRecipient, RequestType, - TransferType, USBHost, WValue, + ConfigurationDescriptor, DescriptorType, DeviceDescriptor, Direction, Driver, DriverError, + Endpoint, RequestCode, RequestDirection, RequestKind, RequestRecipient, RequestType, + TransferError, TransferType, USBHost, WValue, }; use core::mem::{self, MaybeUninit}; @@ -21,6 +23,11 @@ const MAX_ENDPOINTS: usize = 2; pub struct BootKeyboard { devices: [Option<Device>; MAX_DEVICES], } +impl core::fmt::Debug for BootKeyboard { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "BootKeyboard") + } +} impl BootKeyboard { pub fn new() -> Self { @@ -35,14 +42,14 @@ impl Driver for BootKeyboard { true } - fn add_device(&mut self, _device: DeviceDescriptor, address: u8) -> Result<(), USBError> { + fn add_device(&mut self, _device: DeviceDescriptor, address: u8) -> Result<(), DriverError> { for i in 0..self.devices.len() { if self.devices[i].is_none() { self.devices[i] = Some(Device::new(address)); return Ok(()); } } - Err(USBError::Permanent("out of devices")) + Err(DriverError::Permanent(address, "out of devices")) } fn remove_device(&mut self, address: u8) { @@ -56,20 +63,15 @@ impl Driver for BootKeyboard { } } - fn tick_device( - &mut self, - address: u8, - millis: usize, - host: &mut dyn USBHost, - ) -> Result<(), USBError> { - for i in 0..self.devices.len() { - if let Some(ref mut dev) = self.devices[i] { - if dev.addr == address { - return dev.fsm(millis, host); + fn tick(&mut self, millis: usize, host: &mut dyn USBHost) -> Result<(), DriverError> { + for d in &mut self.devices[..] { + if let Some(ref mut dev) = d { + if let Err(TransferError::Permanent(e)) = dev.fsm(millis, host) { + return Err(DriverError::Permanent(dev.addr, e)); } } } - Err(USBError::Permanent("no device at address")) + Ok(()) } } @@ -110,7 +112,7 @@ impl Device { } } - fn fsm(&mut self, millis: usize, host: &mut dyn USBHost) -> Result<(), USBError> { + fn fsm(&mut self, millis: usize, host: &mut dyn USBHost) -> Result<(), TransferError> { // TODO: either we need another `control_transfer` that // doesn't take data, or this `none` value needs to be put in // the usb-host layer. None of these options are good. @@ -252,8 +254,8 @@ impl Device { let mut buf: [u8; 8] = [0; 8]; if let Some(ref mut ep) = self.endpoints[0] { match host.in_transfer(ep, &mut buf) { - Err(USBError::Permanent(msg)) => error!("reading report: {}", msg), - Err(USBError::Retry(_)) => return Ok(()), + Err(TransferError::Permanent(msg)) => error!("reading report: {}", msg), + Err(TransferError::Retry(_)) => return Ok(()), Ok(len) => info!("report: {} - {:?}", len, buf), } } diff --git a/app/.cargo/config b/samd21-host/.cargo/config index b6cdff0..b6cdff0 100644 --- a/app/.cargo/config +++ b/samd21-host/.cargo/config diff --git a/app/Cargo.lock b/samd21-host/Cargo.lock index 599825f..7c24483 100644 --- a/app/Cargo.lock +++ b/samd21-host/Cargo.lock @@ -14,15 +14,6 @@ dependencies = [ ] [[package]] -name = "apa102-spi" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smart-leds-trait 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "as-slice" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -51,7 +42,7 @@ version = "0.4.0" dependencies = [ "bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "cortex-m 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-rt 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rt 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -69,6 +60,14 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "bootkbd" +version = "0.1.0" +dependencies = [ + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "usb-host 0.1.0", +] + +[[package]] name = "cfg-if" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -103,7 +102,7 @@ dependencies = [ [[package]] name = "cortex-m-rt" -version = "0.6.8" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cortex-m-rt-macros 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -116,9 +115,9 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.38 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -140,7 +139,7 @@ dependencies = [ [[package]] name = "log" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -152,17 +151,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "panic-halt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "paste" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "paste-impl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -170,20 +164,20 @@ name = "paste-impl" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro-hack 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.38 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "proc-macro-hack" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.38 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -196,7 +190,7 @@ dependencies = [ [[package]] name = "quote" -version = "0.6.12" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", @@ -233,11 +227,6 @@ name = "rb" version = "0.1.0" [[package]] -name = "rgb" -version = "0.8.13" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -246,22 +235,20 @@ dependencies = [ ] [[package]] -name = "samd21-demo" +name = "samd21-host" version = "0.1.0" dependencies = [ - "apa102-spi 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atsamd-hal 0.5.0", + "bootkbd 0.1.0", "clint 0.2.0", "cortex-m 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-rt 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rt 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rb 0.1.0", - "smart-leds 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smart-leds-trait 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "trinket_m0 0.3.0", - "usbh 0.1.0", + "usb-host 0.1.0", ] [[package]] @@ -278,33 +265,17 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "smart-leds" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "smart-leds-trait 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "smart-leds-trait" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rgb 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "0.15.38" +version = "0.15.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -314,7 +285,7 @@ version = "0.3.0" dependencies = [ "atsamd-hal 0.5.0", "cortex-m 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m-rt 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-m-rt 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -330,15 +301,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "usbh" +name = "usb-host" version = "0.1.0" -dependencies = [ - "atsamd-hal 0.5.0", - "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "rb 0.1.0", - "vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "vcell" @@ -361,37 +325,32 @@ dependencies = [ [metadata] "checksum aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d39da9b88ae1a81c03c9c082b8db83f1d0e93914126041962af61034ab44c4a5" "checksum aligned 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d3a316c7ea8e1e9ece54862c992def5a7ac14de9f5832b69d71760680efeeefa" -"checksum apa102-spi 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "244c305e53cabeadfce23409fe24bfcedaa20166aa3f16f6c4ab256302158fd5" "checksum as-slice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "293dac66b274fab06f95e7efb05ec439a6b70136081ea522d270bc351ae5bb27" "checksum bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a3caf393d93b2d453e80638d0674597020cef3382ada454faacd43d1a55a735a" "checksum bitfield 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" "checksum cortex-m 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0b159a1e8306949579de3698c841dba58058197b65c60807194e4fa1e7a554" "checksum cortex-m 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f3c18719fdc57db65668bfc977db9a0fa1a41d718c5d9cd4f652c9d4b0e0956a" -"checksum cortex-m-rt 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7e1ccc9052352415ec4e3f762f4541098d012016f9354a1a5b2dede39b67f426" +"checksum cortex-m-rt 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "17805910e3ecf029bdbfcc42b7384d9e3d9e5626153fa810002c1ef9839338ac" "checksum cortex-m-rt-macros 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d7ae692573e0acccb1579fef1abf5a5bf1d2f3f0149a22b16870ec9309aee25f" "checksum embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c275b6ad54070ac2d665eef9197db647b32239c9d244bfb6f041a766d00da5b3" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc" -"checksum panic-halt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" "checksum paste 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1f4a4a1c555c6505821f9d58b8779d0f630a6b7e4e1be24ba718610acf01fa79" "checksum paste-impl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "26e796e623b8b257215f27e6c80a5478856cae305f5b59810ff9acdaa34570e6" -"checksum proc-macro-hack 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0c1dd4172a1e1f96f709341418f49b11ea6c2d95d53dca08c0f74cbd332d9cf3" +"checksum proc-macro-hack 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "982a35d1194084ba319d65c4a68d24ca28f5fdb5b8bc20899e4eef8641ea5178" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" +"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f" "checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" "checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" -"checksum rgb 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "4f089652ca87f5a82a62935ec6172a534066c7b97be003cc8f702ee9a7a59c92" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum smart-leds 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ab0a30e934e60850bfc020d4c2727cc8a3635371f6af2cb3f55f36f0134ed080" -"checksum smart-leds-trait 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fef18e60d41a6fde19e640cd7590c03fb27aa23146bf60e4da85028d7410cee7" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" -"checksum syn 0.15.38 (registry+https://github.com/rust-lang/crates.io-index)" = "37ea458a750f59ab679b47fef9b6722c586c5742f4cfe18a120bbc807e5e01fd" +"checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d" diff --git a/app/Cargo.toml b/samd21-host/Cargo.toml index 1bf5ab8..446bf1d 100644 --- a/app/Cargo.toml +++ b/samd21-host/Cargo.toml @@ -1,27 +1,24 @@ [package] -name = "samd21-demo" +name = "samd21-host" version = "0.1.0" authors = ["Brian Cully <bjc@kublai.com>"] edition = "2018" -readme = "../README.md" license = "GPL-3.0-or-later" [dependencies] +usb-host = { path = "../usb-host" } rb = { path = "../rb" } -usbh = { path = "../usbh" } +log = "~0.4" +embedded-hal = "~0.2" +atsamd-hal = { path = "../../atsamd/hal" } -trinket_m0 = { path = "../../atsamd/boards/trinket_m0" } +[dev-dependencies] +bootkbd = { path = "../bootkbd" } clint = { path = "../../clint" } - -panic-halt = "~0.2" -smart-leds = "~0.2" -smart-leds-trait = "~0.2" -apa102-spi = "~0.2" # DotStar driver. -nb = "~0.1" cortex-m = "~0.6" cortex-m-rt = "~0.6" -embedded-hal = "~0.2" -log = { version = "~0.4", features = ["max_level_trace", "release_max_level_warn"] } +nb = "~0.1" +trinket_m0 = { path = "../../atsamd/boards/trinket_m0" } [features] -default = ["cortex-m/const-fn"] +default = ["atsamd-hal/samd21e18a"] diff --git a/app/Makefile b/samd21-host/Makefile index a5ef39f..87fbc8d 100644 --- a/app/Makefile +++ b/samd21-host/Makefile @@ -1,4 +1,4 @@ -APP = samd21-demo +APP = simple USBVID = 239a USBPID = '001e|801e' @@ -7,7 +7,7 @@ BOSSAC = bossac SERIAL = $(shell ./find-serial-port $(USBVID) $(USBPID) || echo 'cant-find-serial-port') OFFSET = 0x2000 -.PHONY: all clean cargo-build +.PHONY: all clean cargo-build target/thumbv6m-none-eabi/debug/examples/$(APP) all: $(APP).uf2 @@ -17,12 +17,12 @@ clean: cargo clean cargo-build: - cargo build + cargo build --example $(APP) -target/thumbv6m-none-eabi/debug/$(APP): cargo-build +target/thumbv6m-none-eabi/debug/examples/$(APP): cargo-build -$(APP).bin: target/thumbv6m-none-eabi/debug/$(APP) - cargo objcopy --bin $(APP) -- -O binary $(APP).bin +$(APP).bin: target/thumbv6m-none-eabi/debug/examples/$(APP) + cargo objcopy --example $(APP) -- -O binary $(APP).bin # Requires https://github.com/sajattack/uf2conv-rs.git %.uf2: %.bin diff --git a/app/src/logger.rs b/samd21-host/examples/simple/logger.rs index 9f9452e..9f9452e 100644 --- a/app/src/logger.rs +++ b/samd21-host/examples/simple/logger.rs diff --git a/app/src/macros.rs b/samd21-host/examples/simple/macros.rs index 46d2d07..46d2d07 100644 --- a/app/src/macros.rs +++ b/samd21-host/examples/simple/macros.rs diff --git a/app/src/main.rs b/samd21-host/examples/simple/main.rs index 1f8805b..c262905 100755 --- a/app/src/main.rs +++ b/samd21-host/examples/simple/main.rs @@ -4,21 +4,17 @@ #![feature(const_transmute)] #![allow(dead_code)] -mod dotstar; mod logger; mod macros; mod rtc; -//#[allow(unused)] -//use panic_halt; - +use bootkbd::BootKeyboard; use clint::HandlerArray; use cortex_m::asm::wfi; use cortex_m_rt::{entry, exception, ExceptionFrame}; use embedded_hal::digital::v2::OutputPin; use log::{debug, info, LevelFilter}; -use smart_leds::colors; -use smart_leds_trait::SmartLedsWrite; +use samd21_host::SAMDHost; use trinket_m0::{ self as hal, clock::GenericClockController, @@ -28,7 +24,7 @@ use trinket_m0::{ time::*, CorePeripherals, Peripherals, }; -use usbh::USBHost; +use usb_host::Driver; static HANDLERS: HandlerArray = HandlerArray::new(); @@ -79,26 +75,11 @@ fn main() -> ! { unsafe { log::set_logger_racy(logger_ref).expect("couldn't set logger") }; log::set_max_level(LevelFilter::Info); - info!("setting up dotstar"); - - let mut dotstar = dotstar::new( - peripherals.SERCOM1, - pins.swdio, - pins.dotstar_di, - pins.dotstar_ci, - &mut pins.port, - &mut peripherals.PM, - &mut clocks, - ); - - let black = [colors::BLACK]; - let blue = [colors::DARK_MAGENTA]; - info!("setting up timer"); let mut rtc_handler = rtc::setup(peripherals.RTC, &mut clocks); info!("setting up usb host"); - let (mut usb_host, mut usb_handler) = USBHost::new( + let (mut usb_host, mut usb_handler) = SAMDHost::new( peripherals.USB, pins.usb_sof, pins.usb_dm, @@ -110,6 +91,9 @@ fn main() -> ! { &rtc::millis, ); + let mut bootkbd = BootKeyboard::new(); + let mut drivers: [&mut dyn Driver; 1] = [&mut bootkbd]; + info!("setting up handlers"); HANDLERS.with_overrides(|hs| { hs.register(0, &mut rtc_handler); @@ -121,27 +105,8 @@ fn main() -> ! { info!("Boot up complete."); - let mut last_tick = 0; loop { - cortex_m::interrupt::free(|_cs| { - dotstar - .write(black.iter().cloned()) - .expect("turning off dotstar"); - }); - - let tick = rtc::millis(); - if tick >= last_tick + 1_024 { - last_tick = tick; - // trace!("{}: tick\r\n", rtc::millis()); - } - - usb_host.task(); - - cortex_m::interrupt::free(|_cs| { - dotstar - .write(blue.iter().cloned()) - .expect("turning on dotstar"); - }); + usb_host.task(&mut drivers[..]); wfi(); } }); diff --git a/app/src/rtc.rs b/samd21-host/examples/simple/rtc.rs index 79e450a..79e450a 100644 --- a/app/src/rtc.rs +++ b/samd21-host/examples/simple/rtc.rs diff --git a/app/find-serial-port b/samd21-host/find-serial-port index 62e2c0c..62e2c0c 100755 --- a/app/find-serial-port +++ b/samd21-host/find-serial-port diff --git a/samd21-host/simple.bin b/samd21-host/simple.bin Binary files differnew file mode 100755 index 0000000..26b330f --- /dev/null +++ b/samd21-host/simple.bin diff --git a/samd21-host/simple.uf2 b/samd21-host/simple.uf2 Binary files differnew file mode 100644 index 0000000..817dcb3 --- /dev/null +++ b/samd21-host/simple.uf2 diff --git a/usbh/src/lib.rs b/samd21-host/src/lib.rs index 1ed414c..98ebcff 100755 --- a/usbh/src/lib.rs +++ b/samd21-host/src/lib.rs @@ -1,14 +1,13 @@ #![no_std] -#![allow(dead_code)] - -mod device; mod pipe; -mod usbproto; -use device::{Device, DeviceTable}; -use pipe::{PipeErr, PipeTable, NO_DATA_STAGE}; -use rb::{Reader, RingBuffer, Writer}; -use usbproto::*; +use pipe::{PipeErr, PipeTable}; + +use usb_host::{ + DescriptorType, DeviceDescriptor, Direction, Driver, DriverError, Endpoint, RequestCode, + RequestDirection, RequestKind, RequestRecipient, RequestType, TransferError, TransferType, + USBHost, WValue, +}; use atsamd_hal::{ calibration::{usb_transn_cal, usb_transp_cal, usb_trim_cal}, @@ -18,6 +17,7 @@ use atsamd_hal::{ }; use embedded_hal::digital::v2::OutputPin; use log::{debug, error, trace, warn}; +use rb::{Reader, RingBuffer, Writer}; #[derive(Clone, Copy, Debug, PartialEq)] pub enum Event { @@ -29,6 +29,17 @@ type Events = RingBuffer<Event>; type EventReader = Reader<'static, Event>; type EventWriter = Writer<'static, Event>; +const NAK_LIMIT: usize = 15; + +// Must be at least 100ms. cf §9.1.2 of USB 2.0. +const SETTLE_DELAY: usize = 205; // Delay in sec/1024 + +// Ring buffer for sharing events from interrupt context. +static mut EVENTS: Events = Events::new(Event::Error); +// FIXME: this is just for testing. The enum needs to be +// thread-safe if this is the way we're going. +static mut LATEST_EVENT: Event = Event::Detached; + #[derive(Clone, Copy, Debug, PartialEq)] enum DetachedState { Initialize, @@ -57,19 +68,53 @@ enum TaskState { Steady(SteadyState), } -// Must be at least 100ms. cf §9.1.2 of USB 2.0. -const SETTLE_DELAY: usize = 205; // Delay in sec/1024 -const NAK_LIMIT: usize = 15; +use core::mem::{self, MaybeUninit}; +use core::ptr; -static mut EVENTS: Events = Events::new(Event::Error); -// FIXME: this is just for testing. The enum needs to be -// thread-safe if this is the way we're going. -static mut LATEST_EVENT: Event = Event::Detached; +const MAX_DEVICES: usize = 4; +struct DeviceTable { + tbl: [Option<Device>; MAX_DEVICES], +} +impl DeviceTable { + fn new() -> Self { + let tbl = { + let mut devs: [MaybeUninit<Option<Device>>; MAX_DEVICES] = + unsafe { MaybeUninit::uninit().assume_init() }; + for d in &mut devs[..] { + unsafe { ptr::write(d.as_mut_ptr(), None) } + } + unsafe { mem::transmute(devs) } + }; -pub struct USBHost<F> -where - F: Fn() -> usize + 'static, -{ + Self { tbl: tbl } + } + + /// 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. + fn next(&mut self) -> Option<&mut Device> { + for i in 1..self.tbl.len() { + if self.tbl[i].is_none() { + let d = Device { addr: i as u8 }; + self.tbl[i] = Some(d); + return self.tbl[i].as_mut(); + } + } + None + } + + /// Remove the device at address `addr`. + fn remove(&mut self, addr: u8) -> Option<Device> { + let v = core::mem::replace(&mut self.tbl[addr as usize], None); + v + } +} + +struct Device { + addr: u8, +} + +pub struct SAMDHost<'a, F> { usb: USB, events: EventReader, @@ -80,7 +125,6 @@ where pipe_table: PipeTable, devices: DeviceTable, - addr0: Device, // need sof 1kHz pad? _sof_pad: gpio::Pa23<gpio::PfG>, @@ -89,12 +133,12 @@ where host_enable_pin: Option<gpio::Pa28<Output<OpenDrain>>>, // To get current milliseconds. - millis: &'static F, + millis: &'a F, } -impl<F> USBHost<F> +impl<'a, F> SAMDHost<'a, F> where - F: Fn() -> usize + 'static, + F: Fn() -> usize, { pub fn new( usb: USB, @@ -105,7 +149,7 @@ where port: &mut gpio::Port, clocks: &mut GenericClockController, pm: &mut PM, - millis: &'static F, + millis: &'a F, ) -> (Self, impl FnMut()) { let (eventr, mut eventw) = unsafe { EVENTS.split() }; @@ -117,8 +161,7 @@ where pipe_table: PipeTable::new(), - devices: DeviceTable::new(millis), - addr0: Device::new(0, 8, millis), + devices: DeviceTable::new(), _sof_pad: sof_pin.into_function_g(port), _dm_pad: dm_pin.into_function_g(port), @@ -197,7 +240,7 @@ where debug!("...done"); } - pub fn task(&mut self) { + pub fn task(&mut self, drivers: &mut [&mut dyn Driver]) { static mut LAST_EVENT: Event = Event::Error; unsafe { if LAST_EVENT != LATEST_EVENT { @@ -267,21 +310,18 @@ where // }; } - self.fsm(); + self.fsm(drivers); unsafe { LAST_EVENT = LATEST_EVENT; } } - - fn poll_devices(&mut self) {} - - fn fsm(&mut self) { + fn fsm(&mut self, drivers: &mut [&mut dyn Driver]) { // respond to events from interrupt. match self.task_state { TaskState::Detached(s) => self.detached_fsm(s), TaskState::Attached(s) => self.attached_fsm(s), - TaskState::Steady(s) => self.steady_fsm(s), + TaskState::Steady(s) => self.steady_fsm(s, drivers), }; } @@ -337,10 +377,10 @@ where } } - fn steady_fsm(&mut self, s: SteadyState) { + fn steady_fsm(&mut self, s: SteadyState, drivers: &mut [&mut dyn Driver]) { match s { SteadyState::Configuring => { - self.task_state = match self.configure_dev() { + self.task_state = match self.configure_dev(drivers) { Ok(_) => TaskState::Steady(SteadyState::Running), Err(e) => { warn!("Enumeration error: {:?}", e); @@ -350,51 +390,117 @@ where } SteadyState::Running => { - self.devices.run(&mut self.pipe_table, self.usb.host_mut()); + for d in &mut drivers[..] { + if let Err(e) = d.tick((self.millis)(), self) { + warn!("running driver {:?}: {:?}", d, e); + if let DriverError::Permanent(a, _) = e { + self.devices.remove(a); + } + } + } } SteadyState::Error => {} } } - fn configure_dev(&mut self) -> Result<(), PipeErr> { - let mut pipe = self - .pipe_table - .pipe_for(self.usb.host_mut(), &self.addr0.ep0); - let mut vol_descr = ::vcell::VolatileCell::<DeviceDescriptor>::new(Default::default()); - pipe.control_transfer( - &mut self.addr0.ep0, - RequestType::get_descr(), + fn configure_dev(&mut self, drivers: &mut [&mut dyn Driver]) -> Result<(), TransferError> { + let none: Option<&mut [u8]> = None; + let mut a0ep0 = Addr0EP0 { + in_toggle: true, + out_toggle: true, + }; + let mut dev_desc: DeviceDescriptor = + unsafe { MaybeUninit::<DeviceDescriptor>::uninit().assume_init() }; + self.control_transfer( + &mut a0ep0, + RequestType::from(( + RequestDirection::DeviceToHost, + RequestKind::Standard, + RequestRecipient::Device, + )), RequestCode::GetDescriptor, WValue::from((0, DescriptorType::Device as u8)), 0, - Some(&mut vol_descr), - self.millis, + Some(unsafe { to_slice_mut(&mut dev_desc) }), + )?; + + trace!(" -- dev_desc: {:?}", dev_desc); + + // TODO: new error for being out of devices. + let addr = self + .devices + .next() + .ok_or(TransferError::Permanent("out of devices"))? + .addr; + debug!("Setting address to {}.", addr); + self.control_transfer( + &mut a0ep0, + RequestType::from(( + RequestDirection::HostToDevice, + RequestKind::Standard, + RequestRecipient::Device, + )), + RequestCode::SetAddress, + WValue::from((addr, 0)), + 0, + none, )?; - let desc = vol_descr.get(); - trace!(" -- devDesc: {:?}", desc); - - match self.devices.next(desc.b_max_packet_size, self.millis) { - // TODO: new error for being out of devices. - None => Err(PipeErr::Other("out of devices")), - Some(device) => { - debug!("Setting address to {}.", device.addr); - pipe.control_transfer( - &mut self.addr0.ep0, - RequestType::set(), - RequestCode::SetAddress, - WValue::from((device.addr, 0)), - 0, - NO_DATA_STAGE, - self.millis, - )?; - - // Now that the device is addressed, `Device` can handle the - // rest of the setup in its FSM. - Ok(()) + // Now that the device is addressed, see if any drivers want + // it. + for d in &mut drivers[..] { + if d.want_device(&dev_desc) { + let res = d.add_device(dev_desc, addr); + match res { + Ok(_) => return Ok(()), + Err(_) => return Err(TransferError::Permanent("out of addresses")), + } } } + Ok(()) + } +} + +struct Addr0EP0 { + in_toggle: bool, + out_toggle: bool, +} +impl Endpoint for Addr0EP0 { + fn address(&self) -> u8 { + 0 + } + + fn endpoint_num(&self) -> u8 { + 0 + } + + fn transfer_type(&self) -> TransferType { + TransferType::Control + } + + fn direction(&self) -> Direction { + Direction::In + } + + fn max_packet_size(&self) -> u16 { + 8 + } + + fn in_toggle(&self) -> bool { + self.in_toggle + } + + fn set_in_toggle(&mut self, toggle: bool) { + self.in_toggle = toggle; + } + + fn out_toggle(&self) -> bool { + self.out_toggle + } + + fn set_out_toggle(&mut self, toggle: bool) { + self.out_toggle = toggle; } } @@ -464,11 +570,76 @@ pub fn handler(usbp: usize, events: &mut EventWriter) { trace!(" +ddisc"); usb.host().intflag.write(|w| w.ddisc().set_bit()); usb.host().intenclr.write(|w| w.ddisc().set_bit()); - // // Stop reset signal, in case of disconnection during reset - // uhd_stop_reset(); // nothing on samd21 usb.host().intflag.write(|w| w.dconn().set_bit()); usb.host().intenset.write(|w| w.dconn().set_bit()); usb.host().intflag.write(|w| w.ddisc().set_bit()); unshift_event(Event::Detached); } } + +impl From<PipeErr> for TransferError { + fn from(v: PipeErr) -> Self { + match v { + PipeErr::TransferFail => Self::Retry("transfer failed"), + PipeErr::Flow => Self::Retry("data flow"), + PipeErr::DataToggle => Self::Retry("toggle sequence"), + PipeErr::ShortPacket => Self::Permanent("short packet"), + PipeErr::InvalidPipe => Self::Permanent("invalid pipe"), + PipeErr::InvalidToken => Self::Permanent("invalid token"), + PipeErr::Stall => Self::Permanent("stall"), + PipeErr::PipeErr => Self::Permanent("pipe error"), + PipeErr::HWTimeout => Self::Permanent("hardware timeout"), + PipeErr::SWTimeout => Self::Permanent("software timeout"), + PipeErr::Other(s) => Self::Permanent(s), + } + } +} + +impl<F> USBHost for SAMDHost<'_, F> +where + F: Fn() -> usize, +{ + fn control_transfer( + &mut self, + ep: &mut dyn Endpoint, + bm_request_type: RequestType, + b_request: RequestCode, + w_value: WValue, + w_index: u16, + buf: Option<&mut [u8]>, + ) -> Result<usize, TransferError> { + let mut pipe = self.pipe_table.pipe_for(self.usb.host_mut(), ep); + let len = pipe.control_transfer( + ep, + bm_request_type, + b_request, + w_value, + w_index, + buf, + self.millis, + )?; + Ok(len) + } + + fn in_transfer( + &mut self, + ep: &mut dyn Endpoint, + buf: &mut [u8], + ) -> Result<usize, TransferError> { + let mut pipe = self.pipe_table.pipe_for(self.usb.host_mut(), ep); + let len = pipe.in_transfer(ep, buf, NAK_LIMIT, self.millis)?; + Ok(len) + } + + fn out_transfer(&mut self, ep: &mut dyn Endpoint, buf: &[u8]) -> Result<usize, TransferError> { + let mut pipe = self.pipe_table.pipe_for(self.usb.host_mut(), ep); + let len = pipe.out_transfer(ep, buf, NAK_LIMIT, self.millis)?; + Ok(len) + } +} + +unsafe fn to_slice_mut<T>(v: &mut T) -> &mut [u8] { + let ptr = v as *mut T as *mut u8; + let len = mem::size_of::<T>(); + core::slice::from_raw_parts_mut(ptr, len) +} diff --git a/usbh/src/pipe.rs b/samd21-host/src/pipe.rs index 80ebe11..0da0e7f 100644 --- a/usbh/src/pipe.rs +++ b/samd21-host/src/pipe.rs @@ -1,8 +1,14 @@ +#[allow(unused)] pub mod addr; +#[allow(unused)] pub mod ctrl_pipe; +#[allow(unused)] pub mod ext_reg; +#[allow(unused)] pub mod pck_size; +#[allow(unused)] pub mod status_bk; +#[allow(unused)] pub mod status_pipe; use addr::Addr; @@ -12,16 +18,16 @@ use pck_size::PckSize; use status_bk::StatusBk; use status_pipe::StatusPipe; -use super::device::{Endpoint, TransferType}; - -use super::usbproto::*; +use usb_host::{ + Endpoint, RequestCode, RequestDirection, RequestType, SetupPacket, TransferType, WValue, +}; use atsamd_hal::target_device::usb::{ self, host::{BINTERVAL, PCFG, PINTFLAG, PSTATUS, PSTATUSCLR, PSTATUSSET}, }; use core::convert::TryInto; -use log::{trace, warn}; +use log::trace; // Maximum time to wait for a control request with data to finish. cf // §9.2.6.1 of USB 2.0. @@ -33,9 +39,6 @@ const MAX_PIPES: usize = 8; // How many times to retry a transaction that has transient errors. const NAK_LIMIT: usize = 15; -// For use in control requests that do not require a data stage. -pub(crate) const NO_DATA_STAGE: Option<&mut ()> = None; - #[derive(Copy, Clone, Debug, PartialEq)] pub(crate) enum PipeErr { ShortPacket, @@ -78,31 +81,31 @@ impl PipeTable { pub(crate) fn pipe_for<'a, 'b>( &'a mut self, host: &'b mut usb::HOST, - endpoint: &Endpoint, + endpoint: &dyn Endpoint, ) -> Pipe<'a, 'b> { // Just use two pipes for now. 0 is always for control // endpoints, 1 for everything else. // // TODO: cache in-use pipes and return them without init if // possible. - let i = if endpoint.num == 0 { 0 } else { 1 }; + let i = if endpoint.endpoint_num() == 0 { 0 } else { 1 }; let pregs = PipeRegs::from(host, i); let pdesc = &mut self.tbl[i]; pregs.cfg.write(|w| { - let ptype = PType::from(endpoint.transfer_type) as u8; + let ptype = PType::from(endpoint.transfer_type()) as u8; unsafe { w.ptype().bits(ptype) } }); trace!( "setting paddr of pipe {} to {}:{}", i, - endpoint.addr, - endpoint.num + endpoint.address(), + endpoint.endpoint_num() ); pdesc.bank0.ctrl_pipe.write(|w| { - w.pdaddr().set_addr(endpoint.addr); - w.pepnum().set_epnum(endpoint.num) + w.pdaddr().set_addr(endpoint.address()); + w.pepnum().set_epnum(endpoint.endpoint_num()) }); Pipe { num: i, @@ -120,30 +123,26 @@ pub(crate) struct Pipe<'a, 'b> { pub(crate) desc: &'a mut PipeDesc, } impl Pipe<'_, '_> { - pub(crate) fn control_transfer<T>( + pub(crate) fn control_transfer( &mut self, - ep: &mut Endpoint, + ep: &mut dyn Endpoint, bm_request_type: RequestType, b_request: RequestCode, w_value: WValue, w_index: u16, - buf: Option<&mut T>, + buf: Option<&mut [u8]>, millis: &dyn Fn() -> usize, - ) -> Result<(), PipeErr> { - let len = match buf { - None => 0, - _ => core::mem::size_of::<T>(), - }; - + ) -> Result<usize, PipeErr> { /* * Setup stage. */ + let buflen = buf.as_ref().map_or(0, |b| b.len() as u16); let mut setup_packet = SetupPacket { bm_request_type: bm_request_type, b_request: b_request, w_value: w_value, w_index: w_index, - w_length: len as u16, + w_length: buflen, }; self.send( ep, @@ -156,16 +155,17 @@ impl Pipe<'_, '_> { /* * Data stage. */ + let mut transfer_len = 0; if let Some(b) = buf { // TODO: data stage, has up to 5,000ms (in 500ms // per-packet chunks) to complete. cf §9.2.6.4 of USB 2.0. match bm_request_type.direction()? { RequestDirection::DeviceToHost => { - self.in_transfer(ep, b, NAK_LIMIT, millis)?; + transfer_len = self.in_transfer(ep, b, NAK_LIMIT, millis)?; } RequestDirection::HostToDevice => { - self.out_transfer(ep, b, NAK_LIMIT, millis)?; + transfer_len = self.out_transfer(ep, b, NAK_LIMIT, millis)?; } } } @@ -188,12 +188,12 @@ impl Pipe<'_, '_> { trace!("dispatching status stage"); self.dispatch_retries(ep, token, NAK_LIMIT, millis)?; - Ok(()) + Ok(transfer_len) } fn send( &mut self, - ep: &mut Endpoint, + ep: &mut dyn Endpoint, token: PToken, buf: &DataBuf, nak_limit: usize, @@ -214,21 +214,19 @@ impl Pipe<'_, '_> { self.dispatch_retries(ep, token, nak_limit, millis) } - pub(crate) fn in_transfer<T>( + pub(crate) fn in_transfer( &mut self, - ep: &mut Endpoint, - buf: &mut T, + ep: &mut dyn Endpoint, + buf: &mut [u8], nak_limit: usize, millis: &dyn Fn() -> usize, ) -> Result<usize, PipeErr> { // TODO: pull this from pipe descriptor for this addr/ep. let packet_size = 8; - let db: DataBuf = buf.into(); - - trace!("p{}: Should IN for {}b.", self.num, db.len); + trace!("p{}: Should IN for {}b.", self.num, buf.len()); self.desc.bank0.pcksize.write(|w| { - unsafe { w.byte_count().bits(db.len as u16) }; + unsafe { w.byte_count().bits(buf.len() as u16) }; unsafe { w.multi_packet_size().bits(0) } }); @@ -240,12 +238,12 @@ impl Pipe<'_, '_> { // variable length data is desired by the driver. cf §5.3.2 of // USB 2.0. let mut bytes_received = 0; - while bytes_received < db.len { + while bytes_received < buf.len() { // Move the buffer pointer forward as we get data. - self.desc - .bank0 - .addr - .write(|w| unsafe { w.addr().bits(db.ptr as u32 + bytes_received as u32) }); + self.desc.bank0.addr.write(|w| unsafe { + w.addr() + .bits(buf.as_mut_ptr() as u32 + bytes_received as u32) + }); self.regs.statusclr.write(|w| w.bk0rdy().set_bit()); trace!("--- !!! regs-pre-dispatch !!! ---"); self.log_regs(); @@ -253,17 +251,17 @@ impl Pipe<'_, '_> { self.dispatch_retries(ep, PToken::In, nak_limit, millis)?; let recvd = self.desc.bank0.pcksize.read().byte_count().bits() as usize; bytes_received += recvd; - trace!("!! read {} of {}", bytes_received, db.len); + trace!("!! read {} of {}", bytes_received, buf.len()); if recvd < packet_size { break; } // Don't allow writing past the buffer. - assert!(bytes_received <= db.len); + assert!(bytes_received <= buf.len()); } self.regs.statusset.write(|w| w.pfreeze().set_bit()); - if bytes_received < db.len { + if bytes_received < buf.len() { self.log_regs(); // TODO: honestly, this is probably a panic condition, // since whatever's in DataBuf.ptr is totally @@ -277,38 +275,36 @@ impl Pipe<'_, '_> { } } - pub(crate) fn out_transfer<T>( + pub(crate) fn out_transfer( &mut self, - ep: &mut Endpoint, - buf: &mut T, + ep: &mut dyn Endpoint, + buf: &[u8], nak_limit: usize, millis: &dyn Fn() -> usize, ) -> Result<usize, PipeErr> { - let db: DataBuf = buf.into(); - - trace!("p{}: Should OUT for {}b.", self.num, db.len); + trace!("p{}: Should OUT for {}b.", self.num, buf.len()); self.desc.bank0.pcksize.write(|w| { - unsafe { w.byte_count().bits(db.len as u16) }; + unsafe { w.byte_count().bits(buf.len() as u16) }; unsafe { w.multi_packet_size().bits(0) } }); let mut bytes_sent = 0; - while bytes_sent < db.len { + while bytes_sent < buf.len() { self.desc .bank0 .addr - .write(|w| unsafe { w.addr().bits(db.ptr as u32 + bytes_sent as u32) }); + .write(|w| unsafe { w.addr().bits(buf.as_ptr() as u32 + bytes_sent as u32) }); self.dispatch_retries(ep, PToken::Out, nak_limit, millis)?; let sent = self.desc.bank0.pcksize.read().byte_count().bits() as usize; bytes_sent += sent; - trace!("!! wrote {} of {}", bytes_sent, db.len); + trace!("!! wrote {} of {}", bytes_sent, buf.len()); } Ok(bytes_sent) } - pub(crate) fn dtgl(&mut self, ep: &mut Endpoint, token: PToken) { + fn dtgl(&mut self, ep: &mut dyn Endpoint, token: PToken) { // 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 @@ -317,28 +313,40 @@ impl Pipe<'_, '_> { "~~~ tok: {:?}, dtgl: {}, i: {}, o: {}", token, self.regs.status.read().dtgl().bit(), - ep.in_toggle, - ep.out_toggle, + ep.in_toggle(), + ep.out_toggle(), ); - if self.regs.status.read().dtgl().bit_is_set() { + + let toggle = match token { + PToken::In => { + let t = !ep.in_toggle(); + ep.set_in_toggle(t); + t + } + + PToken::Out => { + let t = !ep.out_toggle(); + ep.set_out_toggle(t); + t + } + + _ => !self.regs.status.read().dtgl().bit_is_set(), + }; + + if toggle { self.dtgl_set(); } else { self.dtgl_clear(); } - if token == PToken::In { - ep.in_toggle = self.regs.status.read().dtgl().bit_is_set() - } else { - ep.out_toggle = self.regs.status.read().dtgl().bit_is_set() - } } - pub(crate) fn dtgl_set(&mut self) { + fn dtgl_set(&mut self) { self.regs.statusset.write(|w| w.dtgl().set_bit()); } - pub(crate) fn dtgl_clear(&mut self) { + fn dtgl_clear(&mut self) { self.regs.statusclr.write(|w| unsafe { - // No function for this. FIXME: need to patch the SVD for + // 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) @@ -350,7 +358,7 @@ impl Pipe<'_, '_> { // non-blocking. fn dispatch_retries( &mut self, - ep: &mut Endpoint, + ep: &mut dyn Endpoint, token: PToken, retries: usize, millis: &dyn Fn() -> usize, @@ -365,9 +373,9 @@ impl Pipe<'_, '_> { match res { Ok(true) => { if token == PToken::In { - ep.in_toggle = self.regs.status.read().dtgl().bit_is_set(); + ep.set_in_toggle(self.regs.status.read().dtgl().bit_is_set()); } else if token == PToken::Out { - ep.out_toggle = self.regs.status.read().dtgl().bit_is_set(); + ep.set_out_toggle(self.regs.status.read().dtgl().bit_is_set()); } return Ok(()); } @@ -382,7 +390,7 @@ impl Pipe<'_, '_> { // a NAK, which in this context means there's // no data. cf §32.8.7.5 of SAM D21 data // sheet. - PipeErr::Flow if ep.transfer_type == TransferType::Interrupt => break, + PipeErr::Flow if ep.transfer_type() == TransferType::Interrupt => break, PipeErr::Stall => break, _ => { @@ -401,7 +409,7 @@ impl Pipe<'_, '_> { Err(last_err) } - fn dispatch_packet(&mut self, ep: &mut Endpoint, token: PToken) { + fn dispatch_packet(&mut self, ep: &mut dyn Endpoint, token: PToken) { self.regs .cfg .modify(|_, w| unsafe { w.ptoken().bits(token as u8) }); @@ -417,12 +425,12 @@ impl Pipe<'_, '_> { // sequence at end of setup transaction. cf §8.6.1 of // USB 2.0. self.dtgl_clear(); - ep.in_toggle = true; - ep.out_toggle = true; + ep.set_in_toggle(true); + ep.set_out_toggle(true); } PToken::In => { self.regs.statusclr.write(|w| w.bk0rdy().set_bit()); - if ep.in_toggle { + if ep.in_toggle() { self.dtgl_set(); } else { self.dtgl_clear(); @@ -431,7 +439,7 @@ impl Pipe<'_, '_> { PToken::Out => { self.regs.intflag.write(|w| w.trcpt0().set_bit()); self.regs.statusset.write(|w| w.bk0rdy().set_bit()); - if ep.out_toggle { + if ep.out_toggle() { self.dtgl_set(); } else { self.dtgl_clear(); @@ -552,6 +560,7 @@ pub(crate) enum PToken { } // TODO: merge into SVD for pipe cfg register. +#[allow(unused)] #[derive(Copy, Clone, Debug, PartialEq)] pub(crate) enum PType { Disabled = 0x0, diff --git a/usbh/src/pipe/addr.rs b/samd21-host/src/pipe/addr.rs index aa26048..b3548cf 100644 --- a/usbh/src/pipe/addr.rs +++ b/samd21-host/src/pipe/addr.rs @@ -6,13 +6,13 @@ /// Property: NA #[derive(Clone, Copy, Debug)] #[repr(C, packed)] -pub(crate) struct Addr(u32); +pub struct Addr(u32); -pub(crate) struct R { +pub struct R { bits: u32, } -pub(crate) struct W { +pub struct W { bits: u32, } @@ -63,7 +63,7 @@ impl R { /// These bits define the data pointer address as an absolute double /// word address in RAM. The two least significant bits must be zero /// to ensure the descriptor is 32-bit aligned. -pub(crate) struct AddrR(u32); +pub struct AddrR(u32); impl AddrR { pub fn bits(&self) -> u32 { self.0 @@ -82,7 +82,7 @@ impl W { } } -pub(crate) struct AddrW<'a> { +pub struct AddrW<'a> { w: &'a mut W, } impl<'a> AddrW<'a> { diff --git a/usbh/src/pipe/ctrl_pipe.rs b/samd21-host/src/pipe/ctrl_pipe.rs index 179b615..9301d51 100644 --- a/usbh/src/pipe/ctrl_pipe.rs +++ b/samd21-host/src/pipe/ctrl_pipe.rs @@ -5,13 +5,13 @@ /// Property: PAC Write-Protection, Write-Synchronized, Read-Synchronized #[derive(Clone, Copy, Debug)] #[repr(C, packed)] -pub(crate) struct CtrlPipe(u16); +pub struct CtrlPipe(u16); -pub(crate) struct R { +pub struct R { bits: u16, } -pub(crate) struct W { +pub struct W { bits: u16, } @@ -77,7 +77,7 @@ impl R { /// /// These bits define the maximum number of error for this Pipe before /// freezing the pipe automatically. -pub(crate) struct PErMaxR(u8); +pub struct PErMaxR(u8); impl PErMaxR { pub fn max(&self) -> u8 { self.0 @@ -87,7 +87,7 @@ impl PErMaxR { /// Pipe EndPoint Number /// /// These bits define the number of endpoint for this Pipe. -pub(crate) struct PEpNumR(u8); +pub struct PEpNumR(u8); impl PEpNumR { pub fn epnum(&self) -> u8 { self.0 @@ -97,7 +97,7 @@ impl PEpNumR { /// Pipe Device Address /// /// These bits define the Device Address for this pipe. -pub(crate) struct PDAddrR(u8); +pub struct PDAddrR(u8); impl PDAddrR { pub fn addr(&self) -> u8 { self.0 @@ -125,7 +125,7 @@ impl W { } } -pub(crate) struct PErMaxW<'a> { +pub struct PErMaxW<'a> { w: &'a mut W, } impl<'a> PErMaxW<'a> { @@ -142,7 +142,7 @@ impl<'a> PErMaxW<'a> { } } -pub(crate) struct PEpNumW<'a> { +pub struct PEpNumW<'a> { w: &'a mut W, } impl<'a> PEpNumW<'a> { @@ -159,7 +159,7 @@ impl<'a> PEpNumW<'a> { } } -pub(crate) struct PDAddrW<'a> { +pub struct PDAddrW<'a> { w: &'a mut W, } impl<'a> PDAddrW<'a> { diff --git a/usbh/src/pipe/ext_reg.rs b/samd21-host/src/pipe/ext_reg.rs index 9778f1b..9778f1b 100644 --- a/usbh/src/pipe/ext_reg.rs +++ b/samd21-host/src/pipe/ext_reg.rs diff --git a/usbh/src/pipe/pck_size.rs b/samd21-host/src/pipe/pck_size.rs index acc499f..acc499f 100644 --- a/usbh/src/pipe/pck_size.rs +++ b/samd21-host/src/pipe/pck_size.rs diff --git a/usbh/src/pipe/status_bk.rs b/samd21-host/src/pipe/status_bk.rs index 489fc62..489fc62 100644 --- a/usbh/src/pipe/status_bk.rs +++ b/samd21-host/src/pipe/status_bk.rs diff --git a/usbh/src/pipe/status_pipe.rs b/samd21-host/src/pipe/status_pipe.rs index be135c5..be135c5 100644 --- a/usbh/src/pipe/status_pipe.rs +++ b/samd21-host/src/pipe/status_pipe.rs diff --git a/usb-host/src/lib.rs b/usb-host/src/lib.rs index 831a67c..551824a 100644..100755 --- a/usb-host/src/lib.rs +++ b/usb-host/src/lib.rs @@ -1,10 +1,13 @@ +#![no_std] + mod descriptor; mod setup; pub use descriptor::*; pub use setup::*; -pub enum Error { +#[derive(Debug)] +pub enum TransferError { Retry(&'static str), Permanent(&'static str), } @@ -18,11 +21,15 @@ pub trait USBHost { w_value: WValue, w_index: u16, buf: Option<&mut [u8]>, - ) -> Result<usize, Error>; + ) -> Result<usize, TransferError>; - fn in_transfer(&mut self, ep: &mut dyn Endpoint, buf: &mut [u8]) -> Result<usize, Error>; + fn in_transfer( + &mut self, + ep: &mut dyn Endpoint, + buf: &mut [u8], + ) -> Result<usize, TransferError>; - fn out_transfer(&mut self, ep: &mut dyn Endpoint, buf: &[u8]) -> Result<usize, Error>; + fn out_transfer(&mut self, ep: &mut dyn Endpoint, buf: &[u8]) -> Result<usize, TransferError>; } // cf §9.6.6 of USB 2.0 @@ -40,6 +47,7 @@ pub enum Direction { Out, In, } + pub trait Endpoint { fn address(&self) -> u8; @@ -60,17 +68,23 @@ pub trait Endpoint { fn set_out_toggle(&mut self, toggle: bool); } -pub trait Driver { +#[derive(Copy, Clone, Debug)] +pub enum DriverError { + Retry(u8, &'static str), + Permanent(u8, &'static str), +} +pub trait Driver: core::fmt::Debug { fn want_device(&self, device: &DeviceDescriptor) -> bool; - fn add_device(&mut self, device: DeviceDescriptor, address: u8) -> Result<(), Error>; + fn add_device(&mut self, device: DeviceDescriptor, address: u8) -> Result<(), DriverError>; fn remove_device(&mut self, address: u8); - fn tick_device( - &mut self, - address: u8, - millis: usize, - usbhost: &mut dyn USBHost, - ) -> Result<(), Error>; + fn tick(&mut self, millis: usize, usbhost: &mut dyn USBHost) -> Result<(), DriverError>; } + +// TODO: There needs to be a per-interface/function driver trait, as +// well, since that's how most drivers will actually work. +// +// As a result, the host driver has to at least get the full +// configuration. diff --git a/usbh/Cargo.lock b/usbh/Cargo.lock deleted file mode 100644 index 5b973da..0000000 --- a/usbh/Cargo.lock +++ /dev/null @@ -1,269 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "aligned" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "aligned" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "as-slice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "as-slice" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "atsamd-hal" -version = "0.5.0" -dependencies = [ - "atsamd21e18a 0.4.0", - "bitfield 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "paste 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "atsamd21e18a" -version = "0.4.0" -dependencies = [ - "bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bare-metal" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bitfield" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cortex-m" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-m 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cortex-m" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aligned 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "embedded-hal" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "generic-array" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "log" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "nb" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "paste" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "paste-impl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "paste-impl" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro-hack 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rb" -version = "0.1.0" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "stable_deref_trait" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "syn" -version = "0.15.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "typenum" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "usbh" -version = "0.1.0" -dependencies = [ - "atsamd-hal 0.5.0", - "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "rb 0.1.0", - "vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "vcell" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "volatile-register" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d39da9b88ae1a81c03c9c082b8db83f1d0e93914126041962af61034ab44c4a5" -"checksum aligned 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d3a316c7ea8e1e9ece54862c992def5a7ac14de9f5832b69d71760680efeeefa" -"checksum as-slice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "293dac66b274fab06f95e7efb05ec439a6b70136081ea522d270bc351ae5bb27" -"checksum bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a3caf393d93b2d453e80638d0674597020cef3382ada454faacd43d1a55a735a" -"checksum bitfield 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" -"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" -"checksum cortex-m 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0b159a1e8306949579de3698c841dba58058197b65c60807194e4fa1e7a554" -"checksum cortex-m 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f3c18719fdc57db65668bfc977db9a0fa1a41d718c5d9cd4f652c9d4b0e0956a" -"checksum embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b" -"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c275b6ad54070ac2d665eef9197db647b32239c9d244bfb6f041a766d00da5b3" -"checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc" -"checksum paste 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1f4a4a1c555c6505821f9d58b8779d0f630a6b7e4e1be24ba718610acf01fa79" -"checksum paste-impl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "26e796e623b8b257215f27e6c80a5478856cae305f5b59810ff9acdaa34570e6" -"checksum proc-macro-hack 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "982a35d1194084ba319d65c4a68d24ca28f5fdb5b8bc20899e4eef8641ea5178" -"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" -"checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e" -"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d" -"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" diff --git a/usbh/Cargo.toml b/usbh/Cargo.toml deleted file mode 100644 index ecbd6c5..0000000 --- a/usbh/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "usbh" -version = "0.1.0" -authors = ["Brian Cully <bjc@kublai.com>"] -edition = "2018" -license = "GPL-3.0-or-later" - -[dependencies] -rb = { path = "../rb" } -atsamd-hal = { path = "../../atsamd/hal" } -embedded-hal = "~0.2" -log = "~0.4" -vcell = "~0.1" - -[features] -default = ["atsamd-hal/samd21e18a"] 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<PipeErr> 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<Device>; 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<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`. - 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<Device> { - 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<u8> = 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<Endpoint>; 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<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: 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::<DeviceDescriptor>::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::<ConfigurationDescriptor>::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, - } - } -} diff --git a/usbh/src/usbproto.rs b/usbh/src/usbproto.rs deleted file mode 100644 index 33bc28c..0000000 --- a/usbh/src/usbproto.rs +++ /dev/null @@ -1,438 +0,0 @@ -/// USB Protocol level types and functions. -/// -/// Everything in here is defined by the USB specification, and -/// hardware independent. -use core::convert::TryFrom; -use core::convert::TryInto; - -// TODO: Put protocol section references in for types and -// documentation. - -#[derive(Copy, Clone, Debug, Default, PartialEq)] -#[repr(C, packed)] -pub struct DeviceDescriptor { - pub b_length: u8, - pub b_descriptor_type: DescriptorType, - pub bcd_usb: u16, - pub b_device_class: u8, - pub b_device_sub_class: u8, - pub b_device_protocol: u8, - pub b_max_packet_size: u8, - pub id_vendor: u16, - pub id_product: u16, - pub bcd_device: u16, - pub i_manufacturer: u8, - pub i_product: u8, - pub i_serial_number: u8, - pub b_num_configurations: u8, -} - -#[derive(Copy, Clone, Debug, Default, PartialEq)] -#[repr(C, packed)] -pub struct ConfigurationDescriptor { - pub b_length: u8, - pub b_descriptor_type: DescriptorType, - pub w_total_length: u16, - pub b_num_interfaces: u8, - pub b_configuration_value: u8, - pub i_configuration: u8, - pub bm_attributes: u8, - pub b_max_power: u8, -} - -#[derive(Copy, Clone, Debug, Default, PartialEq)] -#[repr(C, packed)] -pub struct InterfaceDescriptor { - pub b_length: u8, - pub b_descriptor_type: DescriptorType, - pub b_interface_number: u8, - pub b_alternate_setting: u8, - pub b_num_endpoints: u8, - pub b_interface_class: u8, - pub b_interface_sub_class: u8, - pub b_interface_protocol: u8, - pub i_interface: u8, -} - -#[derive(Copy, Clone, Debug, Default, PartialEq)] -#[repr(C, packed)] -pub struct EndpointDescriptor { - pub b_length: u8, - pub b_descriptor_type: DescriptorType, - pub b_endpoint_address: u8, - pub bm_attributes: u8, - pub w_max_packet_size: u16, - pub b_interval: u8, -} - -#[derive(Copy, Clone, Debug, Default, PartialEq)] -#[repr(C, packed)] -pub struct SetupPacket { - pub bm_request_type: RequestType, - pub b_request: RequestCode, - pub w_value: WValue, - pub w_index: u16, - pub w_length: u16, -} - -#[derive(Clone, Copy, Debug, Default, PartialEq)] -#[repr(C)] -pub struct RequestType(u8); -impl RequestType { - // Get descriptor request type. - pub fn get_descr() -> Self { - Self::from(( - RequestDirection::DeviceToHost, - RequestKind::Standard, - RequestRecipient::Device, - )) - } - - // Set request type for all but 'set feature' and 'set interface'. - pub fn set() -> Self { - Self::from(( - RequestDirection::HostToDevice, - RequestKind::Standard, - RequestRecipient::Device, - )) - } - - // Get interface request type. - pub fn cl_get_intf() -> Self { - Self::from(( - RequestDirection::DeviceToHost, - RequestKind::Class, - RequestRecipient::Interface, - )) - } - - pub fn recipient(&self) -> Result<RequestRecipient, &'static str> { - const POS: u8 = 0; - const MASK: u8 = 0x1f; - (self.0 & (MASK << POS)).try_into() - } - - pub fn set_recipient(&mut self, v: RequestRecipient) { - const POS: u8 = 0; - const MASK: u8 = 0x1f; - self.0 &= !(MASK << POS); - self.0 |= v as u8 & MASK; - } - - pub fn kind(&self) -> Result<RequestKind, &'static str> { - const POS: u8 = 5; - const MASK: u8 = 0x3; - (self.0 & (MASK << POS)).try_into() - } - - pub fn set_kind(&mut self, v: RequestKind) { - const POS: u8 = 5; - const MASK: u8 = 0x3; - self.0 &= !(MASK << POS); - self.0 |= v as u8 & MASK; - } - - pub fn direction(&self) -> Result<RequestDirection, &'static str> { - const POS: u8 = 7; - const MASK: u8 = 0x1; - (self.0 & (MASK << POS)).try_into() - } - - pub fn set_direction(&mut self, v: RequestDirection) { - const POS: u8 = 7; - const MASK: u8 = 0x1; - self.0 &= !(MASK << POS); - self.0 |= v as u8 & MASK; - } -} -impl From<(RequestDirection, RequestKind, RequestRecipient)> for RequestType { - fn from(v: (RequestDirection, RequestKind, RequestRecipient)) -> Self { - Self(v.0 as u8 | v.1 as u8 | v.2 as u8) - } -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum RequestDirection { - HostToDevice = 0x00, - DeviceToHost = 0x80, -} -impl TryFrom<u8> for RequestDirection { - type Error = &'static str; - - fn try_from(v: u8) -> Result<Self, Self::Error> { - match v { - 0x00 => Ok(Self::HostToDevice), - 0x80 => Ok(Self::DeviceToHost), - _ => Err("direction can only be 0x00 or 0x80"), - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum RequestKind { - Standard = 0x00, - Class = 0x20, - Vendor = 0x40, -} -impl TryFrom<u8> for RequestKind { - type Error = &'static str; - - fn try_from(v: u8) -> Result<Self, Self::Error> { - match v { - 0x00 => Ok(Self::Standard), - 0x20 => Ok(Self::Class), - 0x40 => Ok(Self::Vendor), - _ => Err("type can only be 0x00, 0x20, or 0x40"), - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum RequestRecipient { - Device = 0x00, - Interface = 0x01, - Endpoint = 0x02, - Other = 0x03, -} -impl TryFrom<u8> for RequestRecipient { - type Error = &'static str; - - fn try_from(v: u8) -> Result<Self, Self::Error> { - match v { - 0x00 => Ok(Self::Device), - 0x01 => Ok(Self::Interface), - 0x02 => Ok(Self::Endpoint), - 0x03 => Ok(Self::Other), - _ => Err("recipient can only be between 0 and 3"), - } - } -} - -#[derive(Clone, Copy, Debug, Default, PartialEq)] -#[repr(C)] -pub struct WValue(u16); -impl WValue { - pub fn w_value_lo(&self) -> u8 { - const POS: u8 = 0; - const MASK: u16 = 0xff; - ((self.0 >> POS) & MASK) as u8 - } - - pub fn set_w_value_lo(&mut self, v: u8) { - const POS: u8 = 0; - const MASK: u8 = 0xff; - self.0 &= !((MASK as u16) << POS); - self.0 |= ((v & MASK) as u16) << POS; - } - - pub fn w_value_hi(&self) -> u8 { - const POS: u8 = 8; - const MASK: u16 = 0xff; - ((self.0 >> POS) & MASK) as u8 - } - - pub fn set_w_value_hi(&mut self, v: u8) { - const POS: u8 = 8; - const MASK: u8 = 0xff; - self.0 &= !((MASK as u16) << POS); - self.0 |= ((v & MASK) as u16) << POS; - } -} -impl From<(u8, u8)> for WValue { - fn from(v: (u8, u8)) -> Self { - let mut rc = Self(0); - rc.set_w_value_lo(v.0); - rc.set_w_value_hi(v.1); - rc - } -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum RequestCode { - GetStatus = 0, - ClearFeature = 1, - SetFeature = 3, - SetAddress = 5, - GetDescriptor = 6, - SetDescriptor = 7, - GetConfiguration = 8, - SetConfiguration = 9, - GetInterface = 10, - SetInterface = 11, - SynchFrame = 12, -} -impl TryFrom<u8> for RequestCode { - type Error = &'static str; - - fn try_from(v: u8) -> Result<Self, Self::Error> { - match v { - 0 => Ok(Self::GetStatus), - 1 => Ok(Self::ClearFeature), - 3 => Ok(Self::SetFeature), - 5 => Ok(Self::SetAddress), - 6 => Ok(Self::GetDescriptor), - 7 => Ok(Self::SetDescriptor), - 8 => Ok(Self::GetConfiguration), - 9 => Ok(Self::SetConfiguration), - 10 => Ok(Self::GetInterface), - 11 => Ok(Self::SetInterface), - 12 => Ok(Self::SynchFrame), - _ => Err("invalid request value"), - } - } -} -impl Default for RequestCode { - fn default() -> Self { - Self::GetStatus - } -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum USBFeature { - EndpointHalt = 0, - DeviceRemoteWakeup = 1, - TestMode = 2, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum DescriptorType { - Device = 1, - Configuration = 2, - String = 3, - Interface = 4, - Endpoint = 5, - DeviceQualifier = 6, - OtherSpeed = 7, - InterfacePower = 8, -} -impl Default for DescriptorType { - fn default() -> Self { - Self::Device - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn dev_desc_serialize() { - let desc = DeviceDescriptor { - b_length: 18, - b_descriptor_type: DescriptorType::Device, - bcd_usb: 0x0110, - b_device_class: 0x02, - b_device_sub_class: 0x03, - b_device_protocol: 0x04, - b_max_packet_size: 64, - id_vendor: 0xdead, - id_product: 0xbeef, - bcd_device: 0x5432, - i_manufacturer: 0xa0, - i_product: 0xa1, - i_serial_number: 0xa2, - b_num_configurations: 1, - }; - let want = [ - 0x12, 0x01, 0x10, 0x01, 0x02, 0x03, 0x04, 64, 0xad, 0xde, 0xef, 0xbe, 0x32, 0x54, 0xa0, - 0xa1, 0xa2, 1, - ]; - - serde_test(&desc, &want); - } - - #[test] - fn config_desc_serialize() { - let desc = ConfigurationDescriptor { - b_length: 18, - b_descriptor_type: DescriptorType::Configuration, - w_total_length: 0x20, - b_num_interfaces: 5, - b_configuration_value: 10, - i_configuration: 1, - bm_attributes: 0x40, - b_max_power: 0xfa, - }; - let want = [0x12, 0x02, 0x20, 0x00, 0x05, 0x0a, 0x01, 0x40, 0xfa]; - - serde_test(&desc, &want); - } - - #[test] - fn interface_desc_serialize() { - let desc = InterfaceDescriptor { - b_length: 18, - b_descriptor_type: DescriptorType::Interface, - b_interface_number: 2, - b_alternate_setting: 0xaa, - b_num_endpoints: 5, - b_interface_class: 0x11, - b_interface_sub_class: 0x22, - b_interface_protocol: 0x33, - i_interface: 10, - }; - let want = [0x12, 0x04, 0x02, 0xaa, 0x05, 0x11, 0x22, 0x33, 0x0a]; - - serde_test(&desc, &want); - } - - #[test] - fn endpoint_desc_serialize() { - let desc = EndpointDescriptor { - b_length: 18, - b_descriptor_type: DescriptorType::Endpoint, - b_endpoint_address: 1, - bm_attributes: 0x22, - w_max_packet_size: 0xdead, - b_interval: 0x33, - }; - let want = [0x12, 0x05, 0x01, 0x22, 0xad, 0xde, 0x33]; - - serde_test(&desc, &want); - } - - #[test] - fn setup_packet_serialize() { - let setup_packet = SetupPacket { - bm_request_type: RequestType::get_descr(), - b_request: RequestCode::GetDescriptor, - w_value: WValue::from((0x00, 0x01)), - w_index: 0xbeef, - w_length: 18, - }; - let want = [0x80, 0x06, 0x00, 0x01, 0xef, 0xbe, 0x012, 0x00]; - - serde_test(&setup_packet, &want); - } - - fn serde_test<T>(native: &T, raw: &[u8]) - where - T: Default + PartialEq + core::fmt::Debug, - { - ser_test(native, raw); - de_test(raw, native); - } - - fn ser_test<T>(native: &T, raw: &[u8]) { - let ptr = native as *const T as *const u8; - let len = core::mem::size_of::<T>(); - assert_eq!(len, raw.len()); - let got_raw = unsafe { core::slice::from_raw_parts(ptr, len) }; - assert_eq!(got_raw, raw); - } - - fn de_test<T>(raw: &[u8], native: &T) - where - T: Default + PartialEq + core::fmt::Debug, - { - let mut got_native: T = Default::default(); - let ptr = &mut got_native as *mut T as *mut u8; - let len = core::mem::size_of::<T>(); - assert_eq!(raw.len(), len); - let dbslice = unsafe { core::slice::from_raw_parts_mut(ptr, len) }; - for i in 0..len { - dbslice[i] = raw[i]; - } - assert_eq!(got_native, *native); - } -} |