embassy_usb/
descriptor.rs

1//! Utilities for writing USB descriptors.
2use embassy_usb_driver::EndpointType;
3
4use crate::builder::Config;
5use crate::driver::EndpointInfo;
6use crate::types::{InterfaceNumber, StringIndex};
7use crate::CONFIGURATION_VALUE;
8
9/// Standard descriptor types
10#[allow(missing_docs)]
11pub mod descriptor_type {
12    pub const DEVICE: u8 = 1;
13    pub const CONFIGURATION: u8 = 2;
14    pub const STRING: u8 = 3;
15    pub const INTERFACE: u8 = 4;
16    pub const ENDPOINT: u8 = 5;
17    pub const DEVICE_QUALIFIER: u8 = 6;
18    pub const OTHER_SPEED_CONFIGURATION: u8 = 7;
19    pub const IAD: u8 = 11;
20    pub const BOS: u8 = 15;
21    pub const CAPABILITY: u8 = 16;
22}
23
24/// String descriptor language IDs.
25pub mod lang_id {
26    /// English (US)
27    ///
28    /// Recommended for use as the first language ID for compatibility.
29    pub const ENGLISH_US: u16 = 0x0409;
30}
31
32/// Standard capability descriptor types
33#[allow(missing_docs)]
34pub mod capability_type {
35    pub const WIRELESS_USB: u8 = 1;
36    pub const USB_2_0_EXTENSION: u8 = 2;
37    pub const SS_USB_DEVICE: u8 = 3;
38    pub const CONTAINER_ID: u8 = 4;
39    pub const PLATFORM: u8 = 5;
40}
41
42/// USB endpoint synchronization type. The values of this enum can be directly
43/// cast into `u8` to get the bmAttributes synchronization type bits.
44/// Values other than `NoSynchronization` are only allowed on isochronous endpoints.
45#[repr(u8)]
46#[derive(Copy, Clone, Eq, PartialEq, Debug)]
47#[cfg_attr(feature = "defmt", derive(defmt::Format))]
48pub enum SynchronizationType {
49    /// No synchronization is used.
50    NoSynchronization = 0b00,
51    /// Unsynchronized, although sinks provide data rate feedback.
52    Asynchronous = 0b01,
53    /// Synchronized using feedback or feedforward data rate information.
54    Adaptive = 0b10,
55    /// Synchronized to the USB’s SOF.
56    Synchronous = 0b11,
57}
58
59/// USB endpoint usage type. The values of this enum can be directly cast into
60/// `u8` to get the bmAttributes usage type bits.
61/// Values other than `DataEndpoint` are only allowed on isochronous endpoints.
62#[repr(u8)]
63#[derive(Copy, Clone, Eq, PartialEq, Debug)]
64#[cfg_attr(feature = "defmt", derive(defmt::Format))]
65pub enum UsageType {
66    /// Use the endpoint for regular data transfer.
67    DataEndpoint = 0b00,
68    /// Endpoint conveys explicit feedback information for one or more data endpoints.
69    FeedbackEndpoint = 0b01,
70    /// A data endpoint that also serves as an implicit feedback endpoint for one or more data endpoints.
71    ImplicitFeedbackDataEndpoint = 0b10,
72    /// Reserved usage type.
73    Reserved = 0b11,
74}
75
76/// A writer for USB descriptors.
77pub(crate) struct DescriptorWriter<'a> {
78    pub buf: &'a mut [u8],
79    position: usize,
80    num_interfaces_mark: Option<usize>,
81    num_endpoints_mark: Option<usize>,
82}
83
84impl<'a> DescriptorWriter<'a> {
85    pub(crate) fn new(buf: &'a mut [u8]) -> Self {
86        DescriptorWriter {
87            buf,
88            position: 0,
89            num_interfaces_mark: None,
90            num_endpoints_mark: None,
91        }
92    }
93
94    pub fn into_buf(self) -> &'a mut [u8] {
95        &mut self.buf[..self.position]
96    }
97
98    /// Gets the current position in the buffer, i.e. the number of bytes written so far.
99    pub const fn position(&self) -> usize {
100        self.position
101    }
102
103    /// Writes an arbitrary (usually class-specific) descriptor with optional extra fields.
104    pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8], extra_fields: &[u8]) {
105        let descriptor_length = descriptor.len();
106        let extra_fields_length = extra_fields.len();
107        let total_length = descriptor_length + extra_fields_length;
108
109        assert!(
110            (self.position + 2 + total_length) <= self.buf.len() && (total_length + 2) <= 255,
111            "Descriptor buffer full"
112        );
113
114        self.buf[self.position] = (total_length + 2) as u8;
115        self.buf[self.position + 1] = descriptor_type;
116
117        let start = self.position + 2;
118
119        self.buf[start..start + descriptor_length].copy_from_slice(descriptor);
120        self.buf[start + descriptor_length..start + total_length].copy_from_slice(extra_fields);
121
122        self.position = start + total_length;
123    }
124
125    pub(crate) fn configuration(&mut self, config: &Config) {
126        self.num_interfaces_mark = Some(self.position + 4);
127
128        self.write(
129            descriptor_type::CONFIGURATION,
130            &[
131                0,
132                0,                   // wTotalLength
133                0,                   // bNumInterfaces
134                CONFIGURATION_VALUE, // bConfigurationValue
135                0,                   // iConfiguration
136                0x80 | if config.self_powered { 0x40 } else { 0x00 }
137                    | if config.supports_remote_wakeup { 0x20 } else { 0x00 }, // bmAttributes
138                (config.max_power / 2) as u8, // bMaxPower
139            ],
140            &[],
141        );
142    }
143
144    #[allow(unused)]
145    pub(crate) fn end_class(&mut self) {
146        self.num_endpoints_mark = None;
147    }
148
149    pub(crate) fn end_configuration(&mut self) {
150        let position = self.position as u16;
151        self.buf[2..4].copy_from_slice(&position.to_le_bytes());
152    }
153
154    /// Writes a interface association descriptor. Call from `UsbClass::get_configuration_descriptors`
155    /// before writing the USB class or function's interface descriptors if your class has more than
156    /// one interface and wants to play nicely with composite devices on Windows. If the USB device
157    /// hosting the class was not configured as composite with IADs enabled, calling this function
158    /// does nothing, so it is safe to call from libraries.
159    ///
160    /// # Arguments
161    ///
162    /// * `first_interface` - Number of the function's first interface, previously allocated with
163    ///   [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::interface).
164    /// * `interface_count` - Number of interfaces in the function.
165    /// * `function_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices
166    ///   that do not conform to any class.
167    /// * `function_sub_class` - Sub-class code. Depends on class.
168    /// * `function_protocol` - Protocol code. Depends on class and sub-class.
169    pub fn iad(
170        &mut self,
171        first_interface: InterfaceNumber,
172        interface_count: u8,
173        function_class: u8,
174        function_sub_class: u8,
175        function_protocol: u8,
176    ) {
177        self.write(
178            descriptor_type::IAD,
179            &[
180                first_interface.into(), // bFirstInterface
181                interface_count,        // bInterfaceCount
182                function_class,
183                function_sub_class,
184                function_protocol,
185                0,
186            ],
187            &[],
188        );
189    }
190
191    /// Writes a interface descriptor with a specific alternate setting and
192    /// interface string identifier.
193    ///
194    /// # Arguments
195    ///
196    /// * `number` - Interface number previously allocated with
197    ///   [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::interface).
198    /// * `alternate_setting` - Number of the alternate setting
199    /// * `interface_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices
200    ///   that do not conform to any class.
201    /// * `interface_sub_class` - Sub-class code. Depends on class.
202    /// * `interface_protocol` - Protocol code. Depends on class and sub-class.
203    /// * `interface_string` - Index of string descriptor describing this interface
204
205    pub fn interface_alt(
206        &mut self,
207        number: InterfaceNumber,
208        alternate_setting: u8,
209        interface_class: u8,
210        interface_sub_class: u8,
211        interface_protocol: u8,
212        interface_string: Option<StringIndex>,
213    ) {
214        if alternate_setting == 0 {
215            match self.num_interfaces_mark {
216                Some(mark) => self.buf[mark] += 1,
217                None => {
218                    panic!("you can only call `interface/interface_alt` after `configuration`.")
219                }
220            };
221        }
222
223        let str_index = interface_string.map_or(0, Into::into);
224
225        self.num_endpoints_mark = Some(self.position + 4);
226
227        self.write(
228            descriptor_type::INTERFACE,
229            &[
230                number.into(),       // bInterfaceNumber
231                alternate_setting,   // bAlternateSetting
232                0,                   // bNumEndpoints
233                interface_class,     // bInterfaceClass
234                interface_sub_class, // bInterfaceSubClass
235                interface_protocol,  // bInterfaceProtocol
236                str_index,           // iInterface
237            ],
238            &[],
239        );
240    }
241
242    /// Writes an endpoint descriptor.
243    ///
244    /// # Arguments
245    ///
246    /// * `endpoint` - Endpoint previously allocated with
247    ///   [`UsbDeviceBuilder`](crate::bus::UsbDeviceBuilder).
248    /// * `synchronization_type` - The synchronization type of the endpoint.
249    /// * `usage_type` - The usage type of the endpoint.
250    /// * `extra_fields` - Additional, class-specific entries at the end of the endpoint descriptor.
251    pub fn endpoint(
252        &mut self,
253        endpoint: &EndpointInfo,
254        synchronization_type: SynchronizationType,
255        usage_type: UsageType,
256        extra_fields: &[u8],
257    ) {
258        match self.num_endpoints_mark {
259            Some(mark) => self.buf[mark] += 1,
260            None => panic!("you can only call `endpoint` after `interface/interface_alt`."),
261        };
262
263        let mut bm_attributes = endpoint.ep_type as u8;
264
265        // Synchronization types other than `NoSynchronization`,
266        // and usage types other than `DataEndpoint`
267        // are only allowed for isochronous endpoints.
268        if endpoint.ep_type != EndpointType::Isochronous {
269            assert_eq!(synchronization_type, SynchronizationType::NoSynchronization);
270            assert_eq!(usage_type, UsageType::DataEndpoint);
271        } else {
272            if usage_type == UsageType::FeedbackEndpoint {
273                assert_eq!(synchronization_type, SynchronizationType::NoSynchronization)
274            }
275
276            let synchronization_bm_attibutes: u8 = (synchronization_type as u8) << 2;
277            let usage_bm_attibutes: u8 = (usage_type as u8) << 4;
278
279            bm_attributes |= usage_bm_attibutes | synchronization_bm_attibutes;
280        }
281
282        self.write(
283            descriptor_type::ENDPOINT,
284            &[
285                endpoint.addr.into(), // bEndpointAddress
286                bm_attributes,        // bmAttributes
287                endpoint.max_packet_size as u8,
288                (endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize
289                endpoint.interval_ms,                  // bInterval
290            ],
291            extra_fields,
292        );
293    }
294
295    /// Writes a string descriptor.
296    #[allow(unused)]
297    pub(crate) fn string(&mut self, string: &str) {
298        let mut pos = self.position;
299
300        assert!(pos + 2 <= self.buf.len(), "Descriptor buffer full");
301
302        self.buf[pos] = 0; // length placeholder
303        self.buf[pos + 1] = descriptor_type::STRING;
304
305        pos += 2;
306
307        for c in string.encode_utf16() {
308            assert!(pos < self.buf.len(), "Descriptor buffer full");
309
310            self.buf[pos..pos + 2].copy_from_slice(&c.to_le_bytes());
311            pos += 2;
312        }
313
314        self.buf[self.position] = (pos - self.position) as u8;
315
316        self.position = pos;
317    }
318}
319
320/// Create a new Device Descriptor array.
321///
322/// All device descriptors are always 18 bytes, so there's no need for
323/// a variable-length buffer or DescriptorWriter.
324pub(crate) fn device_descriptor(config: &Config) -> [u8; 18] {
325    [
326        18,   // bLength
327        0x01, // bDescriptorType
328        config.bcd_usb as u8,
329        (config.bcd_usb as u16 >> 8) as u8, // bcdUSB
330        config.device_class,                // bDeviceClass
331        config.device_sub_class,            // bDeviceSubClass
332        config.device_protocol,             // bDeviceProtocol
333        config.max_packet_size_0,           // bMaxPacketSize0
334        config.vendor_id as u8,
335        (config.vendor_id >> 8) as u8, // idVendor
336        config.product_id as u8,
337        (config.product_id >> 8) as u8, // idProduct
338        config.device_release as u8,
339        (config.device_release >> 8) as u8,    // bcdDevice
340        config.manufacturer.map_or(0, |_| 1),  // iManufacturer
341        config.product.map_or(0, |_| 2),       // iProduct
342        config.serial_number.map_or(0, |_| 3), // iSerialNumber
343        1,                                     // bNumConfigurations
344    ]
345}
346
347/// Create a new Device Qualifier Descriptor array.
348///
349/// All device qualifier descriptors are always 10 bytes, so there's no need for
350/// a variable-length buffer or DescriptorWriter.
351pub(crate) fn device_qualifier_descriptor(config: &Config) -> [u8; 10] {
352    [
353        10,   // bLength
354        0x06, // bDescriptorType
355        config.bcd_usb as u8,
356        (config.bcd_usb as u16 >> 8) as u8, // bcdUSB
357        config.device_class,                // bDeviceClass
358        config.device_sub_class,            // bDeviceSubClass
359        config.device_protocol,             // bDeviceProtocol
360        config.max_packet_size_0,           // bMaxPacketSize0
361        1,                                  // bNumConfigurations
362        0,                                  // Reserved
363    ]
364}
365
366/// A writer for Binary Object Store descriptor.
367pub struct BosWriter<'a> {
368    pub(crate) writer: DescriptorWriter<'a>,
369    num_caps_mark: Option<usize>,
370}
371
372impl<'a> BosWriter<'a> {
373    pub(crate) const fn new(writer: DescriptorWriter<'a>) -> Self {
374        Self {
375            writer,
376            num_caps_mark: None,
377        }
378    }
379
380    pub(crate) fn bos(&mut self) {
381        if (self.writer.buf.len() - self.writer.position) < 5 {
382            return;
383        }
384        self.num_caps_mark = Some(self.writer.position + 4);
385        self.writer.write(
386            descriptor_type::BOS,
387            &[
388                0x00, 0x00, // wTotalLength
389                0x00, // bNumDeviceCaps
390            ],
391            &[],
392        );
393
394        self.capability(capability_type::USB_2_0_EXTENSION, &[0; 4]);
395    }
396
397    /// Writes capability descriptor to a BOS
398    ///
399    /// # Arguments
400    ///
401    /// * `capability_type` - Type of a capability
402    /// * `data` - Binary data of the descriptor
403    pub fn capability(&mut self, capability_type: u8, data: &[u8]) {
404        match self.num_caps_mark {
405            Some(mark) => self.writer.buf[mark] += 1,
406            None => panic!("called `capability` not between `bos` and `end_bos`."),
407        }
408
409        let mut start = self.writer.position;
410        let blen = data.len();
411
412        assert!(
413            (start + blen + 3) <= self.writer.buf.len() && (blen + 3) <= 255,
414            "Descriptor buffer full"
415        );
416
417        self.writer.buf[start] = (blen + 3) as u8;
418        self.writer.buf[start + 1] = descriptor_type::CAPABILITY;
419        self.writer.buf[start + 2] = capability_type;
420
421        start += 3;
422        self.writer.buf[start..start + blen].copy_from_slice(data);
423        self.writer.position = start + blen;
424    }
425
426    pub(crate) fn end_bos(&mut self) {
427        if self.writer.position == 0 {
428            return;
429        }
430        self.num_caps_mark = None;
431        let position = self.writer.position as u16;
432        self.writer.buf[2..4].copy_from_slice(&position.to_le_bytes());
433    }
434}