usb_device/device_builder.rs
1use crate::bus::{UsbBus, UsbBusAllocator};
2use crate::descriptor::lang_id::LangID;
3use crate::device::{Config, UsbDevice, UsbRev};
4
5/// A USB vendor ID and product ID pair.
6pub struct UsbVidPid(pub u16, pub u16);
7
8/// Used to build new [`UsbDevice`]s.
9pub struct UsbDeviceBuilder<'a, B: UsbBus> {
10 alloc: &'a UsbBusAllocator<B>,
11 config: Config<'a>,
12}
13
14macro_rules! builder_fields {
15 ( $( $(#[$meta:meta])* $name:ident: $type:ty, )* ) => {
16 $(
17 $(#[$meta])*
18 pub fn $name(mut self, $name: $type) -> Self {
19 self.config.$name = $name;
20 self
21 }
22 )*
23 }
24}
25
26#[derive(Copy, Clone, Debug, PartialEq)]
27/// Error type for the USB device builder
28pub enum BuilderError {
29 /// String descriptors were provided in more languages than are supported
30 TooManyLanguages,
31 /// Control endpoint can only be 8, 16, 32, or 64 byte max packet size
32 InvalidPacketSize,
33 /// Configuration specifies higher USB power draw than allowed
34 PowerTooHigh,
35}
36
37/// Provides basic string descriptors about the device, including the manufacturer, product name,
38/// and serial number of the device in a specified language.
39#[derive(Copy, Clone, Debug, PartialEq)]
40pub struct StringDescriptors<'a> {
41 pub(crate) id: LangID,
42 pub(crate) serial: Option<&'a str>,
43 pub(crate) product: Option<&'a str>,
44 pub(crate) manufacturer: Option<&'a str>,
45}
46
47impl<'a> Default for StringDescriptors<'a> {
48 fn default() -> Self {
49 Self::new(LangID::EN_US)
50 }
51}
52
53impl<'a> StringDescriptors<'a> {
54 /// Create a new descriptor list with the provided language.
55 pub fn new(lang_id: LangID) -> Self {
56 Self {
57 id: lang_id,
58 serial: None,
59 product: None,
60 manufacturer: None,
61 }
62 }
63
64 /// Specify the serial number for this language.
65 pub fn serial_number(mut self, serial: &'a str) -> Self {
66 self.serial.replace(serial);
67 self
68 }
69
70 /// Specify the manufacturer name for this language.
71 pub fn manufacturer(mut self, manufacturer: &'a str) -> Self {
72 self.manufacturer.replace(manufacturer);
73 self
74 }
75
76 /// Specify the product name for this language.
77 pub fn product(mut self, product: &'a str) -> Self {
78 self.product.replace(product);
79 self
80 }
81}
82
83impl<'a, B: UsbBus> UsbDeviceBuilder<'a, B> {
84 /// Creates a builder for constructing a new [`UsbDevice`].
85 pub fn new(alloc: &'a UsbBusAllocator<B>, vid_pid: UsbVidPid) -> UsbDeviceBuilder<'a, B> {
86 UsbDeviceBuilder {
87 alloc,
88 config: Config {
89 device_class: 0x00,
90 device_sub_class: 0x00,
91 device_protocol: 0x00,
92 max_packet_size_0: 8,
93 vendor_id: vid_pid.0,
94 product_id: vid_pid.1,
95 usb_rev: UsbRev::Usb210,
96 device_release: 0x0010,
97 string_descriptors: heapless::Vec::new(),
98 self_powered: false,
99 supports_remote_wakeup: false,
100 composite_with_iads: false,
101 max_power: 50,
102 },
103 }
104 }
105
106 /// Creates the [`UsbDevice`] instance with the configuration in this builder.
107 pub fn build(self) -> UsbDevice<'a, B> {
108 UsbDevice::build(self.alloc, self.config)
109 }
110
111 builder_fields! {
112 /// Sets the device class code assigned by USB.org. Set to `0xff` for vendor-specific
113 /// devices that do not conform to any class.
114 ///
115 /// Default: `0x00` (class code specified by interfaces)
116 device_class: u8,
117
118 /// Sets the device sub-class code. Depends on class.
119 ///
120 /// Default: `0x00`
121 device_sub_class: u8,
122
123 /// Sets the device protocol code. Depends on class and sub-class.
124 ///
125 /// Default: `0x00`
126 device_protocol: u8,
127
128 /// Sets the device release version in BCD.
129 ///
130 /// Default: `0x0010` ("0.1")
131 device_release: u16,
132
133 /// Sets whether the device may have an external power source.
134 ///
135 /// This should be set to `true` even if the device is sometimes self-powered and may not
136 /// always draw power from the USB bus.
137 ///
138 /// Default: `false`
139 ///
140 /// See also: `max_power`
141 self_powered: bool,
142
143 /// Sets whether the device supports remotely waking up the host is requested.
144 ///
145 /// Default: `false`
146 supports_remote_wakeup: bool,
147
148 /// Sets which Usb 2 revision to comply to.
149 ///
150 /// Default: `UsbRev::Usb210`
151 usb_rev: UsbRev,
152 }
153
154 /// Configures the device as a composite device with interface association descriptors.
155 pub fn composite_with_iads(mut self) -> Self {
156 // Magic values specified in USB-IF ECN on IADs.
157 self.config.device_class = 0xEF;
158 self.config.device_sub_class = 0x02;
159 self.config.device_protocol = 0x01;
160
161 self.config.composite_with_iads = true;
162 self
163 }
164
165 /// Specify the strings for the device.
166 ///
167 /// # Note
168 /// Up to 16 languages may be provided.
169 pub fn strings(mut self, descriptors: &[StringDescriptors<'a>]) -> Result<Self, BuilderError> {
170 // The 16 language limit comes from the size of the buffer used to provide the list of
171 // language descriptors to the host.
172 self.config.string_descriptors =
173 heapless::Vec::from_slice(descriptors).map_err(|_| BuilderError::TooManyLanguages)?;
174
175 Ok(self)
176 }
177
178 /// Sets the maximum packet size in bytes for the control endpoint 0.
179 ///
180 /// Valid values are 8, 16, 32 and 64. There's generally no need to change this from the default
181 /// value of 8 bytes unless a class uses control transfers for sending large amounts of data, in
182 /// which case using a larger packet size may be more efficient.
183 ///
184 /// Default: 8 bytes
185 pub fn max_packet_size_0(mut self, max_packet_size_0: u8) -> Result<Self, BuilderError> {
186 match max_packet_size_0 {
187 8 | 16 | 32 | 64 => {}
188 _ => return Err(BuilderError::InvalidPacketSize),
189 }
190
191 self.config.max_packet_size_0 = max_packet_size_0;
192 Ok(self)
193 }
194
195 /// Sets the maximum current drawn from the USB bus by the device in milliamps.
196 ///
197 /// The default is 100 mA. If your device always uses an external power source and never draws
198 /// power from the USB bus, this can be set to 0.
199 ///
200 /// See also: `self_powered`
201 ///
202 /// Default: 100mA
203 pub fn max_power(mut self, max_power_ma: usize) -> Result<Self, BuilderError> {
204 if max_power_ma > 500 {
205 return Err(BuilderError::PowerTooHigh);
206 }
207
208 self.config.max_power = (max_power_ma / 2) as u8;
209 Ok(self)
210 }
211}