aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-usb/Cargo.toml3
-rw-r--r--embassy-usb/src/builder.rs78
-rw-r--r--embassy-usb/src/lib.rs14
-rw-r--r--embassy-usb/src/msos.rs477
-rw-r--r--examples/nrf52840/Cargo.toml7
-rw-r--r--examples/nrf52840/src/bin/usb_serial_winusb.rs139
6 files changed, 453 insertions, 265 deletions
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml
index 31d1f4cae..54a8f27c7 100644
--- a/embassy-usb/Cargo.toml
+++ b/embassy-usb/Cargo.toml
@@ -13,6 +13,7 @@ target = "thumbv7em-none-eabi"
13[features] 13[features]
14defmt = ["dep:defmt", "embassy-usb-driver/defmt"] 14defmt = ["dep:defmt", "embassy-usb-driver/defmt"]
15usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"] 15usbd-hid = ["dep:usbd-hid", "dep:ssmarshal"]
16msos-descriptor = ["dep:widestring"]
16default = ["usbd-hid"] 17default = ["usbd-hid"]
17 18
18[dependencies] 19[dependencies]
@@ -24,7 +25,7 @@ embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-
24defmt = { version = "0.3", optional = true } 25defmt = { version = "0.3", optional = true }
25log = { version = "0.4.14", optional = true } 26log = { version = "0.4.14", optional = true }
26heapless = "0.7.10" 27heapless = "0.7.10"
27widestring = { version = "1.0.2", default-features = false } 28widestring = { version = "1.0.2", default-features = false, optional = true }
28 29
29# for HID 30# for HID
30usbd-hid = { version = "0.6.0", optional = true } 31usbd-hid = { version = "0.6.0", optional = true }
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs
index 2c42fd64e..d1cbf674b 100644
--- a/embassy-usb/src/builder.rs
+++ b/embassy-usb/src/builder.rs
@@ -3,6 +3,8 @@ use heapless::Vec;
3use crate::control::ControlHandler; 3use crate::control::ControlHandler;
4use crate::descriptor::{BosWriter, DescriptorWriter}; 4use crate::descriptor::{BosWriter, DescriptorWriter};
5use crate::driver::{Driver, Endpoint, EndpointType}; 5use crate::driver::{Driver, Endpoint, EndpointType};
6#[cfg(feature = "msos-descriptor")]
7use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter};
6use crate::types::*; 8use crate::types::*;
7use crate::{DeviceStateHandler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; 9use crate::{DeviceStateHandler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START};
8 10
@@ -130,7 +132,9 @@ pub struct Builder<'d, D: Driver<'d>> {
130 device_descriptor: DescriptorWriter<'d>, 132 device_descriptor: DescriptorWriter<'d>,
131 config_descriptor: DescriptorWriter<'d>, 133 config_descriptor: DescriptorWriter<'d>,
132 bos_descriptor: BosWriter<'d>, 134 bos_descriptor: BosWriter<'d>,
133 msos_descriptor: Option<crate::msos::MsOsDescriptorSet<'d>>, 135
136 #[cfg(feature = "msos-descriptor")]
137 msos_descriptor: MsOsDescriptorWriter<'d>,
134} 138}
135 139
136impl<'d, D: Driver<'d>> Builder<'d, D> { 140impl<'d, D: Driver<'d>> Builder<'d, D> {
@@ -145,6 +149,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
145 device_descriptor_buf: &'d mut [u8], 149 device_descriptor_buf: &'d mut [u8],
146 config_descriptor_buf: &'d mut [u8], 150 config_descriptor_buf: &'d mut [u8],
147 bos_descriptor_buf: &'d mut [u8], 151 bos_descriptor_buf: &'d mut [u8],
152 #[cfg(feature = "msos-descriptor")] msos_descriptor_buf: &'d mut [u8],
148 control_buf: &'d mut [u8], 153 control_buf: &'d mut [u8],
149 handler: Option<&'d dyn DeviceStateHandler>, 154 handler: Option<&'d dyn DeviceStateHandler>,
150 ) -> Self { 155 ) -> Self {
@@ -183,12 +188,17 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
183 device_descriptor, 188 device_descriptor,
184 config_descriptor, 189 config_descriptor,
185 bos_descriptor, 190 bos_descriptor,
186 msos_descriptor: None, 191
192 #[cfg(feature = "msos-descriptor")]
193 msos_descriptor: MsOsDescriptorWriter::new(msos_descriptor_buf),
187 } 194 }
188 } 195 }
189 196
190 /// Creates the [`UsbDevice`] instance with the configuration in this builder. 197 /// Creates the [`UsbDevice`] instance with the configuration in this builder.
191 pub fn build(mut self) -> UsbDevice<'d, D> { 198 pub fn build(mut self) -> UsbDevice<'d, D> {
199 #[cfg(feature = "msos-descriptor")]
200 let msos_descriptor = self.msos_descriptor.build(&mut self.bos_descriptor);
201
192 self.config_descriptor.end_configuration(); 202 self.config_descriptor.end_configuration();
193 self.bos_descriptor.end_bos(); 203 self.bos_descriptor.end_bos();
194 204
@@ -201,7 +211,8 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
201 self.bos_descriptor.writer.into_buf(), 211 self.bos_descriptor.writer.into_buf(),
202 self.interfaces, 212 self.interfaces,
203 self.control_buf, 213 self.control_buf,
204 self.msos_descriptor, 214 #[cfg(feature = "msos-descriptor")]
215 msos_descriptor,
205 ) 216 )
206 } 217 }
207 218
@@ -218,14 +229,10 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
218 /// 229 ///
219 /// If it's not set, no IAD descriptor is added. 230 /// If it's not set, no IAD descriptor is added.
220 pub fn function(&mut self, class: u8, subclass: u8, protocol: u8) -> FunctionBuilder<'_, 'd, D> { 231 pub fn function(&mut self, class: u8, subclass: u8, protocol: u8) -> FunctionBuilder<'_, 'd, D> {
232 let first_interface = InterfaceNumber::new(self.interfaces.len() as u8);
221 let iface_count_index = if self.config.composite_with_iads { 233 let iface_count_index = if self.config.composite_with_iads {
222 self.config_descriptor.iad( 234 self.config_descriptor
223 InterfaceNumber::new(self.interfaces.len() as _), 235 .iad(first_interface, 0, class, subclass, protocol);
224 0,
225 class,
226 subclass,
227 protocol,
228 );
229 236
230 Some(self.config_descriptor.position() - 5) 237 Some(self.config_descriptor.position() - 5)
231 } else { 238 } else {
@@ -235,19 +242,31 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
235 FunctionBuilder { 242 FunctionBuilder {
236 builder: self, 243 builder: self,
237 iface_count_index, 244 iface_count_index,
245
246 #[cfg(feature = "msos-descriptor")]
247 first_interface,
238 } 248 }
239 } 249 }
240 250
251 #[cfg(feature = "msos-descriptor")]
241 /// Add an MS OS 2.0 Descriptor Set. 252 /// Add an MS OS 2.0 Descriptor Set.
242 /// 253 ///
243 /// Panics if called more than once. 254 /// Panics if called more than once.
244 pub fn msos_descriptor(&mut self, msos_descriptor: crate::msos::MsOsDescriptorSet<'d>) { 255 pub fn msos_descriptor(&mut self, windows_version: u32, vendor_code: u8) {
245 if self.msos_descriptor.is_some() { 256 self.msos_descriptor.header(windows_version, vendor_code);
246 panic!("msos_descriptor already set"); 257 }
247 } 258
248 self.msos_descriptor 259 #[cfg(feature = "msos-descriptor")]
249 .insert(msos_descriptor) 260 /// Add an MS OS 2.0 Device Level Feature Descriptor.
250 .write_bos_capability(&mut self.bos_descriptor); 261 pub fn msos_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) {
262 self.msos_descriptor.device_feature(desc);
263 }
264
265 #[cfg(feature = "msos-descriptor")]
266 /// Gets the underlying [`MsOsDescriptorWriter`] to allow adding subsets and features for classes that
267 /// do not add their own.
268 pub fn msos_writer(&mut self) -> &mut MsOsDescriptorWriter<'d> {
269 &mut self.msos_descriptor
251 } 270 }
252} 271}
253 272
@@ -259,6 +278,16 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
259pub struct FunctionBuilder<'a, 'd, D: Driver<'d>> { 278pub struct FunctionBuilder<'a, 'd, D: Driver<'d>> {
260 builder: &'a mut Builder<'d, D>, 279 builder: &'a mut Builder<'d, D>,
261 iface_count_index: Option<usize>, 280 iface_count_index: Option<usize>,
281
282 #[cfg(feature = "msos-descriptor")]
283 first_interface: InterfaceNumber,
284}
285
286impl<'a, 'd, D: Driver<'d>> Drop for FunctionBuilder<'a, 'd, D> {
287 fn drop(&mut self) {
288 #[cfg(feature = "msos-descriptor")]
289 self.builder.msos_descriptor.end_function();
290 }
262} 291}
263 292
264impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { 293impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
@@ -288,6 +317,21 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
288 next_alt_setting_number: 0, 317 next_alt_setting_number: 0,
289 } 318 }
290 } 319 }
320
321 #[cfg(feature = "msos-descriptor")]
322 /// Add an MS OS 2.0 Function Level Feature Descriptor.
323 pub fn msos_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) {
324 if !self.builder.msos_descriptor.is_in_config_subset() {
325 self.builder.msos_descriptor.configuration(0);
326 }
327
328 if !self.builder.msos_descriptor.is_in_function_subset() {
329 self.builder.msos_descriptor.function(self.first_interface.0);
330 }
331
332 #[cfg(feature = "msos-descriptor")]
333 self.builder.msos_descriptor.function_feature(desc);
334 }
291} 335}
292 336
293/// Interface builder. 337/// Interface builder.
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs
index 948b8d523..aec18524b 100644
--- a/embassy-usb/src/lib.rs
+++ b/embassy-usb/src/lib.rs
@@ -136,8 +136,8 @@ struct Inner<'d, D: Driver<'d>> {
136 set_address_pending: bool, 136 set_address_pending: bool,
137 137
138 interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>, 138 interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>,
139 139 #[cfg(feature = "msos-descriptor")]
140 msos_descriptor: Option<crate::msos::MsOsDescriptorSet<'d>>, 140 msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
141} 141}
142 142
143impl<'d, D: Driver<'d>> UsbDevice<'d, D> { 143impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
@@ -150,7 +150,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
150 bos_descriptor: &'d [u8], 150 bos_descriptor: &'d [u8],
151 interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>, 151 interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>,
152 control_buf: &'d mut [u8], 152 control_buf: &'d mut [u8],
153 msos_descriptor: Option<crate::msos::MsOsDescriptorSet<'d>>, 153 #[cfg(feature = "msos-descriptor")] msos_descriptor: crate::msos::MsOsDescriptorSet<'d>,
154 ) -> UsbDevice<'d, D> { 154 ) -> UsbDevice<'d, D> {
155 // Start the USB bus. 155 // Start the USB bus.
156 // This prevent further allocation by consuming the driver. 156 // This prevent further allocation by consuming the driver.
@@ -174,6 +174,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
174 address: 0, 174 address: 0,
175 set_address_pending: false, 175 set_address_pending: false,
176 interfaces, 176 interfaces,
177 #[cfg(feature = "msos-descriptor")]
177 msos_descriptor, 178 msos_descriptor,
178 }, 179 },
179 } 180 }
@@ -608,11 +609,12 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
608 None => InResponse::Rejected, 609 None => InResponse::Rejected,
609 } 610 }
610 } 611 }
612 #[cfg(feature = "msos-descriptor")]
611 (RequestType::Vendor, Recipient::Device) => { 613 (RequestType::Vendor, Recipient::Device) => {
612 if let Some(msos) = &self.msos_descriptor { 614 if !self.msos_descriptor.is_empty() {
613 if req.request == msos.vendor_code() && req.index == 7 { 615 if req.request == self.msos_descriptor.vendor_code() && req.index == 7 {
614 // Index 7 retrieves the MS OS Descriptor Set 616 // Index 7 retrieves the MS OS Descriptor Set
615 InResponse::Accepted(msos.descriptor()) 617 InResponse::Accepted(self.msos_descriptor.descriptor())
616 } else { 618 } else {
617 InResponse::Rejected 619 InResponse::Rejected
618 } 620 }
diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs
index df8461258..360f80d91 100644
--- a/embassy-usb/src/msos.rs
+++ b/embassy-usb/src/msos.rs
@@ -1,3 +1,5 @@
1#![cfg(feature = "msos-descriptor")]
2
1//! Microsoft OS Descriptors 3//! Microsoft OS Descriptors
2//! 4//!
3//! <https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification> 5//! <https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification>
@@ -5,10 +7,9 @@
5use core::mem::size_of; 7use core::mem::size_of;
6use core::ops::Range; 8use core::ops::Range;
7 9
8pub use widestring::{u16cstr, U16CStr}; 10pub use widestring::{u16cstr, u16str, U16CStr, U16Str};
9 11
10use crate::descriptor::{capability_type, BosWriter}; 12use super::{capability_type, BosWriter};
11use crate::types::InterfaceNumber;
12 13
13fn write_u16<T: Into<u16>>(buf: &mut [u8], range: Range<usize>, data: T) { 14fn write_u16<T: Into<u16>>(buf: &mut [u8], range: Range<usize>, data: T) {
14 (&mut buf[range]).copy_from_slice(data.into().to_le_bytes().as_slice()) 15 (&mut buf[range]).copy_from_slice(data.into().to_le_bytes().as_slice())
@@ -17,13 +18,12 @@ fn write_u16<T: Into<u16>>(buf: &mut [u8], range: Range<usize>, data: T) {
17/// A serialized Microsoft OS 2.0 Descriptor set. 18/// A serialized Microsoft OS 2.0 Descriptor set.
18/// 19///
19/// Create with [`DeviceDescriptorSetBuilder`]. 20/// Create with [`DeviceDescriptorSetBuilder`].
20pub struct MsOsDescriptorSet<'a> { 21pub struct MsOsDescriptorSet<'d> {
21 descriptor: &'a [u8], 22 descriptor: &'d [u8],
22 windows_version: u32,
23 vendor_code: u8, 23 vendor_code: u8,
24} 24}
25 25
26impl<'a> MsOsDescriptorSet<'a> { 26impl<'d> MsOsDescriptorSet<'d> {
27 pub fn descriptor(&self) -> &[u8] { 27 pub fn descriptor(&self) -> &[u8] {
28 self.descriptor 28 self.descriptor
29 } 29 }
@@ -32,9 +32,150 @@ impl<'a> MsOsDescriptorSet<'a> {
32 self.vendor_code 32 self.vendor_code
33 } 33 }
34 34
35 pub fn write_bos_capability(&self, bos: &mut BosWriter) { 35 pub fn is_empty(&self) -> bool {
36 let windows_version = self.windows_version.to_le_bytes(); 36 self.descriptor.is_empty()
37 let len = self.descriptor.len().to_le_bytes(); 37 }
38}
39
40/// Writes a Microsoft OS 2.0 Descriptor set into a buffer.
41pub struct MsOsDescriptorWriter<'d> {
42 pub buf: &'d mut [u8],
43
44 position: usize,
45 config_mark: Option<usize>,
46 function_mark: Option<usize>,
47 vendor_code: u8,
48}
49
50impl<'d> MsOsDescriptorWriter<'d> {
51 pub(crate) fn new(buf: &'d mut [u8]) -> Self {
52 MsOsDescriptorWriter {
53 buf,
54 position: 0,
55 config_mark: None,
56 function_mark: None,
57 vendor_code: 0,
58 }
59 }
60
61 pub(crate) fn build(mut self, bos: &mut BosWriter) -> MsOsDescriptorSet<'d> {
62 self.end();
63
64 if self.is_empty() {
65 MsOsDescriptorSet {
66 descriptor: &[],
67 vendor_code: 0,
68 }
69 } else {
70 self.write_bos(bos);
71 MsOsDescriptorSet {
72 descriptor: &self.buf[..self.position],
73 vendor_code: self.vendor_code,
74 }
75 }
76 }
77
78 pub fn is_empty(&self) -> bool {
79 self.position == 0
80 }
81
82 pub fn is_in_config_subset(&self) -> bool {
83 self.config_mark.is_some()
84 }
85
86 pub fn is_in_function_subset(&self) -> bool {
87 self.function_mark.is_some()
88 }
89
90 /// Write the MS OS descriptor set header.
91 ///
92 /// - `windows_version` is an NTDDI version constant that describes a windows version. See the [`windows_version`]
93 /// module.
94 /// - `vendor_code` is the vendor request code used to read the MS OS descriptor set.
95 pub fn header(&mut self, windows_version: u32, vendor_code: u8) {
96 assert!(self.is_empty(), "You can only call MsOsDescriptorWriter::header once");
97 self.write(DescriptorSetHeader::new(windows_version));
98 self.vendor_code = vendor_code;
99 }
100
101 /// Add a device level feature descriptor.
102 ///
103 /// Note that some feature descriptors may only be used at the device level in non-composite devices.
104 /// Those features must be written before the first call to [`Self::configuration`].
105 pub fn device_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) {
106 assert!(
107 !self.is_empty(),
108 "device features may only be added after the header is written"
109 );
110 assert!(
111 self.config_mark.is_none(),
112 "device features must be added before the first configuration subset"
113 );
114 self.write(desc);
115 }
116
117 /// Add a configuration subset.
118 pub fn configuration(&mut self, config: u8) {
119 assert!(
120 !self.is_empty(),
121 "MsOsDescriptorWriter: configuration must be called after header"
122 );
123 Self::end_subset::<ConfigurationSubsetHeader>(self.buf, self.position, &mut self.config_mark);
124 self.config_mark = Some(self.position);
125 self.write(ConfigurationSubsetHeader::new(config));
126 }
127
128 /// Add a function subset.
129 pub fn function(&mut self, first_interface: u8) {
130 assert!(
131 self.config_mark.is_some(),
132 "MsOsDescriptorWriter: function subset requires a configuration subset"
133 );
134 self.end_function();
135 self.function_mark = Some(self.position);
136 self.write(FunctionSubsetHeader::new(first_interface));
137 }
138
139 /// Add a function level feature descriptor.
140 ///
141 /// Note that some features may only be used at the function level. Those features must be written after a call
142 /// to [`Self::function`].
143 pub fn function_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) {
144 assert!(
145 self.function_mark.is_some(),
146 "function features may only be added to a function subset"
147 );
148 self.write(desc);
149 }
150
151 pub fn end_function(&mut self) {
152 Self::end_subset::<FunctionSubsetHeader>(self.buf, self.position, &mut self.function_mark);
153 }
154
155 fn write<T: Descriptor>(&mut self, desc: T) {
156 desc.write_to(&mut self.buf[self.position..]);
157 self.position += desc.size();
158 }
159
160 fn end_subset<T: DescriptorSet>(buf: &mut [u8], position: usize, mark: &mut Option<usize>) {
161 if let Some(mark) = mark.take() {
162 let len = position - mark;
163 let p = mark + T::LENGTH_OFFSET;
164 buf[p..(p + 2)].copy_from_slice(&(len as u16).to_le_bytes());
165 }
166 }
167
168 fn end(&mut self) {
169 if self.position > 0 {
170 Self::end_subset::<FunctionSubsetHeader>(self.buf, self.position, &mut self.function_mark);
171 Self::end_subset::<ConfigurationSubsetHeader>(self.buf, self.position, &mut self.config_mark);
172 Self::end_subset::<DescriptorSetHeader>(self.buf, self.position, &mut Some(0));
173 }
174 }
175
176 fn write_bos(&mut self, bos: &mut BosWriter) {
177 let windows_version = &self.buf[4..8];
178 let len = (self.position as u16).to_le_bytes();
38 bos.capability( 179 bos.capability(
39 capability_type::PLATFORM, 180 capability_type::PLATFORM,
40 &[ 181 &[
@@ -67,30 +208,7 @@ impl<'a> MsOsDescriptorSet<'a> {
67 self.vendor_code, 208 self.vendor_code,
68 0x0, // Device does not support alternate enumeration 209 0x0, // Device does not support alternate enumeration
69 ], 210 ],
70 ) 211 );
71 }
72}
73
74/// A helper struct to implement the different descriptor set builders.
75struct DescriptorSetBuilder<'a> {
76 used: usize,
77 buf: &'a mut [u8],
78}
79
80impl<'a> DescriptorSetBuilder<'a> {
81 pub fn descriptor<T>(&mut self, desc: T)
82 where
83 T: Descriptor + 'a,
84 {
85 let size = desc.size();
86 let start = self.used;
87 let end = start + size;
88 desc.write_to(&mut self.buf[start..end]);
89 self.used += size;
90 }
91
92 pub fn remaining(&mut self) -> &mut [u8] {
93 &mut self.buf[self.used..]
94 } 212 }
95} 213}
96 214
@@ -120,182 +238,27 @@ pub mod windows_version {
120 pub const WIN10: u32 = 0x0A000000; 238 pub const WIN10: u32 = 0x0A000000;
121} 239}
122 240
123/// Helps build a Microsoft OS 2.0 Descriptor set. 241mod sealed {
124/// 242 use core::mem::size_of;
125/// # Example
126/// ```rust
127/// # use embassy_usb::types::InterfaceNumber;
128/// # use embassy_usb::msos::*;
129/// # let cdc_interface = unsafe { core::mem::transmute::<u8, InterfaceNumber>(0) };
130/// # let dfu_interface = unsafe { core::mem::transmute::<u8, InterfaceNumber>(1) };
131/// let mut buf = [0u8; 256];
132/// let mut builder = DeviceDescriptorSetBuilder::new(&mut buf[..], windows_version::WIN8_1);
133/// builder.feature(MinimumRecoveryTimeDescriptor::new(5, 10));
134/// builder.feature(ModelIdDescriptor::new(0xdeadbeef1234u128));
135/// builder.configuration(1, |conf| {
136/// conf.function(cdc_interface, |func| {
137/// func.winusb_device();
138/// func.feature(VendorRevisionDescriptor::new(1));
139/// });
140/// conf.function(dfu_interface, |func| {
141/// func.winusb_device();
142/// func.feature(VendorRevisionDescriptor::new(1));
143/// });
144/// });
145/// ```
146pub struct DeviceDescriptorSetBuilder<'a> {
147 builder: DescriptorSetBuilder<'a>,
148 windows_version: u32,
149 vendor_code: u8,
150}
151 243
152impl<'a> DeviceDescriptorSetBuilder<'a> { 244 /// A trait for descriptors
153 /// Create a device descriptor set builder. 245 pub trait Descriptor: Sized {
154 /// 246 const TYPE: super::DescriptorType;
155 /// - `windows_version` is an NTDDI version constant that describes a windows version. See the [`windows_version`]
156 /// module.
157 /// - `vendor_code` is the vendor request code used to read the MS OS descriptor set.
158 pub fn new<'b: 'a>(buf: &'b mut [u8], windows_version: u32, vendor_code: u8) -> Self {
159 let mut builder = DescriptorSetBuilder { used: 0, buf };
160 builder.descriptor(DescriptorSetHeader {
161 wLength: (size_of::<DescriptorSetHeader>() as u16).to_le(),
162 wDescriptorType: (DescriptorSetHeader::TYPE as u16).to_le(),
163 dwWindowsVersion: windows_version.to_le(),
164 wTotalLength: 0,
165 });
166 Self {
167 builder,
168 windows_version,
169 vendor_code,
170 }
171 }
172 247
173 /// Add a device-level feature descriptor. 248 /// The size of the descriptor's header.
174 /// 249 fn size(&self) -> usize {
175 /// Note that some feature descriptors may only be used at the device level in non-composite devices. 250 size_of::<Self>()
176 pub fn feature<T>(&mut self, desc: T)
177 where
178 T: Descriptor + DeviceLevelDescriptor + 'a,
179 {
180 self.builder.descriptor(desc)
181 }
182
183 /// Add a configuration subset.
184 pub fn configuration(&mut self, configuration: u8, build_conf: impl FnOnce(&mut ConfigurationSubsetBuilder<'_>)) {
185 let mut cb = ConfigurationSubsetBuilder::new(self.builder.remaining(), configuration);
186 build_conf(&mut cb);
187 self.builder.used += cb.finalize();
188 }
189
190 /// Finishes writing the data.
191 pub fn finalize(self) -> MsOsDescriptorSet<'a> {
192 let used = self.builder.used;
193 let buf = self.builder.buf;
194 // Update length in header with final length
195 let total_len = &mut buf[8..10];
196 total_len.copy_from_slice((used as u16).to_le_bytes().as_slice());
197
198 MsOsDescriptorSet {
199 descriptor: &buf[..used],
200 windows_version: self.windows_version,
201 vendor_code: self.vendor_code,
202 } 251 }
203 }
204}
205
206pub struct ConfigurationSubsetBuilder<'a> {
207 builder: DescriptorSetBuilder<'a>,
208}
209
210impl<'a> ConfigurationSubsetBuilder<'a> {
211 pub fn new<'b: 'a>(buf: &'b mut [u8], configuration: u8) -> Self {
212 let mut builder = DescriptorSetBuilder { used: 0, buf };
213 builder.descriptor(ConfigurationSubsetHeader {
214 wLength: (size_of::<ConfigurationSubsetHeader>() as u16).to_le(),
215 wDescriptorType: (ConfigurationSubsetHeader::TYPE as u16).to_le(),
216 bConfigurationValue: configuration,
217 bReserved: 0,
218 wTotalLength: 0,
219 });
220 Self { builder }
221 }
222
223 /// Add a function subset.
224 pub fn function(&mut self, interface: InterfaceNumber, build_func: impl FnOnce(&mut FunctionSubsetBuilder<'_>)) {
225 let mut fb = FunctionSubsetBuilder::new(self.builder.remaining(), interface);
226 build_func(&mut fb);
227 self.builder.used += fb.finalize();
228 }
229
230 /// Finishes writing the data. Returns the total number of bytes used by the descriptor set.
231 pub fn finalize(self) -> usize {
232 let used = self.builder.used;
233 let buf = self.builder.buf;
234 // Update length in header with final length
235 let total_len = &mut buf[6..8];
236 total_len.copy_from_slice((used as u16).to_le_bytes().as_slice());
237 used
238 }
239}
240
241pub struct FunctionSubsetBuilder<'a> {
242 builder: DescriptorSetBuilder<'a>,
243}
244
245impl<'a> FunctionSubsetBuilder<'a> {
246 pub fn new<'b: 'a>(buf: &'b mut [u8], interface: InterfaceNumber) -> Self {
247 let mut builder = DescriptorSetBuilder { used: 0, buf };
248 builder.descriptor(FunctionSubsetHeader {
249 wLength: (size_of::<FunctionSubsetHeader>() as u16).to_le(),
250 wDescriptorType: (FunctionSubsetHeader::TYPE as u16).to_le(),
251 bFirstInterface: interface.0,
252 bReserved: 0,
253 wSubsetLength: 0,
254 });
255 Self { builder }
256 }
257 252
258 /// Add a function-level descriptor. 253 fn write_to(&self, buf: &mut [u8]);
259 ///
260 /// Note that many descriptors can only be used at function-level in a composite device.
261 pub fn feature<T>(&mut self, desc: T)
262 where
263 T: Descriptor + FunctionLevelDescriptor + 'a,
264 {
265 self.builder.descriptor(desc)
266 } 254 }
267 255
268 /// Adds the feature descriptors to configure this function to use the WinUSB driver. 256 pub trait DescriptorSet: Descriptor {
269 /// 257 const LENGTH_OFFSET: usize;
270 /// Adds a compatible id descriptor "WINUSB" and a registry descriptor that sets the DeviceInterfaceGUID to the
271 /// USB_DEVICE GUID.
272 pub fn winusb_device(&mut self) {
273 self.feature(CompatibleIdFeatureDescriptor::new_winusb());
274 self.feature(RegistryPropertyFeatureDescriptor::new_usb_deviceinterfaceguid());
275 }
276
277 /// Finishes writing the data. Returns the total number of bytes used by the descriptor set.
278 pub fn finalize(self) -> usize {
279 let used = self.builder.used;
280 let buf = self.builder.buf;
281 // Update length in header with final length
282 let total_len = &mut buf[6..8];
283 total_len.copy_from_slice((used as u16).to_le_bytes().as_slice());
284 used
285 } 258 }
286} 259}
287 260
288/// A trait for descriptors 261use sealed::*;
289pub trait Descriptor: Sized {
290 const TYPE: DescriptorType;
291
292 /// The size of the descriptor's header.
293 fn size(&self) -> usize {
294 size_of::<Self>()
295 }
296
297 fn write_to(&self, buf: &mut [u8]);
298}
299 262
300/// Copies the data of `t` into `buf`. 263/// Copies the data of `t` into `buf`.
301/// 264///
@@ -303,7 +266,7 @@ pub trait Descriptor: Sized {
303/// The type `T` must be able to be safely cast to `&[u8]`. (e.g. it is a `#[repr(packed)]` struct) 266/// The type `T` must be able to be safely cast to `&[u8]`. (e.g. it is a `#[repr(packed)]` struct)
304unsafe fn transmute_write_to<T: Sized>(t: &T, buf: &mut [u8]) { 267unsafe fn transmute_write_to<T: Sized>(t: &T, buf: &mut [u8]) {
305 let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::<T>()); 268 let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::<T>());
306 assert!(buf.len() >= bytes.len()); 269 assert!(buf.len() >= bytes.len(), "MSOS descriptor buffer full");
307 (&mut buf[..bytes.len()]).copy_from_slice(bytes); 270 (&mut buf[..bytes.len()]).copy_from_slice(bytes);
308} 271}
309 272
@@ -354,6 +317,17 @@ pub struct DescriptorSetHeader {
354 wTotalLength: u16, 317 wTotalLength: u16,
355} 318}
356 319
320impl DescriptorSetHeader {
321 pub fn new(windows_version: u32) -> Self {
322 DescriptorSetHeader {
323 wLength: (size_of::<Self>() as u16).to_le(),
324 wDescriptorType: (Self::TYPE as u16).to_le(),
325 dwWindowsVersion: windows_version.to_le(),
326 wTotalLength: 0,
327 }
328 }
329}
330
357impl Descriptor for DescriptorSetHeader { 331impl Descriptor for DescriptorSetHeader {
358 const TYPE: DescriptorType = DescriptorType::SetHeaderDescriptor; 332 const TYPE: DescriptorType = DescriptorType::SetHeaderDescriptor;
359 fn write_to(&self, buf: &mut [u8]) { 333 fn write_to(&self, buf: &mut [u8]) {
@@ -361,6 +335,10 @@ impl Descriptor for DescriptorSetHeader {
361 } 335 }
362} 336}
363 337
338impl DescriptorSet for DescriptorSetHeader {
339 const LENGTH_OFFSET: usize = 8;
340}
341
364/// Table 11. Configuration subset header. 342/// Table 11. Configuration subset header.
365#[allow(non_snake_case)] 343#[allow(non_snake_case)]
366#[repr(C, packed(1))] 344#[repr(C, packed(1))]
@@ -372,6 +350,18 @@ pub struct ConfigurationSubsetHeader {
372 wTotalLength: u16, 350 wTotalLength: u16,
373} 351}
374 352
353impl ConfigurationSubsetHeader {
354 pub fn new(config: u8) -> Self {
355 ConfigurationSubsetHeader {
356 wLength: (size_of::<Self>() as u16).to_le(),
357 wDescriptorType: (Self::TYPE as u16).to_le(),
358 bConfigurationValue: config,
359 bReserved: 0,
360 wTotalLength: 0,
361 }
362 }
363}
364
375impl Descriptor for ConfigurationSubsetHeader { 365impl Descriptor for ConfigurationSubsetHeader {
376 const TYPE: DescriptorType = DescriptorType::SubsetHeaderConfiguration; 366 const TYPE: DescriptorType = DescriptorType::SubsetHeaderConfiguration;
377 fn write_to(&self, buf: &mut [u8]) { 367 fn write_to(&self, buf: &mut [u8]) {
@@ -379,6 +369,10 @@ impl Descriptor for ConfigurationSubsetHeader {
379 } 369 }
380} 370}
381 371
372impl DescriptorSet for ConfigurationSubsetHeader {
373 const LENGTH_OFFSET: usize = 6;
374}
375
382/// Table 12. Function subset header. 376/// Table 12. Function subset header.
383#[allow(non_snake_case)] 377#[allow(non_snake_case)]
384#[repr(C, packed(1))] 378#[repr(C, packed(1))]
@@ -390,6 +384,18 @@ pub struct FunctionSubsetHeader {
390 wSubsetLength: u16, 384 wSubsetLength: u16,
391} 385}
392 386
387impl FunctionSubsetHeader {
388 pub fn new(first_interface: u8) -> Self {
389 FunctionSubsetHeader {
390 wLength: (size_of::<Self>() as u16).to_le(),
391 wDescriptorType: (Self::TYPE as u16).to_le(),
392 bFirstInterface: first_interface,
393 bReserved: 0,
394 wSubsetLength: 0,
395 }
396 }
397}
398
393impl Descriptor for FunctionSubsetHeader { 399impl Descriptor for FunctionSubsetHeader {
394 const TYPE: DescriptorType = DescriptorType::SubsetHeaderFunction; 400 const TYPE: DescriptorType = DescriptorType::SubsetHeaderFunction;
395 fn write_to(&self, buf: &mut [u8]) { 401 fn write_to(&self, buf: &mut [u8]) {
@@ -397,16 +403,17 @@ impl Descriptor for FunctionSubsetHeader {
397 } 403 }
398} 404}
399 405
406impl DescriptorSet for FunctionSubsetHeader {
407 const LENGTH_OFFSET: usize = 6;
408}
409
400// Feature Descriptors 410// Feature Descriptors
401 411
402/// A marker trait for feature descriptors that are valid at the device level. 412/// A marker trait for feature descriptors that are valid at the device level.
403pub trait DeviceLevelDescriptor {} 413pub trait DeviceLevelDescriptor: Descriptor {}
404 414
405/// A marker trait for feature descriptors that are valid at the function level. 415/// A marker trait for feature descriptors that are valid at the function level.
406pub trait FunctionLevelDescriptor { 416pub trait FunctionLevelDescriptor: Descriptor {}
407 /// `true` when the feature descriptor may only be used at the function level in composite devices.
408 const COMPOSITE_ONLY: bool = false;
409}
410 417
411/// Table 13. Microsoft OS 2.0 compatible ID descriptor. 418/// Table 13. Microsoft OS 2.0 compatible ID descriptor.
412#[allow(non_snake_case)] 419#[allow(non_snake_case)]
@@ -419,9 +426,7 @@ pub struct CompatibleIdFeatureDescriptor {
419} 426}
420 427
421impl DeviceLevelDescriptor for CompatibleIdFeatureDescriptor {} 428impl DeviceLevelDescriptor for CompatibleIdFeatureDescriptor {}
422impl FunctionLevelDescriptor for CompatibleIdFeatureDescriptor { 429impl FunctionLevelDescriptor for CompatibleIdFeatureDescriptor {}
423 const COMPOSITE_ONLY: bool = true;
424}
425 430
426impl Descriptor for CompatibleIdFeatureDescriptor { 431impl Descriptor for CompatibleIdFeatureDescriptor {
427 const TYPE: DescriptorType = DescriptorType::FeatureCompatibleId; 432 const TYPE: DescriptorType = DescriptorType::FeatureCompatibleId;
@@ -462,16 +467,12 @@ pub struct RegistryPropertyFeatureDescriptor<'a> {
462 wLength: u16, 467 wLength: u16,
463 wDescriptorType: u16, 468 wDescriptorType: u16,
464 wPropertyDataType: u16, 469 wPropertyDataType: u16,
465 wPropertyNameLength: u16,
466 PropertyName: &'a [u8], 470 PropertyName: &'a [u8],
467 wPropertyDataLength: u16,
468 PropertyData: &'a [u8], 471 PropertyData: &'a [u8],
469} 472}
470 473
471impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} 474impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {}
472impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> { 475impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {}
473 const COMPOSITE_ONLY: bool = true;
474}
475 476
476impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> { 477impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> {
477 const TYPE: DescriptorType = DescriptorType::FeatureRegProperty; 478 const TYPE: DescriptorType = DescriptorType::FeatureRegProperty;
@@ -479,34 +480,44 @@ impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> {
479 10 + self.PropertyName.len() + self.PropertyData.len() 480 10 + self.PropertyName.len() + self.PropertyData.len()
480 } 481 }
481 fn write_to(&self, buf: &mut [u8]) { 482 fn write_to(&self, buf: &mut [u8]) {
482 assert!(buf.len() >= self.size()); 483 assert!(buf.len() >= self.size(), "MSOS descriptor buffer full");
483 assert!(self.wPropertyNameLength as usize == self.PropertyName.len());
484 assert!(self.wPropertyDataLength as usize == self.PropertyData.len());
485 write_u16(buf, 0..2, self.wLength); 484 write_u16(buf, 0..2, self.wLength);
486 write_u16(buf, 2..4, self.wDescriptorType); 485 write_u16(buf, 2..4, self.wDescriptorType);
487 write_u16(buf, 4..6, self.wPropertyDataType); 486 write_u16(buf, 4..6, self.wPropertyDataType);
488 write_u16(buf, 6..8, self.wPropertyNameLength); 487 write_u16(buf, 6..8, (self.PropertyName.len() as u16).to_le());
489 let pne = 8 + self.PropertyName.len(); 488 let pne = 8 + self.PropertyName.len();
490 (&mut buf[8..pne]).copy_from_slice(self.PropertyName); 489 (&mut buf[8..pne]).copy_from_slice(self.PropertyName);
491 let pds = pne + 2; 490 let pds = pne + 2;
492 let pde = pds + self.PropertyData.len(); 491 let pde = pds + self.PropertyData.len();
493 write_u16(buf, pne..pds, self.wPropertyDataLength); 492 write_u16(buf, pne..pds, (self.PropertyData.len() as u16).to_le());
494 (&mut buf[pds..pde]).copy_from_slice(self.PropertyData); 493 (&mut buf[pds..pde]).copy_from_slice(self.PropertyData);
495 } 494 }
496} 495}
497 496
498impl<'a> RegistryPropertyFeatureDescriptor<'a> { 497impl<'a> RegistryPropertyFeatureDescriptor<'a> {
498 pub const DEVICE_INTERFACE_GUIDS_NAME: &U16CStr = {
499 // Can't use defmt::panic in constant expressions (inside u16cstr!)
500 macro_rules! panic {
501 ($($x:tt)*) => {
502 {
503 ::core::panic!($($x)*);
504 }
505 };
506 }
507
508 u16cstr!("DeviceInterfaceGUIDs")
509 };
510
499 /// A registry property. 511 /// A registry property.
500 /// 512 ///
501 /// `name` should be a NUL-terminated 16-bit Unicode string. 513 /// `name` should be a NUL-terminated 16-bit Unicode string.
502 pub fn new_raw<'n: 'a, 'd: 'a>(name: &'a [u8], data: &'d [u8], data_type: PropertyDataType) -> Self { 514 pub fn new_raw<'n: 'a, 'd: 'a>(name: &'a [u8], data: &'d [u8], data_type: PropertyDataType) -> Self {
515 assert!(name.len() < usize::from(u16::MAX) && data.len() < usize::from(u16::MAX));
503 Self { 516 Self {
504 wLength: ((10 + name.len() + data.len()) as u16).to_le(), 517 wLength: ((10 + name.len() + data.len()) as u16).to_le(),
505 wDescriptorType: (Self::TYPE as u16).to_le(), 518 wDescriptorType: (Self::TYPE as u16).to_le(),
506 wPropertyDataType: (data_type as u16).to_le(), 519 wPropertyDataType: (data_type as u16).to_le(),
507 wPropertyNameLength: (name.len() as u16).to_le(),
508 PropertyName: name, 520 PropertyName: name,
509 wPropertyDataLength: (data.len() as u16).to_le(),
510 PropertyData: data, 521 PropertyData: data,
511 } 522 }
512 } 523 }
@@ -515,24 +526,6 @@ impl<'a> RegistryPropertyFeatureDescriptor<'a> {
515 unsafe { core::slice::from_raw_parts(s.as_ptr() as *const u8, (s.len() + 1) * 2) } 526 unsafe { core::slice::from_raw_parts(s.as_ptr() as *const u8, (s.len() + 1) * 2) }
516 } 527 }
517 528
518 /// A registry property that sets the DeviceInterfaceGUIDs to the device interface class for USB devices which are
519 /// attached to a USB hub.
520 pub fn new_usb_deviceinterfaceguid() -> Self {
521 // Can't use defmt::panic in constant expressions (inside u16cstr!)
522 macro_rules! panic {
523 ($($x:tt)*) => {
524 {
525 ::core::panic!($($x)*);
526 }
527 };
528 }
529
530 Self::new_multi_string(
531 u16cstr!("DeviceInterfaceGUIDs"),
532 u16cstr!("{A5DCBF10-6530-11D2-901F-00C04FB951ED}").as_slice_with_nul(),
533 )
534 }
535
536 /// A registry property containing a NUL-terminated 16-bit Unicode string. 529 /// A registry property containing a NUL-terminated 16-bit Unicode string.
537 pub fn new_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { 530 pub fn new_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self {
538 Self::new_raw(Self::u16str_bytes(name), Self::u16str_bytes(data), PropertyDataType::Sz) 531 Self::new_raw(Self::u16str_bytes(name), Self::u16str_bytes(data), PropertyDataType::Sz)
@@ -558,6 +551,10 @@ impl<'a> RegistryPropertyFeatureDescriptor<'a> {
558 551
559 /// A registry property containing multiple NUL-terminated 16-bit Unicode strings. 552 /// A registry property containing multiple NUL-terminated 16-bit Unicode strings.
560 pub fn new_multi_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u16]) -> Self { 553 pub fn new_multi_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u16]) -> Self {
554 assert!(
555 data.len() >= 2 && data[data.len() - 1] == 0 && data[data.len() - 2] == 0,
556 "multi-strings must end in double nul terminators"
557 );
561 Self::new_raw( 558 Self::new_raw(
562 Self::u16str_bytes(name), 559 Self::u16str_bytes(name),
563 unsafe { core::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 2) }, 560 unsafe { core::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 2) },
diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml
index 95d939873..cfdda076e 100644
--- a/examples/nrf52840/Cargo.toml
+++ b/examples/nrf52840/Cargo.toml
@@ -6,6 +6,7 @@ license = "MIT OR Apache-2.0"
6 6
7[features] 7[features]
8default = ["nightly"] 8default = ["nightly"]
9msos-descriptor = ["embassy-usb/msos-descriptor"]
9nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net", 10nightly = ["embassy-executor/nightly", "embassy-nrf/nightly", "embassy-net/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embedded-io/async", "embassy-net",
10 "embassy-lora", "lorawan-device", "lorawan"] 11 "embassy-lora", "lorawan-device", "lorawan"]
11 12
@@ -34,4 +35,8 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa
34rand = { version = "0.8.4", default-features = false } 35rand = { version = "0.8.4", default-features = false }
35embedded-storage = "0.3.0" 36embedded-storage = "0.3.0"
36usbd-hid = "0.6.0" 37usbd-hid = "0.6.0"
37serde = { version = "1.0.136", default-features = false } \ No newline at end of file 38serde = { version = "1.0.136", default-features = false }
39
40[[bin]]
41name = "usb_serial_winusb"
42required-features = ["msos-descriptor"]
diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs
new file mode 100644
index 000000000..443379a07
--- /dev/null
+++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs
@@ -0,0 +1,139 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use core::mem;
6
7use defmt::{info, panic};
8use embassy_executor::Spawner;
9use embassy_futures::join::join;
10use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply};
11use embassy_nrf::{interrupt, pac};
12use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
13use embassy_usb::driver::EndpointError;
14use embassy_usb::msos::{self, windows_version};
15use embassy_usb::{Builder, Config};
16use {defmt_rtt as _, panic_probe as _};
17
18const DEVICE_INTERFACE_GUIDS: &[u16] = {
19 // Can't use defmt::panic in constant expressions (inside u16str!)
20 macro_rules! panic {
21 ($($x:tt)*) => {
22 {
23 ::core::panic!($($x)*);
24 }
25 };
26 }
27 msos::u16str!("{EAA9A5DC-30BA-44BC-9232-606CDC875321}\0\0").as_slice()
28};
29
30#[embassy_executor::main]
31async fn main(_spawner: Spawner) {
32 let p = embassy_nrf::init(Default::default());
33 let clock: pac::CLOCK = unsafe { mem::transmute(()) };
34
35 info!("Enabling ext hfosc...");
36 clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) });
37 while clock.events_hfclkstarted.read().bits() != 1 {}
38
39 // Create the driver, from the HAL.
40 let irq = interrupt::take!(USBD);
41 let power_irq = interrupt::take!(POWER_CLOCK);
42 let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq));
43
44 // Create embassy-usb Config
45 let mut config = Config::new(0xc0de, 0xcafe);
46 config.manufacturer = Some("Embassy");
47 config.product = Some("USB-serial example");
48 config.serial_number = Some("12345678");
49 config.max_power = 100;
50 config.max_packet_size_0 = 64;
51
52 // Required for windows compatiblity.
53 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
54 config.device_class = 0xEF;
55 config.device_sub_class = 0x02;
56 config.device_protocol = 0x01;
57 config.composite_with_iads = true;
58
59 // Create embassy-usb DeviceBuilder using the driver and config.
60 // It needs some buffers for building the descriptors.
61 let mut device_descriptor = [0; 256];
62 let mut config_descriptor = [0; 256];
63 let mut bos_descriptor = [0; 256];
64 let mut msos_descriptor = [0; 256];
65 let mut control_buf = [0; 64];
66
67 let mut state = State::new();
68
69 let mut builder = Builder::new(
70 driver,
71 config,
72 &mut device_descriptor,
73 &mut config_descriptor,
74 &mut bos_descriptor,
75 &mut msos_descriptor,
76 &mut control_buf,
77 None,
78 );
79
80 builder.msos_descriptor(windows_version::WIN8_1, 2);
81
82 // Create classes on the builder.
83 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
84
85 // Since we want to create MS OS feature descriptors that apply to a function that has already been added to the
86 // builder, need to get the MsOsDescriptorWriter from the builder and manually add those descriptors.
87 // Inside a class constructor, you would just need to call `FunctionBuilder::msos_feature` instead.
88 let msos_writer = builder.msos_writer();
89 msos_writer.configuration(0);
90 msos_writer.function(0);
91 msos_writer.function_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", ""));
92 msos_writer.function_feature(msos::RegistryPropertyFeatureDescriptor::new_multi_string(
93 msos::RegistryPropertyFeatureDescriptor::DEVICE_INTERFACE_GUIDS_NAME,
94 DEVICE_INTERFACE_GUIDS,
95 ));
96
97 // Build the builder.
98 let mut usb = builder.build();
99
100 // Run the USB device.
101 let usb_fut = usb.run();
102
103 // Do stuff with the class!
104 let echo_fut = async {
105 loop {
106 class.wait_connection().await;
107 info!("Connected");
108 let _ = echo(&mut class).await;
109 info!("Disconnected");
110 }
111 };
112
113 // Run everything concurrently.
114 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
115 join(usb_fut, echo_fut).await;
116}
117
118struct Disconnected {}
119
120impl From<EndpointError> for Disconnected {
121 fn from(val: EndpointError) -> Self {
122 match val {
123 EndpointError::BufferOverflow => panic!("Buffer overflow"),
124 EndpointError::Disabled => Disconnected {},
125 }
126 }
127}
128
129async fn echo<'d, T: Instance + 'd, P: UsbSupply + 'd>(
130 class: &mut CdcAcmClass<'d, Driver<'d, T, P>>,
131) -> Result<(), Disconnected> {
132 let mut buf = [0; 64];
133 loop {
134 let n = class.read_packet(&mut buf).await?;
135 let data = &buf[..n];
136 info!("data: {:x}", data);
137 class.write_packet(data).await?;
138 }
139}