feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
@@ -0,0 +1 @@
|
||||
{"files":{".cargo_vcs_info.json":"1288a8af41f1d3b9ec669e62d1195bf96e7581e3cbfc5b4dc84fa4109b97d8a4","CHANGELOG.md":"dd5acd5faf176a8e4001ea62f2e9ef55adf4a7dca6733f5756015e13e6ba1c89","Cargo.lock":"b4f03eda797218a6bdd81964ddc888e6e754a7dac797717ad2749b67436826a8","Cargo.toml":"5b7fc1736b127ac91cf8ec62a0ce478914c6ac11cc9c276e0661d17c3e4e1f7e","Cargo.toml.orig":"94abef473094c1b6d2ab4d4ab58a340f2565dda030c26a01ac2c32b491fd7db1","README.md":"4b6463ed8326388ffcd1f44c3898e6bc7d1e8e34cef330ecf2c4b9e1e8cd7c29","release.toml":"b411a8172bfc7714d3ba0ef3e9d990c735e7fc53fe5614f59319038e93de4622","src/lib.rs":"647818adef0fc48b222d9e54f11601dedae3162075d463f01d04e7c887fda3ff"},"package":"340c5ce591ef58c6449e43f51d2c53efe1bf0bb6a40cbf80afa0d259c7d52c76"}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "5869489cc9e14d58ae2d4f9e6265795603bd2f6b"
|
||||
},
|
||||
"path_in_vcs": "embassy-usb-driver"
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# Changelog for embassy-usb-driver
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
<!-- next-header -->
|
||||
## Unreleased - ReleaseDate
|
||||
|
||||
## 0.1.1 - 2025-07-15
|
||||
|
||||
- Add `embedded_io_async::Error` implementation for `EndpointError` ([#4176](https://github.com/embassy-rs/embassy/pull/4176))
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "defmt"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"defmt-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt-macros"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e"
|
||||
dependencies = [
|
||||
"defmt-parser",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt-parser"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embassy-usb-driver"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"defmt",
|
||||
"embedded-io-async",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io-async"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f"
|
||||
dependencies = [
|
||||
"embedded-io",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
Vendored
+59
@@ -0,0 +1,59 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "embassy-usb-driver"
|
||||
version = "0.1.1"
|
||||
build = false
|
||||
autolib = false
|
||||
autobins = false
|
||||
autoexamples = false
|
||||
autotests = false
|
||||
autobenches = false
|
||||
description = "Driver trait for `embassy-usb`, an async USB device stack for embedded devices."
|
||||
documentation = "https://docs.embassy.dev/embassy-usb-driver"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"embedded",
|
||||
"async",
|
||||
"usb",
|
||||
"hal",
|
||||
"embedded-hal",
|
||||
]
|
||||
categories = [
|
||||
"embedded",
|
||||
"hardware-support",
|
||||
"no-std",
|
||||
"asynchronous",
|
||||
]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/embassy-rs/embassy"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["defmt"]
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
features = ["defmt"]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-driver-v$VERSION/embassy-usb-driver/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-usb-driver/src/"
|
||||
target = "thumbv7em-none-eabi"
|
||||
|
||||
[lib]
|
||||
name = "embassy_usb_driver"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies.defmt]
|
||||
version = "1"
|
||||
optional = true
|
||||
|
||||
[dependencies.embedded-io-async]
|
||||
version = "0.6.1"
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
# embassy-usb-driver
|
||||
|
||||
This crate contains the driver traits for [`embassy-usb`]. HAL/BSP crates can implement these
|
||||
traits to add support for using `embassy-usb` for a given chip/platform.
|
||||
|
||||
The traits are kept in a separate crate so that breaking changes in the higher-level [`embassy-usb`]
|
||||
APIs don't cause a semver-major bump of this crate. This allows existing HALs/BSPs to be used
|
||||
with the newer `embassy-usb` without needing updates.
|
||||
|
||||
If you're writing an application using USB, you should depend on the main [`embassy-usb`] crate
|
||||
instead of this one.
|
||||
|
||||
[`embassy-usb`]: https://crates.io/crates/embassy-usb
|
||||
|
||||
## Interoperability
|
||||
|
||||
This crate can run on any executor.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
pre-release-replacements = [
|
||||
{file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1},
|
||||
{file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1},
|
||||
{file="CHANGELOG.md", search="<!-- next-header -->", replace="<!-- next-header -->\n## Unreleased - ReleaseDate\n", exactly=1},
|
||||
]
|
||||
Vendored
+406
@@ -0,0 +1,406 @@
|
||||
#![no_std]
|
||||
#![allow(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<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
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<Self::EndpointOut, EndpointAllocError>;
|
||||
|
||||
/// 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<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);
|
||||
}
|
||||
|
||||
/// 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<usize, EndpointError>;
|
||||
}
|
||||
|
||||
/// 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<usize, EndpointError>;
|
||||
|
||||
/// 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,
|
||||
}
|
||||
|
||||
impl embedded_io_async::Error for EndpointError {
|
||||
fn kind(&self) -> embedded_io_async::ErrorKind {
|
||||
match self {
|
||||
Self::BufferOverflow => embedded_io_async::ErrorKind::OutOfMemory,
|
||||
Self::Disabled => embedded_io_async::ErrorKind::NotConnected,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user