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, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START};
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!("if composite_with_iads is set, you must set device_class = 0xEF, device_sub_class = 0x02, device_protocol = 0x01");
180        }
181
182        assert!(
183            config.max_power <= 500,
184            "The maximum allowed value for `max_power` is 500mA"
185        );
186
187        match config.max_packet_size_0 {
188            8 | 16 | 32 | 64 => {}
189            _ => panic!("invalid max_packet_size_0, the allowed values are 8, 16, 32 or 64"),
190        }
191
192        let mut config_descriptor = DescriptorWriter::new(config_descriptor_buf);
193        let mut bos_descriptor = BosWriter::new(DescriptorWriter::new(bos_descriptor_buf));
194
195        config_descriptor.configuration(&config);
196        bos_descriptor.bos();
197
198        Builder {
199            driver,
200            config,
201            interfaces: Vec::new(),
202            handlers: Vec::new(),
203            control_buf,
204            next_string_index: STRING_INDEX_CUSTOM_START,
205
206            config_descriptor,
207            bos_descriptor,
208
209            msos_descriptor: MsOsDescriptorWriter::new(msos_descriptor_buf),
210        }
211    }
212
213    /// Creates the [`UsbDevice`] instance with the configuration in this builder.
214    pub fn build(mut self) -> UsbDevice<'d, D> {
215        let msos_descriptor = self.msos_descriptor.build(&mut self.bos_descriptor);
216
217        self.config_descriptor.end_configuration();
218        self.bos_descriptor.end_bos();
219
220        // Log the number of allocator bytes actually used in descriptor buffers
221        trace!("USB: config_descriptor used: {}", self.config_descriptor.position());
222        trace!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position());
223        trace!("USB: msos_descriptor used: {}", msos_descriptor.len());
224        trace!("USB: control_buf size: {}", self.control_buf.len());
225
226        UsbDevice::build(
227            self.driver,
228            self.config,
229            self.handlers,
230            self.config_descriptor.into_buf(),
231            self.bos_descriptor.writer.into_buf(),
232            msos_descriptor,
233            self.interfaces,
234            self.control_buf,
235        )
236    }
237
238    /// Returns the size of the control request data buffer. Can be used by
239    /// classes to validate the buffer is large enough for their needs.
240    pub fn control_buf_len(&self) -> usize {
241        self.control_buf.len()
242    }
243
244    /// Add an USB function.
245    ///
246    /// If [`Config::composite_with_iads`] is set, this will add an IAD descriptor
247    /// with the given class/subclass/protocol, associating all the child interfaces.
248    ///
249    /// If it's not set, no IAD descriptor is added.
250    pub fn function(&mut self, class: u8, subclass: u8, protocol: u8) -> FunctionBuilder<'_, 'd, D> {
251        let first_interface = InterfaceNumber::new(self.interfaces.len() as u8);
252        let iface_count_index = if self.config.composite_with_iads {
253            self.config_descriptor
254                .iad(first_interface, 0, class, subclass, protocol);
255
256            Some(self.config_descriptor.position() - 5)
257        } else {
258            None
259        };
260
261        FunctionBuilder {
262            builder: self,
263            iface_count_index,
264
265            first_interface,
266        }
267    }
268
269    /// Add a Handler.
270    ///
271    /// The Handler is called on some USB bus events, and to handle all control requests not already
272    /// handled by the USB stack.
273    pub fn handler(&mut self, handler: &'d mut dyn Handler) {
274        assert!(
275            self.handlers.push(handler).is_ok(),
276            "embassy-usb: handler list full. Increase the `max_handler_count` compile-time setting. Current value: {}",
277            MAX_HANDLER_COUNT
278        );
279    }
280
281    /// Allocates a new string index.
282    pub fn string(&mut self) -> StringIndex {
283        let index = self.next_string_index;
284        self.next_string_index += 1;
285        StringIndex::new(index)
286    }
287
288    /// Add an MS OS 2.0 Descriptor Set.
289    ///
290    /// Panics if called more than once.
291    pub fn msos_descriptor(&mut self, windows_version: u32, vendor_code: u8) {
292        self.msos_descriptor.header(windows_version, vendor_code);
293    }
294
295    /// Add an MS OS 2.0 Device Level Feature Descriptor.
296    pub fn msos_feature<T: DeviceLevelDescriptor>(&mut self, desc: T) {
297        self.msos_descriptor.device_feature(desc);
298    }
299
300    /// Gets the underlying [`MsOsDescriptorWriter`] to allow adding subsets and features for classes that
301    /// do not add their own.
302    pub fn msos_writer(&mut self) -> &mut MsOsDescriptorWriter<'d> {
303        &mut self.msos_descriptor
304    }
305}
306
307/// Function builder.
308///
309/// A function is a logical grouping of interfaces that perform a given USB function.
310/// If [`Config::composite_with_iads`] is set, each function will have an IAD descriptor.
311/// If not, functions will not be visible as descriptors.
312pub struct FunctionBuilder<'a, 'd, D: Driver<'d>> {
313    builder: &'a mut Builder<'d, D>,
314    iface_count_index: Option<usize>,
315
316    first_interface: InterfaceNumber,
317}
318
319impl<'a, 'd, D: Driver<'d>> Drop for FunctionBuilder<'a, 'd, D> {
320    fn drop(&mut self) {
321        self.builder.msos_descriptor.end_function();
322    }
323}
324
325impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> {
326    /// Add an interface to the function.
327    ///
328    /// Interface numbers are guaranteed to be allocated consecutively, starting from 0.
329    pub fn interface(&mut self) -> InterfaceBuilder<'_, 'd, D> {
330        if let Some(i) = self.iface_count_index {
331            self.builder.config_descriptor.buf[i] += 1;
332        }
333
334        let number = self.builder.interfaces.len() as _;
335        let iface = Interface {
336            current_alt_setting: 0,
337            num_alt_settings: 0,
338        };
339
340        assert!(self.builder.interfaces.push(iface).is_ok(),
341            "embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}",
342            MAX_INTERFACE_COUNT
343        );
344
345        InterfaceBuilder {
346            builder: self.builder,
347            interface_number: InterfaceNumber::new(number),
348            next_alt_setting_number: 0,
349        }
350    }
351
352    /// Add an MS OS 2.0 Function Level Feature Descriptor.
353    pub fn msos_feature<T: FunctionLevelDescriptor>(&mut self, desc: T) {
354        if !self.builder.msos_descriptor.is_in_config_subset() {
355            self.builder.msos_descriptor.configuration(0);
356        }
357
358        if !self.builder.msos_descriptor.is_in_function_subset() {
359            self.builder.msos_descriptor.function(self.first_interface);
360        }
361
362        self.builder.msos_descriptor.function_feature(desc);
363    }
364}
365
366/// Interface builder.
367pub struct InterfaceBuilder<'a, 'd, D: Driver<'d>> {
368    builder: &'a mut Builder<'d, D>,
369    interface_number: InterfaceNumber,
370    next_alt_setting_number: u8,
371}
372
373impl<'a, 'd, D: Driver<'d>> InterfaceBuilder<'a, 'd, D> {
374    /// Get the interface number.
375    pub const fn interface_number(&self) -> InterfaceNumber {
376        self.interface_number
377    }
378
379    /// Allocates a new string index.
380    pub fn string(&mut self) -> StringIndex {
381        self.builder.string()
382    }
383
384    /// Add an alternate setting to the interface and write its descriptor.
385    ///
386    /// Alternate setting numbers are guaranteed to be allocated consecutively, starting from 0.
387    ///
388    /// The first alternate setting, with number 0, is the default one.
389    pub fn alt_setting(
390        &mut self,
391        class: u8,
392        subclass: u8,
393        protocol: u8,
394        interface_string: Option<StringIndex>,
395    ) -> InterfaceAltBuilder<'_, 'd, D> {
396        let number = self.next_alt_setting_number;
397        self.next_alt_setting_number += 1;
398        self.builder.interfaces[self.interface_number.0 as usize].num_alt_settings += 1;
399
400        self.builder.config_descriptor.interface_alt(
401            self.interface_number,
402            number,
403            class,
404            subclass,
405            protocol,
406            interface_string,
407        );
408
409        InterfaceAltBuilder {
410            builder: self.builder,
411            interface_number: self.interface_number,
412            alt_setting_number: number,
413        }
414    }
415}
416
417/// Interface alternate setting builder.
418pub struct InterfaceAltBuilder<'a, 'd, D: Driver<'d>> {
419    builder: &'a mut Builder<'d, D>,
420    interface_number: InterfaceNumber,
421    alt_setting_number: u8,
422}
423
424impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> {
425    /// Get the interface number.
426    pub const fn interface_number(&self) -> InterfaceNumber {
427        self.interface_number
428    }
429
430    /// Get the alternate setting number.
431    pub const fn alt_setting_number(&self) -> u8 {
432        self.alt_setting_number
433    }
434
435    /// Add a custom descriptor to this alternate setting.
436    ///
437    /// Descriptors are written in the order builder functions are called. Note that some
438    /// classes care about the order.
439    pub fn descriptor(&mut self, descriptor_type: u8, descriptor: &[u8]) {
440        self.builder.config_descriptor.write(descriptor_type, descriptor, &[]);
441    }
442
443    /// Add a custom Binary Object Store (BOS) descriptor to this alternate setting.
444    pub fn bos_capability(&mut self, capability_type: u8, capability: &[u8]) {
445        self.builder.bos_descriptor.capability(capability_type, capability);
446    }
447
448    /// Write a custom endpoint descriptor for a certain endpoint.
449    ///
450    /// This can be necessary, if the endpoint descriptors can only be written
451    /// after the endpoint was created. As an example, an endpoint descriptor
452    /// may contain the address of an endpoint that was allocated earlier.
453    pub fn endpoint_descriptor(
454        &mut self,
455        endpoint: &EndpointInfo,
456        synchronization_type: SynchronizationType,
457        usage_type: UsageType,
458        extra_fields: &[u8],
459    ) {
460        self.builder
461            .config_descriptor
462            .endpoint(endpoint, synchronization_type, usage_type, extra_fields);
463    }
464
465    /// Allocate an IN endpoint, without writing its descriptor.
466    ///
467    /// Used for granular control over the order of endpoint and descriptor creation.
468    pub fn alloc_endpoint_in(
469        &mut self,
470        ep_type: EndpointType,
471        ep_addr: Option<EndpointAddress>,
472        max_packet_size: u16,
473        interval_ms: u8,
474    ) -> D::EndpointIn {
475        let ep = self
476            .builder
477            .driver
478            .alloc_endpoint_in(ep_type, ep_addr, max_packet_size, interval_ms)
479            .expect("alloc_endpoint_in failed");
480
481        ep
482    }
483
484    fn endpoint_in(
485        &mut self,
486        ep_type: EndpointType,
487        ep_addr: Option<EndpointAddress>,
488        max_packet_size: u16,
489        interval_ms: u8,
490        synchronization_type: SynchronizationType,
491        usage_type: UsageType,
492        extra_fields: &[u8],
493    ) -> D::EndpointIn {
494        let ep = self.alloc_endpoint_in(ep_type, ep_addr, max_packet_size, interval_ms);
495        self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields);
496
497        ep
498    }
499
500    /// Allocate an OUT endpoint, without writing its descriptor.
501    ///
502    /// Use for granular control over the order of endpoint and descriptor creation.
503    pub fn alloc_endpoint_out(
504        &mut self,
505        ep_type: EndpointType,
506        ep_addr: Option<EndpointAddress>,
507        max_packet_size: u16,
508        interval_ms: u8,
509    ) -> D::EndpointOut {
510        let ep = self
511            .builder
512            .driver
513            .alloc_endpoint_out(ep_type, ep_addr, max_packet_size, interval_ms)
514            .expect("alloc_endpoint_out failed");
515
516        ep
517    }
518
519    fn endpoint_out(
520        &mut self,
521        ep_type: EndpointType,
522        ep_addr: Option<EndpointAddress>,
523        max_packet_size: u16,
524        interval_ms: u8,
525        synchronization_type: SynchronizationType,
526        usage_type: UsageType,
527        extra_fields: &[u8],
528    ) -> D::EndpointOut {
529        let ep = self.alloc_endpoint_out(ep_type, ep_addr, max_packet_size, interval_ms);
530        self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields);
531
532        ep
533    }
534
535    /// Allocate a BULK IN endpoint and write its descriptor.
536    ///
537    /// Descriptors are written in the order builder functions are called. Note that some
538    /// classes care about the order.
539    pub fn endpoint_bulk_in(&mut self, ep_addr: Option<EndpointAddress>, max_packet_size: u16) -> D::EndpointIn {
540        self.endpoint_in(
541            EndpointType::Bulk,
542            ep_addr,
543            max_packet_size,
544            0,
545            SynchronizationType::NoSynchronization,
546            UsageType::DataEndpoint,
547            &[],
548        )
549    }
550
551    /// Allocate a BULK OUT endpoint and write its descriptor.
552    ///
553    /// Descriptors are written in the order builder functions are called. Note that some
554    /// classes care about the order.
555    pub fn endpoint_bulk_out(&mut self, ep_addr: Option<EndpointAddress>, max_packet_size: u16) -> D::EndpointOut {
556        self.endpoint_out(
557            EndpointType::Bulk,
558            ep_addr,
559            max_packet_size,
560            0,
561            SynchronizationType::NoSynchronization,
562            UsageType::DataEndpoint,
563            &[],
564        )
565    }
566
567    /// Allocate a INTERRUPT IN endpoint and write its descriptor.
568    ///
569    /// Descriptors are written in the order builder functions are called. Note that some
570    /// classes care about the order.
571    pub fn endpoint_interrupt_in(
572        &mut self,
573        ep_addr: Option<EndpointAddress>,
574        max_packet_size: u16,
575        interval_ms: u8,
576    ) -> D::EndpointIn {
577        self.endpoint_in(
578            EndpointType::Interrupt,
579            ep_addr,
580            max_packet_size,
581            interval_ms,
582            SynchronizationType::NoSynchronization,
583            UsageType::DataEndpoint,
584            &[],
585        )
586    }
587
588    /// Allocate a INTERRUPT OUT endpoint and write its descriptor.
589    pub fn endpoint_interrupt_out(
590        &mut self,
591        ep_addr: Option<EndpointAddress>,
592        max_packet_size: u16,
593        interval_ms: u8,
594    ) -> D::EndpointOut {
595        self.endpoint_out(
596            EndpointType::Interrupt,
597            ep_addr,
598            max_packet_size,
599            interval_ms,
600            SynchronizationType::NoSynchronization,
601            UsageType::DataEndpoint,
602            &[],
603        )
604    }
605
606    /// Allocate a ISOCHRONOUS IN endpoint and write its descriptor.
607    ///
608    /// Descriptors are written in the order builder functions are called. Note that some
609    /// classes care about the order.
610    pub fn endpoint_isochronous_in(
611        &mut self,
612        ep_addr: Option<EndpointAddress>,
613        max_packet_size: u16,
614        interval_ms: u8,
615        synchronization_type: SynchronizationType,
616        usage_type: UsageType,
617        extra_fields: &[u8],
618    ) -> D::EndpointIn {
619        self.endpoint_in(
620            EndpointType::Isochronous,
621            ep_addr,
622            max_packet_size,
623            interval_ms,
624            synchronization_type,
625            usage_type,
626            extra_fields,
627        )
628    }
629
630    /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor.
631    pub fn endpoint_isochronous_out(
632        &mut self,
633        ep_addr: Option<EndpointAddress>,
634        max_packet_size: u16,
635        interval_ms: u8,
636        synchronization_type: SynchronizationType,
637        usage_type: UsageType,
638        extra_fields: &[u8],
639    ) -> D::EndpointOut {
640        self.endpoint_out(
641            EndpointType::Isochronous,
642            ep_addr,
643            max_packet_size,
644            interval_ms,
645            synchronization_type,
646            usage_type,
647            extra_fields,
648        )
649    }
650}