| // Licensed under the Apache-2.0 license |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| #![cfg_attr(not(test), no_std)] |
| |
| //! USB Hardware Abstraction Layer (HAL) for blocking I/O. |
| //! |
| //! This module provides the foundational types and traits for implementing |
| //! USB device drivers and protocol stacks. It includes definitions for |
| //! standard USB requests, setup packets, and descriptors. |
| |
| mod descriptor; |
| pub mod driver; |
| |
| #[cfg(feature = "ufmt_console")] |
| use ufmt::derive::uDebug; |
| |
| pub use descriptor::*; |
| |
| // Big endian is dead; code in this file assumes little-endian |
| const _: () = assert!(cfg!(target_endian = "little")); |
| |
| /// Represents a USB control request. |
| /// |
| /// A request combines the `bmRequestType` and `bRequest` fields from a |
| /// USB SETUP packet into a single type-safe representation. |
| #[derive(Clone, Copy, Eq, PartialEq)] |
| #[repr(transparent)] |
| pub struct Request(u16); |
| #[allow(clippy::identity_op)] |
| impl Request { |
| /// Creates a new USB request. |
| pub const fn new( |
| direction: Direction, |
| ty: RequestType, |
| recipient: Recipient, |
| request: u8, |
| ) -> Self { |
| Self( |
| ((direction as u16) << 7) |
| | ((ty as u16) << 5) |
| | ((recipient as u16) << 0) |
| | ((request as u16) << 8), |
| ) |
| } |
| /// Returns the direction of the request (Host-to-Device or Device-to-Host). |
| pub fn direction(&self) -> Direction { |
| Direction::try_from((u32::from(self.0) >> 7) & 0x1).unwrap() |
| } |
| /// Returns the type of the request (Standard, Class, Vendor, or Reserved). |
| pub fn request_type(&self) -> RequestType { |
| RequestType::try_from(u32::from((self.0 >> 5) & 0x3)).unwrap() |
| } |
| /// Returns the recipient of the request (Device, Interface, Endpoint, or Other). |
| pub fn recipient(&self) -> Recipient { |
| Recipient::try_from(u32::from((self.0 >> 0) & 0x1f)).unwrap() |
| } |
| /// Returns the specific request code. |
| pub fn request(&self) -> u8 { |
| u8::try_from((self.0 >> 8) & 0xff).unwrap() |
| } |
| } |
| #[cfg(feature = "ufmt_console")] |
| impl ufmt::uDebug for Request { |
| fn fmt<W: ufmt::uWrite + ?Sized>( |
| &self, |
| f: &mut ufmt::Formatter<'_, W>, |
| ) -> Result<(), W::Error> { |
| f.debug_struct("usb::Request")? |
| .field("request_type", &self.request_type())? |
| .field("direction", &self.direction())? |
| .field("recipient", &self.recipient())? |
| .field("request", &self.request())? |
| .finish() |
| } |
| } |
| impl Request { |
| /// Standard DEVICE request to get the current status. |
| pub const DEVICE_GET_STATUS: Self = Self::new( |
| Direction::DeviceToHost, |
| RequestType::Standard, |
| Recipient::Device, |
| 0x00, |
| ); |
| /// Standard DEVICE request to clear a feature. |
| pub const DEVICE_CLEAR_FEATURE: Self = Self::new( |
| Direction::HostToDevice, |
| RequestType::Standard, |
| Recipient::Device, |
| 0x01, |
| ); |
| /// Standard DEVICE request to set a feature. |
| pub const DEVICE_SET_FEATURE: Self = Self::new( |
| Direction::HostToDevice, |
| RequestType::Standard, |
| Recipient::Device, |
| 0x03, |
| ); |
| /// Standard DEVICE request to set the device address. |
| pub const DEVICE_SET_ADDRESS: Self = Self::new( |
| Direction::HostToDevice, |
| RequestType::Standard, |
| Recipient::Device, |
| 0x05, |
| ); |
| /// Standard DEVICE request to get a descriptor. |
| pub const DEVICE_GET_DESCRIPTOR: Self = Self::new( |
| Direction::DeviceToHost, |
| RequestType::Standard, |
| Recipient::Device, |
| 0x06, |
| ); |
| /// Standard DEVICE request to set a descriptor. |
| pub const DEVICE_SET_DESCRIPTOR: Self = Self::new( |
| Direction::HostToDevice, |
| RequestType::Standard, |
| Recipient::Device, |
| 0x07, |
| ); |
| /// Standard DEVICE request to get the current configuration. |
| pub const DEVICE_GET_CONFIGURATION: Self = Self::new( |
| Direction::DeviceToHost, |
| RequestType::Standard, |
| Recipient::Device, |
| 0x08, |
| ); |
| /// Standard DEVICE request to set the current configuration. |
| pub const DEVICE_SET_CONFIGURATION: Self = Self::new( |
| Direction::HostToDevice, |
| RequestType::Standard, |
| Recipient::Device, |
| 0x09, |
| ); |
| /// Standard INTERFACE request to get the current status. |
| pub const INTERFACE_GET_STATUS: Self = Self::new( |
| Direction::DeviceToHost, |
| RequestType::Standard, |
| Recipient::Interface, |
| 0x00, |
| ); |
| /// Standard INTERFACE request to clear a feature. |
| pub const INTERFACE_CLEAR_FEATURE: Self = Self::new( |
| Direction::HostToDevice, |
| RequestType::Standard, |
| Recipient::Interface, |
| 0x01, |
| ); |
| /// Standard INTERFACE request to set a feature. |
| pub const INTERFACE_SET_FEATURE: Self = Self::new( |
| Direction::HostToDevice, |
| RequestType::Standard, |
| Recipient::Interface, |
| 0x03, |
| ); |
| /// Standard INTERFACE request to get the current interface setting. |
| pub const INTERFACE_GET_INTERFACE: Self = Self::new( |
| Direction::DeviceToHost, |
| RequestType::Standard, |
| Recipient::Interface, |
| 0x0a, |
| ); |
| /// Standard INTERFACE request to set the interface setting. |
| pub const INTERFACE_SET_INTERFACE: Self = Self::new( |
| Direction::HostToDevice, |
| RequestType::Standard, |
| Recipient::Interface, |
| 0x0b, |
| ); |
| /// Standard ENDPOINT request to get the current status. |
| pub const ENDPOINT_GET_STATUS: Self = Self::new( |
| Direction::DeviceToHost, |
| RequestType::Standard, |
| Recipient::Endpoint, |
| 0x00, |
| ); |
| /// Standard ENDPOINT request to clear a feature. |
| pub const ENDPOINT_CLEAR_FEATURE: Self = Self::new( |
| Direction::HostToDevice, |
| RequestType::Standard, |
| Recipient::Endpoint, |
| 0x01, |
| ); |
| /// Standard ENDPOINT request to set a feature. |
| pub const ENDPOINT_SET_FEATURE: Self = Self::new( |
| Direction::HostToDevice, |
| RequestType::Standard, |
| Recipient::Endpoint, |
| 0x03, |
| ); |
| /// Standard ENDPOINT request to synchronize frames. |
| pub const ENDPOINT_SYNCH_FRAME: Self = Self::new( |
| Direction::DeviceToHost, |
| RequestType::Standard, |
| Recipient::Endpoint, |
| 0x12, |
| ); |
| } |
| impl From<Request> for u16 { |
| fn from(val: Request) -> Self { |
| val.0 |
| } |
| } |
| #[cfg(test)] |
| mod request_tests { |
| use super::*; |
| #[test] |
| fn test_constants() { |
| assert_eq!(u16::from(Request::DEVICE_GET_STATUS), 0x0080); |
| assert_eq!(u16::from(Request::DEVICE_CLEAR_FEATURE), 0x0100); |
| assert_eq!(u16::from(Request::DEVICE_SET_FEATURE), 0x0300); |
| assert_eq!(u16::from(Request::DEVICE_SET_ADDRESS), 0x0500); |
| assert_eq!(u16::from(Request::DEVICE_GET_DESCRIPTOR), 0x0680); |
| assert_eq!(u16::from(Request::DEVICE_SET_DESCRIPTOR), 0x0700); |
| assert_eq!(u16::from(Request::DEVICE_GET_CONFIGURATION), 0x0880); |
| assert_eq!(u16::from(Request::DEVICE_SET_CONFIGURATION), 0x0900); |
| assert_eq!(u16::from(Request::INTERFACE_GET_STATUS), 0x0081); |
| assert_eq!(u16::from(Request::INTERFACE_CLEAR_FEATURE), 0x0101); |
| assert_eq!(u16::from(Request::INTERFACE_SET_FEATURE), 0x0301); |
| assert_eq!(u16::from(Request::INTERFACE_GET_INTERFACE), 0x0a81); |
| assert_eq!(u16::from(Request::INTERFACE_SET_INTERFACE), 0x0b01); |
| assert_eq!(u16::from(Request::ENDPOINT_GET_STATUS), 0x0082); |
| assert_eq!(u16::from(Request::ENDPOINT_CLEAR_FEATURE), 0x0102); |
| assert_eq!(u16::from(Request::ENDPOINT_SET_FEATURE), 0x0302); |
| assert_eq!(u16::from(Request::ENDPOINT_SYNCH_FRAME), 0x1282); |
| } |
| } |
| |
| /// Information about a USB descriptor request. |
| #[derive(Clone, Copy, Eq, PartialEq)] |
| #[cfg_attr(feature = "ufmt_console", derive(uDebug))] |
| pub struct DescriptorInfo { |
| /// The index of the descriptor. |
| pub index: u8, |
| /// The type of the descriptor. |
| pub ty: DescriptorType, |
| /// The language ID (for string descriptors). |
| pub lang: u16, |
| } |
| impl From<&SetupPacket> for DescriptorInfo { |
| fn from(pkt: &SetupPacket) -> Self { |
| DescriptorInfo { |
| index: u8::try_from(pkt.value() & 0xff).unwrap(), |
| ty: DescriptorType::from(u8::try_from((pkt.value() >> 8) & 0xff).unwrap()), |
| lang: pkt.index(), |
| } |
| } |
| } |
| |
| /// Represents a standard USB SETUP packet. |
| /// |
| /// A SETUP packet is always 8 bytes long and is used for all control transfers |
| /// on Endpoint 0. |
| #[derive(Clone, Copy)] |
| #[repr(C)] |
| pub struct SetupPacket { |
| buf: [u32; 2], |
| } |
| impl SetupPacket { |
| /// Creates a new `SetupPacket` from two 32-bit words. |
| pub fn new(buf: [u32; 2]) -> SetupPacket { |
| SetupPacket { buf } |
| } |
| /// Returns the control request information. |
| pub fn request(&self) -> Request { |
| Request(u16::try_from(self.buf[0] & 0xffff).unwrap()) |
| } |
| /// Returns the `wValue` field of the SETUP packet. |
| pub fn value(&self) -> u16 { |
| u16::try_from((self.buf[0] >> 16) & 0xffff).unwrap() |
| } |
| /// Returns the `wIndex` field of the SETUP packet. |
| #[allow(clippy::identity_op)] |
| pub fn index(&self) -> u16 { |
| u16::try_from((self.buf[1] >> 0) & 0xffff).unwrap() |
| } |
| /// Returns the `wLength` field of the SETUP packet, which indicates |
| /// the number of bytes to transfer in the data stage. |
| pub fn length(&self) -> u16 { |
| u16::try_from((self.buf[1] >> 16) & 0xffff).unwrap() |
| } |
| } |
| #[cfg(feature = "ufmt_console")] |
| impl ufmt::uDebug for SetupPacket { |
| fn fmt<W: ufmt::uWrite + ?Sized>( |
| &self, |
| f: &mut ufmt::Formatter<'_, W>, |
| ) -> Result<(), W::Error> { |
| f.debug_struct("usb::SetupPacket")? |
| .field("request", &self.request())? |
| .field("value", &self.value())? |
| .field("index", &self.index())? |
| .field("length", &self.length())? |
| .finish() |
| } |
| } |
| |
| /// USB data transfer direction. |
| #[derive(Clone, Copy, Eq, PartialEq)] |
| #[cfg_attr(feature = "ufmt_console", derive(uDebug))] |
| pub enum Direction { |
| /// Host to Device (OUT). |
| HostToDevice = 0, |
| /// Device to Host (IN). |
| DeviceToHost = 1, |
| } |
| impl From<Direction> for u32 { |
| fn from(val: Direction) -> u32 { |
| val as u32 |
| } |
| } |
| impl TryFrom<u32> for Direction { |
| type Error = (); |
| #[inline(always)] |
| fn try_from(val: u32) -> Result<Direction, ()> { |
| match val { |
| 0 => Ok(Self::HostToDevice), |
| 1 => Ok(Self::DeviceToHost), |
| _ => Err(()), |
| } |
| } |
| } |
| |
| /// The type of a USB control request. |
| #[derive(Clone, Copy, Eq, PartialEq)] |
| #[cfg_attr(feature = "ufmt_console", derive(uDebug))] |
| pub enum RequestType { |
| /// Standard USB request. |
| Standard = 0, |
| /// Class-specific request. |
| Class = 1, |
| /// Vendor-specific request. |
| Vendor = 2, |
| /// Reserved for future use. |
| Reserved = 3, |
| } |
| impl TryFrom<u32> for RequestType { |
| type Error = (); |
| #[inline(always)] |
| fn try_from(val: u32) -> Result<RequestType, ()> { |
| match val { |
| 0 => Ok(Self::Standard), |
| 1 => Ok(Self::Class), |
| 2 => Ok(Self::Vendor), |
| 3 => Ok(Self::Reserved), |
| _ => Err(()), |
| } |
| } |
| } |
| impl From<RequestType> for u32 { |
| fn from(val: RequestType) -> Self { |
| val as u32 |
| } |
| } |
| |
| /// The intended recipient of a USB control request. |
| #[derive(Clone, Copy, Eq, PartialEq)] |
| #[cfg_attr(feature = "ufmt_console", derive(uDebug))] |
| pub enum Recipient { |
| /// The device itself. |
| Device = 0, |
| /// A specific interface on the device. |
| Interface = 1, |
| /// A specific endpoint on the device. |
| Endpoint = 2, |
| /// Other recipients (e.g., class-specific). |
| Other = 3, |
| Reserved4 = 4, |
| Reserved5 = 5, |
| Reserved6 = 6, |
| Reserved7 = 7, |
| Reserved8 = 8, |
| Reserved9 = 9, |
| Reserved10 = 10, |
| Reserved11 = 11, |
| Reserved12 = 12, |
| Reserved13 = 13, |
| Reserved14 = 14, |
| Reserved15 = 15, |
| Reserved16 = 16, |
| Reserved17 = 17, |
| Reserved18 = 18, |
| Reserved19 = 19, |
| Reserved20 = 20, |
| Reserved21 = 21, |
| Reserved22 = 22, |
| Reserved23 = 23, |
| Reserved24 = 24, |
| Reserved25 = 25, |
| Reserved26 = 26, |
| Reserved27 = 27, |
| Reserved28 = 28, |
| Reserved29 = 29, |
| Reserved30 = 30, |
| Reserved31 = 31, |
| } |
| impl TryFrom<u32> for Recipient { |
| type Error = (); |
| #[inline(always)] |
| fn try_from(val: u32) -> Result<Recipient, ()> { |
| // TODO: Evaluate whether the optimizer is smart enough for this, and use |
| // transmute if it's not. |
| match val { |
| 0 => Ok(Self::Device), |
| 1 => Ok(Self::Interface), |
| 2 => Ok(Self::Endpoint), |
| 3 => Ok(Self::Other), |
| 4 => Ok(Self::Reserved4), |
| 5 => Ok(Self::Reserved5), |
| 6 => Ok(Self::Reserved6), |
| 7 => Ok(Self::Reserved7), |
| 8 => Ok(Self::Reserved8), |
| 9 => Ok(Self::Reserved9), |
| 10 => Ok(Self::Reserved10), |
| 11 => Ok(Self::Reserved11), |
| 12 => Ok(Self::Reserved12), |
| 13 => Ok(Self::Reserved13), |
| 14 => Ok(Self::Reserved14), |
| 15 => Ok(Self::Reserved15), |
| 16 => Ok(Self::Reserved16), |
| 17 => Ok(Self::Reserved17), |
| 18 => Ok(Self::Reserved18), |
| 19 => Ok(Self::Reserved19), |
| 20 => Ok(Self::Reserved20), |
| 21 => Ok(Self::Reserved21), |
| 22 => Ok(Self::Reserved22), |
| 23 => Ok(Self::Reserved23), |
| 24 => Ok(Self::Reserved24), |
| 25 => Ok(Self::Reserved25), |
| 26 => Ok(Self::Reserved26), |
| 27 => Ok(Self::Reserved27), |
| 28 => Ok(Self::Reserved28), |
| 29 => Ok(Self::Reserved29), |
| 30 => Ok(Self::Reserved30), |
| 31 => Ok(Self::Reserved31), |
| _ => Err(()), |
| } |
| } |
| } |
| impl From<Recipient> for u32 { |
| fn from(val: Recipient) -> Self { |
| val as u32 |
| } |
| } |
| |
| /// Standard USB descriptor types. |
| #[derive(Clone, Copy, Eq, PartialEq)] |
| pub struct DescriptorType(u8); |
| impl DescriptorType { |
| /// Device descriptor. |
| pub const DEVICE: Self = Self(1); |
| /// Configuration descriptor. |
| pub const CONFIGURATION: Self = Self(2); |
| /// String descriptor. |
| pub const STRING: Self = Self(3); |
| /// Interface descriptor. |
| pub const INTERFACE: Self = Self(4); |
| /// Endpoint descriptor. |
| pub const ENDPOINT: Self = Self(5); |
| /// Device qualifier descriptor. |
| pub const DEVICE_QUALIFIER: Self = Self(6); |
| } |
| impl From<u8> for DescriptorType { |
| fn from(val: u8) -> Self { |
| DescriptorType(val) |
| } |
| } |
| impl From<DescriptorType> for u8 { |
| fn from(val: DescriptorType) -> Self { |
| val.0 |
| } |
| } |
| impl From<DescriptorType> for u32 { |
| fn from(val: DescriptorType) -> Self { |
| u32::from(val.0) |
| } |
| } |
| #[cfg(feature = "ufmt_console")] |
| impl ufmt::uDebug for DescriptorType { |
| fn fmt<W: ufmt::uWrite + ?Sized>( |
| &self, |
| f: &mut ufmt::Formatter<'_, W>, |
| ) -> Result<(), W::Error> { |
| match *self { |
| Self::DEVICE => f.write_str("DEVICE"), |
| Self::CONFIGURATION => f.write_str("CONFIGURATION"), |
| Self::STRING => f.write_str("STRING"), |
| Self::INTERFACE => f.write_str("INTERFACE"), |
| Self::ENDPOINT => f.write_str("ENDPOINT"), |
| Self::DEVICE_QUALIFIER => f.write_str("DEVICE_QUALIFIER"), |
| other => ufmt::uwrite!(f, "{}", other.0), |
| } |
| } |
| } |