aboutsummaryrefslogtreecommitdiff
path: root/embassy-usb
diff options
context:
space:
mode:
authorMatt Ickstadt <[email protected]>2023-01-12 14:59:25 -0600
committeralexmoon <[email protected]>2023-02-07 14:24:35 -0500
commitf5ff3c4ac31c79cedf077f559dbd5685886399cc (patch)
tree6f8b76bd443453fde4e01a8f366e45cfb9edb9da /embassy-usb
parenta7fa7d0de2fa7b8fab889879b6003df8427c6841 (diff)
usb: add support for MS OS Descriptors
Diffstat (limited to 'embassy-usb')
-rw-r--r--embassy-usb/Cargo.toml1
-rw-r--r--embassy-usb/src/builder.rs15
-rw-r--r--embassy-usb/src/lib.rs17
-rw-r--r--embassy-usb/src/msos.rs746
4 files changed, 779 insertions, 0 deletions
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml
index 1e567bb94..31d1f4cae 100644
--- a/embassy-usb/Cargo.toml
+++ b/embassy-usb/Cargo.toml
@@ -24,6 +24,7 @@ embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-
24defmt = { version = "0.3", optional = true } 24defmt = { version = "0.3", optional = true }
25log = { version = "0.4.14", optional = true } 25log = { version = "0.4.14", optional = true }
26heapless = "0.7.10" 26heapless = "0.7.10"
27widestring = { version = "1.0.2", default-features = false }
27 28
28# for HID 29# for HID
29usbd-hid = { version = "0.6.0", optional = true } 30usbd-hid = { version = "0.6.0", optional = true }
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs
index 41b24fecf..2c42fd64e 100644
--- a/embassy-usb/src/builder.rs
+++ b/embassy-usb/src/builder.rs
@@ -130,6 +130,7 @@ pub struct Builder<'d, D: Driver<'d>> {
130 device_descriptor: DescriptorWriter<'d>, 130 device_descriptor: DescriptorWriter<'d>,
131 config_descriptor: DescriptorWriter<'d>, 131 config_descriptor: DescriptorWriter<'d>,
132 bos_descriptor: BosWriter<'d>, 132 bos_descriptor: BosWriter<'d>,
133 msos_descriptor: Option<crate::msos::MsOsDescriptorSet<'d>>,
133} 134}
134 135
135impl<'d, D: Driver<'d>> Builder<'d, D> { 136impl<'d, D: Driver<'d>> Builder<'d, D> {
@@ -182,6 +183,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
182 device_descriptor, 183 device_descriptor,
183 config_descriptor, 184 config_descriptor,
184 bos_descriptor, 185 bos_descriptor,
186 msos_descriptor: None,
185 } 187 }
186 } 188 }
187 189
@@ -199,6 +201,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
199 self.bos_descriptor.writer.into_buf(), 201 self.bos_descriptor.writer.into_buf(),
200 self.interfaces, 202 self.interfaces,
201 self.control_buf, 203 self.control_buf,
204 self.msos_descriptor,
202 ) 205 )
203 } 206 }
204 207
@@ -234,6 +237,18 @@ impl<'d, D: Driver<'d>> Builder<'d, D> {
234 iface_count_index, 237 iface_count_index,
235 } 238 }
236 } 239 }
240
241 /// Add an MS OS 2.0 Descriptor Set.
242 ///
243 /// Panics if called more than once.
244 pub fn msos_descriptor(&mut self, msos_descriptor: crate::msos::MsOsDescriptorSet<'d>) {
245 if self.msos_descriptor.is_some() {
246 panic!("msos_descriptor already set");
247 }
248 self.msos_descriptor
249 .insert(msos_descriptor)
250 .write_bos_capability(&mut self.bos_descriptor);
251 }
237} 252}
238 253
239/// Function builder. 254/// Function builder.
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs
index 2656af29d..948b8d523 100644
--- a/embassy-usb/src/lib.rs
+++ b/embassy-usb/src/lib.rs
@@ -13,6 +13,7 @@ pub mod class;
13pub mod control; 13pub mod control;
14pub mod descriptor; 14pub mod descriptor;
15mod descriptor_reader; 15mod descriptor_reader;
16pub mod msos;
16pub mod types; 17pub mod types;
17 18
18use embassy_futures::select::{select, Either}; 19use embassy_futures::select::{select, Either};
@@ -135,6 +136,8 @@ struct Inner<'d, D: Driver<'d>> {
135 set_address_pending: bool, 136 set_address_pending: bool,
136 137
137 interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>, 138 interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>,
139
140 msos_descriptor: Option<crate::msos::MsOsDescriptorSet<'d>>,
138} 141}
139 142
140impl<'d, D: Driver<'d>> UsbDevice<'d, D> { 143impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
@@ -147,6 +150,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
147 bos_descriptor: &'d [u8], 150 bos_descriptor: &'d [u8],
148 interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>, 151 interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>,
149 control_buf: &'d mut [u8], 152 control_buf: &'d mut [u8],
153 msos_descriptor: Option<crate::msos::MsOsDescriptorSet<'d>>,
150 ) -> UsbDevice<'d, D> { 154 ) -> UsbDevice<'d, D> {
151 // Start the USB bus. 155 // Start the USB bus.
152 // This prevent further allocation by consuming the driver. 156 // This prevent further allocation by consuming the driver.
@@ -170,6 +174,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
170 address: 0, 174 address: 0,
171 set_address_pending: false, 175 set_address_pending: false,
172 interfaces, 176 interfaces,
177 msos_descriptor,
173 }, 178 },
174 } 179 }
175 } 180 }
@@ -603,6 +608,18 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
603 None => InResponse::Rejected, 608 None => InResponse::Rejected,
604 } 609 }
605 } 610 }
611 (RequestType::Vendor, Recipient::Device) => {
612 if let Some(msos) = &self.msos_descriptor {
613 if req.request == msos.vendor_code() && req.index == 7 {
614 // Index 7 retrieves the MS OS Descriptor Set
615 InResponse::Accepted(msos.descriptor())
616 } else {
617 InResponse::Rejected
618 }
619 } else {
620 InResponse::Rejected
621 }
622 }
606 _ => InResponse::Rejected, 623 _ => InResponse::Rejected,
607 } 624 }
608 } 625 }
diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs
new file mode 100644
index 000000000..08a5074bf
--- /dev/null
+++ b/embassy-usb/src/msos.rs
@@ -0,0 +1,746 @@
1//! Microsoft OS Descriptors
2//!
3//! <https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification>
4
5#![allow(dead_code)]
6
7use core::mem::size_of;
8use core::ops::Range;
9
10pub use widestring::{u16cstr, U16CStr};
11
12use crate::descriptor::{capability_type, BosWriter};
13use crate::types::InterfaceNumber;
14
15fn write_u16<T: Into<u16>>(buf: &mut [u8], range: Range<usize>, data: T) {
16 (&mut buf[range]).copy_from_slice(data.into().to_le_bytes().as_slice())
17}
18
19/// A serialized Microsoft OS 2.0 Descriptor set.
20///
21/// Create with [`DeviceDescriptorSetBuilder`].
22pub struct MsOsDescriptorSet<'a> {
23 descriptor: &'a [u8],
24 windows_version: u32,
25 vendor_code: u8,
26}
27
28impl<'a> MsOsDescriptorSet<'a> {
29 pub fn descriptor(&self) -> &[u8] {
30 self.descriptor
31 }
32
33 pub fn vendor_code(&self) -> u8 {
34 self.vendor_code
35 }
36
37 pub fn write_bos_capability(&self, bos: &mut BosWriter) {
38 let windows_version = self.windows_version.to_le_bytes();
39 let len = self.descriptor.len().to_le_bytes();
40 bos.capability(
41 capability_type::PLATFORM,
42 &[
43 0, // reserved
44 // platform capability UUID, Microsoft OS 2.0 platform compabitility
45 0xdf,
46 0x60,
47 0xdd,
48 0xd8,
49 0x89,
50 0x45,
51 0xc7,
52 0x4c,
53 0x9c,
54 0xd2,
55 0x65,
56 0x9d,
57 0x9e,
58 0x64,
59 0x8a,
60 0x9f,
61 // Minimum compatible Windows version
62 windows_version[0],
63 windows_version[1],
64 windows_version[2],
65 windows_version[3],
66 // Descriptor set length
67 len[0],
68 len[1],
69 self.vendor_code,
70 0x0, // Device does not support alternate enumeration
71 ],
72 )
73 }
74}
75
76/// A helper struct to implement the different descriptor set builders.
77struct DescriptorSetBuilder<'a> {
78 used: usize,
79 buf: &'a mut [u8],
80}
81
82impl<'a> DescriptorSetBuilder<'a> {
83 pub fn descriptor<T>(&mut self, desc: T)
84 where
85 T: Descriptor + 'a,
86 {
87 let size = desc.size();
88 let start = self.used;
89 let end = start + size;
90 desc.write_to(&mut self.buf[start..end]);
91 self.used += size;
92 }
93
94 pub fn subset(&mut self, build_subset: impl FnOnce(&mut DescriptorSetBuilder<'_>)) {
95 self.used += {
96 let mut subset = DescriptorSetBuilder {
97 used: 0,
98 buf: self.remaining(),
99 };
100 build_subset(&mut subset);
101 subset.used
102 };
103 }
104
105 pub fn remaining(&mut self) -> &mut [u8] {
106 &mut self.buf[self.used..]
107 }
108}
109
110pub mod windows_version {
111 pub const WIN2K: u32 = 0x05000000;
112 pub const WIN2KSP1: u32 = 0x05000100;
113 pub const WIN2KSP2: u32 = 0x05000200;
114 pub const WIN2KSP3: u32 = 0x05000300;
115 pub const WIN2KSP4: u32 = 0x05000400;
116
117 pub const WINXP: u32 = 0x05010000;
118 pub const WINXPSP1: u32 = 0x05010100;
119 pub const WINXPSP2: u32 = 0x05010200;
120 pub const WINXPSP3: u32 = 0x05010300;
121 pub const WINXPSP4: u32 = 0x05010400;
122
123 pub const VISTA: u32 = 0x06000000;
124 pub const VISTASP1: u32 = 0x06000100;
125 pub const VISTASP2: u32 = 0x06000200;
126 pub const VISTASP3: u32 = 0x06000300;
127 pub const VISTASP4: u32 = 0x06000400;
128
129 pub const WIN7: u32 = 0x06010000;
130 pub const WIN8: u32 = 0x06020000;
131 /// AKA `NTDDI_WINBLUE`
132 pub const WIN8_1: u32 = 0x06030000;
133 pub const WIN10: u32 = 0x0A000000;
134}
135
136/// Helps build a Microsoft OS 2.0 Descriptor set.
137///
138/// # Example
139/// ```rust
140/// # use embassy_usb::types::InterfaceNumber;
141/// # use embassy_usb::msos::*;
142/// # let cdc_interface = unsafe { core::mem::transmute::<u8, InterfaceNumber>(0) };
143/// # let dfu_interface = unsafe { core::mem::transmute::<u8, InterfaceNumber>(1) };
144/// let mut buf = [0u8; 256];
145/// let mut builder = DeviceDescriptorSetBuilder::new(&mut buf[..], windows_version::WIN8_1);
146/// builder.feature(MinimumRecoveryTimeDescriptor::new(5, 10));
147/// builder.feature(ModelIdDescriptor::new(0xdeadbeef1234u128));
148/// builder.configuration(1, |conf| {
149/// conf.function(cdc_interface, |func| {
150/// func.winusb_device();
151/// func.feature(VendorRevisionDescriptor::new(1));
152/// });
153/// conf.function(dfu_interface, |func| {
154/// func.winusb_device();
155/// func.feature(VendorRevisionDescriptor::new(1));
156/// });
157/// });
158/// ```
159pub struct DeviceDescriptorSetBuilder<'a> {
160 builder: DescriptorSetBuilder<'a>,
161 windows_version: u32,
162 vendor_code: u8,
163}
164
165impl<'a> DeviceDescriptorSetBuilder<'a> {
166 /// Create a device descriptor set builder.
167 ///
168 /// - `windows_version` is an NTDDI version constant that describes a windows version. See the [`windows_version`]
169 /// module.
170 /// - `vendor_code` is the vendor request code used to read the MS OS descriptor set.
171 pub fn new<'b: 'a>(buf: &'b mut [u8], windows_version: u32, vendor_code: u8) -> Self {
172 let mut builder = DescriptorSetBuilder { used: 0, buf };
173 builder.descriptor(DescriptorSetHeader {
174 wLength: (size_of::<DescriptorSetHeader>() as u16).to_le(),
175 wDescriptorType: (DescriptorSetHeader::TYPE as u16).to_le(),
176 dwWindowsVersion: windows_version.to_le(),
177 wTotalLength: 0,
178 });
179 Self {
180 builder,
181 windows_version,
182 vendor_code,
183 }
184 }
185
186 /// Add a device-level feature descriptor.
187 ///
188 /// Note that some feature descriptors may only be used at the device level in non-composite devices.
189 pub fn feature<T>(&mut self, desc: T)
190 where
191 T: Descriptor + DeviceLevelDescriptor + 'a,
192 {
193 self.builder.descriptor(desc)
194 }
195
196 /// Add a configuration subset.
197 pub fn configuration(&mut self, configuration: u8, build_conf: impl FnOnce(&mut ConfigurationSubsetBuilder<'_>)) {
198 let mut cb = ConfigurationSubsetBuilder::new(self.builder.remaining(), configuration);
199 build_conf(&mut cb);
200 self.builder.used += cb.finalize();
201 }
202
203 /// Finishes writing the data.
204 pub fn finalize(self) -> MsOsDescriptorSet<'a> {
205 let used = self.builder.used;
206 let buf = self.builder.buf;
207 // Update length in header with final length
208 let total_len = &mut buf[4..6];
209 total_len.copy_from_slice((used as u16).to_le_bytes().as_slice());
210
211 MsOsDescriptorSet {
212 descriptor: &buf[..used],
213 windows_version: self.windows_version,
214 vendor_code: self.vendor_code,
215 }
216 }
217}
218
219pub struct ConfigurationSubsetBuilder<'a> {
220 builder: DescriptorSetBuilder<'a>,
221}
222
223impl<'a> ConfigurationSubsetBuilder<'a> {
224 pub fn new<'b: 'a>(buf: &'b mut [u8], configuration: u8) -> Self {
225 let mut builder = DescriptorSetBuilder { used: 0, buf };
226 builder.descriptor(ConfigurationSubsetHeader {
227 wLength: (size_of::<ConfigurationSubsetHeader>() as u16).to_le(),
228 wDescriptorType: (ConfigurationSubsetHeader::TYPE as u16).to_le(),
229 bConfigurationValue: configuration,
230 bReserved: 0,
231 wTotalLength: 0,
232 });
233 Self { builder }
234 }
235
236 /// Add a function subset.
237 pub fn function(&mut self, interface: InterfaceNumber, build_func: impl FnOnce(&mut FunctionSubsetBuilder<'_>)) {
238 let mut fb = FunctionSubsetBuilder::new(self.builder.remaining(), interface);
239 build_func(&mut fb);
240 self.builder.used += fb.finalize();
241 }
242
243 /// Finishes writing the data. Returns the total number of bytes used by the descriptor set.
244 pub fn finalize(self) -> usize {
245 let used = self.builder.used;
246 let buf = self.builder.buf;
247 // Update length in header with final length
248 let total_len = &mut buf[6..8];
249 total_len.copy_from_slice((used as u16).to_le_bytes().as_slice());
250 used
251 }
252}
253
254pub struct FunctionSubsetBuilder<'a> {
255 builder: DescriptorSetBuilder<'a>,
256}
257
258impl<'a> FunctionSubsetBuilder<'a> {
259 pub fn new<'b: 'a>(buf: &'b mut [u8], interface: InterfaceNumber) -> Self {
260 let mut builder = DescriptorSetBuilder { used: 0, buf };
261 builder.descriptor(FunctionSubsetHeader {
262 wLength: (size_of::<FunctionSubsetHeader>() as u16).to_le(),
263 wDescriptorType: (FunctionSubsetHeader::TYPE as u16).to_le(),
264 bFirstInterface: interface.0,
265 bReserved: 0,
266 wSubsetLength: 0,
267 });
268 Self { builder }
269 }
270
271 /// Add a function-level descriptor.
272 ///
273 /// Note that many descriptors can only be used at function-level in a composite device.
274 pub fn feature<T>(&mut self, desc: T)
275 where
276 T: Descriptor + FunctionLevelDescriptor + 'a,
277 {
278 self.builder.descriptor(desc)
279 }
280
281 /// Adds the feature descriptors to configure this function to use the WinUSB driver.
282 ///
283 /// Adds a compatible id descriptor "WINUSB" and a registry descriptor that sets the DeviceInterfaceGUID to the
284 /// USB_DEVICE GUID.
285 pub fn winusb_device(&mut self) {
286 self.feature(CompatibleIdFeatureDescriptor::new_winusb());
287 self.feature(RegistryPropertyFeatureDescriptor::new_usb_deviceinterfaceguid());
288 }
289
290 /// Finishes writing the data. Returns the total number of bytes used by the descriptor set.
291 pub fn finalize(self) -> usize {
292 let used = self.builder.used;
293 let buf = self.builder.buf;
294 // Update length in header with final length
295 let total_len = &mut buf[6..8];
296 total_len.copy_from_slice((used as u16).to_le_bytes().as_slice());
297 used
298 }
299}
300
301/// A trait for descriptors
302pub trait Descriptor: Sized {
303 const TYPE: DescriptorType;
304
305 /// The size of the descriptor's header.
306 fn size(&self) -> usize {
307 size_of::<Self>()
308 }
309
310 fn write_to(&self, buf: &mut [u8]);
311}
312
313/// Copies the data of `t` into `buf`.
314///
315/// # Safety
316/// The type `T` must be able to be safely cast to `&[u8]`. (e.g. it is a `#[repr(packed)]` struct)
317unsafe fn transmute_write_to<T: Sized>(t: &T, buf: &mut [u8]) {
318 let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::<T>());
319 assert!(buf.len() >= bytes.len());
320 (&mut buf[..bytes.len()]).copy_from_slice(bytes);
321}
322
323/// Table 9. Microsoft OS 2.0 descriptor wDescriptorType values.
324#[derive(Clone, Copy, PartialEq, Eq)]
325#[repr(u16)]
326pub enum DescriptorType {
327 SetHeaderDescriptor = 0,
328 SubsetHeaderConfiguration = 1,
329 SubsetHeaderFunction = 2,
330 FeatureCompatibleId = 3,
331 FeatureRegProperty = 4,
332 FeatureMinResumeTime = 5,
333 FeatureModelId = 6,
334 FeatureCcgpDevice = 7,
335 FeatureVendorRevision = 8,
336}
337
338/// Table 5. Descriptor set information structure.
339#[allow(non_snake_case)]
340#[repr(C, packed(1))]
341pub struct DescriptorSetInformation {
342 dwWindowsVersion: u32,
343 wMSOSDescriptorSetTotalLength: u16,
344 bMS_VendorCode: u8,
345 bAltEnumCode: u8,
346}
347
348/// Table 4. Microsoft OS 2.0 platform capability descriptor header.
349#[allow(non_snake_case)]
350#[repr(C, packed(1))]
351pub struct PlatformDescriptor {
352 bLength: u8,
353 bDescriptorType: u8,
354 bDevCapabilityType: u8,
355 bReserved: u8,
356 platformCapabilityUUID: [u8; 16],
357 descriptor_set_information: DescriptorSetInformation,
358}
359
360/// Table 10. Microsoft OS 2.0 descriptor set header.
361#[allow(non_snake_case)]
362#[repr(C, packed(1))]
363pub struct DescriptorSetHeader {
364 wLength: u16,
365 wDescriptorType: u16,
366 dwWindowsVersion: u32,
367 wTotalLength: u16,
368}
369
370impl Descriptor for DescriptorSetHeader {
371 const TYPE: DescriptorType = DescriptorType::SetHeaderDescriptor;
372 fn write_to(&self, buf: &mut [u8]) {
373 unsafe { transmute_write_to(self, buf) }
374 }
375}
376
377/// Table 11. Configuration subset header.
378#[allow(non_snake_case)]
379#[repr(C, packed(1))]
380pub struct ConfigurationSubsetHeader {
381 wLength: u16,
382 wDescriptorType: u16,
383 bConfigurationValue: u8,
384 bReserved: u8,
385 wTotalLength: u16,
386}
387
388impl Descriptor for ConfigurationSubsetHeader {
389 const TYPE: DescriptorType = DescriptorType::SubsetHeaderConfiguration;
390 fn write_to(&self, buf: &mut [u8]) {
391 unsafe { transmute_write_to(self, buf) }
392 }
393}
394
395/// Table 12. Function subset header.
396#[allow(non_snake_case)]
397#[repr(C, packed(1))]
398pub struct FunctionSubsetHeader {
399 wLength: u16,
400 wDescriptorType: u16,
401 bFirstInterface: u8,
402 bReserved: u8,
403 wSubsetLength: u16,
404}
405
406impl Descriptor for FunctionSubsetHeader {
407 const TYPE: DescriptorType = DescriptorType::SubsetHeaderFunction;
408 fn write_to(&self, buf: &mut [u8]) {
409 unsafe { transmute_write_to(self, buf) }
410 }
411}
412
413// Feature Descriptors
414
415/// A marker trait for feature descriptors that are valid at the device level.
416pub trait DeviceLevelDescriptor {}
417
418/// A marker trait for feature descriptors that are valid at the function level.
419pub trait FunctionLevelDescriptor {
420 /// `true` when the feature descriptor may only be used at the function level in composite devices.
421 const COMPOSITE_ONLY: bool = false;
422}
423
424/// Table 13. Microsoft OS 2.0 compatible ID descriptor.
425#[allow(non_snake_case)]
426#[repr(C, packed(1))]
427pub struct CompatibleIdFeatureDescriptor {
428 wLength: u16,
429 wDescriptorType: u16,
430 compatibleId: [u8; 8],
431 subCompatibleId: [u8; 8],
432}
433
434impl DeviceLevelDescriptor for CompatibleIdFeatureDescriptor {}
435impl FunctionLevelDescriptor for CompatibleIdFeatureDescriptor {
436 const COMPOSITE_ONLY: bool = true;
437}
438
439impl Descriptor for CompatibleIdFeatureDescriptor {
440 const TYPE: DescriptorType = DescriptorType::FeatureCompatibleId;
441 fn write_to(&self, buf: &mut [u8]) {
442 unsafe { transmute_write_to(self, buf) }
443 }
444}
445
446impl CompatibleIdFeatureDescriptor {
447 /// Creates a compatible ID descriptor that signals WINUSB driver compatiblilty.
448 pub fn new_winusb() -> Self {
449 Self::new_raw([b'W', b'I', b'N', b'U', b'S', b'B', 0, 0], [0u8; 8])
450 }
451
452 /// The ids must be 8 ASCII bytes or fewer.
453 pub fn new(compatible_id: &str, sub_compatible_id: &str) -> Self {
454 assert!(compatible_id.len() <= 8 && sub_compatible_id.len() <= 8);
455 let mut cid = [0u8; 8];
456 (&mut cid[..compatible_id.len()]).copy_from_slice(compatible_id.as_bytes());
457 let mut scid = [0u8; 8];
458 (&mut scid[..sub_compatible_id.len()]).copy_from_slice(sub_compatible_id.as_bytes());
459 Self::new_raw(cid, scid)
460 }
461
462 pub fn new_raw(compatible_id: [u8; 8], sub_compatible_id: [u8; 8]) -> Self {
463 Self {
464 wLength: (size_of::<Self>() as u16).to_le(),
465 wDescriptorType: (Self::TYPE as u16).to_le(),
466 compatibleId: compatible_id,
467 subCompatibleId: sub_compatible_id,
468 }
469 }
470}
471
472/// Table 14. Microsoft OS 2.0 registry property descriptor
473#[allow(non_snake_case)]
474pub struct RegistryPropertyFeatureDescriptor<'a> {
475 wLength: u16,
476 wDescriptorType: u16,
477 wPropertyDataType: u16,
478 wPropertyNameLength: u16,
479 PropertyName: &'a [u8],
480 wPropertyDataLength: u16,
481 PropertyData: &'a [u8],
482}
483
484impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {}
485impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {
486 const COMPOSITE_ONLY: bool = true;
487}
488
489impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> {
490 const TYPE: DescriptorType = DescriptorType::FeatureRegProperty;
491 fn size(&self) -> usize {
492 10 + self.PropertyName.len() + self.PropertyData.len()
493 }
494 fn write_to(&self, buf: &mut [u8]) {
495 assert!(buf.len() >= self.size());
496 assert!(self.wPropertyNameLength as usize == self.PropertyName.len());
497 assert!(self.wPropertyDataLength as usize == self.PropertyData.len());
498 write_u16(buf, 0..2, self.wLength);
499 write_u16(buf, 2..4, self.wDescriptorType);
500 write_u16(buf, 4..6, self.wPropertyDataType);
501 write_u16(buf, 6..8, self.wPropertyNameLength);
502 let pne = 8 + self.PropertyName.len();
503 (&mut buf[8..pne]).copy_from_slice(self.PropertyName);
504 let pds = pne + 2;
505 let pde = pds + self.PropertyData.len();
506 write_u16(buf, pne..pds, self.wPropertyDataLength);
507 (&mut buf[pds..pde]).copy_from_slice(self.PropertyData);
508 }
509}
510
511impl<'a> RegistryPropertyFeatureDescriptor<'a> {
512 /// A registry property.
513 ///
514 /// `name` should be a NUL-terminated 16-bit Unicode string.
515 pub fn new_raw<'n: 'a, 'd: 'a>(name: &'a [u8], data: &'d [u8], data_type: PropertyDataType) -> Self {
516 Self {
517 wLength: ((10 + name.len() + data.len()) as u16).to_le(),
518 wDescriptorType: (Self::TYPE as u16).to_le(),
519 wPropertyDataType: (data_type as u16).to_le(),
520 wPropertyNameLength: (name.len() as u16).to_le(),
521 PropertyName: name,
522 wPropertyDataLength: (data.len() as u16).to_le(),
523 PropertyData: data,
524 }
525 }
526
527 fn u16str_bytes(s: &U16CStr) -> &[u8] {
528 unsafe { core::slice::from_raw_parts(s.as_ptr() as *const u8, (s.len() + 1) * 2) }
529 }
530
531 /// A registry property that sets the DeviceInterfaceGUID to the device interface class for USB devices which are
532 /// attached to a USB hub.
533 pub fn new_usb_deviceinterfaceguid() -> Self {
534 // Can't use defmt::panic in constant expressions (inside u16cstr!)
535 macro_rules! panic {
536 ($($x:tt)*) => {
537 {
538 ::core::panic!($($x)*);
539 }
540 };
541 }
542
543 Self::new_string(
544 u16cstr!("DeviceInterfaceGUID"),
545 u16cstr!("{A5DCBF10-6530-11D2-901F-00C04FB951ED}"),
546 )
547 }
548
549 /// A registry property containing a NUL-terminated 16-bit Unicode string.
550 pub fn new_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self {
551 Self::new_raw(Self::u16str_bytes(name), Self::u16str_bytes(data), PropertyDataType::Sz)
552 }
553
554 /// A registry property containing a NUL-terminated 16-bit Unicode string that expands environment variables.
555 pub fn new_string_expand<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self {
556 Self::new_raw(
557 Self::u16str_bytes(name),
558 Self::u16str_bytes(data),
559 PropertyDataType::ExpandSz,
560 )
561 }
562
563 /// A registry property containing a NUL-terminated 16-bit Unicode string that contains a symbolic link.
564 pub fn new_link<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self {
565 Self::new_raw(
566 Self::u16str_bytes(name),
567 Self::u16str_bytes(data),
568 PropertyDataType::Link,
569 )
570 }
571
572 /// A registry property containing multiple NUL-terminated 16-bit Unicode strings.
573 pub fn new_multi_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u16]) -> Self {
574 Self::new_raw(
575 Self::u16str_bytes(name),
576 unsafe { core::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 2) },
577 PropertyDataType::RegMultiSz,
578 )
579 }
580
581 /// A registry property containing binary data.
582 pub fn new_binary<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u8]) -> Self {
583 Self::new_raw(Self::u16str_bytes(name), data, PropertyDataType::Binary)
584 }
585
586 /// A registry property containing a Little-Endian 32-bit integer.
587 ///
588 /// The function assumes that `data` is already little-endian, it does not convert it.
589 pub fn new_dword_le<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d i32) -> Self {
590 Self::new_raw(
591 Self::u16str_bytes(name),
592 unsafe { core::slice::from_raw_parts(data as *const i32 as *const u8, size_of::<i32>()) },
593 PropertyDataType::DwordLittleEndian,
594 )
595 }
596
597 /// A registry property containing a big-endian 32-bit integer.
598 ///
599 /// The function assumes that `data` is already big-endian, it does not convert it.
600 pub fn new_dword_be<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d i32) -> Self {
601 Self::new_raw(
602 Self::u16str_bytes(name),
603 unsafe { core::slice::from_raw_parts(data as *const i32 as *const u8, size_of::<i32>()) },
604 PropertyDataType::DwordBigEndian,
605 )
606 }
607}
608
609/// Table 15. wPropertyDataType values for the Microsoft OS 2.0 registry property descriptor.
610#[derive(Clone, Copy, PartialEq, Eq)]
611#[repr(u16)]
612pub enum PropertyDataType {
613 Sz = 1,
614 ExpandSz = 2,
615 Binary = 3,
616 DwordLittleEndian = 4,
617 DwordBigEndian = 5,
618 Link = 6,
619 RegMultiSz = 7,
620}
621
622/// Table 16. Microsoft OS 2.0 minimum USB recovery time descriptor.
623#[allow(non_snake_case)]
624#[repr(C, packed(1))]
625pub struct MinimumRecoveryTimeDescriptor {
626 wLength: u16,
627 wDescriptorType: u16,
628 bResumeRecoveryTime: u8,
629 bResumeSignalingTime: u8,
630}
631
632impl DeviceLevelDescriptor for MinimumRecoveryTimeDescriptor {}
633
634impl Descriptor for MinimumRecoveryTimeDescriptor {
635 const TYPE: DescriptorType = DescriptorType::FeatureMinResumeTime;
636 fn write_to(&self, buf: &mut [u8]) {
637 unsafe { transmute_write_to(self, buf) }
638 }
639}
640
641impl MinimumRecoveryTimeDescriptor {
642 /// Times are in milliseconds.
643 ///
644 /// `resume_recovery_time` must be >= 0 and <= 10.
645 /// `resume_signaling_time` must be >= 1 and <= 20.
646 pub fn new(resume_recovery_time: u8, resume_signaling_time: u8) -> Self {
647 assert!(resume_recovery_time <= 10);
648 assert!(resume_signaling_time >= 1 && resume_signaling_time <= 20);
649 Self {
650 wLength: (size_of::<Self>() as u16).to_le(),
651 wDescriptorType: (Self::TYPE as u16).to_le(),
652 bResumeRecoveryTime: resume_recovery_time,
653 bResumeSignalingTime: resume_signaling_time,
654 }
655 }
656}
657
658/// Table 17. Microsoft OS 2.0 model ID descriptor.
659#[allow(non_snake_case)]
660#[repr(C, packed(1))]
661pub struct ModelIdDescriptor {
662 wLength: u16,
663 wDescriptorType: u16,
664 modelId: [u8; 16],
665}
666
667impl DeviceLevelDescriptor for ModelIdDescriptor {}
668
669impl Descriptor for ModelIdDescriptor {
670 const TYPE: DescriptorType = DescriptorType::FeatureModelId;
671 fn write_to(&self, buf: &mut [u8]) {
672 unsafe { transmute_write_to(self, buf) }
673 }
674}
675
676impl ModelIdDescriptor {
677 pub fn new(model_id: u128) -> Self {
678 Self::new_bytes(model_id.to_le_bytes())
679 }
680
681 pub fn new_bytes(model_id: [u8; 16]) -> Self {
682 Self {
683 wLength: (size_of::<Self>() as u16).to_le(),
684 wDescriptorType: (Self::TYPE as u16).to_le(),
685 modelId: model_id,
686 }
687 }
688}
689
690/// Table 18. Microsoft OS 2.0 CCGP device descriptor.
691#[allow(non_snake_case)]
692#[repr(C, packed(1))]
693pub struct CcgpDeviceDescriptor {
694 wLength: u16,
695 wDescriptorType: u16,
696}
697
698impl DeviceLevelDescriptor for CcgpDeviceDescriptor {}
699
700impl Descriptor for CcgpDeviceDescriptor {
701 const TYPE: DescriptorType = DescriptorType::FeatureCcgpDevice;
702 fn write_to(&self, buf: &mut [u8]) {
703 unsafe { transmute_write_to(self, buf) }
704 }
705}
706
707impl CcgpDeviceDescriptor {
708 pub fn new() -> Self {
709 Self {
710 wLength: (size_of::<Self>() as u16).to_le(),
711 wDescriptorType: (Self::TYPE as u16).to_le(),
712 }
713 }
714}
715
716/// Table 19. Microsoft OS 2.0 vendor revision descriptor.
717#[allow(non_snake_case)]
718#[repr(C, packed(1))]
719pub struct VendorRevisionDescriptor {
720 wLength: u16,
721 wDescriptorType: u16,
722 /// Revision number associated with the descriptor set. Modify it every time you add/modify a registry property or
723 /// other MSOS descriptor. Shell set to greater than or equal to 1.
724 VendorRevision: u16,
725}
726
727impl DeviceLevelDescriptor for VendorRevisionDescriptor {}
728impl FunctionLevelDescriptor for VendorRevisionDescriptor {}
729
730impl Descriptor for VendorRevisionDescriptor {
731 const TYPE: DescriptorType = DescriptorType::FeatureVendorRevision;
732 fn write_to(&self, buf: &mut [u8]) {
733 unsafe { transmute_write_to(self, buf) }
734 }
735}
736
737impl VendorRevisionDescriptor {
738 pub fn new(revision: u16) -> Self {
739 assert!(revision >= 1);
740 Self {
741 wLength: (size_of::<Self>() as u16).to_le(),
742 wDescriptorType: (Self::TYPE as u16).to_le(),
743 VendorRevision: revision.to_le(),
744 }
745 }
746}