embassy/embassy-usb-driver/src/lib.rs

358 lines
12 KiB
Rust
Raw Normal View History

#![no_std]
2022-11-21 23:31:31 +01:00
#![feature(async_fn_in_trait)]
#![allow(incomplete_features)]
2022-03-09 01:34:35 +01:00
/// Direction of USB traffic. Note that in the USB standard the direction is always indicated from
/// the perspective of the host, which is backward for devices, but the standard directions are used
/// for consistency.
///
/// The values of the enum also match the direction bit used in endpoint addresses and control
/// request types.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Direction {
/// Host to device (OUT)
Out,
/// Device to host (IN)
In,
}
/// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the
/// transfer bmAttributes transfer type bits.
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum EndpointType {
/// Control endpoint. Used for device management. Only the host can initiate requests. Usually
/// used only endpoint 0.
Control = 0b00,
/// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet.
Isochronous = 0b01,
/// Bulk endpoint. Used for large amounts of best-effort reliable data.
Bulk = 0b10,
/// Interrupt endpoint. Used for small amounts of time-critical reliable data.
Interrupt = 0b11,
}
/// Type-safe endpoint address.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EndpointAddress(u8);
impl From<u8> for EndpointAddress {
#[inline]
fn from(addr: u8) -> EndpointAddress {
EndpointAddress(addr)
}
}
impl From<EndpointAddress> for u8 {
#[inline]
fn from(addr: EndpointAddress) -> u8 {
addr.0
}
}
impl EndpointAddress {
const INBITS: u8 = 0x80;
/// Constructs a new EndpointAddress with the given index and direction.
#[inline]
pub fn from_parts(index: usize, dir: Direction) -> Self {
let dir_u8 = match dir {
Direction::Out => 0x00,
Direction::In => Self::INBITS,
};
EndpointAddress(index as u8 | dir_u8)
}
/// Gets the direction part of the address.
#[inline]
pub fn direction(&self) -> Direction {
if (self.0 & Self::INBITS) != 0 {
Direction::In
} else {
Direction::Out
}
}
/// Returns true if the direction is IN, otherwise false.
#[inline]
pub fn is_in(&self) -> bool {
(self.0 & Self::INBITS) != 0
}
/// Returns true if the direction is OUT, otherwise false.
#[inline]
pub fn is_out(&self) -> bool {
(self.0 & Self::INBITS) == 0
}
/// Gets the index part of the endpoint address.
#[inline]
pub fn index(&self) -> usize {
(self.0 & !Self::INBITS) as usize
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EndpointInfo {
pub addr: EndpointAddress,
pub ep_type: EndpointType,
pub max_packet_size: u16,
pub interval: u8,
}
2022-03-09 01:34:35 +01:00
/// Driver for a specific USB peripheral. Implement this to add support for a new hardware
/// platform.
pub trait Driver<'a> {
type EndpointOut: EndpointOut + 'a;
type EndpointIn: EndpointIn + 'a;
type ControlPipe: ControlPipe + 'a;
2022-03-09 01:34:35 +01:00
type Bus: Bus + 'a;
/// Allocates an endpoint and specified endpoint parameters. This method is called by the device
/// and class implementations to allocate endpoints, and can only be called before
2022-08-22 10:36:33 +02:00
/// [`start`](Self::start) is called.
2022-03-09 01:34:35 +01:00
///
/// # Arguments
///
/// * `ep_addr` - A static endpoint address to allocate. If Some, the implementation should
/// attempt to return an endpoint with the specified address. If None, the implementation
/// should return the next available one.
/// * `max_packet_size` - Maximum packet size in bytes.
/// * `interval` - Polling interval parameter for interrupt endpoints.
fn alloc_endpoint_out(
&mut self,
ep_type: EndpointType,
max_packet_size: u16,
interval: u8,
) -> Result<Self::EndpointOut, EndpointAllocError>;
fn alloc_endpoint_in(
&mut self,
ep_type: EndpointType,
max_packet_size: u16,
interval: u8,
) -> Result<Self::EndpointIn, EndpointAllocError>;
/// Start operation of the USB device.
///
/// This returns the `Bus` and `ControlPipe` instances that are used to operate
/// the USB device. Additionally, this makes all the previously allocated endpoints
/// start operating.
///
/// This consumes the `Driver` instance, so it's no longer possible to allocate more
/// endpoints.
fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe);
2022-03-09 01:34:35 +01:00
/// Indicates that `set_device_address` must be called before accepting the corresponding
/// control transfer, not after.
///
/// The default value for this constant is `false`, which corresponds to the USB 2.0 spec, 9.4.6
const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = false;
}
pub trait Bus {
/// Enables the USB peripheral. Soon after enabling the device will be reset, so
/// there is no need to perform a USB reset in this method.
2022-11-21 23:31:31 +01:00
async fn enable(&mut self);
/// Disables and powers down the USB peripheral.
2022-11-21 23:31:31 +01:00
async fn disable(&mut self);
2022-03-09 23:06:27 +01:00
2022-11-21 23:31:31 +01:00
async fn poll(&mut self) -> Event;
2022-03-09 01:34:35 +01:00
/// Sets the device USB address to `addr`.
fn set_address(&mut self, addr: u8);
2022-03-09 01:34:35 +01:00
/// Enables or disables an endpoint.
fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool);
2022-03-09 01:34:35 +01:00
/// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it
/// should be prepared to receive data again. Only used during control transfers.
fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool);
2022-03-09 01:34:35 +01:00
/// Gets whether the STALL condition is set for an endpoint. Only used during control transfers.
fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool;
2022-03-09 01:34:35 +01:00
/// Simulates a disconnect from the USB bus, causing the host to reset and re-enumerate the
/// device.
///
/// The default implementation just returns `Unsupported`.
///
/// # Errors
///
2022-11-27 22:32:18 +01:00
/// * [`Unsupported`](crate::Unsupported) - This UsbBus implementation doesn't support
2022-03-09 01:34:35 +01:00
/// simulating a disconnect or it has not been enabled at creation time.
fn force_reset(&mut self) -> Result<(), Unsupported> {
Err(Unsupported)
}
/// Initiates a remote wakeup of the host by the device.
///
/// # Errors
///
2022-11-27 22:32:18 +01:00
/// * [`Unsupported`](crate::Unsupported) - This UsbBus implementation doesn't support
/// remote wakeup or it has not been enabled at creation time.
2022-11-21 23:31:31 +01:00
async fn remote_wakeup(&mut self) -> Result<(), Unsupported>;
2022-03-09 01:34:35 +01:00
}
pub trait Endpoint {
/// Get the endpoint address
fn info(&self) -> &EndpointInfo;
/// Waits for the endpoint to be enabled.
2022-11-21 23:31:31 +01:00
async fn wait_enabled(&mut self);
2022-03-09 01:34:35 +01:00
}
pub trait EndpointOut: Endpoint {
/// Reads a single packet of data from the endpoint, and returns the actual length of
/// the packet.
///
/// This should also clear any NAK flags and prepare the endpoint to receive the next packet.
2022-11-21 23:31:31 +01:00
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError>;
2022-03-09 01:34:35 +01:00
}
2023-01-05 01:46:35 +01:00
/// Trait for USB control pipe.
///
/// The USB control pipe owns both OUT ep 0 and IN ep 0 in a single
/// unit, and manages them together to implement the control pipe state machine.
///
/// The reason this is a separate trait instead of using EndpointOut/EndpointIn is that
/// many USB peripherals treat the control pipe endpoints differently (different registers,
/// different procedures), usually to accelerate processing in hardware somehow. A separate
/// trait allows the driver to handle it specially.
///
/// The call sequences made by the USB stack to the ControlPipe are the following:
///
/// - control in/out with len=0:
///
/// ```not_rust
/// setup()
/// (...processing...)
/// accept() or reject()
/// ```
///
/// - control out with len != 0:
///
/// ```not_rust
/// setup()
/// data_out(first=true, last=false)
/// data_out(first=false, last=false)
/// ...
/// data_out(first=false, last=false)
/// data_out(first=false, last=true)
/// (...processing...)
/// accept() or reject()
/// ```
///
/// - control in with len != 0, accepted:
///
/// ```not_rust
/// setup()
/// (...processing...)
/// data_in(first=true, last=false)
/// data_in(first=false, last=false)
/// ...
/// data_in(first=false, last=false)
/// data_in(first=false, last=true)
/// (NO `accept()`!!! This is because calling `data_in` already implies acceptance.)
/// ```
///
/// - control in with len != 0, rejected:
///
/// ```not_rust
/// setup()
/// (...processing...)
/// reject()
/// ```
///
/// The driver is responsible for handling the status stage. The stack DOES NOT do zero-length
/// calls to `data_in` or `data_out` for the status zero-length packet. The status stage should
/// be triggered by either `accept()`, or `data_in` with `last = true`.
///
2023-01-05 14:46:31 +01:00
/// Note that the host can abandon a control request and send a new SETUP packet any time. If
/// a SETUP packet arrives at any time during `data_out`, `data_in`, `accept` or `reject`,
2023-01-05 01:46:35 +01:00
/// the driver must immediately return (with `EndpointError::Disabled` from `data_in`, `data_out`)
/// to let the stack call `setup()` again to start handling the new control request. Not doing
/// so will cause things to get stuck, because the host will never read/send the packet we're
/// waiting for.
pub trait ControlPipe {
/// Maximum packet size for the control pipe
fn max_packet_size(&self) -> usize;
/// Reads a single setup packet from the endpoint.
2022-11-27 22:26:58 +01:00
async fn setup(&mut self) -> [u8; 8];
/// Reads a DATA OUT packet into `buf` in response to a control write request.
///
/// Must be called after `setup()` for requests with `direction` of `Out`
/// and `length` greater than zero.
2022-11-21 23:31:31 +01:00
async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result<usize, EndpointError>;
/// Sends a DATA IN packet with `data` in response to a control read request.
///
/// If `last_packet` is true, the STATUS packet will be ACKed following the transfer of `data`.
2022-11-21 23:31:31 +01:00
async fn data_in(&mut self, data: &[u8], first: bool, last: bool) -> Result<(), EndpointError>;
/// Accepts a control request.
///
/// Causes the STATUS packet for the current request to be ACKed.
2022-11-21 23:31:31 +01:00
async fn accept(&mut self);
/// Rejects a control request.
///
/// Sets a STALL condition on the pipe to indicate an error.
2022-11-21 23:31:31 +01:00
async fn reject(&mut self);
}
2022-03-09 01:34:35 +01:00
pub trait EndpointIn: Endpoint {
/// Writes a single packet of data to the endpoint.
2022-11-21 23:31:31 +01:00
async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>;
2022-03-09 01:34:35 +01:00
}
2022-03-09 23:06:27 +01:00
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
/// Event returned by [`Bus::poll`].
pub enum Event {
/// The USB reset condition has been detected.
Reset,
/// A USB suspend request has been detected or, in the case of self-powered devices, the device
/// has been disconnected from the USB bus.
Suspend,
/// A USB resume request has been detected after being suspended or, in the case of self-powered
/// devices, the device has been connected to the USB bus.
Resume,
/// The USB power has been detected.
PowerDetected,
/// The USB power has been removed. Not supported by all devices.
PowerRemoved,
2022-03-09 23:06:27 +01:00
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct EndpointAllocError;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
/// Operation is unsupported by the driver.
pub struct Unsupported;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
/// Errors returned by [`EndpointIn::write`] and [`EndpointOut::read`]
pub enum EndpointError {
/// Either the packet to be written is too long to fit in the transmission
/// buffer or the received packet is too long to fit in `buf`.
2022-03-09 23:06:27 +01:00
BufferOverflow,
/// The endpoint is disabled.
Disabled,
2022-03-09 23:06:27 +01:00
}