Skip to main content

embassy_usb_host/
control.rs

1//! Standard USB control request builders and the `ControlPipeExt` trait.
2
3use core::num::NonZeroU8;
4
5use embassy_usb::control::Request;
6use embassy_usb_driver::Direction;
7pub use embassy_usb_driver::host::pipe;
8use embassy_usb_driver::host::{HostError, UsbPipe};
9
10use crate::descriptor::{USBDescriptor, descriptor_type};
11
12/// Recipient of a USB control request.
13///
14/// This is the 5-bit Recipient sub-field of `bmRequestType`
15/// (USB 2.0 spec Table 9-2, bits 4..0). The discriminant of each variant
16/// matches the on-wire value.
17#[repr(u8)]
18#[derive(Copy, Clone, Eq, PartialEq, Debug)]
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20pub enum Recipient {
21    /// The request is intended for the entire device.
22    Device = 0,
23    /// The request is intended for an interface.
24    Interface = 1,
25    /// The request is intended for an endpoint.
26    Endpoint = 2,
27    /// The recipient of the request is unspecified.
28    Other = 3,
29    /// Any reserved recipient value (4..=31).
30    Reserved = 4,
31}
32
33/// Type of a USB control request.
34///
35/// This is the 2-bit Type sub-field of `bmRequestType`
36/// (USB 2.0 spec Table 9-2, bits 6..5). The discriminant of each variant
37/// matches the on-wire value.
38#[repr(u8)]
39#[derive(Copy, Clone, Eq, PartialEq, Debug)]
40#[cfg_attr(feature = "defmt", derive(defmt::Format))]
41pub enum ControlType {
42    /// A standard USB request (USB 2.0 spec §9.4).
43    Standard = 0,
44    /// A class-specific request.
45    Class = 1,
46    /// A vendor-specific request.
47    Vendor = 2,
48    /// Reserved.
49    Reserved = 3,
50}
51
52/// USB control request type (`bmRequestType`).
53///
54/// Encodes the three sub-fields of `bmRequestType` (USB 2.0 spec Table 9-2):
55/// direction (bit 7), type (bits 6..5) and recipient (bits 4..0).
56#[derive(Copy, Clone, Eq, PartialEq, Debug)]
57#[cfg_attr(feature = "defmt", derive(defmt::Format))]
58pub struct RequestType {
59    /// Transfer direction (IN = device→host, OUT = host→device).
60    pub direction: Direction,
61
62    /// Whether this is a standard, class, vendor, or reserved request.
63    pub control_type: ControlType,
64
65    /// Recipient of the request.
66    pub recipient: Recipient,
67}
68
69impl RequestType {
70    /// Encode this request type to its wire-format `bmRequestType` byte.
71    pub const fn to_bits(self) -> u8 {
72        let d = match self.direction {
73            Direction::Out => 0,
74            Direction::In => 1 << 7,
75        };
76        let t = (self.control_type as u8) << 5;
77        let r = self.recipient as u8;
78        d | t | r
79    }
80
81    /// Decode a wire-format `bmRequestType` byte.
82    ///
83    /// Reserved type values decode to [`ControlType::Reserved`]; reserved
84    /// recipient values (4..=31) decode to [`Recipient::Reserved`].
85    pub const fn from_bits(b: u8) -> Self {
86        let direction = if b & 0x80 != 0 { Direction::In } else { Direction::Out };
87        let control_type = match (b >> 5) & 0b11 {
88            0 => ControlType::Standard,
89            1 => ControlType::Class,
90            2 => ControlType::Vendor,
91            _ => ControlType::Reserved,
92        };
93        let recipient = match b & 0b1_1111 {
94            0 => Recipient::Device,
95            1 => Recipient::Interface,
96            2 => Recipient::Endpoint,
97            3 => Recipient::Other,
98            _ => Recipient::Reserved,
99        };
100        Self {
101            direction,
102            control_type,
103            recipient,
104        }
105    }
106}
107
108/// USB Control Setup Packet.
109///
110/// Convenience type for building SETUP packets; serialize with
111/// [`SetupPacket::to_bytes`] before passing to a USB driver.
112///
113/// Setup data format is described in USB spec Table 9-2.
114#[cfg_attr(feature = "defmt", derive(defmt::Format))]
115#[derive(Copy, Clone, Eq, PartialEq, Debug)]
116pub struct SetupPacket {
117    /// `bmRequestType` - Request characteristics: direction, type, recipient.
118    ///
119    /// See [`RequestType`] for details.
120    pub request_type: RequestType,
121
122    /// `bRequest` - Request code.
123    ///
124    /// See Table 9-3 of USB spec for standard ones.
125    pub request: u8,
126
127    /// `wValue` - Use depending on request field.
128    pub value: u16,
129
130    /// `wIndex` - Use depending on request field.
131    pub index: u16,
132
133    /// `wLength` - Number of bytes to transfer in data stage if there is one.
134    pub length: u16,
135}
136
137/// HID class descriptor type: Report (HID 1.11 §7.1.1).
138const HID_REPORT_DESCRIPTOR_TYPE: u8 = 0x22;
139
140impl SetupPacket {
141    /// Serialize this SETUP packet to its 8-byte wire format.
142    ///
143    /// Multi-byte fields are emitted in little-endian order, as required by
144    /// USB 2.0 spec §8.1.
145    pub const fn to_bytes(self) -> [u8; 8] {
146        let v = self.value.to_le_bytes();
147        let i = self.index.to_le_bytes();
148        let l = self.length.to_le_bytes();
149        [
150            self.request_type.to_bits(),
151            self.request,
152            v[0],
153            v[1],
154            i[0],
155            i[1],
156            l[0],
157            l[1],
158        ]
159    }
160
161    /// Deserialize a wire format SETUP packet.
162    ///
163    /// Multi-byte fields are interpreted in little-endian order, as required by
164    /// USB 2.0 spec §8.1.
165    pub const fn from_bytes(wire: [u8; 8]) -> Self {
166        Self {
167            request_type: RequestType::from_bits(wire[0]),
168            request: wire[1],
169            value: u16::from_le_bytes([wire[2], wire[3]]),
170            index: u16::from_le_bytes([wire[4], wire[5]]),
171            length: u16::from_le_bytes([wire[6], wire[7]]),
172        }
173    }
174
175    /// Build a GET_DESCRIPTOR SETUP packet delivered to the Device recipient.
176    ///
177    /// `class` selects Standard (`false`) vs Class (`true`) request type.
178    pub const fn get_descriptor(class: bool, desc_type: u8, index: u8, max_len: u16) -> Self {
179        Self {
180            request_type: RequestType {
181                direction: Direction::In,
182                control_type: if class {
183                    ControlType::Class
184                } else {
185                    ControlType::Standard
186                },
187                recipient: Recipient::Device,
188            },
189            request: Request::GET_DESCRIPTOR,
190            value: ((desc_type as u16) << 8) | index as u16,
191            index: 0,
192            length: max_len,
193        }
194    }
195
196    /// Build a GET_DESCRIPTOR(Device) SETUP packet.
197    pub const fn get_device_descriptor(max_len: u16) -> Self {
198        Self::get_descriptor(false, descriptor_type::DEVICE, 0, max_len)
199    }
200
201    /// Build a GET_DESCRIPTOR(Configuration) SETUP packet.
202    pub const fn get_config_descriptor(index: u8, max_len: u16) -> Self {
203        Self::get_descriptor(false, descriptor_type::CONFIGURATION, index, max_len)
204    }
205
206    /// Build a standard GET_DESCRIPTOR SETUP packet delivered to an Interface recipient.
207    ///
208    /// Used for interface-owned descriptors such as the HID Report Descriptor.
209    pub const fn get_interface_descriptor(desc_type: u8, interface: u16, max_len: u16) -> Self {
210        Self {
211            request_type: RequestType {
212                direction: Direction::In,
213                control_type: ControlType::Standard,
214                recipient: Recipient::Interface,
215            },
216            request: Request::GET_DESCRIPTOR,
217            value: (desc_type as u16) << 8,
218            index: interface,
219            length: max_len,
220        }
221    }
222
223    /// Build a GET_DESCRIPTOR(HID Report Descriptor) SETUP packet.
224    ///
225    /// `interface` is the HID interface number; `len` is from `HidInfo::report_descriptor_len`.
226    pub const fn get_hid_report_descriptor(interface: u8, len: u16) -> Self {
227        Self::get_interface_descriptor(HID_REPORT_DESCRIPTOR_TYPE, interface as u16, len)
228    }
229
230    /// Build a SET_ADDRESS SETUP packet.
231    pub const fn set_address(address: u8) -> Self {
232        Self {
233            request_type: RequestType {
234                direction: Direction::Out,
235                control_type: ControlType::Standard,
236                recipient: Recipient::Device,
237            },
238            request: Request::SET_ADDRESS,
239            value: address as u16,
240            index: 0,
241            length: 0,
242        }
243    }
244
245    /// Build a SET_CONFIGURATION SETUP packet.
246    pub const fn set_configuration(config_value: u8) -> Self {
247        Self {
248            request_type: RequestType {
249                direction: Direction::Out,
250                control_type: ControlType::Standard,
251                recipient: Recipient::Device,
252            },
253            request: Request::SET_CONFIGURATION,
254            value: config_value as u16,
255            index: 0,
256            length: 0,
257        }
258    }
259
260    /// Build a GET_CONFIGURATION SETUP packet.
261    pub const fn get_configuration() -> Self {
262        Self {
263            request_type: RequestType {
264                direction: Direction::In,
265                control_type: ControlType::Standard,
266                recipient: Recipient::Device,
267            },
268            request: Request::GET_CONFIGURATION,
269            value: 0,
270            index: 0,
271            length: 1,
272        }
273    }
274
275    /// Build a class-specific interface request SETUP packet, host-to-device.
276    ///
277    /// Pass `length = 0` for requests with no data stage.
278    pub const fn class_interface_out(request: u8, value: u16, interface: u16, length: u16) -> Self {
279        Self {
280            request_type: RequestType {
281                direction: Direction::Out,
282                control_type: ControlType::Class,
283                recipient: Recipient::Interface,
284            },
285            request,
286            value,
287            index: interface,
288            length,
289        }
290    }
291
292    /// Build a class-specific interface request SETUP packet, device-to-host.
293    pub const fn class_interface_in(request: u8, value: u16, interface: u16, length: u16) -> Self {
294        Self {
295            request_type: RequestType {
296                direction: Direction::In,
297                control_type: ControlType::Class,
298                recipient: Recipient::Interface,
299            },
300            request,
301            value,
302            index: interface,
303            length,
304        }
305    }
306
307    /// Build a vendor-specific interface request SETUP packet, host-to-device.
308    pub const fn vendor_interface_out(request: u8, value: u16, interface: u16, length: u16) -> Self {
309        Self {
310            request_type: RequestType {
311                direction: Direction::Out,
312                control_type: ControlType::Vendor,
313                recipient: Recipient::Interface,
314            },
315            request,
316            value,
317            index: interface,
318            length,
319        }
320    }
321
322    /// Build a vendor-specific interface request SETUP packet, device-to-host.
323    pub const fn vendor_interface_in(request: u8, value: u16, interface: u16, length: u16) -> Self {
324        Self {
325            request_type: RequestType {
326                direction: Direction::In,
327                control_type: ControlType::Vendor,
328                recipient: Recipient::Interface,
329            },
330            request,
331            value,
332            index: interface,
333            length,
334        }
335    }
336}
337
338// ── ControlPipeExt ─────────────────────────────────────────────────────────────
339
340/// Extension trait providing higher-level control request methods on a USB control pipe.
341pub trait ControlPipeExt<D: pipe::Direction>: UsbPipe<pipe::Control, D> {
342    /// Request and parse a fixed-size descriptor.
343    async fn request_descriptor<T: USBDescriptor, const SIZE: usize>(
344        &mut self,
345        index: u8,
346        class: bool,
347    ) -> Result<T, HostError>
348    where
349        D: pipe::IsIn,
350    {
351        let mut buf = [0u8; SIZE];
352        let setup = SetupPacket::get_descriptor(class, T::DESC_TYPE, index, SIZE as u16);
353        self.control_in(&setup.to_bytes(), &mut buf).await?;
354        trace!("Descriptor {}: {:?}", core::any::type_name::<T>(), buf);
355        T::try_from_bytes(&buf).map_err(|_| HostError::InvalidDescriptor)
356    }
357
358    /// Request the raw bytes of a descriptor by type and index.
359    async fn request_descriptor_bytes(&mut self, desc_type: u8, index: u8, buf: &mut [u8]) -> Result<usize, HostError>
360    where
361        D: pipe::IsIn,
362    {
363        let setup = SetupPacket::get_descriptor(false, desc_type, index, buf.len() as u16);
364        self.control_in(&setup.to_bytes(), buf)
365            .await
366            .map_err(HostError::PipeError)
367    }
368
369    /// Request the raw bytes of a class-specific interface descriptor.
370    async fn interface_request_descriptor_bytes<T: USBDescriptor>(
371        &mut self,
372        interface_num: u8,
373        buf: &mut [u8],
374    ) -> Result<usize, HostError>
375    where
376        D: pipe::IsIn,
377    {
378        let setup = SetupPacket::get_interface_descriptor(T::DESC_TYPE, interface_num as u16, buf.len() as u16);
379        self.control_in(&setup.to_bytes(), buf)
380            .await
381            .map_err(HostError::PipeError)
382    }
383
384    /// GET_CONFIGURATION — returns the active configuration value, or `None` if unconfigured.
385    async fn active_configuration_value(&mut self) -> Result<Option<NonZeroU8>, HostError>
386    where
387        D: pipe::IsIn,
388    {
389        let setup = SetupPacket::get_configuration();
390        let mut buf = [0u8; 1];
391        self.control_in(&setup.to_bytes(), &mut buf).await?;
392        Ok(NonZeroU8::new(buf[0]))
393    }
394
395    /// SET_CONFIGURATION.
396    async fn set_configuration(&mut self, config_no: u8) -> Result<(), HostError>
397    where
398        D: pipe::IsOut,
399    {
400        let setup = SetupPacket::set_configuration(config_no);
401        self.control_out(&setup.to_bytes(), &[]).await?;
402        Ok(())
403    }
404
405    /// SET_ADDRESS — assign the device a new address.
406    ///
407    /// # Warning
408    /// Breaks host channel state; use only during enumeration.
409    async fn device_set_address(&mut self, new_addr: u8) -> Result<(), HostError>
410    where
411        D: pipe::IsOut,
412    {
413        let setup = SetupPacket::set_address(new_addr);
414        self.control_out(&setup.to_bytes(), &[]).await?;
415        Ok(())
416    }
417
418    /// Class + Interface OUT request (no data stage).
419    async fn class_request_out(&mut self, request: u8, value: u16, index: u16, buf: &[u8]) -> Result<(), HostError>
420    where
421        D: pipe::IsOut,
422    {
423        let setup = SetupPacket::class_interface_out(request, value, index, buf.len() as u16);
424        self.control_out(&setup.to_bytes(), buf).await?;
425        Ok(())
426    }
427}
428
429impl<D: pipe::Direction, C> ControlPipeExt<D> for C where C: UsbPipe<pipe::Control, D> {}
430
431#[cfg(test)]
432mod tests {
433    use super::*;
434
435    #[test]
436    fn roundtrip_setup_packet() {
437        let directions = [Direction::In, Direction::Out];
438        let control_types = [ControlType::Standard, ControlType::Class, ControlType::Vendor];
439        let recipients = [
440            Recipient::Device,
441            Recipient::Interface,
442            Recipient::Endpoint,
443            Recipient::Other,
444        ];
445        for direction in directions {
446            for control_type in control_types {
447                for recipient in recipients {
448                    let setup = SetupPacket {
449                        request_type: RequestType {
450                            direction,
451                            control_type,
452                            recipient,
453                        },
454                        request: 0x11,
455                        value: 0x2233,
456                        index: 0x4455,
457                        length: 0x6677,
458                    };
459                    let bytes = setup.to_bytes();
460                    assert!(setup == SetupPacket::from_bytes(bytes));
461                }
462            }
463        }
464    }
465}