aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian Cully <bjc@kublai.com>2019-08-02 22:46:48 -0400
committerBrian Cully <bjc@kublai.com>2019-08-02 22:46:48 -0400
commitabd478d9425dd2d4acd5b58202b1a95b73ff217b (patch)
tree3d3cd7d896c42ee4546d8e23234f53fbcfe84d93
parent024fc1503cb73e538dcd05258a6a63059029a48f (diff)
downloadsamd21-demo-abd478d9425dd2d4acd5b58202b1a95b73ff217b.tar.gz
samd21-demo-abd478d9425dd2d4acd5b58202b1a95b73ff217b.zip
Add boot keyboard driver.
-rw-r--r--bootkbd/Cargo.lock30
-rw-r--r--bootkbd/Cargo.toml11
-rw-r--r--bootkbd/src/lib.rs331
3 files changed, 372 insertions, 0 deletions
diff --git a/bootkbd/Cargo.lock b/bootkbd/Cargo.lock
new file mode 100644
index 0000000..a20b93c
--- /dev/null
+++ b/bootkbd/Cargo.lock
@@ -0,0 +1,30 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[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"
+
+[[package]]
+name = "log"
+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)",
+]
+
+[[package]]
+name = "usb-host"
+version = "0.1.0"
+
+[metadata]
+"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
+"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
diff --git a/bootkbd/Cargo.toml b/bootkbd/Cargo.toml
new file mode 100644
index 0000000..7b1b02d
--- /dev/null
+++ b/bootkbd/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "bootkbd"
+version = "0.1.0"
+authors = ["Brian Cully <bjc@kublai.com>"]
+edition = "2018"
+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
new file mode 100644
index 0000000..c143512
--- /dev/null
+++ b/bootkbd/src/lib.rs
@@ -0,0 +1,331 @@
+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,
+};
+
+use core::mem::{self, MaybeUninit};
+use core::ptr;
+
+// 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;
+
+// How many total devices this driver can support.
+const MAX_DEVICES: usize = 1;
+
+// And how many endpoints we can support per-device.
+const MAX_ENDPOINTS: usize = 2;
+
+pub struct BootKeyboard {
+ devices: [Option<Device>; MAX_DEVICES],
+}
+
+impl BootKeyboard {
+ pub fn new() -> Self {
+ Self {
+ devices: [None; MAX_DEVICES],
+ }
+ }
+}
+
+impl Driver for BootKeyboard {
+ fn want_device(&self, _device: &DeviceDescriptor) -> bool {
+ true
+ }
+
+ fn add_device(&mut self, _device: DeviceDescriptor, address: u8) -> Result<(), USBError> {
+ 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"))
+ }
+
+ fn remove_device(&mut self, address: u8) {
+ for i in 0..self.devices.len() {
+ if let Some(ref dev) = self.devices[i] {
+ if dev.addr == address {
+ self.devices[i] = None;
+ return;
+ }
+ }
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+ Err(USBError::Permanent("no device at address"))
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+enum DeviceState {
+ Addressed,
+ WaitForSettle(usize),
+ GetConfig,
+ SetConfig,
+ SetIdle,
+ SetReport,
+ Running,
+}
+
+struct Device {
+ addr: u8,
+ ep0: EP,
+ endpoints: [Option<EP>; MAX_ENDPOINTS],
+ state: DeviceState,
+}
+
+impl Device {
+ fn new(addr: u8) -> Self {
+ let endpoints: [Option<EP>; MAX_ENDPOINTS] = {
+ let mut eps: [MaybeUninit<Option<EP>>; MAX_ENDPOINTS] =
+ unsafe { mem::MaybeUninit::uninit().assume_init() };
+ for ep in &mut eps[..] {
+ unsafe { ptr::write(ep.as_mut_ptr(), None) }
+ }
+ unsafe { mem::transmute(eps) }
+ };
+
+ Self {
+ addr: addr,
+ ep0: EP::new(addr, 0, TransferType::Control, Direction::In),
+ endpoints: endpoints,
+ state: DeviceState::Addressed,
+ }
+ }
+
+ fn fsm(&mut self, millis: usize, host: &mut dyn USBHost) -> Result<(), USBError> {
+ // 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.
+ let none: Option<&mut [u8]> = None;
+ unsafe {
+ static mut LAST_STATE: DeviceState = DeviceState::Addressed;
+ if LAST_STATE != self.state {
+ debug!("{:?} -> {:?}", LAST_STATE, self.state);
+ LAST_STATE = self.state;
+ }
+ }
+
+ match self.state {
+ DeviceState::Addressed => {
+ self.state = DeviceState::WaitForSettle(millis + SETTLE_DELAY)
+ }
+
+ DeviceState::WaitForSettle(until) => {
+ if millis > until {
+ let mut dev_desc: MaybeUninit<DeviceDescriptor> = MaybeUninit::uninit();
+ let buf = unsafe { to_slice_mut(&mut dev_desc) };
+ let len = host.control_transfer(
+ &mut self.ep0,
+ RequestType::from((
+ RequestDirection::DeviceToHost,
+ RequestKind::Standard,
+ RequestRecipient::Device,
+ )),
+ RequestCode::GetDescriptor,
+ WValue::from((0, DescriptorType::Device as u8)),
+ 0,
+ Some(buf),
+ )?;
+ assert!(len == mem::size_of::<DeviceDescriptor>());
+ self.state = DeviceState::GetConfig
+ }
+ }
+
+ DeviceState::GetConfig => {
+ let mut conf_desc: MaybeUninit<ConfigurationDescriptor> = MaybeUninit::uninit();
+ let buf = unsafe { to_slice_mut(&mut conf_desc) };
+ let len = host.control_transfer(
+ &mut self.ep0,
+ RequestType::from((
+ RequestDirection::DeviceToHost,
+ RequestKind::Standard,
+ RequestRecipient::Device,
+ )),
+ RequestCode::GetDescriptor,
+ WValue::from((0, DescriptorType::Configuration as u8)),
+ 0,
+ Some(buf),
+ )?;
+ assert!(len == mem::size_of::<ConfigurationDescriptor>());
+ let conf_desc = unsafe { conf_desc.assume_init() };
+
+ // TODO: do a real allocation later.
+ assert!(conf_desc.w_total_length < 64);
+ let mut buf: [u8; 64] = [0; 64];
+ let mut tmp = &mut buf[..conf_desc.w_total_length as usize];
+ let len = host.control_transfer(
+ &mut self.ep0,
+ RequestType::from((
+ RequestDirection::DeviceToHost,
+ RequestKind::Standard,
+ RequestRecipient::Device,
+ )),
+ RequestCode::GetDescriptor,
+ WValue::from((0, DescriptorType::Configuration as u8)),
+ 0,
+ Some(&mut tmp),
+ )?;
+ assert!(len == conf_desc.w_total_length as usize);
+
+ self.state = DeviceState::SetConfig
+ }
+
+ DeviceState::SetConfig => {
+ // TODO: extract real configuration to choose.
+ let conf: u8 = 1;
+ host.control_transfer(
+ &mut self.ep0,
+ RequestType::from((
+ RequestDirection::HostToDevice,
+ RequestKind::Standard,
+ RequestRecipient::Device,
+ )),
+ RequestCode::SetConfiguration,
+ WValue::from((conf, 0)),
+ 0,
+ none,
+ )?;
+
+ self.endpoints[0] = Some(EP::new(
+ self.addr,
+ 1,
+ TransferType::Interrupt,
+ Direction::In,
+ ));
+
+ self.state = DeviceState::SetIdle
+ }
+
+ DeviceState::SetIdle => {
+ host.control_transfer(
+ &mut self.ep0,
+ RequestType::from((
+ RequestDirection::HostToDevice,
+ RequestKind::Class,
+ RequestRecipient::Interface,
+ )),
+ RequestCode::GetInterface,
+ WValue::from((0, 0)),
+ 0,
+ none,
+ )?;
+ self.state = DeviceState::SetReport
+ }
+
+ DeviceState::SetReport => {
+ let mut report: [u8; 1] = [0];
+ host.control_transfer(
+ &mut self.ep0,
+ RequestType::from((
+ RequestDirection::HostToDevice,
+ RequestKind::Class,
+ RequestRecipient::Interface,
+ )),
+ RequestCode::SetConfiguration,
+ WValue::from((0, 2)),
+ 0,
+ Some(&mut report),
+ )?;
+
+ self.state = DeviceState::Running
+ }
+
+ DeviceState::Running => {
+ 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(()),
+ Ok(len) => info!("report: {} - {:?}", len, buf),
+ }
+ }
+ }
+ }
+
+ Ok(())
+ }
+}
+
+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)
+}
+
+struct EP {
+ addr: u8,
+ num: u8,
+ transfer_type: TransferType,
+ direction: Direction,
+ in_toggle: bool,
+ out_toggle: bool,
+}
+
+impl EP {
+ fn new(addr: u8, num: u8, transfer_type: TransferType, direction: Direction) -> Self {
+ Self {
+ addr: addr,
+ num: num,
+ transfer_type: transfer_type,
+ direction: direction,
+ in_toggle: false,
+ out_toggle: false,
+ }
+ }
+}
+
+impl Endpoint for EP {
+ fn address(&self) -> u8 {
+ self.addr
+ }
+
+ fn endpoint_num(&self) -> u8 {
+ self.num
+ }
+
+ fn transfer_type(&self) -> TransferType {
+ self.transfer_type
+ }
+
+ fn direction(&self) -> Direction {
+ self.direction
+ }
+
+ 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
+ }
+}