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}