Skip to main content

embassy_usb/
msos.rs

1//! Microsoft OS Descriptors
2//!
3//! <https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-os-2-0-descriptors-specification>
4
5use core::mem::size_of;
6
7use crate::descriptor::{BosWriter, capability_type};
8use crate::types::InterfaceNumber;
9
10/// A serialized Microsoft OS 2.0 Descriptor set.
11///
12/// Create with [`DeviceDescriptorSetBuilder`].
13pub struct MsOsDescriptorSet<'d> {
14    descriptor: &'d [u8],
15    vendor_code: u8,
16}
17
18impl<'d> MsOsDescriptorSet<'d> {
19    /// Gets the raw bytes of the MS OS descriptor
20    pub fn descriptor(&self) -> &[u8] {
21        self.descriptor
22    }
23
24    /// Gets the vendor code used by the host to retrieve the MS OS descriptor
25    pub fn vendor_code(&self) -> u8 {
26        self.vendor_code
27    }
28
29    /// Returns `true` if no MS OS descriptor data is available
30    pub fn is_empty(&self) -> bool {
31        self.descriptor.is_empty()
32    }
33
34    /// Returns the length of the descriptor field
35    pub fn len(&self) -> usize {
36        self.descriptor.len()
37    }
38}
39
40/// Writes a Microsoft OS 2.0 Descriptor set into a buffer.
41pub struct MsOsDescriptorWriter<'d> {
42    buf: &'d mut [u8],
43
44    position: usize,
45    config_mark: Option<usize>,
46    function_mark: Option<usize>,
47    vendor_code: u8,
48}
49
50impl<'d> MsOsDescriptorWriter<'d> {
51    pub(crate) fn new(buf: &'d mut [u8]) -> Self {
52        MsOsDescriptorWriter {
53            buf,
54            position: 0,
55            config_mark: None,
56            function_mark: None,
57            vendor_code: 0,
58        }
59    }
60
61    pub(crate) fn build(mut self, bos: &mut BosWriter) -> MsOsDescriptorSet<'d> {
62        self.end();
63
64        if self.is_empty() {
65            MsOsDescriptorSet {
66                descriptor: &[],
67                vendor_code: 0,
68            }
69        } else {
70            self.write_bos(bos);
71            MsOsDescriptorSet {
72                descriptor: &self.buf[..self.position],
73                vendor_code: self.vendor_code,
74            }
75        }
76    }
77
78    /// Returns `true` if the MS OS descriptor header has not yet been written
79    pub fn is_empty(&self) -> bool {
80        self.position == 0
81    }
82
83    /// Returns `true` if a configuration subset header has been started
84    pub fn is_in_config_subset(&self) -> bool {
85        self.config_mark.is_some()
86    }
87
88    /// Returns `true` if a function subset header has been started and not yet ended
89    pub fn is_in_function_subset(&self) -> bool {
90        self.function_mark.is_some()
91    }
92
93    /// Write the MS OS descriptor set header.
94    ///
95    /// - `windows_version` is an NTDDI version constant that describes a windows version. See the [`windows_version`]
96    /// module.
97    /// - `vendor_code` is the vendor request code used to read the MS OS descriptor set.
98    pub fn header(&mut self, windows_version: u32, vendor_code: u8) {
99        assert!(self.is_empty(), "You can only call MsOsDescriptorWriter::header once");
100        self.write(DescriptorSetHeader::new(windows_version));
101        self.vendor_code = vendor_code;
102    }
103
104    /// Add a device level feature descriptor.
105    ///
106    /// Note that some feature descriptors may only be used at the device level in non-composite devices.
107    /// Those features must be written before the first call to [`Self::configuration`].
108    pub fn device_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) {
109        assert!(
110            !self.is_empty(),
111            "device features may only be added after the header is written"
112        );
113        self.write(desc);
114    }
115
116    /// Add a configuration subset.
117    pub fn configuration(&mut self, config: u8) {
118        assert!(
119            !self.is_empty(),
120            "MsOsDescriptorWriter: configuration must be called after header"
121        );
122        Self::end_subset::<ConfigurationSubsetHeader>(self.buf, self.position, &mut self.config_mark);
123        self.config_mark = Some(self.position);
124        self.write(ConfigurationSubsetHeader::new(config));
125    }
126
127    /// Add a function subset.
128    pub fn function(&mut self, first_interface: InterfaceNumber) {
129        assert!(
130            self.config_mark.is_some(),
131            "MsOsDescriptorWriter: function subset requires a configuration subset"
132        );
133        self.end_function();
134        self.function_mark = Some(self.position);
135        self.write(FunctionSubsetHeader::new(first_interface));
136    }
137
138    /// Add a function level feature descriptor.
139    ///
140    /// Note that some features may only be used at the function level. Those features must be written after a call
141    /// to [`Self::function`].
142    pub fn function_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) {
143        assert!(
144            self.function_mark.is_some(),
145            "function features may only be added to a function subset"
146        );
147        self.write(desc);
148    }
149
150    /// Ends the current function subset (if any)
151    pub fn end_function(&mut self) {
152        Self::end_subset::<FunctionSubsetHeader>(self.buf, self.position, &mut self.function_mark);
153    }
154
155    fn write<T: Descriptor>(&mut self, desc: T) {
156        desc.write_to(&mut self.buf[self.position..]);
157        self.position += desc.size();
158    }
159
160    fn end_subset<T: DescriptorSet>(buf: &mut [u8], position: usize, mark: &mut Option<usize>) {
161        if let Some(mark) = mark.take() {
162            let len = position - mark;
163            let p = mark + T::LENGTH_OFFSET;
164            buf[p..(p + 2)].copy_from_slice(&(len as u16).to_le_bytes());
165        }
166    }
167
168    fn end(&mut self) {
169        if self.position > 0 {
170            Self::end_subset::<FunctionSubsetHeader>(self.buf, self.position, &mut self.function_mark);
171            Self::end_subset::<ConfigurationSubsetHeader>(self.buf, self.position, &mut self.config_mark);
172            Self::end_subset::<DescriptorSetHeader>(self.buf, self.position, &mut Some(0));
173        }
174    }
175
176    fn write_bos(&mut self, bos: &mut BosWriter) {
177        let windows_version = &self.buf[4..8];
178        let len = (self.position as u16).to_le_bytes();
179        bos.capability(
180            capability_type::PLATFORM,
181            &[
182                0, // reserved
183                // platform capability UUID, Microsoft OS 2.0 platform compatibility
184                0xdf,
185                0x60,
186                0xdd,
187                0xd8,
188                0x89,
189                0x45,
190                0xc7,
191                0x4c,
192                0x9c,
193                0xd2,
194                0x65,
195                0x9d,
196                0x9e,
197                0x64,
198                0x8a,
199                0x9f,
200                // Minimum compatible Windows version
201                windows_version[0],
202                windows_version[1],
203                windows_version[2],
204                windows_version[3],
205                // Descriptor set length
206                len[0],
207                len[1],
208                self.vendor_code,
209                0x0, // Device does not support alternate enumeration
210            ],
211        );
212    }
213}
214
215/// Microsoft Windows version codes
216///
217/// Windows 8.1 is the minimum version allowed for MS OS 2.0 descriptors.
218pub mod windows_version {
219    /// Windows 8.1 (aka `NTDDI_WINBLUE`)
220    pub const WIN8_1: u32 = 0x06030000;
221    /// Windows 10
222    pub const WIN10: u32 = 0x0A000000;
223}
224
225/// A trait for descriptors
226trait Descriptor: Sized {
227    const TYPE: DescriptorType;
228
229    /// The size of the descriptor's header.
230    fn size(&self) -> usize {
231        size_of::<Self>()
232    }
233
234    fn write_to(&self, buf: &mut [u8]);
235}
236
237trait DescriptorSet: Descriptor {
238    const LENGTH_OFFSET: usize;
239}
240
241/// Copies the data of `t` into `buf`.
242///
243/// # Safety
244/// The type `T` must be able to be safely cast to `&[u8]`. (e.g. it is a `#[repr(packed)]` struct)
245unsafe fn transmute_write_to<T: Sized>(t: &T, buf: &mut [u8]) {
246    let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::<T>());
247    assert!(buf.len() >= bytes.len(), "MS OS descriptor buffer full");
248    buf[..bytes.len()].copy_from_slice(bytes);
249}
250
251/// Table 9. Microsoft OS 2.0 descriptor wDescriptorType values.
252#[derive(Clone, Copy, PartialEq, Eq)]
253#[repr(u16)]
254pub enum DescriptorType {
255    /// MS OS descriptor set header
256    SetHeaderDescriptor = 0,
257    /// Configuration subset header
258    SubsetHeaderConfiguration = 1,
259    /// Function subset header
260    SubsetHeaderFunction = 2,
261    /// Compatible device ID feature descriptor
262    FeatureCompatibleId = 3,
263    /// Registry property feature descriptor
264    FeatureRegProperty = 4,
265    /// Minimum USB resume time feature descriptor
266    FeatureMinResumeTime = 5,
267    /// Vendor revision feature descriptor
268    FeatureModelId = 6,
269    /// CCGP device descriptor feature descriptor
270    FeatureCcgpDevice = 7,
271    /// Vendor revision feature descriptor
272    FeatureVendorRevision = 8,
273}
274
275/// Table 5. Descriptor set information structure.
276#[allow(non_snake_case)]
277#[allow(unused)]
278#[repr(C, packed(1))]
279pub struct DescriptorSetInformation {
280    dwWindowsVersion: u32,
281    wMSOSDescriptorSetTotalLength: u16,
282    bMS_VendorCode: u8,
283    bAltEnumCode: u8,
284}
285
286/// Table 4. Microsoft OS 2.0 platform capability descriptor header.
287#[allow(non_snake_case)]
288#[allow(unused)]
289#[repr(C, packed(1))]
290pub struct PlatformDescriptor {
291    bLength: u8,
292    bDescriptorType: u8,
293    bDevCapabilityType: u8,
294    bReserved: u8,
295    platformCapabilityUUID: [u8; 16],
296    descriptor_set_information: DescriptorSetInformation,
297}
298
299/// Table 10. Microsoft OS 2.0 descriptor set header.
300#[allow(non_snake_case)]
301#[repr(C, packed(1))]
302pub struct DescriptorSetHeader {
303    wLength: u16,
304    wDescriptorType: u16,
305    dwWindowsVersion: u32,
306    wTotalLength: u16,
307}
308
309impl DescriptorSetHeader {
310    /// Creates a MS OS descriptor set header.
311    ///
312    /// `windows_version` is the minimum Windows version the descriptor set can apply to.
313    pub fn new(windows_version: u32) -> Self {
314        DescriptorSetHeader {
315            wLength: (size_of::<Self>() as u16).to_le(),
316            wDescriptorType: (Self::TYPE as u16).to_le(),
317            dwWindowsVersion: windows_version.to_le(),
318            wTotalLength: 0,
319        }
320    }
321}
322
323impl Descriptor for DescriptorSetHeader {
324    const TYPE: DescriptorType = DescriptorType::SetHeaderDescriptor;
325    fn write_to(&self, buf: &mut [u8]) {
326        unsafe { transmute_write_to(self, buf) }
327    }
328}
329
330impl DescriptorSet for DescriptorSetHeader {
331    const LENGTH_OFFSET: usize = 8;
332}
333
334/// Table 11. Configuration subset header.
335#[allow(non_snake_case)]
336#[repr(C, packed(1))]
337pub struct ConfigurationSubsetHeader {
338    wLength: u16,
339    wDescriptorType: u16,
340    bConfigurationValue: u8,
341    bReserved: u8,
342    wTotalLength: u16,
343}
344
345impl ConfigurationSubsetHeader {
346    /// Creates a configuration subset header
347    pub fn new(config: u8) -> Self {
348        ConfigurationSubsetHeader {
349            wLength: (size_of::<Self>() as u16).to_le(),
350            wDescriptorType: (Self::TYPE as u16).to_le(),
351            bConfigurationValue: config,
352            bReserved: 0,
353            wTotalLength: 0,
354        }
355    }
356}
357
358impl Descriptor for ConfigurationSubsetHeader {
359    const TYPE: DescriptorType = DescriptorType::SubsetHeaderConfiguration;
360    fn write_to(&self, buf: &mut [u8]) {
361        unsafe { transmute_write_to(self, buf) }
362    }
363}
364
365impl DescriptorSet for ConfigurationSubsetHeader {
366    const LENGTH_OFFSET: usize = 6;
367}
368
369/// Table 12. Function subset header.
370#[allow(non_snake_case)]
371#[repr(C, packed(1))]
372pub struct FunctionSubsetHeader {
373    wLength: u16,
374    wDescriptorType: u16,
375    bFirstInterface: InterfaceNumber,
376    bReserved: u8,
377    wSubsetLength: u16,
378}
379
380impl FunctionSubsetHeader {
381    /// Creates a function subset header
382    pub fn new(first_interface: InterfaceNumber) -> Self {
383        FunctionSubsetHeader {
384            wLength: (size_of::<Self>() as u16).to_le(),
385            wDescriptorType: (Self::TYPE as u16).to_le(),
386            bFirstInterface: first_interface,
387            bReserved: 0,
388            wSubsetLength: 0,
389        }
390    }
391}
392
393impl Descriptor for FunctionSubsetHeader {
394    const TYPE: DescriptorType = DescriptorType::SubsetHeaderFunction;
395    fn write_to(&self, buf: &mut [u8]) {
396        unsafe { transmute_write_to(self, buf) }
397    }
398}
399
400impl DescriptorSet for FunctionSubsetHeader {
401    const LENGTH_OFFSET: usize = 6;
402}
403
404// Feature Descriptors
405
406/// A marker trait for feature descriptors that are valid at the device level.
407#[allow(private_bounds)]
408pub trait DeviceLevelDescriptor: Descriptor {}
409
410/// A marker trait for feature descriptors that are valid at the function level.
411#[allow(private_bounds)]
412pub trait FunctionLevelDescriptor: Descriptor {}
413
414/// Table 13. Microsoft OS 2.0 compatible ID descriptor.
415#[allow(non_snake_case)]
416#[repr(C, packed(1))]
417pub struct CompatibleIdFeatureDescriptor {
418    wLength: u16,
419    wDescriptorType: u16,
420    compatibleId: [u8; 8],
421    subCompatibleId: [u8; 8],
422}
423
424impl DeviceLevelDescriptor for CompatibleIdFeatureDescriptor {}
425impl FunctionLevelDescriptor for CompatibleIdFeatureDescriptor {}
426
427impl Descriptor for CompatibleIdFeatureDescriptor {
428    const TYPE: DescriptorType = DescriptorType::FeatureCompatibleId;
429    fn write_to(&self, buf: &mut [u8]) {
430        unsafe { transmute_write_to(self, buf) }
431    }
432}
433
434impl CompatibleIdFeatureDescriptor {
435    /// Creates a compatible ID feature descriptor
436    ///
437    /// The ids must be 8 ASCII bytes or fewer.
438    pub fn new(compatible_id: &str, sub_compatible_id: &str) -> Self {
439        assert!(compatible_id.len() <= 8 && sub_compatible_id.len() <= 8);
440        let mut cid = [0u8; 8];
441        cid[..compatible_id.len()].copy_from_slice(compatible_id.as_bytes());
442        let mut scid = [0u8; 8];
443        scid[..sub_compatible_id.len()].copy_from_slice(sub_compatible_id.as_bytes());
444        Self::new_raw(cid, scid)
445    }
446
447    fn new_raw(compatible_id: [u8; 8], sub_compatible_id: [u8; 8]) -> Self {
448        Self {
449            wLength: (size_of::<Self>() as u16).to_le(),
450            wDescriptorType: (Self::TYPE as u16).to_le(),
451            compatibleId: compatible_id,
452            subCompatibleId: sub_compatible_id,
453        }
454    }
455}
456
457/// Table 14. Microsoft OS 2.0 registry property descriptor
458#[allow(non_snake_case)]
459pub struct RegistryPropertyFeatureDescriptor<'a> {
460    name: &'a str,
461    data: PropertyData<'a>,
462}
463
464/// Data values that can be encoded into a registry property descriptor
465#[derive(Debug, Clone, Copy, PartialEq, Eq)]
466#[cfg_attr(feature = "defmt", derive(defmt::Format))]
467pub enum PropertyData<'a> {
468    /// A registry property containing a string.
469    Sz(&'a str),
470    /// A registry property containing a string that expands environment variables.
471    ExpandSz(&'a str),
472    /// A registry property containing binary data.
473    Binary(&'a [u8]),
474    /// A registry property containing a little-endian 32-bit integer.
475    DwordLittleEndian(u32),
476    /// A registry property containing a big-endian 32-bit integer.
477    DwordBigEndian(u32),
478    /// A registry property containing a string that contains a symbolic link.
479    Link(&'a str),
480    /// A registry property containing multiple strings.
481    RegMultiSz(&'a [&'a str]),
482}
483
484fn write_bytes(val: &[u8], buf: &mut [u8]) -> usize {
485    assert!(buf.len() >= val.len());
486    buf[..val.len()].copy_from_slice(val);
487    val.len()
488}
489
490fn write_utf16(val: &str, buf: &mut [u8]) -> usize {
491    let mut pos = 0;
492    for c in val.encode_utf16() {
493        pos += write_bytes(&c.to_le_bytes(), &mut buf[pos..]);
494    }
495    pos + write_bytes(&0u16.to_le_bytes(), &mut buf[pos..])
496}
497
498impl<'a> PropertyData<'a> {
499    /// Gets the `PropertyDataType` for this property value
500    pub fn kind(&self) -> PropertyDataType {
501        match self {
502            PropertyData::Sz(_) => PropertyDataType::Sz,
503            PropertyData::ExpandSz(_) => PropertyDataType::ExpandSz,
504            PropertyData::Binary(_) => PropertyDataType::Binary,
505            PropertyData::DwordLittleEndian(_) => PropertyDataType::DwordLittleEndian,
506            PropertyData::DwordBigEndian(_) => PropertyDataType::DwordBigEndian,
507            PropertyData::Link(_) => PropertyDataType::Link,
508            PropertyData::RegMultiSz(_) => PropertyDataType::RegMultiSz,
509        }
510    }
511
512    /// Gets the size (in bytes) of this property value when encoded.
513    pub fn size(&self) -> usize {
514        match self {
515            PropertyData::Sz(val) | PropertyData::ExpandSz(val) | PropertyData::Link(val) => {
516                core::mem::size_of::<u16>() * (val.encode_utf16().count() + 1)
517            }
518            PropertyData::Binary(val) => val.len(),
519            PropertyData::DwordLittleEndian(val) | PropertyData::DwordBigEndian(val) => core::mem::size_of_val(val),
520            PropertyData::RegMultiSz(val) => {
521                core::mem::size_of::<u16>() * (val.iter().map(|x| x.encode_utf16().count() + 1).sum::<usize>() + 1)
522            }
523        }
524    }
525
526    /// Encodes the data for this property value and writes it to `buf`.
527    pub fn write(&self, buf: &mut [u8]) -> usize {
528        match self {
529            PropertyData::Sz(val) | PropertyData::ExpandSz(val) | PropertyData::Link(val) => write_utf16(val, buf),
530            PropertyData::Binary(val) => write_bytes(val, buf),
531            PropertyData::DwordLittleEndian(val) => write_bytes(&val.to_le_bytes(), buf),
532            PropertyData::DwordBigEndian(val) => write_bytes(&val.to_be_bytes(), buf),
533            PropertyData::RegMultiSz(val) => {
534                let mut pos = 0;
535                for s in *val {
536                    pos += write_utf16(s, &mut buf[pos..]);
537                }
538                pos + write_bytes(&0u16.to_le_bytes(), &mut buf[pos..])
539            }
540        }
541    }
542}
543
544/// Table 15. wPropertyDataType values for the Microsoft OS 2.0 registry property descriptor.
545#[derive(Clone, Copy, PartialEq, Eq)]
546#[repr(u16)]
547pub enum PropertyDataType {
548    /// A registry property containing a string.
549    Sz = 1,
550    /// A registry property containing a string that expands environment variables.
551    ExpandSz = 2,
552    /// A registry property containing binary data.
553    Binary = 3,
554    /// A registry property containing a little-endian 32-bit integer.
555    DwordLittleEndian = 4,
556    /// A registry property containing a big-endian 32-bit integer.
557    DwordBigEndian = 5,
558    /// A registry property containing a string that contains a symbolic link.
559    Link = 6,
560    /// A registry property containing multiple strings.
561    RegMultiSz = 7,
562}
563
564impl<'a> DeviceLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {}
565impl<'a> FunctionLevelDescriptor for RegistryPropertyFeatureDescriptor<'a> {}
566
567impl<'a> Descriptor for RegistryPropertyFeatureDescriptor<'a> {
568    const TYPE: DescriptorType = DescriptorType::FeatureRegProperty;
569
570    fn size(&self) -> usize {
571        10 + self.name_size() + self.data.size()
572    }
573
574    fn write_to(&self, buf: &mut [u8]) {
575        assert!(buf.len() >= self.size(), "MS OS descriptor buffer full");
576
577        let mut pos = 0;
578        pos += write_bytes(&(self.size() as u16).to_le_bytes(), &mut buf[pos..]);
579        pos += write_bytes(&(Self::TYPE as u16).to_le_bytes(), &mut buf[pos..]);
580        pos += write_bytes(&(self.data.kind() as u16).to_le_bytes(), &mut buf[pos..]);
581        pos += write_bytes(&(self.name_size() as u16).to_le_bytes(), &mut buf[pos..]);
582        pos += write_utf16(self.name, &mut buf[pos..]);
583        pos += write_bytes(&(self.data.size() as u16).to_le_bytes(), &mut buf[pos..]);
584        self.data.write(&mut buf[pos..]);
585    }
586}
587
588impl<'a> RegistryPropertyFeatureDescriptor<'a> {
589    /// A registry property.
590    pub fn new(name: &'a str, data: PropertyData<'a>) -> Self {
591        Self { name, data }
592    }
593
594    fn name_size(&self) -> usize {
595        core::mem::size_of::<u16>() * (self.name.encode_utf16().count() + 1)
596    }
597}
598
599/// Table 16. Microsoft OS 2.0 minimum USB recovery time descriptor.
600#[allow(non_snake_case)]
601#[repr(C, packed(1))]
602pub struct MinimumRecoveryTimeDescriptor {
603    wLength: u16,
604    wDescriptorType: u16,
605    bResumeRecoveryTime: u8,
606    bResumeSignalingTime: u8,
607}
608
609impl DeviceLevelDescriptor for MinimumRecoveryTimeDescriptor {}
610
611impl Descriptor for MinimumRecoveryTimeDescriptor {
612    const TYPE: DescriptorType = DescriptorType::FeatureMinResumeTime;
613    fn write_to(&self, buf: &mut [u8]) {
614        unsafe { transmute_write_to(self, buf) }
615    }
616}
617
618impl MinimumRecoveryTimeDescriptor {
619    /// Times are in milliseconds.
620    ///
621    /// `resume_recovery_time` must be >= 0 and <= 10.
622    /// `resume_signaling_time` must be >= 1 and <= 20.
623    pub fn new(resume_recovery_time: u8, resume_signaling_time: u8) -> Self {
624        assert!(resume_recovery_time <= 10);
625        assert!(resume_signaling_time >= 1 && resume_signaling_time <= 20);
626        Self {
627            wLength: (size_of::<Self>() as u16).to_le(),
628            wDescriptorType: (Self::TYPE as u16).to_le(),
629            bResumeRecoveryTime: resume_recovery_time,
630            bResumeSignalingTime: resume_signaling_time,
631        }
632    }
633}
634
635/// Table 17. Microsoft OS 2.0 model ID descriptor.
636#[allow(non_snake_case)]
637#[repr(C, packed(1))]
638pub struct ModelIdDescriptor {
639    wLength: u16,
640    wDescriptorType: u16,
641    modelId: [u8; 16],
642}
643
644impl DeviceLevelDescriptor for ModelIdDescriptor {}
645
646impl Descriptor for ModelIdDescriptor {
647    const TYPE: DescriptorType = DescriptorType::FeatureModelId;
648    fn write_to(&self, buf: &mut [u8]) {
649        unsafe { transmute_write_to(self, buf) }
650    }
651}
652
653impl ModelIdDescriptor {
654    /// Creates a new model ID descriptor
655    ///
656    /// `model_id` should be a uuid that uniquely identifies a physical device.
657    pub fn new(model_id: u128) -> Self {
658        Self {
659            wLength: (size_of::<Self>() as u16).to_le(),
660            wDescriptorType: (Self::TYPE as u16).to_le(),
661            modelId: model_id.to_le_bytes(),
662        }
663    }
664}
665
666/// Table 18. Microsoft OS 2.0 CCGP device descriptor.
667#[allow(non_snake_case)]
668#[repr(C, packed(1))]
669pub struct CcgpDeviceDescriptor {
670    wLength: u16,
671    wDescriptorType: u16,
672}
673
674impl DeviceLevelDescriptor for CcgpDeviceDescriptor {}
675
676impl Descriptor for CcgpDeviceDescriptor {
677    const TYPE: DescriptorType = DescriptorType::FeatureCcgpDevice;
678    fn write_to(&self, buf: &mut [u8]) {
679        unsafe { transmute_write_to(self, buf) }
680    }
681}
682
683impl CcgpDeviceDescriptor {
684    /// Creates a new CCGP device descriptor
685    pub fn new() -> Self {
686        Self {
687            wLength: (size_of::<Self>() as u16).to_le(),
688            wDescriptorType: (Self::TYPE as u16).to_le(),
689        }
690    }
691}
692
693/// Table 19. Microsoft OS 2.0 vendor revision descriptor.
694#[allow(non_snake_case)]
695#[repr(C, packed(1))]
696pub struct VendorRevisionDescriptor {
697    wLength: u16,
698    wDescriptorType: u16,
699    /// Revision number associated with the descriptor set. Modify it every time you add/modify a registry property or
700    /// other MS OS descriptor. Shell set to greater than or equal to 1.
701    VendorRevision: u16,
702}
703
704impl DeviceLevelDescriptor for VendorRevisionDescriptor {}
705impl FunctionLevelDescriptor for VendorRevisionDescriptor {}
706
707impl Descriptor for VendorRevisionDescriptor {
708    const TYPE: DescriptorType = DescriptorType::FeatureVendorRevision;
709    fn write_to(&self, buf: &mut [u8]) {
710        unsafe { transmute_write_to(self, buf) }
711    }
712}
713
714impl VendorRevisionDescriptor {
715    /// Creates a new vendor revision descriptor
716    pub fn new(revision: u16) -> Self {
717        assert!(revision >= 1);
718        Self {
719            wLength: (size_of::<Self>() as u16).to_le(),
720            wDescriptorType: (Self::TYPE as u16).to_le(),
721            VendorRevision: revision.to_le(),
722        }
723    }
724}