aboutsummaryrefslogtreecommitdiff
path: root/embassy-usb
diff options
context:
space:
mode:
authoralexmoon <[email protected]>2023-02-07 14:19:51 -0500
committeralexmoon <[email protected]>2023-02-07 14:24:35 -0500
commitaa21aebb0b321a2085571e5be5fffcea4703584d (patch)
tree9b45fb26d9a32b806931fcc6d379af00f4ffec21 /embassy-usb
parent9f9230ae7abb545822e59c6f06cabb721b63e0a1 (diff)
Lazily encode UTF16 values and add docs
Diffstat (limited to 'embassy-usb')
-rw-r--r--embassy-usb/Cargo.toml3
-rw-r--r--embassy-usb/src/msos.rs316
2 files changed, 157 insertions, 162 deletions
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml
index 54a8f27c7..eb9ba36f4 100644
--- a/embassy-usb/Cargo.toml
+++ b/embassy-usb/Cargo.toml
@@ -13,7 +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"] 16msos-descriptor = []
17default = ["usbd-hid"] 17default = ["usbd-hid"]
18 18
19[dependencies] 19[dependencies]
@@ -25,7 +25,6 @@ embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-
25defmt = { version = "0.3", optional = true } 25defmt = { version = "0.3", optional = true }
26log = { version = "0.4.14", optional = true } 26log = { version = "0.4.14", optional = true }
27heapless = "0.7.10" 27heapless = "0.7.10"
28widestring = { version = "1.0.2", default-features = false, optional = true }
29 28
30# for HID 29# for HID
31usbd-hid = { version = "0.6.0", optional = true } 30usbd-hid = { version = "0.6.0", optional = true }
diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs
index 360f80d91..19ed34979 100644
--- a/embassy-usb/src/msos.rs
+++ b/embassy-usb/src/msos.rs
@@ -5,16 +5,9 @@
5//! <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>
6 6
7use core::mem::size_of; 7use core::mem::size_of;
8use core::ops::Range;
9
10pub use widestring::{u16cstr, u16str, U16CStr, U16Str};
11 8
12use super::{capability_type, BosWriter}; 9use super::{capability_type, BosWriter};
13 10
14fn write_u16<T: Into<u16>>(buf: &mut [u8], range: Range<usize>, data: T) {
15 (&mut buf[range]).copy_from_slice(data.into().to_le_bytes().as_slice())
16}
17
18/// A serialized Microsoft OS 2.0 Descriptor set. 11/// A serialized Microsoft OS 2.0 Descriptor set.
19/// 12///
20/// Create with [`DeviceDescriptorSetBuilder`]. 13/// Create with [`DeviceDescriptorSetBuilder`].
@@ -24,14 +17,17 @@ pub struct MsOsDescriptorSet<'d> {
24} 17}
25 18
26impl<'d> MsOsDescriptorSet<'d> { 19impl<'d> MsOsDescriptorSet<'d> {
20 /// Gets the raw bytes of the MS OS descriptor
27 pub fn descriptor(&self) -> &[u8] { 21 pub fn descriptor(&self) -> &[u8] {
28 self.descriptor 22 self.descriptor
29 } 23 }
30 24
25 /// Gets the vendor code used by the host to retrieve the MS OS descriptor
31 pub fn vendor_code(&self) -> u8 { 26 pub fn vendor_code(&self) -> u8 {
32 self.vendor_code 27 self.vendor_code
33 } 28 }
34 29
30 /// Returns `true` if no MS OS descriptor data is available
35 pub fn is_empty(&self) -> bool { 31 pub fn is_empty(&self) -> bool {
36 self.descriptor.is_empty() 32 self.descriptor.is_empty()
37 } 33 }
@@ -39,7 +35,7 @@ impl<'d> MsOsDescriptorSet<'d> {
39 35
40/// Writes a Microsoft OS 2.0 Descriptor set into a buffer. 36/// Writes a Microsoft OS 2.0 Descriptor set into a buffer.
41pub struct MsOsDescriptorWriter<'d> { 37pub struct MsOsDescriptorWriter<'d> {
42 pub buf: &'d mut [u8], 38 buf: &'d mut [u8],
43 39
44 position: usize, 40 position: usize,
45 config_mark: Option<usize>, 41 config_mark: Option<usize>,
@@ -75,14 +71,17 @@ impl<'d> MsOsDescriptorWriter<'d> {
75 } 71 }
76 } 72 }
77 73
74 /// Returns `true` if the MS OS descriptor header has not yet been written
78 pub fn is_empty(&self) -> bool { 75 pub fn is_empty(&self) -> bool {
79 self.position == 0 76 self.position == 0
80 } 77 }
81 78
79 /// Returns `true` if a configuration subset header has been started
82 pub fn is_in_config_subset(&self) -> bool { 80 pub fn is_in_config_subset(&self) -> bool {
83 self.config_mark.is_some() 81 self.config_mark.is_some()
84 } 82 }
85 83
84 /// Returns `true` if a function subset header has been started and not yet ended
86 pub fn is_in_function_subset(&self) -> bool { 85 pub fn is_in_function_subset(&self) -> bool {
87 self.function_mark.is_some() 86 self.function_mark.is_some()
88 } 87 }
@@ -148,6 +147,7 @@ impl<'d> MsOsDescriptorWriter<'d> {
148 self.write(desc); 147 self.write(desc);
149 } 148 }
150 149
150 /// Ends the current function subset (if any)
151 pub fn end_function(&mut self) { 151 pub fn end_function(&mut self) {
152 Self::end_subset::<FunctionSubsetHeader>(self.buf, self.position, &mut self.function_mark); 152 Self::end_subset::<FunctionSubsetHeader>(self.buf, self.position, &mut self.function_mark);
153 } 153 }
@@ -212,29 +212,13 @@ impl<'d> MsOsDescriptorWriter<'d> {
212 } 212 }
213} 213}
214 214
215/// Microsoft Windows version codes
216///
217/// Windows 8.1 is the minimum version allowed for MS OS 2.0 descriptors.
215pub mod windows_version { 218pub mod windows_version {
216 pub const WIN2K: u32 = 0x05000000; 219 /// Windows 8.1 (aka `NTDDI_WINBLUE`)
217 pub const WIN2KSP1: u32 = 0x05000100;
218 pub const WIN2KSP2: u32 = 0x05000200;
219 pub const WIN2KSP3: u32 = 0x05000300;
220 pub const WIN2KSP4: u32 = 0x05000400;
221
222 pub const WINXP: u32 = 0x05010000;
223 pub const WINXPSP1: u32 = 0x05010100;
224 pub const WINXPSP2: u32 = 0x05010200;
225 pub const WINXPSP3: u32 = 0x05010300;
226 pub const WINXPSP4: u32 = 0x05010400;
227
228 pub const VISTA: u32 = 0x06000000;
229 pub const VISTASP1: u32 = 0x06000100;
230 pub const VISTASP2: u32 = 0x06000200;
231 pub const VISTASP3: u32 = 0x06000300;
232 pub const VISTASP4: u32 = 0x06000400;
233
234 pub const WIN7: u32 = 0x06010000;
235 pub const WIN8: u32 = 0x06020000;
236 /// AKA `NTDDI_WINBLUE`
237 pub const WIN8_1: u32 = 0x06030000; 220 pub const WIN8_1: u32 = 0x06030000;
221 /// Windows 10
238 pub const WIN10: u32 = 0x0A000000; 222 pub const WIN10: u32 = 0x0A000000;
239} 223}
240 224
@@ -266,7 +250,7 @@ use sealed::*;
266/// The type `T` must be able to be safely cast to `&[u8]`. (e.g. it is a `#[repr(packed)]` struct) 250/// The type `T` must be able to be safely cast to `&[u8]`. (e.g. it is a `#[repr(packed)]` struct)
267unsafe fn transmute_write_to<T: Sized>(t: &T, buf: &mut [u8]) { 251unsafe fn transmute_write_to<T: Sized>(t: &T, buf: &mut [u8]) {
268 let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::<T>()); 252 let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::<T>());
269 assert!(buf.len() >= bytes.len(), "MSOS descriptor buffer full"); 253 assert!(buf.len() >= bytes.len(), "MS OS descriptor buffer full");
270 (&mut buf[..bytes.len()]).copy_from_slice(bytes); 254 (&mut buf[..bytes.len()]).copy_from_slice(bytes);
271} 255}
272 256
@@ -274,14 +258,23 @@ unsafe fn transmute_write_to<T: Sized>(t: &T, buf: &mut [u8]) {
274#[derive(Clone, Copy, PartialEq, Eq)] 258#[derive(Clone, Copy, PartialEq, Eq)]
275#[repr(u16)] 259#[repr(u16)]
276pub enum DescriptorType { 260pub enum DescriptorType {
261 /// MS OS descriptor set header
277 SetHeaderDescriptor = 0, 262 SetHeaderDescriptor = 0,
263 /// Configuration subset header
278 SubsetHeaderConfiguration = 1, 264 SubsetHeaderConfiguration = 1,
265 /// Function subset header
279 SubsetHeaderFunction = 2, 266 SubsetHeaderFunction = 2,
267 /// Compatible device ID feature descriptor
280 FeatureCompatibleId = 3, 268 FeatureCompatibleId = 3,
269 /// Registry property feature descriptor
281 FeatureRegProperty = 4, 270 FeatureRegProperty = 4,
271 /// Minimum USB resume time feature descriptor
282 FeatureMinResumeTime = 5, 272 FeatureMinResumeTime = 5,
273 /// Vendor revision feature descriptor
283 FeatureModelId = 6, 274 FeatureModelId = 6,
275 /// CCGP device descriptor feature descriptor
284 FeatureCcgpDevice = 7, 276 FeatureCcgpDevice = 7,
277 /// Vendor revision feature descriptor
285 FeatureVendorRevision = 8, 278 FeatureVendorRevision = 8,
286} 279}
287 280
@@ -318,6 +311,9 @@ pub struct DescriptorSetHeader {
318} 311}
319 312
320impl DescriptorSetHeader { 313impl DescriptorSetHeader {
314 /// Creates a MS OS descriptor set header.
315 ///
316 /// `windows_version` is the minimum Windows version the descriptor set can apply to.
321 pub fn new(windows_version: u32) -> Self { 317 pub fn new(windows_version: u32) -> Self {
322 DescriptorSetHeader { 318 DescriptorSetHeader {
323 wLength: (size_of::<Self>() as u16).to_le(), 319 wLength: (size_of::<Self>() as u16).to_le(),
@@ -351,6 +347,7 @@ pub struct ConfigurationSubsetHeader {
351} 347}
352 348
353impl ConfigurationSubsetHeader { 349impl ConfigurationSubsetHeader {
350 /// Creates a configuration subset header
354 pub fn new(config: u8) -> Self { 351 pub fn new(config: u8) -> Self {
355 ConfigurationSubsetHeader { 352 ConfigurationSubsetHeader {
356 wLength: (size_of::<Self>() as u16).to_le(), 353 wLength: (size_of::<Self>() as u16).to_le(),
@@ -385,6 +382,7 @@ pub struct FunctionSubsetHeader {
385} 382}
386 383
387impl FunctionSubsetHeader { 384impl FunctionSubsetHeader {
385 /// Creates a function subset header
388 pub fn new(first_interface: u8) -> Self { 386 pub fn new(first_interface: u8) -> Self {
389 FunctionSubsetHeader { 387 FunctionSubsetHeader {
390 wLength: (size_of::<Self>() as u16).to_le(), 388 wLength: (size_of::<Self>() as u16).to_le(),
@@ -436,11 +434,8 @@ impl Descriptor for CompatibleIdFeatureDescriptor {
436} 434}
437 435
438impl CompatibleIdFeatureDescriptor { 436impl CompatibleIdFeatureDescriptor {
439 /// Creates a compatible ID descriptor that signals WINUSB driver compatiblilty. 437 /// Creates a compatible ID feature descriptor
440 pub fn new_winusb() -> Self { 438 ///
441 Self::new_raw([b'W', b'I', b'N', b'U', b'S', b'B', 0, 0], [0u8; 8])
442 }
443
444 /// The ids must be 8 ASCII bytes or fewer. 439 /// The ids must be 8 ASCII bytes or fewer.
445 pub fn new(compatible_id: &str, sub_compatible_id: &str) -> Self { 440 pub fn new(compatible_id: &str, sub_compatible_id: &str) -> Self {
446 assert!(compatible_id.len() <= 8 && sub_compatible_id.len() <= 8); 441 assert!(compatible_id.len() <= 8 && sub_compatible_id.len() <= 8);
@@ -451,7 +446,7 @@ impl CompatibleIdFeatureDescriptor {
451 Self::new_raw(cid, scid) 446 Self::new_raw(cid, scid)
452 } 447 }
453 448
454 pub fn new_raw(compatible_id: [u8; 8], sub_compatible_id: [u8; 8]) -> Self { 449 fn new_raw(compatible_id: [u8; 8], sub_compatible_id: [u8; 8]) -> Self {
455 Self { 450 Self {
456 wLength: (size_of::<Self>() as u16).to_le(), 451 wLength: (size_of::<Self>() as u16).to_le(),
457 wDescriptorType: (Self::TYPE as u16).to_le(), 452 wDescriptorType: (Self::TYPE as u16).to_le(),
@@ -464,129 +459,87 @@ impl CompatibleIdFeatureDescriptor {
464/// Table 14. Microsoft OS 2.0 registry property descriptor 459/// Table 14. Microsoft OS 2.0 registry property descriptor
465#[allow(non_snake_case)] 460#[allow(non_snake_case)]
466pub struct RegistryPropertyFeatureDescriptor<'a> { 461pub struct RegistryPropertyFeatureDescriptor<'a> {
467 wLength: u16, 462 name: &'a str,
468 wDescriptorType: u16, 463 data: PropertyData<'a>,
469 wPropertyDataType: u16, 464}
470 PropertyName: &'a [u8], 465
471 PropertyData: &'a [u8], 466/// Data values that can be encoded into a registry property descriptor
472} 467#[derive(Debug, Clone, Copy, PartialEq, Eq)]
473 468#[cfg_attr(feature = "defmt", derive(defmt::Format))]
474impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} 469pub enum PropertyData<'a> {
475impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} 470 /// A registry property containing a string.
476 471 Sz(&'a str),
477impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> { 472 /// A registry property containing a string that expands environment variables.
478 const TYPE: DescriptorType = DescriptorType::FeatureRegProperty; 473 ExpandSz(&'a str),
479 fn size(&self) -> usize {
480 10 + self.PropertyName.len() + self.PropertyData.len()
481 }
482 fn write_to(&self, buf: &mut [u8]) {
483 assert!(buf.len() >= self.size(), "MSOS descriptor buffer full");
484 write_u16(buf, 0..2, self.wLength);
485 write_u16(buf, 2..4, self.wDescriptorType);
486 write_u16(buf, 4..6, self.wPropertyDataType);
487 write_u16(buf, 6..8, (self.PropertyName.len() as u16).to_le());
488 let pne = 8 + self.PropertyName.len();
489 (&mut buf[8..pne]).copy_from_slice(self.PropertyName);
490 let pds = pne + 2;
491 let pde = pds + self.PropertyData.len();
492 write_u16(buf, pne..pds, (self.PropertyData.len() as u16).to_le());
493 (&mut buf[pds..pde]).copy_from_slice(self.PropertyData);
494 }
495}
496
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
511 /// A registry property.
512 ///
513 /// `name` should be a NUL-terminated 16-bit Unicode string.
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));
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 PropertyName: name,
521 PropertyData: data,
522 }
523 }
524
525 fn u16str_bytes(s: &U16CStr) -> &[u8] {
526 unsafe { core::slice::from_raw_parts(s.as_ptr() as *const u8, (s.len() + 1) * 2) }
527 }
528
529 /// A registry property containing a NUL-terminated 16-bit Unicode string.
530 pub fn new_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self {
531 Self::new_raw(Self::u16str_bytes(name), Self::u16str_bytes(data), PropertyDataType::Sz)
532 }
533
534 /// A registry property containing a NUL-terminated 16-bit Unicode string that expands environment variables.
535 pub fn new_string_expand<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self {
536 Self::new_raw(
537 Self::u16str_bytes(name),
538 Self::u16str_bytes(data),
539 PropertyDataType::ExpandSz,
540 )
541 }
542
543 /// A registry property containing a NUL-terminated 16-bit Unicode string that contains a symbolic link.
544 pub fn new_link<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self {
545 Self::new_raw(
546 Self::u16str_bytes(name),
547 Self::u16str_bytes(data),
548 PropertyDataType::Link,
549 )
550 }
551
552 /// A registry property containing multiple NUL-terminated 16-bit Unicode strings.
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 );
558 Self::new_raw(
559 Self::u16str_bytes(name),
560 unsafe { core::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 2) },
561 PropertyDataType::RegMultiSz,
562 )
563 }
564
565 /// A registry property containing binary data. 474 /// A registry property containing binary data.
566 pub fn new_binary<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u8]) -> Self { 475 Binary(&'a [u8]),
567 Self::new_raw(Self::u16str_bytes(name), data, PropertyDataType::Binary) 476 /// A registry property containing a little-endian 32-bit integer.
477 DwordLittleEndian(u32),
478 /// A registry property containing a big-endian 32-bit integer.
479 DwordBigEndian(u32),
480 /// A registry property containing a string that contains a symbolic link.
481 Link(&'a str),
482 /// A registry property containing multiple strings.
483 RegMultiSz(&'a [&'a str]),
484}
485
486fn write_bytes(val: &[u8], buf: &mut [u8]) -> usize {
487 assert!(buf.len() >= val.len());
488 buf[..val.len()].copy_from_slice(val);
489 val.len()
490}
491
492fn write_utf16(val: &str, buf: &mut [u8]) -> usize {
493 let mut pos = 0;
494 for c in val.encode_utf16() {
495 pos += write_bytes(&c.to_le_bytes(), &mut buf[pos..]);
496 }
497 pos + write_bytes(&0u16.to_le_bytes(), &mut buf[pos..])
498}
499
500impl<'a> PropertyData<'a> {
501 /// Gets the `PropertyDataType` for this property value
502 pub fn kind(&self) -> PropertyDataType {
503 match self {
504 PropertyData::Sz(_) => PropertyDataType::Sz,
505 PropertyData::ExpandSz(_) => PropertyDataType::ExpandSz,
506 PropertyData::Binary(_) => PropertyDataType::Binary,
507 PropertyData::DwordLittleEndian(_) => PropertyDataType::DwordLittleEndian,
508 PropertyData::DwordBigEndian(_) => PropertyDataType::DwordBigEndian,
509 PropertyData::Link(_) => PropertyDataType::Link,
510 PropertyData::RegMultiSz(_) => PropertyDataType::RegMultiSz,
511 }
568 } 512 }
569 513
570 /// A registry property containing a Little-Endian 32-bit integer. 514 /// Gets the size (in bytes) of this property value when encoded.
571 /// 515 pub fn size(&self) -> usize {
572 /// The function assumes that `data` is already little-endian, it does not convert it. 516 match self {
573 pub fn new_dword_le<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d i32) -> Self { 517 PropertyData::Sz(val) | PropertyData::ExpandSz(val) | PropertyData::Link(val) => {
574 Self::new_raw( 518 core::mem::size_of::<u16>() * (val.encode_utf16().count() + 1)
575 Self::u16str_bytes(name), 519 }
576 unsafe { core::slice::from_raw_parts(data as *const i32 as *const u8, size_of::<i32>()) }, 520 PropertyData::Binary(val) => val.len(),
577 PropertyDataType::DwordLittleEndian, 521 PropertyData::DwordLittleEndian(val) | PropertyData::DwordBigEndian(val) => core::mem::size_of_val(val),
578 ) 522 PropertyData::RegMultiSz(val) => {
523 core::mem::size_of::<u16>() * val.iter().map(|x| x.encode_utf16().count() + 1).sum::<usize>() + 1
524 }
525 }
579 } 526 }
580 527
581 /// A registry property containing a big-endian 32-bit integer. 528 /// Encodes the data for this property value and writes it to `buf`.
582 /// 529 pub fn write(&self, buf: &mut [u8]) -> usize {
583 /// The function assumes that `data` is already big-endian, it does not convert it. 530 match self {
584 pub fn new_dword_be<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d i32) -> Self { 531 PropertyData::Sz(val) | PropertyData::ExpandSz(val) | PropertyData::Link(val) => write_utf16(val, buf),
585 Self::new_raw( 532 PropertyData::Binary(val) => write_bytes(val, buf),
586 Self::u16str_bytes(name), 533 PropertyData::DwordLittleEndian(val) => write_bytes(&val.to_le_bytes(), buf),
587 unsafe { core::slice::from_raw_parts(data as *const i32 as *const u8, size_of::<i32>()) }, 534 PropertyData::DwordBigEndian(val) => write_bytes(&val.to_be_bytes(), buf),
588 PropertyDataType::DwordBigEndian, 535 PropertyData::RegMultiSz(val) => {
589 ) 536 let mut pos = 0;
537 for s in *val {
538 pos += write_utf16(s, &mut buf[pos..]);
539 }
540 pos + write_bytes(&0u16.to_le_bytes(), &mut buf[pos..])
541 }
542 }
590 } 543 }
591} 544}
592 545
@@ -594,15 +547,57 @@ impl<'a> RegistryPropertyFeatureDescriptor<'a> {
594#[derive(Clone, Copy, PartialEq, Eq)] 547#[derive(Clone, Copy, PartialEq, Eq)]
595#[repr(u16)] 548#[repr(u16)]
596pub enum PropertyDataType { 549pub enum PropertyDataType {
550 /// A registry property containing a string.
597 Sz = 1, 551 Sz = 1,
552 /// A registry property containing a string that expands environment variables.
598 ExpandSz = 2, 553 ExpandSz = 2,
554 /// A registry property containing binary data.
599 Binary = 3, 555 Binary = 3,
556 /// A registry property containing a little-endian 32-bit integer.
600 DwordLittleEndian = 4, 557 DwordLittleEndian = 4,
558 /// A registry property containing a big-endian 32-bit integer.
601 DwordBigEndian = 5, 559 DwordBigEndian = 5,
560 /// A registry property containing a string that contains a symbolic link.
602 Link = 6, 561 Link = 6,
562 /// A registry property containing multiple strings.
603 RegMultiSz = 7, 563 RegMultiSz = 7,
604} 564}
605 565
566impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {}
567impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {}
568
569impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> {
570 const TYPE: DescriptorType = DescriptorType::FeatureRegProperty;
571
572 fn size(&self) -> usize {
573 10 + self.name_size() + self.data.size()
574 }
575
576 fn write_to(&self, buf: &mut [u8]) {
577 assert!(buf.len() >= self.size(), "MS OS descriptor buffer full");
578
579 let mut pos = 0;
580 pos += write_bytes(&(self.size() as u16).to_le_bytes(), &mut buf[pos..]);
581 pos += write_bytes(&(Self::TYPE as u16).to_le_bytes(), &mut buf[pos..]);
582 pos += write_bytes(&(self.data.kind() as u16).to_le_bytes(), &mut buf[pos..]);
583 pos += write_bytes(&(self.name_size() as u16).to_le_bytes(), &mut buf[pos..]);
584 pos += write_utf16(self.name, &mut buf[pos..]);
585 pos += write_bytes(&(self.data.size() as u16).to_le_bytes(), &mut buf[pos..]);
586 self.data.write(&mut buf[pos..]);
587 }
588}
589
590impl<'a> RegistryPropertyFeatureDescriptor<'a> {
591 /// A registry property.
592 pub fn new(name: &'a str, data: PropertyData<'a>) -> Self {
593 Self { name, data }
594 }
595
596 fn name_size(&self) -> usize {
597 core::mem::size_of::<u16>() * (self.name.encode_utf16().count() + 1)
598 }
599}
600
606/// Table 16. Microsoft OS 2.0 minimum USB recovery time descriptor. 601/// Table 16. Microsoft OS 2.0 minimum USB recovery time descriptor.
607#[allow(non_snake_case)] 602#[allow(non_snake_case)]
608#[repr(C, packed(1))] 603#[repr(C, packed(1))]
@@ -658,15 +653,14 @@ impl Descriptor for ModelIdDescriptor {
658} 653}
659 654
660impl ModelIdDescriptor { 655impl ModelIdDescriptor {
656 /// Creates a new model ID descriptor
657 ///
658 /// `model_id` should be a uuid that uniquely identifies a physical device.
661 pub fn new(model_id: u128) -> Self { 659 pub fn new(model_id: u128) -> Self {
662 Self::new_bytes(model_id.to_le_bytes())
663 }
664
665 pub fn new_bytes(model_id: [u8; 16]) -> Self {
666 Self { 660 Self {
667 wLength: (size_of::<Self>() as u16).to_le(), 661 wLength: (size_of::<Self>() as u16).to_le(),
668 wDescriptorType: (Self::TYPE as u16).to_le(), 662 wDescriptorType: (Self::TYPE as u16).to_le(),
669 modelId: model_id, 663 modelId: model_id.to_le_bytes(),
670 } 664 }
671 } 665 }
672} 666}
@@ -689,6 +683,7 @@ impl Descriptor for CcgpDeviceDescriptor {
689} 683}
690 684
691impl CcgpDeviceDescriptor { 685impl CcgpDeviceDescriptor {
686 /// Creates a new CCGP device descriptor
692 pub fn new() -> Self { 687 pub fn new() -> Self {
693 Self { 688 Self {
694 wLength: (size_of::<Self>() as u16).to_le(), 689 wLength: (size_of::<Self>() as u16).to_le(),
@@ -704,7 +699,7 @@ pub struct VendorRevisionDescriptor {
704 wLength: u16, 699 wLength: u16,
705 wDescriptorType: u16, 700 wDescriptorType: u16,
706 /// Revision number associated with the descriptor set. Modify it every time you add/modify a registry property or 701 /// Revision number associated with the descriptor set. Modify it every time you add/modify a registry property or
707 /// other MSOS descriptor. Shell set to greater than or equal to 1. 702 /// other MS OS descriptor. Shell set to greater than or equal to 1.
708 VendorRevision: u16, 703 VendorRevision: u16,
709} 704}
710 705
@@ -719,6 +714,7 @@ impl Descriptor for VendorRevisionDescriptor {
719} 714}
720 715
721impl VendorRevisionDescriptor { 716impl VendorRevisionDescriptor {
717 /// Creates a new vendor revision descriptor
722 pub fn new(revision: u16) -> Self { 718 pub fn new(revision: u16) -> Self {
723 assert!(revision >= 1); 719 assert!(revision >= 1);
724 Self { 720 Self {