aboutsummaryrefslogtreecommitdiffstats
path: root/usb-host/src
diff options
context:
space:
mode:
Diffstat (limited to 'usb-host/src')
-rw-r--r--usb-host/src/descriptor.rs68
-rw-r--r--usb-host/src/lib.rs76
-rw-r--r--usb-host/src/setup.rs196
3 files changed, 340 insertions, 0 deletions
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,
+}