From f5ff3c4ac31c79cedf077f559dbd5685886399cc Mon Sep 17 00:00:00 2001 From: Matt Ickstadt Date: Thu, 12 Jan 2023 14:59:25 -0600 Subject: [PATCH] usb: add support for MS OS Descriptors --- embassy-usb/Cargo.toml | 1 + embassy-usb/src/builder.rs | 15 + embassy-usb/src/lib.rs | 17 + embassy-usb/src/msos.rs | 746 +++++++++++++++++++++++++++++++++++++ 4 files changed, 779 insertions(+) create mode 100644 embassy-usb/src/msos.rs 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- defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } heapless = "0.7.10" +widestring = { version = "1.0.2", default-features = false } # for HID usbd-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>> { device_descriptor: DescriptorWriter<'d>, config_descriptor: DescriptorWriter<'d>, bos_descriptor: BosWriter<'d>, + msos_descriptor: Option>, } impl<'d, D: Driver<'d>> Builder<'d, D> { @@ -182,6 +183,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { device_descriptor, config_descriptor, bos_descriptor, + msos_descriptor: None, } } @@ -199,6 +201,7 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { self.bos_descriptor.writer.into_buf(), self.interfaces, self.control_buf, + self.msos_descriptor, ) } @@ -234,6 +237,18 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { iface_count_index, } } + + /// Add an MS OS 2.0 Descriptor Set. + /// + /// Panics if called more than once. + pub fn msos_descriptor(&mut self, msos_descriptor: crate::msos::MsOsDescriptorSet<'d>) { + if self.msos_descriptor.is_some() { + panic!("msos_descriptor already set"); + } + self.msos_descriptor + .insert(msos_descriptor) + .write_bos_capability(&mut self.bos_descriptor); + } } /// 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; pub mod control; pub mod descriptor; mod descriptor_reader; +pub mod msos; pub mod types; use embassy_futures::select::{select, Either}; @@ -135,6 +136,8 @@ struct Inner<'d, D: Driver<'d>> { set_address_pending: bool, interfaces: Vec, MAX_INTERFACE_COUNT>, + + msos_descriptor: Option>, } impl<'d, D: Driver<'d>> UsbDevice<'d, D> { @@ -147,6 +150,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { bos_descriptor: &'d [u8], interfaces: Vec, MAX_INTERFACE_COUNT>, control_buf: &'d mut [u8], + msos_descriptor: Option>, ) -> UsbDevice<'d, D> { // Start the USB bus. // This prevent further allocation by consuming the driver. @@ -170,6 +174,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { address: 0, set_address_pending: false, interfaces, + msos_descriptor, }, } } @@ -603,6 +608,18 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { None => InResponse::Rejected, } } + (RequestType::Vendor, Recipient::Device) => { + if let Some(msos) = &self.msos_descriptor { + if req.request == msos.vendor_code() && req.index == 7 { + // Index 7 retrieves the MS OS Descriptor Set + InResponse::Accepted(msos.descriptor()) + } else { + InResponse::Rejected + } + } else { + InResponse::Rejected + } + } _ => InResponse::Rejected, } } 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 @@ +//! Microsoft OS Descriptors +//! +//! + +#![allow(dead_code)] + +use core::mem::size_of; +use core::ops::Range; + +pub use widestring::{u16cstr, U16CStr}; + +use crate::descriptor::{capability_type, BosWriter}; +use crate::types::InterfaceNumber; + +fn write_u16>(buf: &mut [u8], range: Range, data: T) { + (&mut buf[range]).copy_from_slice(data.into().to_le_bytes().as_slice()) +} + +/// A serialized Microsoft OS 2.0 Descriptor set. +/// +/// Create with [`DeviceDescriptorSetBuilder`]. +pub struct MsOsDescriptorSet<'a> { + descriptor: &'a [u8], + windows_version: u32, + vendor_code: u8, +} + +impl<'a> MsOsDescriptorSet<'a> { + pub fn descriptor(&self) -> &[u8] { + self.descriptor + } + + pub fn vendor_code(&self) -> u8 { + self.vendor_code + } + + pub fn write_bos_capability(&self, bos: &mut BosWriter) { + let windows_version = self.windows_version.to_le_bytes(); + let len = self.descriptor.len().to_le_bytes(); + bos.capability( + capability_type::PLATFORM, + &[ + 0, // reserved + // platform capability UUID, Microsoft OS 2.0 platform compabitility + 0xdf, + 0x60, + 0xdd, + 0xd8, + 0x89, + 0x45, + 0xc7, + 0x4c, + 0x9c, + 0xd2, + 0x65, + 0x9d, + 0x9e, + 0x64, + 0x8a, + 0x9f, + // Minimum compatible Windows version + windows_version[0], + windows_version[1], + windows_version[2], + windows_version[3], + // Descriptor set length + len[0], + len[1], + self.vendor_code, + 0x0, // Device does not support alternate enumeration + ], + ) + } +} + +/// A helper struct to implement the different descriptor set builders. +struct DescriptorSetBuilder<'a> { + used: usize, + buf: &'a mut [u8], +} + +impl<'a> DescriptorSetBuilder<'a> { + pub fn descriptor(&mut self, desc: T) + where + T: Descriptor + 'a, + { + let size = desc.size(); + let start = self.used; + let end = start + size; + desc.write_to(&mut self.buf[start..end]); + self.used += size; + } + + pub fn subset(&mut self, build_subset: impl FnOnce(&mut DescriptorSetBuilder<'_>)) { + self.used += { + let mut subset = DescriptorSetBuilder { + used: 0, + buf: self.remaining(), + }; + build_subset(&mut subset); + subset.used + }; + } + + pub fn remaining(&mut self) -> &mut [u8] { + &mut self.buf[self.used..] + } +} + +pub mod windows_version { + pub const WIN2K: u32 = 0x05000000; + pub const WIN2KSP1: u32 = 0x05000100; + pub const WIN2KSP2: u32 = 0x05000200; + pub const WIN2KSP3: u32 = 0x05000300; + pub const WIN2KSP4: u32 = 0x05000400; + + pub const WINXP: u32 = 0x05010000; + pub const WINXPSP1: u32 = 0x05010100; + pub const WINXPSP2: u32 = 0x05010200; + pub const WINXPSP3: u32 = 0x05010300; + pub const WINXPSP4: u32 = 0x05010400; + + pub const VISTA: u32 = 0x06000000; + pub const VISTASP1: u32 = 0x06000100; + pub const VISTASP2: u32 = 0x06000200; + pub const VISTASP3: u32 = 0x06000300; + pub const VISTASP4: u32 = 0x06000400; + + pub const WIN7: u32 = 0x06010000; + pub const WIN8: u32 = 0x06020000; + /// AKA `NTDDI_WINBLUE` + pub const WIN8_1: u32 = 0x06030000; + pub const WIN10: u32 = 0x0A000000; +} + +/// Helps build a Microsoft OS 2.0 Descriptor set. +/// +/// # Example +/// ```rust +/// # use embassy_usb::types::InterfaceNumber; +/// # use embassy_usb::msos::*; +/// # let cdc_interface = unsafe { core::mem::transmute::(0) }; +/// # let dfu_interface = unsafe { core::mem::transmute::(1) }; +/// let mut buf = [0u8; 256]; +/// let mut builder = DeviceDescriptorSetBuilder::new(&mut buf[..], windows_version::WIN8_1); +/// builder.feature(MinimumRecoveryTimeDescriptor::new(5, 10)); +/// builder.feature(ModelIdDescriptor::new(0xdeadbeef1234u128)); +/// builder.configuration(1, |conf| { +/// conf.function(cdc_interface, |func| { +/// func.winusb_device(); +/// func.feature(VendorRevisionDescriptor::new(1)); +/// }); +/// conf.function(dfu_interface, |func| { +/// func.winusb_device(); +/// func.feature(VendorRevisionDescriptor::new(1)); +/// }); +/// }); +/// ``` +pub struct DeviceDescriptorSetBuilder<'a> { + builder: DescriptorSetBuilder<'a>, + windows_version: u32, + vendor_code: u8, +} + +impl<'a> DeviceDescriptorSetBuilder<'a> { + /// Create a device descriptor set builder. + /// + /// - `windows_version` is an NTDDI version constant that describes a windows version. See the [`windows_version`] + /// module. + /// - `vendor_code` is the vendor request code used to read the MS OS descriptor set. + pub fn new<'b: 'a>(buf: &'b mut [u8], windows_version: u32, vendor_code: u8) -> Self { + let mut builder = DescriptorSetBuilder { used: 0, buf }; + builder.descriptor(DescriptorSetHeader { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (DescriptorSetHeader::TYPE as u16).to_le(), + dwWindowsVersion: windows_version.to_le(), + wTotalLength: 0, + }); + Self { + builder, + windows_version, + vendor_code, + } + } + + /// Add a device-level feature descriptor. + /// + /// Note that some feature descriptors may only be used at the device level in non-composite devices. + pub fn feature(&mut self, desc: T) + where + T: Descriptor + DeviceLevelDescriptor + 'a, + { + self.builder.descriptor(desc) + } + + /// Add a configuration subset. + pub fn configuration(&mut self, configuration: u8, build_conf: impl FnOnce(&mut ConfigurationSubsetBuilder<'_>)) { + let mut cb = ConfigurationSubsetBuilder::new(self.builder.remaining(), configuration); + build_conf(&mut cb); + self.builder.used += cb.finalize(); + } + + /// Finishes writing the data. + pub fn finalize(self) -> MsOsDescriptorSet<'a> { + let used = self.builder.used; + let buf = self.builder.buf; + // Update length in header with final length + let total_len = &mut buf[4..6]; + total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); + + MsOsDescriptorSet { + descriptor: &buf[..used], + windows_version: self.windows_version, + vendor_code: self.vendor_code, + } + } +} + +pub struct ConfigurationSubsetBuilder<'a> { + builder: DescriptorSetBuilder<'a>, +} + +impl<'a> ConfigurationSubsetBuilder<'a> { + pub fn new<'b: 'a>(buf: &'b mut [u8], configuration: u8) -> Self { + let mut builder = DescriptorSetBuilder { used: 0, buf }; + builder.descriptor(ConfigurationSubsetHeader { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (ConfigurationSubsetHeader::TYPE as u16).to_le(), + bConfigurationValue: configuration, + bReserved: 0, + wTotalLength: 0, + }); + Self { builder } + } + + /// Add a function subset. + pub fn function(&mut self, interface: InterfaceNumber, build_func: impl FnOnce(&mut FunctionSubsetBuilder<'_>)) { + let mut fb = FunctionSubsetBuilder::new(self.builder.remaining(), interface); + build_func(&mut fb); + self.builder.used += fb.finalize(); + } + + /// Finishes writing the data. Returns the total number of bytes used by the descriptor set. + pub fn finalize(self) -> usize { + let used = self.builder.used; + let buf = self.builder.buf; + // Update length in header with final length + let total_len = &mut buf[6..8]; + total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); + used + } +} + +pub struct FunctionSubsetBuilder<'a> { + builder: DescriptorSetBuilder<'a>, +} + +impl<'a> FunctionSubsetBuilder<'a> { + pub fn new<'b: 'a>(buf: &'b mut [u8], interface: InterfaceNumber) -> Self { + let mut builder = DescriptorSetBuilder { used: 0, buf }; + builder.descriptor(FunctionSubsetHeader { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (FunctionSubsetHeader::TYPE as u16).to_le(), + bFirstInterface: interface.0, + bReserved: 0, + wSubsetLength: 0, + }); + Self { builder } + } + + /// Add a function-level descriptor. + /// + /// Note that many descriptors can only be used at function-level in a composite device. + pub fn feature(&mut self, desc: T) + where + T: Descriptor + FunctionLevelDescriptor + 'a, + { + self.builder.descriptor(desc) + } + + /// Adds the feature descriptors to configure this function to use the WinUSB driver. + /// + /// Adds a compatible id descriptor "WINUSB" and a registry descriptor that sets the DeviceInterfaceGUID to the + /// USB_DEVICE GUID. + pub fn winusb_device(&mut self) { + self.feature(CompatibleIdFeatureDescriptor::new_winusb()); + self.feature(RegistryPropertyFeatureDescriptor::new_usb_deviceinterfaceguid()); + } + + /// Finishes writing the data. Returns the total number of bytes used by the descriptor set. + pub fn finalize(self) -> usize { + let used = self.builder.used; + let buf = self.builder.buf; + // Update length in header with final length + let total_len = &mut buf[6..8]; + total_len.copy_from_slice((used as u16).to_le_bytes().as_slice()); + used + } +} + +/// A trait for descriptors +pub trait Descriptor: Sized { + const TYPE: DescriptorType; + + /// The size of the descriptor's header. + fn size(&self) -> usize { + size_of::() + } + + fn write_to(&self, buf: &mut [u8]); +} + +/// Copies the data of `t` into `buf`. +/// +/// # Safety +/// The type `T` must be able to be safely cast to `&[u8]`. (e.g. it is a `#[repr(packed)]` struct) +unsafe fn transmute_write_to(t: &T, buf: &mut [u8]) { + let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::()); + assert!(buf.len() >= bytes.len()); + (&mut buf[..bytes.len()]).copy_from_slice(bytes); +} + +/// Table 9. Microsoft OS 2.0 descriptor wDescriptorType values. +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(u16)] +pub enum DescriptorType { + SetHeaderDescriptor = 0, + SubsetHeaderConfiguration = 1, + SubsetHeaderFunction = 2, + FeatureCompatibleId = 3, + FeatureRegProperty = 4, + FeatureMinResumeTime = 5, + FeatureModelId = 6, + FeatureCcgpDevice = 7, + FeatureVendorRevision = 8, +} + +/// Table 5. Descriptor set information structure. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct DescriptorSetInformation { + dwWindowsVersion: u32, + wMSOSDescriptorSetTotalLength: u16, + bMS_VendorCode: u8, + bAltEnumCode: u8, +} + +/// Table 4. Microsoft OS 2.0 platform capability descriptor header. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct PlatformDescriptor { + bLength: u8, + bDescriptorType: u8, + bDevCapabilityType: u8, + bReserved: u8, + platformCapabilityUUID: [u8; 16], + descriptor_set_information: DescriptorSetInformation, +} + +/// Table 10. Microsoft OS 2.0 descriptor set header. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct DescriptorSetHeader { + wLength: u16, + wDescriptorType: u16, + dwWindowsVersion: u32, + wTotalLength: u16, +} + +impl Descriptor for DescriptorSetHeader { + const TYPE: DescriptorType = DescriptorType::SetHeaderDescriptor; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +/// Table 11. Configuration subset header. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct ConfigurationSubsetHeader { + wLength: u16, + wDescriptorType: u16, + bConfigurationValue: u8, + bReserved: u8, + wTotalLength: u16, +} + +impl Descriptor for ConfigurationSubsetHeader { + const TYPE: DescriptorType = DescriptorType::SubsetHeaderConfiguration; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +/// Table 12. Function subset header. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct FunctionSubsetHeader { + wLength: u16, + wDescriptorType: u16, + bFirstInterface: u8, + bReserved: u8, + wSubsetLength: u16, +} + +impl Descriptor for FunctionSubsetHeader { + const TYPE: DescriptorType = DescriptorType::SubsetHeaderFunction; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +// Feature Descriptors + +/// A marker trait for feature descriptors that are valid at the device level. +pub trait DeviceLevelDescriptor {} + +/// A marker trait for feature descriptors that are valid at the function level. +pub trait FunctionLevelDescriptor { + /// `true` when the feature descriptor may only be used at the function level in composite devices. + const COMPOSITE_ONLY: bool = false; +} + +/// Table 13. Microsoft OS 2.0 compatible ID descriptor. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct CompatibleIdFeatureDescriptor { + wLength: u16, + wDescriptorType: u16, + compatibleId: [u8; 8], + subCompatibleId: [u8; 8], +} + +impl DeviceLevelDescriptor for CompatibleIdFeatureDescriptor {} +impl FunctionLevelDescriptor for CompatibleIdFeatureDescriptor { + const COMPOSITE_ONLY: bool = true; +} + +impl Descriptor for CompatibleIdFeatureDescriptor { + const TYPE: DescriptorType = DescriptorType::FeatureCompatibleId; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +impl CompatibleIdFeatureDescriptor { + /// Creates a compatible ID descriptor that signals WINUSB driver compatiblilty. + pub fn new_winusb() -> Self { + Self::new_raw([b'W', b'I', b'N', b'U', b'S', b'B', 0, 0], [0u8; 8]) + } + + /// The ids must be 8 ASCII bytes or fewer. + pub fn new(compatible_id: &str, sub_compatible_id: &str) -> Self { + assert!(compatible_id.len() <= 8 && sub_compatible_id.len() <= 8); + let mut cid = [0u8; 8]; + (&mut cid[..compatible_id.len()]).copy_from_slice(compatible_id.as_bytes()); + let mut scid = [0u8; 8]; + (&mut scid[..sub_compatible_id.len()]).copy_from_slice(sub_compatible_id.as_bytes()); + Self::new_raw(cid, scid) + } + + pub fn new_raw(compatible_id: [u8; 8], sub_compatible_id: [u8; 8]) -> Self { + Self { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + compatibleId: compatible_id, + subCompatibleId: sub_compatible_id, + } + } +} + +/// Table 14. Microsoft OS 2.0 registry property descriptor +#[allow(non_snake_case)] +pub struct RegistryPropertyFeatureDescriptor<'a> { + wLength: u16, + wDescriptorType: u16, + wPropertyDataType: u16, + wPropertyNameLength: u16, + PropertyName: &'a [u8], + wPropertyDataLength: u16, + PropertyData: &'a [u8], +} + +impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {} +impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> { + const COMPOSITE_ONLY: bool = true; +} + +impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> { + const TYPE: DescriptorType = DescriptorType::FeatureRegProperty; + fn size(&self) -> usize { + 10 + self.PropertyName.len() + self.PropertyData.len() + } + fn write_to(&self, buf: &mut [u8]) { + assert!(buf.len() >= self.size()); + assert!(self.wPropertyNameLength as usize == self.PropertyName.len()); + assert!(self.wPropertyDataLength as usize == self.PropertyData.len()); + write_u16(buf, 0..2, self.wLength); + write_u16(buf, 2..4, self.wDescriptorType); + write_u16(buf, 4..6, self.wPropertyDataType); + write_u16(buf, 6..8, self.wPropertyNameLength); + let pne = 8 + self.PropertyName.len(); + (&mut buf[8..pne]).copy_from_slice(self.PropertyName); + let pds = pne + 2; + let pde = pds + self.PropertyData.len(); + write_u16(buf, pne..pds, self.wPropertyDataLength); + (&mut buf[pds..pde]).copy_from_slice(self.PropertyData); + } +} + +impl<'a> RegistryPropertyFeatureDescriptor<'a> { + /// A registry property. + /// + /// `name` should be a NUL-terminated 16-bit Unicode string. + pub fn new_raw<'n: 'a, 'd: 'a>(name: &'a [u8], data: &'d [u8], data_type: PropertyDataType) -> Self { + Self { + wLength: ((10 + name.len() + data.len()) as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + wPropertyDataType: (data_type as u16).to_le(), + wPropertyNameLength: (name.len() as u16).to_le(), + PropertyName: name, + wPropertyDataLength: (data.len() as u16).to_le(), + PropertyData: data, + } + } + + fn u16str_bytes(s: &U16CStr) -> &[u8] { + unsafe { core::slice::from_raw_parts(s.as_ptr() as *const u8, (s.len() + 1) * 2) } + } + + /// A registry property that sets the DeviceInterfaceGUID to the device interface class for USB devices which are + /// attached to a USB hub. + pub fn new_usb_deviceinterfaceguid() -> Self { + // Can't use defmt::panic in constant expressions (inside u16cstr!) + macro_rules! panic { + ($($x:tt)*) => { + { + ::core::panic!($($x)*); + } + }; + } + + Self::new_string( + u16cstr!("DeviceInterfaceGUID"), + u16cstr!("{A5DCBF10-6530-11D2-901F-00C04FB951ED}"), + ) + } + + /// A registry property containing a NUL-terminated 16-bit Unicode string. + pub fn new_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { + Self::new_raw(Self::u16str_bytes(name), Self::u16str_bytes(data), PropertyDataType::Sz) + } + + /// A registry property containing a NUL-terminated 16-bit Unicode string that expands environment variables. + pub fn new_string_expand<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { + Self::new_raw( + Self::u16str_bytes(name), + Self::u16str_bytes(data), + PropertyDataType::ExpandSz, + ) + } + + /// A registry property containing a NUL-terminated 16-bit Unicode string that contains a symbolic link. + pub fn new_link<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d U16CStr) -> Self { + Self::new_raw( + Self::u16str_bytes(name), + Self::u16str_bytes(data), + PropertyDataType::Link, + ) + } + + /// A registry property containing multiple NUL-terminated 16-bit Unicode strings. + pub fn new_multi_string<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u16]) -> Self { + Self::new_raw( + Self::u16str_bytes(name), + unsafe { core::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 2) }, + PropertyDataType::RegMultiSz, + ) + } + + /// A registry property containing binary data. + pub fn new_binary<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d [u8]) -> Self { + Self::new_raw(Self::u16str_bytes(name), data, PropertyDataType::Binary) + } + + /// A registry property containing a Little-Endian 32-bit integer. + /// + /// The function assumes that `data` is already little-endian, it does not convert it. + pub fn new_dword_le<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d i32) -> Self { + Self::new_raw( + Self::u16str_bytes(name), + unsafe { core::slice::from_raw_parts(data as *const i32 as *const u8, size_of::()) }, + PropertyDataType::DwordLittleEndian, + ) + } + + /// A registry property containing a big-endian 32-bit integer. + /// + /// The function assumes that `data` is already big-endian, it does not convert it. + pub fn new_dword_be<'n: 'a, 'd: 'a>(name: &'n U16CStr, data: &'d i32) -> Self { + Self::new_raw( + Self::u16str_bytes(name), + unsafe { core::slice::from_raw_parts(data as *const i32 as *const u8, size_of::()) }, + PropertyDataType::DwordBigEndian, + ) + } +} + +/// Table 15. wPropertyDataType values for the Microsoft OS 2.0 registry property descriptor. +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(u16)] +pub enum PropertyDataType { + Sz = 1, + ExpandSz = 2, + Binary = 3, + DwordLittleEndian = 4, + DwordBigEndian = 5, + Link = 6, + RegMultiSz = 7, +} + +/// Table 16. Microsoft OS 2.0 minimum USB recovery time descriptor. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct MinimumRecoveryTimeDescriptor { + wLength: u16, + wDescriptorType: u16, + bResumeRecoveryTime: u8, + bResumeSignalingTime: u8, +} + +impl DeviceLevelDescriptor for MinimumRecoveryTimeDescriptor {} + +impl Descriptor for MinimumRecoveryTimeDescriptor { + const TYPE: DescriptorType = DescriptorType::FeatureMinResumeTime; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +impl MinimumRecoveryTimeDescriptor { + /// Times are in milliseconds. + /// + /// `resume_recovery_time` must be >= 0 and <= 10. + /// `resume_signaling_time` must be >= 1 and <= 20. + pub fn new(resume_recovery_time: u8, resume_signaling_time: u8) -> Self { + assert!(resume_recovery_time <= 10); + assert!(resume_signaling_time >= 1 && resume_signaling_time <= 20); + Self { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + bResumeRecoveryTime: resume_recovery_time, + bResumeSignalingTime: resume_signaling_time, + } + } +} + +/// Table 17. Microsoft OS 2.0 model ID descriptor. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct ModelIdDescriptor { + wLength: u16, + wDescriptorType: u16, + modelId: [u8; 16], +} + +impl DeviceLevelDescriptor for ModelIdDescriptor {} + +impl Descriptor for ModelIdDescriptor { + const TYPE: DescriptorType = DescriptorType::FeatureModelId; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +impl ModelIdDescriptor { + pub fn new(model_id: u128) -> Self { + Self::new_bytes(model_id.to_le_bytes()) + } + + pub fn new_bytes(model_id: [u8; 16]) -> Self { + Self { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + modelId: model_id, + } + } +} + +/// Table 18. Microsoft OS 2.0 CCGP device descriptor. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct CcgpDeviceDescriptor { + wLength: u16, + wDescriptorType: u16, +} + +impl DeviceLevelDescriptor for CcgpDeviceDescriptor {} + +impl Descriptor for CcgpDeviceDescriptor { + const TYPE: DescriptorType = DescriptorType::FeatureCcgpDevice; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +impl CcgpDeviceDescriptor { + pub fn new() -> Self { + Self { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + } + } +} + +/// Table 19. Microsoft OS 2.0 vendor revision descriptor. +#[allow(non_snake_case)] +#[repr(C, packed(1))] +pub struct VendorRevisionDescriptor { + wLength: u16, + wDescriptorType: u16, + /// Revision number associated with the descriptor set. Modify it every time you add/modify a registry property or + /// other MSOS descriptor. Shell set to greater than or equal to 1. + VendorRevision: u16, +} + +impl DeviceLevelDescriptor for VendorRevisionDescriptor {} +impl FunctionLevelDescriptor for VendorRevisionDescriptor {} + +impl Descriptor for VendorRevisionDescriptor { + const TYPE: DescriptorType = DescriptorType::FeatureVendorRevision; + fn write_to(&self, buf: &mut [u8]) { + unsafe { transmute_write_to(self, buf) } + } +} + +impl VendorRevisionDescriptor { + pub fn new(revision: u16) -> Self { + assert!(revision >= 1); + Self { + wLength: (size_of::() as u16).to_le(), + wDescriptorType: (Self::TYPE as u16).to_le(), + VendorRevision: revision.to_le(), + } + } +}