aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorchemicstry <[email protected]>2023-01-11 17:56:47 +0100
committerDario Nieuwenhuis <[email protected]>2023-01-11 17:56:47 +0100
commit0feecd5cded86d29870330341d5c2c8eb40688d0 (patch)
tree6a0025ffb830e9d3201d8063bde1e58d1ccda60e
parent065a0a1ee71c3e4333f2e38140e9ca86b61b59d2 (diff)
stm32: add USB OTG support.
Co-authored-by: Dario Nieuwenhuis <[email protected]>
-rw-r--r--embassy-stm32/src/usb_otg/mod.rs23
-rw-r--r--embassy-stm32/src/usb_otg/usb.rs1346
2 files changed, 1369 insertions, 0 deletions
diff --git a/embassy-stm32/src/usb_otg/mod.rs b/embassy-stm32/src/usb_otg/mod.rs
index 2ee2891df..84fef78cb 100644
--- a/embassy-stm32/src/usb_otg/mod.rs
+++ b/embassy-stm32/src/usb_otg/mod.rs
@@ -3,6 +3,15 @@ use embassy_cortex_m::interrupt::Interrupt;
3use crate::peripherals; 3use crate::peripherals;
4use crate::rcc::RccPeripheral; 4use crate::rcc::RccPeripheral;
5 5
6#[cfg(feature = "nightly")]
7mod usb;
8#[cfg(feature = "nightly")]
9pub use usb::*;
10
11// Using Instance::ENDPOINT_COUNT requires feature(const_generic_expr) so just define maximum eps
12#[cfg(feature = "nightly")]
13const MAX_EP_COUNT: usize = 9;
14
6pub(crate) mod sealed { 15pub(crate) mod sealed {
7 pub trait Instance { 16 pub trait Instance {
8 const HIGH_SPEED: bool; 17 const HIGH_SPEED: bool;
@@ -10,6 +19,8 @@ pub(crate) mod sealed {
10 const ENDPOINT_COUNT: usize; 19 const ENDPOINT_COUNT: usize;
11 20
12 fn regs() -> crate::pac::otg::Otg; 21 fn regs() -> crate::pac::otg::Otg;
22 #[cfg(feature = "nightly")]
23 fn state() -> &'static super::State<{ super::MAX_EP_COUNT }>;
13 } 24 }
14} 25}
15 26
@@ -86,6 +97,12 @@ foreach_interrupt!(
86 fn regs() -> crate::pac::otg::Otg { 97 fn regs() -> crate::pac::otg::Otg {
87 crate::pac::USB_OTG_FS 98 crate::pac::USB_OTG_FS
88 } 99 }
100
101 #[cfg(feature = "nightly")]
102 fn state() -> &'static State<MAX_EP_COUNT> {
103 static STATE: State<MAX_EP_COUNT> = State::new();
104 &STATE
105 }
89 } 106 }
90 107
91 impl Instance for peripherals::USB_OTG_FS { 108 impl Instance for peripherals::USB_OTG_FS {
@@ -129,6 +146,12 @@ foreach_interrupt!(
129 // OTG HS registers are a superset of FS registers 146 // OTG HS registers are a superset of FS registers
130 crate::pac::otg::Otg(crate::pac::USB_OTG_HS.0) 147 crate::pac::otg::Otg(crate::pac::USB_OTG_HS.0)
131 } 148 }
149
150 #[cfg(feature = "nightly")]
151 fn state() -> &'static State<MAX_EP_COUNT> {
152 static STATE: State<MAX_EP_COUNT> = State::new();
153 &STATE
154 }
132 } 155 }
133 156
134 impl Instance for peripherals::USB_OTG_HS { 157 impl Instance for peripherals::USB_OTG_HS {
diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs
new file mode 100644
index 000000000..2d9b613ea
--- /dev/null
+++ b/embassy-stm32/src/usb_otg/usb.rs
@@ -0,0 +1,1346 @@
1use core::cell::UnsafeCell;
2use core::marker::PhantomData;
3use core::task::Poll;
4
5use atomic_polyfill::{AtomicBool, AtomicU16, Ordering};
6use embassy_cortex_m::interrupt::InterruptExt;
7use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
8use embassy_sync::waitqueue::AtomicWaker;
9use embassy_usb_driver::{
10 self, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, EndpointOut,
11 EndpointType, Event, Unsupported,
12};
13use futures::future::poll_fn;
14
15use super::*;
16use crate::gpio::sealed::AFType;
17use crate::pac::otg::{regs, vals};
18use crate::rcc::sealed::RccPeripheral;
19use crate::time::Hertz;
20
21macro_rules! config_ulpi_pins {
22 ($($pin:ident),*) => {
23 into_ref!($($pin),*);
24 // NOTE(unsafe) Exclusive access to the registers
25 critical_section::with(|_| unsafe {
26 $(
27 $pin.set_as_af($pin.af_num(), AFType::OutputPushPull);
28 #[cfg(gpio_v2)]
29 $pin.set_speed(crate::gpio::Speed::VeryHigh);
30 )*
31 })
32 };
33}
34
35// From `synopsys-usb-otg` crate:
36// This calculation doesn't correspond to one in a Reference Manual.
37// In fact, the required number of words is higher than indicated in RM.
38// The following numbers are pessimistic and were figured out empirically.
39const RX_FIFO_EXTRA_SIZE_WORDS: u16 = 30;
40
41/// USB PHY type
42#[derive(Copy, Clone, Debug, Eq, PartialEq)]
43pub enum PhyType {
44 /// Internal Full-Speed PHY
45 ///
46 /// Available on most High-Speed peripherals.
47 InternalFullSpeed,
48 /// Internal High-Speed PHY
49 ///
50 /// Available on a few STM32 chips.
51 InternalHighSpeed,
52 /// External ULPI High-Speed PHY
53 ExternalHighSpeed,
54}
55
56impl PhyType {
57 pub fn internal(&self) -> bool {
58 match self {
59 PhyType::InternalFullSpeed | PhyType::InternalHighSpeed => true,
60 PhyType::ExternalHighSpeed => false,
61 }
62 }
63
64 pub fn high_speed(&self) -> bool {
65 match self {
66 PhyType::InternalFullSpeed => false,
67 PhyType::ExternalHighSpeed | PhyType::InternalHighSpeed => true,
68 }
69 }
70
71 pub fn to_dspd(&self) -> vals::Dspd {
72 match self {
73 PhyType::InternalFullSpeed => vals::Dspd::FULL_SPEED_INTERNAL,
74 PhyType::InternalHighSpeed => vals::Dspd::HIGH_SPEED,
75 PhyType::ExternalHighSpeed => vals::Dspd::HIGH_SPEED,
76 }
77 }
78}
79
80/// Indicates that [State::ep_out_buffers] is empty.
81const EP_OUT_BUFFER_EMPTY: u16 = u16::MAX;
82
83pub struct State<const EP_COUNT: usize> {
84 /// Holds received SETUP packets. Available if [State::ep0_setup_ready] is true.
85 ep0_setup_data: UnsafeCell<[u8; 8]>,
86 ep0_setup_ready: AtomicBool,
87 ep_in_wakers: [AtomicWaker; EP_COUNT],
88 ep_out_wakers: [AtomicWaker; EP_COUNT],
89 /// RX FIFO is shared so extra buffers are needed to dequeue all data without waiting on each endpoint.
90 /// Buffers are ready when associated [State::ep_out_size] != [EP_OUT_BUFFER_EMPTY].
91 ep_out_buffers: [UnsafeCell<*mut u8>; EP_COUNT],
92 ep_out_size: [AtomicU16; EP_COUNT],
93 bus_waker: AtomicWaker,
94}
95
96unsafe impl<const EP_COUNT: usize> Send for State<EP_COUNT> {}
97unsafe impl<const EP_COUNT: usize> Sync for State<EP_COUNT> {}
98
99impl<const EP_COUNT: usize> State<EP_COUNT> {
100 pub const fn new() -> Self {
101 const NEW_AW: AtomicWaker = AtomicWaker::new();
102 const NEW_BUF: UnsafeCell<*mut u8> = UnsafeCell::new(0 as _);
103 const NEW_SIZE: AtomicU16 = AtomicU16::new(EP_OUT_BUFFER_EMPTY);
104
105 Self {
106 ep0_setup_data: UnsafeCell::new([0u8; 8]),
107 ep0_setup_ready: AtomicBool::new(false),
108 ep_in_wakers: [NEW_AW; EP_COUNT],
109 ep_out_wakers: [NEW_AW; EP_COUNT],
110 ep_out_buffers: [NEW_BUF; EP_COUNT],
111 ep_out_size: [NEW_SIZE; EP_COUNT],
112 bus_waker: NEW_AW,
113 }
114 }
115}
116
117#[derive(Debug, Clone, Copy)]
118struct EndpointData {
119 ep_type: EndpointType,
120 max_packet_size: u16,
121 fifo_size_words: u16,
122}
123
124pub struct Driver<'d, T: Instance> {
125 phantom: PhantomData<&'d mut T>,
126 irq: PeripheralRef<'d, T::Interrupt>,
127 ep_in: [Option<EndpointData>; MAX_EP_COUNT],
128 ep_out: [Option<EndpointData>; MAX_EP_COUNT],
129 ep_out_buffer: &'d mut [u8],
130 ep_out_buffer_offset: usize,
131 phy_type: PhyType,
132}
133
134impl<'d, T: Instance> Driver<'d, T> {
135 /// Initializes USB OTG peripheral with internal Full-Speed PHY.
136 ///
137 /// # Arguments
138 ///
139 /// * `ep_out_buffer` - An internal buffer used to temporarily store recevied packets.
140 /// Must be large enough to fit all OUT endpoint max packet sizes.
141 /// Endpoint allocation will fail if it is too small.
142 pub fn new_fs(
143 _peri: impl Peripheral<P = T> + 'd,
144 irq: impl Peripheral<P = T::Interrupt> + 'd,
145 dp: impl Peripheral<P = impl DpPin<T>> + 'd,
146 dm: impl Peripheral<P = impl DmPin<T>> + 'd,
147 ep_out_buffer: &'d mut [u8],
148 ) -> Self {
149 into_ref!(dp, dm, irq);
150
151 unsafe {
152 dp.set_as_af(dp.af_num(), AFType::OutputPushPull);
153 dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
154 }
155
156 Self {
157 phantom: PhantomData,
158 irq,
159 ep_in: [None; MAX_EP_COUNT],
160 ep_out: [None; MAX_EP_COUNT],
161 ep_out_buffer,
162 ep_out_buffer_offset: 0,
163 phy_type: PhyType::InternalFullSpeed,
164 }
165 }
166
167 /// Initializes USB OTG peripheral with external High-Speed PHY.
168 ///
169 /// # Arguments
170 ///
171 /// * `ep_out_buffer` - An internal buffer used to temporarily store recevied packets.
172 /// Must be large enough to fit all OUT endpoint max packet sizes.
173 /// Endpoint allocation will fail if it is too small.
174 pub fn new_hs_ulpi(
175 _peri: impl Peripheral<P = T> + 'd,
176 irq: impl Peripheral<P = T::Interrupt> + 'd,
177 ulpi_clk: impl Peripheral<P = impl UlpiClkPin<T>> + 'd,
178 ulpi_dir: impl Peripheral<P = impl UlpiDirPin<T>> + 'd,
179 ulpi_nxt: impl Peripheral<P = impl UlpiNxtPin<T>> + 'd,
180 ulpi_stp: impl Peripheral<P = impl UlpiStpPin<T>> + 'd,
181 ulpi_d0: impl Peripheral<P = impl UlpiD0Pin<T>> + 'd,
182 ulpi_d1: impl Peripheral<P = impl UlpiD1Pin<T>> + 'd,
183 ulpi_d2: impl Peripheral<P = impl UlpiD2Pin<T>> + 'd,
184 ulpi_d3: impl Peripheral<P = impl UlpiD3Pin<T>> + 'd,
185 ulpi_d4: impl Peripheral<P = impl UlpiD4Pin<T>> + 'd,
186 ulpi_d5: impl Peripheral<P = impl UlpiD5Pin<T>> + 'd,
187 ulpi_d6: impl Peripheral<P = impl UlpiD6Pin<T>> + 'd,
188 ulpi_d7: impl Peripheral<P = impl UlpiD7Pin<T>> + 'd,
189 ep_out_buffer: &'d mut [u8],
190 ) -> Self {
191 assert!(T::HIGH_SPEED == true, "Peripheral is not capable of high-speed USB");
192
193 config_ulpi_pins!(
194 ulpi_clk, ulpi_dir, ulpi_nxt, ulpi_stp, ulpi_d0, ulpi_d1, ulpi_d2, ulpi_d3, ulpi_d4, ulpi_d5, ulpi_d6,
195 ulpi_d7
196 );
197
198 into_ref!(irq);
199
200 Self {
201 phantom: PhantomData,
202 irq,
203 ep_in: [None; MAX_EP_COUNT],
204 ep_out: [None; MAX_EP_COUNT],
205 ep_out_buffer,
206 ep_out_buffer_offset: 0,
207 phy_type: PhyType::ExternalHighSpeed,
208 }
209 }
210
211 // Returns total amount of words (u32) allocated in dedicated FIFO
212 fn allocated_fifo_words(&self) -> u16 {
213 RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out) + ep_fifo_size(&self.ep_in)
214 }
215
216 fn alloc_endpoint<D: Dir>(
217 &mut self,
218 ep_type: EndpointType,
219 max_packet_size: u16,
220 interval: u8,
221 ) -> Result<Endpoint<'d, T, D>, EndpointAllocError> {
222 trace!(
223 "allocating type={:?} mps={:?} interval={}, dir={:?}",
224 ep_type,
225 max_packet_size,
226 interval,
227 D::dir()
228 );
229
230 if D::dir() == Direction::Out {
231 if self.ep_out_buffer_offset + max_packet_size as usize >= self.ep_out_buffer.len() {
232 error!("Not enough endpoint out buffer capacity");
233 return Err(EndpointAllocError);
234 }
235 };
236
237 let fifo_size_words = match D::dir() {
238 Direction::Out => (max_packet_size + 3) / 4,
239 // INEPTXFD requires minimum size of 16 words
240 Direction::In => u16::max((max_packet_size + 3) / 4, 16),
241 };
242
243 if fifo_size_words + self.allocated_fifo_words() > T::FIFO_DEPTH_WORDS {
244 error!("Not enough FIFO capacity");
245 return Err(EndpointAllocError);
246 }
247
248 let eps = match D::dir() {
249 Direction::Out => &mut self.ep_out,
250 Direction::In => &mut self.ep_in,
251 };
252
253 // Find free endpoint slot
254 let slot = eps.iter_mut().enumerate().find(|(i, ep)| {
255 if *i == 0 && ep_type != EndpointType::Control {
256 // reserved for control pipe
257 false
258 } else {
259 ep.is_none()
260 }
261 });
262
263 let index = match slot {
264 Some((index, ep)) => {
265 *ep = Some(EndpointData {
266 ep_type,
267 max_packet_size,
268 fifo_size_words,
269 });
270 index
271 }
272 None => {
273 error!("No free endpoints available");
274 return Err(EndpointAllocError);
275 }
276 };
277
278 trace!(" index={}", index);
279
280 if D::dir() == Direction::Out {
281 // Buffer capacity check was done above, now allocation cannot fail
282 unsafe {
283 *T::state().ep_out_buffers[index].get() =
284 self.ep_out_buffer.as_mut_ptr().offset(self.ep_out_buffer_offset as _);
285 }
286 self.ep_out_buffer_offset += max_packet_size as usize;
287 }
288
289 Ok(Endpoint {
290 _phantom: PhantomData,
291 info: EndpointInfo {
292 addr: EndpointAddress::from_parts(index, D::dir()),
293 ep_type,
294 max_packet_size,
295 interval,
296 },
297 })
298 }
299}
300
301impl<'d, T: Instance> embassy_usb_driver::Driver<'d> for Driver<'d, T> {
302 type EndpointOut = Endpoint<'d, T, Out>;
303 type EndpointIn = Endpoint<'d, T, In>;
304 type ControlPipe = ControlPipe<'d, T>;
305 type Bus = Bus<'d, T>;
306
307 fn alloc_endpoint_in(
308 &mut self,
309 ep_type: EndpointType,
310 max_packet_size: u16,
311 interval: u8,
312 ) -> Result<Self::EndpointIn, EndpointAllocError> {
313 self.alloc_endpoint(ep_type, max_packet_size, interval)
314 }
315
316 fn alloc_endpoint_out(
317 &mut self,
318 ep_type: EndpointType,
319 max_packet_size: u16,
320 interval: u8,
321 ) -> Result<Self::EndpointOut, EndpointAllocError> {
322 self.alloc_endpoint(ep_type, max_packet_size, interval)
323 }
324
325 fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
326 let ep_out = self
327 .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0)
328 .unwrap();
329 let ep_in = self
330 .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0)
331 .unwrap();
332 assert_eq!(ep_out.info.addr.index(), 0);
333 assert_eq!(ep_in.info.addr.index(), 0);
334
335 trace!("start");
336
337 (
338 Bus {
339 phantom: PhantomData,
340 irq: self.irq,
341 ep_in: self.ep_in,
342 ep_out: self.ep_out,
343 phy_type: self.phy_type,
344 enabled: false,
345 },
346 ControlPipe {
347 _phantom: PhantomData,
348 max_packet_size: control_max_packet_size,
349 ep_out,
350 ep_in,
351 },
352 )
353 }
354}
355
356pub struct Bus<'d, T: Instance> {
357 phantom: PhantomData<&'d mut T>,
358 irq: PeripheralRef<'d, T::Interrupt>,
359 ep_in: [Option<EndpointData>; MAX_EP_COUNT],
360 ep_out: [Option<EndpointData>; MAX_EP_COUNT],
361 phy_type: PhyType,
362 enabled: bool,
363}
364
365impl<'d, T: Instance> Bus<'d, T> {
366 fn restore_irqs() {
367 // SAFETY: atomic write
368 unsafe {
369 T::regs().gintmsk().write(|w| {
370 w.set_usbrst(true);
371 w.set_enumdnem(true);
372 w.set_usbsuspm(true);
373 w.set_wuim(true);
374 w.set_iepint(true);
375 w.set_oepint(true);
376 w.set_rxflvlm(true);
377 });
378 }
379 }
380}
381
382impl<'d, T: Instance> Bus<'d, T> {
383 fn init_fifo(&mut self) {
384 trace!("init_fifo");
385
386 let r = T::regs();
387
388 // Configure RX fifo size. All endpoints share the same FIFO area.
389 let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out);
390 trace!("configuring rx fifo size={}", rx_fifo_size_words);
391
392 // SAFETY: register is exclusive to `Bus` with `&mut self`
393 unsafe { r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words)) };
394
395 // Configure TX (USB in direction) fifo size for each endpoint
396 let mut fifo_top = rx_fifo_size_words;
397 for i in 0..T::ENDPOINT_COUNT {
398 if let Some(ep) = self.ep_in[i] {
399 trace!(
400 "configuring tx fifo ep={}, offset={}, size={}",
401 i,
402 fifo_top,
403 ep.fifo_size_words
404 );
405
406 let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) };
407
408 // SAFETY: register is exclusive to `Bus` with `&mut self`
409 unsafe {
410 dieptxf.write(|w| {
411 w.set_fd(ep.fifo_size_words);
412 w.set_sa(fifo_top);
413 });
414 }
415
416 fifo_top += ep.fifo_size_words;
417 }
418 }
419
420 assert!(
421 fifo_top <= T::FIFO_DEPTH_WORDS,
422 "FIFO allocations exceeded maximum capacity"
423 );
424 }
425
426 fn configure_endpoints(&mut self) {
427 trace!("configure_endpoints");
428
429 let r = T::regs();
430
431 // Configure IN endpoints
432 for (index, ep) in self.ep_in.iter().enumerate() {
433 if let Some(ep) = ep {
434 // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW
435 critical_section::with(|_| unsafe {
436 r.diepctl(index).write(|w| {
437 if index == 0 {
438 w.set_mpsiz(ep0_mpsiz(ep.max_packet_size));
439 } else {
440 w.set_mpsiz(ep.max_packet_size);
441 w.set_eptyp(to_eptyp(ep.ep_type));
442 w.set_sd0pid_sevnfrm(true);
443 }
444 });
445 });
446 }
447 }
448
449 // Configure OUT endpoints
450 for (index, ep) in self.ep_out.iter().enumerate() {
451 if let Some(ep) = ep {
452 // SAFETY: DOEPCTL/DOEPTSIZ is shared with `Endpoint` so critical section is needed for RMW
453 critical_section::with(|_| unsafe {
454 r.doepctl(index).write(|w| {
455 if index == 0 {
456 w.set_mpsiz(ep0_mpsiz(ep.max_packet_size));
457 } else {
458 w.set_mpsiz(ep.max_packet_size);
459 w.set_eptyp(to_eptyp(ep.ep_type));
460 w.set_sd0pid_sevnfrm(true);
461 }
462 });
463
464 r.doeptsiz(index).modify(|w| {
465 w.set_xfrsiz(ep.max_packet_size as _);
466 if index == 0 {
467 w.set_rxdpid_stupcnt(1);
468 } else {
469 w.set_pktcnt(1);
470 }
471 });
472 });
473 }
474 }
475
476 // Enable IRQs for allocated endpoints
477 // SAFETY: register is exclusive to `Bus` with `&mut self`
478 unsafe {
479 r.daintmsk().modify(|w| {
480 w.set_iepm(ep_irq_mask(&self.ep_in));
481 // OUT interrupts not used, handled in RXFLVL
482 // w.set_oepm(ep_irq_mask(&self.ep_out));
483 });
484 }
485 }
486
487 fn disable(&mut self) {
488 self.irq.disable();
489 self.irq.remove_handler();
490
491 <T as RccPeripheral>::disable();
492
493 #[cfg(stm32l4)]
494 unsafe {
495 crate::pac::PWR.cr2().modify(|w| w.set_usv(false));
496 // Cannot disable PWR, because other peripherals might be using it
497 }
498 }
499
500 fn on_interrupt(_: *mut ()) {
501 trace!("irq");
502 let r = T::regs();
503 let state = T::state();
504
505 // SAFETY: atomic read/write
506 let ints = unsafe { r.gintsts().read() };
507 if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() {
508 // Mask interrupts and notify `Bus` to process them
509 unsafe { r.gintmsk().write(|_| {}) };
510 T::state().bus_waker.wake();
511 }
512
513 // Handle RX
514 // SAFETY: atomic read with no side effects
515 while unsafe { r.gintsts().read().rxflvl() } {
516 // SAFETY: atomic "pop" register
517 let status = unsafe { r.grxstsp().read() };
518 let ep_num = status.epnum() as usize;
519 let len = status.bcnt() as usize;
520
521 assert!(ep_num < T::ENDPOINT_COUNT);
522
523 match status.pktstsd() {
524 vals::Pktstsd::SETUP_DATA_RX => {
525 trace!("SETUP_DATA_RX");
526 assert!(len == 8, "invalid SETUP packet length={}", len);
527 assert!(ep_num == 0, "invalid SETUP packet endpoint={}", ep_num);
528
529 if state.ep0_setup_ready.load(Ordering::Relaxed) == false {
530 // SAFETY: exclusive access ensured by atomic bool
531 let data = unsafe { &mut *state.ep0_setup_data.get() };
532 // SAFETY: FIFO reads are exclusive to this IRQ
533 unsafe {
534 data[0..4].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes());
535 data[4..8].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes());
536 }
537 state.ep0_setup_ready.store(true, Ordering::Release);
538 state.ep_out_wakers[0].wake();
539 } else {
540 error!("received SETUP before previous finished processing");
541 // discard FIFO
542 // SAFETY: FIFO reads are exclusive to IRQ
543 unsafe {
544 r.fifo(0).read();
545 r.fifo(0).read();
546 }
547 }
548 }
549 vals::Pktstsd::OUT_DATA_RX => {
550 trace!("OUT_DATA_RX ep={} len={}", ep_num, len);
551
552 if state.ep_out_size[ep_num].load(Ordering::Acquire) == EP_OUT_BUFFER_EMPTY {
553 // SAFETY: Buffer size is allocated to be equal to endpoint's maximum packet size
554 // We trust the peripheral to not exceed its configured MPSIZ
555 let buf = unsafe { core::slice::from_raw_parts_mut(*state.ep_out_buffers[ep_num].get(), len) };
556
557 for chunk in buf.chunks_mut(4) {
558 // RX FIFO is shared so always read from fifo(0)
559 // SAFETY: FIFO reads are exclusive to IRQ
560 let data = unsafe { r.fifo(0).read().0 };
561 chunk.copy_from_slice(&data.to_ne_bytes()[0..chunk.len()]);
562 }
563
564 state.ep_out_size[ep_num].store(len as u16, Ordering::Release);
565 state.ep_out_wakers[ep_num].wake();
566 } else {
567 error!("ep_out buffer overflow index={}", ep_num);
568
569 // discard FIFO data
570 let len_words = (len + 3) / 4;
571 for _ in 0..len_words {
572 // SAFETY: FIFO reads are exclusive to IRQ
573 unsafe { r.fifo(0).read().data() };
574 }
575 }
576 }
577 vals::Pktstsd::OUT_DATA_DONE => {
578 trace!("OUT_DATA_DONE ep={}", ep_num);
579 }
580 vals::Pktstsd::SETUP_DATA_DONE => {
581 trace!("SETUP_DATA_DONE ep={}", ep_num);
582 }
583 x => trace!("unknown PKTSTS: {}", x.0),
584 }
585 }
586
587 // IN endpoint interrupt
588 if ints.iepint() {
589 // SAFETY: atomic read with no side effects
590 let mut ep_mask = unsafe { r.daint().read().iepint() };
591 let mut ep_num = 0;
592
593 // Iterate over endpoints while there are non-zero bits in the mask
594 while ep_mask != 0 {
595 if ep_mask & 1 != 0 {
596 // SAFETY: atomic read with no side effects
597 let ep_ints = unsafe { r.diepint(ep_num).read() };
598
599 // clear all
600 // SAFETY: DIEPINT is exclusive to IRQ
601 unsafe { r.diepint(ep_num).write_value(ep_ints) };
602
603 // TXFE is cleared in DIEPEMPMSK
604 if ep_ints.txfe() {
605 // SAFETY: DIEPEMPMSK is shared with `Endpoint` so critical section is needed for RMW
606 critical_section::with(|_| unsafe {
607 r.diepempmsk().modify(|w| {
608 w.set_ineptxfem(w.ineptxfem() & !(1 << ep_num));
609 });
610 });
611 }
612
613 state.ep_in_wakers[ep_num].wake();
614 trace!("in ep={} irq val={:b}", ep_num, ep_ints.0);
615 }
616
617 ep_mask >>= 1;
618 ep_num += 1;
619 }
620 }
621
622 // not needed? reception handled in rxflvl
623 // OUT endpoint interrupt
624 // if ints.oepint() {
625 // let mut ep_mask = r.daint().read().oepint();
626 // let mut ep_num = 0;
627
628 // while ep_mask != 0 {
629 // if ep_mask & 1 != 0 {
630 // let ep_ints = r.doepint(ep_num).read();
631 // // clear all
632 // r.doepint(ep_num).write_value(ep_ints);
633 // state.ep_out_wakers[ep_num].wake();
634 // trace!("out ep={} irq val={=u32:b}", ep_num, ep_ints.0);
635 // }
636
637 // ep_mask >>= 1;
638 // ep_num += 1;
639 // }
640 // }
641 }
642}
643
644impl<'d, T: Instance> embassy_usb_driver::Bus for Bus<'d, T> {
645 async fn poll(&mut self) -> Event {
646 poll_fn(move |cx| {
647 // TODO: implement VBUS detection
648 if !self.enabled {
649 return Poll::Ready(Event::PowerDetected);
650 }
651
652 let r = T::regs();
653
654 T::state().bus_waker.register(cx.waker());
655
656 let ints = unsafe { r.gintsts().read() };
657 if ints.usbrst() {
658 trace!("reset");
659
660 self.init_fifo();
661 self.configure_endpoints();
662
663 // Reset address
664 // SAFETY: DCFG is shared with `ControlPipe` so critical section is needed for RMW
665 critical_section::with(|_| unsafe {
666 r.dcfg().modify(|w| {
667 w.set_dad(0);
668 });
669 });
670
671 // SAFETY: atomic clear on rc_w1 register
672 unsafe { r.gintsts().write(|w| w.set_usbrst(true)) }; // clear
673 Self::restore_irqs();
674 }
675
676 if ints.enumdne() {
677 trace!("enumdne");
678
679 // SAFETY: atomic read with no side effects
680 let speed = unsafe { r.dsts().read().enumspd() };
681 trace!(" speed={}", speed.0);
682
683 // SAFETY: register is only accessed by `Bus` under `&mut self`
684 unsafe {
685 r.gusbcfg().modify(|w| {
686 w.set_trdt(calculate_trdt(speed, T::frequency()));
687 })
688 };
689
690 // SAFETY: atomic clear on rc_w1 register
691 unsafe { r.gintsts().write(|w| w.set_enumdne(true)) }; // clear
692 Self::restore_irqs();
693
694 return Poll::Ready(Event::Reset);
695 }
696
697 if ints.usbsusp() {
698 trace!("suspend");
699 // SAFETY: atomic clear on rc_w1 register
700 unsafe { r.gintsts().write(|w| w.set_usbsusp(true)) }; // clear
701 Self::restore_irqs();
702 return Poll::Ready(Event::Suspend);
703 }
704
705 if ints.wkupint() {
706 trace!("resume");
707 // SAFETY: atomic clear on rc_w1 register
708 unsafe { r.gintsts().write(|w| w.set_wkupint(true)) }; // clear
709 Self::restore_irqs();
710 return Poll::Ready(Event::Resume);
711 }
712
713 Poll::Pending
714 })
715 .await
716 }
717
718 fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
719 trace!("endpoint_set_stalled ep={:?} en={}", ep_addr, stalled);
720
721 assert!(
722 ep_addr.index() < T::ENDPOINT_COUNT,
723 "endpoint_set_stalled index {} out of range",
724 ep_addr.index()
725 );
726
727 let regs = T::regs();
728 match ep_addr.direction() {
729 Direction::Out => {
730 // SAFETY: DOEPCTL is shared with `Endpoint` so critical section is needed for RMW
731 critical_section::with(|_| unsafe {
732 regs.doepctl(ep_addr.index()).modify(|w| {
733 w.set_stall(stalled);
734 });
735 });
736
737 T::state().ep_out_wakers[ep_addr.index()].wake();
738 }
739 Direction::In => {
740 // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW
741 critical_section::with(|_| unsafe {
742 regs.diepctl(ep_addr.index()).modify(|w| {
743 w.set_stall(stalled);
744 });
745 });
746
747 T::state().ep_in_wakers[ep_addr.index()].wake();
748 }
749 }
750 }
751
752 fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {
753 assert!(
754 ep_addr.index() < T::ENDPOINT_COUNT,
755 "endpoint_is_stalled index {} out of range",
756 ep_addr.index()
757 );
758
759 let regs = T::regs();
760
761 // SAFETY: atomic read with no side effects
762 match ep_addr.direction() {
763 Direction::Out => unsafe { regs.doepctl(ep_addr.index()).read().stall() },
764 Direction::In => unsafe { regs.diepctl(ep_addr.index()).read().stall() },
765 }
766 }
767
768 fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {
769 trace!("endpoint_set_enabled ep={:?} en={}", ep_addr, enabled);
770
771 assert!(
772 ep_addr.index() < T::ENDPOINT_COUNT,
773 "endpoint_set_enabled index {} out of range",
774 ep_addr.index()
775 );
776
777 let r = T::regs();
778 match ep_addr.direction() {
779 Direction::Out => {
780 // SAFETY: DOEPCTL is shared with `Endpoint` so critical section is needed for RMW
781 critical_section::with(|_| unsafe {
782 // cancel transfer if active
783 if !enabled && r.doepctl(ep_addr.index()).read().epena() {
784 r.doepctl(ep_addr.index()).modify(|w| {
785 w.set_snak(true);
786 w.set_epdis(true);
787 })
788 }
789
790 r.doepctl(ep_addr.index()).modify(|w| {
791 w.set_usbaep(enabled);
792 })
793 });
794 }
795 Direction::In => {
796 // SAFETY: DIEPCTL is shared with `Endpoint` so critical section is needed for RMW
797 critical_section::with(|_| unsafe {
798 // cancel transfer if active
799 if !enabled && r.diepctl(ep_addr.index()).read().epena() {
800 r.diepctl(ep_addr.index()).modify(|w| {
801 w.set_snak(true);
802 w.set_epdis(true);
803 })
804 }
805
806 r.diepctl(ep_addr.index()).modify(|w| {
807 w.set_usbaep(enabled);
808 })
809 });
810 }
811 }
812 }
813
814 async fn enable(&mut self) {
815 trace!("enable");
816
817 // SAFETY: registers are only accessed by `Bus` under `&mut self`
818 unsafe {
819 #[cfg(stm32l4)]
820 {
821 crate::peripherals::PWR::enable();
822 critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true)));
823 }
824
825 #[cfg(stm32h7)]
826 {
827 // If true, VDD33USB is generated by internal regulator from VDD50USB
828 // If false, VDD33USB and VDD50USB must be suplied directly with 3.3V (default on nucleo)
829 // TODO: unhardcode
830 let internal_regulator = false;
831
832 // Enable USB power
833 critical_section::with(|_| {
834 crate::pac::PWR.cr3().modify(|w| {
835 w.set_usb33den(true);
836 w.set_usbregen(internal_regulator);
837 })
838 });
839
840 // Wait for USB power to stabilize
841 while !crate::pac::PWR.cr3().read().usb33rdy() {}
842
843 // Use internal 48MHz HSI clock. Should be enabled in RCC by default.
844 critical_section::with(|_| {
845 crate::pac::RCC
846 .d2ccip2r()
847 .modify(|w| w.set_usbsel(crate::pac::rcc::vals::Usbsel::HSI48))
848 });
849
850 // Enable ULPI clock if external PHY is used
851 let ulpien = !self.phy_type.internal();
852 critical_section::with(|_| {
853 crate::pac::RCC.ahb1enr().modify(|w| {
854 if T::HIGH_SPEED {
855 w.set_usb_otg_hs_ulpien(ulpien);
856 } else {
857 w.set_usb_otg_fs_ulpien(ulpien);
858 }
859 });
860 crate::pac::RCC.ahb1lpenr().modify(|w| {
861 if T::HIGH_SPEED {
862 w.set_usb_otg_hs_ulpilpen(ulpien);
863 } else {
864 w.set_usb_otg_fs_ulpilpen(ulpien);
865 }
866 });
867 });
868 }
869
870 #[cfg(stm32u5)]
871 {
872 // Enable USB power
873 critical_section::with(|_| {
874 crate::pac::RCC.ahb3enr().modify(|w| {
875 w.set_pwren(true);
876 });
877 cortex_m::asm::delay(2);
878
879 crate::pac::PWR.svmcr().modify(|w| {
880 w.set_usv(true);
881 w.set_uvmen(true);
882 });
883 });
884
885 // Wait for USB power to stabilize
886 while !crate::pac::PWR.svmsr().read().vddusbrdy() {}
887
888 // Select HSI48 as USB clock source.
889 critical_section::with(|_| {
890 crate::pac::RCC.ccipr1().modify(|w| {
891 w.set_iclksel(crate::pac::rcc::vals::Iclksel::HSI48);
892 })
893 });
894 }
895
896 <T as RccPeripheral>::enable();
897 <T as RccPeripheral>::reset();
898
899 self.irq.set_handler(Self::on_interrupt);
900 self.irq.unpend();
901 self.irq.enable();
902
903 let r = T::regs();
904 let core_id = r.cid().read().0;
905 info!("Core id {:08x}", core_id);
906
907 // Wait for AHB ready.
908 while !r.grstctl().read().ahbidl() {}
909
910 // Configure as device.
911 r.gusbcfg().write(|w| {
912 // Force device mode
913 w.set_fdmod(true);
914 // Enable internal full-speed PHY
915 w.set_physel(self.phy_type.internal() && !self.phy_type.high_speed());
916 });
917
918 // Configuring Vbus sense and SOF output
919 match core_id {
920 0x0000_1200 | 0x0000_1100 => {
921 assert!(self.phy_type != PhyType::InternalHighSpeed);
922
923 r.gccfg_v1().modify(|w| {
924 // Enable internal full-speed PHY, logic is inverted
925 w.set_pwrdwn(self.phy_type.internal());
926 });
927
928 // F429-like chips have the GCCFG.NOVBUSSENS bit
929 r.gccfg_v1().modify(|w| {
930 w.set_novbussens(true);
931 w.set_vbusasen(false);
932 w.set_vbusbsen(false);
933 w.set_sofouten(false);
934 });
935 }
936 0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => {
937 // F446-like chips have the GCCFG.VBDEN bit with the opposite meaning
938 r.gccfg_v2().modify(|w| {
939 // Enable internal full-speed PHY, logic is inverted
940 w.set_pwrdwn(self.phy_type.internal() && !self.phy_type.high_speed());
941 w.set_phyhsen(self.phy_type.internal() && self.phy_type.high_speed());
942 });
943
944 r.gccfg_v2().modify(|w| {
945 w.set_vbden(false);
946 });
947
948 // Force B-peripheral session
949 r.gotgctl().modify(|w| {
950 w.set_bvaloen(true);
951 w.set_bvaloval(true);
952 });
953 }
954 _ => unimplemented!("Unknown USB core id {:X}", core_id),
955 }
956
957 // Soft disconnect.
958 r.dctl().write(|w| w.set_sdis(true));
959
960 // Set speed.
961 r.dcfg().write(|w| {
962 w.set_pfivl(vals::Pfivl::FRAME_INTERVAL_80);
963 w.set_dspd(self.phy_type.to_dspd());
964 });
965
966 // Unmask transfer complete EP interrupt
967 r.diepmsk().write(|w| {
968 w.set_xfrcm(true);
969 });
970
971 // Unmask and clear core interrupts
972 Bus::<T>::restore_irqs();
973 r.gintsts().write_value(regs::Gintsts(0xFFFF_FFFF));
974
975 // Unmask global interrupt
976 r.gahbcfg().write(|w| {
977 w.set_gint(true); // unmask global interrupt
978 });
979
980 // Connect
981 r.dctl().write(|w| w.set_sdis(false));
982 }
983
984 self.enabled = true;
985 }
986
987 async fn disable(&mut self) {
988 trace!("disable");
989
990 Bus::disable(self);
991
992 self.enabled = false;
993 }
994
995 async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {
996 Err(Unsupported)
997 }
998}
999
1000impl<'d, T: Instance> Drop for Bus<'d, T> {
1001 fn drop(&mut self) {
1002 Bus::disable(self);
1003 }
1004}
1005
1006trait Dir {
1007 fn dir() -> Direction;
1008}
1009
1010pub enum In {}
1011impl Dir for In {
1012 fn dir() -> Direction {
1013 Direction::In
1014 }
1015}
1016
1017pub enum Out {}
1018impl Dir for Out {
1019 fn dir() -> Direction {
1020 Direction::Out
1021 }
1022}
1023
1024pub struct Endpoint<'d, T: Instance, D> {
1025 _phantom: PhantomData<(&'d mut T, D)>,
1026 info: EndpointInfo,
1027}
1028
1029impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, In> {
1030 fn info(&self) -> &EndpointInfo {
1031 &self.info
1032 }
1033
1034 async fn wait_enabled(&mut self) {}
1035}
1036
1037impl<'d, T: Instance> embassy_usb_driver::Endpoint for Endpoint<'d, T, Out> {
1038 fn info(&self) -> &EndpointInfo {
1039 &self.info
1040 }
1041
1042 async fn wait_enabled(&mut self) {}
1043}
1044
1045impl<'d, T: Instance> embassy_usb_driver::EndpointOut for Endpoint<'d, T, Out> {
1046 async fn read(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
1047 trace!("read start len={}", buf.len());
1048
1049 poll_fn(|cx| {
1050 let index = self.info.addr.index();
1051 let state = T::state();
1052
1053 state.ep_out_wakers[index].register(cx.waker());
1054
1055 let len = state.ep_out_size[index].load(Ordering::Relaxed);
1056 if len != EP_OUT_BUFFER_EMPTY {
1057 trace!("read done len={}", len);
1058
1059 if len as usize > buf.len() {
1060 return Poll::Ready(Err(EndpointError::BufferOverflow));
1061 }
1062
1063 // SAFETY: exclusive access ensured by `ep_out_size` atomic variable
1064 let data = unsafe { core::slice::from_raw_parts(*state.ep_out_buffers[index].get(), len as usize) };
1065 buf[..len as usize].copy_from_slice(data);
1066
1067 // Release buffer
1068 state.ep_out_size[index].store(EP_OUT_BUFFER_EMPTY, Ordering::Release);
1069
1070 // SAFETY: DOEPCTL/DOEPTSIZ is shared with `Bus` so a critical section is needed for RMW
1071 critical_section::with(|_| unsafe {
1072 // Receive 1 packet
1073 T::regs().doeptsiz(index).modify(|w| {
1074 w.set_xfrsiz(self.info.max_packet_size as _);
1075 w.set_pktcnt(1);
1076 });
1077
1078 // Clear NAK to indicate we are ready to receive more data
1079 T::regs().doepctl(index).modify(|w| {
1080 w.set_cnak(true);
1081 });
1082 });
1083
1084 Poll::Ready(Ok(len as usize))
1085 } else {
1086 Poll::Pending
1087 }
1088 })
1089 .await
1090 }
1091}
1092
1093impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> {
1094 async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> {
1095 if buf.len() > self.info.max_packet_size as usize {
1096 return Err(EndpointError::BufferOverflow);
1097 }
1098
1099 let r = T::regs();
1100 let index = self.info.addr.index();
1101 let state = T::state();
1102
1103 // Wait for previous transfer to complete
1104 poll_fn(|cx| {
1105 state.ep_in_wakers[index].register(cx.waker());
1106
1107 // SAFETY: atomic read with no side effects
1108 if unsafe { r.diepctl(index).read().epena() } {
1109 Poll::Pending
1110 } else {
1111 Poll::Ready(())
1112 }
1113 })
1114 .await;
1115
1116 if buf.len() > 0 {
1117 poll_fn(|cx| {
1118 state.ep_in_wakers[index].register(cx.waker());
1119
1120 let size_words = (buf.len() + 3) / 4;
1121
1122 // SAFETY: atomic read with no side effects
1123 let fifo_space = unsafe { r.dtxfsts(index).read().ineptfsav() as usize };
1124 if size_words > fifo_space {
1125 // Not enough space in fifo, enable tx fifo empty interrupt
1126 // SAFETY: DIEPEMPMSK is shared with IRQ so critical section is needed for RMW
1127 critical_section::with(|_| unsafe {
1128 r.diepempmsk().modify(|w| {
1129 w.set_ineptxfem(w.ineptxfem() | (1 << index));
1130 });
1131 });
1132
1133 trace!("tx fifo for ep={} full, waiting for txfe", index);
1134
1135 Poll::Pending
1136 } else {
1137 Poll::Ready(())
1138 }
1139 })
1140 .await
1141 }
1142
1143 // SAFETY: DIEPTSIZ is exclusive to this endpoint under `&mut self`
1144 unsafe {
1145 // Setup transfer size
1146 r.dieptsiz(index).write(|w| {
1147 w.set_mcnt(1);
1148 w.set_pktcnt(1);
1149 w.set_xfrsiz(buf.len() as _);
1150 });
1151 }
1152
1153 // SAFETY: DIEPCTL is shared with `Bus` so a critical section is needed for RMW
1154 critical_section::with(|_| unsafe {
1155 // Enable endpoint
1156 r.diepctl(index).modify(|w| {
1157 w.set_cnak(true);
1158 w.set_epena(true);
1159 });
1160 });
1161
1162 // Write data to FIFO
1163 for chunk in buf.chunks(4) {
1164 let mut tmp = [0u8; 4];
1165 tmp[0..chunk.len()].copy_from_slice(chunk);
1166 // SAFETY: FIFO is exclusive to this endpoint under `&mut self`
1167 unsafe { r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))) };
1168 }
1169
1170 trace!("WRITE OK");
1171
1172 Ok(())
1173 }
1174}
1175
1176pub struct ControlPipe<'d, T: Instance> {
1177 _phantom: PhantomData<&'d mut T>,
1178 max_packet_size: u16,
1179 ep_in: Endpoint<'d, T, In>,
1180 ep_out: Endpoint<'d, T, Out>,
1181}
1182
1183impl<'d, T: Instance> embassy_usb_driver::ControlPipe for ControlPipe<'d, T> {
1184 fn max_packet_size(&self) -> usize {
1185 usize::from(self.max_packet_size)
1186 }
1187
1188 async fn setup(&mut self) -> [u8; 8] {
1189 poll_fn(|cx| {
1190 let state = T::state();
1191
1192 state.ep_out_wakers[0].register(cx.waker());
1193
1194 if state.ep0_setup_ready.load(Ordering::Relaxed) {
1195 let data = unsafe { *state.ep0_setup_data.get() };
1196 state.ep0_setup_ready.store(false, Ordering::Release);
1197
1198 // EP0 should not be controlled by `Bus` so this RMW does not need a critical section
1199 unsafe {
1200 // Receive 1 SETUP packet
1201 T::regs().doeptsiz(self.ep_out.info.addr.index()).modify(|w| {
1202 w.set_rxdpid_stupcnt(1);
1203 });
1204
1205 // Clear NAK to indicate we are ready to receive more data
1206 T::regs().doepctl(self.ep_out.info.addr.index()).modify(|w| {
1207 w.set_cnak(true);
1208 })
1209 };
1210
1211 trace!("SETUP received: {:?}", data);
1212 Poll::Ready(data)
1213 } else {
1214 trace!("SETUP waiting");
1215 Poll::Pending
1216 }
1217 })
1218 .await
1219 }
1220
1221 async fn data_out(&mut self, buf: &mut [u8], _first: bool, _last: bool) -> Result<usize, EndpointError> {
1222 trace!("control: data_out");
1223 let len = self.ep_out.read(buf).await?;
1224 trace!("control: data_out read: {:?}", &buf[..len]);
1225 Ok(len)
1226 }
1227
1228 async fn data_in(&mut self, data: &[u8], _first: bool, last: bool) -> Result<(), EndpointError> {
1229 trace!("control: data_in write: {:?}", data);
1230 self.ep_in.write(data).await?;
1231
1232 // wait for status response from host after sending the last packet
1233 if last {
1234 trace!("control: data_in waiting for status");
1235 self.ep_out.read(&mut []).await?;
1236 trace!("control: complete");
1237 }
1238
1239 Ok(())
1240 }
1241
1242 async fn accept(&mut self) {
1243 trace!("control: accept");
1244
1245 self.ep_in.write(&[]).await.ok();
1246
1247 trace!("control: accept OK");
1248 }
1249
1250 async fn reject(&mut self) {
1251 trace!("control: reject");
1252
1253 // EP0 should not be controlled by `Bus` so this RMW does not need a critical section
1254 unsafe {
1255 let regs = T::regs();
1256 regs.diepctl(self.ep_in.info.addr.index()).modify(|w| {
1257 w.set_stall(true);
1258 });
1259 regs.doepctl(self.ep_out.info.addr.index()).modify(|w| {
1260 w.set_stall(true);
1261 });
1262 }
1263 }
1264
1265 async fn accept_set_address(&mut self, addr: u8) {
1266 trace!("setting addr: {}", addr);
1267 critical_section::with(|_| unsafe {
1268 T::regs().dcfg().modify(|w| {
1269 w.set_dad(addr);
1270 });
1271 });
1272
1273 // synopsys driver requires accept to be sent after changing address
1274 self.accept().await
1275 }
1276}
1277
1278/// Translates HAL [EndpointType] into PAC [vals::Eptyp]
1279fn to_eptyp(ep_type: EndpointType) -> vals::Eptyp {
1280 match ep_type {
1281 EndpointType::Control => vals::Eptyp::CONTROL,
1282 EndpointType::Isochronous => vals::Eptyp::ISOCHRONOUS,
1283 EndpointType::Bulk => vals::Eptyp::BULK,
1284 EndpointType::Interrupt => vals::Eptyp::INTERRUPT,
1285 }
1286}
1287
1288/// Calculates total allocated FIFO size in words
1289fn ep_fifo_size(eps: &[Option<EndpointData>]) -> u16 {
1290 eps.iter().map(|ep| ep.map(|ep| ep.fifo_size_words).unwrap_or(0)).sum()
1291}
1292
1293/// Generates IRQ mask for enabled endpoints
1294fn ep_irq_mask(eps: &[Option<EndpointData>]) -> u16 {
1295 eps.iter().enumerate().fold(
1296 0,
1297 |mask, (index, ep)| {
1298 if ep.is_some() {
1299 mask | (1 << index)
1300 } else {
1301 mask
1302 }
1303 },
1304 )
1305}
1306
1307/// Calculates MPSIZ value for EP0, which uses special values.
1308fn ep0_mpsiz(max_packet_size: u16) -> u16 {
1309 match max_packet_size {
1310 8 => 0b11,
1311 16 => 0b10,
1312 32 => 0b01,
1313 64 => 0b00,
1314 other => panic!("Unsupported EP0 size: {}", other),
1315 }
1316}
1317
1318fn calculate_trdt(speed: vals::Dspd, ahb_freq: Hertz) -> u8 {
1319 match speed {
1320 vals::Dspd::HIGH_SPEED => {
1321 // From RM0431 (F72xx), RM0090 (F429), RM0390 (F446)
1322 if ahb_freq.0 >= 30_000_000 {
1323 0x9
1324 } else {
1325 panic!("AHB frequency is too low")
1326 }
1327 }
1328 vals::Dspd::FULL_SPEED_EXTERNAL | vals::Dspd::FULL_SPEED_INTERNAL => {
1329 // From RM0431 (F72xx), RM0090 (F429)
1330 match ahb_freq.0 {
1331 0..=14_199_999 => panic!("AHB frequency is too low"),
1332 14_200_000..=14_999_999 => 0xF,
1333 15_000_000..=15_999_999 => 0xE,
1334 16_000_000..=17_199_999 => 0xD,
1335 17_200_000..=18_499_999 => 0xC,
1336 18_500_000..=19_999_999 => 0xB,
1337 20_000_000..=21_799_999 => 0xA,
1338 21_800_000..=23_999_999 => 0x9,
1339 24_000_000..=27_499_999 => 0x8,
1340 27_500_000..=31_999_999 => 0x7, // 27.7..32 in code from CubeIDE
1341 32_000_000..=u32::MAX => 0x6,
1342 }
1343 }
1344 _ => unimplemented!(),
1345 }
1346}