aboutsummaryrefslogtreecommitdiff
path: root/embassy-usb/src/class
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-usb/src/class')
-rw-r--r--embassy-usb/src/class/cdc_acm.rs9
-rw-r--r--embassy-usb/src/class/cdc_ncm/embassy_net.rs11
-rw-r--r--embassy-usb/src/class/cdc_ncm/mod.rs51
-rw-r--r--embassy-usb/src/class/hid.rs21
-rw-r--r--embassy-usb/src/class/mod.rs1
5 files changed, 77 insertions, 16 deletions
diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs
index 84db20621..09f17456c 100644
--- a/embassy-usb/src/class/cdc_acm.rs
+++ b/embassy-usb/src/class/cdc_acm.rs
@@ -1,3 +1,5 @@
1//! CDC-ACM class implementation, aka Serial over USB.
2
1use core::cell::Cell; 3use core::cell::Cell;
2use core::mem::{self, MaybeUninit}; 4use core::mem::{self, MaybeUninit};
3use core::sync::atomic::{AtomicBool, Ordering}; 5use core::sync::atomic::{AtomicBool, Ordering};
@@ -28,12 +30,14 @@ const REQ_SET_LINE_CODING: u8 = 0x20;
28const REQ_GET_LINE_CODING: u8 = 0x21; 30const REQ_GET_LINE_CODING: u8 = 0x21;
29const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; 31const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22;
30 32
33/// Internal state for CDC-ACM
31pub struct State<'a> { 34pub struct State<'a> {
32 control: MaybeUninit<Control<'a>>, 35 control: MaybeUninit<Control<'a>>,
33 shared: ControlShared, 36 shared: ControlShared,
34} 37}
35 38
36impl<'a> State<'a> { 39impl<'a> State<'a> {
40 /// Create a new `State`.
37 pub fn new() -> Self { 41 pub fn new() -> Self {
38 Self { 42 Self {
39 control: MaybeUninit::uninit(), 43 control: MaybeUninit::uninit(),
@@ -284,10 +288,15 @@ impl From<u8> for StopBits {
284#[derive(Copy, Clone, Debug, PartialEq, Eq)] 288#[derive(Copy, Clone, Debug, PartialEq, Eq)]
285#[cfg_attr(feature = "defmt", derive(defmt::Format))] 289#[cfg_attr(feature = "defmt", derive(defmt::Format))]
286pub enum ParityType { 290pub enum ParityType {
291 /// No parity bit.
287 None = 0, 292 None = 0,
293 /// Parity bit is 1 if the amount of `1` bits in the data byte is odd.
288 Odd = 1, 294 Odd = 1,
295 /// Parity bit is 1 if the amount of `1` bits in the data byte is even.
289 Even = 2, 296 Even = 2,
297 /// Parity bit is always 1
290 Mark = 3, 298 Mark = 3,
299 /// Parity bit is always 0
291 Space = 4, 300 Space = 4,
292} 301}
293 302
diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs
index 501df2d8c..bc79b3671 100644
--- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs
+++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs
@@ -1,3 +1,4 @@
1//! [`embassy-net`](crates.io/crates/embassy-net) driver for the CDC-NCM class.
1use embassy_futures::select::{select, Either}; 2use embassy_futures::select::{select, Either};
2use embassy_net_driver_channel as ch; 3use embassy_net_driver_channel as ch;
3use embassy_net_driver_channel::driver::LinkState; 4use embassy_net_driver_channel::driver::LinkState;
@@ -5,11 +6,13 @@ use embassy_usb_driver::Driver;
5 6
6use super::{CdcNcmClass, Receiver, Sender}; 7use super::{CdcNcmClass, Receiver, Sender};
7 8
9/// Internal state for the embassy-net integration.
8pub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> { 10pub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> {
9 ch_state: ch::State<MTU, N_RX, N_TX>, 11 ch_state: ch::State<MTU, N_RX, N_TX>,
10} 12}
11 13
12impl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_TX> { 14impl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_TX> {
15 /// Create a new `State`.
13 pub const fn new() -> Self { 16 pub const fn new() -> Self {
14 Self { 17 Self {
15 ch_state: ch::State::new(), 18 ch_state: ch::State::new(),
@@ -17,6 +20,9 @@ impl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_
17 } 20 }
18} 21}
19 22
23/// Background runner for the CDC-NCM class.
24///
25/// You must call `.run()` in a background task for the class to operate.
20pub struct Runner<'d, D: Driver<'d>, const MTU: usize> { 26pub struct Runner<'d, D: Driver<'d>, const MTU: usize> {
21 tx_usb: Sender<'d, D>, 27 tx_usb: Sender<'d, D>,
22 rx_usb: Receiver<'d, D>, 28 rx_usb: Receiver<'d, D>,
@@ -24,6 +30,9 @@ pub struct Runner<'d, D: Driver<'d>, const MTU: usize> {
24} 30}
25 31
26impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { 32impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> {
33 /// Run the CDC-NCM class.
34 ///
35 /// You must call this in a background task for the class to operate.
27 pub async fn run(mut self) -> ! { 36 pub async fn run(mut self) -> ! {
28 let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split(); 37 let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();
29 let rx_fut = async move { 38 let rx_fut = async move {
@@ -66,9 +75,11 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> {
66 75
67// would be cool to use a TAIT here, but it gives a "may not live long enough". rustc bug? 76// would be cool to use a TAIT here, but it gives a "may not live long enough". rustc bug?
68//pub type Device<'d, const MTU: usize> = impl embassy_net_driver_channel::driver::Driver + 'd; 77//pub type Device<'d, const MTU: usize> = impl embassy_net_driver_channel::driver::Driver + 'd;
78/// Type alias for the embassy-net driver for CDC-NCM.
69pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>; 79pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>;
70 80
71impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { 81impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
82 /// Obtain a driver for using the CDC-NCM class with [`embassy-net`](crates.io/crates/embassy-net).
72 pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>( 83 pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>(
73 self, 84 self,
74 state: &'d mut State<MTU, N_RX, N_TX>, 85 state: &'d mut State<MTU, N_RX, N_TX>,
diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs
index 4954a65bc..5e59b72fe 100644
--- a/embassy-usb/src/class/cdc_ncm/mod.rs
+++ b/embassy-usb/src/class/cdc_ncm/mod.rs
@@ -1,18 +1,19 @@
1/// CDC-NCM, aka Ethernet over USB. 1//! CDC-NCM class implementation, aka Ethernet over USB.
2/// 2//!
3/// # Compatibility 3//! # Compatibility
4/// 4//!
5/// Windows: NOT supported in Windows 10. Supported in Windows 11. 5//! Windows: NOT supported in Windows 10 (though there's apparently a driver you can install?). Supported out of the box in Windows 11.
6/// 6//!
7/// Linux: Well-supported since forever. 7//! Linux: Well-supported since forever.
8/// 8//!
9/// Android: Support for CDC-NCM is spotty and varies across manufacturers. 9//! Android: Support for CDC-NCM is spotty and varies across manufacturers.
10/// 10//!
11/// - On Pixel 4a, it refused to work on Android 11, worked on Android 12. 11//! - On Pixel 4a, it refused to work on Android 11, worked on Android 12.
12/// - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), 12//! - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte),
13/// it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. 13//! it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled.
14/// This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 14//! This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417
15/// and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 15//! and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757
16
16use core::intrinsics::copy_nonoverlapping; 17use core::intrinsics::copy_nonoverlapping;
17use core::mem::{size_of, MaybeUninit}; 18use core::mem::{size_of, MaybeUninit};
18 19
@@ -114,6 +115,7 @@ fn byteify<T>(buf: &mut [u8], data: T) -> &[u8] {
114 &buf[..len] 115 &buf[..len]
115} 116}
116 117
118/// Internal state for the CDC-NCM class.
117pub struct State<'a> { 119pub struct State<'a> {
118 comm_control: MaybeUninit<CommControl<'a>>, 120 comm_control: MaybeUninit<CommControl<'a>>,
119 data_control: MaybeUninit<DataControl>, 121 data_control: MaybeUninit<DataControl>,
@@ -121,6 +123,7 @@ pub struct State<'a> {
121} 123}
122 124
123impl<'a> State<'a> { 125impl<'a> State<'a> {
126 /// Create a new `State`.
124 pub fn new() -> Self { 127 pub fn new() -> Self {
125 Self { 128 Self {
126 comm_control: MaybeUninit::uninit(), 129 comm_control: MaybeUninit::uninit(),
@@ -223,6 +226,7 @@ impl ControlHandler for DataControl {
223 } 226 }
224} 227}
225 228
229/// CDC-NCM class
226pub struct CdcNcmClass<'d, D: Driver<'d>> { 230pub struct CdcNcmClass<'d, D: Driver<'d>> {
227 _comm_if: InterfaceNumber, 231 _comm_if: InterfaceNumber,
228 comm_ep: D::EndpointIn, 232 comm_ep: D::EndpointIn,
@@ -235,6 +239,7 @@ pub struct CdcNcmClass<'d, D: Driver<'d>> {
235} 239}
236 240
237impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { 241impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
242 /// Create a new CDC NCM class.
238 pub fn new( 243 pub fn new(
239 builder: &mut Builder<'d, D>, 244 builder: &mut Builder<'d, D>,
240 state: &'d mut State<'d>, 245 state: &'d mut State<'d>,
@@ -319,6 +324,9 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
319 } 324 }
320 } 325 }
321 326
327 /// Split the class into a sender and receiver.
328 ///
329 /// This allows concurrently sending and receiving packets from separate tasks.
322 pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) { 330 pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) {
323 ( 331 (
324 Sender { 332 Sender {
@@ -334,12 +342,18 @@ impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
334 } 342 }
335} 343}
336 344
345/// CDC NCM class packet sender.
346///
347/// You can obtain a `Sender` with [`CdcNcmClass::split`]
337pub struct Sender<'d, D: Driver<'d>> { 348pub struct Sender<'d, D: Driver<'d>> {
338 write_ep: D::EndpointIn, 349 write_ep: D::EndpointIn,
339 seq: u16, 350 seq: u16,
340} 351}
341 352
342impl<'d, D: Driver<'d>> Sender<'d, D> { 353impl<'d, D: Driver<'d>> Sender<'d, D> {
354 /// Write a packet.
355 ///
356 /// This waits until the packet is succesfully stored in the CDC-NCM endpoint buffers.
343 pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> { 357 pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
344 let seq = self.seq; 358 let seq = self.seq;
345 self.seq = self.seq.wrapping_add(1); 359 self.seq = self.seq.wrapping_add(1);
@@ -393,6 +407,9 @@ impl<'d, D: Driver<'d>> Sender<'d, D> {
393 } 407 }
394} 408}
395 409
410/// CDC NCM class packet receiver.
411///
412/// You can obtain a `Receiver` with [`CdcNcmClass::split`]
396pub struct Receiver<'d, D: Driver<'d>> { 413pub struct Receiver<'d, D: Driver<'d>> {
397 data_if: InterfaceNumber, 414 data_if: InterfaceNumber,
398 comm_ep: D::EndpointIn, 415 comm_ep: D::EndpointIn,
@@ -400,7 +417,9 @@ pub struct Receiver<'d, D: Driver<'d>> {
400} 417}
401 418
402impl<'d, D: Driver<'d>> Receiver<'d, D> { 419impl<'d, D: Driver<'d>> Receiver<'d, D> {
403 /// Reads a single packet from the OUT endpoint. 420 /// Write a network packet.
421 ///
422 /// This waits until a packet is succesfully received from the endpoint buffers.
404 pub async fn read_packet(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> { 423 pub async fn read_packet(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
405 // Retry loop 424 // Retry loop
406 loop { 425 loop {
diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs
index b967aba0e..2493061b0 100644
--- a/embassy-usb/src/class/hid.rs
+++ b/embassy-usb/src/class/hid.rs
@@ -1,3 +1,5 @@
1//! USB HID (Human Interface Device) class implementation.
2
1use core::mem::MaybeUninit; 3use core::mem::MaybeUninit;
2use core::ops::Range; 4use core::ops::Range;
3use core::sync::atomic::{AtomicUsize, Ordering}; 5use core::sync::atomic::{AtomicUsize, Ordering};
@@ -28,6 +30,7 @@ const HID_REQ_SET_REPORT: u8 = 0x09;
28const HID_REQ_GET_PROTOCOL: u8 = 0x03; 30const HID_REQ_GET_PROTOCOL: u8 = 0x03;
29const HID_REQ_SET_PROTOCOL: u8 = 0x0b; 31const HID_REQ_SET_PROTOCOL: u8 = 0x0b;
30 32
33/// Configuration for the HID class.
31pub struct Config<'d> { 34pub struct Config<'d> {
32 /// HID report descriptor. 35 /// HID report descriptor.
33 pub report_descriptor: &'d [u8], 36 pub report_descriptor: &'d [u8],
@@ -46,11 +49,15 @@ pub struct Config<'d> {
46 pub max_packet_size: u16, 49 pub max_packet_size: u16,
47} 50}
48 51
52/// Report ID
49#[derive(Debug, Clone, Copy, PartialEq, Eq)] 53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50#[cfg_attr(feature = "defmt", derive(defmt::Format))] 54#[cfg_attr(feature = "defmt", derive(defmt::Format))]
51pub enum ReportId { 55pub enum ReportId {
56 /// IN report
52 In(u8), 57 In(u8),
58 /// OUT report
53 Out(u8), 59 Out(u8),
60 /// Feature report
54 Feature(u8), 61 Feature(u8),
55} 62}
56 63
@@ -65,12 +72,14 @@ impl ReportId {
65 } 72 }
66} 73}
67 74
75/// Internal state for USB HID.
68pub struct State<'d> { 76pub struct State<'d> {
69 control: MaybeUninit<Control<'d>>, 77 control: MaybeUninit<Control<'d>>,
70 out_report_offset: AtomicUsize, 78 out_report_offset: AtomicUsize,
71} 79}
72 80
73impl<'d> State<'d> { 81impl<'d> State<'d> {
82 /// Create a new `State`.
74 pub fn new() -> Self { 83 pub fn new() -> Self {
75 State { 84 State {
76 control: MaybeUninit::uninit(), 85 control: MaybeUninit::uninit(),
@@ -79,6 +88,7 @@ impl<'d> State<'d> {
79 } 88 }
80} 89}
81 90
91/// USB HID reader/writer.
82pub struct HidReaderWriter<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> { 92pub struct HidReaderWriter<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> {
83 reader: HidReader<'d, D, READ_N>, 93 reader: HidReader<'d, D, READ_N>,
84 writer: HidWriter<'d, D, WRITE_N>, 94 writer: HidWriter<'d, D, WRITE_N>,
@@ -180,20 +190,30 @@ impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> HidReaderWrit
180 } 190 }
181} 191}
182 192
193/// USB HID writer.
194///
195/// You can obtain a `HidWriter` using [`HidReaderWriter::split`].
183pub struct HidWriter<'d, D: Driver<'d>, const N: usize> { 196pub struct HidWriter<'d, D: Driver<'d>, const N: usize> {
184 ep_in: D::EndpointIn, 197 ep_in: D::EndpointIn,
185} 198}
186 199
200/// USB HID reader.
201///
202/// You can obtain a `HidReader` using [`HidReaderWriter::split`].
187pub struct HidReader<'d, D: Driver<'d>, const N: usize> { 203pub struct HidReader<'d, D: Driver<'d>, const N: usize> {
188 ep_out: D::EndpointOut, 204 ep_out: D::EndpointOut,
189 offset: &'d AtomicUsize, 205 offset: &'d AtomicUsize,
190} 206}
191 207
208/// Error when reading a HID report.
192#[derive(Debug, Clone, PartialEq, Eq)] 209#[derive(Debug, Clone, PartialEq, Eq)]
193#[cfg_attr(feature = "defmt", derive(defmt::Format))] 210#[cfg_attr(feature = "defmt", derive(defmt::Format))]
194pub enum ReadError { 211pub enum ReadError {
212 /// The given buffer was too small to read the received report.
195 BufferOverflow, 213 BufferOverflow,
214 /// The endpoint is disabled.
196 Disabled, 215 Disabled,
216 /// The report was only partially read. See [`HidReader::read`] for details.
197 Sync(Range<usize>), 217 Sync(Range<usize>),
198} 218}
199 219
@@ -344,6 +364,7 @@ impl<'d, D: Driver<'d>, const N: usize> HidReader<'d, D, N> {
344 } 364 }
345} 365}
346 366
367/// Handler for HID-related control requests.
347pub trait RequestHandler { 368pub trait RequestHandler {
348 /// Reads the value of report `id` into `buf` returning the size. 369 /// Reads the value of report `id` into `buf` returning the size.
349 /// 370 ///
diff --git a/embassy-usb/src/class/mod.rs b/embassy-usb/src/class/mod.rs
index af27577a6..b23e03d40 100644
--- a/embassy-usb/src/class/mod.rs
+++ b/embassy-usb/src/class/mod.rs
@@ -1,3 +1,4 @@
1//! Implementations of well-known USB classes.
1pub mod cdc_acm; 2pub mod cdc_acm;
2pub mod cdc_ncm; 3pub mod cdc_ncm;
3pub mod hid; 4pub mod hid;