blob: ffd7bba989532ba3da08b5059c6e1ad59bcdd9eb [file]
// 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),
}
}
}