#![no_std] #![feature(async_fn_in_trait)] #![allow(incomplete_features)] /// 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 for EndpointAddress { #[inline] fn from(addr: u8) -> EndpointAddress { EndpointAddress(addr) } } impl From 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, } /// 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; 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 /// [`start`](Self::start) is called. /// /// # 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; fn alloc_endpoint_in( &mut self, ep_type: EndpointType, max_packet_size: u16, interval: u8, ) -> Result; /// 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); /// 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. async fn enable(&mut self); /// Disables and powers down the USB peripheral. async fn disable(&mut self); async fn poll(&mut self) -> Event; /// Sets the device USB address to `addr`. fn set_address(&mut self, addr: u8); /// Enables or disables an endpoint. fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool); /// 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); /// 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; /// Simulates a disconnect from the USB bus, causing the host to reset and re-enumerate the /// device. /// /// The default implementation just returns `Unsupported`. /// /// # Errors /// /// * [`Unsupported`](crate::Unsupported) - This UsbBus implementation doesn't support /// 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 /// /// * [`Unsupported`](crate::Unsupported) - This UsbBus implementation doesn't support /// remote wakeup or it has not been enabled at creation time. async fn remote_wakeup(&mut self) -> Result<(), Unsupported>; } pub trait Endpoint { /// Get the endpoint address fn info(&self) -> &EndpointInfo; /// Waits for the endpoint to be enabled. async fn wait_enabled(&mut self); } 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. async fn read(&mut self, buf: &mut [u8]) -> Result; } /// 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`. /// /// 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`, /// 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. 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. async fn data_out(&mut self, buf: &mut [u8], first: bool, last: bool) -> Result; /// 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`. 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. async fn accept(&mut self); /// Rejects a control request. /// /// Sets a STALL condition on the pipe to indicate an error. async fn reject(&mut self); } pub trait EndpointIn: Endpoint { /// Writes a single packet of data to the endpoint. async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError>; } #[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, } #[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`. BufferOverflow, /// The endpoint is disabled. Disabled, }