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