imxrt_usbd/
bus.rs

1//! USB bus implementation
2//!
3//! The bus
4//!
5//! - initializes the USB driver
6//! - adapts the USB driver to meet the `usb-device` `Sync` requirements
7//! - dispatches reads and writes to the proper endpoints
8//! - exposes the i.MX RT-specific API to the user (`configure`, `set_interrupts`)
9//!
10//! Most of the interesting behavior happens in the driver.
11
12use super::driver::Driver;
13use crate::gpt;
14use core::cell::RefCell;
15use cortex_m::interrupt::{self, Mutex};
16use usb_device::{
17    bus::{PollResult, UsbBus},
18    endpoint::{EndpointAddress, EndpointType},
19    UsbDirection,
20};
21
22pub use super::driver::Speed;
23
24/// A full- and high-speed `UsbBus` implementation
25///
26/// The `BusAdapter` adapts the USB peripheral instances, and exposes a `UsbBus` implementation.
27///
28/// # Requirements
29///
30/// The driver assumes that you've prepared all USB clocks (CCM clock gates, CCM analog PLLs).
31///
32/// Before polling for USB class traffic, you must call [`configure()`](BusAdapter::configure())
33/// *after* your device has been configured. This can be accomplished by polling the USB
34/// device and checking its state until it's been configured. Once configured, use `UsbDevice::bus()`
35/// to access the i.MX RT `BusAdapter`, and call `configure()`. You should only do this once.
36/// After that, you may poll for class traffic.
37///
38/// # Example
39///
40/// This example shows you how to create a `BusAdapter`, build a simple USB device, and
41/// prepare the device for class traffic.
42///
43/// Note that this example does not demonstrate USB class allocation or polling. See
44/// your USB class' documentation for details. This example also skips the clock initialization.
45///
46/// ```no_run
47/// use imxrt_usbd::BusAdapter;
48///
49/// # struct Ps;
50/// # unsafe impl imxrt_usbd::Peripherals for Ps { fn usb(&self) -> *const () { panic!() } fn usbphy(&self) -> *const () { panic!() }}
51/// static EP_MEMORY: imxrt_usbd::EndpointMemory<1024> = imxrt_usbd::EndpointMemory::new();
52/// static EP_STATE: imxrt_usbd::EndpointState = imxrt_usbd::EndpointState::max_endpoints();
53///
54/// // TODO initialize clocks...
55///
56/// let my_usb_peripherals = // Your Peripherals instance...
57/// #   Ps;
58/// let bus_adapter = BusAdapter::new(
59///     my_usb_peripherals,
60///     &EP_MEMORY,
61///     &EP_STATE,
62/// );
63///
64/// // Create the USB device...
65/// use usb_device::prelude::*;
66/// let bus_allocator = usb_device::bus::UsbBusAllocator::new(bus_adapter);
67/// let mut device = UsbDeviceBuilder::new(&bus_allocator, UsbVidPid(0x5824, 0x27dd))
68///     .strings(&[StringDescriptors::default().product("imxrt-usbd")]).unwrap()
69///     // Other builder methods...
70///     .build();
71///
72/// // Poll until configured...
73/// loop {
74///     if device.poll(&mut []) {
75///         let state = device.state();
76///         if state == usb_device::device::UsbDeviceState::Configured {
77///             break;
78///         }
79///     }
80/// }
81///
82/// // Configure the bus
83/// device.bus().configure();
84///
85/// // Ready for class traffic!
86/// ```
87///
88/// # Design
89///
90/// This section talks about the driver design. It assumes that
91/// you're familiar with the details of the i.MX RT USB peripheral. If you
92/// just want to use the driver, you can skip this section.
93///
94/// ## Packets and transfers
95///
96/// All i.MX RT USB drivers manage queue heads (QH), and transfer
97/// descriptors (TD). For the driver, each (QH) is assigned
98/// only one (TD) to perform I/O. We then assume each TD describes a single
99/// packet. This is simple to implement, but it means that the
100/// driver can only have one packet in flight per endpoint. You're expected
101/// to quickly respond to `poll()` outputs, and schedule the next transfer
102/// in the time required for devices. This becomes more important as you
103/// increase driver speeds.
104///
105/// The hardware can zero-length terminate (ZLT) packets as needed if you
106/// call [`enable_zlt`](BusAdapter::enable_zlt). By default, this feature is
107/// off, because most `usb-device` classes / devices take care to send zero-length
108/// packets, and enabling this feature could interfere with the class / device
109/// behaviors.
110pub struct BusAdapter {
111    usb: Mutex<RefCell<Driver>>,
112    cs: Option<cortex_m::interrupt::CriticalSection>,
113}
114
115impl BusAdapter {
116    /// Create a high-speed USB bus adapter
117    ///
118    /// This is equivalent to [`BusAdapter::with_speed`] when supplying [`Speed::High`]. See
119    /// the `with_speed` documentation for more information.
120    ///
121    /// # Panics
122    ///
123    /// Panics if `buffer` or `state` has already been associated with another USB bus.
124    pub fn new<P: crate::Peripherals, const SIZE: usize, const EP_COUNT: usize>(
125        peripherals: P,
126        buffer: &'static crate::buffer::EndpointMemory<SIZE>,
127        state: &'static crate::state::EndpointState<EP_COUNT>,
128    ) -> Self {
129        Self::with_speed(peripherals, buffer, state, Speed::High)
130    }
131
132    /// Create a USB bus adapter with the given speed
133    ///
134    /// Specify [`Speed::LowFull`] to throttle the USB data rate.
135    ///
136    /// When this function returns, the `BusAdapter` has initialized the PHY and USB core peripherals.
137    /// The adapter expects to own these two peripherals, along with the other peripherals required
138    /// by the [`Peripherals`](crate::Peripherals) safety contract.
139    ///
140    /// You must also provide a region of memory that will used for endpoint I/O. The
141    /// memory region will be partitioned for the endpoints, based on their requirements.
142    ///
143    /// # Panics
144    ///
145    /// Panics if `buffer` or `state` has already been associated with another USB bus.
146    pub fn with_speed<P: crate::Peripherals, const SIZE: usize, const EP_COUNT: usize>(
147        peripherals: P,
148        buffer: &'static crate::buffer::EndpointMemory<SIZE>,
149        state: &'static crate::state::EndpointState<EP_COUNT>,
150        speed: Speed,
151    ) -> Self {
152        Self::init(peripherals, buffer, state, speed, None)
153    }
154
155    /// Create a USB bus adapter that never takes a critical section
156    ///
157    /// See [`BusAdapter::with_speed`] for general information.
158    ///
159    /// # Safety
160    ///
161    /// The returned object fakes its `Sync` safety. Specifically, the object
162    /// will not take critical sections in its `&[mut] self` methods to ensure safe
163    /// access. By using this object, you must manually hold the guarantees of
164    /// `Sync` without the compiler's help.
165    ///
166    /// # Panics
167    ///
168    /// Panics if `buffer` or `state` has already been associated with another USB bus.
169    pub unsafe fn without_critical_sections<
170        P: crate::Peripherals,
171        const SIZE: usize,
172        const EP_COUNT: usize,
173    >(
174        peripherals: P,
175        buffer: &'static crate::buffer::EndpointMemory<SIZE>,
176        state: &'static crate::state::EndpointState<EP_COUNT>,
177        speed: Speed,
178    ) -> Self {
179        Self::init(
180            peripherals,
181            buffer,
182            state,
183            speed,
184            // Safety: see the above API docs. Caller knows that we're faking our
185            // Sync capability.
186            Some(unsafe { cortex_m::interrupt::CriticalSection::new() }),
187        )
188    }
189
190    fn init<P: crate::Peripherals, const SIZE: usize, const EP_COUNT: usize>(
191        peripherals: P,
192        buffer: &'static crate::buffer::EndpointMemory<SIZE>,
193        state: &'static crate::state::EndpointState<EP_COUNT>,
194        speed: Speed,
195        cs: Option<cortex_m::interrupt::CriticalSection>,
196    ) -> Self {
197        let mut usb = Driver::new(peripherals, buffer, state);
198
199        usb.initialize(speed);
200
201        BusAdapter {
202            usb: Mutex::new(RefCell::new(usb)),
203            cs,
204        }
205    }
206    /// Enable (`true`) or disable (`false`) interrupts for this USB peripheral
207    ///
208    /// The interrupt causes are implementation specific. To handle the interrupt,
209    /// call [`poll()`](BusAdapter::poll).
210    pub fn set_interrupts(&self, interrupts: bool) {
211        self.with_usb_mut(|usb| usb.set_interrupts(interrupts));
212    }
213
214    /// Enable zero-length termination (ZLT) for the given endpoint
215    ///
216    /// When ZLT is enabled, software does not need to send a zero-length packet
217    /// to terminate a transfer where the number of bytes equals the max packet size.
218    /// The hardware will send this zero-length packet itself. By default, ZLT is off,
219    /// and software is expected to send these packets. Enable this if you're confident
220    /// that your (third-party) device / USB class isn't already sending these packets.
221    ///
222    /// This call does nothing if the endpoint isn't allocated.
223    pub fn enable_zlt(&self, ep_addr: EndpointAddress) {
224        self.with_usb_mut(|usb| usb.enable_zlt(ep_addr));
225    }
226
227    /// Immutable access to the USB peripheral
228    fn with_usb<R>(&self, func: impl FnOnce(&Driver) -> R) -> R {
229        let with_cs = |cs: &'_ _| {
230            let usb = self.usb.borrow(cs);
231            let usb = usb.borrow();
232            func(&usb)
233        };
234        if let Some(cs) = &self.cs {
235            with_cs(cs)
236        } else {
237            interrupt::free(with_cs)
238        }
239    }
240
241    /// Mutable access to the USB peripheral
242    fn with_usb_mut<R>(&self, func: impl FnOnce(&mut Driver) -> R) -> R {
243        let with_cs = |cs: &'_ _| {
244            let usb = self.usb.borrow(cs);
245            let mut usb = usb.borrow_mut();
246            func(&mut usb)
247        };
248        if let Some(cs) = &self.cs {
249            with_cs(cs)
250        } else {
251            interrupt::free(with_cs)
252        }
253    }
254
255    /// Apply device configurations, and perform other post-configuration actions
256    ///
257    /// You must invoke this once, and only after your device has been configured. If
258    /// the device is reset and reconfigured, you must invoke `configure()` again. See
259    /// the top-level example for how this could be achieved.
260    pub fn configure(&self) {
261        self.with_usb_mut(|usb| {
262            usb.on_configured();
263            debug!("CONFIGURED");
264        });
265    }
266
267    /// Acquire one of the GPT timer instances.
268    ///
269    /// `instance` identifies which GPT instance you're accessing.
270    /// This may take a critical section for the duration of `func`.
271    ///
272    /// # Panics
273    ///
274    /// Panics if the GPT instance is already borrowed. This could happen
275    /// if you call `gpt_mut` again within the `func` callback.
276    pub fn gpt_mut<R>(&self, instance: gpt::Instance, func: impl FnOnce(&mut gpt::Gpt) -> R) -> R {
277        self.with_usb_mut(|usb| usb.gpt_mut(instance, func))
278    }
279}
280
281impl UsbBus for BusAdapter {
282    /// The USB hardware can guarantee that we set the status before we receive
283    /// the status, and we're taking advantage of that. We expect this flag to
284    /// result in a call to set_address before the status happens. This means
285    /// that we can meet the timing requirements without help from software.
286    ///
287    /// It's not a quirk; it's a feature :)
288    const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = true;
289
290    fn alloc_ep(
291        &mut self,
292        ep_dir: UsbDirection,
293        ep_addr: Option<EndpointAddress>,
294        ep_type: EndpointType,
295        max_packet_size: u16,
296        _interval: u8,
297    ) -> usb_device::Result<EndpointAddress> {
298        self.with_usb_mut(|usb| {
299            if let Some(addr) = ep_addr {
300                if usb.is_allocated(addr) {
301                    return Err(usb_device::UsbError::InvalidEndpoint);
302                }
303                let buffer = usb
304                    .allocate_buffer(max_packet_size as usize)
305                    .ok_or(usb_device::UsbError::EndpointMemoryOverflow)?;
306                usb.allocate_ep(addr, buffer, ep_type);
307                Ok(addr)
308            } else {
309                for idx in 1..8 {
310                    let addr = EndpointAddress::from_parts(idx, ep_dir);
311                    if usb.is_allocated(addr) {
312                        continue;
313                    }
314                    let buffer = usb
315                        .allocate_buffer(max_packet_size as usize)
316                        .ok_or(usb_device::UsbError::EndpointMemoryOverflow)?;
317                    usb.allocate_ep(addr, buffer, ep_type);
318                    return Ok(addr);
319                }
320                Err(usb_device::UsbError::EndpointOverflow)
321            }
322        })
323    }
324
325    fn set_device_address(&self, addr: u8) {
326        self.with_usb_mut(|usb| {
327            usb.set_address(addr);
328        });
329    }
330
331    fn enable(&mut self) {
332        self.with_usb_mut(|usb| usb.attach());
333    }
334
335    fn reset(&self) {
336        self.with_usb_mut(|usb| {
337            usb.bus_reset();
338        });
339    }
340
341    fn write(&self, ep_addr: EndpointAddress, buf: &[u8]) -> usb_device::Result<usize> {
342        self.with_usb_mut(|usb| {
343            if !usb.is_allocated(ep_addr) {
344                return Err(usb_device::UsbError::InvalidEndpoint);
345            }
346
347            // Keep map_err if warn! is compiled out.
348            #[allow(clippy::map_identity)]
349            let written = if ep_addr.index() == 0 {
350                usb.ctrl0_write(buf)
351            } else {
352                usb.ep_write(buf, ep_addr)
353            }
354            .map_err(|status| {
355                warn!(
356                    "EP{=usize} {} STATUS {}",
357                    ep_addr.index(),
358                    ep_addr.direction(),
359                    status
360                );
361                status
362            })?;
363
364            Ok(written)
365        })
366    }
367
368    fn read(&self, ep_addr: EndpointAddress, buf: &mut [u8]) -> usb_device::Result<usize> {
369        self.with_usb_mut(|usb| {
370            if !usb.is_allocated(ep_addr) {
371                return Err(usb_device::UsbError::InvalidEndpoint);
372            }
373
374            // Keep map_err if warn! is compiled out.
375            #[allow(clippy::map_identity)]
376            let read = if ep_addr.index() == 0 {
377                usb.ctrl0_read(buf)
378            } else {
379                usb.ep_read(buf, ep_addr)
380            }
381            .map_err(|status| {
382                warn!(
383                    "EP{=usize} {} STATUS {}",
384                    ep_addr.index(),
385                    ep_addr.direction(),
386                    status
387                );
388                status
389            })?;
390
391            Ok(read)
392        })
393    }
394
395    fn set_stalled(&self, ep_addr: EndpointAddress, stalled: bool) {
396        self.with_usb_mut(|usb| {
397            if usb.is_allocated(ep_addr) {
398                usb.ep_stall(stalled, ep_addr);
399            }
400        });
401    }
402
403    fn is_stalled(&self, ep_addr: EndpointAddress) -> bool {
404        self.with_usb(|usb| usb.is_ep_stalled(ep_addr))
405    }
406
407    fn suspend(&self) {
408        // TODO
409    }
410
411    fn resume(&self) {
412        // TODO
413    }
414
415    fn poll(&self) -> PollResult {
416        self.with_usb_mut(|usb| usb.poll())
417    }
418}