usb: split driver trait to separate crate.
This commit is contained in:
@ -9,8 +9,12 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb/s
|
||||
features = ["defmt"]
|
||||
target = "thumbv7em-none-eabi"
|
||||
|
||||
[features]
|
||||
defmt = ["dep:defmt", "embassy-usb-driver/defmt"]
|
||||
|
||||
[dependencies]
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" }
|
||||
|
||||
defmt = { version = "0.3", optional = true }
|
||||
log = { version = "0.4.14", optional = true }
|
||||
|
@ -1,11 +1,10 @@
|
||||
use heapless::Vec;
|
||||
|
||||
use super::control::ControlHandler;
|
||||
use super::descriptor::{BosWriter, DescriptorWriter};
|
||||
use super::driver::{Driver, Endpoint};
|
||||
use super::types::*;
|
||||
use super::{DeviceStateHandler, UsbDevice, MAX_INTERFACE_COUNT};
|
||||
use crate::{Interface, STRING_INDEX_CUSTOM_START};
|
||||
use crate::control::ControlHandler;
|
||||
use crate::descriptor::{BosWriter, DescriptorWriter};
|
||||
use crate::driver::{Driver, Endpoint, EndpointType};
|
||||
use crate::types::*;
|
||||
use crate::{DeviceStateHandler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START};
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
|
@ -1,7 +1,8 @@
|
||||
//! USB control data types.
|
||||
use core::mem;
|
||||
|
||||
use super::types::*;
|
||||
use crate::driver::Direction;
|
||||
use crate::types::StringIndex;
|
||||
|
||||
/// Control request type.
|
||||
#[repr(u8)]
|
||||
@ -42,7 +43,7 @@ pub enum Recipient {
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Request {
|
||||
/// Direction of the request.
|
||||
pub direction: UsbDirection,
|
||||
pub direction: Direction,
|
||||
/// Type of the request.
|
||||
pub request_type: RequestType,
|
||||
/// Recipient of the request.
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::builder::Config;
|
||||
use super::types::*;
|
||||
use super::CONFIGURATION_VALUE;
|
||||
use crate::builder::Config;
|
||||
use crate::driver::EndpointInfo;
|
||||
use crate::types::*;
|
||||
use crate::CONFIGURATION_VALUE;
|
||||
|
||||
/// Standard descriptor types
|
||||
#[allow(missing_docs)]
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::descriptor::descriptor_type;
|
||||
use crate::types::EndpointAddress;
|
||||
use crate::driver::EndpointAddress;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
|
@ -1,232 +0,0 @@
|
||||
use core::future::Future;
|
||||
|
||||
use super::types::*;
|
||||
|
||||
/// 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<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);
|
||||
|
||||
/// 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 {
|
||||
type EnableFuture<'a>: Future<Output = ()> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
type DisableFuture<'a>: Future<Output = ()> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
type PollFuture<'a>: Future<Output = Event> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
type RemoteWakeupFuture<'a>: Future<Output = Result<(), Unsupported>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
/// 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.
|
||||
fn enable(&mut self) -> Self::EnableFuture<'_>;
|
||||
|
||||
/// Disables and powers down the USB peripheral.
|
||||
fn disable(&mut self) -> Self::DisableFuture<'_>;
|
||||
|
||||
fn poll<'a>(&'a mut self) -> Self::PollFuture<'a>;
|
||||
|
||||
/// 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::driver::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::driver::Unsupported) - This UsbBus implementation doesn't support
|
||||
/// remote wakeup or it has not been enabled at creation time.
|
||||
fn remote_wakeup(&mut self) -> Self::RemoteWakeupFuture<'_>;
|
||||
}
|
||||
|
||||
pub trait Endpoint {
|
||||
type WaitEnabledFuture<'a>: Future<Output = ()> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
/// Get the endpoint address
|
||||
fn info(&self) -> &EndpointInfo;
|
||||
|
||||
/// Waits for the endpoint to be enabled.
|
||||
fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_>;
|
||||
}
|
||||
|
||||
pub trait EndpointOut: Endpoint {
|
||||
type ReadFuture<'a>: Future<Output = Result<usize, EndpointError>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
/// 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.
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a>;
|
||||
}
|
||||
|
||||
pub trait ControlPipe {
|
||||
type SetupFuture<'a>: Future<Output = [u8; 8]> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
type DataOutFuture<'a>: Future<Output = Result<usize, EndpointError>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
type DataInFuture<'a>: Future<Output = Result<(), EndpointError>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
type AcceptFuture<'a>: Future<Output = ()> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
type RejectFuture<'a>: Future<Output = ()> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
/// Maximum packet size for the control pipe
|
||||
fn max_packet_size(&self) -> usize;
|
||||
|
||||
/// Reads a single setup packet from the endpoint.
|
||||
fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a>;
|
||||
|
||||
/// 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.
|
||||
fn data_out<'a>(&'a mut self, buf: &'a mut [u8], first: bool, last: bool) -> Self::DataOutFuture<'a>;
|
||||
|
||||
/// 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`.
|
||||
fn data_in<'a>(&'a mut self, data: &'a [u8], first: bool, last: bool) -> Self::DataInFuture<'a>;
|
||||
|
||||
/// Accepts a control request.
|
||||
///
|
||||
/// Causes the STATUS packet for the current request to be ACKed.
|
||||
fn accept<'a>(&'a mut self) -> Self::AcceptFuture<'a>;
|
||||
|
||||
/// Rejects a control request.
|
||||
///
|
||||
/// Sets a STALL condition on the pipe to indicate an error.
|
||||
fn reject<'a>(&'a mut self) -> Self::RejectFuture<'a>;
|
||||
}
|
||||
|
||||
pub trait EndpointIn: Endpoint {
|
||||
type WriteFuture<'a>: Future<Output = Result<(), EndpointError>> + 'a
|
||||
where
|
||||
Self: 'a;
|
||||
|
||||
/// Writes a single packet of data to the endpoint.
|
||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a>;
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
@ -4,23 +4,23 @@
|
||||
// This mod MUST go first, so that the others see its macros.
|
||||
pub(crate) mod fmt;
|
||||
|
||||
pub use embassy_usb_driver as driver;
|
||||
|
||||
mod builder;
|
||||
pub mod control;
|
||||
pub mod descriptor;
|
||||
mod descriptor_reader;
|
||||
pub mod driver;
|
||||
pub mod types;
|
||||
|
||||
use embassy_futures::select::{select, Either};
|
||||
use heapless::Vec;
|
||||
|
||||
pub use self::builder::{Builder, Config};
|
||||
use self::control::*;
|
||||
use self::descriptor::*;
|
||||
use self::driver::{Bus, Driver, Event};
|
||||
use self::types::*;
|
||||
pub use crate::builder::{Builder, Config};
|
||||
use crate::control::*;
|
||||
use crate::descriptor::*;
|
||||
use crate::descriptor_reader::foreach_endpoint;
|
||||
use crate::driver::ControlPipe;
|
||||
use crate::driver::{Bus, ControlPipe, Direction, Driver, EndpointAddress, Event};
|
||||
use crate::types::*;
|
||||
|
||||
/// The global state of the USB device.
|
||||
///
|
||||
@ -250,8 +250,8 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
|
||||
trace!("control request: {:?}", req);
|
||||
|
||||
match req.direction {
|
||||
UsbDirection::In => self.handle_control_in(req).await,
|
||||
UsbDirection::Out => self.handle_control_out(req).await,
|
||||
Direction::In => self.handle_control_in(req).await,
|
||||
Direction::Out => self.handle_control_out(req).await,
|
||||
}
|
||||
|
||||
if self.inner.set_address_pending {
|
||||
|
@ -1,108 +1,3 @@
|
||||
/// 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.
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum UsbDirection {
|
||||
/// Host to device (OUT)
|
||||
Out = 0x00,
|
||||
/// Device to host (IN)
|
||||
In = 0x80,
|
||||
}
|
||||
|
||||
impl From<u8> for UsbDirection {
|
||||
fn from(value: u8) -> Self {
|
||||
unsafe { core::mem::transmute(value & 0x80) }
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 = UsbDirection::In as u8;
|
||||
|
||||
/// Constructs a new EndpointAddress with the given index and direction.
|
||||
#[inline]
|
||||
pub fn from_parts(index: usize, dir: UsbDirection) -> Self {
|
||||
EndpointAddress(index as u8 | dir as u8)
|
||||
}
|
||||
|
||||
/// Gets the direction part of the address.
|
||||
#[inline]
|
||||
pub fn direction(&self) -> UsbDirection {
|
||||
if (self.0 & Self::INBITS) != 0 {
|
||||
UsbDirection::In
|
||||
} else {
|
||||
UsbDirection::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,
|
||||
}
|
||||
|
||||
/// A handle for a USB interface that contains its number.
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
|
Reference in New Issue
Block a user