aboutsummaryrefslogtreecommitdiff
path: root/embassy-usb
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-01-31 22:27:19 +0100
committerDario Nieuwenhuis <[email protected]>2023-01-31 22:27:19 +0100
commitca10fe7135d10084e38038f3cd433da39e505bea (patch)
tree075aca4a76caccd1bba95869c64bbb838969c8b1 /embassy-usb
parent4c1946454874597c358e7c7d5bf555b687376a5b (diff)
usb: docs
Diffstat (limited to 'embassy-usb')
-rw-r--r--embassy-usb/README.md22
-rw-r--r--embassy-usb/src/builder.rs24
-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
-rw-r--r--embassy-usb/src/control.rs7
-rw-r--r--embassy-usb/src/descriptor.rs4
-rw-r--r--embassy-usb/src/lib.rs8
-rw-r--r--embassy-usb/src/types.rs2
11 files changed, 130 insertions, 30 deletions
diff --git a/embassy-usb/README.md b/embassy-usb/README.md
new file mode 100644
index 000000000..581c3290f
--- /dev/null
+++ b/embassy-usb/README.md
@@ -0,0 +1,22 @@
1# embassy-usb
2
3TODO crate description/
4
5## Interoperability
6
7This crate can run on any executor.
8
9## Minimum supported Rust version (MSRV)
10
11This crate requires nightly Rust, due to using "async fn in trait" support.
12
13## License
14
15This work is licensed under either of
16
17- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
18 <http://www.apache.org/licenses/LICENSE-2.0>)
19- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
20
21at your option.
22
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs
index fc16d2b41..484989949 100644
--- a/embassy-usb/src/builder.rs
+++ b/embassy-usb/src/builder.rs
@@ -349,11 +349,11 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
349 self.builder.config_descriptor.write(descriptor_type, descriptor) 349 self.builder.config_descriptor.write(descriptor_type, descriptor)
350 } 350 }
351 351
352 fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval: u8) -> D::EndpointIn { 352 fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
353 let ep = self 353 let ep = self
354 .builder 354 .builder
355 .driver 355 .driver
356 .alloc_endpoint_in(ep_type, max_packet_size, interval) 356 .alloc_endpoint_in(ep_type, max_packet_size, interval_ms)
357 .expect("alloc_endpoint_in failed"); 357 .expect("alloc_endpoint_in failed");
358 358
359 self.builder.config_descriptor.endpoint(ep.info()); 359 self.builder.config_descriptor.endpoint(ep.info());
@@ -361,11 +361,11 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
361 ep 361 ep
362 } 362 }
363 363
364 fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval: u8) -> D::EndpointOut { 364 fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
365 let ep = self 365 let ep = self
366 .builder 366 .builder
367 .driver 367 .driver
368 .alloc_endpoint_out(ep_type, max_packet_size, interval) 368 .alloc_endpoint_out(ep_type, max_packet_size, interval_ms)
369 .expect("alloc_endpoint_out failed"); 369 .expect("alloc_endpoint_out failed");
370 370
371 self.builder.config_descriptor.endpoint(ep.info()); 371 self.builder.config_descriptor.endpoint(ep.info());
@@ -393,25 +393,25 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
393 /// 393 ///
394 /// Descriptors are written in the order builder functions are called. Note that some 394 /// Descriptors are written in the order builder functions are called. Note that some
395 /// classes care about the order. 395 /// classes care about the order.
396 pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointIn { 396 pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
397 self.endpoint_in(EndpointType::Interrupt, max_packet_size, interval) 397 self.endpoint_in(EndpointType::Interrupt, max_packet_size, interval_ms)
398 } 398 }
399 399
400 /// Allocate a INTERRUPT OUT endpoint and write its descriptor. 400 /// Allocate a INTERRUPT OUT endpoint and write its descriptor.
401 pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut { 401 pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
402 self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval) 402 self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval_ms)
403 } 403 }
404 404
405 /// Allocate a ISOCHRONOUS IN endpoint and write its descriptor. 405 /// Allocate a ISOCHRONOUS IN endpoint and write its descriptor.
406 /// 406 ///
407 /// Descriptors are written in the order builder functions are called. Note that some 407 /// Descriptors are written in the order builder functions are called. Note that some
408 /// classes care about the order. 408 /// classes care about the order.
409 pub fn endpoint_isochronous_in(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointIn { 409 pub fn endpoint_isochronous_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn {
410 self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval) 410 self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval_ms)
411 } 411 }
412 412
413 /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor. 413 /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor.
414 pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval: u8) -> D::EndpointOut { 414 pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut {
415 self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval) 415 self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval_ms)
416 } 416 }
417} 417}
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;
diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs
index d6d0c6565..39b499f03 100644
--- a/embassy-usb/src/control.rs
+++ b/embassy-usb/src/control.rs
@@ -126,17 +126,23 @@ impl Request {
126 } 126 }
127} 127}
128 128
129/// Response for a CONTROL OUT request.
129#[derive(Copy, Clone, Eq, PartialEq, Debug)] 130#[derive(Copy, Clone, Eq, PartialEq, Debug)]
130#[cfg_attr(feature = "defmt", derive(defmt::Format))] 131#[cfg_attr(feature = "defmt", derive(defmt::Format))]
131pub enum OutResponse { 132pub enum OutResponse {
133 /// The request was accepted.
132 Accepted, 134 Accepted,
135 /// The request was rejected.
133 Rejected, 136 Rejected,
134} 137}
135 138
139/// Response for a CONTROL IN request.
136#[derive(Copy, Clone, Eq, PartialEq, Debug)] 140#[derive(Copy, Clone, Eq, PartialEq, Debug)]
137#[cfg_attr(feature = "defmt", derive(defmt::Format))] 141#[cfg_attr(feature = "defmt", derive(defmt::Format))]
138pub enum InResponse<'a> { 142pub enum InResponse<'a> {
143 /// The request was accepted. The buffer contains the response data.
139 Accepted(&'a [u8]), 144 Accepted(&'a [u8]),
145 /// The request was rejected.
140 Rejected, 146 Rejected,
141} 147}
142 148
@@ -148,6 +154,7 @@ pub trait ControlHandler {
148 /// Called after a USB reset after the bus reset sequence is complete. 154 /// Called after a USB reset after the bus reset sequence is complete.
149 fn reset(&mut self) {} 155 fn reset(&mut self) {}
150 156
157 /// Called when a "set alternate setting" control request is done on the interface.
151 fn set_alternate_setting(&mut self, alternate_setting: u8) { 158 fn set_alternate_setting(&mut self, alternate_setting: u8) {
152 let _ = alternate_setting; 159 let _ = alternate_setting;
153 } 160 }
diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs
index 497f03196..ae38e26ca 100644
--- a/embassy-usb/src/descriptor.rs
+++ b/embassy-usb/src/descriptor.rs
@@ -1,3 +1,5 @@
1//! Utilities for writing USB descriptors.
2
1use crate::builder::Config; 3use crate::builder::Config;
2use crate::driver::EndpointInfo; 4use crate::driver::EndpointInfo;
3use crate::types::*; 5use crate::types::*;
@@ -236,7 +238,7 @@ impl<'a> DescriptorWriter<'a> {
236 endpoint.ep_type as u8, // bmAttributes 238 endpoint.ep_type as u8, // bmAttributes
237 endpoint.max_packet_size as u8, 239 endpoint.max_packet_size as u8,
238 (endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize 240 (endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize
239 endpoint.interval, // bInterval 241 endpoint.interval_ms, // bInterval
240 ], 242 ],
241 ); 243 );
242 } 244 }
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs
index 096e8b07a..2656af29d 100644
--- a/embassy-usb/src/lib.rs
+++ b/embassy-usb/src/lib.rs
@@ -1,5 +1,7 @@
1#![no_std] 1#![no_std]
2#![feature(type_alias_impl_trait)] 2#![feature(type_alias_impl_trait)]
3#![doc = include_str!("../README.md")]
4#![warn(missing_docs)]
3 5
4// This mod MUST go first, so that the others see its macros. 6// This mod MUST go first, so that the others see its macros.
5pub(crate) mod fmt; 7pub(crate) mod fmt;
@@ -46,10 +48,13 @@ pub enum UsbDeviceState {
46 Configured, 48 Configured,
47} 49}
48 50
51/// Error returned by [`UsbDevice::remote_wakeup`].
49#[derive(PartialEq, Eq, Copy, Clone, Debug)] 52#[derive(PartialEq, Eq, Copy, Clone, Debug)]
50#[cfg_attr(feature = "defmt", derive(defmt::Format))] 53#[cfg_attr(feature = "defmt", derive(defmt::Format))]
51pub enum RemoteWakeupError { 54pub enum RemoteWakeupError {
55 /// The USB device is not suspended, or remote wakeup was not enabled.
52 InvalidState, 56 InvalidState,
57 /// The underlying driver doesn't support remote wakeup.
53 Unsupported, 58 Unsupported,
54} 59}
55 60
@@ -65,6 +70,7 @@ pub const CONFIGURATION_NONE: u8 = 0;
65/// The bConfiguration value for the single configuration supported by this device. 70/// The bConfiguration value for the single configuration supported by this device.
66pub const CONFIGURATION_VALUE: u8 = 1; 71pub const CONFIGURATION_VALUE: u8 = 1;
67 72
73/// Maximum interface count, configured at compile time.
68pub const MAX_INTERFACE_COUNT: usize = 4; 74pub const MAX_INTERFACE_COUNT: usize = 4;
69 75
70const STRING_INDEX_MANUFACTURER: u8 = 1; 76const STRING_INDEX_MANUFACTURER: u8 = 1;
@@ -100,6 +106,7 @@ struct Interface<'d> {
100 num_strings: u8, 106 num_strings: u8,
101} 107}
102 108
109/// Main struct for the USB device stack.
103pub struct UsbDevice<'d, D: Driver<'d>> { 110pub struct UsbDevice<'d, D: Driver<'d>> {
104 control_buf: &'d mut [u8], 111 control_buf: &'d mut [u8],
105 control: D::ControlPipe, 112 control: D::ControlPipe,
@@ -489,7 +496,6 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
489 .unwrap(); 496 .unwrap();
490 497
491 // TODO check it is valid (not out of range) 498 // TODO check it is valid (not out of range)
492 // TODO actually enable/disable endpoints.
493 499
494 if let Some(handler) = &mut iface.handler { 500 if let Some(handler) = &mut iface.handler {
495 handler.set_alternate_setting(new_altsetting); 501 handler.set_alternate_setting(new_altsetting);
diff --git a/embassy-usb/src/types.rs b/embassy-usb/src/types.rs
index aeab063d1..1743e61ff 100644
--- a/embassy-usb/src/types.rs
+++ b/embassy-usb/src/types.rs
@@ -1,3 +1,5 @@
1//! USB types.
2
1/// A handle for a USB interface that contains its number. 3/// A handle for a USB interface that contains its number.
2#[derive(Copy, Clone, Eq, PartialEq)] 4#[derive(Copy, Clone, Eq, PartialEq)]
3#[cfg_attr(feature = "defmt", derive(defmt::Format))] 5#[cfg_attr(feature = "defmt", derive(defmt::Format))]