Skip to main content

imxrt_usbd/
driver.rs

1//! Internal USB driver
2//!
3//! The goal is to keep this somewhat agnostic from the usb-device
4//! bus behaviors, so that it could be used separately. However, it's
5//! not yet exposed in the package's API.
6
7use crate::{buffer, gpt, ral};
8use usb_device::{
9    UsbDirection, UsbError,
10    bus::PollResult,
11    endpoint::{EndpointAddress, EndpointType},
12};
13
14/// Direct index to the OUT control endpoint
15fn ctrl_ep0_out() -> EndpointAddress {
16    // Constructor not currently const. Otherwise, this would
17    // be a const.
18    EndpointAddress::from_parts(0, UsbDirection::Out)
19}
20
21/// Direct index to the IN control endpoint
22fn ctrl_ep0_in() -> EndpointAddress {
23    EndpointAddress::from_parts(0, UsbDirection::In)
24}
25
26/// USB low / full / high speed setting.
27#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
28pub enum Speed {
29    /// Throttle to low / full speeds.
30    ///
31    /// If a host is capable of high-speed, this will prevent
32    /// the device from enumerating as a high-speed device.
33    LowFull,
34    /// High speed.
35    ///
36    /// A high-speed device can still interface a low / full
37    /// speed host, so use this setting for the most flexibility.
38    #[default]
39    High,
40}
41
42/// A USB driver
43///
44/// After you allocate a `Driver` with [`new()`](Driver::new), you must
45///
46/// - call [`initialize()`](Driver::initialize) once
47/// - supply endpoint memory with [`set_endpoint_memory()`](USB::set_endpoint_memory)
48pub struct Driver {
49    usb: ral::AnyUsbInstance,
50    phy: ral::AnyUsbphyInstance,
51    buffer_allocator: buffer::Allocator,
52    ep_allocator: crate::state::EndpointAllocator<'static>,
53    /// Track which read endpoints have completed, so as to not
54    /// confuse the device and appear out of sync with poll() calls.
55    ///
56    /// Persisting the ep_out mask across poll() calls lets us make
57    /// sure that results of ep_read calls match what's signaled from
58    /// poll() calls. During testing, we saw that poll() wouldn't signal
59    /// ep_out complete. But, the class could still call ep_read(), and
60    /// it would return data. The usb-device test_class treats that as
61    /// a failure, so we should keep behaviors consistent.
62    ep_out: u16,
63}
64
65impl Driver {
66    /// Create a new `Driver`
67    ///
68    /// Creation does nothing except for assign static memory to the driver.
69    /// After creating the driver, call [`initialize()`](USB::initialize).
70    ///
71    /// # Panics
72    ///
73    /// Panics if the endpoint bufer or state has already been assigned to another USB
74    /// driver.
75    pub fn new<const N: u8, const SIZE: usize, const EP_COUNT: usize>(
76        instances: crate::Instances<N>,
77        buffer: &'static crate::buffer::EndpointMemory<SIZE>,
78        state: &'static crate::state::EndpointState<EP_COUNT>,
79    ) -> Self {
80        let ral::ErasedInstances { usb, usbphy: phy } = ral::erase_instances(instances);
81        let ep_allocator = state.allocator().expect("Endpoint state already assigned");
82        Driver {
83            usb,
84            phy,
85            buffer_allocator: buffer
86                .allocator()
87                .expect("Endpoint memory already assigned"),
88            ep_allocator,
89            ep_out: 0,
90        }
91    }
92
93    /// Initialize the USB physical layer, and the USB core registers
94    ///
95    /// Assumes that the CCM clock gates are enabled, and the PLL is on.
96    ///
97    /// You **must** call this once, before creating the complete USB
98    /// bus.
99    pub fn initialize(&mut self, speed: Speed) {
100        ral::write_reg!(ral::usbphy, self.phy, CTRL_SET, SFTRST: 1);
101        ral::write_reg!(ral::usbphy, self.phy, CTRL_CLR, SFTRST: 1);
102        ral::write_reg!(ral::usbphy, self.phy, CTRL_CLR, CLKGATE: 1);
103        ral::write_reg!(ral::usbphy, self.phy, PWD, 0);
104
105        ral::write_reg!(ral::usb, self.usb, USBCMD, RST: 1);
106        while ral::read_reg!(ral::usb, self.usb, USBCMD, RST == 1) {}
107        // ITC is reset to some non-immediate value. Use the 'immediate' value by default.
108        // (Note: this also zeros all other USBCMD fields.)
109        ral::write_reg!(ral::usb, self.usb, USBCMD, ITC: 0);
110
111        ral::write_reg!(ral::usb, self.usb, USBMODE, CM: CM_2, SLOM: 1);
112        ral::modify_reg!(ral::usb, self.usb, PORTSC1, PFSC: (speed == Speed::LowFull) as u32);
113
114        ral::modify_reg!(ral::usb, self.usb, USBSTS, |usbsts| usbsts);
115        // Disable interrupts by default
116        ral::write_reg!(ral::usb, self.usb, USBINTR, 0);
117
118        ral::write_reg!(
119            ral::usb,
120            self.usb,
121            ASYNCLISTADDR,
122            self.ep_allocator.qh_list_addr() as u32
123        )
124    }
125
126    /// Enable zero-length termination (ZLT) for the given endpoint
127    ///
128    /// When ZLT is enabled, software does not need to send a zero-length packet
129    /// to terminate a transfer where the number of bytes equals the max packet size.
130    /// The hardware will send this zero-length packet itself. By default, ZLT is off,
131    /// and software is expected to send these packets. Enable this if you're confident
132    /// that your (third-party) device / USB class isn't already sending these packets.
133    ///
134    /// This call does nothing if the endpoint isn't allocated.
135    pub fn enable_zlt(&mut self, ep_addr: EndpointAddress) {
136        if let Some(ep) = self.ep_allocator.endpoint_mut(ep_addr) {
137            ep.enable_zlt();
138        }
139    }
140
141    /// Enable (`true`) or disable (`false`) USB interrupts
142    pub fn set_interrupts(&mut self, interrupts: bool) {
143        if interrupts {
144            // Keep this in sync with the poll() behaviors
145            ral::modify_reg!(ral::usb, self.usb, USBINTR, UE: 1, URE: 1);
146        } else {
147            ral::modify_reg!(ral::usb, self.usb, USBINTR, UE: 0, URE: 0);
148        }
149    }
150
151    /// Acquire mutable access to a GPT timer
152    pub fn gpt_mut<R>(&mut self, instance: gpt::Instance, f: impl FnOnce(&mut gpt::Gpt) -> R) -> R {
153        let mut gpt = gpt::Gpt::new(&mut self.usb, instance);
154        f(&mut gpt)
155    }
156
157    pub fn set_address(&mut self, address: u8) {
158        // See the "quirk" note in the UsbBus impl. We're using USBADRA to let
159        // the hardware set the address before the status phase.
160        ral::write_reg!(ral::usb, self.usb, DEVICEADDR, USBADR: address as u32, USBADRA: 1);
161        debug!("ADDRESS {=u8}", address);
162    }
163
164    pub fn attach(&mut self) {
165        ral::modify_reg!(ral::usb, self.usb, USBCMD, RS: 1);
166    }
167
168    pub fn bus_reset(&mut self) {
169        ral::modify_reg!(ral::usb, self.usb, ENDPTSTAT, |endptstat| endptstat);
170
171        ral::modify_reg!(ral::usb, self.usb, ENDPTCOMPLETE, |endptcomplete| {
172            endptcomplete
173        });
174        ral::modify_reg!(ral::usb, self.usb, ENDPTNAK, |endptnak| endptnak);
175        ral::write_reg!(ral::usb, self.usb, ENDPTNAKEN, 0);
176
177        while ral::read_reg!(ral::usb, self.usb, ENDPTPRIME) != 0 {}
178        ral::write_reg!(ral::usb, self.usb, ENDPTFLUSH, u32::MAX);
179        while ral::read_reg!(ral::usb, self.usb, ENDPTFLUSH) != 0 {}
180
181        debug_assert!(
182            ral::read_reg!(ral::usb, self.usb, PORTSC1, PR == 1),
183            "Took too long to handle bus reset"
184        );
185        debug!("RESET");
186
187        self.initialize_endpoints();
188    }
189
190    /// Check if the endpoint is valid
191    pub fn is_allocated(&self, addr: EndpointAddress) -> bool {
192        self.ep_allocator.endpoint(addr).is_some()
193    }
194
195    /// Read either a setup, or a data buffer, from EP0 OUT
196    ///
197    /// # Panics
198    ///
199    /// Panics if EP0 OUT isn't allocated.
200    pub fn ctrl0_read(&mut self, buffer: &mut [u8]) -> Result<usize, UsbError> {
201        let ctrl_out = self.ep_allocator.endpoint_mut(ctrl_ep0_out()).unwrap();
202        if ctrl_out.has_setup(&self.usb) && buffer.len() >= 8 {
203            debug!("EP0 Out SETUP");
204            let setup = ctrl_out.read_setup(&self.usb);
205            buffer[..8].copy_from_slice(&setup.to_le_bytes());
206
207            if !ctrl_out.is_primed(&self.usb) {
208                ctrl_out.clear_nack(&self.usb);
209                let max_packet_len = ctrl_out.max_packet_len();
210                ctrl_out.schedule_transfer(&self.usb, max_packet_len);
211            }
212
213            Ok(8)
214        } else {
215            ctrl_out.check_errors()?;
216
217            if ctrl_out.is_primed(&self.usb) {
218                return Err(UsbError::WouldBlock);
219            }
220
221            ctrl_out.clear_complete(&self.usb);
222            ctrl_out.clear_nack(&self.usb);
223
224            let read = ctrl_out.read(buffer);
225            debug!("EP0 Out {=usize}", read);
226            let max_packet_len = ctrl_out.max_packet_len();
227            ctrl_out.schedule_transfer(&self.usb, max_packet_len);
228
229            Ok(read)
230        }
231    }
232
233    /// Write to the host from EP0 IN
234    ///
235    /// Schedules the next OUT transfer to satisfy a status phase.
236    ///
237    /// # Panics
238    ///
239    /// Panics if EP0 IN isn't allocated, or if EP0 OUT isn't allocated.
240    pub fn ctrl0_write(&mut self, buffer: &[u8]) -> Result<usize, UsbError> {
241        let ctrl_in = self.ep_allocator.endpoint_mut(ctrl_ep0_in()).unwrap();
242        debug!("EP0 In {=usize}", buffer.len());
243        ctrl_in.check_errors()?;
244
245        if ctrl_in.is_primed(&self.usb) {
246            return Err(UsbError::WouldBlock);
247        }
248
249        ctrl_in.clear_nack(&self.usb);
250
251        let written = ctrl_in.write(buffer);
252        ctrl_in.schedule_transfer(&self.usb, written);
253
254        // Might need an OUT schedule for a status phase...
255        let ctrl_out = self.ep_allocator.endpoint_mut(ctrl_ep0_out()).unwrap();
256        if !ctrl_out.is_primed(&self.usb) {
257            ctrl_out.clear_complete(&self.usb);
258            ctrl_out.clear_nack(&self.usb);
259            ctrl_out.schedule_transfer(&self.usb, 0);
260        }
261
262        Ok(written)
263    }
264
265    /// Read data from an endpoint, and schedule the next transfer
266    ///
267    /// # Panics
268    ///
269    /// Panics if the endpoint isn't allocated.
270    pub fn ep_read(&mut self, buffer: &mut [u8], addr: EndpointAddress) -> Result<usize, UsbError> {
271        let ep = self.ep_allocator.endpoint_mut(addr).unwrap();
272        debug!("EP{=usize} Out", ep.address().index());
273        ep.check_errors()?;
274
275        if ep.is_primed(&self.usb) || (self.ep_out & (1 << ep.address().index()) == 0) {
276            return Err(UsbError::WouldBlock);
277        }
278
279        ep.clear_complete(&self.usb); // Clears self.ep_out bit on the next poll() call...
280        ep.clear_nack(&self.usb);
281
282        let read = ep.read(buffer);
283
284        let max_packet_len = ep.max_packet_len();
285        ep.schedule_transfer(&self.usb, max_packet_len);
286
287        Ok(read)
288    }
289
290    /// Write data to an endpoint
291    ///
292    /// # Panics
293    ///
294    /// Panics if the endpoint isn't allocated.
295    pub fn ep_write(&mut self, buffer: &[u8], addr: EndpointAddress) -> Result<usize, UsbError> {
296        let ep = self.ep_allocator.endpoint_mut(addr).unwrap();
297        ep.check_errors()?;
298
299        if ep.is_primed(&self.usb) {
300            return Err(UsbError::WouldBlock);
301        }
302
303        ep.clear_nack(&self.usb);
304
305        let written = ep.write(buffer);
306        ep.schedule_transfer(&self.usb, written);
307
308        Ok(written)
309    }
310
311    /// Stall an endpoint
312    ///
313    /// # Panics
314    ///
315    /// Panics if the endpoint isn't allocated
316    pub fn ep_stall(&mut self, stall: bool, addr: EndpointAddress) {
317        let ep = self.ep_allocator.endpoint_mut(addr).unwrap();
318        ep.set_stalled(&self.usb, stall);
319
320        // Re-prime any OUT endpoints if we're unstalling. Only prime an *enabled*
321        // endpoint: priming a disabled OUT endpoint (e.g. a class clearing stalls
322        // in its reset() before SET_CONFIGURATION enables endpoints) leaves a stale
323        // transfer descriptor that the controller never completes once the endpoint
324        // is later enabled.
325        if !stall
326            && addr.direction() == UsbDirection::Out
327            && ep.is_enabled(&self.usb)
328            && !ep.is_primed(&self.usb)
329        {
330            let max_packet_len = ep.max_packet_len();
331            ep.schedule_transfer(&self.usb, max_packet_len);
332        }
333    }
334
335    /// Checks if an endpoint is stalled
336    ///
337    /// # Panics
338    ///
339    /// Panics if the endpoint isn't allocated
340    pub fn is_ep_stalled(&self, addr: EndpointAddress) -> bool {
341        self.ep_allocator
342            .endpoint(addr)
343            .unwrap()
344            .is_stalled(&self.usb)
345    }
346
347    /// Allocate a buffer from the endpoint memory
348    pub fn allocate_buffer(&mut self, max_packet_len: usize) -> Option<buffer::Buffer> {
349        self.buffer_allocator.allocate(max_packet_len)
350    }
351
352    /// Allocate a specific endpoint
353    ///
354    /// # Panics
355    ///
356    /// Panics if the endpoint is already allocated.
357    pub fn allocate_ep(
358        &mut self,
359        addr: EndpointAddress,
360        buffer: buffer::Buffer,
361        kind: EndpointType,
362    ) {
363        self.ep_allocator
364            .allocate_endpoint(addr, buffer, kind)
365            .unwrap();
366
367        debug!(
368            "ALLOC EP{=usize} {} {}",
369            addr.index(),
370            addr.direction(),
371            kind
372        );
373    }
374
375    /// Invoked when the device transitions into the configured state
376    pub fn on_configured(&mut self) {
377        self.enable_endpoints();
378        self.prime_endpoints();
379    }
380
381    /// Enable all non-zero endpoints
382    ///
383    /// This should only be called when the device is configured
384    fn enable_endpoints(&mut self) {
385        for ep in self.ep_allocator.nonzero_endpoints_iter_mut() {
386            ep.enable(&self.usb);
387        }
388    }
389
390    /// Prime all non-zero, enabled OUT endpoints
391    fn prime_endpoints(&mut self) {
392        for ep in self.ep_allocator.nonzero_endpoints_iter_mut() {
393            if ep.is_enabled(&self.usb) && ep.address().direction() == UsbDirection::Out {
394                let max_packet_len = ep.max_packet_len();
395                ep.schedule_transfer(&self.usb, max_packet_len);
396            }
397        }
398    }
399
400    /// Initialize (or reinitialize) all non-zero endpoints
401    fn initialize_endpoints(&mut self) {
402        for ep in self.ep_allocator.nonzero_endpoints_iter_mut() {
403            ep.initialize(&self.usb);
404        }
405    }
406
407    /// Poll for reset or USB traffic
408    pub fn poll(&mut self) -> PollResult {
409        let usbsts = ral::read_reg!(ral::usb, self.usb, USBSTS);
410        use ral::usb::USBSTS;
411
412        if usbsts & USBSTS::URI::mask != 0 {
413            ral::write_reg!(ral::usb, self.usb, USBSTS, URI: 1);
414            return PollResult::Reset;
415        }
416
417        if usbsts & USBSTS::UI::mask != 0 {
418            ral::write_reg!(ral::usb, self.usb, USBSTS, UI: 1);
419
420            trace!(
421                "ENDPTSETUPSTAT: {=u32:#010X}  ENDPTCOMPLETE: {=u32:#010X}",
422                ral::read_reg!(ral::usb, self.usb, ENDPTSETUPSTAT),
423                ral::read_reg!(ral::usb, self.usb, ENDPTCOMPLETE)
424            );
425            // Note: could be complete in one register read, but this is a little
426            // easier to comprehend...
427            self.ep_out = ral::read_reg!(ral::usb, self.usb, ENDPTCOMPLETE, ERCE) as u16;
428
429            let ep_in_complete = ral::read_reg!(ral::usb, self.usb, ENDPTCOMPLETE, ETCE);
430            ral::write_reg!(ral::usb, self.usb, ENDPTCOMPLETE, ETCE: ep_in_complete);
431
432            let ep_setup = ral::read_reg!(ral::usb, self.usb, ENDPTSETUPSTAT) as u16;
433
434            PollResult::Data {
435                ep_out: self.ep_out,
436                ep_in_complete: ep_in_complete as u16,
437                ep_setup,
438            }
439        } else {
440            PollResult::None
441        }
442    }
443}