Skip to main content

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