Skip to main content

embassy_usb/
builder.rs

1use heapless::Vec;
2
3use crate::config::MAX_HANDLER_COUNT;
4use crate::descriptor::{BosWriter, DescriptorWriter, SynchronizationType, UsageType};
5use crate::driver::{Driver, Endpoint, EndpointAddress, EndpointInfo, EndpointType};
6use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter};
7use crate::types::{InterfaceNumber, StringIndex};
8use crate::{Handler, Interface, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START, UsbDevice};
9
10#[derive(Debug, Copy, Clone)]
11#[cfg_attr(feature = "defmt", derive(defmt::Format))]
12#[non_exhaustive]
13/// Allows Configuring the Bcd USB version below 2.1
14pub enum UsbVersion {
15    /// Usb version 2.0
16    Two = 0x0200,
17    /// Usb version 2.1
18    TwoOne = 0x0210,
19}
20
21#[derive(Debug, Copy, Clone)]
22#[cfg_attr(feature = "defmt", derive(defmt::Format))]
23#[non_exhaustive]
24/// Configuration used when creating [`UsbDevice`].
25pub struct Config<'a> {
26    pub(crate) vendor_id: u16,
27    pub(crate) product_id: u16,
28
29    /// Device BCD USB version.
30    ///
31    /// Default: `0x0210` ("2.1")
32    pub bcd_usb: UsbVersion,
33
34    /// Device class code assigned by USB.org. Set to `0xff` for vendor-specific
35    /// devices that do not conform to any class.
36    ///
37    /// Default: `0xEF`
38    /// See also: `composite_with_iads`
39    pub device_class: u8,
40
41    /// Device sub-class code. Depends on class.
42    ///
43    /// Default: `0x02`
44    /// See also: `composite_with_iads`
45    pub device_sub_class: u8,
46
47    /// Device protocol code. Depends on class and sub-class.
48    ///
49    /// Default: `0x01`
50    /// See also: `composite_with_iads`
51    pub device_protocol: u8,
52
53    /// Device release version in BCD.
54    ///
55    /// Default: `0x0010` ("0.1")
56    pub device_release: u16,
57
58    /// Maximum packet size in bytes for the control endpoint 0.
59    ///
60    /// Valid values depend on the speed at which the bus is enumerated.
61    /// - low speed: 8
62    /// - full speed: 8, 16, 32, or 64
63    /// - high speed: 64
64    ///
65    /// Default: 64 bytes
66    pub max_packet_size_0: u8,
67
68    /// Manufacturer name string descriptor.
69    ///
70    /// Default: (none)
71    pub manufacturer: Option<&'a str>,
72
73    /// Product name string descriptor.
74    ///
75    /// Default: (none)
76    pub product: Option<&'a str>,
77
78    /// Serial number string descriptor.
79    ///
80    /// Default: (none)
81    pub serial_number: Option<&'a str>,
82
83    /// Whether the device supports remotely waking up the host is requested.
84    ///
85    /// Default: `false`
86    pub supports_remote_wakeup: bool,
87
88    /// Configures the device as a composite device with interface association descriptors.
89    ///
90    /// If set to `true` (default), the following fields should have the given values:
91    ///
92    /// - `device_class` = `0xEF`
93    /// - `device_sub_class` = `0x02`
94    /// - `device_protocol` = `0x01`
95    ///
96    /// If set to `false`, those fields must be set correctly for the classes that will be
97    /// installed on the USB device.
98    pub composite_with_iads: bool,
99
100    /// Whether the device has its own power source.
101    ///
102    /// This should be set to `true` even if the device is sometimes self-powered and may not
103    /// always draw power from the USB bus.
104    ///
105    /// Default: `false`
106    ///
107    /// See also: `max_power`
108    pub self_powered: bool,
109
110    /// Maximum current drawn from the USB bus by the device, in milliamps.
111    ///
112    /// The default is 100 mA. If your device always uses an external power source and never draws
113    /// power from the USB bus, this can be set to 0.
114    ///
115    /// See also: `self_powered`
116    ///
117    /// Default: 100mA
118    /// Max: 500mA
119    pub max_power: u16,
120}
121
122impl<'a> Config<'a> {
123    /// Create default configuration with the provided vid and pid values.
124    pub const fn new(vid: u16, pid: u16) -> Self {
125        Self {
126            device_class: 0xEF,
127            device_sub_class: 0x02,
128            device_protocol: 0x01,
129            max_packet_size_0: 64,
130            vendor_id: vid,
131            product_id: pid,
132            device_release: 0x0010,
133            bcd_usb: UsbVersion::TwoOne,
134            manufacturer: None,
135            product: None,
136            serial_number: None,
137            self_powered: false,
138            supports_remote_wakeup: false,
139            composite_with_iads: true,
140            max_power: 100,
141        }
142    }
143}
144
145/// [`UsbDevice`] builder.
146pub struct Builder<'d, D: Driver<'d>> {
147    config: Config<'d>,
148    handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>,
149    interfaces: Vec<Interface, MAX_INTERFACE_COUNT>,
150    control_buf: &'d mut [u8],
151
152    driver: D,
153    next_string_index: u8,
154
155    config_descriptor: DescriptorWriter<'d>,
156    bos_descriptor: BosWriter<'d>,
157
158    msos_descriptor: MsOsDescriptorWriter<'d>,
159}
160
161impl<'d, D: Driver<'d>> Builder<'d, D> {
162    /// Creates a builder for constructing a new [`UsbDevice`].
163    ///
164    /// `control_buf` is a buffer used for USB control request data. It should be sized
165    /// large enough for the length of the largest control request (in or out)
166    /// anticipated by any class added to the device.
167    pub fn new(
168        driver: D,
169        config: Config<'d>,
170        config_descriptor_buf: &'d mut [u8],
171        bos_descriptor_buf: &'d mut [u8],
172        msos_descriptor_buf: &'d mut [u8],
173        control_buf: &'d mut [u8],
174    ) -> Self {
175        // Magic values specified in USB-IF ECN on IADs.
176        if config.composite_with_iads
177            && (config.device_class != 0xEF || config.device_sub_class != 0x02 || config.device_protocol != 0x01)
178        {
179            panic!(
180                "if composite_with_iads is set, you must set device_class = 0xEF, device_sub_class = 0x02, device_protocol = 0x01"
181            );
182        }
183
184        assert!(
185            config.max_power <= 500,
186            "The maximum allowed value for `max_power` is 500mA"
187        );
188
189        match config.max_packet_size_0 {
190            8 | 16 | 32 | 64 => {}
191            _ => panic!("invalid max_packet_size_0, the allowed values are 8, 16, 32 or 64"),
192        }
193
194        let mut config_descriptor = DescriptorWriter::new(config_descriptor_buf);
195        let mut bos_descriptor = BosWriter::new(DescriptorWriter::new(bos_descriptor_buf));
196
197        config_descriptor.configuration(&config);
198        bos_descriptor.bos();
199
200        Builder {
201            driver,
202            config,
203            interfaces: Vec::new(),
204            handlers: Vec::new(),
205            control_buf,
206            next_string_index: STRING_INDEX_CUSTOM_START,
207
208            config_descriptor,
209            bos_descriptor,
210
211            msos_descriptor: MsOsDescriptorWriter::new(msos_descriptor_buf),
212        }
213    }
214
215    /// Creates the [`UsbDevice`] instance with the configuration in this builder.
216    pub fn build(mut self) -> UsbDevice<'d, D> {
217        let msos_descriptor = self.msos_descriptor.build(&mut self.bos_descriptor);
218
219        self.config_descriptor.end_configuration();
220        self.bos_descriptor.end_bos();
221
222        // Log the number of allocator bytes actually used in descriptor buffers
223        trace!("USB: config_descriptor used: {}", self.config_descriptor.position());
224        trace!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position());
225        trace!("USB: msos_descriptor used: {}", msos_descriptor.len());
226        trace!("USB: control_buf size: {}", self.control_buf.len());
227
228        UsbDevice::build(
229            self.driver,
230            self.config,
231            self.handlers,
232            self.config_descriptor.into_buf(),
233            self.bos_descriptor.writer.into_buf(),
234            msos_descriptor,
235            self.interfaces,
236            self.control_buf,
237        )
238    }
239
240    /// Returns the size of the control request data buffer. Can be used by
241    /// classes to validate the buffer is large enough for their needs.
242    pub fn control_buf_len(&self) -> usize {
243        self.control_buf.len()
244    }
245
246    /// Add an USB function.
247    ///
248    /// If [`Config::composite_with_iads`] is set, this will add an IAD descriptor
249    /// with the given class/subclass/protocol, associating all the child interfaces.
250    ///
251    /// If it's not set, no IAD descriptor is added.
252    pub fn function(&mut self, class: u8, subclass: u8, protocol: u8) -> FunctionBuilder<'_, 'd, D> {
253        let first_interface = InterfaceNumber::new(self.interfaces.len() as u8);
254        let iface_count_index = if self.config.composite_with_iads {
255            self.config_descriptor
256                .iad(first_interface, 0, class, subclass, protocol);
257
258            Some(self.config_descriptor.position() - 5)
259        } else {
260            None
261        };
262
263        FunctionBuilder {
264            builder: self,
265            iface_count_index,
266
267            first_interface,
268        }
269    }
270
271    /// Add a Handler.
272    ///
273    /// The Handler is called on some USB bus events, and to handle all control requests not already
274    /// handled by the USB stack.
275    pub fn handler(&mut self, handler: &'d mut dyn Handler) {
276        assert!(
277            self.handlers.push(handler).is_ok(),
278            "embassy-usb: handler list full. Increase the `max_handler_count` compile-time setting. Current value: {}",
279            MAX_HANDLER_COUNT
280        );
281    }
282
283    /// Allocates a new string index.
284    pub fn string(&mut self) -> StringIndex {
285        let index = self.next_string_index;
286        self.next_string_index += 1;
287        StringIndex::new(index)
288    }
289
290    /// Add an MS OS 2.0 Descriptor Set.
291    ///
292    /// Panics if called more than once.
293    pub fn msos_descriptor(&mut self, windows_version: u32, vendor_code: u8) {
294        self.msos_descriptor.header(windows_version, vendor_code);
295    }
296
297    /// Add an MS OS 2.0 Device Level Feature Descriptor.
298    pub fn msos_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) {
299        self.msos_descriptor.device_feature(desc);
300    }
301
302    /// Gets the underlying [`MsOsDescriptorWriter`] to allow adding subsets and features for classes that
303    /// do not add their own.
304    pub fn msos_writer(&mut self) -> &mut MsOsDescriptorWriter<'d> {
305        &mut self.msos_descriptor
306    }
307}
308
309/// Function builder.
310///
311/// A function is a logical grouping of interfaces that perform a given USB function.
312/// If [`Config::composite_with_iads`] is set, each function will have an IAD descriptor.
313/// If not, functions will not be visible as descriptors.
314pub struct FunctionBuilder<'a, 'd, D: Driver<'d>> {
315    builder: &'a mut Builder<'d, D>,
316    iface_count_index: Option<usize>,
317
318    first_interface: InterfaceNumber,
319}
320
321impl<'a, 'd, D: Driver<'d>> Drop for FunctionBuilder<'a, 'd, D> {
322    fn drop(&mut self) {
323        self.builder.msos_descriptor.end_function();
324    }
325}
326
327impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
328    /// Add an interface to the function.
329    ///
330    /// Interface numbers are guaranteed to be allocated consecutively, starting from 0.
331    pub fn interface(&mut self) -> InterfaceBuilder<'_, 'd, D> {
332        if let Some(i) = self.iface_count_index {
333            self.builder.config_descriptor.buf[i] += 1;
334        }
335
336        let number = self.builder.interfaces.len() as _;
337        let iface = Interface {
338            current_alt_setting: 0,
339            num_alt_settings: 0,
340        };
341
342        assert!(
343            self.builder.interfaces.push(iface).is_ok(),
344            "embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}",
345            MAX_INTERFACE_COUNT
346        );
347
348        InterfaceBuilder {
349            builder: self.builder,
350            interface_number: InterfaceNumber::new(number),
351            next_alt_setting_number: 0,
352        }
353    }
354
355    /// Add an MS OS 2.0 Function Level Feature Descriptor.
356    pub fn msos_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) {
357        if !self.builder.msos_descriptor.is_in_config_subset() {
358            self.builder.msos_descriptor.configuration(0);
359        }
360
361        if !self.builder.msos_descriptor.is_in_function_subset() {
362            self.builder.msos_descriptor.function(self.first_interface);
363        }
364
365        self.builder.msos_descriptor.function_feature(desc);
366    }
367}
368
369/// Interface builder.
370pub struct InterfaceBuilder<'a, 'd, D: Driver<'d>> {
371    builder: &'a mut Builder<'d, D>,
372    interface_number: InterfaceNumber,
373    next_alt_setting_number: u8,
374}
375
376impl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> {
377    /// Get the interface number.
378    pub const fn interface_number(&self) -> InterfaceNumber {
379        self.interface_number
380    }
381
382    /// Allocates a new string index.
383    pub fn string(&mut self) -> StringIndex {
384        self.builder.string()
385    }
386
387    /// Add an alternate setting to the interface and write its descriptor.
388    ///
389    /// Alternate setting numbers are guaranteed to be allocated consecutively, starting from 0.
390    ///
391    /// The first alternate setting, with number 0, is the default one.
392    pub fn alt_setting(
393        &mut self,
394        class: u8,
395        subclass: u8,
396        protocol: u8,
397        interface_string: Option<StringIndex>,
398    ) -> InterfaceAltBuilder<'_, 'd, D> {
399        let number = self.next_alt_setting_number;
400        self.next_alt_setting_number += 1;
401        self.builder.interfaces[self.interface_number.0 as usize].num_alt_settings += 1;
402
403        self.builder.config_descriptor.interface_alt(
404            self.interface_number,
405            number,
406            class,
407            subclass,
408            protocol,
409            interface_string,
410        );
411
412        InterfaceAltBuilder {
413            builder: self.builder,
414            interface_number: self.interface_number,
415            alt_setting_number: number,
416        }
417    }
418}
419
420/// Interface alternate setting builder.
421pub struct InterfaceAltBuilder<'a, 'd, D: Driver<'d>> {
422    builder: &'a mut Builder<'d, D>,
423    interface_number: InterfaceNumber,
424    alt_setting_number: u8,
425}
426
427impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
428    /// Get the interface number.
429    pub const fn interface_number(&self) -> InterfaceNumber {
430        self.interface_number
431    }
432
433    /// Get the alternate setting number.
434    pub const fn alt_setting_number(&self) -> u8 {
435        self.alt_setting_number
436    }
437
438    /// Add a custom descriptor to this alternate setting.
439    ///
440    /// Descriptors are written in the order builder functions are called. Note that some
441    /// classes care about the order.
442    pub fn descriptor(&mut self, descriptor_type: u8, descriptor: &[u8]) {
443        self.builder.config_descriptor.write(descriptor_type, descriptor, &[]);
444    }
445
446    /// Add a custom Binary Object Store (BOS) descriptor to this alternate setting.
447    pub fn bos_capability(&mut self, capability_type: u8, capability: &[u8]) {
448        self.builder.bos_descriptor.capability(capability_type, capability);
449    }
450
451    /// Write a custom endpoint descriptor for a certain endpoint.
452    ///
453    /// This can be necessary, if the endpoint descriptors can only be written
454    /// after the endpoint was created. As an example, an endpoint descriptor
455    /// may contain the address of an endpoint that was allocated earlier.
456    pub fn endpoint_descriptor(
457        &mut self,
458        endpoint: &EndpointInfo,
459        synchronization_type: SynchronizationType,
460        usage_type: UsageType,
461        extra_fields: &[u8],
462    ) {
463        self.builder
464            .config_descriptor
465            .endpoint(endpoint, synchronization_type, usage_type, extra_fields);
466    }
467
468    /// Allocate an IN endpoint, without writing its descriptor.
469    ///
470    /// Used for granular control over the order of endpoint and descriptor creation.
471    pub fn alloc_endpoint_in(
472        &mut self,
473        ep_type: EndpointType,
474        ep_addr: Option<EndpointAddress>,
475        max_packet_size: u16,
476        interval_ms: u8,
477    ) -> D::EndpointIn {
478        let ep = self
479            .builder
480            .driver
481            .alloc_endpoint_in(ep_type, ep_addr, max_packet_size, interval_ms)
482            .expect("alloc_endpoint_in failed");
483
484        ep
485    }
486
487    fn endpoint_in(
488        &mut self,
489        ep_type: EndpointType,
490        ep_addr: Option<EndpointAddress>,
491        max_packet_size: u16,
492        interval_ms: u8,
493        synchronization_type: SynchronizationType,
494        usage_type: UsageType,
495        extra_fields: &[u8],
496    ) -> D::EndpointIn {
497        let ep = self.alloc_endpoint_in(ep_type, ep_addr, max_packet_size, interval_ms);
498        self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields);
499
500        ep
501    }
502
503    /// Allocate an OUT endpoint, without writing its descriptor.
504    ///
505    /// Use for granular control over the order of endpoint and descriptor creation.
506    pub fn alloc_endpoint_out(
507        &mut self,
508        ep_type: EndpointType,
509        ep_addr: Option<EndpointAddress>,
510        max_packet_size: u16,
511        interval_ms: u8,
512    ) -> D::EndpointOut {
513        let ep = self
514            .builder
515            .driver
516            .alloc_endpoint_out(ep_type, ep_addr, max_packet_size, interval_ms)
517            .expect("alloc_endpoint_out failed");
518
519        ep
520    }
521
522    fn endpoint_out(
523        &mut self,
524        ep_type: EndpointType,
525        ep_addr: Option<EndpointAddress>,
526        max_packet_size: u16,
527        interval_ms: u8,
528        synchronization_type: SynchronizationType,
529        usage_type: UsageType,
530        extra_fields: &[u8],
531    ) -> D::EndpointOut {
532        let ep = self.alloc_endpoint_out(ep_type, ep_addr, max_packet_size, interval_ms);
533        self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields);
534
535        ep
536    }
537
538    /// Allocate a BULK IN endpoint and write its descriptor.
539    ///
540    /// Descriptors are written in the order builder functions are called. Note that some
541    /// classes care about the order.
542    pub fn endpoint_bulk_in(&mut self, ep_addr: Option<EndpointAddress>, max_packet_size: u16) -> D::EndpointIn {
543        self.endpoint_in(
544            EndpointType::Bulk,
545            ep_addr,
546            max_packet_size,
547            0,
548            SynchronizationType::NoSynchronization,
549            UsageType::DataEndpoint,
550            &[],
551        )
552    }
553
554    /// Allocate a BULK OUT endpoint and write its descriptor.
555    ///
556    /// Descriptors are written in the order builder functions are called. Note that some
557    /// classes care about the order.
558    pub fn endpoint_bulk_out(&mut self, ep_addr: Option<EndpointAddress>, max_packet_size: u16) -> D::EndpointOut {
559        self.endpoint_out(
560            EndpointType::Bulk,
561            ep_addr,
562            max_packet_size,
563            0,
564            SynchronizationType::NoSynchronization,
565            UsageType::DataEndpoint,
566            &[],
567        )
568    }
569
570    /// Allocate a INTERRUPT IN endpoint and write its descriptor.
571    ///
572    /// Descriptors are written in the order builder functions are called. Note that some
573    /// classes care about the order.
574    pub fn endpoint_interrupt_in(
575        &mut self,
576        ep_addr: Option<EndpointAddress>,
577        max_packet_size: u16,
578        interval_ms: u8,
579    ) -> D::EndpointIn {
580        self.endpoint_in(
581            EndpointType::Interrupt,
582            ep_addr,
583            max_packet_size,
584            interval_ms,
585            SynchronizationType::NoSynchronization,
586            UsageType::DataEndpoint,
587            &[],
588        )
589    }
590
591    /// Allocate a INTERRUPT OUT endpoint and write its descriptor.
592    pub fn endpoint_interrupt_out(
593        &mut self,
594        ep_addr: Option<EndpointAddress>,
595        max_packet_size: u16,
596        interval_ms: u8,
597    ) -> D::EndpointOut {
598        self.endpoint_out(
599            EndpointType::Interrupt,
600            ep_addr,
601            max_packet_size,
602            interval_ms,
603            SynchronizationType::NoSynchronization,
604            UsageType::DataEndpoint,
605            &[],
606        )
607    }
608
609    /// Allocate a ISOCHRONOUS IN endpoint and write its descriptor.
610    ///
611    /// Descriptors are written in the order builder functions are called. Note that some
612    /// classes care about the order.
613    pub fn endpoint_isochronous_in(
614        &mut self,
615        ep_addr: Option<EndpointAddress>,
616        max_packet_size: u16,
617        interval_ms: u8,
618        synchronization_type: SynchronizationType,
619        usage_type: UsageType,
620        extra_fields: &[u8],
621    ) -> D::EndpointIn {
622        self.endpoint_in(
623            EndpointType::Isochronous,
624            ep_addr,
625            max_packet_size,
626            interval_ms,
627            synchronization_type,
628            usage_type,
629            extra_fields,
630        )
631    }
632
633    /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor.
634    pub fn endpoint_isochronous_out(
635        &mut self,
636        ep_addr: Option<EndpointAddress>,
637        max_packet_size: u16,
638        interval_ms: u8,
639        synchronization_type: SynchronizationType,
640        usage_type: UsageType,
641        extra_fields: &[u8],
642    ) -> D::EndpointOut {
643        self.endpoint_out(
644            EndpointType::Isochronous,
645            ep_addr,
646            max_packet_size,
647            interval_ms,
648            synchronization_type,
649            usage_type,
650            extra_fields,
651        )
652    }
653}