aboutsummaryrefslogtreecommitdiff
path: root/embassy-usb/src
diff options
context:
space:
mode:
authorRaul Alimbekov <[email protected]>2025-12-16 09:05:22 +0300
committerGitHub <[email protected]>2025-12-16 09:05:22 +0300
commitc9a04b4b732b7a3b696eb8223664c1a7942b1875 (patch)
tree6dbe5c02e66eed8d8762f13f95afd24f8db2b38c /embassy-usb/src
parentcde24a3ef1117653ba5ed4184102b33f745782fb (diff)
parent5ae6e060ec1c90561719aabdc29d5b6e7b8b0a82 (diff)
Merge branch 'main' into main
Diffstat (limited to 'embassy-usb/src')
-rw-r--r--embassy-usb/src/builder.rs9
-rw-r--r--embassy-usb/src/class/cdc_acm.rs19
-rw-r--r--embassy-usb/src/class/cdc_ncm/embassy_net.rs2
-rw-r--r--embassy-usb/src/class/cdc_ncm/mod.rs2
-rw-r--r--embassy-usb/src/class/cmsis_dap_v2.rs2
-rw-r--r--embassy-usb/src/class/hid.rs101
-rw-r--r--embassy-usb/src/class/midi.rs2
-rw-r--r--embassy-usb/src/class/uac1/speaker.rs4
-rw-r--r--embassy-usb/src/descriptor.rs2
-rw-r--r--embassy-usb/src/lib.rs3
-rw-r--r--embassy-usb/src/msos.rs2
11 files changed, 122 insertions, 26 deletions
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs
index 8d7abe46c..6fc2a5a22 100644
--- a/embassy-usb/src/builder.rs
+++ b/embassy-usb/src/builder.rs
@@ -5,7 +5,7 @@ use crate::descriptor::{BosWriter, DescriptorWriter, SynchronizationType, UsageT
5use crate::driver::{Driver, Endpoint, EndpointAddress, EndpointInfo, EndpointType}; 5use crate::driver::{Driver, Endpoint, EndpointAddress, EndpointInfo, EndpointType};
6use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; 6use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter};
7use crate::types::{InterfaceNumber, StringIndex}; 7use crate::types::{InterfaceNumber, StringIndex};
8use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; 8use crate::{Handler, Interface, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START, UsbDevice};
9 9
10#[derive(Debug, Copy, Clone)] 10#[derive(Debug, Copy, Clone)]
11#[cfg_attr(feature = "defmt", derive(defmt::Format))] 11#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -176,7 +176,9 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
176 if config.composite_with_iads 176 if config.composite_with_iads
177 && (config.device_class != 0xEF || config.device_sub_class != 0x02 || config.device_protocol != 0x01) 177 && (config.device_class != 0xEF || config.device_sub_class != 0x02 || config.device_protocol != 0x01)
178 { 178 {
179 panic!("if composite_with_iads is set, you must set device_class = 0xEF, device_sub_class = 0x02, device_protocol = 0x01"); 179 panic!(
180 "if composite_with_iads is set, you must set device_class = 0xEF, device_sub_class = 0x02, device_protocol = 0x01"
181 );
180 } 182 }
181 183
182 assert!( 184 assert!(
@@ -337,7 +339,8 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
337 num_alt_settings: 0, 339 num_alt_settings: 0,
338 }; 340 };
339 341
340 assert!(self.builder.interfaces.push(iface).is_ok(), 342 assert!(
343 self.builder.interfaces.push(iface).is_ok(),
341 "embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}", 344 "embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}",
342 MAX_INTERFACE_COUNT 345 MAX_INTERFACE_COUNT
343 ); 346 );
diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs
index 0a1a5e64f..ab2311f4e 100644
--- a/embassy-usb/src/class/cdc_acm.rs
+++ b/embassy-usb/src/class/cdc_acm.rs
@@ -1,7 +1,7 @@
1//! CDC-ACM class implementation, aka Serial over USB. 1//! CDC-ACM class implementation, aka Serial over USB.
2 2
3use core::cell::{Cell, RefCell}; 3use core::cell::{Cell, RefCell};
4use core::future::{poll_fn, Future}; 4use core::future::{Future, poll_fn};
5use core::mem::{self, MaybeUninit}; 5use core::mem::{self, MaybeUninit};
6use core::sync::atomic::{AtomicBool, Ordering}; 6use core::sync::atomic::{AtomicBool, Ordering};
7use core::task::Poll; 7use core::task::Poll;
@@ -366,6 +366,16 @@ impl<'d> ControlChanged<'d> {
366 pub async fn control_changed(&self) { 366 pub async fn control_changed(&self) {
367 self.control.changed().await; 367 self.control.changed().await;
368 } 368 }
369
370 /// Gets the DTR (data terminal ready) state
371 pub fn dtr(&self) -> bool {
372 self.control.dtr.load(Ordering::Relaxed)
373 }
374
375 /// Gets the RTS (request to send) state
376 pub fn rts(&self) -> bool {
377 self.control.rts.load(Ordering::Relaxed)
378 }
369} 379}
370 380
371/// CDC ACM class packet sender. 381/// CDC ACM class packet sender.
@@ -545,9 +555,12 @@ impl<'d, D: Driver<'d>> embedded_io_async::Read for BufferedReceiver<'d, D> {
545 return self.receiver.read_packet(buf).await; 555 return self.receiver.read_packet(buf).await;
546 } 556 }
547 557
548 // Otherwise read a packet into the internal buffer, and return some of it to the caller 558 // Otherwise read a packet into the internal buffer, and return some of it to the caller.
549 self.start = 0; 559 //
560 // It's important that `start` and `end` be updated in this order so they're left in a
561 // consistent state if the `read` future is dropped mid-execution, e.g. from a timeout.
550 self.end = self.receiver.read_packet(&mut self.buffer).await?; 562 self.end = self.receiver.read_packet(&mut self.buffer).await?;
563 self.start = 0;
551 return Ok(self.read_from_buffer(buf)); 564 return Ok(self.read_from_buffer(buf));
552 } 565 }
553} 566}
diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs
index 57d322946..c83ff468a 100644
--- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs
+++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs
@@ -1,6 +1,6 @@
1//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the CDC-NCM class. 1//! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the CDC-NCM class.
2 2
3use embassy_futures::select::{select, Either}; 3use embassy_futures::select::{Either, select};
4use embassy_net_driver_channel as ch; 4use embassy_net_driver_channel as ch;
5use embassy_net_driver_channel::driver::LinkState; 5use embassy_net_driver_channel::driver::LinkState;
6use embassy_usb_driver::Driver; 6use embassy_usb_driver::Driver;
diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs
index 3af853091..9b6dd9f21 100644
--- a/embassy-usb/src/class/cdc_ncm/mod.rs
+++ b/embassy-usb/src/class/cdc_ncm/mod.rs
@@ -14,7 +14,7 @@
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 16
17use core::mem::{size_of, MaybeUninit}; 17use core::mem::{MaybeUninit, size_of};
18use core::ptr::{addr_of, copy_nonoverlapping}; 18use core::ptr::{addr_of, copy_nonoverlapping};
19 19
20use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; 20use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};
diff --git a/embassy-usb/src/class/cmsis_dap_v2.rs b/embassy-usb/src/class/cmsis_dap_v2.rs
index a9fd9cdf0..0e2356f17 100644
--- a/embassy-usb/src/class/cmsis_dap_v2.rs
+++ b/embassy-usb/src/class/cmsis_dap_v2.rs
@@ -4,7 +4,7 @@ use core::mem::MaybeUninit;
4 4
5use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; 5use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
6use crate::types::StringIndex; 6use crate::types::StringIndex;
7use crate::{msos, Builder, Handler}; 7use crate::{Builder, Handler, msos};
8 8
9/// State for the CMSIS-DAP v2 USB class. 9/// State for the CMSIS-DAP v2 USB class.
10pub struct State { 10pub struct State {
diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs
index 182e1f83f..64e8fd59f 100644
--- a/embassy-usb/src/class/hid.rs
+++ b/embassy-usb/src/class/hid.rs
@@ -15,8 +15,6 @@ use crate::types::InterfaceNumber;
15use crate::{Builder, Handler}; 15use crate::{Builder, Handler};
16 16
17const USB_CLASS_HID: u8 = 0x03; 17const USB_CLASS_HID: u8 = 0x03;
18const USB_SUBCLASS_NONE: u8 = 0x00;
19const USB_PROTOCOL_NONE: u8 = 0x00;
20 18
21// HID 19// HID
22const HID_DESC_DESCTYPE_HID: u8 = 0x21; 20const HID_DESC_DESCTYPE_HID: u8 = 0x21;
@@ -31,6 +29,52 @@ const HID_REQ_SET_REPORT: u8 = 0x09;
31const HID_REQ_GET_PROTOCOL: u8 = 0x03; 29const HID_REQ_GET_PROTOCOL: u8 = 0x03;
32const HID_REQ_SET_PROTOCOL: u8 = 0x0b; 30const HID_REQ_SET_PROTOCOL: u8 = 0x0b;
33 31
32/// Get/Set Protocol mapping
33/// See (7.2.5 and 7.2.6): <https://www.usb.org/sites/default/files/hid1_11.pdf>
34#[derive(Copy, Clone, Debug, PartialEq, Eq)]
35#[cfg_attr(feature = "defmt", derive(defmt::Format))]
36#[repr(u8)]
37pub enum HidProtocolMode {
38 /// Hid Boot Protocol Mode
39 Boot = 0,
40 /// Hid Report Protocol Mode
41 Report = 1,
42}
43
44impl From<u8> for HidProtocolMode {
45 fn from(mode: u8) -> HidProtocolMode {
46 if mode == HidProtocolMode::Boot as u8 {
47 HidProtocolMode::Boot
48 } else {
49 HidProtocolMode::Report
50 }
51 }
52}
53
54/// USB HID interface subclass values.
55#[derive(Copy, Clone, Debug, PartialEq, Eq)]
56#[cfg_attr(feature = "defmt", derive(defmt::Format))]
57#[repr(u8)]
58pub enum HidSubclass {
59 /// No subclass, standard HID device.
60 No = 0,
61 /// Boot interface subclass, supports BIOS boot protocol.
62 Boot = 1,
63}
64
65/// USB HID protocol values.
66#[derive(Copy, Clone, Debug, PartialEq, Eq)]
67#[cfg_attr(feature = "defmt", derive(defmt::Format))]
68#[repr(u8)]
69pub enum HidBootProtocol {
70 /// No boot protocol.
71 None = 0,
72 /// Keyboard boot protocol.
73 Keyboard = 1,
74 /// Mouse boot protocol.
75 Mouse = 2,
76}
77
34/// Configuration for the HID class. 78/// Configuration for the HID class.
35pub struct Config<'d> { 79pub struct Config<'d> {
36 /// HID report descriptor. 80 /// HID report descriptor.
@@ -48,6 +92,12 @@ pub struct Config<'d> {
48 92
49 /// Max packet size for both the IN and OUT endpoints. 93 /// Max packet size for both the IN and OUT endpoints.
50 pub max_packet_size: u16, 94 pub max_packet_size: u16,
95
96 /// The HID subclass of this interface
97 pub hid_subclass: HidSubclass,
98
99 /// The HID boot protocol of this interface
100 pub hid_boot_protocol: HidBootProtocol,
51} 101}
52 102
53/// Report ID 103/// Report ID
@@ -109,10 +159,15 @@ fn build<'d, D: Driver<'d>>(
109) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) { 159) -> (Option<D::EndpointOut>, D::EndpointIn, &'d AtomicUsize) {
110 let len = config.report_descriptor.len(); 160 let len = config.report_descriptor.len();
111 161
112 let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); 162 let mut func = builder.function(USB_CLASS_HID, config.hid_subclass as u8, config.hid_boot_protocol as u8);
113 let mut iface = func.interface(); 163 let mut iface = func.interface();
114 let if_num = iface.interface_number(); 164 let if_num = iface.interface_number();
115 let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None); 165 let mut alt = iface.alt_setting(
166 USB_CLASS_HID,
167 config.hid_subclass as u8,
168 config.hid_boot_protocol as u8,
169 None,
170 );
116 171
117 // HID descriptor 172 // HID descriptor
118 alt.descriptor( 173 alt.descriptor(
@@ -389,6 +444,23 @@ pub trait RequestHandler {
389 OutResponse::Rejected 444 OutResponse::Rejected
390 } 445 }
391 446
447 /// Gets the current hid protocol.
448 ///
449 /// Returns `Report` protocol by default.
450 fn get_protocol(&self) -> HidProtocolMode {
451 HidProtocolMode::Report
452 }
453
454 /// Sets the current hid protocol to `protocol`.
455 ///
456 /// Accepts only `Report` protocol by default.
457 fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse {
458 match protocol {
459 HidProtocolMode::Report => OutResponse::Accepted,
460 HidProtocolMode::Boot => OutResponse::Rejected,
461 }
462 }
463
392 /// Get the idle rate for `id`. 464 /// Get the idle rate for `id`.
393 /// 465 ///
394 /// If `id` is `None`, get the idle rate for all reports. Returning `None` 466 /// If `id` is `None`, get the idle rate for all reports. Returning `None`
@@ -482,11 +554,14 @@ impl<'d> Handler for Control<'d> {
482 _ => Some(OutResponse::Rejected), 554 _ => Some(OutResponse::Rejected),
483 }, 555 },
484 HID_REQ_SET_PROTOCOL => { 556 HID_REQ_SET_PROTOCOL => {
485 if req.value == 1 { 557 let hid_protocol = HidProtocolMode::from(req.value as u8);
486 Some(OutResponse::Accepted) 558 match (self.request_handler.as_mut(), hid_protocol) {
487 } else { 559 (Some(request_handler), hid_protocol) => Some(request_handler.set_protocol(hid_protocol)),
488 warn!("HID Boot Protocol is unsupported."); 560 (None, HidProtocolMode::Report) => Some(OutResponse::Accepted),
489 Some(OutResponse::Rejected) // UNSUPPORTED: Boot Protocol 561 (None, HidProtocolMode::Boot) => {
562 info!("Received request to switch to Boot protocol mode, but it is disabled by default.");
563 Some(OutResponse::Rejected)
564 }
490 } 565 }
491 } 566 }
492 _ => Some(OutResponse::Rejected), 567 _ => Some(OutResponse::Rejected),
@@ -539,8 +614,12 @@ impl<'d> Handler for Control<'d> {
539 } 614 }
540 } 615 }
541 HID_REQ_GET_PROTOCOL => { 616 HID_REQ_GET_PROTOCOL => {
542 // UNSUPPORTED: Boot Protocol 617 if let Some(request_handler) = self.request_handler.as_mut() {
543 buf[0] = 1; 618 buf[0] = request_handler.get_protocol() as u8;
619 } else {
620 // Return `Report` protocol mode by default
621 buf[0] = HidProtocolMode::Report as u8;
622 }
544 Some(InResponse::Accepted(&buf[0..1])) 623 Some(InResponse::Accepted(&buf[0..1]))
545 } 624 }
546 _ => Some(InResponse::Rejected), 625 _ => Some(InResponse::Rejected),
diff --git a/embassy-usb/src/class/midi.rs b/embassy-usb/src/class/midi.rs
index 1d152ca44..d29172be1 100644
--- a/embassy-usb/src/class/midi.rs
+++ b/embassy-usb/src/class/midi.rs
@@ -1,7 +1,7 @@
1//! MIDI class implementation. 1//! MIDI class implementation.
2 2
3use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
4use crate::Builder; 3use crate::Builder;
4use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
5 5
6/// This should be used as `device_class` when building the `UsbDevice`. 6/// This should be used as `device_class` when building the `UsbDevice`.
7pub const USB_AUDIO_CLASS: u8 = 0x01; 7pub const USB_AUDIO_CLASS: u8 = 0x01;
diff --git a/embassy-usb/src/class/uac1/speaker.rs b/embassy-usb/src/class/uac1/speaker.rs
index 9565e2a25..46e0420d5 100644
--- a/embassy-usb/src/class/uac1/speaker.rs
+++ b/embassy-usb/src/class/uac1/speaker.rs
@@ -11,7 +11,7 @@
11//! The class provides volume and mute controls for each channel. 11//! The class provides volume and mute controls for each channel.
12 12
13use core::cell::{Cell, RefCell}; 13use core::cell::{Cell, RefCell};
14use core::future::{poll_fn, Future}; 14use core::future::{Future, poll_fn};
15use core::marker::PhantomData; 15use core::marker::PhantomData;
16use core::sync::atomic::{AtomicBool, AtomicU32, Ordering}; 16use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
17use core::task::Poll; 17use core::task::Poll;
@@ -22,7 +22,7 @@ use heapless::Vec;
22 22
23use super::class_codes::*; 23use super::class_codes::*;
24use super::terminal_type::TerminalType; 24use super::terminal_type::TerminalType;
25use super::{Channel, ChannelConfig, FeedbackRefresh, SampleWidth, MAX_AUDIO_CHANNEL_COUNT, MAX_AUDIO_CHANNEL_INDEX}; 25use super::{Channel, ChannelConfig, FeedbackRefresh, MAX_AUDIO_CHANNEL_COUNT, MAX_AUDIO_CHANNEL_INDEX, SampleWidth};
26use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; 26use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType};
27use crate::descriptor::{SynchronizationType, UsageType}; 27use crate::descriptor::{SynchronizationType, UsageType};
28use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut, EndpointType}; 28use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut, EndpointType};
diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs
index e9a6fd79a..c79dd02eb 100644
--- a/embassy-usb/src/descriptor.rs
+++ b/embassy-usb/src/descriptor.rs
@@ -1,10 +1,10 @@
1//! Utilities for writing USB descriptors. 1//! Utilities for writing USB descriptors.
2use embassy_usb_driver::EndpointType; 2use embassy_usb_driver::EndpointType;
3 3
4use crate::CONFIGURATION_VALUE;
4use crate::builder::Config; 5use crate::builder::Config;
5use crate::driver::EndpointInfo; 6use crate::driver::EndpointInfo;
6use crate::types::{InterfaceNumber, StringIndex}; 7use crate::types::{InterfaceNumber, StringIndex};
7use crate::CONFIGURATION_VALUE;
8 8
9/// Standard descriptor types 9/// Standard descriptor types
10#[allow(missing_docs)] 10#[allow(missing_docs)]
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs
index 0638fd0a2..0c10c08df 100644
--- a/embassy-usb/src/lib.rs
+++ b/embassy-usb/src/lib.rs
@@ -1,4 +1,5 @@
1#![no_std] 1#![no_std]
2#![allow(unsafe_op_in_unsafe_fn)]
2#![doc = include_str!("../README.md")] 3#![doc = include_str!("../README.md")]
3#![warn(missing_docs)] 4#![warn(missing_docs)]
4 5
@@ -20,7 +21,7 @@ mod config {
20 include!(concat!(env!("OUT_DIR"), "/config.rs")); 21 include!(concat!(env!("OUT_DIR"), "/config.rs"));
21} 22}
22 23
23use embassy_futures::select::{select, Either}; 24use embassy_futures::select::{Either, select};
24use heapless::Vec; 25use heapless::Vec;
25 26
26pub use crate::builder::{Builder, Config, FunctionBuilder, InterfaceAltBuilder, InterfaceBuilder, UsbVersion}; 27pub use crate::builder::{Builder, Config, FunctionBuilder, InterfaceAltBuilder, InterfaceBuilder, UsbVersion};
diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs
index 9f4e1a57b..66689871e 100644
--- a/embassy-usb/src/msos.rs
+++ b/embassy-usb/src/msos.rs
@@ -4,7 +4,7 @@
4 4
5use core::mem::size_of; 5use core::mem::size_of;
6 6
7use crate::descriptor::{capability_type, BosWriter}; 7use crate::descriptor::{BosWriter, capability_type};
8use crate::types::InterfaceNumber; 8use crate::types::InterfaceNumber;
9 9
10/// A serialized Microsoft OS 2.0 Descriptor set. 10/// A serialized Microsoft OS 2.0 Descriptor set.