pub mod descriptor;
pub mod function;
#[cfg(test)]
mod tests;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::fmt;
use crate::core::sync::{AtomicU32, LockRank, Mutex, Ordering};
pub use descriptor::{
ConfigurationDescriptor, DescriptorKind, Descriptors, DeviceDescriptor, EndpointDescriptor,
InterfaceDescriptor, language_descriptor, string_descriptor,
};
pub use function::{Endpoint0, Function, Peripheral};
pub const HCD_RANK: LockRank = LockRank::new(0x4a00);
pub const FABRIC_RANK: LockRank = LockRank::new(0x4b00);
pub const EP0_RANK: LockRank = LockRank::new(0x4c00);
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct DeviceAddress(pub u8);
impl DeviceAddress {
pub const DEFAULT: DeviceAddress = DeviceAddress(0);
pub const MAX: u8 = 127;
#[must_use]
pub const fn is_valid(self) -> bool {
self.0 <= DeviceAddress::MAX
}
}
impl fmt::Display for DeviceAddress {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "usb{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum Speed {
Low,
Full,
#[default]
High,
}
impl Speed {
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Speed::Low => "low",
Speed::Full => "full",
Speed::High => "high",
}
}
#[must_use]
pub fn from_name(name: &str) -> Option<Speed> {
match name {
"low" => Some(Speed::Low),
"full" => Some(Speed::Full),
"high" => Some(Speed::High),
_ => None,
}
}
pub const NAMES: &'static [&'static str] = &["low", "full", "high"];
#[must_use]
pub const fn max_control_packet(self) -> u16 {
match self {
Speed::Low => 8,
Speed::Full | Speed::High => 64,
}
}
}
impl fmt::Display for Speed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum Direction {
#[default]
Out,
In,
}
impl Direction {
pub const BIT: u8 = 0x80;
#[must_use]
pub const fn from_bit(value: u8) -> Direction {
if value & Direction::BIT != 0 {
Direction::In
} else {
Direction::Out
}
}
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Direction::Out => "out",
Direction::In => "in",
}
}
}
impl fmt::Display for Direction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum TransferType {
#[default]
Control,
Isochronous,
Bulk,
Interrupt,
}
impl TransferType {
#[must_use]
pub const fn attribute_bits(self) -> u8 {
match self {
TransferType::Control => 0,
TransferType::Isochronous => 1,
TransferType::Bulk => 2,
TransferType::Interrupt => 3,
}
}
#[must_use]
pub const fn from_attribute_bits(bits: u8) -> TransferType {
match bits & 0x3 {
0 => TransferType::Control,
1 => TransferType::Isochronous,
2 => TransferType::Bulk,
_ => TransferType::Interrupt,
}
}
#[must_use]
pub const fn name(self) -> &'static str {
match self {
TransferType::Control => "control",
TransferType::Isochronous => "isochronous",
TransferType::Bulk => "bulk",
TransferType::Interrupt => "interrupt",
}
}
}
impl fmt::Display for TransferType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum Status {
#[default]
Ack,
Nak,
Stall,
NoDevice,
Babble,
Error,
}
impl Status {
#[must_use]
pub const fn is_final(self) -> bool {
!matches!(self, Status::Nak)
}
#[must_use]
pub const fn is_error(self) -> bool {
matches!(
self,
Status::Stall | Status::NoDevice | Status::Babble | Status::Error
)
}
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Status::Ack => "ack",
Status::Nak => "nak",
Status::Stall => "stall",
Status::NoDevice => "no device",
Status::Babble => "babble",
Status::Error => "error",
}
}
}
impl fmt::Display for Status {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Completion {
pub status: Status,
pub len: u64,
}
impl Completion {
#[must_use]
pub const fn ack(len: u64) -> Completion {
Completion {
status: Status::Ack,
len,
}
}
#[must_use]
pub const fn nak() -> Completion {
Completion {
status: Status::Nak,
len: 0,
}
}
#[must_use]
pub const fn stall() -> Completion {
Completion {
status: Status::Stall,
len: 0,
}
}
#[must_use]
pub const fn absent() -> Completion {
Completion {
status: Status::NoDevice,
len: 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum Recipient {
#[default]
Device,
Interface,
Endpoint,
Other,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum RequestKind {
#[default]
Standard,
Class,
Vendor,
Reserved,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct SetupPacket {
pub request_type: u8,
pub request: u8,
pub value: u16,
pub index: u16,
pub length: u16,
}
impl SetupPacket {
pub const SIZE: u64 = 8;
#[must_use]
pub const fn decode(bytes: &[u8; 8]) -> SetupPacket {
SetupPacket {
request_type: bytes[0],
request: bytes[1],
value: u16::from_le_bytes([bytes[2], bytes[3]]),
index: u16::from_le_bytes([bytes[4], bytes[5]]),
length: u16::from_le_bytes([bytes[6], bytes[7]]),
}
}
#[must_use]
pub const fn encode(&self) -> [u8; 8] {
let value = self.value.to_le_bytes();
let index = self.index.to_le_bytes();
let length = self.length.to_le_bytes();
[
self.request_type,
self.request,
value[0],
value[1],
index[0],
index[1],
length[0],
length[1],
]
}
#[must_use]
pub const fn direction(&self) -> Direction {
Direction::from_bit(self.request_type)
}
#[must_use]
pub const fn kind(&self) -> RequestKind {
match (self.request_type >> 5) & 0x3 {
0 => RequestKind::Standard,
1 => RequestKind::Class,
2 => RequestKind::Vendor,
_ => RequestKind::Reserved,
}
}
#[must_use]
pub const fn recipient(&self) -> Recipient {
match self.request_type & 0x1f {
0 => Recipient::Device,
1 => Recipient::Interface,
2 => Recipient::Endpoint,
_ => Recipient::Other,
}
}
#[must_use]
pub const fn descriptor(&self) -> (u8, u8) {
((self.value >> 8) as u8, self.value as u8)
}
}
pub mod request {
pub const GET_STATUS: u8 = 0;
pub const CLEAR_FEATURE: u8 = 1;
pub const SET_FEATURE: u8 = 3;
pub const SET_ADDRESS: u8 = 5;
pub const GET_DESCRIPTOR: u8 = 6;
pub const SET_DESCRIPTOR: u8 = 7;
pub const GET_CONFIGURATION: u8 = 8;
pub const SET_CONFIGURATION: u8 = 9;
pub const GET_INTERFACE: u8 = 10;
pub const SET_INTERFACE: u8 = 11;
pub const SYNCH_FRAME: u8 = 12;
}
pub mod feature {
pub const ENDPOINT_HALT: u16 = 0;
pub const DEVICE_REMOTE_WAKEUP: u16 = 1;
pub const TEST_MODE: u16 = 2;
}
pub trait UsbDevice: Send + Sync + fmt::Debug {
fn speed(&self) -> Speed;
fn address(&self) -> DeviceAddress;
fn bus_reset(&self);
fn setup(&self, endpoint: u8, packet: SetupPacket) -> Status;
fn transfer_in(&self, endpoint: u8, dst: &mut [u8]) -> Completion;
fn transfer_out(&self, endpoint: u8, src: &[u8]) -> Completion;
fn peek_in(&self, endpoint: u8, dst: &mut [u8]) -> Completion {
let _ = (endpoint, dst);
Completion::nak()
}
}
pub const MAX_PORTS: usize = 15;
#[derive(Debug, Clone, Default)]
struct Slot {
device: Option<Arc<dyn UsbDevice>>,
enabled: bool,
changed: bool,
}
pub struct UsbBus {
ports: Mutex<Vec<Slot>>,
changes: AtomicU32,
}
impl fmt::Debug for UsbBus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("UsbBus");
s.field("changes", &self.changes.load(Ordering::Relaxed));
match self.ports.try_lock() {
Some(ports) => {
s.field("ports", &ports.len());
s.field(
"attached",
&ports.iter().filter(|p| p.device.is_some()).count(),
)
}
None => s.field("ports", &"<in use>"),
};
s.finish()
}
}
impl UsbBus {
#[must_use]
pub fn new(ports: u8) -> UsbBus {
let count = usize::from(ports).clamp(1, MAX_PORTS);
UsbBus {
ports: Mutex::with_rank(FABRIC_RANK, alloc::vec![Slot::default(); count]),
changes: AtomicU32::new(0),
}
}
#[must_use]
pub fn port_count(&self) -> u8 {
self.ports.lock().len() as u8
}
pub fn attach(&self, port: u8, device: Arc<dyn UsbDevice>) -> crate::Result<()> {
let index = usize::from(port);
let mut ports = self.ports.lock();
if index >= ports.len() {
return Err(crate::Error::Config {
at: alloc::format!("port {port}"),
message: alloc::format!("this USB bus has {} ports", ports.len()),
});
}
if ports[index].device.is_some() {
return Err(crate::Error::Config {
at: alloc::format!("port {port}"),
message: alloc::string::String::from(
"two devices on one USB port; give one of them another `port`, \
or add a hub — which this tree does not model yet",
),
});
}
ports[index].device = Some(device);
ports[index].enabled = false;
ports[index].changed = true;
drop(ports);
self.changes.fetch_or(1u32 << port, Ordering::Relaxed);
Ok(())
}
pub fn detach(&self, port: u8) -> bool {
let index = usize::from(port);
let mut ports = self.ports.lock();
if index >= ports.len() {
return false;
}
let had = ports[index].device.take().is_some();
ports[index].enabled = false;
if had {
ports[index].changed = true;
}
drop(ports);
if had {
self.changes.fetch_or(1u32 << port, Ordering::Relaxed);
}
had
}
#[must_use]
pub fn device(&self, port: u8) -> Option<Arc<dyn UsbDevice>> {
let index = usize::from(port);
self.ports.lock().get(index).and_then(|p| p.device.clone())
}
#[must_use]
pub fn connected(&self, port: u8) -> bool {
self.device(port).is_some()
}
#[must_use]
pub fn speed(&self, port: u8) -> Option<Speed> {
self.device(port).map(|d| d.speed())
}
#[must_use]
pub fn enabled(&self, port: u8) -> bool {
let index = usize::from(port);
self.ports.lock().get(index).is_some_and(|p| p.enabled)
}
pub fn set_enabled(&self, port: u8, enabled: bool) {
let index = usize::from(port);
if let Some(slot) = self.ports.lock().get_mut(index) {
slot.enabled = enabled;
}
}
pub fn take_change(&self, port: u8) -> bool {
let index = usize::from(port);
let mut ports = self.ports.lock();
let Some(slot) = ports.get_mut(index) else {
return false;
};
let changed = core::mem::replace(&mut slot.changed, false);
drop(ports);
if changed {
self.changes.fetch_and(!(1u32 << port), Ordering::Relaxed);
}
changed
}
#[must_use]
pub fn any_change(&self) -> bool {
self.changes.load(Ordering::Relaxed) != 0
}
pub fn reset_port(&self, port: u8) {
self.set_enabled(port, false);
if let Some(device) = self.device(port) {
device.bus_reset();
}
}
#[must_use]
pub fn find(&self, address: DeviceAddress) -> Option<Arc<dyn UsbDevice>> {
let candidates: Vec<Arc<dyn UsbDevice>> = {
let ports = self.ports.lock();
ports
.iter()
.filter(|p| p.enabled)
.filter_map(|p| p.device.clone())
.collect()
};
candidates.into_iter().find(|d| d.address() == address)
}
pub fn setup(&self, address: DeviceAddress, endpoint: u8, packet: SetupPacket) -> Status {
match self.find(address) {
Some(device) => device.setup(endpoint, packet),
None => Status::NoDevice,
}
}
pub fn read(&self, address: DeviceAddress, endpoint: u8, dst: &mut [u8]) -> Completion {
match self.find(address) {
Some(device) => device.transfer_in(endpoint, dst),
None => Completion::absent(),
}
}
pub fn write(&self, address: DeviceAddress, endpoint: u8, src: &[u8]) -> Completion {
match self.find(address) {
Some(device) => device.transfer_out(endpoint, src),
None => Completion::absent(),
}
}
#[must_use]
pub fn peek(&self, address: DeviceAddress, endpoint: u8, dst: &mut [u8]) -> Completion {
match self.find(address) {
Some(device) => device.peek_in(endpoint, dst),
None => Completion::absent(),
}
}
}
pub mod buses {
use super::UsbBus;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use crate::core::error::Result;
use crate::core::hosts::{HostKind, HostObjects};
use crate::core::props::Props;
pub const KIND: HostKind = HostKind::new("usb-bus");
pub fn open(hosts: &HostObjects, name: &str, ports: u8) -> Result<Arc<UsbBus>> {
hosts.open(KIND, name, || UsbBus::new(ports))
}
pub fn attach(props: &Props, name: &str, ports: u8) -> Result<Arc<UsbBus>> {
props.host(KIND, name, || UsbBus::new(ports))
}
pub fn get(hosts: &HostObjects, name: &str) -> Result<Option<Arc<UsbBus>>> {
hosts.get(KIND, name)
}
pub fn close(hosts: &HostObjects, name: &str) -> bool {
hosts.close(KIND, name)
}
#[must_use]
pub fn names(hosts: &HostObjects) -> Vec<String> {
hosts.names(KIND)
}
}