diff options
author | Brian Cully <bjc@kublai.com> | 2019-08-02 22:46:31 -0400 |
---|---|---|
committer | Brian Cully <bjc@kublai.com> | 2019-08-02 22:46:31 -0400 |
commit | 024fc1503cb73e538dcd05258a6a63059029a48f (patch) | |
tree | 55f02e523b0f59cc5bd7382df3f5473d3f2f8986 | |
parent | f560a4e9ce76aca32765409f53b570bf0e3c6b4c (diff) | |
download | samd21-demo-024fc1503cb73e538dcd05258a6a63059029a48f.tar.gz samd21-demo-024fc1503cb73e538dcd05258a6a63059029a48f.zip |
Add traits for host controller and driver.
-rw-r--r-- | usb-host/Cargo.lock | 6 | ||||
-rw-r--r-- | usb-host/Cargo.toml | 6 | ||||
-rw-r--r-- | usb-host/src/descriptor.rs | 68 | ||||
-rw-r--r-- | usb-host/src/lib.rs | 76 | ||||
-rw-r--r-- | usb-host/src/setup.rs | 196 |
5 files changed, 352 insertions, 0 deletions
diff --git a/usb-host/Cargo.lock b/usb-host/Cargo.lock new file mode 100644 index 0000000..7724e68 --- /dev/null +++ b/usb-host/Cargo.lock @@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "usb-host" +version = "0.1.0" + diff --git a/usb-host/Cargo.toml b/usb-host/Cargo.toml new file mode 100644 index 0000000..69d1e02 --- /dev/null +++ b/usb-host/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "usb-host" +version = "0.1.0" +authors = ["Brian Cully <bjc@kublai.com>"] +edition = "2018" +license = "GPL-3.0-or-later" diff --git a/usb-host/src/descriptor.rs b/usb-host/src/descriptor.rs new file mode 100644 index 0000000..8461413 --- /dev/null +++ b/usb-host/src/descriptor.rs @@ -0,0 +1,68 @@ +#[derive(Clone, Copy, Debug)] +pub enum DescriptorType { + Device = 1, + Configuration = 2, + String = 3, + Interface = 4, + Endpoint = 5, + DeviceQualifier = 6, + OtherSpeed = 7, + InterfacePower = 8, +} + +#[derive(Copy, Clone, Debug)] +#[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)] +#[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)] +#[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)] +#[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, +} diff --git a/usb-host/src/lib.rs b/usb-host/src/lib.rs new file mode 100644 index 0000000..831a67c --- /dev/null +++ b/usb-host/src/lib.rs @@ -0,0 +1,76 @@ +mod descriptor; +mod setup; + +pub use descriptor::*; +pub use setup::*; + +pub enum Error { + Retry(&'static str), + Permanent(&'static str), +} + +pub trait USBHost { + 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, Error>; + + fn in_transfer(&mut self, ep: &mut dyn Endpoint, buf: &mut [u8]) -> Result<usize, Error>; + + fn out_transfer(&mut self, ep: &mut dyn Endpoint, buf: &[u8]) -> Result<usize, Error>; +} + +// cf ยง9.6.6 of USB 2.0 +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum TransferType { + Control = 0, + Isochronous = 1, + Bulk = 2, + Interrupt = 3, +} + +// ibid +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Direction { + Out, + In, +} +pub trait Endpoint { + fn address(&self) -> u8; + + fn endpoint_num(&self) -> u8; + + fn transfer_type(&self) -> TransferType; + + fn direction(&self) -> Direction; + + fn max_packet_size(&self) -> u16; + + fn in_toggle(&self) -> bool; + + fn set_in_toggle(&mut self, toggle: bool); + + fn out_toggle(&self) -> bool; + + fn set_out_toggle(&mut self, toggle: bool); +} + +pub trait Driver { + fn want_device(&self, device: &DeviceDescriptor) -> bool; + + fn add_device(&mut self, device: DeviceDescriptor, address: u8) -> Result<(), Error>; + + fn remove_device(&mut self, address: u8); + + fn tick_device( + &mut self, + address: u8, + millis: usize, + usbhost: &mut dyn USBHost, + ) -> Result<(), Error>; +} diff --git a/usb-host/src/setup.rs b/usb-host/src/setup.rs new file mode 100644 index 0000000..8fa6414 --- /dev/null +++ b/usb-host/src/setup.rs @@ -0,0 +1,196 @@ +use core::convert::{TryFrom, TryInto}; + +#[derive(Clone, Copy, Debug, PartialEq)] +#[repr(C)] +pub struct RequestType(u8); +impl RequestType { + 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(Copy, Clone, Debug)] +#[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, +} |