Skip to main content

cotton_usb_host/
wire.rs

1use crate::debug;
2
3/// A SETUP packet as transmitted on control endpoints.
4///
5/// All transactions on control endpoints start with a SETUP packet of
6/// this format. (Some are then followed by IN or OUT data packets, but
7/// others are not).
8///
9/// The format of this packet (and the un-Rust-like names of its
10/// fields) are defined in the USB 2.0 specification, section 9.3.
11/// Other sections of the USB specification, and of the specifications
12/// of particular device classes, dictate what to put in these fields.
13///
14/// Control transactions are performed using
15/// [`UsbBus::control_transfer()`](crate::usb_bus::UsbBus::control_transfer).
16///
17/// For instance, here is how to read the MAC address of an AX88772
18/// USB-to-Ethernet adaptor:
19///
20/// ```no_run
21/// # use cotton_usb_host::host_controller::{UsbError, HostController, DataPhase};
22/// # use cotton_usb_host::usb_bus::{UsbBus, UsbDevice, DeviceInfo};
23/// # use cotton_usb_host::wire::{SetupPacket, DEVICE_TO_HOST, VENDOR_REQUEST};
24/// # use futures::{Stream, StreamExt};
25/// # async fn foo<HC: HostController>(bus: UsbBus<HC>, device: UsbDevice, info: DeviceInfo) {
26/// let mut data = [0u8; 6];
27/// let rc = bus.control_transfer(
28///         &device,
29///         SetupPacket {
30///             bmRequestType: DEVICE_TO_HOST | VENDOR_REQUEST,
31///             bRequest: 0x13,
32///             wValue: 0,
33///             wIndex: 0,
34///             wLength: 6,
35///         },
36///         DataPhase::In(&mut data),
37///     )
38///     .await;
39/// # }
40/// ```
41///
42/// Here, the "Request Type" indicates a vendor-specific (AX88772-specific)
43/// request, and the "0x13" is taken from the AX88772 datasheet and is the
44/// code for "read MAC address". And a MAC address is 6 bytes long, as seen
45/// in `wLength`.
46///
47#[repr(C)]
48#[cfg_attr(feature = "defmt", derive(defmt::Format))]
49#[cfg_attr(feature = "std", derive(Debug))]
50#[allow(non_snake_case)] // These names are from USB 2.0 table 9-2
51pub struct SetupPacket {
52    /// The type and specific target of the request.
53    pub bmRequestType: u8,
54    /// The particular request.
55    pub bRequest: u8,
56    /// A parameter to the request.
57    pub wValue: u16,
58    /// A second parameter to the request.
59    pub wIndex: u16,
60    /// The length of the subsequent IN or OUT data phase; can be zero
61    /// if the setup packet itself contains all the required
62    /// information.
63    pub wLength: u16,
64}
65
66/// A device descriptor, see USB 2.0 section 9.6.1
67#[repr(C)]
68#[cfg_attr(feature = "defmt", derive(defmt::Format))]
69#[cfg_attr(feature = "std", derive(Debug))]
70#[allow(non_snake_case)] // These names are from USB 2.0 table 9-8
71#[allow(missing_docs)]
72pub struct DeviceDescriptor {
73    pub bLength: u8,
74    pub bDescriptorType: u8,
75    pub bcdUSB: [u8; 2],
76    pub bDeviceClass: u8,
77    pub bDeviceSubClass: u8,
78    pub bDeviceProtocol: u8,
79    pub bMaxPacketSize0: u8,
80
81    pub idVendor: [u8; 2],
82    pub idProduct: [u8; 2],
83    pub bcdDevice: [u8; 2],
84    pub iManufacturer: u8,
85    pub iProduct: u8,
86    pub iSerialNumber: u8,
87    pub bNumConfigurations: u8,
88}
89
90/// A configuration descriptor, see USB 2.0 section 9.6.3
91#[repr(C)]
92#[cfg_attr(feature = "defmt", derive(defmt::Format))]
93#[cfg_attr(feature = "std", derive(Debug))]
94#[derive(Copy, Clone)]
95#[allow(non_snake_case)] // These names are from USB 2.0 table 9-10
96#[allow(missing_docs)]
97pub struct ConfigurationDescriptor {
98    pub bLength: u8,
99    pub bDescriptorType: u8,
100    pub wTotalLength: [u8; 2],
101    pub bNumInterfaces: u8,
102    pub bConfigurationValue: u8,
103    pub iConfiguration: u8,
104    pub bmAttributes: u8,
105    pub bMaxPower: u8,
106}
107
108// SAFETY: all fields zeroable
109unsafe impl bytemuck::Zeroable for ConfigurationDescriptor {}
110// SAFETY: no padding, no disallowed bit patterns
111unsafe impl bytemuck::Pod for ConfigurationDescriptor {}
112
113/// An interface descriptor, see USB 2.0 section 9.6.5
114#[repr(C)]
115#[cfg_attr(feature = "defmt", derive(defmt::Format))]
116#[cfg_attr(feature = "std", derive(Debug))]
117#[derive(Copy, Clone)]
118#[allow(non_snake_case)] // These names are from USB 2.0 table 9-12
119#[allow(missing_docs)]
120pub struct InterfaceDescriptor {
121    pub bLength: u8,
122    pub bDescriptorType: u8,
123    pub bInterfaceNumber: u8,
124    pub bAlternateSetting: u8,
125    pub bNumEndpoints: u8,
126    pub bInterfaceClass: u8,
127    pub bInterfaceSubClass: u8,
128    pub bInterfaceProtocol: u8,
129    pub iInterface: u8,
130}
131
132// SAFETY: all fields zeroable
133unsafe impl bytemuck::Zeroable for InterfaceDescriptor {}
134// SAFETY: no padding, no disallowed bit patterns
135unsafe impl bytemuck::Pod for InterfaceDescriptor {}
136
137/// An endpoint descriptor, see USB 2.0 section 9.6.6
138#[repr(C)]
139#[cfg_attr(feature = "defmt", derive(defmt::Format))]
140#[cfg_attr(feature = "std", derive(Debug))]
141#[derive(Copy, Clone)]
142#[allow(non_snake_case)] // These names are from USB 2.0 table 9-13
143#[allow(missing_docs)]
144pub struct EndpointDescriptor {
145    pub bLength: u8,
146    pub bDescriptorType: u8,
147    pub bEndpointAddress: u8,
148    pub bmAttributes: u8,
149    pub wMaxPacketSize: [u8; 2],
150    pub bInterval: u8,
151}
152
153// SAFETY: all fields zeroable
154unsafe impl bytemuck::Zeroable for EndpointDescriptor {}
155// SAFETY: no padding, no disallowed bit patterns
156unsafe impl bytemuck::Pod for EndpointDescriptor {}
157
158/// A hub descriptor, see USB 2.0 section 11.23.2.1
159#[repr(C)]
160#[cfg_attr(feature = "defmt", derive(defmt::Format))]
161#[cfg_attr(feature = "std", derive(Debug))]
162#[derive(Copy, Clone)]
163#[allow(non_snake_case)] // These names are from USB 2.0 table 11-13
164#[allow(missing_docs)]
165pub struct HubDescriptor {
166    bDescLength: u8,
167    bDescriptorType: u8,
168    bNbrPorts: u8,
169    wHubCharacteristics: [u8; 2],
170    bPwrOn2PwrGood: u8,
171    bHubContrCurrent: u8,
172    DeviceRemovable: u8, // NB only for hubs up to 8 (true) ports
173    PortPwrCtrlMask: u8, // NB only for hubs up to 8 (true) ports
174}
175
176// SAFETY: all fields zeroable
177unsafe impl bytemuck::Zeroable for HubDescriptor {}
178// SAFETY: no padding, no disallowed bit patterns
179unsafe impl bytemuck::Pod for HubDescriptor {}
180
181// For request_type (USB 2.0 table 9-2)
182
183/// Control transfer: device-to-host
184pub const DEVICE_TO_HOST: u8 = 0x80;
185
186/// Control transfer: host-to-device
187pub const HOST_TO_DEVICE: u8 = 0;
188
189/// Control transfer: request defined by USB standard
190pub const STANDARD_REQUEST: u8 = 0;
191
192/// Control transfer: request defined by USB class definition
193pub const CLASS_REQUEST: u8 = 0x20;
194
195/// Control transfer: request is vendor-specific
196pub const VENDOR_REQUEST: u8 = 0x40;
197
198/// Control transfer: request targets entire device
199pub const RECIPIENT_DEVICE: u8 = 0;
200
201/// Control transfer: request targets a particular interface
202pub const RECIPIENT_INTERFACE: u8 = 1;
203
204/// Control transfer: request targets a particular endpoing
205pub const RECIPIENT_ENDPOINT: u8 = 2;
206
207/// Control transfer: request targets something else
208pub const RECIPIENT_OTHER: u8 = 3;
209
210// For request (USB 2.0 table 9-4)
211
212/// Request status (USB 2.0 section 9.4.5)
213pub const GET_STATUS: u8 = 0;
214
215/// Clear feature (USB 2.0 section 9.4.1)
216pub const CLEAR_FEATURE: u8 = 1;
217
218/// Set feature (USB 2.0 section 9.4.9)
219pub const SET_FEATURE: u8 = 3;
220
221/// Set address (USB 2.0 section 9.4.6)
222pub const SET_ADDRESS: u8 = 5;
223
224/// Get descriptor (USB 2.0 section 9.4.3)
225pub const GET_DESCRIPTOR: u8 = 6;
226
227/// Set descriptor (rarely used)
228pub const SET_DESCRIPTOR: u8 = 7;
229
230/// Set configuration (USB 2.0 section 9.4.7)
231pub const SET_CONFIGURATION: u8 = 9;
232
233// Descriptor types (USB 2.0 table 9-5)
234
235/// Device descriptor (USB 2.0 section 9.6.1)
236pub const DEVICE_DESCRIPTOR: u8 = 1;
237
238/// Configuration descriptor (USB 2.0 section 9.6.3)
239pub const CONFIGURATION_DESCRIPTOR: u8 = 2;
240
241/// String descriptor (USB 2.0 section 9.6.7)
242pub const STRING_DESCRIPTOR: u8 = 3;
243
244/// Interface descriptor (USB 2.0 section 9.6.5)
245pub const INTERFACE_DESCRIPTOR: u8 = 4;
246
247/// Endpoint descriptor (USB 2.0 section 9.6.6)
248pub const ENDPOINT_DESCRIPTOR: u8 = 5;
249
250/// Hub descriptor (USB 2.0 section 11.23.3.1 and table 11-13)
251pub const HUB_DESCRIPTOR: u8 = 0x29;
252
253// Class codes (DeviceDescriptor.bDeviceClass)
254
255/// Class code for USB hubs (USB 2.0 section 11.23.1)
256pub const HUB_CLASSCODE: u8 = 9;
257
258// Values for SET_FEATURE for hubs (USB 2.0 table 11-17)
259
260/// Reset a port (USB 2.0 section 11.5.1.5)
261pub const PORT_RESET: u16 = 4;
262
263/// Power-on a port (USB 2.0 section 11.5.1.13)
264pub const PORT_POWER: u16 = 8;
265
266/// Endpoint type, see USB 2.0 sections 9.3.6 and 5.3.1
267#[cfg_attr(feature = "defmt", derive(defmt::Format))]
268#[cfg_attr(feature = "std", derive(Debug))]
269#[derive(Copy, Clone, PartialEq, Eq)]
270#[allow(missing_docs)]
271pub enum EndpointType {
272    Control = 0,
273    Isochronous = 1,
274    Bulk = 2,
275    Interrupt = 3,
276}
277
278/// Direction of a USB transfer
279#[cfg_attr(feature = "defmt", derive(defmt::Format))]
280#[cfg_attr(feature = "std", derive(Debug))]
281#[derive(Copy, Clone, PartialEq, Eq)]
282pub enum Direction {
283    /// IN transactions are device-to-host transfers
284    In,
285    /// OUT transactions are host-to-device transfers
286    Out,
287}
288
289/// Callbacks from [`parse_descriptors()`]
290///
291/// And hence from [`UsbBus::get_configuration()`](crate::usb_bus::UsbBus::get_configuration).
292pub trait DescriptorVisitor {
293    /// A configuration descriptor has been reported
294    fn on_configuration(&mut self, _c: &ConfigurationDescriptor) {}
295
296    /// An interface descriptor has been reported
297    fn on_interface(&mut self, _i: &InterfaceDescriptor) {}
298
299    /// An endpoint descriptor has been reported
300    fn on_endpoint(&mut self, _e: &EndpointDescriptor) {}
301
302    /// Some other descriptor has been reported (perhaps a vendor-defined one)
303    fn on_other(&mut self, _d: &[u8]) {}
304}
305
306/// [`A DescriptorVisitor`] that just logs the descriptors to the debug stream
307pub struct ShowDescriptors;
308
309impl DescriptorVisitor for ShowDescriptors {
310    fn on_configuration(&mut self, c: &ConfigurationDescriptor) {
311        debug::println!("{:?}", c);
312    }
313    fn on_interface(&mut self, i: &InterfaceDescriptor) {
314        debug::println!("  {:?}", i);
315    }
316    fn on_endpoint(&mut self, e: &EndpointDescriptor) {
317        debug::println!("    {:?}", e);
318    }
319    fn on_other(&mut self, d: &[u8]) {
320        let dlen = d[0];
321        let dtype = d[1];
322        let domain = match dtype & 0x60 {
323            0x00 => "standard",
324            0x20 => "class",
325            0x40 => "vendor",
326            _ => "reserved",
327        };
328        debug::println!("  {} type {} len {} skipped", domain, dtype, dlen);
329    }
330}
331
332/// Parse a configuration-descriptor sequence
333///
334/// And make callbacks via the [`DescriptorVisitor`] for everything
335/// that's found.
336///
337/// USB 2.0 s9.5 says that undersize descriptors should be rejected,
338/// oversize descriptors should be silently truncated.
339pub fn parse_descriptors(buf: &[u8], v: &mut impl DescriptorVisitor) {
340    let mut index = 0;
341
342    while buf.len() > index + 2 {
343        let dlen = buf[index] as usize;
344        let dtype = buf[index + 1];
345
346        if dlen < 2 || buf.len() < index + dlen {
347            return;
348        }
349
350        match dtype {
351            CONFIGURATION_DESCRIPTOR => {
352                let min_dlen =
353                    Ord::min(dlen, size_of::<ConfigurationDescriptor>());
354
355                if let Ok(c) =
356                    bytemuck::try_from_bytes(&buf[index..index + min_dlen])
357                {
358                    v.on_configuration(c);
359                }
360            }
361            INTERFACE_DESCRIPTOR => {
362                let min_dlen =
363                    Ord::min(dlen, size_of::<InterfaceDescriptor>());
364
365                if let Ok(i) =
366                    bytemuck::try_from_bytes(&buf[index..index + min_dlen])
367                {
368                    v.on_interface(i);
369                }
370            }
371            ENDPOINT_DESCRIPTOR => {
372                let min_dlen = Ord::min(dlen, size_of::<EndpointDescriptor>());
373
374                if let Ok(e) =
375                    bytemuck::try_from_bytes(&buf[index..index + min_dlen])
376                {
377                    v.on_endpoint(e);
378                }
379            }
380            _ => {
381                v.on_other(&buf[index..(index + dlen)]);
382            }
383        }
384
385        index += dlen;
386    }
387}
388
389#[cfg(all(test, feature = "std"))]
390#[path = "tests/wire.rs"]
391mod tests;