aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2023-01-11 17:05:59 +0000
committerGitHub <[email protected]>2023-01-11 17:05:59 +0000
commit88fd521b016be18230b5b3d4b3bf96c29f10e202 (patch)
treed416bfda40860e61898e5599809c503c7149285a
parent96b97c4711af0851f7d3a6785be13b0202b6e902 (diff)
parent1af102a1aaa11d03bfa37831a3284546b605efd8 (diff)
Merge #1145
1145: STM32 USB OTG #2 r=Dirbaio a=chemicstry This is a continuation of #799 The usb serial example is already working! TODO: - [x] Add critical sections to registers shared with IRQ - [x] Fix `disable()` - [x] ~Implement cable disconnect detection~ - postponed - [x] Fix `endpoint_set_enabled()` - [x] ~Endpoint `wait_enabled`~ USB OTG does not have enable delay (?) - [x] HS internal and HS ULPI PHY - untested Co-authored-by: chemicstry <[email protected]> Co-authored-by: Dario Nieuwenhuis <[email protected]>
-rw-r--r--embassy-nrf/src/usb.rs10
-rw-r--r--embassy-rp/src/usb.rs15
-rw-r--r--embassy-stm32/build.rs30
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/rcc/u5.rs30
-rw-r--r--embassy-stm32/src/usb/usb.rs26
-rw-r--r--embassy-stm32/src/usb_otg.rs213
-rw-r--r--embassy-stm32/src/usb_otg/mod.rs161
-rw-r--r--embassy-stm32/src/usb_otg/usb.rs1346
-rw-r--r--embassy-usb-driver/src/lib.rs9
-rw-r--r--embassy-usb/src/lib.rs23
-rw-r--r--examples/stm32f4/Cargo.toml5
-rw-r--r--examples/stm32f4/src/bin/usb_serial.rs106
-rw-r--r--examples/stm32f7/Cargo.toml1
-rw-r--r--examples/stm32f7/src/bin/usb_serial.rs107
-rw-r--r--examples/stm32h7/Cargo.toml1
-rw-r--r--examples/stm32h7/src/bin/usb_serial.rs106
-rw-r--r--examples/stm32l4/Cargo.toml4
-rw-r--r--examples/stm32l4/src/bin/usb_serial.rs108
-rw-r--r--examples/stm32u5/Cargo.toml1
-rw-r--r--examples/stm32u5/src/bin/usb_serial.rs108
m---------stm32-data0
22 files changed, 2131 insertions, 281 deletions
diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs
index ed4d5cf35..6be4fec8c 100644
--- a/embassy-nrf/src/usb.rs
+++ b/embassy-nrf/src/usb.rs
@@ -391,11 +391,6 @@ impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
391 .await 391 .await
392 } 392 }
393 393
394 #[inline]
395 fn set_address(&mut self, _addr: u8) {
396 // Nothing to do, the peripheral handles this.
397 }
398
399 fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { 394 fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
400 let regs = T::regs(); 395 let regs = T::regs();
401 unsafe { 396 unsafe {
@@ -841,6 +836,11 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
841 let regs = T::regs(); 836 let regs = T::regs();
842 regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(true)); 837 regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(true));
843 } 838 }
839
840 async fn accept_set_address(&mut self, _addr: u8) {
841 self.accept().await;
842 // Nothing to do, the peripheral handles this.
843 }
844} 844}
845 845
846fn dma_start() { 846fn dma_start() {
diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs
index 8361736a9..2710c4ad9 100644
--- a/embassy-rp/src/usb.rs
+++ b/embassy-rp/src/usb.rs
@@ -406,13 +406,6 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
406 .await 406 .await
407 } 407 }
408 408
409 #[inline]
410 fn set_address(&mut self, addr: u8) {
411 let regs = T::regs();
412 trace!("setting addr: {}", addr);
413 unsafe { regs.addr_endp().write(|w| w.set_address(addr)) }
414 }
415
416 fn endpoint_set_stalled(&mut self, _ep_addr: EndpointAddress, _stalled: bool) { 409 fn endpoint_set_stalled(&mut self, _ep_addr: EndpointAddress, _stalled: bool) {
417 todo!(); 410 todo!();
418 } 411 }
@@ -812,4 +805,12 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
812 T::dpram().ep_in_buffer_control(0).write(|w| w.set_stall(true)); 805 T::dpram().ep_in_buffer_control(0).write(|w| w.set_stall(true));
813 } 806 }
814 } 807 }
808
809 async fn accept_set_address(&mut self, addr: u8) {
810 self.accept().await;
811
812 let regs = T::regs();
813 trace!("setting addr: {}", addr);
814 unsafe { regs.addr_endp().write(|w| w.set_address(addr)) }
815 }
815} 816}
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index ddfa97b29..dbfc1370d 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -277,22 +277,20 @@ fn main() {
277 (("dcmi", "PIXCLK"), quote!(crate::dcmi::PixClkPin)), 277 (("dcmi", "PIXCLK"), quote!(crate::dcmi::PixClkPin)),
278 (("usb", "DP"), quote!(crate::usb::DpPin)), 278 (("usb", "DP"), quote!(crate::usb::DpPin)),
279 (("usb", "DM"), quote!(crate::usb::DmPin)), 279 (("usb", "DM"), quote!(crate::usb::DmPin)),
280 (("otgfs", "DP"), quote!(crate::usb_otg::DpPin)), 280 (("otg", "DP"), quote!(crate::usb_otg::DpPin)),
281 (("otgfs", "DM"), quote!(crate::usb_otg::DmPin)), 281 (("otg", "DM"), quote!(crate::usb_otg::DmPin)),
282 (("otghs", "DP"), quote!(crate::usb_otg::DpPin)), 282 (("otg", "ULPI_CK"), quote!(crate::usb_otg::UlpiClkPin)),
283 (("otghs", "DM"), quote!(crate::usb_otg::DmPin)), 283 (("otg", "ULPI_DIR"), quote!(crate::usb_otg::UlpiDirPin)),
284 (("otghs", "ULPI_CK"), quote!(crate::usb_otg::UlpiClkPin)), 284 (("otg", "ULPI_NXT"), quote!(crate::usb_otg::UlpiNxtPin)),
285 (("otghs", "ULPI_DIR"), quote!(crate::usb_otg::UlpiDirPin)), 285 (("otg", "ULPI_STP"), quote!(crate::usb_otg::UlpiStpPin)),
286 (("otghs", "ULPI_NXT"), quote!(crate::usb_otg::UlpiNxtPin)), 286 (("otg", "ULPI_D0"), quote!(crate::usb_otg::UlpiD0Pin)),
287 (("otghs", "ULPI_STP"), quote!(crate::usb_otg::UlpiStpPin)), 287 (("otg", "ULPI_D1"), quote!(crate::usb_otg::UlpiD1Pin)),
288 (("otghs", "ULPI_D0"), quote!(crate::usb_otg::UlpiD0Pin)), 288 (("otg", "ULPI_D2"), quote!(crate::usb_otg::UlpiD2Pin)),
289 (("otghs", "ULPI_D1"), quote!(crate::usb_otg::UlpiD1Pin)), 289 (("otg", "ULPI_D3"), quote!(crate::usb_otg::UlpiD3Pin)),
290 (("otghs", "ULPI_D2"), quote!(crate::usb_otg::UlpiD2Pin)), 290 (("otg", "ULPI_D4"), quote!(crate::usb_otg::UlpiD4Pin)),
291 (("otghs", "ULPI_D3"), quote!(crate::usb_otg::UlpiD3Pin)), 291 (("otg", "ULPI_D5"), quote!(crate::usb_otg::UlpiD5Pin)),
292 (("otghs", "ULPI_D4"), quote!(crate::usb_otg::UlpiD4Pin)), 292 (("otg", "ULPI_D6"), quote!(crate::usb_otg::UlpiD6Pin)),
293 (("otghs", "ULPI_D5"), quote!(crate::usb_otg::UlpiD5Pin)), 293 (("otg", "ULPI_D7"), quote!(crate::usb_otg::UlpiD7Pin)),
294 (("otghs", "ULPI_D6"), quote!(crate::usb_otg::UlpiD6Pin)),
295 (("otghs", "ULPI_D7"), quote!(crate::usb_otg::UlpiD7Pin)),
296 (("can", "TX"), quote!(crate::can::TxPin)), 294 (("can", "TX"), quote!(crate::can::TxPin)),
297 (("can", "RX"), quote!(crate::can::RxPin)), 295 (("can", "RX"), quote!(crate::can::RxPin)),
298 (("eth", "REF_CLK"), quote!(crate::eth::RefClkPin)), 296 (("eth", "REF_CLK"), quote!(crate::eth::RefClkPin)),
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 16c46ca22..610c24888 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -58,7 +58,7 @@ pub mod spi;
58pub mod usart; 58pub mod usart;
59#[cfg(all(usb, feature = "time"))] 59#[cfg(all(usb, feature = "time"))]
60pub mod usb; 60pub mod usb;
61#[cfg(any(otgfs, otghs))] 61#[cfg(otg)]
62pub mod usb_otg; 62pub mod usb_otg;
63 63
64#[cfg(iwdg)] 64#[cfg(iwdg)]
diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs
index 960c45322..2ba339ecf 100644
--- a/embassy-stm32/src/rcc/u5.rs
+++ b/embassy-stm32/src/rcc/u5.rs
@@ -295,6 +295,7 @@ pub struct Config {
295 pub apb1_pre: APBPrescaler, 295 pub apb1_pre: APBPrescaler,
296 pub apb2_pre: APBPrescaler, 296 pub apb2_pre: APBPrescaler,
297 pub apb3_pre: APBPrescaler, 297 pub apb3_pre: APBPrescaler,
298 pub hsi48: bool,
298} 299}
299 300
300impl Default for Config { 301impl Default for Config {
@@ -305,6 +306,7 @@ impl Default for Config {
305 apb1_pre: Default::default(), 306 apb1_pre: Default::default(),
306 apb2_pre: Default::default(), 307 apb2_pre: Default::default(),
307 apb3_pre: Default::default(), 308 apb3_pre: Default::default(),
309 hsi48: false,
308 } 310 }
309 } 311 }
310} 312}
@@ -320,7 +322,6 @@ pub(crate) unsafe fn init(config: Config) {
320 RCC.cr().write(|w| { 322 RCC.cr().write(|w| {
321 w.set_msipllen(false); 323 w.set_msipllen(false);
322 w.set_msison(true); 324 w.set_msison(true);
323 w.set_msison(true);
324 }); 325 });
325 while !RCC.cr().read().msisrdy() {} 326 while !RCC.cr().read().msisrdy() {}
326 327
@@ -340,9 +341,20 @@ pub(crate) unsafe fn init(config: Config) {
340 } 341 }
341 ClockSrc::PLL1R(src, m, n, div) => { 342 ClockSrc::PLL1R(src, m, n, div) => {
342 let freq = match src { 343 let freq = match src {
343 PllSrc::MSI(_) => MSIRange::default().into(), 344 PllSrc::MSI(_) => {
344 PllSrc::HSE(hertz) => hertz.0, 345 // TODO: enable MSI
345 PllSrc::HSI16 => HSI_FREQ.0, 346 MSIRange::default().into()
347 }
348 PllSrc::HSE(hertz) => {
349 // TODO: enable HSE
350 hertz.0
351 }
352 PllSrc::HSI16 => {
353 RCC.cr().write(|w| w.set_hsion(true));
354 while !RCC.cr().read().hsirdy() {}
355
356 HSI_FREQ.0
357 }
346 }; 358 };
347 359
348 // disable 360 // disable
@@ -355,6 +367,7 @@ pub(crate) unsafe fn init(config: Config) {
355 RCC.pll1cfgr().write(|w| { 367 RCC.pll1cfgr().write(|w| {
356 w.set_pllm(m.into()); 368 w.set_pllm(m.into());
357 w.set_pllsrc(src.into()); 369 w.set_pllsrc(src.into());
370 w.set_pllren(true);
358 }); 371 });
359 372
360 RCC.pll1divr().modify(|w| { 373 RCC.pll1divr().modify(|w| {
@@ -365,15 +378,16 @@ pub(crate) unsafe fn init(config: Config) {
365 // Enable PLL 378 // Enable PLL
366 RCC.cr().modify(|w| w.set_pllon(0, true)); 379 RCC.cr().modify(|w| w.set_pllon(0, true));
367 while !RCC.cr().read().pllrdy(0) {} 380 while !RCC.cr().read().pllrdy(0) {}
368 RCC.pll1cfgr().modify(|w| w.set_pllren(true));
369
370 RCC.cr().write(|w| w.set_pllon(0, true));
371 while !RCC.cr().read().pllrdy(0) {}
372 381
373 pll_ck 382 pll_ck
374 } 383 }
375 }; 384 };
376 385
386 if config.hsi48 {
387 RCC.cr().modify(|w| w.set_hsi48on(true));
388 while !RCC.cr().read().hsi48rdy() {}
389 }
390
377 // TODO make configurable 391 // TODO make configurable
378 let power_vos = VoltageScale::Range4; 392 let power_vos = VoltageScale::Range4;
379 393
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs
index cba4915c1..03e792a21 100644
--- a/embassy-stm32/src/usb/usb.rs
+++ b/embassy-stm32/src/usb/usb.rs
@@ -154,6 +154,7 @@ impl<'d, T: Instance> Driver<'d, T> {
154 154
155 block_for(Duration::from_millis(100)); 155 block_for(Duration::from_millis(100));
156 156
157 #[cfg(not(usb_v4))]
157 regs.btable().write(|w| w.set_btable(0)); 158 regs.btable().write(|w| w.set_btable(0));
158 159
159 dp.set_as_af(dp.af_num(), AFType::OutputPushPull); 160 dp.set_as_af(dp.af_num(), AFType::OutputPushPull);
@@ -489,18 +490,6 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
489 .await 490 .await
490 } 491 }
491 492
492 #[inline]
493 fn set_address(&mut self, addr: u8) {
494 let regs = T::regs();
495 trace!("setting addr: {}", addr);
496 unsafe {
497 regs.daddr().write(|w| {
498 w.set_ef(true);
499 w.set_add(addr);
500 })
501 }
502 }
503
504 fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { 493 fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
505 // This can race, so do a retry loop. 494 // This can race, so do a retry loop.
506 let reg = T::regs().epr(ep_addr.index() as _); 495 let reg = T::regs().epr(ep_addr.index() as _);
@@ -1017,4 +1006,17 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
1017 }); 1006 });
1018 } 1007 }
1019 } 1008 }
1009
1010 async fn accept_set_address(&mut self, addr: u8) {
1011 self.accept().await;
1012
1013 let regs = T::regs();
1014 trace!("setting addr: {}", addr);
1015 unsafe {
1016 regs.daddr().write(|w| {
1017 w.set_ef(true);
1018 w.set_add(addr);
1019 })
1020 }
1021 }
1020} 1022}
diff --git a/embassy-stm32/src/usb_otg.rs b/embassy-stm32/src/usb_otg.rs
deleted file mode 100644
index f7faf12a8..000000000
--- a/embassy-stm32/src/usb_otg.rs
+++ /dev/null
@@ -1,213 +0,0 @@
1use core::marker::PhantomData;
2
3use embassy_hal_common::into_ref;
4
5use crate::gpio::sealed::AFType;
6use crate::rcc::RccPeripheral;
7use crate::{peripherals, Peripheral};
8
9macro_rules! config_ulpi_pins {
10 ($($pin:ident),*) => {
11 into_ref!($($pin),*);
12 // NOTE(unsafe) Exclusive access to the registers
13 critical_section::with(|_| unsafe {
14 $(
15 $pin.set_as_af($pin.af_num(), AFType::OutputPushPull);
16 #[cfg(gpio_v2)]
17 $pin.set_speed(crate::gpio::Speed::VeryHigh);
18 )*
19 })
20 };
21}
22
23/// USB PHY type
24#[derive(Copy, Clone, Debug, Eq, PartialEq)]
25pub enum PhyType {
26 /// Internal Full-Speed PHY
27 ///
28 /// Available on most High-Speed peripherals.
29 InternalFullSpeed,
30 /// Internal High-Speed PHY
31 ///
32 /// Available on a few STM32 chips.
33 InternalHighSpeed,
34 /// External ULPI High-Speed PHY
35 ExternalHighSpeed,
36}
37
38pub struct UsbOtg<'d, T: Instance> {
39 phantom: PhantomData<&'d mut T>,
40 _phy_type: PhyType,
41}
42
43impl<'d, T: Instance> UsbOtg<'d, T> {
44 /// Initializes USB OTG peripheral with internal Full-Speed PHY
45 pub fn new_fs(
46 _peri: impl Peripheral<P = T> + 'd,
47 dp: impl Peripheral<P = impl DpPin<T>> + 'd,
48 dm: impl Peripheral<P = impl DmPin<T>> + 'd,
49 ) -> Self {
50 into_ref!(dp, dm);
51
52 unsafe {
53 dp.set_as_af(dp.af_num(), AFType::OutputPushPull);
54 dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
55 }
56
57 Self {
58 phantom: PhantomData,
59 _phy_type: PhyType::InternalFullSpeed,
60 }
61 }
62
63 /// Initializes USB OTG peripheral with external High-Speed PHY
64 pub fn new_hs_ulpi(
65 _peri: impl Peripheral<P = T> + 'd,
66 ulpi_clk: impl Peripheral<P = impl UlpiClkPin<T>> + 'd,
67 ulpi_dir: impl Peripheral<P = impl UlpiDirPin<T>> + 'd,
68 ulpi_nxt: impl Peripheral<P = impl UlpiNxtPin<T>> + 'd,
69 ulpi_stp: impl Peripheral<P = impl UlpiStpPin<T>> + 'd,
70 ulpi_d0: impl Peripheral<P = impl UlpiD0Pin<T>> + 'd,
71 ulpi_d1: impl Peripheral<P = impl UlpiD1Pin<T>> + 'd,
72 ulpi_d2: impl Peripheral<P = impl UlpiD2Pin<T>> + 'd,
73 ulpi_d3: impl Peripheral<P = impl UlpiD3Pin<T>> + 'd,
74 ulpi_d4: impl Peripheral<P = impl UlpiD4Pin<T>> + 'd,
75 ulpi_d5: impl Peripheral<P = impl UlpiD5Pin<T>> + 'd,
76 ulpi_d6: impl Peripheral<P = impl UlpiD6Pin<T>> + 'd,
77 ulpi_d7: impl Peripheral<P = impl UlpiD7Pin<T>> + 'd,
78 ) -> Self {
79 config_ulpi_pins!(
80 ulpi_clk, ulpi_dir, ulpi_nxt, ulpi_stp, ulpi_d0, ulpi_d1, ulpi_d2, ulpi_d3, ulpi_d4, ulpi_d5, ulpi_d6,
81 ulpi_d7
82 );
83
84 Self {
85 phantom: PhantomData,
86 _phy_type: PhyType::ExternalHighSpeed,
87 }
88 }
89}
90
91impl<'d, T: Instance> Drop for UsbOtg<'d, T> {
92 fn drop(&mut self) {
93 T::reset();
94 T::disable();
95 }
96}
97
98pub(crate) mod sealed {
99 pub trait Instance {
100 const REGISTERS: *const ();
101 const HIGH_SPEED: bool;
102 const FIFO_DEPTH_WORDS: usize;
103 const ENDPOINT_COUNT: usize;
104 }
105}
106
107pub trait Instance: sealed::Instance + RccPeripheral {}
108
109// Internal PHY pins
110pin_trait!(DpPin, Instance);
111pin_trait!(DmPin, Instance);
112
113// External PHY pins
114pin_trait!(UlpiClkPin, Instance);
115pin_trait!(UlpiDirPin, Instance);
116pin_trait!(UlpiNxtPin, Instance);
117pin_trait!(UlpiStpPin, Instance);
118pin_trait!(UlpiD0Pin, Instance);
119pin_trait!(UlpiD1Pin, Instance);
120pin_trait!(UlpiD2Pin, Instance);
121pin_trait!(UlpiD3Pin, Instance);
122pin_trait!(UlpiD4Pin, Instance);
123pin_trait!(UlpiD5Pin, Instance);
124pin_trait!(UlpiD6Pin, Instance);
125pin_trait!(UlpiD7Pin, Instance);
126
127foreach_peripheral!(
128 (otgfs, $inst:ident) => {
129 impl sealed::Instance for peripherals::$inst {
130 const REGISTERS: *const () = crate::pac::$inst.0 as *const ();
131 const HIGH_SPEED: bool = false;
132
133 cfg_if::cfg_if! {
134 if #[cfg(stm32f1)] {
135 const FIFO_DEPTH_WORDS: usize = 128;
136 const ENDPOINT_COUNT: usize = 8;
137 } else if #[cfg(any(
138 stm32f2,
139 stm32f401,
140 stm32f405,
141 stm32f407,
142 stm32f411,
143 stm32f415,
144 stm32f417,
145 stm32f427,
146 stm32f429,
147 stm32f437,
148 stm32f439,
149 ))] {
150 const FIFO_DEPTH_WORDS: usize = 320;
151 const ENDPOINT_COUNT: usize = 4;
152 } else if #[cfg(any(
153 stm32f412,
154 stm32f413,
155 stm32f423,
156 stm32f446,
157 stm32f469,
158 stm32f479,
159 stm32f7,
160 stm32l4,
161 stm32u5,
162 ))] {
163 const FIFO_DEPTH_WORDS: usize = 320;
164 const ENDPOINT_COUNT: usize = 6;
165 } else if #[cfg(stm32g0x1)] {
166 const FIFO_DEPTH_WORDS: usize = 512;
167 const ENDPOINT_COUNT: usize = 8;
168 } else {
169 compile_error!("USB_OTG_FS peripheral is not supported by this chip.");
170 }
171 }
172 }
173
174 impl Instance for peripherals::$inst {}
175 };
176
177 (otghs, $inst:ident) => {
178 impl sealed::Instance for peripherals::$inst {
179 const REGISTERS: *const () = crate::pac::$inst.0 as *const ();
180 const HIGH_SPEED: bool = true;
181
182 cfg_if::cfg_if! {
183 if #[cfg(any(
184 stm32f2,
185 stm32f405,
186 stm32f407,
187 stm32f415,
188 stm32f417,
189 stm32f427,
190 stm32f429,
191 stm32f437,
192 stm32f439,
193 ))] {
194 const FIFO_DEPTH_WORDS: usize = 1024;
195 const ENDPOINT_COUNT: usize = 6;
196 } else if #[cfg(any(
197 stm32f446,
198 stm32f469,
199 stm32f479,
200 stm32f7,
201 stm32h7,
202 ))] {
203 const FIFO_DEPTH_WORDS: usize = 1024;
204 const ENDPOINT_COUNT: usize = 9;
205 } else {
206 compile_error!("USB_OTG_HS peripheral is not supported by this chip.");
207 }
208 }
209 }
210
211 impl Instance for peripherals::$inst {}
212 };
213);
diff --git a/embassy-stm32/src/usb_otg/mod.rs b/embassy-stm32/src/usb_otg/mod.rs
new file mode 100644
index 000000000..84fef78cb
--- /dev/null
+++ b/embassy-stm32/src/usb_otg/mod.rs
@@ -0,0 +1,161 @@
1use embassy_cortex_m::interrupt::Interrupt;
2
3use crate::peripherals;
4use crate::rcc::RccPeripheral;
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
15pub(crate) mod sealed {
16 pub trait Instance {
17 const HIGH_SPEED: bool;
18 const FIFO_DEPTH_WORDS: u16;
19 const ENDPOINT_COUNT: usize;
20
21 fn regs() -> crate::pac::otg::Otg;
22 #[cfg(feature = "nightly")]
23 fn state() -> &'static super::State<{ super::MAX_EP_COUNT }>;
24 }
25}
26
27pub trait Instance: sealed::Instance + RccPeripheral {
28 type Interrupt: Interrupt;
29}
30
31// Internal PHY pins
32pin_trait!(DpPin, Instance);
33pin_trait!(DmPin, Instance);
34
35// External PHY pins
36pin_trait!(UlpiClkPin, Instance);
37pin_trait!(UlpiDirPin, Instance);
38pin_trait!(UlpiNxtPin, Instance);
39pin_trait!(UlpiStpPin, Instance);
40pin_trait!(UlpiD0Pin, Instance);
41pin_trait!(UlpiD1Pin, Instance);
42pin_trait!(UlpiD2Pin, Instance);
43pin_trait!(UlpiD3Pin, Instance);
44pin_trait!(UlpiD4Pin, Instance);
45pin_trait!(UlpiD5Pin, Instance);
46pin_trait!(UlpiD6Pin, Instance);
47pin_trait!(UlpiD7Pin, Instance);
48
49foreach_interrupt!(
50 (USB_OTG_FS, otg, $block:ident, GLOBAL, $irq:ident) => {
51 impl sealed::Instance for peripherals::USB_OTG_FS {
52 const HIGH_SPEED: bool = false;
53
54 cfg_if::cfg_if! {
55 if #[cfg(stm32f1)] {
56 const FIFO_DEPTH_WORDS: u16 = 128;
57 const ENDPOINT_COUNT: usize = 8;
58 } else if #[cfg(any(
59 stm32f2,
60 stm32f401,
61 stm32f405,
62 stm32f407,
63 stm32f411,
64 stm32f415,
65 stm32f417,
66 stm32f427,
67 stm32f429,
68 stm32f437,
69 stm32f439,
70 ))] {
71 const FIFO_DEPTH_WORDS: u16 = 320;
72 const ENDPOINT_COUNT: usize = 4;
73 } else if #[cfg(any(
74 stm32f412,
75 stm32f413,
76 stm32f423,
77 stm32f446,
78 stm32f469,
79 stm32f479,
80 stm32f7,
81 stm32l4,
82 stm32u5,
83 ))] {
84 const FIFO_DEPTH_WORDS: u16 = 320;
85 const ENDPOINT_COUNT: usize = 6;
86 } else if #[cfg(stm32g0x1)] {
87 const FIFO_DEPTH_WORDS: u16 = 512;
88 const ENDPOINT_COUNT: usize = 8;
89 } else if #[cfg(stm32h7)] {
90 const FIFO_DEPTH_WORDS: u16 = 1024;
91 const ENDPOINT_COUNT: usize = 9;
92 } else {
93 compile_error!("USB_OTG_FS peripheral is not supported by this chip.");
94 }
95 }
96
97 fn regs() -> crate::pac::otg::Otg {
98 crate::pac::USB_OTG_FS
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 }
106 }
107
108 impl Instance for peripherals::USB_OTG_FS {
109 type Interrupt = crate::interrupt::$irq;
110 }
111 };
112
113 (USB_OTG_HS, otg, $block:ident, GLOBAL, $irq:ident) => {
114 impl sealed::Instance for peripherals::USB_OTG_HS {
115 const HIGH_SPEED: bool = true;
116
117 cfg_if::cfg_if! {
118 if #[cfg(any(
119 stm32f2,
120 stm32f405,
121 stm32f407,
122 stm32f415,
123 stm32f417,
124 stm32f427,
125 stm32f429,
126 stm32f437,
127 stm32f439,
128 ))] {
129 const FIFO_DEPTH_WORDS: u16 = 1024;
130 const ENDPOINT_COUNT: usize = 6;
131 } else if #[cfg(any(
132 stm32f446,
133 stm32f469,
134 stm32f479,
135 stm32f7,
136 stm32h7,
137 ))] {
138 const FIFO_DEPTH_WORDS: u16 = 1024;
139 const ENDPOINT_COUNT: usize = 9;
140 } else {
141 compile_error!("USB_OTG_HS peripheral is not supported by this chip.");
142 }
143 }
144
145 fn regs() -> crate::pac::otg::Otg {
146 // OTG HS registers are a superset of FS registers
147 crate::pac::otg::Otg(crate::pac::USB_OTG_HS.0)
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 }
155 }
156
157 impl Instance for peripherals::USB_OTG_HS {
158 type Interrupt = crate::interrupt::$irq;
159 }
160 };
161);
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}
diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs
index d7238dc7d..64d647351 100644
--- a/embassy-usb-driver/src/lib.rs
+++ b/embassy-usb-driver/src/lib.rs
@@ -164,9 +164,6 @@ pub trait Bus {
164 164
165 async fn poll(&mut self) -> Event; 165 async fn poll(&mut self) -> Event;
166 166
167 /// Sets the device USB address to `addr`.
168 fn set_address(&mut self, addr: u8);
169
170 /// Enables or disables an endpoint. 167 /// Enables or disables an endpoint.
171 fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool); 168 fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool);
172 169
@@ -306,6 +303,12 @@ pub trait ControlPipe {
306 /// 303 ///
307 /// Sets a STALL condition on the pipe to indicate an error. 304 /// Sets a STALL condition on the pipe to indicate an error.
308 async fn reject(&mut self); 305 async fn reject(&mut self);
306
307 /// Accept SET_ADDRESS control and change bus address.
308 ///
309 /// For most drivers this function should firstly call `accept()` and then change the bus address.
310 /// However, there are peripherals (Synopsys USB OTG) that have reverse order.
311 async fn accept_set_address(&mut self, addr: u8);
309} 312}
310 313
311pub trait EndpointIn: Endpoint { 314pub trait EndpointIn: Endpoint {
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs
index 661b84119..096e8b07a 100644
--- a/embassy-usb/src/lib.rs
+++ b/embassy-usb/src/lib.rs
@@ -122,10 +122,9 @@ struct Inner<'d, D: Driver<'d>> {
122 122
123 /// Our device address, or 0 if none. 123 /// Our device address, or 0 if none.
124 address: u8, 124 address: u8,
125 /// When receiving a set addr control request, we have to apply it AFTER we've 125 /// SET_ADDRESS requests have special handling depending on the driver.
126 /// finished handling the control request, as the status stage still has to be 126 /// This flag indicates that requests must be handled by `ControlPipe::accept_set_address()`
127 /// handled with addr 0. 127 /// instead of regular `accept()`.
128 /// If true, do a set_addr after finishing the current control req.
129 set_address_pending: bool, 128 set_address_pending: bool,
130 129
131 interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>, 130 interfaces: Vec<Interface<'d>, MAX_INTERFACE_COUNT>,
@@ -254,11 +253,6 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
254 Direction::In => self.handle_control_in(req).await, 253 Direction::In => self.handle_control_in(req).await,
255 Direction::Out => self.handle_control_out(req).await, 254 Direction::Out => self.handle_control_out(req).await,
256 } 255 }
257
258 if self.inner.set_address_pending {
259 self.inner.bus.set_address(self.inner.address);
260 self.inner.set_address_pending = false;
261 }
262 } 256 }
263 257
264 async fn handle_control_in(&mut self, req: Request) { 258 async fn handle_control_in(&mut self, req: Request) {
@@ -328,7 +322,14 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
328 trace!(" control out data: {:02x?}", data); 322 trace!(" control out data: {:02x?}", data);
329 323
330 match self.inner.handle_control_out(req, data) { 324 match self.inner.handle_control_out(req, data) {
331 OutResponse::Accepted => self.control.accept().await, 325 OutResponse::Accepted => {
326 if self.inner.set_address_pending {
327 self.control.accept_set_address(self.inner.address).await;
328 self.inner.set_address_pending = false;
329 } else {
330 self.control.accept().await
331 }
332 }
332 OutResponse::Rejected => self.control.reject().await, 333 OutResponse::Rejected => self.control.reject().await,
333 } 334 }
334 } 335 }
@@ -655,7 +656,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
655 buf[1] = descriptor_type::STRING; 656 buf[1] = descriptor_type::STRING;
656 let mut pos = 2; 657 let mut pos = 2;
657 for c in s.encode_utf16() { 658 for c in s.encode_utf16() {
658 if pos >= buf.len() { 659 if pos + 2 >= buf.len() {
659 panic!("control buffer too small"); 660 panic!("control buffer too small");
660 } 661 }
661 662
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml
index 62d3f08df..252d60855 100644
--- a/examples/stm32f4/Cargo.toml
+++ b/examples/stm32f4/Cargo.toml
@@ -10,6 +10,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de
10embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 10embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
11embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } 11embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
12embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } 12embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
13embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
13 14
14defmt = "0.3" 15defmt = "0.3"
15defmt-rtt = "0.4" 16defmt-rtt = "0.4"
@@ -26,5 +27,5 @@ embedded-storage = "0.3.0"
26micromath = "2.0.0" 27micromath = "2.0.0"
27static_cell = "1.0" 28static_cell = "1.0"
28 29
29usb-device = "0.2" 30[profile.release]
30usbd-serial = "0.1.1" 31debug = 2
diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs
new file mode 100644
index 000000000..014647762
--- /dev/null
+++ b/examples/stm32f4/src/bin/usb_serial.rs
@@ -0,0 +1,106 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{panic, *};
6use embassy_executor::Spawner;
7use embassy_stm32::time::mhz;
8use embassy_stm32::usb_otg::{Driver, Instance};
9use embassy_stm32::{interrupt, Config};
10use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
11use embassy_usb::driver::EndpointError;
12use embassy_usb::Builder;
13use futures::future::join;
14use {defmt_rtt as _, panic_probe as _};
15
16#[embassy_executor::main]
17async fn main(_spawner: Spawner) {
18 info!("Hello World!");
19
20 let mut config = Config::default();
21 config.rcc.pll48 = true;
22 config.rcc.sys_ck = Some(mhz(48));
23
24 let p = embassy_stm32::init(config);
25
26 // Create the driver, from the HAL.
27 let irq = interrupt::take!(OTG_FS);
28 let mut ep_out_buffer = [0u8; 256];
29 let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer);
30
31 // Create embassy-usb Config
32 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
33 config.manufacturer = Some("Embassy");
34 config.product = Some("USB-serial example");
35 config.serial_number = Some("12345678");
36
37 // Required for windows compatiblity.
38 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
39 config.device_class = 0xEF;
40 config.device_sub_class = 0x02;
41 config.device_protocol = 0x01;
42 config.composite_with_iads = true;
43
44 // Create embassy-usb DeviceBuilder using the driver and config.
45 // It needs some buffers for building the descriptors.
46 let mut device_descriptor = [0; 256];
47 let mut config_descriptor = [0; 256];
48 let mut bos_descriptor = [0; 256];
49 let mut control_buf = [0; 64];
50
51 let mut state = State::new();
52
53 let mut builder = Builder::new(
54 driver,
55 config,
56 &mut device_descriptor,
57 &mut config_descriptor,
58 &mut bos_descriptor,
59 &mut control_buf,
60 None,
61 );
62
63 // Create classes on the builder.
64 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
65
66 // Build the builder.
67 let mut usb = builder.build();
68
69 // Run the USB device.
70 let usb_fut = usb.run();
71
72 // Do stuff with the class!
73 let echo_fut = async {
74 loop {
75 class.wait_connection().await;
76 info!("Connected");
77 let _ = echo(&mut class).await;
78 info!("Disconnected");
79 }
80 };
81
82 // Run everything concurrently.
83 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
84 join(usb_fut, echo_fut).await;
85}
86
87struct Disconnected {}
88
89impl From<EndpointError> for Disconnected {
90 fn from(val: EndpointError) -> Self {
91 match val {
92 EndpointError::BufferOverflow => panic!("Buffer overflow"),
93 EndpointError::Disabled => Disconnected {},
94 }
95 }
96}
97
98async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
99 let mut buf = [0; 64];
100 loop {
101 let n = class.read_packet(&mut buf).await?;
102 let data = &buf[..n];
103 info!("data: {:x}", data);
104 class.write_packet(data).await?;
105 }
106}
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml
index b80dbbf9c..ea4cbd808 100644
--- a/examples/stm32f7/Cargo.toml
+++ b/examples/stm32f7/Cargo.toml
@@ -11,6 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] }
12embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } 12embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
13embedded-io = { version = "0.4.0", features = ["async"] } 13embedded-io = { version = "0.4.0", features = ["async"] }
14embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
14 15
15defmt = "0.3" 16defmt = "0.3"
16defmt-rtt = "0.4" 17defmt-rtt = "0.4"
diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs
new file mode 100644
index 000000000..688bd0dab
--- /dev/null
+++ b/examples/stm32f7/src/bin/usb_serial.rs
@@ -0,0 +1,107 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{panic, *};
6use embassy_executor::Spawner;
7use embassy_stm32::time::mhz;
8use embassy_stm32::usb_otg::{Driver, Instance};
9use embassy_stm32::{interrupt, Config};
10use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
11use embassy_usb::driver::EndpointError;
12use embassy_usb::Builder;
13use futures::future::join;
14use {defmt_rtt as _, panic_probe as _};
15
16#[embassy_executor::main]
17async fn main(_spawner: Spawner) {
18 info!("Hello World!");
19
20 let mut config = Config::default();
21 config.rcc.hse = Some(mhz(8));
22 config.rcc.pll48 = true;
23 config.rcc.sys_ck = Some(mhz(200));
24
25 let p = embassy_stm32::init(config);
26
27 // Create the driver, from the HAL.
28 let irq = interrupt::take!(OTG_FS);
29 let mut ep_out_buffer = [0u8; 256];
30 let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer);
31
32 // Create embassy-usb Config
33 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
34 config.manufacturer = Some("Embassy");
35 config.product = Some("USB-serial example");
36 config.serial_number = Some("12345678");
37
38 // Required for windows compatiblity.
39 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
40 config.device_class = 0xEF;
41 config.device_sub_class = 0x02;
42 config.device_protocol = 0x01;
43 config.composite_with_iads = true;
44
45 // Create embassy-usb DeviceBuilder using the driver and config.
46 // It needs some buffers for building the descriptors.
47 let mut device_descriptor = [0; 256];
48 let mut config_descriptor = [0; 256];
49 let mut bos_descriptor = [0; 256];
50 let mut control_buf = [0; 64];
51
52 let mut state = State::new();
53
54 let mut builder = Builder::new(
55 driver,
56 config,
57 &mut device_descriptor,
58 &mut config_descriptor,
59 &mut bos_descriptor,
60 &mut control_buf,
61 None,
62 );
63
64 // Create classes on the builder.
65 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
66
67 // Build the builder.
68 let mut usb = builder.build();
69
70 // Run the USB device.
71 let usb_fut = usb.run();
72
73 // Do stuff with the class!
74 let echo_fut = async {
75 loop {
76 class.wait_connection().await;
77 info!("Connected");
78 let _ = echo(&mut class).await;
79 info!("Disconnected");
80 }
81 };
82
83 // Run everything concurrently.
84 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
85 join(usb_fut, echo_fut).await;
86}
87
88struct Disconnected {}
89
90impl From<EndpointError> for Disconnected {
91 fn from(val: EndpointError) -> Self {
92 match val {
93 EndpointError::BufferOverflow => panic!("Buffer overflow"),
94 EndpointError::Disabled => Disconnected {},
95 }
96 }
97}
98
99async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
100 let mut buf = [0; 64];
101 loop {
102 let n = class.read_packet(&mut buf).await?;
103 let data = &buf[..n];
104 info!("data: {:x}", data);
105 class.write_packet(data).await?;
106 }
107}
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml
index d30c42b1f..ff38440a7 100644
--- a/examples/stm32h7/Cargo.toml
+++ b/examples/stm32h7/Cargo.toml
@@ -11,6 +11,7 @@ embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["de
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] }
12embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits"] } 12embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits"] }
13embedded-io = { version = "0.4.0", features = ["async"] } 13embedded-io = { version = "0.4.0", features = ["async"] }
14embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
14 15
15defmt = "0.3" 16defmt = "0.3"
16defmt-rtt = "0.4" 17defmt-rtt = "0.4"
diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs
new file mode 100644
index 000000000..b319d12c3
--- /dev/null
+++ b/examples/stm32h7/src/bin/usb_serial.rs
@@ -0,0 +1,106 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{panic, *};
6use embassy_executor::Spawner;
7use embassy_stm32::time::mhz;
8use embassy_stm32::usb_otg::{Driver, Instance};
9use embassy_stm32::{interrupt, Config};
10use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
11use embassy_usb::driver::EndpointError;
12use embassy_usb::Builder;
13use futures::future::join;
14use {defmt_rtt as _, panic_probe as _};
15
16#[embassy_executor::main]
17async fn main(_spawner: Spawner) {
18 info!("Hello World!");
19
20 let mut config = Config::default();
21 config.rcc.sys_ck = Some(mhz(400));
22 config.rcc.hclk = Some(mhz(200));
23 config.rcc.pll1.q_ck = Some(mhz(100));
24 let p = embassy_stm32::init(config);
25
26 // Create the driver, from the HAL.
27 let irq = interrupt::take!(OTG_FS);
28 let mut ep_out_buffer = [0u8; 256];
29 let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer);
30
31 // Create embassy-usb Config
32 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
33 config.manufacturer = Some("Embassy");
34 config.product = Some("USB-serial example");
35 config.serial_number = Some("12345678");
36
37 // Required for windows compatiblity.
38 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
39 config.device_class = 0xEF;
40 config.device_sub_class = 0x02;
41 config.device_protocol = 0x01;
42 config.composite_with_iads = true;
43
44 // Create embassy-usb DeviceBuilder using the driver and config.
45 // It needs some buffers for building the descriptors.
46 let mut device_descriptor = [0; 256];
47 let mut config_descriptor = [0; 256];
48 let mut bos_descriptor = [0; 256];
49 let mut control_buf = [0; 64];
50
51 let mut state = State::new();
52
53 let mut builder = Builder::new(
54 driver,
55 config,
56 &mut device_descriptor,
57 &mut config_descriptor,
58 &mut bos_descriptor,
59 &mut control_buf,
60 None,
61 );
62
63 // Create classes on the builder.
64 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
65
66 // Build the builder.
67 let mut usb = builder.build();
68
69 // Run the USB device.
70 let usb_fut = usb.run();
71
72 // Do stuff with the class!
73 let echo_fut = async {
74 loop {
75 class.wait_connection().await;
76 info!("Connected");
77 let _ = echo(&mut class).await;
78 info!("Disconnected");
79 }
80 };
81
82 // Run everything concurrently.
83 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
84 join(usb_fut, echo_fut).await;
85}
86
87struct Disconnected {}
88
89impl From<EndpointError> for Disconnected {
90 fn from(val: EndpointError) -> Self {
91 match val {
92 EndpointError::BufferOverflow => panic!("Buffer overflow"),
93 EndpointError::Disabled => Disconnected {},
94 }
95 }
96}
97
98async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
99 let mut buf = [0; 64];
100 loop {
101 let n = class.read_packet(&mut buf).await?;
102 let data = &buf[..n];
103 info!("data: {:x}", data);
104 class.write_packet(data).await?;
105 }
106}
diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml
index 45d3dd366..5627760ef 100644
--- a/examples/stm32l4/Cargo.toml
+++ b/examples/stm32l4/Cargo.toml
@@ -12,6 +12,7 @@ embassy-executor = { version = "0.1.0", path = "../../embassy-executor", feature
12embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" } 13embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
14embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] } 14embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] }
15embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
15 16
16defmt = "0.3" 17defmt = "0.3"
17defmt-rtt = "0.4" 18defmt-rtt = "0.4"
@@ -26,6 +27,3 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa
26heapless = { version = "0.7.5", default-features = false } 27heapless = { version = "0.7.5", default-features = false }
27 28
28micromath = "2.0.0" 29micromath = "2.0.0"
29usb-device = "0.2"
30usbd-serial = "0.1.1"
31
diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs
new file mode 100644
index 000000000..3e38b10a3
--- /dev/null
+++ b/examples/stm32l4/src/bin/usb_serial.rs
@@ -0,0 +1,108 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{panic, *};
6use defmt_rtt as _; // global logger
7use embassy_executor::Spawner;
8use embassy_stm32::rcc::*;
9use embassy_stm32::usb_otg::{Driver, Instance};
10use embassy_stm32::{interrupt, Config};
11use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
12use embassy_usb::driver::EndpointError;
13use embassy_usb::Builder;
14use futures::future::join;
15use panic_probe as _;
16
17#[embassy_executor::main]
18async fn main(_spawner: Spawner) {
19 info!("Hello World!");
20
21 let mut config = Config::default();
22 config.rcc.mux = ClockSrc::PLL(PLLSource::HSI16, PLLClkDiv::Div2, PLLSrcDiv::Div1, PLLMul::Mul10, None);
23 config.rcc.hsi48 = true;
24
25 let p = embassy_stm32::init(config);
26
27 // Create the driver, from the HAL.
28 let irq = interrupt::take!(OTG_FS);
29 let mut ep_out_buffer = [0u8; 256];
30 let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer);
31
32 // Create embassy-usb Config
33 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
34 config.max_packet_size_0 = 64;
35 config.manufacturer = Some("Embassy");
36 config.product = Some("USB-serial example");
37 config.serial_number = Some("12345678");
38
39 // Required for windows compatiblity.
40 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
41 config.device_class = 0xEF;
42 config.device_sub_class = 0x02;
43 config.device_protocol = 0x01;
44 config.composite_with_iads = true;
45
46 // Create embassy-usb DeviceBuilder using the driver and config.
47 // It needs some buffers for building the descriptors.
48 let mut device_descriptor = [0; 256];
49 let mut config_descriptor = [0; 256];
50 let mut bos_descriptor = [0; 256];
51 let mut control_buf = [0; 64];
52
53 let mut state = State::new();
54
55 let mut builder = Builder::new(
56 driver,
57 config,
58 &mut device_descriptor,
59 &mut config_descriptor,
60 &mut bos_descriptor,
61 &mut control_buf,
62 None,
63 );
64
65 // Create classes on the builder.
66 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
67
68 // Build the builder.
69 let mut usb = builder.build();
70
71 // Run the USB device.
72 let usb_fut = usb.run();
73
74 // Do stuff with the class!
75 let echo_fut = async {
76 loop {
77 class.wait_connection().await;
78 info!("Connected");
79 let _ = echo(&mut class).await;
80 info!("Disconnected");
81 }
82 };
83
84 // Run everything concurrently.
85 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
86 join(usb_fut, echo_fut).await;
87}
88
89struct Disconnected {}
90
91impl From<EndpointError> for Disconnected {
92 fn from(val: EndpointError) -> Self {
93 match val {
94 EndpointError::BufferOverflow => panic!("Buffer overflow"),
95 EndpointError::Disabled => Disconnected {},
96 }
97 }
98}
99
100async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
101 let mut buf = [0; 64];
102 loop {
103 let n = class.read_packet(&mut buf).await?;
104 let data = &buf[..n];
105 info!("data: {:x}", data);
106 class.write_packet(data).await?;
107 }
108}
diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml
index d88fdda50..2b02eda92 100644
--- a/examples/stm32u5/Cargo.toml
+++ b/examples/stm32u5/Cargo.toml
@@ -9,6 +9,7 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] }
12embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
12 13
13defmt = "0.3" 14defmt = "0.3"
14defmt-rtt = "0.4" 15defmt-rtt = "0.4"
diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs
new file mode 100644
index 000000000..c846836b0
--- /dev/null
+++ b/examples/stm32u5/src/bin/usb_serial.rs
@@ -0,0 +1,108 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5use defmt::{panic, *};
6use defmt_rtt as _; // global logger
7use embassy_executor::Spawner;
8use embassy_stm32::rcc::*;
9use embassy_stm32::usb_otg::{Driver, Instance};
10use embassy_stm32::{interrupt, Config};
11use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
12use embassy_usb::driver::EndpointError;
13use embassy_usb::Builder;
14use futures::future::join;
15use panic_probe as _;
16
17#[embassy_executor::main]
18async fn main(_spawner: Spawner) {
19 info!("Hello World!");
20
21 let mut config = Config::default();
22 config.rcc.mux = ClockSrc::PLL1R(PllSrc::HSI16, PllM::Div2, PllN::Mul10, PllClkDiv::NotDivided);
23 //config.rcc.mux = ClockSrc::MSI(MSIRange::Range48mhz);
24 config.rcc.hsi48 = true;
25
26 let p = embassy_stm32::init(config);
27
28 // Create the driver, from the HAL.
29 let irq = interrupt::take!(OTG_FS);
30 let mut ep_out_buffer = [0u8; 256];
31 let driver = Driver::new_fs(p.USB_OTG_FS, irq, p.PA12, p.PA11, &mut ep_out_buffer);
32
33 // Create embassy-usb Config
34 let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
35 config.manufacturer = Some("Embassy");
36 config.product = Some("USB-serial example");
37 config.serial_number = Some("12345678");
38
39 // Required for windows compatiblity.
40 // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
41 config.device_class = 0xEF;
42 config.device_sub_class = 0x02;
43 config.device_protocol = 0x01;
44 config.composite_with_iads = true;
45
46 // Create embassy-usb DeviceBuilder using the driver and config.
47 // It needs some buffers for building the descriptors.
48 let mut device_descriptor = [0; 256];
49 let mut config_descriptor = [0; 256];
50 let mut bos_descriptor = [0; 256];
51 let mut control_buf = [0; 64];
52
53 let mut state = State::new();
54
55 let mut builder = Builder::new(
56 driver,
57 config,
58 &mut device_descriptor,
59 &mut config_descriptor,
60 &mut bos_descriptor,
61 &mut control_buf,
62 None,
63 );
64
65 // Create classes on the builder.
66 let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
67
68 // Build the builder.
69 let mut usb = builder.build();
70
71 // Run the USB device.
72 let usb_fut = usb.run();
73
74 // Do stuff with the class!
75 let echo_fut = async {
76 loop {
77 class.wait_connection().await;
78 info!("Connected");
79 let _ = echo(&mut class).await;
80 info!("Disconnected");
81 }
82 };
83
84 // Run everything concurrently.
85 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
86 join(usb_fut, echo_fut).await;
87}
88
89struct Disconnected {}
90
91impl From<EndpointError> for Disconnected {
92 fn from(val: EndpointError) -> Self {
93 match val {
94 EndpointError::BufferOverflow => panic!("Buffer overflow"),
95 EndpointError::Disabled => Disconnected {},
96 }
97 }
98}
99
100async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
101 let mut buf = [0; 64];
102 loop {
103 let n = class.read_packet(&mut buf).await?;
104 let data = &buf[..n];
105 info!("data: {:x}", data);
106 class.write_packet(data).await?;
107 }
108}
diff --git a/stm32-data b/stm32-data
Subproject 14a448c318192fe9da1c95a4de1beb4ec4892f1 Subproject 844793fc3da2ba3f12ab6a69b78cd8e6fb5497b