#![no_std] #![feature(async_fn_in_trait)] #![allow(stable_features, unknown_lints, async_fn_in_trait)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] /// 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 } } /// Information for an endpoint. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct EndpointInfo { /// Endpoint's address. pub addr: EndpointAddress, /// Endpoint's type. pub ep_type: EndpointType, /// Max packet size, in bytes. pub max_packet_size: u16, /// Polling interval, in milliseconds. pub interval_ms: u8, } /// Main USB driver trait. /// /// Implement this to add support for a new hardware platform. pub trait Driver<'a> { /// Type of the OUT endpoints for this driver. type EndpointOut: EndpointOut + 'a; /// Type of the IN endpoints for this driver. type EndpointIn: EndpointIn + 'a; /// Type of the control pipe for this driver. type ControlPipe: ControlPipe + 'a; /// Type for bus control for this driver. type Bus: Bus + 'a; /// Allocates an OUT endpoint. /// /// This method is called by the USB stack to allocate endpoints. /// It can only be called before [`start`](Self::start) is called. /// /// # Arguments /// /// * `ep_type` - the endpoint's type. /// * `max_packet_size` - Maximum packet size in bytes. /// * `interval_ms` - Polling interval parameter for interrupt endpoints. fn alloc_endpoint_out( &mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8, ) -> Result; /// Allocates an IN endpoint. /// /// This method is called by the USB stack to allocate endpoints. /// It can only be called before [`start`](Self::start) is called. /// /// # Arguments /// /// * `ep_type` - the endpoint's type. /// * `max_packet_size` - Maximum packet size in bytes. /// * `interval_ms` - Polling interval parameter for interrupt endpoints. fn alloc_endpoint_in( &mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: 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); } /// USB bus trait. /// /// This trait provides methods that act on the whole bus. It is kept owned by /// the main USB task, and used to manage the bus. pub trait Bus { /// Enable the USB peripheral. async fn enable(&mut self); /// Disable and powers down the USB peripheral. async fn disable(&mut self); /// Wait for a bus-related event. /// /// This method should asynchronously wait for an event to happen, then /// return it. See [`Event`] for the list of events this method should return. async fn poll(&mut self) -> Event; /// Enable or disable an endpoint. fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool); /// Set or clear the STALL condition for an endpoint. /// /// If the endpoint is an OUT endpoint, it should be prepared to receive data again. fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool); /// Get whether the STALL condition is set for an endpoint. fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool; /// Simulate 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) } /// Initiate 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>; } /// Endpoint trait, common for OUT and IN. pub trait Endpoint { /// Get the endpoint address fn info(&self) -> &EndpointInfo; /// Wait for the endpoint to be enabled. async fn wait_enabled(&mut self); } /// OUT Endpoint trait. pub trait EndpointOut: Endpoint { /// Read a single packet of data from the endpoint, and return 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; } /// USB control pipe trait. /// /// The USB control pipe owns both OUT endpoint 0 and IN endpoint 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 for setting the device address: /// /// ```not_rust /// setup() /// (...processing...) /// accept_set_address(addr) 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; /// Read a single setup packet from the endpoint. async fn setup(&mut self) -> [u8; 8]; /// Read 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; /// Send 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>; /// Accept a control request. /// /// Causes the STATUS packet for the current request to be ACKed. async fn accept(&mut self); /// Reject a control request. /// /// Sets a STALL condition on the pipe to indicate an error. async fn reject(&mut self); /// Accept SET_ADDRESS control and change bus address. /// /// For most drivers this function should firstly call `accept()` and then change the bus address. /// However, there are peripherals (Synopsys USB OTG) that have reverse order. async fn accept_set_address(&mut self, addr: u8); } /// IN Endpoint trait. pub trait EndpointIn: Endpoint { /// Write 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, } /// Allocating an endpoint failed. /// /// This can be due to running out of endpoints, or out of endpoint memory, /// or because the hardware doesn't support the requested combination of features. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct EndpointAllocError; /// Operation is unsupported by the driver. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Unsupported; /// Errors returned by [`EndpointIn::write`] and [`EndpointOut::read`] #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] 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, }