2023-02-02 22:13:16 +01:00
|
|
|
#![cfg(feature = "msos-descriptor")]
|
|
|
|
|
2023-01-12 21:59:25 +01:00
|
|
|
//! Microsoft OS Descriptors
|
|
|
|
//!
|
|
|
|
//! <https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification>
|
|
|
|
|
|
|
|
use core::mem::size_of;
|
|
|
|
|
2023-02-02 22:13:16 +01:00
|
|
|
use super::{capability_type, BosWriter};
|
2023-02-07 23:45:01 +01:00
|
|
|
use crate::types::InterfaceNumber;
|
2023-01-12 21:59:25 +01:00
|
|
|
|
|
|
|
/// A serialized Microsoft OS 2.0 Descriptor set.
|
|
|
|
///
|
|
|
|
/// Create with [`DeviceDescriptorSetBuilder`].
|
2023-02-02 22:13:16 +01:00
|
|
|
pub struct MsOsDescriptorSet<'d> {
|
|
|
|
descriptor: &'d [u8],
|
2023-01-12 21:59:25 +01:00
|
|
|
vendor_code: u8,
|
|
|
|
}
|
|
|
|
|
2023-02-02 22:13:16 +01:00
|
|
|
impl<'d> MsOsDescriptorSet<'d> {
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Gets the raw bytes of the MS OS descriptor
|
2023-01-12 21:59:25 +01:00
|
|
|
pub fn descriptor(&self) -> &[u8] {
|
|
|
|
self.descriptor
|
|
|
|
}
|
|
|
|
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Gets the vendor code used by the host to retrieve the MS OS descriptor
|
2023-01-12 21:59:25 +01:00
|
|
|
pub fn vendor_code(&self) -> u8 {
|
|
|
|
self.vendor_code
|
|
|
|
}
|
|
|
|
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Returns `true` if no MS OS descriptor data is available
|
2023-02-02 22:13:16 +01:00
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.descriptor.is_empty()
|
|
|
|
}
|
2023-03-27 14:19:00 +02:00
|
|
|
|
|
|
|
/// Returns the length of the descriptor field
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.descriptor.len()
|
|
|
|
}
|
2023-02-02 22:13:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Writes a Microsoft OS 2.0 Descriptor set into a buffer.
|
|
|
|
pub struct MsOsDescriptorWriter<'d> {
|
2023-02-07 20:19:51 +01:00
|
|
|
buf: &'d mut [u8],
|
2023-02-02 22:13:16 +01:00
|
|
|
|
|
|
|
position: usize,
|
|
|
|
config_mark: Option<usize>,
|
|
|
|
function_mark: Option<usize>,
|
|
|
|
vendor_code: u8,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'d> MsOsDescriptorWriter<'d> {
|
|
|
|
pub(crate) fn new(buf: &'d mut [u8]) -> Self {
|
|
|
|
MsOsDescriptorWriter {
|
|
|
|
buf,
|
|
|
|
position: 0,
|
|
|
|
config_mark: None,
|
|
|
|
function_mark: None,
|
|
|
|
vendor_code: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn build(mut self, bos: &mut BosWriter) -> MsOsDescriptorSet<'d> {
|
|
|
|
self.end();
|
|
|
|
|
|
|
|
if self.is_empty() {
|
|
|
|
MsOsDescriptorSet {
|
|
|
|
descriptor: &[],
|
|
|
|
vendor_code: 0,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.write_bos(bos);
|
|
|
|
MsOsDescriptorSet {
|
|
|
|
descriptor: &self.buf[..self.position],
|
|
|
|
vendor_code: self.vendor_code,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Returns `true` if the MS OS descriptor header has not yet been written
|
2023-02-02 22:13:16 +01:00
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.position == 0
|
|
|
|
}
|
|
|
|
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Returns `true` if a configuration subset header has been started
|
2023-02-02 22:13:16 +01:00
|
|
|
pub fn is_in_config_subset(&self) -> bool {
|
|
|
|
self.config_mark.is_some()
|
|
|
|
}
|
|
|
|
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Returns `true` if a function subset header has been started and not yet ended
|
2023-02-02 22:13:16 +01:00
|
|
|
pub fn is_in_function_subset(&self) -> bool {
|
|
|
|
self.function_mark.is_some()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Write the MS OS descriptor set header.
|
|
|
|
///
|
|
|
|
/// - `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 header(&mut self, windows_version: u32, vendor_code: u8) {
|
|
|
|
assert!(self.is_empty(), "You can only call MsOsDescriptorWriter::header once");
|
|
|
|
self.write(DescriptorSetHeader::new(windows_version));
|
|
|
|
self.vendor_code = 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.
|
|
|
|
/// Those features must be written before the first call to [`Self::configuration`].
|
|
|
|
pub fn device_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) {
|
|
|
|
assert!(
|
|
|
|
!self.is_empty(),
|
|
|
|
"device features may only be added after the header is written"
|
|
|
|
);
|
|
|
|
assert!(
|
|
|
|
self.config_mark.is_none(),
|
|
|
|
"device features must be added before the first configuration subset"
|
|
|
|
);
|
|
|
|
self.write(desc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Add a configuration subset.
|
|
|
|
pub fn configuration(&mut self, config: u8) {
|
|
|
|
assert!(
|
|
|
|
!self.is_empty(),
|
|
|
|
"MsOsDescriptorWriter: configuration must be called after header"
|
|
|
|
);
|
|
|
|
Self::end_subset::<ConfigurationSubsetHeader>(self.buf, self.position, &mut self.config_mark);
|
|
|
|
self.config_mark = Some(self.position);
|
|
|
|
self.write(ConfigurationSubsetHeader::new(config));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Add a function subset.
|
2023-02-07 23:45:01 +01:00
|
|
|
pub fn function(&mut self, first_interface: InterfaceNumber) {
|
2023-02-02 22:13:16 +01:00
|
|
|
assert!(
|
|
|
|
self.config_mark.is_some(),
|
|
|
|
"MsOsDescriptorWriter: function subset requires a configuration subset"
|
|
|
|
);
|
|
|
|
self.end_function();
|
|
|
|
self.function_mark = Some(self.position);
|
|
|
|
self.write(FunctionSubsetHeader::new(first_interface));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Add a function level feature descriptor.
|
|
|
|
///
|
|
|
|
/// Note that some features may only be used at the function level. Those features must be written after a call
|
|
|
|
/// to [`Self::function`].
|
|
|
|
pub fn function_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) {
|
|
|
|
assert!(
|
|
|
|
self.function_mark.is_some(),
|
|
|
|
"function features may only be added to a function subset"
|
|
|
|
);
|
|
|
|
self.write(desc);
|
|
|
|
}
|
|
|
|
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Ends the current function subset (if any)
|
2023-02-02 22:13:16 +01:00
|
|
|
pub fn end_function(&mut self) {
|
|
|
|
Self::end_subset::<FunctionSubsetHeader>(self.buf, self.position, &mut self.function_mark);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write<T: Descriptor>(&mut self, desc: T) {
|
|
|
|
desc.write_to(&mut self.buf[self.position..]);
|
|
|
|
self.position += desc.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn end_subset<T: DescriptorSet>(buf: &mut [u8], position: usize, mark: &mut Option<usize>) {
|
|
|
|
if let Some(mark) = mark.take() {
|
|
|
|
let len = position - mark;
|
|
|
|
let p = mark + T::LENGTH_OFFSET;
|
|
|
|
buf[p..(p + 2)].copy_from_slice(&(len as u16).to_le_bytes());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn end(&mut self) {
|
|
|
|
if self.position > 0 {
|
|
|
|
Self::end_subset::<FunctionSubsetHeader>(self.buf, self.position, &mut self.function_mark);
|
|
|
|
Self::end_subset::<ConfigurationSubsetHeader>(self.buf, self.position, &mut self.config_mark);
|
|
|
|
Self::end_subset::<DescriptorSetHeader>(self.buf, self.position, &mut Some(0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_bos(&mut self, bos: &mut BosWriter) {
|
|
|
|
let windows_version = &self.buf[4..8];
|
|
|
|
let len = (self.position as u16).to_le_bytes();
|
2023-01-12 21:59:25 +01:00
|
|
|
bos.capability(
|
|
|
|
capability_type::PLATFORM,
|
|
|
|
&[
|
|
|
|
0, // reserved
|
2023-05-08 23:25:01 +02:00
|
|
|
// platform capability UUID, Microsoft OS 2.0 platform compatibility
|
2023-01-12 21:59:25 +01:00
|
|
|
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
|
|
|
|
],
|
2023-02-02 22:13:16 +01:00
|
|
|
);
|
2023-01-12 21:59:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Microsoft Windows version codes
|
|
|
|
///
|
|
|
|
/// Windows 8.1 is the minimum version allowed for MS OS 2.0 descriptors.
|
2023-01-12 21:59:25 +01:00
|
|
|
pub mod windows_version {
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Windows 8.1 (aka `NTDDI_WINBLUE`)
|
2023-01-12 21:59:25 +01:00
|
|
|
pub const WIN8_1: u32 = 0x06030000;
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Windows 10
|
2023-01-12 21:59:25 +01:00
|
|
|
pub const WIN10: u32 = 0x0A000000;
|
|
|
|
}
|
|
|
|
|
2023-02-02 22:13:16 +01:00
|
|
|
mod sealed {
|
|
|
|
use core::mem::size_of;
|
2023-01-12 21:59:25 +01:00
|
|
|
|
2023-02-02 22:13:16 +01:00
|
|
|
/// A trait for descriptors
|
|
|
|
pub trait Descriptor: Sized {
|
|
|
|
const TYPE: super::DescriptorType;
|
2023-01-12 21:59:25 +01:00
|
|
|
|
2023-02-02 22:13:16 +01:00
|
|
|
/// The size of the descriptor's header.
|
|
|
|
fn size(&self) -> usize {
|
|
|
|
size_of::<Self>()
|
2023-01-12 21:59:25 +01:00
|
|
|
}
|
|
|
|
|
2023-02-02 22:13:16 +01:00
|
|
|
fn write_to(&self, buf: &mut [u8]);
|
2023-01-12 21:59:25 +01:00
|
|
|
}
|
|
|
|
|
2023-02-02 22:13:16 +01:00
|
|
|
pub trait DescriptorSet: Descriptor {
|
|
|
|
const LENGTH_OFFSET: usize;
|
2023-01-12 21:59:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-02 22:13:16 +01:00
|
|
|
use sealed::*;
|
2023-01-12 21:59:25 +01:00
|
|
|
|
|
|
|
/// 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: Sized>(t: &T, buf: &mut [u8]) {
|
|
|
|
let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::<T>());
|
2023-02-07 20:19:51 +01:00
|
|
|
assert!(buf.len() >= bytes.len(), "MS OS descriptor buffer full");
|
2023-01-12 21:59:25 +01:00
|
|
|
(&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 {
|
2023-02-07 20:19:51 +01:00
|
|
|
/// MS OS descriptor set header
|
2023-01-12 21:59:25 +01:00
|
|
|
SetHeaderDescriptor = 0,
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Configuration subset header
|
2023-01-12 21:59:25 +01:00
|
|
|
SubsetHeaderConfiguration = 1,
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Function subset header
|
2023-01-12 21:59:25 +01:00
|
|
|
SubsetHeaderFunction = 2,
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Compatible device ID feature descriptor
|
2023-01-12 21:59:25 +01:00
|
|
|
FeatureCompatibleId = 3,
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Registry property feature descriptor
|
2023-01-12 21:59:25 +01:00
|
|
|
FeatureRegProperty = 4,
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Minimum USB resume time feature descriptor
|
2023-01-12 21:59:25 +01:00
|
|
|
FeatureMinResumeTime = 5,
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Vendor revision feature descriptor
|
2023-01-12 21:59:25 +01:00
|
|
|
FeatureModelId = 6,
|
2023-02-07 20:19:51 +01:00
|
|
|
/// CCGP device descriptor feature descriptor
|
2023-01-12 21:59:25 +01:00
|
|
|
FeatureCcgpDevice = 7,
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Vendor revision feature descriptor
|
2023-01-12 21:59:25 +01:00
|
|
|
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,
|
|
|
|
}
|
|
|
|
|
2023-02-02 22:13:16 +01:00
|
|
|
impl DescriptorSetHeader {
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Creates a MS OS descriptor set header.
|
|
|
|
///
|
|
|
|
/// `windows_version` is the minimum Windows version the descriptor set can apply to.
|
2023-02-02 22:13:16 +01:00
|
|
|
pub fn new(windows_version: u32) -> Self {
|
|
|
|
DescriptorSetHeader {
|
|
|
|
wLength: (size_of::<Self>() as u16).to_le(),
|
|
|
|
wDescriptorType: (Self::TYPE as u16).to_le(),
|
|
|
|
dwWindowsVersion: windows_version.to_le(),
|
|
|
|
wTotalLength: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-12 21:59:25 +01:00
|
|
|
impl Descriptor for DescriptorSetHeader {
|
|
|
|
const TYPE: DescriptorType = DescriptorType::SetHeaderDescriptor;
|
|
|
|
fn write_to(&self, buf: &mut [u8]) {
|
|
|
|
unsafe { transmute_write_to(self, buf) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-02 22:13:16 +01:00
|
|
|
impl DescriptorSet for DescriptorSetHeader {
|
|
|
|
const LENGTH_OFFSET: usize = 8;
|
|
|
|
}
|
|
|
|
|
2023-01-12 21:59:25 +01:00
|
|
|
/// 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,
|
|
|
|
}
|
|
|
|
|
2023-02-02 22:13:16 +01:00
|
|
|
impl ConfigurationSubsetHeader {
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Creates a configuration subset header
|
2023-02-02 22:13:16 +01:00
|
|
|
pub fn new(config: u8) -> Self {
|
|
|
|
ConfigurationSubsetHeader {
|
|
|
|
wLength: (size_of::<Self>() as u16).to_le(),
|
|
|
|
wDescriptorType: (Self::TYPE as u16).to_le(),
|
|
|
|
bConfigurationValue: config,
|
|
|
|
bReserved: 0,
|
|
|
|
wTotalLength: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-12 21:59:25 +01:00
|
|
|
impl Descriptor for ConfigurationSubsetHeader {
|
|
|
|
const TYPE: DescriptorType = DescriptorType::SubsetHeaderConfiguration;
|
|
|
|
fn write_to(&self, buf: &mut [u8]) {
|
|
|
|
unsafe { transmute_write_to(self, buf) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-02 22:13:16 +01:00
|
|
|
impl DescriptorSet for ConfigurationSubsetHeader {
|
|
|
|
const LENGTH_OFFSET: usize = 6;
|
|
|
|
}
|
|
|
|
|
2023-01-12 21:59:25 +01:00
|
|
|
/// Table 12. Function subset header.
|
|
|
|
#[allow(non_snake_case)]
|
|
|
|
#[repr(C, packed(1))]
|
|
|
|
pub struct FunctionSubsetHeader {
|
|
|
|
wLength: u16,
|
|
|
|
wDescriptorType: u16,
|
2023-02-07 23:45:01 +01:00
|
|
|
bFirstInterface: InterfaceNumber,
|
2023-01-12 21:59:25 +01:00
|
|
|
bReserved: u8,
|
|
|
|
wSubsetLength: u16,
|
|
|
|
}
|
|
|
|
|
2023-02-02 22:13:16 +01:00
|
|
|
impl FunctionSubsetHeader {
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Creates a function subset header
|
2023-02-07 23:45:01 +01:00
|
|
|
pub fn new(first_interface: InterfaceNumber) -> Self {
|
2023-02-02 22:13:16 +01:00
|
|
|
FunctionSubsetHeader {
|
|
|
|
wLength: (size_of::<Self>() as u16).to_le(),
|
|
|
|
wDescriptorType: (Self::TYPE as u16).to_le(),
|
|
|
|
bFirstInterface: first_interface,
|
|
|
|
bReserved: 0,
|
|
|
|
wSubsetLength: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-12 21:59:25 +01:00
|
|
|
impl Descriptor for FunctionSubsetHeader {
|
|
|
|
const TYPE: DescriptorType = DescriptorType::SubsetHeaderFunction;
|
|
|
|
fn write_to(&self, buf: &mut [u8]) {
|
|
|
|
unsafe { transmute_write_to(self, buf) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-02 22:13:16 +01:00
|
|
|
impl DescriptorSet for FunctionSubsetHeader {
|
|
|
|
const LENGTH_OFFSET: usize = 6;
|
|
|
|
}
|
|
|
|
|
2023-01-12 21:59:25 +01:00
|
|
|
// Feature Descriptors
|
|
|
|
|
|
|
|
/// A marker trait for feature descriptors that are valid at the device level.
|
2023-02-02 22:13:16 +01:00
|
|
|
pub trait DeviceLevelDescriptor: Descriptor {}
|
2023-01-12 21:59:25 +01:00
|
|
|
|
|
|
|
/// A marker trait for feature descriptors that are valid at the function level.
|
2023-02-02 22:13:16 +01:00
|
|
|
pub trait FunctionLevelDescriptor: Descriptor {}
|
2023-01-12 21:59:25 +01:00
|
|
|
|
|
|
|
/// 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 {}
|
2023-02-02 22:13:16 +01:00
|
|
|
impl FunctionLevelDescriptor for CompatibleIdFeatureDescriptor {}
|
2023-01-12 21:59:25 +01:00
|
|
|
|
|
|
|
impl Descriptor for CompatibleIdFeatureDescriptor {
|
|
|
|
const TYPE: DescriptorType = DescriptorType::FeatureCompatibleId;
|
|
|
|
fn write_to(&self, buf: &mut [u8]) {
|
|
|
|
unsafe { transmute_write_to(self, buf) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CompatibleIdFeatureDescriptor {
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Creates a compatible ID feature descriptor
|
|
|
|
///
|
2023-01-12 21:59:25 +01:00
|
|
|
/// 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)
|
|
|
|
}
|
|
|
|
|
2023-02-07 20:19:51 +01:00
|
|
|
fn new_raw(compatible_id: [u8; 8], sub_compatible_id: [u8; 8]) -> Self {
|
2023-01-12 21:59:25 +01:00
|
|
|
Self {
|
|
|
|
wLength: (size_of::<Self>() 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> {
|
2023-02-07 20:19:51 +01:00
|
|
|
name: &'a str,
|
|
|
|
data: PropertyData<'a>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Data values that can be encoded into a registry property descriptor
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
|
|
pub enum PropertyData<'a> {
|
|
|
|
/// A registry property containing a string.
|
|
|
|
Sz(&'a str),
|
|
|
|
/// A registry property containing a string that expands environment variables.
|
|
|
|
ExpandSz(&'a str),
|
2023-01-12 21:59:25 +01:00
|
|
|
/// A registry property containing binary data.
|
2023-02-07 20:19:51 +01:00
|
|
|
Binary(&'a [u8]),
|
|
|
|
/// A registry property containing a little-endian 32-bit integer.
|
|
|
|
DwordLittleEndian(u32),
|
|
|
|
/// A registry property containing a big-endian 32-bit integer.
|
|
|
|
DwordBigEndian(u32),
|
|
|
|
/// A registry property containing a string that contains a symbolic link.
|
|
|
|
Link(&'a str),
|
|
|
|
/// A registry property containing multiple strings.
|
|
|
|
RegMultiSz(&'a [&'a str]),
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_bytes(val: &[u8], buf: &mut [u8]) -> usize {
|
|
|
|
assert!(buf.len() >= val.len());
|
|
|
|
buf[..val.len()].copy_from_slice(val);
|
|
|
|
val.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_utf16(val: &str, buf: &mut [u8]) -> usize {
|
|
|
|
let mut pos = 0;
|
|
|
|
for c in val.encode_utf16() {
|
|
|
|
pos += write_bytes(&c.to_le_bytes(), &mut buf[pos..]);
|
|
|
|
}
|
|
|
|
pos + write_bytes(&0u16.to_le_bytes(), &mut buf[pos..])
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> PropertyData<'a> {
|
|
|
|
/// Gets the `PropertyDataType` for this property value
|
|
|
|
pub fn kind(&self) -> PropertyDataType {
|
|
|
|
match self {
|
|
|
|
PropertyData::Sz(_) => PropertyDataType::Sz,
|
|
|
|
PropertyData::ExpandSz(_) => PropertyDataType::ExpandSz,
|
|
|
|
PropertyData::Binary(_) => PropertyDataType::Binary,
|
|
|
|
PropertyData::DwordLittleEndian(_) => PropertyDataType::DwordLittleEndian,
|
|
|
|
PropertyData::DwordBigEndian(_) => PropertyDataType::DwordBigEndian,
|
|
|
|
PropertyData::Link(_) => PropertyDataType::Link,
|
|
|
|
PropertyData::RegMultiSz(_) => PropertyDataType::RegMultiSz,
|
|
|
|
}
|
2023-01-12 21:59:25 +01:00
|
|
|
}
|
|
|
|
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Gets the size (in bytes) of this property value when encoded.
|
|
|
|
pub fn size(&self) -> usize {
|
|
|
|
match self {
|
|
|
|
PropertyData::Sz(val) | PropertyData::ExpandSz(val) | PropertyData::Link(val) => {
|
|
|
|
core::mem::size_of::<u16>() * (val.encode_utf16().count() + 1)
|
|
|
|
}
|
|
|
|
PropertyData::Binary(val) => val.len(),
|
|
|
|
PropertyData::DwordLittleEndian(val) | PropertyData::DwordBigEndian(val) => core::mem::size_of_val(val),
|
|
|
|
PropertyData::RegMultiSz(val) => {
|
|
|
|
core::mem::size_of::<u16>() * val.iter().map(|x| x.encode_utf16().count() + 1).sum::<usize>() + 1
|
|
|
|
}
|
|
|
|
}
|
2023-01-12 21:59:25 +01:00
|
|
|
}
|
|
|
|
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Encodes the data for this property value and writes it to `buf`.
|
|
|
|
pub fn write(&self, buf: &mut [u8]) -> usize {
|
|
|
|
match self {
|
|
|
|
PropertyData::Sz(val) | PropertyData::ExpandSz(val) | PropertyData::Link(val) => write_utf16(val, buf),
|
|
|
|
PropertyData::Binary(val) => write_bytes(val, buf),
|
|
|
|
PropertyData::DwordLittleEndian(val) => write_bytes(&val.to_le_bytes(), buf),
|
|
|
|
PropertyData::DwordBigEndian(val) => write_bytes(&val.to_be_bytes(), buf),
|
|
|
|
PropertyData::RegMultiSz(val) => {
|
|
|
|
let mut pos = 0;
|
|
|
|
for s in *val {
|
|
|
|
pos += write_utf16(s, &mut buf[pos..]);
|
|
|
|
}
|
|
|
|
pos + write_bytes(&0u16.to_le_bytes(), &mut buf[pos..])
|
|
|
|
}
|
|
|
|
}
|
2023-01-12 21:59:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Table 15. wPropertyDataType values for the Microsoft OS 2.0 registry property descriptor.
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
|
|
#[repr(u16)]
|
|
|
|
pub enum PropertyDataType {
|
2023-02-07 20:19:51 +01:00
|
|
|
/// A registry property containing a string.
|
2023-01-12 21:59:25 +01:00
|
|
|
Sz = 1,
|
2023-02-07 20:19:51 +01:00
|
|
|
/// A registry property containing a string that expands environment variables.
|
2023-01-12 21:59:25 +01:00
|
|
|
ExpandSz = 2,
|
2023-02-07 20:19:51 +01:00
|
|
|
/// A registry property containing binary data.
|
2023-01-12 21:59:25 +01:00
|
|
|
Binary = 3,
|
2023-02-07 20:19:51 +01:00
|
|
|
/// A registry property containing a little-endian 32-bit integer.
|
2023-01-12 21:59:25 +01:00
|
|
|
DwordLittleEndian = 4,
|
2023-02-07 20:19:51 +01:00
|
|
|
/// A registry property containing a big-endian 32-bit integer.
|
2023-01-12 21:59:25 +01:00
|
|
|
DwordBigEndian = 5,
|
2023-02-07 20:19:51 +01:00
|
|
|
/// A registry property containing a string that contains a symbolic link.
|
2023-01-12 21:59:25 +01:00
|
|
|
Link = 6,
|
2023-02-07 20:19:51 +01:00
|
|
|
/// A registry property containing multiple strings.
|
2023-01-12 21:59:25 +01:00
|
|
|
RegMultiSz = 7,
|
|
|
|
}
|
|
|
|
|
2023-02-07 20:19:51 +01:00
|
|
|
impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {}
|
|
|
|
impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {}
|
|
|
|
|
|
|
|
impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> {
|
|
|
|
const TYPE: DescriptorType = DescriptorType::FeatureRegProperty;
|
|
|
|
|
|
|
|
fn size(&self) -> usize {
|
|
|
|
10 + self.name_size() + self.data.size()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_to(&self, buf: &mut [u8]) {
|
|
|
|
assert!(buf.len() >= self.size(), "MS OS descriptor buffer full");
|
|
|
|
|
|
|
|
let mut pos = 0;
|
|
|
|
pos += write_bytes(&(self.size() as u16).to_le_bytes(), &mut buf[pos..]);
|
|
|
|
pos += write_bytes(&(Self::TYPE as u16).to_le_bytes(), &mut buf[pos..]);
|
|
|
|
pos += write_bytes(&(self.data.kind() as u16).to_le_bytes(), &mut buf[pos..]);
|
|
|
|
pos += write_bytes(&(self.name_size() as u16).to_le_bytes(), &mut buf[pos..]);
|
|
|
|
pos += write_utf16(self.name, &mut buf[pos..]);
|
|
|
|
pos += write_bytes(&(self.data.size() as u16).to_le_bytes(), &mut buf[pos..]);
|
|
|
|
self.data.write(&mut buf[pos..]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> RegistryPropertyFeatureDescriptor<'a> {
|
|
|
|
/// A registry property.
|
|
|
|
pub fn new(name: &'a str, data: PropertyData<'a>) -> Self {
|
|
|
|
Self { name, data }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn name_size(&self) -> usize {
|
|
|
|
core::mem::size_of::<u16>() * (self.name.encode_utf16().count() + 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-12 21:59:25 +01:00
|
|
|
/// 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::<Self>() 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 {
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Creates a new model ID descriptor
|
|
|
|
///
|
|
|
|
/// `model_id` should be a uuid that uniquely identifies a physical device.
|
2023-01-12 21:59:25 +01:00
|
|
|
pub fn new(model_id: u128) -> Self {
|
|
|
|
Self {
|
|
|
|
wLength: (size_of::<Self>() as u16).to_le(),
|
|
|
|
wDescriptorType: (Self::TYPE as u16).to_le(),
|
2023-02-07 20:19:51 +01:00
|
|
|
modelId: model_id.to_le_bytes(),
|
2023-01-12 21:59:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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 {
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Creates a new CCGP device descriptor
|
2023-01-12 21:59:25 +01:00
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
wLength: (size_of::<Self>() 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
|
2023-02-07 20:19:51 +01:00
|
|
|
/// other MS OS descriptor. Shell set to greater than or equal to 1.
|
2023-01-12 21:59:25 +01:00
|
|
|
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 {
|
2023-02-07 20:19:51 +01:00
|
|
|
/// Creates a new vendor revision descriptor
|
2023-01-12 21:59:25 +01:00
|
|
|
pub fn new(revision: u16) -> Self {
|
|
|
|
assert!(revision >= 1);
|
|
|
|
Self {
|
|
|
|
wLength: (size_of::<Self>() as u16).to_le(),
|
|
|
|
wDescriptorType: (Self::TYPE as u16).to_le(),
|
|
|
|
VendorRevision: revision.to_le(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|