diff options
| -rw-r--r-- | embassy-stm32/Cargo.toml | 5 | ||||
| -rw-r--r-- | embassy-stm32/build.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/lib.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/usb/mod.rs | 36 | ||||
| -rw-r--r-- | embassy-stm32/src/usb/usb.rs | 1064 | ||||
| -rw-r--r-- | examples/stm32f1/Cargo.toml | 2 | ||||
| -rw-r--r-- | examples/stm32f1/src/bin/usb_serial.rs | 117 | ||||
| -rw-r--r-- | examples/stm32f3/.cargo/config.toml | 2 | ||||
| -rw-r--r-- | examples/stm32f3/Cargo.toml | 5 | ||||
| -rw-r--r-- | examples/stm32f3/src/bin/usb_serial.rs | 116 | ||||
| -rw-r--r-- | examples/stm32l5/.cargo/config.toml | 6 | ||||
| -rw-r--r-- | examples/stm32l5/Cargo.toml | 30 | ||||
| -rw-r--r-- | examples/stm32l5/build.rs | 5 | ||||
| -rw-r--r-- | examples/stm32l5/src/bin/button_exti.rs | 28 | ||||
| -rw-r--r-- | examples/stm32l5/src/bin/rng.rs | 34 | ||||
| -rw-r--r-- | examples/stm32l5/src/bin/usb_ethernet.rs | 290 | ||||
| -rw-r--r-- | examples/stm32l5/src/bin/usb_hid_mouse.rs | 136 | ||||
| -rw-r--r-- | examples/stm32l5/src/bin/usb_serial.rs | 112 | ||||
| m--------- | stm32-data | 0 |
19 files changed, 1988 insertions, 4 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 32311cc75..01a96a5fb 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -37,6 +37,7 @@ embassy = { version = "0.1.0", path = "../embassy" } | |||
| 37 | embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["stm32"] } | 37 | embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["stm32"] } |
| 38 | embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } | 38 | embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } |
| 39 | embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } | 39 | embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } |
| 40 | embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional = true } | ||
| 40 | 41 | ||
| 41 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | 42 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } |
| 42 | embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} | 43 | embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true} |
| @@ -71,7 +72,7 @@ quote = "1.0.15" | |||
| 71 | stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]} | 72 | stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]} |
| 72 | 73 | ||
| 73 | [features] | 74 | [features] |
| 74 | defmt = ["dep:defmt", "embassy/defmt", "embedded-io?/defmt" ] | 75 | defmt = ["dep:defmt", "embassy/defmt", "embedded-io?/defmt", "embassy-usb?/defmt"] |
| 75 | sdmmc-rs = ["embedded-sdmmc"] | 76 | sdmmc-rs = ["embedded-sdmmc"] |
| 76 | net = ["embassy-net" ] | 77 | net = ["embassy-net" ] |
| 77 | memory-x = ["stm32-metapac/memory-x"] | 78 | memory-x = ["stm32-metapac/memory-x"] |
| @@ -90,7 +91,7 @@ time-driver-tim12 = ["_time-driver"] | |||
| 90 | time-driver-tim15 = ["_time-driver"] | 91 | time-driver-tim15 = ["_time-driver"] |
| 91 | 92 | ||
| 92 | # Enable nightly-only features | 93 | # Enable nightly-only features |
| 93 | nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io"] | 94 | nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb"] |
| 94 | 95 | ||
| 95 | # Reexport stm32-metapac at `embassy_stm32::pac`. | 96 | # Reexport stm32-metapac at `embassy_stm32::pac`. |
| 96 | # This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. | 97 | # This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. |
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 490f2d8f2..7b1376f0b 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs | |||
| @@ -279,6 +279,8 @@ fn main() { | |||
| 279 | (("dcmi", "HSYNC"), quote!(crate::dcmi::HSyncPin)), | 279 | (("dcmi", "HSYNC"), quote!(crate::dcmi::HSyncPin)), |
| 280 | (("dcmi", "VSYNC"), quote!(crate::dcmi::VSyncPin)), | 280 | (("dcmi", "VSYNC"), quote!(crate::dcmi::VSyncPin)), |
| 281 | (("dcmi", "PIXCLK"), quote!(crate::dcmi::PixClkPin)), | 281 | (("dcmi", "PIXCLK"), quote!(crate::dcmi::PixClkPin)), |
| 282 | (("usb", "DP"), quote!(crate::usb::DpPin)), | ||
| 283 | (("usb", "DM"), quote!(crate::usb::DmPin)), | ||
| 282 | (("otgfs", "DP"), quote!(crate::usb_otg::DpPin)), | 284 | (("otgfs", "DP"), quote!(crate::usb_otg::DpPin)), |
| 283 | (("otgfs", "DM"), quote!(crate::usb_otg::DmPin)), | 285 | (("otgfs", "DM"), quote!(crate::usb_otg::DmPin)), |
| 284 | (("otghs", "DP"), quote!(crate::usb_otg::DpPin)), | 286 | (("otghs", "DP"), quote!(crate::usb_otg::DpPin)), |
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index fbd2008c8..bb70faab1 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs | |||
| @@ -63,6 +63,8 @@ pub mod sdmmc; | |||
| 63 | pub mod spi; | 63 | pub mod spi; |
| 64 | #[cfg(usart)] | 64 | #[cfg(usart)] |
| 65 | pub mod usart; | 65 | pub mod usart; |
| 66 | #[cfg(usb)] | ||
| 67 | pub mod usb; | ||
| 66 | #[cfg(any(otgfs, otghs))] | 68 | #[cfg(any(otgfs, otghs))] |
| 67 | pub mod usb_otg; | 69 | pub mod usb_otg; |
| 68 | 70 | ||
diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs new file mode 100644 index 000000000..71b407cbd --- /dev/null +++ b/embassy-stm32/src/usb/mod.rs | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | use embassy::interrupt::Interrupt; | ||
| 2 | |||
| 3 | use crate::rcc::RccPeripheral; | ||
| 4 | |||
| 5 | #[cfg(feature = "nightly")] | ||
| 6 | mod usb; | ||
| 7 | #[cfg(feature = "nightly")] | ||
| 8 | pub use usb::*; | ||
| 9 | |||
| 10 | pub(crate) mod sealed { | ||
| 11 | pub trait Instance { | ||
| 12 | fn regs() -> crate::pac::usb::Usb; | ||
| 13 | } | ||
| 14 | } | ||
| 15 | |||
| 16 | pub trait Instance: sealed::Instance + RccPeripheral + 'static { | ||
| 17 | type Interrupt: Interrupt; | ||
| 18 | } | ||
| 19 | |||
| 20 | // Internal PHY pins | ||
| 21 | pin_trait!(DpPin, Instance); | ||
| 22 | pin_trait!(DmPin, Instance); | ||
| 23 | |||
| 24 | foreach_interrupt!( | ||
| 25 | ($inst:ident, usb, $block:ident, LP, $irq:ident) => { | ||
| 26 | impl sealed::Instance for crate::peripherals::$inst { | ||
| 27 | fn regs() -> crate::pac::usb::Usb { | ||
| 28 | crate::pac::$inst | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | impl Instance for crate::peripherals::$inst { | ||
| 33 | type Interrupt = crate::interrupt::$irq; | ||
| 34 | } | ||
| 35 | }; | ||
| 36 | ); | ||
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs new file mode 100644 index 000000000..113b20262 --- /dev/null +++ b/embassy-stm32/src/usb/usb.rs | |||
| @@ -0,0 +1,1064 @@ | |||
| 1 | #![macro_use] | ||
| 2 | |||
| 3 | use atomic_polyfill::{AtomicBool, AtomicU8}; | ||
| 4 | use core::marker::PhantomData; | ||
| 5 | use core::sync::atomic::Ordering; | ||
| 6 | use core::task::Poll; | ||
| 7 | use embassy::interrupt::InterruptExt; | ||
| 8 | use embassy::time::{block_for, Duration}; | ||
| 9 | use embassy::util::Unborrow; | ||
| 10 | use embassy::waitqueue::AtomicWaker; | ||
| 11 | use embassy_hal_common::unborrow; | ||
| 12 | use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported}; | ||
| 13 | use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; | ||
| 14 | use futures::future::poll_fn; | ||
| 15 | use futures::Future; | ||
| 16 | use pac::common::{Reg, RW}; | ||
| 17 | use pac::usb::vals::{EpType, Stat}; | ||
| 18 | |||
| 19 | use crate::gpio::sealed::AFType; | ||
| 20 | use crate::pac; | ||
| 21 | use crate::pac::usb::regs; | ||
| 22 | use crate::rcc::sealed::RccPeripheral; | ||
| 23 | |||
| 24 | use super::{DmPin, DpPin, Instance}; | ||
| 25 | |||
| 26 | const EP_COUNT: usize = 8; | ||
| 27 | |||
| 28 | #[cfg(any(usb_v1_x1, usb_v1_x2))] | ||
| 29 | const EP_MEMORY_SIZE: usize = 512; | ||
| 30 | #[cfg(not(any(usb_v1_x1, usb_v1_x2)))] | ||
| 31 | const EP_MEMORY_SIZE: usize = 1024; | ||
| 32 | |||
| 33 | const NEW_AW: AtomicWaker = AtomicWaker::new(); | ||
| 34 | static BUS_WAKER: AtomicWaker = NEW_AW; | ||
| 35 | static EP0_SETUP: AtomicBool = AtomicBool::new(false); | ||
| 36 | static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; | ||
| 37 | static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; | ||
| 38 | static IRQ_FLAGS: AtomicU8 = AtomicU8::new(0); | ||
| 39 | const IRQ_FLAG_RESET: u8 = 0x01; | ||
| 40 | const IRQ_FLAG_SUSPEND: u8 = 0x02; | ||
| 41 | const IRQ_FLAG_RESUME: u8 = 0x04; | ||
| 42 | |||
| 43 | fn convert_type(t: EndpointType) -> EpType { | ||
| 44 | match t { | ||
| 45 | EndpointType::Bulk => EpType::BULK, | ||
| 46 | EndpointType::Control => EpType::CONTROL, | ||
| 47 | EndpointType::Interrupt => EpType::INTERRUPT, | ||
| 48 | EndpointType::Isochronous => EpType::ISO, | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | fn invariant(mut r: regs::Epr) -> regs::Epr { | ||
| 53 | r.set_ctr_rx(true); // don't clear | ||
| 54 | r.set_ctr_tx(true); // don't clear | ||
| 55 | r.set_dtog_rx(false); // don't toggle | ||
| 56 | r.set_dtog_tx(false); // don't toggle | ||
| 57 | r.set_stat_rx(Stat(0)); | ||
| 58 | r.set_stat_tx(Stat(0)); | ||
| 59 | r | ||
| 60 | } | ||
| 61 | |||
| 62 | // Returns (actual_len, len_bits) | ||
| 63 | fn calc_out_len(len: u16) -> (u16, u16) { | ||
| 64 | match len { | ||
| 65 | 2..=62 => ((len + 1) / 2 * 2, ((len + 1) / 2) << 10), | ||
| 66 | 63..=480 => ((len + 31) / 32 * 32, (((len + 31) / 32 - 1) << 10) | 0x8000), | ||
| 67 | _ => panic!("invalid OUT length {}", len), | ||
| 68 | } | ||
| 69 | } | ||
| 70 | fn ep_in_addr<T: Instance>(index: usize) -> Reg<u16, RW> { | ||
| 71 | T::regs().ep_mem(index * 4 + 0) | ||
| 72 | } | ||
| 73 | fn ep_in_len<T: Instance>(index: usize) -> Reg<u16, RW> { | ||
| 74 | T::regs().ep_mem(index * 4 + 1) | ||
| 75 | } | ||
| 76 | fn ep_out_addr<T: Instance>(index: usize) -> Reg<u16, RW> { | ||
| 77 | T::regs().ep_mem(index * 4 + 2) | ||
| 78 | } | ||
| 79 | fn ep_out_len<T: Instance>(index: usize) -> Reg<u16, RW> { | ||
| 80 | T::regs().ep_mem(index * 4 + 3) | ||
| 81 | } | ||
| 82 | |||
| 83 | struct EndpointBuffer<T: Instance> { | ||
| 84 | addr: u16, | ||
| 85 | len: u16, | ||
| 86 | _phantom: PhantomData<T>, | ||
| 87 | } | ||
| 88 | |||
| 89 | impl<T: Instance> EndpointBuffer<T> { | ||
| 90 | fn read(&mut self, buf: &mut [u8]) { | ||
| 91 | assert!(buf.len() <= self.len as usize); | ||
| 92 | for i in 0..((buf.len() + 1) / 2) { | ||
| 93 | let val = unsafe { T::regs().ep_mem(self.addr as usize / 2 + i).read() }; | ||
| 94 | buf[i * 2] = val as u8; | ||
| 95 | if i * 2 + 1 < buf.len() { | ||
| 96 | buf[i * 2 + 1] = (val >> 8) as u8; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | fn write(&mut self, buf: &[u8]) { | ||
| 102 | assert!(buf.len() <= self.len as usize); | ||
| 103 | for i in 0..((buf.len() + 1) / 2) { | ||
| 104 | let mut val = buf[i * 2] as u16; | ||
| 105 | if i * 2 + 1 < buf.len() { | ||
| 106 | val |= (buf[i * 2 + 1] as u16) << 8; | ||
| 107 | } | ||
| 108 | unsafe { | ||
| 109 | T::regs() | ||
| 110 | .ep_mem(self.addr as usize / 2 + i) | ||
| 111 | .write_value(val) | ||
| 112 | }; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | #[derive(Debug, Clone, Copy)] | ||
| 118 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 119 | struct EndpointData { | ||
| 120 | ep_type: EndpointType, // only valid if used_in || used_out | ||
| 121 | used_in: bool, | ||
| 122 | used_out: bool, | ||
| 123 | } | ||
| 124 | |||
| 125 | pub struct Driver<'d, T: Instance> { | ||
| 126 | phantom: PhantomData<&'d mut T>, | ||
| 127 | alloc: [EndpointData; EP_COUNT], | ||
| 128 | ep_mem_free: u16, // first free address in EP mem, in bytes. | ||
| 129 | } | ||
| 130 | |||
| 131 | impl<'d, T: Instance> Driver<'d, T> { | ||
| 132 | pub fn new( | ||
| 133 | _usb: impl Unborrow<Target = T> + 'd, | ||
| 134 | irq: impl Unborrow<Target = T::Interrupt> + 'd, | ||
| 135 | dp: impl Unborrow<Target = impl DpPin<T>> + 'd, | ||
| 136 | dm: impl Unborrow<Target = impl DmPin<T>> + 'd, | ||
| 137 | ) -> Self { | ||
| 138 | unborrow!(irq, dp, dm); | ||
| 139 | irq.set_handler(Self::on_interrupt); | ||
| 140 | irq.unpend(); | ||
| 141 | irq.enable(); | ||
| 142 | |||
| 143 | let regs = T::regs(); | ||
| 144 | |||
| 145 | #[cfg(stm32l5)] | ||
| 146 | unsafe { | ||
| 147 | crate::peripherals::PWR::enable(); | ||
| 148 | |||
| 149 | pac::PWR | ||
| 150 | .cr2() | ||
| 151 | .modify(|w| w.set_usv(pac::pwr::vals::Usv::VALID)); | ||
| 152 | } | ||
| 153 | |||
| 154 | unsafe { | ||
| 155 | <T as RccPeripheral>::enable(); | ||
| 156 | <T as RccPeripheral>::reset(); | ||
| 157 | |||
| 158 | regs.cntr().write(|w| { | ||
| 159 | w.set_pdwn(false); | ||
| 160 | w.set_fres(true); | ||
| 161 | }); | ||
| 162 | |||
| 163 | block_for(Duration::from_millis(100)); | ||
| 164 | |||
| 165 | regs.btable().write(|w| w.set_btable(0)); | ||
| 166 | |||
| 167 | dp.set_as_af(dp.af_num(), AFType::OutputPushPull); | ||
| 168 | dm.set_as_af(dm.af_num(), AFType::OutputPushPull); | ||
| 169 | } | ||
| 170 | |||
| 171 | Self { | ||
| 172 | phantom: PhantomData, | ||
| 173 | alloc: [EndpointData { | ||
| 174 | ep_type: EndpointType::Bulk, | ||
| 175 | used_in: false, | ||
| 176 | used_out: false, | ||
| 177 | }; EP_COUNT], | ||
| 178 | ep_mem_free: EP_COUNT as u16 * 8, // for each EP, 4 regs, so 8 bytes | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | fn on_interrupt(_: *mut ()) { | ||
| 183 | unsafe { | ||
| 184 | let regs = T::regs(); | ||
| 185 | //let x = regs.istr().read().0; | ||
| 186 | //trace!("USB IRQ: {:08x}", x); | ||
| 187 | |||
| 188 | let istr = regs.istr().read(); | ||
| 189 | |||
| 190 | let mut flags: u8 = 0; | ||
| 191 | |||
| 192 | if istr.susp() { | ||
| 193 | //trace!("USB IRQ: susp"); | ||
| 194 | flags |= IRQ_FLAG_SUSPEND; | ||
| 195 | regs.cntr().modify(|w| { | ||
| 196 | w.set_fsusp(true); | ||
| 197 | w.set_lpmode(true); | ||
| 198 | }) | ||
| 199 | } | ||
| 200 | |||
| 201 | if istr.wkup() { | ||
| 202 | //trace!("USB IRQ: wkup"); | ||
| 203 | flags |= IRQ_FLAG_RESUME; | ||
| 204 | regs.cntr().modify(|w| { | ||
| 205 | w.set_fsusp(false); | ||
| 206 | w.set_lpmode(false); | ||
| 207 | }) | ||
| 208 | } | ||
| 209 | |||
| 210 | if istr.reset() { | ||
| 211 | //trace!("USB IRQ: reset"); | ||
| 212 | flags |= IRQ_FLAG_RESET; | ||
| 213 | |||
| 214 | // Write 0 to clear. | ||
| 215 | let mut clear = regs::Istr(!0); | ||
| 216 | clear.set_reset(false); | ||
| 217 | regs.istr().write_value(clear); | ||
| 218 | } | ||
| 219 | |||
| 220 | if flags != 0 { | ||
| 221 | // Send irqs to main thread. | ||
| 222 | IRQ_FLAGS.fetch_or(flags, Ordering::AcqRel); | ||
| 223 | BUS_WAKER.wake(); | ||
| 224 | |||
| 225 | // Clear them | ||
| 226 | let mut mask = regs::Istr(0); | ||
| 227 | mask.set_wkup(true); | ||
| 228 | mask.set_susp(true); | ||
| 229 | mask.set_reset(true); | ||
| 230 | regs.istr().write_value(regs::Istr(!(istr.0 & mask.0))); | ||
| 231 | } | ||
| 232 | |||
| 233 | if istr.ctr() { | ||
| 234 | let index = istr.ep_id() as usize; | ||
| 235 | let mut epr = regs.epr(index).read(); | ||
| 236 | if epr.ctr_rx() { | ||
| 237 | if index == 0 && epr.setup() { | ||
| 238 | EP0_SETUP.store(true, Ordering::Relaxed); | ||
| 239 | } | ||
| 240 | //trace!("EP {} RX, setup={}", index, epr.setup()); | ||
| 241 | EP_OUT_WAKERS[index].wake(); | ||
| 242 | } | ||
| 243 | if epr.ctr_tx() { | ||
| 244 | //trace!("EP {} TX", index); | ||
| 245 | EP_IN_WAKERS[index].wake(); | ||
| 246 | } | ||
| 247 | epr.set_dtog_rx(false); | ||
| 248 | epr.set_dtog_tx(false); | ||
| 249 | epr.set_stat_rx(Stat(0)); | ||
| 250 | epr.set_stat_tx(Stat(0)); | ||
| 251 | epr.set_ctr_rx(!epr.ctr_rx()); | ||
| 252 | epr.set_ctr_tx(!epr.ctr_tx()); | ||
| 253 | regs.epr(index).write_value(epr); | ||
| 254 | } | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | fn alloc_ep_mem(&mut self, len: u16) -> u16 { | ||
| 259 | let addr = self.ep_mem_free; | ||
| 260 | if addr + len > EP_MEMORY_SIZE as _ { | ||
| 261 | panic!("Endpoint memory full"); | ||
| 262 | } | ||
| 263 | self.ep_mem_free += len; | ||
| 264 | addr | ||
| 265 | } | ||
| 266 | |||
| 267 | fn alloc_endpoint<D: Dir>( | ||
| 268 | &mut self, | ||
| 269 | ep_type: EndpointType, | ||
| 270 | max_packet_size: u16, | ||
| 271 | interval: u8, | ||
| 272 | ) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> { | ||
| 273 | trace!( | ||
| 274 | "allocating type={:?} mps={:?} interval={}, dir={:?}", | ||
| 275 | ep_type, | ||
| 276 | max_packet_size, | ||
| 277 | interval, | ||
| 278 | D::dir() | ||
| 279 | ); | ||
| 280 | |||
| 281 | let index = self.alloc.iter_mut().enumerate().find(|(i, ep)| { | ||
| 282 | if *i == 0 && ep_type != EndpointType::Control { | ||
| 283 | return false; // reserved for control pipe | ||
| 284 | } | ||
| 285 | let used = ep.used_out || ep.used_in; | ||
| 286 | let used_dir = match D::dir() { | ||
| 287 | UsbDirection::Out => ep.used_out, | ||
| 288 | UsbDirection::In => ep.used_in, | ||
| 289 | }; | ||
| 290 | !used || (ep.ep_type == ep_type && !used_dir) | ||
| 291 | }); | ||
| 292 | |||
| 293 | let (index, ep) = match index { | ||
| 294 | Some(x) => x, | ||
| 295 | None => return Err(EndpointAllocError), | ||
| 296 | }; | ||
| 297 | |||
| 298 | ep.ep_type = ep_type; | ||
| 299 | |||
| 300 | let buf = match D::dir() { | ||
| 301 | UsbDirection::Out => { | ||
| 302 | assert!(!ep.used_out); | ||
| 303 | ep.used_out = true; | ||
| 304 | |||
| 305 | let (len, len_bits) = calc_out_len(max_packet_size); | ||
| 306 | let addr = self.alloc_ep_mem(len); | ||
| 307 | |||
| 308 | trace!(" len_bits = {:04x}", len_bits); | ||
| 309 | unsafe { | ||
| 310 | ep_out_addr::<T>(index).write_value(addr); | ||
| 311 | ep_out_len::<T>(index).write_value(len_bits); | ||
| 312 | } | ||
| 313 | |||
| 314 | EndpointBuffer { | ||
| 315 | addr, | ||
| 316 | len, | ||
| 317 | _phantom: PhantomData, | ||
| 318 | } | ||
| 319 | } | ||
| 320 | UsbDirection::In => { | ||
| 321 | assert!(!ep.used_in); | ||
| 322 | ep.used_in = true; | ||
| 323 | |||
| 324 | let len = (max_packet_size + 1) / 2 * 2; | ||
| 325 | let addr = self.alloc_ep_mem(len); | ||
| 326 | |||
| 327 | unsafe { | ||
| 328 | ep_in_addr::<T>(index).write_value(addr); | ||
| 329 | // ep_in_len is written when actually TXing packets. | ||
| 330 | } | ||
| 331 | |||
| 332 | EndpointBuffer { | ||
| 333 | addr, | ||
| 334 | len, | ||
| 335 | _phantom: PhantomData, | ||
| 336 | } | ||
| 337 | } | ||
| 338 | }; | ||
| 339 | |||
| 340 | trace!(" index={} addr={} len={}", index, buf.addr, buf.len); | ||
| 341 | |||
| 342 | Ok(Endpoint { | ||
| 343 | _phantom: PhantomData, | ||
| 344 | info: EndpointInfo { | ||
| 345 | addr: EndpointAddress::from_parts(index, D::dir()), | ||
| 346 | ep_type, | ||
| 347 | max_packet_size, | ||
| 348 | interval, | ||
| 349 | }, | ||
| 350 | buf, | ||
| 351 | }) | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { | ||
| 356 | type EndpointOut = Endpoint<'d, T, Out>; | ||
| 357 | type EndpointIn = Endpoint<'d, T, In>; | ||
| 358 | type ControlPipe = ControlPipe<'d, T>; | ||
| 359 | type Bus = Bus<'d, T>; | ||
| 360 | |||
| 361 | fn alloc_endpoint_in( | ||
| 362 | &mut self, | ||
| 363 | ep_type: EndpointType, | ||
| 364 | max_packet_size: u16, | ||
| 365 | interval: u8, | ||
| 366 | ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { | ||
| 367 | self.alloc_endpoint(ep_type, max_packet_size, interval) | ||
| 368 | } | ||
| 369 | |||
| 370 | fn alloc_endpoint_out( | ||
| 371 | &mut self, | ||
| 372 | ep_type: EndpointType, | ||
| 373 | max_packet_size: u16, | ||
| 374 | interval: u8, | ||
| 375 | ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { | ||
| 376 | self.alloc_endpoint(ep_type, max_packet_size, interval) | ||
| 377 | } | ||
| 378 | |||
| 379 | fn start(mut self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { | ||
| 380 | let ep_out = self | ||
| 381 | .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) | ||
| 382 | .unwrap(); | ||
| 383 | let ep_in = self | ||
| 384 | .alloc_endpoint(EndpointType::Control, control_max_packet_size, 0) | ||
| 385 | .unwrap(); | ||
| 386 | assert_eq!(ep_out.info.addr.index(), 0); | ||
| 387 | assert_eq!(ep_in.info.addr.index(), 0); | ||
| 388 | |||
| 389 | let regs = T::regs(); | ||
| 390 | |||
| 391 | unsafe { | ||
| 392 | regs.cntr().write(|w| { | ||
| 393 | w.set_pdwn(false); | ||
| 394 | w.set_fres(false); | ||
| 395 | w.set_resetm(true); | ||
| 396 | w.set_suspm(true); | ||
| 397 | w.set_wkupm(true); | ||
| 398 | w.set_ctrm(true); | ||
| 399 | }); | ||
| 400 | |||
| 401 | #[cfg(usb_v3)] | ||
| 402 | regs.bcdr().write(|w| w.set_dppu(true)) | ||
| 403 | } | ||
| 404 | |||
| 405 | trace!("enabled"); | ||
| 406 | |||
| 407 | let mut ep_types = [EpType::BULK; EP_COUNT - 1]; | ||
| 408 | for i in 1..EP_COUNT { | ||
| 409 | ep_types[i - 1] = convert_type(self.alloc[i].ep_type); | ||
| 410 | } | ||
| 411 | |||
| 412 | ( | ||
| 413 | Bus { | ||
| 414 | phantom: PhantomData, | ||
| 415 | ep_types, | ||
| 416 | }, | ||
| 417 | ControlPipe { | ||
| 418 | _phantom: PhantomData, | ||
| 419 | max_packet_size: control_max_packet_size, | ||
| 420 | ep_out, | ||
| 421 | ep_in, | ||
| 422 | }, | ||
| 423 | ) | ||
| 424 | } | ||
| 425 | } | ||
| 426 | |||
| 427 | pub struct Bus<'d, T: Instance> { | ||
| 428 | phantom: PhantomData<&'d mut T>, | ||
| 429 | ep_types: [EpType; EP_COUNT - 1], | ||
| 430 | } | ||
| 431 | |||
| 432 | impl<'d, T: Instance> driver::Bus for Bus<'d, T> { | ||
| 433 | type PollFuture<'a> = impl Future<Output = Event> + 'a where Self: 'a; | ||
| 434 | |||
| 435 | fn poll<'a>(&'a mut self) -> Self::PollFuture<'a> { | ||
| 436 | poll_fn(move |cx| unsafe { | ||
| 437 | BUS_WAKER.register(cx.waker()); | ||
| 438 | let regs = T::regs(); | ||
| 439 | |||
| 440 | let flags = IRQ_FLAGS.load(Ordering::Acquire); | ||
| 441 | |||
| 442 | if flags & IRQ_FLAG_RESUME != 0 { | ||
| 443 | IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESUME, Ordering::AcqRel); | ||
| 444 | return Poll::Ready(Event::Resume); | ||
| 445 | } | ||
| 446 | |||
| 447 | if flags & IRQ_FLAG_RESET != 0 { | ||
| 448 | IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESET, Ordering::AcqRel); | ||
| 449 | |||
| 450 | trace!("RESET REGS WRITINGINGING"); | ||
| 451 | regs.daddr().write(|w| { | ||
| 452 | w.set_ef(true); | ||
| 453 | w.set_add(0); | ||
| 454 | }); | ||
| 455 | |||
| 456 | regs.epr(0).write(|w| { | ||
| 457 | w.set_ep_type(EpType::CONTROL); | ||
| 458 | w.set_stat_rx(Stat::NAK); | ||
| 459 | w.set_stat_tx(Stat::NAK); | ||
| 460 | }); | ||
| 461 | |||
| 462 | for i in 1..EP_COUNT { | ||
| 463 | regs.epr(i).write(|w| { | ||
| 464 | w.set_ea(i as _); | ||
| 465 | w.set_ep_type(self.ep_types[i - 1]); | ||
| 466 | }) | ||
| 467 | } | ||
| 468 | |||
| 469 | for w in &EP_IN_WAKERS { | ||
| 470 | w.wake() | ||
| 471 | } | ||
| 472 | for w in &EP_OUT_WAKERS { | ||
| 473 | w.wake() | ||
| 474 | } | ||
| 475 | |||
| 476 | return Poll::Ready(Event::Reset); | ||
| 477 | } | ||
| 478 | |||
| 479 | if flags & IRQ_FLAG_SUSPEND != 0 { | ||
| 480 | IRQ_FLAGS.fetch_and(!IRQ_FLAG_SUSPEND, Ordering::AcqRel); | ||
| 481 | return Poll::Ready(Event::Suspend); | ||
| 482 | } | ||
| 483 | |||
| 484 | Poll::Pending | ||
| 485 | }) | ||
| 486 | } | ||
| 487 | |||
| 488 | #[inline] | ||
| 489 | fn set_address(&mut self, addr: u8) { | ||
| 490 | let regs = T::regs(); | ||
| 491 | trace!("setting addr: {}", addr); | ||
| 492 | unsafe { | ||
| 493 | regs.daddr().write(|w| { | ||
| 494 | w.set_ef(true); | ||
| 495 | w.set_add(addr); | ||
| 496 | }) | ||
| 497 | } | ||
| 498 | } | ||
| 499 | |||
| 500 | fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { | ||
| 501 | // This can race, so do a retry loop. | ||
| 502 | let reg = T::regs().epr(ep_addr.index() as _); | ||
| 503 | match ep_addr.direction() { | ||
| 504 | UsbDirection::In => { | ||
| 505 | loop { | ||
| 506 | let r = unsafe { reg.read() }; | ||
| 507 | match r.stat_tx() { | ||
| 508 | Stat::DISABLED => break, // if disabled, stall does nothing. | ||
| 509 | Stat::STALL => break, // done! | ||
| 510 | _ => { | ||
| 511 | let want_stat = match stalled { | ||
| 512 | false => Stat::NAK, | ||
| 513 | true => Stat::STALL, | ||
| 514 | }; | ||
| 515 | let mut w = invariant(r); | ||
| 516 | w.set_stat_tx(Stat(r.stat_tx().0 ^ want_stat.0)); | ||
| 517 | unsafe { reg.write_value(w) }; | ||
| 518 | } | ||
| 519 | } | ||
| 520 | } | ||
| 521 | EP_IN_WAKERS[ep_addr.index()].wake(); | ||
| 522 | } | ||
| 523 | UsbDirection::Out => { | ||
| 524 | loop { | ||
| 525 | let r = unsafe { reg.read() }; | ||
| 526 | match r.stat_rx() { | ||
| 527 | Stat::DISABLED => break, // if disabled, stall does nothing. | ||
| 528 | Stat::STALL => break, // done! | ||
| 529 | _ => { | ||
| 530 | let want_stat = match stalled { | ||
| 531 | false => Stat::VALID, | ||
| 532 | true => Stat::STALL, | ||
| 533 | }; | ||
| 534 | let mut w = invariant(r); | ||
| 535 | w.set_stat_rx(Stat(r.stat_rx().0 ^ want_stat.0)); | ||
| 536 | unsafe { reg.write_value(w) }; | ||
| 537 | } | ||
| 538 | } | ||
| 539 | } | ||
| 540 | EP_OUT_WAKERS[ep_addr.index()].wake(); | ||
| 541 | } | ||
| 542 | } | ||
| 543 | } | ||
| 544 | |||
| 545 | fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { | ||
| 546 | let regs = T::regs(); | ||
| 547 | let epr = unsafe { regs.epr(ep_addr.index() as _).read() }; | ||
| 548 | match ep_addr.direction() { | ||
| 549 | UsbDirection::In => epr.stat_tx() == Stat::STALL, | ||
| 550 | UsbDirection::Out => epr.stat_rx() == Stat::STALL, | ||
| 551 | } | ||
| 552 | } | ||
| 553 | |||
| 554 | fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) { | ||
| 555 | trace!("set_enabled {:x} {}", ep_addr, enabled); | ||
| 556 | // This can race, so do a retry loop. | ||
| 557 | let reg = T::regs().epr(ep_addr.index() as _); | ||
| 558 | trace!("EPR before: {:04x}", unsafe { reg.read() }.0); | ||
| 559 | match ep_addr.direction() { | ||
| 560 | UsbDirection::In => { | ||
| 561 | loop { | ||
| 562 | let want_stat = match enabled { | ||
| 563 | false => Stat::DISABLED, | ||
| 564 | true => Stat::NAK, | ||
| 565 | }; | ||
| 566 | let r = unsafe { reg.read() }; | ||
| 567 | if r.stat_tx() == want_stat { | ||
| 568 | break; | ||
| 569 | } | ||
| 570 | let mut w = invariant(r); | ||
| 571 | w.set_stat_tx(Stat(r.stat_tx().0 ^ want_stat.0)); | ||
| 572 | unsafe { reg.write_value(w) }; | ||
| 573 | } | ||
| 574 | EP_IN_WAKERS[ep_addr.index()].wake(); | ||
| 575 | } | ||
| 576 | UsbDirection::Out => { | ||
| 577 | loop { | ||
| 578 | let want_stat = match enabled { | ||
| 579 | false => Stat::DISABLED, | ||
| 580 | true => Stat::VALID, | ||
| 581 | }; | ||
| 582 | let r = unsafe { reg.read() }; | ||
| 583 | if r.stat_rx() == want_stat { | ||
| 584 | break; | ||
| 585 | } | ||
| 586 | let mut w = invariant(r); | ||
| 587 | w.set_stat_rx(Stat(r.stat_rx().0 ^ want_stat.0)); | ||
| 588 | unsafe { reg.write_value(w) }; | ||
| 589 | } | ||
| 590 | EP_OUT_WAKERS[ep_addr.index()].wake(); | ||
| 591 | } | ||
| 592 | } | ||
| 593 | trace!("EPR after: {:04x}", unsafe { reg.read() }.0); | ||
| 594 | } | ||
| 595 | |||
| 596 | type EnableFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a; | ||
| 597 | |||
| 598 | fn enable(&mut self) -> Self::EnableFuture<'_> { | ||
| 599 | async move {} | ||
| 600 | } | ||
| 601 | |||
| 602 | type DisableFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a; | ||
| 603 | |||
| 604 | fn disable(&mut self) -> Self::DisableFuture<'_> { | ||
| 605 | async move {} | ||
| 606 | } | ||
| 607 | |||
| 608 | type RemoteWakeupFuture<'a> = impl Future<Output = Result<(), Unsupported>> + 'a where Self: 'a; | ||
| 609 | |||
| 610 | fn remote_wakeup(&mut self) -> Self::RemoteWakeupFuture<'_> { | ||
| 611 | async move { Err(Unsupported) } | ||
| 612 | } | ||
| 613 | } | ||
| 614 | |||
| 615 | trait Dir { | ||
| 616 | fn dir() -> UsbDirection; | ||
| 617 | fn waker(i: usize) -> &'static AtomicWaker; | ||
| 618 | } | ||
| 619 | |||
| 620 | pub enum In {} | ||
| 621 | impl Dir for In { | ||
| 622 | fn dir() -> UsbDirection { | ||
| 623 | UsbDirection::In | ||
| 624 | } | ||
| 625 | |||
| 626 | #[inline] | ||
| 627 | fn waker(i: usize) -> &'static AtomicWaker { | ||
| 628 | &EP_IN_WAKERS[i] | ||
| 629 | } | ||
| 630 | } | ||
| 631 | |||
| 632 | pub enum Out {} | ||
| 633 | impl Dir for Out { | ||
| 634 | fn dir() -> UsbDirection { | ||
| 635 | UsbDirection::Out | ||
| 636 | } | ||
| 637 | |||
| 638 | #[inline] | ||
| 639 | fn waker(i: usize) -> &'static AtomicWaker { | ||
| 640 | &EP_OUT_WAKERS[i] | ||
| 641 | } | ||
| 642 | } | ||
| 643 | |||
| 644 | pub struct Endpoint<'d, T: Instance, D> { | ||
| 645 | _phantom: PhantomData<(&'d mut T, D)>, | ||
| 646 | info: EndpointInfo, | ||
| 647 | buf: EndpointBuffer<T>, | ||
| 648 | } | ||
| 649 | |||
| 650 | impl<'d, T: Instance, D> Endpoint<'d, T, D> { | ||
| 651 | fn write_data(&mut self, buf: &[u8]) { | ||
| 652 | let index = self.info.addr.index(); | ||
| 653 | self.buf.write(buf); | ||
| 654 | unsafe { ep_in_len::<T>(index).write_value(buf.len() as _) }; | ||
| 655 | } | ||
| 656 | |||
| 657 | fn read_data(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> { | ||
| 658 | let index = self.info.addr.index(); | ||
| 659 | let rx_len = unsafe { ep_out_len::<T>(index).read() as usize } & 0x3FF; | ||
| 660 | trace!("READ DONE, rx_len = {}", rx_len); | ||
| 661 | if rx_len > buf.len() { | ||
| 662 | return Err(EndpointError::BufferOverflow); | ||
| 663 | } | ||
| 664 | self.buf.read(&mut buf[..rx_len]); | ||
| 665 | Ok(rx_len) | ||
| 666 | } | ||
| 667 | } | ||
| 668 | |||
| 669 | impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { | ||
| 670 | fn info(&self) -> &EndpointInfo { | ||
| 671 | &self.info | ||
| 672 | } | ||
| 673 | |||
| 674 | type WaitEnabledFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a; | ||
| 675 | |||
| 676 | fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> { | ||
| 677 | async move { | ||
| 678 | trace!("wait_enabled OUT WAITING"); | ||
| 679 | let index = self.info.addr.index(); | ||
| 680 | poll_fn(|cx| { | ||
| 681 | EP_OUT_WAKERS[index].register(cx.waker()); | ||
| 682 | let regs = T::regs(); | ||
| 683 | if unsafe { regs.epr(index).read() }.stat_tx() == Stat::DISABLED { | ||
| 684 | Poll::Pending | ||
| 685 | } else { | ||
| 686 | Poll::Ready(()) | ||
| 687 | } | ||
| 688 | }) | ||
| 689 | .await; | ||
| 690 | trace!("wait_enabled OUT OK"); | ||
| 691 | } | ||
| 692 | } | ||
| 693 | } | ||
| 694 | |||
| 695 | impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, Out> { | ||
| 696 | fn info(&self) -> &EndpointInfo { | ||
| 697 | &self.info | ||
| 698 | } | ||
| 699 | |||
| 700 | type WaitEnabledFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a; | ||
| 701 | |||
| 702 | fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> { | ||
| 703 | async move { | ||
| 704 | trace!("wait_enabled OUT WAITING"); | ||
| 705 | let index = self.info.addr.index(); | ||
| 706 | poll_fn(|cx| { | ||
| 707 | EP_OUT_WAKERS[index].register(cx.waker()); | ||
| 708 | let regs = T::regs(); | ||
| 709 | if unsafe { regs.epr(index).read() }.stat_rx() == Stat::DISABLED { | ||
| 710 | Poll::Pending | ||
| 711 | } else { | ||
| 712 | Poll::Ready(()) | ||
| 713 | } | ||
| 714 | }) | ||
| 715 | .await; | ||
| 716 | trace!("wait_enabled OUT OK"); | ||
| 717 | } | ||
| 718 | } | ||
| 719 | } | ||
| 720 | |||
| 721 | impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { | ||
| 722 | type ReadFuture<'a> = impl Future<Output = Result<usize, EndpointError>> + 'a where Self: 'a; | ||
| 723 | |||
| 724 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { | ||
| 725 | async move { | ||
| 726 | trace!("READ WAITING, buf.len() = {}", buf.len()); | ||
| 727 | let index = self.info.addr.index(); | ||
| 728 | let stat = poll_fn(|cx| { | ||
| 729 | EP_OUT_WAKERS[index].register(cx.waker()); | ||
| 730 | let regs = T::regs(); | ||
| 731 | let stat = unsafe { regs.epr(index).read() }.stat_rx(); | ||
| 732 | if matches!(stat, Stat::NAK | Stat::DISABLED) { | ||
| 733 | Poll::Ready(stat) | ||
| 734 | } else { | ||
| 735 | Poll::Pending | ||
| 736 | } | ||
| 737 | }) | ||
| 738 | .await; | ||
| 739 | |||
| 740 | if stat == Stat::DISABLED { | ||
| 741 | return Err(EndpointError::Disabled); | ||
| 742 | } | ||
| 743 | |||
| 744 | let rx_len = self.read_data(buf)?; | ||
| 745 | |||
| 746 | let regs = T::regs(); | ||
| 747 | unsafe { | ||
| 748 | regs.epr(index).write(|w| { | ||
| 749 | w.set_ep_type(convert_type(self.info.ep_type)); | ||
| 750 | w.set_ea(self.info.addr.index() as _); | ||
| 751 | w.set_stat_rx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); | ||
| 752 | w.set_stat_tx(Stat(0)); | ||
| 753 | w.set_ctr_rx(true); // don't clear | ||
| 754 | w.set_ctr_tx(true); // don't clear | ||
| 755 | }) | ||
| 756 | }; | ||
| 757 | trace!("READ OK, rx_len = {}", rx_len); | ||
| 758 | |||
| 759 | Ok(rx_len) | ||
| 760 | } | ||
| 761 | } | ||
| 762 | } | ||
| 763 | |||
| 764 | impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { | ||
| 765 | type WriteFuture<'a> = impl Future<Output = Result<(), EndpointError>> + 'a where Self: 'a; | ||
| 766 | |||
| 767 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { | ||
| 768 | async move { | ||
| 769 | if buf.len() > self.info.max_packet_size as usize { | ||
| 770 | return Err(EndpointError::BufferOverflow); | ||
| 771 | } | ||
| 772 | |||
| 773 | let index = self.info.addr.index(); | ||
| 774 | |||
| 775 | trace!("WRITE WAITING"); | ||
| 776 | let stat = poll_fn(|cx| { | ||
| 777 | EP_IN_WAKERS[index].register(cx.waker()); | ||
| 778 | let regs = T::regs(); | ||
| 779 | let stat = unsafe { regs.epr(index).read() }.stat_tx(); | ||
| 780 | if matches!(stat, Stat::NAK | Stat::DISABLED) { | ||
| 781 | Poll::Ready(stat) | ||
| 782 | } else { | ||
| 783 | Poll::Pending | ||
| 784 | } | ||
| 785 | }) | ||
| 786 | .await; | ||
| 787 | |||
| 788 | if stat == Stat::DISABLED { | ||
| 789 | return Err(EndpointError::Disabled); | ||
| 790 | } | ||
| 791 | |||
| 792 | self.write_data(buf); | ||
| 793 | |||
| 794 | let regs = T::regs(); | ||
| 795 | unsafe { | ||
| 796 | regs.epr(index).write(|w| { | ||
| 797 | w.set_ep_type(convert_type(self.info.ep_type)); | ||
| 798 | w.set_ea(self.info.addr.index() as _); | ||
| 799 | w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); | ||
| 800 | w.set_stat_rx(Stat(0)); | ||
| 801 | w.set_ctr_rx(true); // don't clear | ||
| 802 | w.set_ctr_tx(true); // don't clear | ||
| 803 | }) | ||
| 804 | }; | ||
| 805 | |||
| 806 | trace!("WRITE OK"); | ||
| 807 | |||
| 808 | Ok(()) | ||
| 809 | } | ||
| 810 | } | ||
| 811 | } | ||
| 812 | |||
| 813 | pub struct ControlPipe<'d, T: Instance> { | ||
| 814 | _phantom: PhantomData<&'d mut T>, | ||
| 815 | max_packet_size: u16, | ||
| 816 | ep_in: Endpoint<'d, T, In>, | ||
| 817 | ep_out: Endpoint<'d, T, Out>, | ||
| 818 | } | ||
| 819 | |||
| 820 | impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { | ||
| 821 | type SetupFuture<'a> = impl Future<Output = [u8;8]> + 'a where Self: 'a; | ||
| 822 | type DataOutFuture<'a> = impl Future<Output = Result<usize, EndpointError>> + 'a where Self: 'a; | ||
| 823 | type DataInFuture<'a> = impl Future<Output = Result<(), EndpointError>> + 'a where Self: 'a; | ||
| 824 | type AcceptFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a; | ||
| 825 | type RejectFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a; | ||
| 826 | |||
| 827 | fn max_packet_size(&self) -> usize { | ||
| 828 | usize::from(self.max_packet_size) | ||
| 829 | } | ||
| 830 | |||
| 831 | fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a> { | ||
| 832 | async move { | ||
| 833 | loop { | ||
| 834 | trace!("SETUP read waiting"); | ||
| 835 | poll_fn(|cx| { | ||
| 836 | EP_OUT_WAKERS[0].register(cx.waker()); | ||
| 837 | if EP0_SETUP.load(Ordering::Relaxed) { | ||
| 838 | Poll::Ready(()) | ||
| 839 | } else { | ||
| 840 | Poll::Pending | ||
| 841 | } | ||
| 842 | }) | ||
| 843 | .await; | ||
| 844 | |||
| 845 | let mut buf = [0; 8]; | ||
| 846 | let rx_len = self.ep_out.read_data(&mut buf); | ||
| 847 | if rx_len != Ok(8) { | ||
| 848 | trace!("SETUP read failed: {:?}", rx_len); | ||
| 849 | continue; | ||
| 850 | } | ||
| 851 | |||
| 852 | EP0_SETUP.store(false, Ordering::Relaxed); | ||
| 853 | |||
| 854 | trace!("SETUP read ok"); | ||
| 855 | return buf; | ||
| 856 | } | ||
| 857 | } | ||
| 858 | } | ||
| 859 | |||
| 860 | fn data_out<'a>( | ||
| 861 | &'a mut self, | ||
| 862 | buf: &'a mut [u8], | ||
| 863 | first: bool, | ||
| 864 | last: bool, | ||
| 865 | ) -> Self::DataOutFuture<'a> { | ||
| 866 | async move { | ||
| 867 | let regs = T::regs(); | ||
| 868 | |||
| 869 | // When a SETUP is received, Stat/Stat is set to NAK. | ||
| 870 | // On first transfer, we must set Stat=VALID, to get the OUT data stage. | ||
| 871 | // We want Stat=STALL so that the host gets a STALL if it switches to the status | ||
| 872 | // stage too soon, except in the last transfer we set Stat=NAK so that it waits | ||
| 873 | // for the status stage, which we will ACK or STALL later. | ||
| 874 | if first || last { | ||
| 875 | let mut stat_rx = 0; | ||
| 876 | let mut stat_tx = 0; | ||
| 877 | if first { | ||
| 878 | // change NAK -> VALID | ||
| 879 | stat_rx ^= Stat::NAK.0 ^ Stat::VALID.0; | ||
| 880 | stat_tx ^= Stat::NAK.0 ^ Stat::STALL.0; | ||
| 881 | } | ||
| 882 | if last { | ||
| 883 | // change STALL -> VALID | ||
| 884 | stat_tx ^= Stat::STALL.0 ^ Stat::NAK.0; | ||
| 885 | } | ||
| 886 | // Note: if this is the first AND last transfer, the above effectively | ||
| 887 | // changes stat_tx like NAK -> NAK, so noop. | ||
| 888 | unsafe { | ||
| 889 | regs.epr(0).write(|w| { | ||
| 890 | w.set_ep_type(EpType::CONTROL); | ||
| 891 | w.set_stat_rx(Stat(stat_rx)); | ||
| 892 | w.set_stat_tx(Stat(stat_tx)); | ||
| 893 | w.set_ctr_rx(true); // don't clear | ||
| 894 | w.set_ctr_tx(true); // don't clear | ||
| 895 | }) | ||
| 896 | } | ||
| 897 | } | ||
| 898 | |||
| 899 | trace!("data_out WAITING, buf.len() = {}", buf.len()); | ||
| 900 | poll_fn(|cx| { | ||
| 901 | EP_OUT_WAKERS[0].register(cx.waker()); | ||
| 902 | let regs = T::regs(); | ||
| 903 | if unsafe { regs.epr(0).read() }.stat_rx() == Stat::NAK { | ||
| 904 | Poll::Ready(()) | ||
| 905 | } else { | ||
| 906 | Poll::Pending | ||
| 907 | } | ||
| 908 | }) | ||
| 909 | .await; | ||
| 910 | |||
| 911 | if EP0_SETUP.load(Ordering::Relaxed) { | ||
| 912 | trace!("received another SETUP, aborting data_out."); | ||
| 913 | return Err(EndpointError::Disabled); | ||
| 914 | } | ||
| 915 | |||
| 916 | let rx_len = self.ep_out.read_data(buf)?; | ||
| 917 | |||
| 918 | unsafe { | ||
| 919 | regs.epr(0).write(|w| { | ||
| 920 | w.set_ep_type(EpType::CONTROL); | ||
| 921 | w.set_stat_rx(Stat(match last { | ||
| 922 | // If last, set STAT_RX=STALL. | ||
| 923 | true => Stat::NAK.0 ^ Stat::STALL.0, | ||
| 924 | // Otherwise, set STAT_RX=VALID, to allow the host to send the next packet. | ||
| 925 | false => Stat::NAK.0 ^ Stat::VALID.0, | ||
| 926 | })); | ||
| 927 | w.set_ctr_rx(true); // don't clear | ||
| 928 | w.set_ctr_tx(true); // don't clear | ||
| 929 | }) | ||
| 930 | }; | ||
| 931 | |||
| 932 | Ok(rx_len) | ||
| 933 | } | ||
| 934 | } | ||
| 935 | |||
| 936 | fn data_in<'a>(&'a mut self, buf: &'a [u8], first: bool, last: bool) -> Self::DataInFuture<'a> { | ||
| 937 | async move { | ||
| 938 | trace!("control: data_in"); | ||
| 939 | |||
| 940 | if buf.len() > self.ep_in.info.max_packet_size as usize { | ||
| 941 | return Err(EndpointError::BufferOverflow); | ||
| 942 | } | ||
| 943 | |||
| 944 | let regs = T::regs(); | ||
| 945 | |||
| 946 | // When a SETUP is received, Stat is set to NAK. | ||
| 947 | // We want it to be STALL in non-last transfers. | ||
| 948 | // We want it to be VALID in last transfer, so the HW does the status stage. | ||
| 949 | if first || last { | ||
| 950 | let mut stat_rx = 0; | ||
| 951 | if first { | ||
| 952 | // change NAK -> STALL | ||
| 953 | stat_rx ^= Stat::NAK.0 ^ Stat::STALL.0; | ||
| 954 | } | ||
| 955 | if last { | ||
| 956 | // change STALL -> VALID | ||
| 957 | stat_rx ^= Stat::STALL.0 ^ Stat::VALID.0; | ||
| 958 | } | ||
| 959 | // Note: if this is the first AND last transfer, the above effectively | ||
| 960 | // does a change of NAK -> VALID. | ||
| 961 | unsafe { | ||
| 962 | regs.epr(0).write(|w| { | ||
| 963 | w.set_ep_type(EpType::CONTROL); | ||
| 964 | w.set_stat_rx(Stat(stat_rx)); | ||
| 965 | w.set_ep_kind(last); // set OUT_STATUS if last. | ||
| 966 | w.set_ctr_rx(true); // don't clear | ||
| 967 | w.set_ctr_tx(true); // don't clear | ||
| 968 | }) | ||
| 969 | } | ||
| 970 | } | ||
| 971 | |||
| 972 | trace!("WRITE WAITING"); | ||
| 973 | poll_fn(|cx| { | ||
| 974 | EP_IN_WAKERS[0].register(cx.waker()); | ||
| 975 | EP_OUT_WAKERS[0].register(cx.waker()); | ||
| 976 | let regs = T::regs(); | ||
| 977 | if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK { | ||
| 978 | Poll::Ready(()) | ||
| 979 | } else { | ||
| 980 | Poll::Pending | ||
| 981 | } | ||
| 982 | }) | ||
| 983 | .await; | ||
| 984 | |||
| 985 | if EP0_SETUP.load(Ordering::Relaxed) { | ||
| 986 | trace!("received another SETUP, aborting data_in."); | ||
| 987 | return Err(EndpointError::Disabled); | ||
| 988 | } | ||
| 989 | |||
| 990 | self.ep_in.write_data(buf); | ||
| 991 | |||
| 992 | let regs = T::regs(); | ||
| 993 | unsafe { | ||
| 994 | regs.epr(0).write(|w| { | ||
| 995 | w.set_ep_type(EpType::CONTROL); | ||
| 996 | w.set_stat_tx(Stat(Stat::NAK.0 ^ Stat::VALID.0)); | ||
| 997 | w.set_ep_kind(last); // set OUT_STATUS if last. | ||
| 998 | w.set_ctr_rx(true); // don't clear | ||
| 999 | w.set_ctr_tx(true); // don't clear | ||
| 1000 | }) | ||
| 1001 | }; | ||
| 1002 | |||
| 1003 | trace!("WRITE OK"); | ||
| 1004 | |||
| 1005 | Ok(()) | ||
| 1006 | } | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | fn accept<'a>(&'a mut self) -> Self::AcceptFuture<'a> { | ||
| 1010 | async move { | ||
| 1011 | let regs = T::regs(); | ||
| 1012 | trace!("control: accept"); | ||
| 1013 | |||
| 1014 | self.ep_in.write_data(&[]); | ||
| 1015 | |||
| 1016 | // Set OUT=stall, IN=accept | ||
| 1017 | unsafe { | ||
| 1018 | let epr = regs.epr(0).read(); | ||
| 1019 | regs.epr(0).write(|w| { | ||
| 1020 | w.set_ep_type(EpType::CONTROL); | ||
| 1021 | w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0)); | ||
| 1022 | w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::VALID.0)); | ||
| 1023 | w.set_ctr_rx(true); // don't clear | ||
| 1024 | w.set_ctr_tx(true); // don't clear | ||
| 1025 | }); | ||
| 1026 | } | ||
| 1027 | trace!("control: accept WAITING"); | ||
| 1028 | |||
| 1029 | // Wait is needed, so that we don't set the address too soon, breaking the status stage. | ||
| 1030 | // (embassy-usb sets the address after accept() returns) | ||
| 1031 | poll_fn(|cx| { | ||
| 1032 | EP_IN_WAKERS[0].register(cx.waker()); | ||
| 1033 | let regs = T::regs(); | ||
| 1034 | if unsafe { regs.epr(0).read() }.stat_tx() == Stat::NAK { | ||
| 1035 | Poll::Ready(()) | ||
| 1036 | } else { | ||
| 1037 | Poll::Pending | ||
| 1038 | } | ||
| 1039 | }) | ||
| 1040 | .await; | ||
| 1041 | |||
| 1042 | trace!("control: accept OK"); | ||
| 1043 | } | ||
| 1044 | } | ||
| 1045 | |||
| 1046 | fn reject<'a>(&'a mut self) -> Self::RejectFuture<'a> { | ||
| 1047 | async move { | ||
| 1048 | let regs = T::regs(); | ||
| 1049 | trace!("control: reject"); | ||
| 1050 | |||
| 1051 | // Set IN+OUT to stall | ||
| 1052 | unsafe { | ||
| 1053 | let epr = regs.epr(0).read(); | ||
| 1054 | regs.epr(0).write(|w| { | ||
| 1055 | w.set_ep_type(EpType::CONTROL); | ||
| 1056 | w.set_stat_rx(Stat(epr.stat_rx().0 ^ Stat::STALL.0)); | ||
| 1057 | w.set_stat_tx(Stat(epr.stat_tx().0 ^ Stat::STALL.0)); | ||
| 1058 | w.set_ctr_rx(true); // don't clear | ||
| 1059 | w.set_ctr_tx(true); // don't clear | ||
| 1060 | }); | ||
| 1061 | } | ||
| 1062 | } | ||
| 1063 | } | ||
| 1064 | } | ||
diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index e09e17fcd..8de736d6b 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml | |||
| @@ -8,6 +8,8 @@ resolver = "2" | |||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } | 9 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } | 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any"] } |
| 11 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 12 | embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } | ||
| 11 | 13 | ||
| 12 | defmt = "0.3" | 14 | defmt = "0.3" |
| 13 | defmt-rtt = "0.3" | 15 | defmt-rtt = "0.3" |
diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs new file mode 100644 index 000000000..fe4aa4cc9 --- /dev/null +++ b/examples/stm32f1/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::panic; | ||
| 6 | use defmt::*; | ||
| 7 | use defmt_rtt as _; // global logger | ||
| 8 | use embassy::executor::Spawner; | ||
| 9 | use embassy::time::Duration; | ||
| 10 | use embassy::time::Timer; | ||
| 11 | use embassy_stm32::gpio::Level; | ||
| 12 | use embassy_stm32::gpio::Output; | ||
| 13 | use embassy_stm32::gpio::Speed; | ||
| 14 | use embassy_stm32::interrupt; | ||
| 15 | use embassy_stm32::time::Hertz; | ||
| 16 | use embassy_stm32::usb::{Driver, Instance}; | ||
| 17 | use embassy_stm32::{Config, Peripherals}; | ||
| 18 | use embassy_usb::driver::EndpointError; | ||
| 19 | use embassy_usb::Builder; | ||
| 20 | use embassy_usb_serial::{CdcAcmClass, State}; | ||
| 21 | use futures::future::join; | ||
| 22 | use panic_probe as _; | ||
| 23 | |||
| 24 | fn config() -> Config { | ||
| 25 | let mut config = Config::default(); | ||
| 26 | config.rcc.hse = Some(Hertz(8_000_000)); | ||
| 27 | config.rcc.sys_ck = Some(Hertz(48_000_000)); | ||
| 28 | config.rcc.pclk1 = Some(Hertz(24_000_000)); | ||
| 29 | config | ||
| 30 | } | ||
| 31 | |||
| 32 | #[embassy::main(config = "config()")] | ||
| 33 | async fn main(_spawner: Spawner, mut p: Peripherals) { | ||
| 34 | info!("Hello World!"); | ||
| 35 | |||
| 36 | { | ||
| 37 | // BluePill board has a pull-up resistor on the D+ line. | ||
| 38 | // Pull the D+ pin down to send a RESET condition to the USB bus. | ||
| 39 | // This forced reset is needed only for development, without it host | ||
| 40 | // will not reset your device when you upload new firmware. | ||
| 41 | let _dp = Output::new(&mut p.PA12, Level::Low, Speed::Low); | ||
| 42 | Timer::after(Duration::from_millis(10)).await; | ||
| 43 | } | ||
| 44 | |||
| 45 | // Create the driver, from the HAL. | ||
| 46 | let irq = interrupt::take!(USB_LP_CAN1_RX0); | ||
| 47 | let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); | ||
| 48 | |||
| 49 | // Create embassy-usb Config | ||
| 50 | let config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 51 | //config.max_packet_size_0 = 64; | ||
| 52 | |||
| 53 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 54 | // It needs some buffers for building the descriptors. | ||
| 55 | let mut device_descriptor = [0; 256]; | ||
| 56 | let mut config_descriptor = [0; 256]; | ||
| 57 | let mut bos_descriptor = [0; 256]; | ||
| 58 | let mut control_buf = [0; 7]; | ||
| 59 | |||
| 60 | let mut state = State::new(); | ||
| 61 | |||
| 62 | let mut builder = Builder::new( | ||
| 63 | driver, | ||
| 64 | config, | ||
| 65 | &mut device_descriptor, | ||
| 66 | &mut config_descriptor, | ||
| 67 | &mut bos_descriptor, | ||
| 68 | &mut control_buf, | ||
| 69 | None, | ||
| 70 | ); | ||
| 71 | |||
| 72 | // Create classes on the builder. | ||
| 73 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 74 | |||
| 75 | // Build the builder. | ||
| 76 | let mut usb = builder.build(); | ||
| 77 | |||
| 78 | // Run the USB device. | ||
| 79 | let usb_fut = usb.run(); | ||
| 80 | |||
| 81 | // Do stuff with the class! | ||
| 82 | let echo_fut = async { | ||
| 83 | loop { | ||
| 84 | class.wait_connection().await; | ||
| 85 | info!("Connected"); | ||
| 86 | let _ = echo(&mut class).await; | ||
| 87 | info!("Disconnected"); | ||
| 88 | } | ||
| 89 | }; | ||
| 90 | |||
| 91 | // Run everything concurrently. | ||
| 92 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 93 | join(usb_fut, echo_fut).await; | ||
| 94 | } | ||
| 95 | |||
| 96 | struct Disconnected {} | ||
| 97 | |||
| 98 | impl From<EndpointError> for Disconnected { | ||
| 99 | fn from(val: EndpointError) -> Self { | ||
| 100 | match val { | ||
| 101 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 102 | EndpointError::Disabled => Disconnected {}, | ||
| 103 | } | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | async fn echo<'d, T: Instance + 'd>( | ||
| 108 | class: &mut CdcAcmClass<'d, Driver<'d, T>>, | ||
| 109 | ) -> Result<(), Disconnected> { | ||
| 110 | let mut buf = [0; 64]; | ||
| 111 | loop { | ||
| 112 | let n = class.read_packet(&mut buf).await?; | ||
| 113 | let data = &buf[..n]; | ||
| 114 | info!("data: {:x}", data); | ||
| 115 | class.write_packet(data).await?; | ||
| 116 | } | ||
| 117 | } | ||
diff --git a/examples/stm32f3/.cargo/config.toml b/examples/stm32f3/.cargo/config.toml index eb8a8b335..84b4b2f19 100644 --- a/examples/stm32f3/.cargo/config.toml +++ b/examples/stm32f3/.cargo/config.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] |
| 2 | # replace STM32F429ZITx with your chip as listed in `probe-run --list-chips` | 2 | # replace STM32F429ZITx with your chip as listed in `probe-run --list-chips` |
| 3 | runner = "probe-run --chip STM32F303VCTx" | 3 | runner = "probe-run --chip STM32F303ZETx" |
| 4 | 4 | ||
| 5 | [build] | 5 | [build] |
| 6 | target = "thumbv7em-none-eabihf" | 6 | target = "thumbv7em-none-eabihf" |
diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index de81f1002..15128ecc9 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml | |||
| @@ -7,7 +7,10 @@ resolver = "2" | |||
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } | 9 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303vc", "unstable-pac", "memory-x", "time-driver-any", "exti"] } | 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } |
| 11 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 12 | embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } | ||
| 13 | embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } | ||
| 11 | 14 | ||
| 12 | defmt = "0.3" | 15 | defmt = "0.3" |
| 13 | defmt-rtt = "0.3" | 16 | defmt-rtt = "0.3" |
diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs new file mode 100644 index 000000000..fc33d0bc7 --- /dev/null +++ b/examples/stm32f3/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::panic; | ||
| 6 | use defmt::*; | ||
| 7 | use defmt_rtt as _; // global logger | ||
| 8 | use embassy::executor::Spawner; | ||
| 9 | use embassy::time::Duration; | ||
| 10 | use embassy::time::Timer; | ||
| 11 | use embassy_stm32::gpio::Level; | ||
| 12 | use embassy_stm32::gpio::Output; | ||
| 13 | use embassy_stm32::gpio::Speed; | ||
| 14 | use embassy_stm32::interrupt; | ||
| 15 | use embassy_stm32::time::U32Ext; | ||
| 16 | use embassy_stm32::usb::{Driver, Instance}; | ||
| 17 | use embassy_stm32::{Config, Peripherals}; | ||
| 18 | use embassy_usb::driver::EndpointError; | ||
| 19 | use embassy_usb::Builder; | ||
| 20 | use embassy_usb_serial::{CdcAcmClass, State}; | ||
| 21 | use futures::future::join; | ||
| 22 | use panic_probe as _; | ||
| 23 | |||
| 24 | fn config() -> Config { | ||
| 25 | let mut config = Config::default(); | ||
| 26 | |||
| 27 | config.rcc.hse = Some(8.mhz().into()); | ||
| 28 | config.rcc.sysclk = Some(48.mhz().into()); | ||
| 29 | config.rcc.pclk1 = Some(24.mhz().into()); | ||
| 30 | config.rcc.pclk2 = Some(24.mhz().into()); | ||
| 31 | config.rcc.pll48 = true; | ||
| 32 | |||
| 33 | config | ||
| 34 | } | ||
| 35 | |||
| 36 | #[embassy::main(config = "config()")] | ||
| 37 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 38 | info!("Hello World!"); | ||
| 39 | |||
| 40 | // Needed for nucleo-stm32f303ze | ||
| 41 | let mut dp_pullup = Output::new(p.PG6, Level::Low, Speed::Medium); | ||
| 42 | Timer::after(Duration::from_millis(10)).await; | ||
| 43 | dp_pullup.set_high(); | ||
| 44 | |||
| 45 | // Create the driver, from the HAL. | ||
| 46 | let irq = interrupt::take!(USB_LP_CAN_RX0); | ||
| 47 | let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); | ||
| 48 | |||
| 49 | // Create embassy-usb Config | ||
| 50 | let config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 51 | |||
| 52 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 53 | // It needs some buffers for building the descriptors. | ||
| 54 | let mut device_descriptor = [0; 256]; | ||
| 55 | let mut config_descriptor = [0; 256]; | ||
| 56 | let mut bos_descriptor = [0; 256]; | ||
| 57 | let mut control_buf = [0; 7]; | ||
| 58 | |||
| 59 | let mut state = State::new(); | ||
| 60 | |||
| 61 | let mut builder = Builder::new( | ||
| 62 | driver, | ||
| 63 | config, | ||
| 64 | &mut device_descriptor, | ||
| 65 | &mut config_descriptor, | ||
| 66 | &mut bos_descriptor, | ||
| 67 | &mut control_buf, | ||
| 68 | None, | ||
| 69 | ); | ||
| 70 | |||
| 71 | // Create classes on the builder. | ||
| 72 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 73 | |||
| 74 | // Build the builder. | ||
| 75 | let mut usb = builder.build(); | ||
| 76 | |||
| 77 | // Run the USB device. | ||
| 78 | let usb_fut = usb.run(); | ||
| 79 | |||
| 80 | // Do stuff with the class! | ||
| 81 | let echo_fut = async { | ||
| 82 | loop { | ||
| 83 | class.wait_connection().await; | ||
| 84 | info!("Connected"); | ||
| 85 | let _ = echo(&mut class).await; | ||
| 86 | info!("Disconnected"); | ||
| 87 | } | ||
| 88 | }; | ||
| 89 | |||
| 90 | // Run everything concurrently. | ||
| 91 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 92 | join(usb_fut, echo_fut).await; | ||
| 93 | } | ||
| 94 | |||
| 95 | struct Disconnected {} | ||
| 96 | |||
| 97 | impl From<EndpointError> for Disconnected { | ||
| 98 | fn from(val: EndpointError) -> Self { | ||
| 99 | match val { | ||
| 100 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 101 | EndpointError::Disabled => Disconnected {}, | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | async fn echo<'d, T: Instance + 'd>( | ||
| 107 | class: &mut CdcAcmClass<'d, Driver<'d, T>>, | ||
| 108 | ) -> Result<(), Disconnected> { | ||
| 109 | let mut buf = [0; 64]; | ||
| 110 | loop { | ||
| 111 | let n = class.read_packet(&mut buf).await?; | ||
| 112 | let data = &buf[..n]; | ||
| 113 | info!("data: {:x}", data); | ||
| 114 | class.write_packet(data).await?; | ||
| 115 | } | ||
| 116 | } | ||
diff --git a/examples/stm32l5/.cargo/config.toml b/examples/stm32l5/.cargo/config.toml new file mode 100644 index 000000000..e63fe37e2 --- /dev/null +++ b/examples/stm32l5/.cargo/config.toml | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 2 | # replace STM32F429ZITx with your chip as listed in `probe-run --list-chips` | ||
| 3 | runner = "probe-run --chip STM32L552ZETxQ" | ||
| 4 | |||
| 5 | [build] | ||
| 6 | target = "thumbv8m.main-none-eabihf" | ||
diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml new file mode 100644 index 000000000..7f60e26da --- /dev/null +++ b/examples/stm32l5/Cargo.toml | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | [package] | ||
| 2 | authors = ["Dario Nieuwenhuis <[email protected]>"] | ||
| 3 | edition = "2018" | ||
| 4 | name = "embassy-stm32l5-examples" | ||
| 5 | version = "0.1.0" | ||
| 6 | resolver = "2" | ||
| 7 | |||
| 8 | [features] | ||
| 9 | |||
| 10 | [dependencies] | ||
| 11 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "time-tick-32768hz"] } | ||
| 12 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } | ||
| 13 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 14 | embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] } | ||
| 15 | embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"] } | ||
| 16 | embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] } | ||
| 17 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } | ||
| 18 | usbd-hid = "0.5.2" | ||
| 19 | |||
| 20 | defmt = "0.3" | ||
| 21 | defmt-rtt = "0.3" | ||
| 22 | panic-probe = { version = "0.3", features = ["print-defmt"] } | ||
| 23 | |||
| 24 | cortex-m = "0.7.3" | ||
| 25 | cortex-m-rt = "0.7.0" | ||
| 26 | embedded-hal = "0.2.6" | ||
| 27 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | ||
| 28 | heapless = { version = "0.7.5", default-features = false } | ||
| 29 | rand_core = { version = "0.6.3", default-features = false } | ||
| 30 | embedded-io = { version = "0.3.0", features = ["async"] } | ||
diff --git a/examples/stm32l5/build.rs b/examples/stm32l5/build.rs new file mode 100644 index 000000000..8cd32d7ed --- /dev/null +++ b/examples/stm32l5/build.rs | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | fn main() { | ||
| 2 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 3 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | ||
| 4 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 5 | } | ||
diff --git a/examples/stm32l5/src/bin/button_exti.rs b/examples/stm32l5/src/bin/button_exti.rs new file mode 100644 index 000000000..304ce0a8a --- /dev/null +++ b/examples/stm32l5/src/bin/button_exti.rs | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use defmt_rtt as _; // global logger | ||
| 7 | use embassy::executor::Spawner; | ||
| 8 | use embassy_stm32::exti::ExtiInput; | ||
| 9 | use embassy_stm32::gpio::{Input, Pull}; | ||
| 10 | use embassy_stm32::Peripherals; | ||
| 11 | use panic_probe as _; | ||
| 12 | |||
| 13 | #[embassy::main] | ||
| 14 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 15 | info!("Hello World!"); | ||
| 16 | |||
| 17 | let button = Input::new(p.PC13, Pull::Down); | ||
| 18 | let mut button = ExtiInput::new(button, p.EXTI13); | ||
| 19 | |||
| 20 | info!("Press the USER button..."); | ||
| 21 | |||
| 22 | loop { | ||
| 23 | button.wait_for_falling_edge().await; | ||
| 24 | info!("Pressed!"); | ||
| 25 | button.wait_for_rising_edge().await; | ||
| 26 | info!("Released!"); | ||
| 27 | } | ||
| 28 | } | ||
diff --git a/examples/stm32l5/src/bin/rng.rs b/examples/stm32l5/src/bin/rng.rs new file mode 100644 index 000000000..5f75c1ff1 --- /dev/null +++ b/examples/stm32l5/src/bin/rng.rs | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use defmt_rtt as _; // global logger | ||
| 7 | use embassy::executor::Spawner; | ||
| 8 | use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv}; | ||
| 9 | use embassy_stm32::rng::Rng; | ||
| 10 | use embassy_stm32::{Config, Peripherals}; | ||
| 11 | use panic_probe as _; | ||
| 12 | |||
| 13 | fn config() -> Config { | ||
| 14 | let mut config = Config::default(); | ||
| 15 | config.rcc.mux = ClockSrc::PLL( | ||
| 16 | PLLSource::HSI16, | ||
| 17 | PLLClkDiv::Div2, | ||
| 18 | PLLSrcDiv::Div1, | ||
| 19 | PLLMul::Mul8, | ||
| 20 | Some(PLLClkDiv::Div2), | ||
| 21 | ); | ||
| 22 | config | ||
| 23 | } | ||
| 24 | |||
| 25 | #[embassy::main(config = "config()")] | ||
| 26 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 27 | info!("Hello World!"); | ||
| 28 | |||
| 29 | let mut rng = Rng::new(p.RNG); | ||
| 30 | |||
| 31 | let mut buf = [0u8; 16]; | ||
| 32 | unwrap!(rng.async_fill_bytes(&mut buf).await); | ||
| 33 | info!("random bytes: {:02x}", buf); | ||
| 34 | } | ||
diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs new file mode 100644 index 000000000..fa445eece --- /dev/null +++ b/examples/stm32l5/src/bin/usb_ethernet.rs | |||
| @@ -0,0 +1,290 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(generic_associated_types)] | ||
| 4 | #![feature(type_alias_impl_trait)] | ||
| 5 | |||
| 6 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 7 | use core::task::Waker; | ||
| 8 | use defmt::*; | ||
| 9 | use defmt_rtt as _; // global logger | ||
| 10 | use embassy::blocking_mutex::raw::ThreadModeRawMutex; | ||
| 11 | use embassy::channel::Channel; | ||
| 12 | use embassy::executor::Spawner; | ||
| 13 | use embassy::util::Forever; | ||
| 14 | use embassy_net::tcp::TcpSocket; | ||
| 15 | use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; | ||
| 16 | use embassy_stm32::interrupt; | ||
| 17 | use embassy_stm32::rcc::*; | ||
| 18 | use embassy_stm32::rng::Rng; | ||
| 19 | use embassy_stm32::time::Hertz; | ||
| 20 | use embassy_stm32::usb::Driver; | ||
| 21 | use embassy_stm32::{Config, Peripherals}; | ||
| 22 | use embassy_usb::{Builder, UsbDevice}; | ||
| 23 | use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; | ||
| 24 | use panic_probe as _; | ||
| 25 | |||
| 26 | use defmt_rtt as _; | ||
| 27 | use embedded_io::asynch::{Read, Write}; | ||
| 28 | // global logger | ||
| 29 | use panic_probe as _; | ||
| 30 | use rand_core::RngCore; | ||
| 31 | |||
| 32 | type MyDriver = Driver<'static, embassy_stm32::peripherals::USB>; | ||
| 33 | |||
| 34 | macro_rules! forever { | ||
| 35 | ($val:expr) => {{ | ||
| 36 | type T = impl Sized; | ||
| 37 | static FOREVER: Forever<T> = Forever::new(); | ||
| 38 | FOREVER.put_with(move || $val) | ||
| 39 | }}; | ||
| 40 | } | ||
| 41 | |||
| 42 | #[embassy::task] | ||
| 43 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { | ||
| 44 | device.run().await | ||
| 45 | } | ||
| 46 | |||
| 47 | #[embassy::task] | ||
| 48 | async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) { | ||
| 49 | loop { | ||
| 50 | warn!("WAITING for connection"); | ||
| 51 | LINK_UP.store(false, Ordering::Relaxed); | ||
| 52 | |||
| 53 | class.wait_connection().await.unwrap(); | ||
| 54 | |||
| 55 | warn!("Connected"); | ||
| 56 | LINK_UP.store(true, Ordering::Relaxed); | ||
| 57 | |||
| 58 | loop { | ||
| 59 | let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new())); | ||
| 60 | let n = match class.read_packet(&mut p[..]).await { | ||
| 61 | Ok(n) => n, | ||
| 62 | Err(e) => { | ||
| 63 | warn!("error reading packet: {:?}", e); | ||
| 64 | break; | ||
| 65 | } | ||
| 66 | }; | ||
| 67 | |||
| 68 | let buf = p.slice(0..n); | ||
| 69 | if RX_CHANNEL.try_send(buf).is_err() { | ||
| 70 | warn!("Failed pushing rx'd packet to channel."); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | #[embassy::task] | ||
| 77 | async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { | ||
| 78 | loop { | ||
| 79 | let pkt = TX_CHANNEL.recv().await; | ||
| 80 | if let Err(e) = class.write_packet(&pkt[..]).await { | ||
| 81 | warn!("Failed to TX packet: {:?}", e); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | #[embassy::task] | ||
| 87 | async fn net_task(stack: &'static Stack<Device>) -> ! { | ||
| 88 | stack.run().await | ||
| 89 | } | ||
| 90 | |||
| 91 | fn config() -> Config { | ||
| 92 | let mut config = Config::default(); | ||
| 93 | config.rcc.mux = ClockSrc::HSE(Hertz(16_000_000)); | ||
| 94 | |||
| 95 | config.rcc.mux = ClockSrc::PLL( | ||
| 96 | PLLSource::HSI16, | ||
| 97 | PLLClkDiv::Div2, | ||
| 98 | PLLSrcDiv::Div1, | ||
| 99 | PLLMul::Mul10, | ||
| 100 | None, | ||
| 101 | ); | ||
| 102 | config.rcc.hsi48 = true; | ||
| 103 | |||
| 104 | config | ||
| 105 | } | ||
| 106 | |||
| 107 | #[embassy::main(config = "config()")] | ||
| 108 | async fn main(spawner: Spawner, p: Peripherals) { | ||
| 109 | // Create the driver, from the HAL. | ||
| 110 | let irq = interrupt::take!(USB_FS); | ||
| 111 | let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); | ||
| 112 | |||
| 113 | // Create embassy-usb Config | ||
| 114 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 115 | config.manufacturer = Some("Embassy"); | ||
| 116 | config.product = Some("USB-Ethernet example"); | ||
| 117 | config.serial_number = Some("12345678"); | ||
| 118 | config.max_power = 100; | ||
| 119 | config.max_packet_size_0 = 64; | ||
| 120 | |||
| 121 | // Required for Windows support. | ||
| 122 | config.composite_with_iads = true; | ||
| 123 | config.device_class = 0xEF; | ||
| 124 | config.device_sub_class = 0x02; | ||
| 125 | config.device_protocol = 0x01; | ||
| 126 | |||
| 127 | struct Resources { | ||
| 128 | device_descriptor: [u8; 256], | ||
| 129 | config_descriptor: [u8; 256], | ||
| 130 | bos_descriptor: [u8; 256], | ||
| 131 | control_buf: [u8; 128], | ||
| 132 | serial_state: State<'static>, | ||
| 133 | } | ||
| 134 | let res: &mut Resources = forever!(Resources { | ||
| 135 | device_descriptor: [0; 256], | ||
| 136 | config_descriptor: [0; 256], | ||
| 137 | bos_descriptor: [0; 256], | ||
| 138 | control_buf: [0; 128], | ||
| 139 | serial_state: State::new(), | ||
| 140 | }); | ||
| 141 | |||
| 142 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 143 | let mut builder = Builder::new( | ||
| 144 | driver, | ||
| 145 | config, | ||
| 146 | &mut res.device_descriptor, | ||
| 147 | &mut res.config_descriptor, | ||
| 148 | &mut res.bos_descriptor, | ||
| 149 | &mut res.control_buf, | ||
| 150 | None, | ||
| 151 | ); | ||
| 152 | |||
| 153 | // WARNINGS for Android ethernet tethering: | ||
| 154 | // - On Pixel 4a, it refused to work on Android 11, worked on Android 12. | ||
| 155 | // - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte), | ||
| 156 | // it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled. | ||
| 157 | // This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417 | ||
| 158 | // and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757 | ||
| 159 | |||
| 160 | // Our MAC addr. | ||
| 161 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; | ||
| 162 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. | ||
| 163 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; | ||
| 164 | |||
| 165 | // Create classes on the builder. | ||
| 166 | let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64); | ||
| 167 | |||
| 168 | // Build the builder. | ||
| 169 | let usb = builder.build(); | ||
| 170 | |||
| 171 | unwrap!(spawner.spawn(usb_task(usb))); | ||
| 172 | |||
| 173 | let (tx, rx) = class.split(); | ||
| 174 | unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); | ||
| 175 | unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); | ||
| 176 | |||
| 177 | let config = embassy_net::ConfigStrategy::Dhcp; | ||
| 178 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { | ||
| 179 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | ||
| 180 | // dns_servers: Vec::new(), | ||
| 181 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | ||
| 182 | //}); | ||
| 183 | |||
| 184 | // Generate random seed | ||
| 185 | let mut rng = Rng::new(p.RNG); | ||
| 186 | let seed = rng.next_u64(); | ||
| 187 | |||
| 188 | // Init network stack | ||
| 189 | let device = Device { | ||
| 190 | mac_addr: our_mac_addr, | ||
| 191 | }; | ||
| 192 | let stack = &*forever!(Stack::new( | ||
| 193 | device, | ||
| 194 | config, | ||
| 195 | forever!(StackResources::<1, 2, 8>::new()), | ||
| 196 | seed | ||
| 197 | )); | ||
| 198 | |||
| 199 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 200 | |||
| 201 | // And now we can use it! | ||
| 202 | |||
| 203 | let mut rx_buffer = [0; 4096]; | ||
| 204 | let mut tx_buffer = [0; 4096]; | ||
| 205 | let mut buf = [0; 4096]; | ||
| 206 | |||
| 207 | loop { | ||
| 208 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 209 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); | ||
| 210 | |||
| 211 | info!("Listening on TCP:1234..."); | ||
| 212 | if let Err(e) = socket.accept(1234).await { | ||
| 213 | warn!("accept error: {:?}", e); | ||
| 214 | continue; | ||
| 215 | } | ||
| 216 | |||
| 217 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 218 | |||
| 219 | loop { | ||
| 220 | let n = match socket.read(&mut buf).await { | ||
| 221 | Ok(0) => { | ||
| 222 | warn!("read EOF"); | ||
| 223 | break; | ||
| 224 | } | ||
| 225 | Ok(n) => n, | ||
| 226 | Err(e) => { | ||
| 227 | warn!("read error: {:?}", e); | ||
| 228 | break; | ||
| 229 | } | ||
| 230 | }; | ||
| 231 | |||
| 232 | info!("rxd {:02x}", &buf[..n]); | ||
| 233 | |||
| 234 | match socket.write_all(&buf[..n]).await { | ||
| 235 | Ok(()) => {} | ||
| 236 | Err(e) => { | ||
| 237 | warn!("write error: {:?}", e); | ||
| 238 | break; | ||
| 239 | } | ||
| 240 | }; | ||
| 241 | } | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | static TX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 246 | static RX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new(); | ||
| 247 | static LINK_UP: AtomicBool = AtomicBool::new(false); | ||
| 248 | |||
| 249 | struct Device { | ||
| 250 | mac_addr: [u8; 6], | ||
| 251 | } | ||
| 252 | |||
| 253 | impl embassy_net::Device for Device { | ||
| 254 | fn register_waker(&mut self, waker: &Waker) { | ||
| 255 | // loopy loopy wakey wakey | ||
| 256 | waker.wake_by_ref() | ||
| 257 | } | ||
| 258 | |||
| 259 | fn link_state(&mut self) -> embassy_net::LinkState { | ||
| 260 | match LINK_UP.load(Ordering::Relaxed) { | ||
| 261 | true => embassy_net::LinkState::Up, | ||
| 262 | false => embassy_net::LinkState::Down, | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | fn capabilities(&self) -> embassy_net::DeviceCapabilities { | ||
| 267 | let mut caps = embassy_net::DeviceCapabilities::default(); | ||
| 268 | caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header | ||
| 269 | caps.medium = embassy_net::Medium::Ethernet; | ||
| 270 | caps | ||
| 271 | } | ||
| 272 | |||
| 273 | fn is_transmit_ready(&mut self) -> bool { | ||
| 274 | true | ||
| 275 | } | ||
| 276 | |||
| 277 | fn transmit(&mut self, pkt: PacketBuf) { | ||
| 278 | if TX_CHANNEL.try_send(pkt).is_err() { | ||
| 279 | warn!("TX failed") | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | fn receive<'a>(&mut self) -> Option<PacketBuf> { | ||
| 284 | RX_CHANNEL.try_recv().ok() | ||
| 285 | } | ||
| 286 | |||
| 287 | fn ethernet_address(&self) -> [u8; 6] { | ||
| 288 | self.mac_addr | ||
| 289 | } | ||
| 290 | } | ||
diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs new file mode 100644 index 000000000..d275aba36 --- /dev/null +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(generic_associated_types)] | ||
| 4 | #![feature(type_alias_impl_trait)] | ||
| 5 | |||
| 6 | use defmt::*; | ||
| 7 | use embassy::executor::Spawner; | ||
| 8 | use embassy::time::{Duration, Timer}; | ||
| 9 | use embassy_stm32::interrupt; | ||
| 10 | use embassy_stm32::rcc::*; | ||
| 11 | use embassy_stm32::time::Hertz; | ||
| 12 | use embassy_stm32::usb::Driver; | ||
| 13 | use embassy_stm32::{Config, Peripherals}; | ||
| 14 | use embassy_usb::control::OutResponse; | ||
| 15 | use embassy_usb::Builder; | ||
| 16 | use embassy_usb_hid::{HidWriter, ReportId, RequestHandler, State}; | ||
| 17 | use futures::future::join; | ||
| 18 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; | ||
| 19 | |||
| 20 | use defmt_rtt as _; // global logger | ||
| 21 | use panic_probe as _; | ||
| 22 | |||
| 23 | fn config() -> Config { | ||
| 24 | let mut config = Config::default(); | ||
| 25 | config.rcc.mux = ClockSrc::HSE(Hertz(16_000_000)); | ||
| 26 | |||
| 27 | config.rcc.mux = ClockSrc::PLL( | ||
| 28 | PLLSource::HSI16, | ||
| 29 | PLLClkDiv::Div2, | ||
| 30 | PLLSrcDiv::Div1, | ||
| 31 | PLLMul::Mul10, | ||
| 32 | None, | ||
| 33 | ); | ||
| 34 | config.rcc.hsi48 = true; | ||
| 35 | |||
| 36 | config | ||
| 37 | } | ||
| 38 | |||
| 39 | #[embassy::main(config = "config()")] | ||
| 40 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 41 | // Create the driver, from the HAL. | ||
| 42 | let irq = interrupt::take!(USB_FS); | ||
| 43 | let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); | ||
| 44 | |||
| 45 | // Create embassy-usb Config | ||
| 46 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 47 | config.manufacturer = Some("Embassy"); | ||
| 48 | config.product = Some("HID mouse example"); | ||
| 49 | config.serial_number = Some("12345678"); | ||
| 50 | config.max_power = 100; | ||
| 51 | config.max_packet_size_0 = 64; | ||
| 52 | |||
| 53 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 54 | // It needs some buffers for building the descriptors. | ||
| 55 | let mut device_descriptor = [0; 256]; | ||
| 56 | let mut config_descriptor = [0; 256]; | ||
| 57 | let mut bos_descriptor = [0; 256]; | ||
| 58 | let mut control_buf = [0; 64]; | ||
| 59 | let request_handler = MyRequestHandler {}; | ||
| 60 | |||
| 61 | let mut state = State::new(); | ||
| 62 | |||
| 63 | let mut builder = Builder::new( | ||
| 64 | driver, | ||
| 65 | config, | ||
| 66 | &mut device_descriptor, | ||
| 67 | &mut config_descriptor, | ||
| 68 | &mut bos_descriptor, | ||
| 69 | &mut control_buf, | ||
| 70 | None, | ||
| 71 | ); | ||
| 72 | |||
| 73 | // Create classes on the builder. | ||
| 74 | let config = embassy_usb_hid::Config { | ||
| 75 | report_descriptor: MouseReport::desc(), | ||
| 76 | request_handler: Some(&request_handler), | ||
| 77 | poll_ms: 60, | ||
| 78 | max_packet_size: 8, | ||
| 79 | }; | ||
| 80 | |||
| 81 | let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); | ||
| 82 | |||
| 83 | // Build the builder. | ||
| 84 | let mut usb = builder.build(); | ||
| 85 | |||
| 86 | // Run the USB device. | ||
| 87 | let usb_fut = usb.run(); | ||
| 88 | |||
| 89 | // Do stuff with the class! | ||
| 90 | let hid_fut = async { | ||
| 91 | let mut y: i8 = 5; | ||
| 92 | loop { | ||
| 93 | Timer::after(Duration::from_millis(500)).await; | ||
| 94 | |||
| 95 | y = -y; | ||
| 96 | let report = MouseReport { | ||
| 97 | buttons: 0, | ||
| 98 | x: 0, | ||
| 99 | y, | ||
| 100 | wheel: 0, | ||
| 101 | pan: 0, | ||
| 102 | }; | ||
| 103 | match writer.write_serialize(&report).await { | ||
| 104 | Ok(()) => {} | ||
| 105 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 106 | } | ||
| 107 | } | ||
| 108 | }; | ||
| 109 | |||
| 110 | // Run everything concurrently. | ||
| 111 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 112 | join(usb_fut, hid_fut).await; | ||
| 113 | } | ||
| 114 | |||
| 115 | struct MyRequestHandler {} | ||
| 116 | |||
| 117 | impl RequestHandler for MyRequestHandler { | ||
| 118 | fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option<usize> { | ||
| 119 | info!("Get report for {:?}", id); | ||
| 120 | None | ||
| 121 | } | ||
| 122 | |||
| 123 | fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse { | ||
| 124 | info!("Set report for {:?}: {=[u8]}", id, data); | ||
| 125 | OutResponse::Accepted | ||
| 126 | } | ||
| 127 | |||
| 128 | fn set_idle(&self, id: Option<ReportId>, dur: Duration) { | ||
| 129 | info!("Set idle rate for {:?} to {:?}", id, dur); | ||
| 130 | } | ||
| 131 | |||
| 132 | fn get_idle(&self, id: Option<ReportId>) -> Option<Duration> { | ||
| 133 | info!("Get idle rate for {:?}", id); | ||
| 134 | None | ||
| 135 | } | ||
| 136 | } | ||
diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs new file mode 100644 index 000000000..987f1b692 --- /dev/null +++ b/examples/stm32l5/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::panic; | ||
| 6 | use defmt::*; | ||
| 7 | use defmt_rtt as _; // global logger | ||
| 8 | use embassy::executor::Spawner; | ||
| 9 | use embassy_stm32::interrupt; | ||
| 10 | use embassy_stm32::rcc::*; | ||
| 11 | use embassy_stm32::time::Hertz; | ||
| 12 | use embassy_stm32::usb::{Driver, Instance}; | ||
| 13 | use embassy_stm32::{Config, Peripherals}; | ||
| 14 | use embassy_usb::driver::EndpointError; | ||
| 15 | use embassy_usb::Builder; | ||
| 16 | use embassy_usb_serial::{CdcAcmClass, State}; | ||
| 17 | use futures::future::join; | ||
| 18 | use panic_probe as _; | ||
| 19 | |||
| 20 | fn config() -> Config { | ||
| 21 | let mut config = Config::default(); | ||
| 22 | config.rcc.mux = ClockSrc::HSE(Hertz(16_000_000)); | ||
| 23 | |||
| 24 | config.rcc.mux = ClockSrc::PLL( | ||
| 25 | PLLSource::HSI16, | ||
| 26 | PLLClkDiv::Div2, | ||
| 27 | PLLSrcDiv::Div1, | ||
| 28 | PLLMul::Mul10, | ||
| 29 | None, | ||
| 30 | ); | ||
| 31 | config.rcc.hsi48 = true; | ||
| 32 | |||
| 33 | config | ||
| 34 | } | ||
| 35 | |||
| 36 | #[embassy::main(config = "config()")] | ||
| 37 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 38 | info!("Hello World!"); | ||
| 39 | |||
| 40 | // Create the driver, from the HAL. | ||
| 41 | let irq = interrupt::take!(USB_FS); | ||
| 42 | let driver = Driver::new(p.USB, irq, p.PA12, p.PA11); | ||
| 43 | |||
| 44 | // Create embassy-usb Config | ||
| 45 | let config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 46 | //config.max_packet_size_0 = 64; | ||
| 47 | |||
| 48 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 49 | // It needs some buffers for building the descriptors. | ||
| 50 | let mut device_descriptor = [0; 256]; | ||
| 51 | let mut config_descriptor = [0; 256]; | ||
| 52 | let mut bos_descriptor = [0; 256]; | ||
| 53 | let mut control_buf = [0; 7]; | ||
| 54 | |||
| 55 | let mut state = State::new(); | ||
| 56 | |||
| 57 | let mut builder = Builder::new( | ||
| 58 | driver, | ||
| 59 | config, | ||
| 60 | &mut device_descriptor, | ||
| 61 | &mut config_descriptor, | ||
| 62 | &mut bos_descriptor, | ||
| 63 | &mut control_buf, | ||
| 64 | None, | ||
| 65 | ); | ||
| 66 | |||
| 67 | // Create classes on the builder. | ||
| 68 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 69 | |||
| 70 | // Build the builder. | ||
| 71 | let mut usb = builder.build(); | ||
| 72 | |||
| 73 | // Run the USB device. | ||
| 74 | let usb_fut = usb.run(); | ||
| 75 | |||
| 76 | // Do stuff with the class! | ||
| 77 | let echo_fut = async { | ||
| 78 | loop { | ||
| 79 | class.wait_connection().await; | ||
| 80 | info!("Connected"); | ||
| 81 | let _ = echo(&mut class).await; | ||
| 82 | info!("Disconnected"); | ||
| 83 | } | ||
| 84 | }; | ||
| 85 | |||
| 86 | // Run everything concurrently. | ||
| 87 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 88 | join(usb_fut, echo_fut).await; | ||
| 89 | } | ||
| 90 | |||
| 91 | struct Disconnected {} | ||
| 92 | |||
| 93 | impl From<EndpointError> for Disconnected { | ||
| 94 | fn from(val: EndpointError) -> Self { | ||
| 95 | match val { | ||
| 96 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 97 | EndpointError::Disabled => Disconnected {}, | ||
| 98 | } | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | async fn echo<'d, T: Instance + 'd>( | ||
| 103 | class: &mut CdcAcmClass<'d, Driver<'d, T>>, | ||
| 104 | ) -> Result<(), Disconnected> { | ||
| 105 | let mut buf = [0; 64]; | ||
| 106 | loop { | ||
| 107 | let n = class.read_packet(&mut buf).await?; | ||
| 108 | let data = &buf[..n]; | ||
| 109 | info!("data: {:x}", data); | ||
| 110 | class.write_packet(data).await?; | ||
| 111 | } | ||
| 112 | } | ||
diff --git a/stm32-data b/stm32-data | |||
| Subproject aa2e63996c0fe35d680c9c48917c07b042905e4 | Subproject fa294eae79c0f33f4cde1e73b4e69db59f7429e | ||
