diff options
30 files changed, 4852 insertions, 188 deletions
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f1256320d..6115e618d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml | |||
| @@ -37,7 +37,7 @@ jobs: | |||
| 37 | key: rust3-${{ runner.os }}-${{ hashFiles('rust-toolchain.toml') }} | 37 | key: rust3-${{ runner.os }}-${{ hashFiles('rust-toolchain.toml') }} |
| 38 | - name: build | 38 | - name: build |
| 39 | run: | | 39 | run: | |
| 40 | curl -L -o /usr/local/bin/cargo-batch https://github.com/embassy-rs/cargo-batch/releases/download/batch-0.2.0/cargo-batch | 40 | curl -L -o /usr/local/bin/cargo-batch https://github.com/embassy-rs/cargo-batch/releases/download/batch-0.3.0/cargo-batch |
| 41 | chmod +x /usr/local/bin/cargo-batch | 41 | chmod +x /usr/local/bin/cargo-batch |
| 42 | ./ci.sh | 42 | ./ci.sh |
| 43 | rm -rf target_ci/*{,/release}/{build,deps,.fingerprint}/{lib,}{embassy,stm32}* | 43 | rm -rf target_ci/*{,/release}/{build,deps,.fingerprint}/{lib,}{embassy,stm32}* |
| @@ -59,7 +59,7 @@ jobs: | |||
| 59 | key: rust-stable-${{ runner.os }}-${{ hashFiles('rust-toolchain.toml') }} | 59 | key: rust-stable-${{ runner.os }}-${{ hashFiles('rust-toolchain.toml') }} |
| 60 | - name: build | 60 | - name: build |
| 61 | run: | | 61 | run: | |
| 62 | curl -L -o /usr/local/bin/cargo-batch https://github.com/embassy-rs/cargo-batch/releases/download/batch-0.1.0/cargo-batch | 62 | curl -L -o /usr/local/bin/cargo-batch https://github.com/embassy-rs/cargo-batch/releases/download/batch-0.3.0/cargo-batch |
| 63 | chmod +x /usr/local/bin/cargo-batch | 63 | chmod +x /usr/local/bin/cargo-batch |
| 64 | ./ci_stable.sh | 64 | ./ci_stable.sh |
| 65 | rm -rf target_ci_stable/*{,/release}/{build,deps,.fingerprint}/{lib,}{embassy,stm32}* | 65 | rm -rf target_ci_stable/*{,/release}/{build,deps,.fingerprint}/{lib,}{embassy,stm32}* |
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 5e69a8878..858ff1f6e 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml | |||
| @@ -18,7 +18,7 @@ flavors = [ | |||
| 18 | [features] | 18 | [features] |
| 19 | 19 | ||
| 20 | # Enable nightly-only features | 20 | # Enable nightly-only features |
| 21 | nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async"] | 21 | nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-usb"] |
| 22 | 22 | ||
| 23 | # Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`. | 23 | # Reexport the PAC for the currently enabled chip at `embassy_nrf::pac`. |
| 24 | # This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version. | 24 | # This is unstable because semver-minor (non-breaking) releases of embassy-nrf may major-bump (breaking) the PAC version. |
| @@ -64,6 +64,7 @@ _gpio-p1 = [] | |||
| 64 | embassy = { version = "0.1.0", path = "../embassy" } | 64 | embassy = { version = "0.1.0", path = "../embassy" } |
| 65 | embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["nrf"]} | 65 | embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["nrf"]} |
| 66 | embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } | 66 | embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } |
| 67 | embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional=true } | ||
| 67 | 68 | ||
| 68 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } | 69 | embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } |
| 69 | embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.7", git = "https://github.com/embassy-rs/embedded-hal", branch = "embassy2", optional = true} | 70 | embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.7", git = "https://github.com/embassy-rs/embedded-hal", branch = "embassy2", optional = true} |
| @@ -80,8 +81,6 @@ rand_core = "0.6.3" | |||
| 80 | fixed = "1.10.0" | 81 | fixed = "1.10.0" |
| 81 | embedded-storage = "0.3.0" | 82 | embedded-storage = "0.3.0" |
| 82 | cfg-if = "1.0.0" | 83 | cfg-if = "1.0.0" |
| 83 | nrf-usbd = {version = "0.1.1"} | ||
| 84 | usb-device = "0.2.8" | ||
| 85 | 84 | ||
| 86 | nrf52805-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } | 85 | nrf52805-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } |
| 87 | nrf52810-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } | 86 | nrf52810-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } |
diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index aa2b2e61d..136ef4ec9 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs | |||
| @@ -125,6 +125,7 @@ embassy_hal_common::peripherals! { | |||
| 125 | TEMP, | 125 | TEMP, |
| 126 | } | 126 | } |
| 127 | 127 | ||
| 128 | #[cfg(feature = "nightly")] | ||
| 128 | impl_usb!(USBD, USBD, USBD); | 129 | impl_usb!(USBD, USBD, USBD); |
| 129 | 130 | ||
| 130 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); | 131 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); |
diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 498a3c307..35cf4224d 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs | |||
| @@ -157,6 +157,7 @@ embassy_hal_common::peripherals! { | |||
| 157 | TEMP, | 157 | TEMP, |
| 158 | } | 158 | } |
| 159 | 159 | ||
| 160 | #[cfg(feature = "nightly")] | ||
| 160 | impl_usb!(USBD, USBD, USBD); | 161 | impl_usb!(USBD, USBD, USBD); |
| 161 | 162 | ||
| 162 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); | 163 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); |
diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 411768146..d20abbfbd 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs | |||
| @@ -160,6 +160,7 @@ embassy_hal_common::peripherals! { | |||
| 160 | TEMP, | 160 | TEMP, |
| 161 | } | 161 | } |
| 162 | 162 | ||
| 163 | #[cfg(feature = "nightly")] | ||
| 163 | impl_usb!(USBD, USBD, USBD); | 164 | impl_usb!(USBD, USBD, USBD); |
| 164 | 165 | ||
| 165 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); | 166 | impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); |
diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index ae6887b3c..89579b69f 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs | |||
| @@ -348,6 +348,7 @@ embassy_hal_common::peripherals! { | |||
| 348 | P1_15, | 348 | P1_15, |
| 349 | } | 349 | } |
| 350 | 350 | ||
| 351 | #[cfg(feature = "nightly")] | ||
| 351 | impl_usb!(USBD, USBD, USBD); | 352 | impl_usb!(USBD, USBD, USBD); |
| 352 | 353 | ||
| 353 | impl_uarte!(UARTETWISPI0, UARTE0, SERIAL0); | 354 | impl_uarte!(UARTETWISPI0, UARTE0, SERIAL0); |
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 0004eb583..3b1809023 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs | |||
| @@ -91,6 +91,7 @@ pub mod uarte; | |||
| 91 | feature = "nrf52833", | 91 | feature = "nrf52833", |
| 92 | feature = "nrf52840" | 92 | feature = "nrf52840" |
| 93 | ))] | 93 | ))] |
| 94 | #[cfg(feature = "nightly")] | ||
| 94 | pub mod usb; | 95 | pub mod usb; |
| 95 | #[cfg(not(feature = "_nrf5340"))] | 96 | #[cfg(not(feature = "_nrf5340"))] |
| 96 | pub mod wdt; | 97 | pub mod wdt; |
diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index deab94544..9f483d96e 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs | |||
| @@ -1,43 +1,810 @@ | |||
| 1 | #![macro_use] | 1 | #![macro_use] |
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | ||
| 4 | use core::mem::MaybeUninit; | ||
| 5 | use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; | ||
| 6 | use core::task::Poll; | ||
| 7 | use cortex_m::peripheral::NVIC; | ||
| 8 | use embassy::interrupt::InterruptExt; | ||
| 9 | use embassy::util::Unborrow; | ||
| 10 | use embassy::waitqueue::AtomicWaker; | ||
| 11 | use embassy_hal_common::unborrow; | ||
| 12 | use embassy_usb::control::Request; | ||
| 13 | use embassy_usb::driver::{self, Event, ReadError, WriteError}; | ||
| 14 | use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection}; | ||
| 15 | use futures::future::poll_fn; | ||
| 16 | use futures::Future; | ||
| 17 | |||
| 18 | pub use embassy_usb; | ||
| 19 | use pac::usbd::RegisterBlock; | ||
| 20 | |||
| 3 | use crate::interrupt::Interrupt; | 21 | use crate::interrupt::Interrupt; |
| 4 | use crate::pac; | 22 | use crate::pac; |
| 23 | use crate::util::slice_in_ram; | ||
| 5 | 24 | ||
| 6 | use core::marker::PhantomData; | 25 | const NEW_AW: AtomicWaker = AtomicWaker::new(); |
| 7 | use embassy::util::Unborrow; | 26 | static BUS_WAKER: AtomicWaker = NEW_AW; |
| 8 | use nrf_usbd::{UsbPeripheral, Usbd}; | 27 | static EP0_WAKER: AtomicWaker = NEW_AW; |
| 9 | use usb_device::bus::UsbBusAllocator; | 28 | static EP_IN_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; |
| 29 | static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; | ||
| 30 | static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0); | ||
| 31 | |||
| 32 | pub struct Driver<'d, T: Instance> { | ||
| 33 | phantom: PhantomData<&'d mut T>, | ||
| 34 | alloc_in: Allocator, | ||
| 35 | alloc_out: Allocator, | ||
| 36 | } | ||
| 37 | |||
| 38 | impl<'d, T: Instance> Driver<'d, T> { | ||
| 39 | pub fn new( | ||
| 40 | _usb: impl Unborrow<Target = T> + 'd, | ||
| 41 | irq: impl Unborrow<Target = T::Interrupt> + 'd, | ||
| 42 | ) -> Self { | ||
| 43 | unborrow!(irq); | ||
| 44 | irq.set_handler(Self::on_interrupt); | ||
| 45 | irq.unpend(); | ||
| 46 | irq.enable(); | ||
| 47 | |||
| 48 | Self { | ||
| 49 | phantom: PhantomData, | ||
| 50 | alloc_in: Allocator::new(), | ||
| 51 | alloc_out: Allocator::new(), | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | fn on_interrupt(_: *mut ()) { | ||
| 56 | let regs = T::regs(); | ||
| 57 | |||
| 58 | if regs.events_usbreset.read().bits() != 0 { | ||
| 59 | regs.intenclr.write(|w| w.usbreset().clear()); | ||
| 60 | BUS_WAKER.wake(); | ||
| 61 | EP0_WAKER.wake(); | ||
| 62 | } | ||
| 63 | |||
| 64 | if regs.events_ep0setup.read().bits() != 0 { | ||
| 65 | regs.intenclr.write(|w| w.ep0setup().clear()); | ||
| 66 | EP0_WAKER.wake(); | ||
| 67 | } | ||
| 68 | |||
| 69 | if regs.events_ep0datadone.read().bits() != 0 { | ||
| 70 | regs.intenclr.write(|w| w.ep0datadone().clear()); | ||
| 71 | EP0_WAKER.wake(); | ||
| 72 | } | ||
| 73 | |||
| 74 | // USBEVENT and EPDATA events are weird. They're the "aggregate" | ||
| 75 | // of individual bits in EVENTCAUSE and EPDATASTATUS. We handle them | ||
| 76 | // differently than events normally. | ||
| 77 | // | ||
| 78 | // They seem to be edge-triggered, not level-triggered: when an | ||
| 79 | // individual bit goes 0->1, the event fires *just once*. | ||
| 80 | // Therefore, it's fine to clear just the event, and let main thread | ||
| 81 | // check the individual bits in EVENTCAUSE and EPDATASTATUS. It | ||
| 82 | // doesn't cause an infinite irq loop. | ||
| 83 | if regs.events_usbevent.read().bits() != 0 { | ||
| 84 | regs.events_usbevent.reset(); | ||
| 85 | //regs.intenclr.write(|w| w.usbevent().clear()); | ||
| 86 | BUS_WAKER.wake(); | ||
| 87 | } | ||
| 88 | |||
| 89 | if regs.events_epdata.read().bits() != 0 { | ||
| 90 | regs.events_epdata.reset(); | ||
| 91 | |||
| 92 | let r = regs.epdatastatus.read().bits(); | ||
| 93 | regs.epdatastatus.write(|w| unsafe { w.bits(r) }); | ||
| 94 | READY_ENDPOINTS.fetch_or(r, Ordering::AcqRel); | ||
| 95 | for i in 1..=7 { | ||
| 96 | if r & In::mask(i) != 0 { | ||
| 97 | In::waker(i).wake(); | ||
| 98 | } | ||
| 99 | if r & Out::mask(i) != 0 { | ||
| 100 | Out::waker(i).wake(); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | fn set_stalled(ep_addr: EndpointAddress, stalled: bool) { | ||
| 107 | let regs = T::regs(); | ||
| 108 | |||
| 109 | unsafe { | ||
| 110 | if ep_addr.index() == 0 { | ||
| 111 | regs.tasks_ep0stall | ||
| 112 | .write(|w| w.tasks_ep0stall().bit(stalled)); | ||
| 113 | } else { | ||
| 114 | regs.epstall.write(|w| { | ||
| 115 | w.ep().bits(ep_addr.index() as u8 & 0b111); | ||
| 116 | w.io().bit(ep_addr.is_in()); | ||
| 117 | w.stall().bit(stalled) | ||
| 118 | }); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | //if stalled { | ||
| 123 | // self.busy_in_endpoints &= !(1 << ep_addr.index()); | ||
| 124 | //} | ||
| 125 | } | ||
| 126 | |||
| 127 | fn is_stalled(ep_addr: EndpointAddress) -> bool { | ||
| 128 | let regs = T::regs(); | ||
| 129 | |||
| 130 | let i = ep_addr.index(); | ||
| 131 | match ep_addr.direction() { | ||
| 132 | UsbDirection::Out => regs.halted.epout[i].read().getstatus().is_halted(), | ||
| 133 | UsbDirection::In => regs.halted.epin[i].read().getstatus().is_halted(), | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { | ||
| 139 | type EndpointOut = Endpoint<'d, T, Out>; | ||
| 140 | type EndpointIn = Endpoint<'d, T, In>; | ||
| 141 | type ControlPipe = ControlPipe<'d, T>; | ||
| 142 | type Bus = Bus<'d, T>; | ||
| 143 | type EnableFuture = impl Future<Output = Self::Bus> + 'd; | ||
| 144 | |||
| 145 | fn alloc_endpoint_in( | ||
| 146 | &mut self, | ||
| 147 | ep_addr: Option<EndpointAddress>, | ||
| 148 | ep_type: EndpointType, | ||
| 149 | max_packet_size: u16, | ||
| 150 | interval: u8, | ||
| 151 | ) -> Result<Self::EndpointIn, driver::EndpointAllocError> { | ||
| 152 | let index = self | ||
| 153 | .alloc_in | ||
| 154 | .allocate(ep_addr, ep_type, max_packet_size, interval)?; | ||
| 155 | let ep_addr = EndpointAddress::from_parts(index, UsbDirection::In); | ||
| 156 | Ok(Endpoint::new(EndpointInfo { | ||
| 157 | addr: ep_addr, | ||
| 158 | ep_type, | ||
| 159 | max_packet_size, | ||
| 160 | interval, | ||
| 161 | })) | ||
| 162 | } | ||
| 163 | |||
| 164 | fn alloc_endpoint_out( | ||
| 165 | &mut self, | ||
| 166 | ep_addr: Option<EndpointAddress>, | ||
| 167 | ep_type: EndpointType, | ||
| 168 | max_packet_size: u16, | ||
| 169 | interval: u8, | ||
| 170 | ) -> Result<Self::EndpointOut, driver::EndpointAllocError> { | ||
| 171 | let index = self | ||
| 172 | .alloc_out | ||
| 173 | .allocate(ep_addr, ep_type, max_packet_size, interval)?; | ||
| 174 | let ep_addr = EndpointAddress::from_parts(index, UsbDirection::Out); | ||
| 175 | Ok(Endpoint::new(EndpointInfo { | ||
| 176 | addr: ep_addr, | ||
| 177 | ep_type, | ||
| 178 | max_packet_size, | ||
| 179 | interval, | ||
| 180 | })) | ||
| 181 | } | ||
| 182 | |||
| 183 | fn alloc_control_pipe( | ||
| 184 | &mut self, | ||
| 185 | max_packet_size: u16, | ||
| 186 | ) -> Result<Self::ControlPipe, driver::EndpointAllocError> { | ||
| 187 | self.alloc_endpoint_out(Some(0x00.into()), EndpointType::Control, max_packet_size, 0)?; | ||
| 188 | self.alloc_endpoint_in(Some(0x80.into()), EndpointType::Control, max_packet_size, 0)?; | ||
| 189 | Ok(ControlPipe { | ||
| 190 | _phantom: PhantomData, | ||
| 191 | max_packet_size, | ||
| 192 | }) | ||
| 193 | } | ||
| 10 | 194 | ||
| 11 | pub use embassy_hal_common::usb::*; | 195 | fn enable(self) -> Self::EnableFuture { |
| 196 | async move { | ||
| 197 | let regs = T::regs(); | ||
| 12 | 198 | ||
| 13 | pub struct UsbBus<'d, T: Instance> { | 199 | errata::pre_enable(); |
| 200 | |||
| 201 | regs.enable.write(|w| w.enable().enabled()); | ||
| 202 | |||
| 203 | // Wait until the peripheral is ready. | ||
| 204 | regs.intenset.write(|w| w.usbevent().set_bit()); | ||
| 205 | poll_fn(|cx| { | ||
| 206 | BUS_WAKER.register(cx.waker()); | ||
| 207 | if regs.eventcause.read().ready().is_ready() { | ||
| 208 | Poll::Ready(()) | ||
| 209 | } else { | ||
| 210 | Poll::Pending | ||
| 211 | } | ||
| 212 | }) | ||
| 213 | .await; | ||
| 214 | regs.eventcause.write(|w| w.ready().set_bit()); // Write 1 to clear. | ||
| 215 | |||
| 216 | errata::post_enable(); | ||
| 217 | |||
| 218 | unsafe { NVIC::unmask(pac::Interrupt::USBD) }; | ||
| 219 | |||
| 220 | regs.intenset.write(|w| { | ||
| 221 | w.usbreset().set_bit(); | ||
| 222 | w.usbevent().set_bit(); | ||
| 223 | w.epdata().set_bit(); | ||
| 224 | w | ||
| 225 | }); | ||
| 226 | // Enable the USB pullup, allowing enumeration. | ||
| 227 | regs.usbpullup.write(|w| w.connect().enabled()); | ||
| 228 | trace!("enabled"); | ||
| 229 | |||
| 230 | Bus { | ||
| 231 | phantom: PhantomData, | ||
| 232 | alloc_in: self.alloc_in, | ||
| 233 | alloc_out: self.alloc_out, | ||
| 234 | } | ||
| 235 | } | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | pub struct Bus<'d, T: Instance> { | ||
| 14 | phantom: PhantomData<&'d mut T>, | 240 | phantom: PhantomData<&'d mut T>, |
| 241 | alloc_in: Allocator, | ||
| 242 | alloc_out: Allocator, | ||
| 15 | } | 243 | } |
| 16 | 244 | ||
| 17 | unsafe impl<'d, T: Instance> UsbPeripheral for UsbBus<'d, T> { | 245 | impl<'d, T: Instance> driver::Bus for Bus<'d, T> { |
| 18 | // todo how to use T::regs | 246 | type PollFuture<'a> = impl Future<Output = Event> + 'a where Self: 'a; |
| 19 | const REGISTERS: *const () = pac::USBD::ptr() as *const (); | 247 | |
| 248 | fn poll<'a>(&'a mut self) -> Self::PollFuture<'a> { | ||
| 249 | poll_fn(|cx| { | ||
| 250 | BUS_WAKER.register(cx.waker()); | ||
| 251 | let regs = T::regs(); | ||
| 252 | |||
| 253 | if regs.events_usbreset.read().bits() != 0 { | ||
| 254 | regs.events_usbreset.reset(); | ||
| 255 | regs.intenset.write(|w| w.usbreset().set()); | ||
| 256 | return Poll::Ready(Event::Reset); | ||
| 257 | } | ||
| 258 | |||
| 259 | let r = regs.eventcause.read(); | ||
| 260 | |||
| 261 | if r.isooutcrc().bit() { | ||
| 262 | regs.eventcause.write(|w| w.isooutcrc().set_bit()); | ||
| 263 | trace!("USB event: isooutcrc"); | ||
| 264 | } | ||
| 265 | if r.usbwuallowed().bit() { | ||
| 266 | regs.eventcause.write(|w| w.usbwuallowed().set_bit()); | ||
| 267 | trace!("USB event: usbwuallowed"); | ||
| 268 | } | ||
| 269 | if r.suspend().bit() { | ||
| 270 | regs.eventcause.write(|w| w.suspend().set_bit()); | ||
| 271 | trace!("USB event: suspend"); | ||
| 272 | } | ||
| 273 | if r.resume().bit() { | ||
| 274 | regs.eventcause.write(|w| w.resume().set_bit()); | ||
| 275 | trace!("USB event: resume"); | ||
| 276 | } | ||
| 277 | if r.ready().bit() { | ||
| 278 | regs.eventcause.write(|w| w.ready().set_bit()); | ||
| 279 | trace!("USB event: ready"); | ||
| 280 | } | ||
| 281 | |||
| 282 | Poll::Pending | ||
| 283 | }) | ||
| 284 | } | ||
| 285 | |||
| 286 | #[inline] | ||
| 287 | fn reset(&mut self) { | ||
| 288 | self.set_configured(false); | ||
| 289 | } | ||
| 290 | |||
| 291 | #[inline] | ||
| 292 | fn set_configured(&mut self, configured: bool) { | ||
| 293 | let regs = T::regs(); | ||
| 294 | |||
| 295 | unsafe { | ||
| 296 | if configured { | ||
| 297 | // TODO: Initialize ISO buffers | ||
| 298 | |||
| 299 | regs.epinen.write(|w| w.bits(self.alloc_in.used.into())); | ||
| 300 | regs.epouten.write(|w| w.bits(self.alloc_out.used.into())); | ||
| 301 | |||
| 302 | for i in 1..8 { | ||
| 303 | let out_enabled = self.alloc_out.used & (1 << i) != 0; | ||
| 304 | |||
| 305 | // when first enabled, bulk/interrupt OUT endpoints will *not* receive data (the | ||
| 306 | // peripheral will NAK all incoming packets) until we write a zero to the SIZE | ||
| 307 | // register (see figure 203 of the 52840 manual). To avoid that we write a 0 to the | ||
| 308 | // SIZE register | ||
| 309 | if out_enabled { | ||
| 310 | regs.size.epout[i].reset(); | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | // IN endpoints (low bits) default to ready. | ||
| 315 | // OUT endpoints (high bits) default to NOT ready, they become ready when data comes in. | ||
| 316 | READY_ENDPOINTS.store(0x0000FFFF, Ordering::Release); | ||
| 317 | } else { | ||
| 318 | // Disable all endpoints except EP0 | ||
| 319 | regs.epinen.write(|w| w.bits(0x01)); | ||
| 320 | regs.epouten.write(|w| w.bits(0x01)); | ||
| 321 | |||
| 322 | READY_ENDPOINTS.store(In::mask(0), Ordering::Release); | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 326 | for i in 1..=7 { | ||
| 327 | In::waker(i).wake(); | ||
| 328 | Out::waker(i).wake(); | ||
| 329 | } | ||
| 330 | } | ||
| 331 | |||
| 332 | #[inline] | ||
| 333 | fn set_device_address(&mut self, _addr: u8) { | ||
| 334 | // Nothing to do, the peripheral handles this. | ||
| 335 | } | ||
| 336 | |||
| 337 | fn set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { | ||
| 338 | Driver::<T>::set_stalled(ep_addr, stalled) | ||
| 339 | } | ||
| 340 | |||
| 341 | fn is_stalled(&mut self, ep_addr: EndpointAddress) -> bool { | ||
| 342 | Driver::<T>::is_stalled(ep_addr) | ||
| 343 | } | ||
| 344 | |||
| 345 | #[inline] | ||
| 346 | fn suspend(&mut self) { | ||
| 347 | let regs = T::regs(); | ||
| 348 | regs.lowpower.write(|w| w.lowpower().low_power()); | ||
| 349 | } | ||
| 350 | |||
| 351 | #[inline] | ||
| 352 | fn resume(&mut self) { | ||
| 353 | let regs = T::regs(); | ||
| 354 | |||
| 355 | errata::pre_wakeup(); | ||
| 356 | |||
| 357 | regs.lowpower.write(|w| w.lowpower().force_normal()); | ||
| 358 | } | ||
| 20 | } | 359 | } |
| 21 | 360 | ||
| 22 | impl<'d, T: Instance> UsbBus<'d, T> { | 361 | pub enum Out {} |
| 23 | pub fn new(_usb: impl Unborrow<Target = T> + 'd) -> UsbBusAllocator<Usbd<UsbBus<'d, T>>> { | 362 | pub enum In {} |
| 24 | let r = T::regs(); | ||
| 25 | 363 | ||
| 26 | r.intenset.write(|w| { | 364 | trait EndpointDir { |
| 27 | w.sof().set_bit(); | 365 | fn waker(i: usize) -> &'static AtomicWaker; |
| 28 | w.usbevent().set_bit(); | 366 | fn mask(i: usize) -> u32; |
| 29 | w.ep0datadone().set_bit(); | 367 | fn is_enabled(regs: &RegisterBlock, i: usize) -> bool; |
| 30 | w.ep0setup().set_bit(); | 368 | } |
| 31 | w.usbreset().set_bit() | ||
| 32 | }); | ||
| 33 | 369 | ||
| 34 | Usbd::new(UsbBus { | 370 | impl EndpointDir for In { |
| 35 | phantom: PhantomData, | 371 | #[inline] |
| 372 | fn waker(i: usize) -> &'static AtomicWaker { | ||
| 373 | &EP_IN_WAKERS[i - 1] | ||
| 374 | } | ||
| 375 | |||
| 376 | #[inline] | ||
| 377 | fn mask(i: usize) -> u32 { | ||
| 378 | 1 << i | ||
| 379 | } | ||
| 380 | |||
| 381 | #[inline] | ||
| 382 | fn is_enabled(regs: &RegisterBlock, i: usize) -> bool { | ||
| 383 | (regs.epinen.read().bits() & (1 << i)) != 0 | ||
| 384 | } | ||
| 385 | } | ||
| 386 | |||
| 387 | impl EndpointDir for Out { | ||
| 388 | #[inline] | ||
| 389 | fn waker(i: usize) -> &'static AtomicWaker { | ||
| 390 | &EP_OUT_WAKERS[i - 1] | ||
| 391 | } | ||
| 392 | |||
| 393 | #[inline] | ||
| 394 | fn mask(i: usize) -> u32 { | ||
| 395 | 1 << (i + 16) | ||
| 396 | } | ||
| 397 | |||
| 398 | #[inline] | ||
| 399 | fn is_enabled(regs: &RegisterBlock, i: usize) -> bool { | ||
| 400 | (regs.epouten.read().bits() & (1 << i)) != 0 | ||
| 401 | } | ||
| 402 | } | ||
| 403 | |||
| 404 | pub struct Endpoint<'d, T: Instance, Dir> { | ||
| 405 | _phantom: PhantomData<(&'d mut T, Dir)>, | ||
| 406 | info: EndpointInfo, | ||
| 407 | } | ||
| 408 | |||
| 409 | impl<'d, T: Instance, Dir> Endpoint<'d, T, Dir> { | ||
| 410 | fn new(info: EndpointInfo) -> Self { | ||
| 411 | Self { | ||
| 412 | info, | ||
| 413 | _phantom: PhantomData, | ||
| 414 | } | ||
| 415 | } | ||
| 416 | } | ||
| 417 | |||
| 418 | impl<'d, T: Instance, Dir: EndpointDir> driver::Endpoint for Endpoint<'d, T, Dir> { | ||
| 419 | fn info(&self) -> &EndpointInfo { | ||
| 420 | &self.info | ||
| 421 | } | ||
| 422 | |||
| 423 | fn set_stalled(&self, stalled: bool) { | ||
| 424 | Driver::<T>::set_stalled(self.info.addr, stalled) | ||
| 425 | } | ||
| 426 | |||
| 427 | fn is_stalled(&self) -> bool { | ||
| 428 | Driver::<T>::is_stalled(self.info.addr) | ||
| 429 | } | ||
| 430 | |||
| 431 | type WaitEnabledFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a; | ||
| 432 | |||
| 433 | fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> { | ||
| 434 | let i = self.info.addr.index(); | ||
| 435 | assert!(i != 0); | ||
| 436 | |||
| 437 | poll_fn(move |cx| { | ||
| 438 | Dir::waker(i).register(cx.waker()); | ||
| 439 | if Dir::is_enabled(T::regs(), i) { | ||
| 440 | Poll::Ready(()) | ||
| 441 | } else { | ||
| 442 | Poll::Pending | ||
| 443 | } | ||
| 444 | }) | ||
| 445 | } | ||
| 446 | } | ||
| 447 | |||
| 448 | impl<'d, T: Instance, Dir> Endpoint<'d, T, Dir> { | ||
| 449 | async fn wait_data_ready(&mut self) -> Result<(), ()> | ||
| 450 | where | ||
| 451 | Dir: EndpointDir, | ||
| 452 | { | ||
| 453 | let i = self.info.addr.index(); | ||
| 454 | assert!(i != 0); | ||
| 455 | poll_fn(|cx| { | ||
| 456 | Dir::waker(i).register(cx.waker()); | ||
| 457 | let r = READY_ENDPOINTS.load(Ordering::Acquire); | ||
| 458 | if !Dir::is_enabled(T::regs(), i) { | ||
| 459 | Poll::Ready(Err(())) | ||
| 460 | } else if r & Dir::mask(i) != 0 { | ||
| 461 | Poll::Ready(Ok(())) | ||
| 462 | } else { | ||
| 463 | Poll::Pending | ||
| 464 | } | ||
| 36 | }) | 465 | }) |
| 466 | .await?; | ||
| 467 | |||
| 468 | // Mark as not ready | ||
| 469 | READY_ENDPOINTS.fetch_and(!Dir::mask(i), Ordering::AcqRel); | ||
| 470 | |||
| 471 | Ok(()) | ||
| 472 | } | ||
| 473 | } | ||
| 474 | |||
| 475 | unsafe fn read_dma<T: Instance>(i: usize, buf: &mut [u8]) -> Result<usize, ReadError> { | ||
| 476 | let regs = T::regs(); | ||
| 477 | |||
| 478 | // Check that the packet fits into the buffer | ||
| 479 | let size = regs.size.epout[i].read().bits() as usize; | ||
| 480 | if size > buf.len() { | ||
| 481 | return Err(ReadError::BufferOverflow); | ||
| 482 | } | ||
| 483 | |||
| 484 | if i == 0 { | ||
| 485 | regs.events_ep0datadone.reset(); | ||
| 486 | } | ||
| 487 | |||
| 488 | let epout = [ | ||
| 489 | ®s.epout0, | ||
| 490 | ®s.epout1, | ||
| 491 | ®s.epout2, | ||
| 492 | ®s.epout3, | ||
| 493 | ®s.epout4, | ||
| 494 | ®s.epout5, | ||
| 495 | ®s.epout6, | ||
| 496 | ®s.epout7, | ||
| 497 | ]; | ||
| 498 | epout[i].ptr.write(|w| w.bits(buf.as_ptr() as u32)); | ||
| 499 | // MAXCNT must match SIZE | ||
| 500 | epout[i].maxcnt.write(|w| w.bits(size as u32)); | ||
| 501 | |||
| 502 | dma_start(); | ||
| 503 | regs.events_endepout[i].reset(); | ||
| 504 | regs.tasks_startepout[i].write(|w| w.tasks_startepout().set_bit()); | ||
| 505 | while regs.events_endepout[i] | ||
| 506 | .read() | ||
| 507 | .events_endepout() | ||
| 508 | .bit_is_clear() | ||
| 509 | {} | ||
| 510 | regs.events_endepout[i].reset(); | ||
| 511 | dma_end(); | ||
| 512 | |||
| 513 | regs.size.epout[i].reset(); | ||
| 514 | |||
| 515 | Ok(size) | ||
| 516 | } | ||
| 517 | |||
| 518 | unsafe fn write_dma<T: Instance>(i: usize, buf: &[u8]) { | ||
| 519 | let regs = T::regs(); | ||
| 520 | assert!(buf.len() <= 64); | ||
| 521 | |||
| 522 | let mut ram_buf: MaybeUninit<[u8; 64]> = MaybeUninit::uninit(); | ||
| 523 | let ptr = if !slice_in_ram(buf) { | ||
| 524 | // EasyDMA can't read FLASH, so we copy through RAM | ||
| 525 | let ptr = ram_buf.as_mut_ptr() as *mut u8; | ||
| 526 | core::ptr::copy_nonoverlapping(buf.as_ptr(), ptr, buf.len()); | ||
| 527 | ptr | ||
| 528 | } else { | ||
| 529 | buf.as_ptr() | ||
| 530 | }; | ||
| 531 | |||
| 532 | let epin = [ | ||
| 533 | ®s.epin0, | ||
| 534 | ®s.epin1, | ||
| 535 | ®s.epin2, | ||
| 536 | ®s.epin3, | ||
| 537 | ®s.epin4, | ||
| 538 | ®s.epin5, | ||
| 539 | ®s.epin6, | ||
| 540 | ®s.epin7, | ||
| 541 | ]; | ||
| 542 | |||
| 543 | // Set the buffer length so the right number of bytes are transmitted. | ||
| 544 | // Safety: `buf.len()` has been checked to be <= the max buffer length. | ||
| 545 | epin[i].ptr.write(|w| w.bits(ptr as u32)); | ||
| 546 | epin[i].maxcnt.write(|w| w.maxcnt().bits(buf.len() as u8)); | ||
| 547 | |||
| 548 | regs.events_endepin[i].reset(); | ||
| 549 | |||
| 550 | dma_start(); | ||
| 551 | regs.tasks_startepin[i].write(|w| w.bits(1)); | ||
| 552 | while regs.events_endepin[i].read().bits() == 0 {} | ||
| 553 | dma_end(); | ||
| 554 | } | ||
| 555 | |||
| 556 | impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { | ||
| 557 | type ReadFuture<'a> = impl Future<Output = Result<usize, ReadError>> + 'a where Self: 'a; | ||
| 558 | |||
| 559 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { | ||
| 560 | async move { | ||
| 561 | let i = self.info.addr.index(); | ||
| 562 | assert!(i != 0); | ||
| 563 | |||
| 564 | self.wait_data_ready() | ||
| 565 | .await | ||
| 566 | .map_err(|_| ReadError::Disabled)?; | ||
| 567 | |||
| 568 | unsafe { read_dma::<T>(i, buf) } | ||
| 569 | } | ||
| 570 | } | ||
| 571 | } | ||
| 572 | |||
| 573 | impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { | ||
| 574 | type WriteFuture<'a> = impl Future<Output = Result<(), WriteError>> + 'a where Self: 'a; | ||
| 575 | |||
| 576 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { | ||
| 577 | async move { | ||
| 578 | let i = self.info.addr.index(); | ||
| 579 | assert!(i != 0); | ||
| 580 | |||
| 581 | self.wait_data_ready() | ||
| 582 | .await | ||
| 583 | .map_err(|_| WriteError::Disabled)?; | ||
| 584 | |||
| 585 | unsafe { write_dma::<T>(i, buf) } | ||
| 586 | |||
| 587 | Ok(()) | ||
| 588 | } | ||
| 37 | } | 589 | } |
| 38 | } | 590 | } |
| 39 | 591 | ||
| 40 | unsafe impl embassy_hal_common::usb::USBInterrupt for crate::interrupt::USBD {} | 592 | pub struct ControlPipe<'d, T: Instance> { |
| 593 | _phantom: PhantomData<&'d mut T>, | ||
| 594 | max_packet_size: u16, | ||
| 595 | } | ||
| 596 | |||
| 597 | impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { | ||
| 598 | type SetupFuture<'a> = impl Future<Output = Request> + 'a where Self: 'a; | ||
| 599 | type DataOutFuture<'a> = impl Future<Output = Result<usize, ReadError>> + 'a where Self: 'a; | ||
| 600 | type DataInFuture<'a> = impl Future<Output = Result<(), WriteError>> + 'a where Self: 'a; | ||
| 601 | |||
| 602 | fn max_packet_size(&self) -> usize { | ||
| 603 | usize::from(self.max_packet_size) | ||
| 604 | } | ||
| 605 | |||
| 606 | fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a> { | ||
| 607 | async move { | ||
| 608 | let regs = T::regs(); | ||
| 609 | |||
| 610 | // Wait for SETUP packet | ||
| 611 | regs.intenset.write(|w| { | ||
| 612 | w.ep0setup().set(); | ||
| 613 | w.ep0datadone().set() | ||
| 614 | }); | ||
| 615 | poll_fn(|cx| { | ||
| 616 | EP0_WAKER.register(cx.waker()); | ||
| 617 | let regs = T::regs(); | ||
| 618 | if regs.events_ep0setup.read().bits() != 0 { | ||
| 619 | Poll::Ready(()) | ||
| 620 | } else { | ||
| 621 | Poll::Pending | ||
| 622 | } | ||
| 623 | }) | ||
| 624 | .await; | ||
| 625 | |||
| 626 | // Reset shorts | ||
| 627 | regs.shorts | ||
| 628 | .modify(|_, w| w.ep0datadone_ep0status().clear_bit()); | ||
| 629 | regs.events_ep0setup.reset(); | ||
| 630 | |||
| 631 | let mut buf = [0; 8]; | ||
| 632 | buf[0] = regs.bmrequesttype.read().bits() as u8; | ||
| 633 | buf[1] = regs.brequest.read().brequest().bits(); | ||
| 634 | buf[2] = regs.wvaluel.read().wvaluel().bits(); | ||
| 635 | buf[3] = regs.wvalueh.read().wvalueh().bits(); | ||
| 636 | buf[4] = regs.windexl.read().windexl().bits(); | ||
| 637 | buf[5] = regs.windexh.read().windexh().bits(); | ||
| 638 | buf[6] = regs.wlengthl.read().wlengthl().bits(); | ||
| 639 | buf[7] = regs.wlengthh.read().wlengthh().bits(); | ||
| 640 | |||
| 641 | let req = Request::parse(&buf); | ||
| 642 | |||
| 643 | if req.direction == UsbDirection::Out { | ||
| 644 | regs.tasks_ep0rcvout | ||
| 645 | .write(|w| w.tasks_ep0rcvout().set_bit()); | ||
| 646 | } | ||
| 647 | |||
| 648 | req | ||
| 649 | } | ||
| 650 | } | ||
| 651 | |||
| 652 | fn data_out<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::DataOutFuture<'a> { | ||
| 653 | async move { | ||
| 654 | let regs = T::regs(); | ||
| 655 | |||
| 656 | // Wait until ready | ||
| 657 | regs.intenset.write(|w| { | ||
| 658 | w.usbreset().set(); | ||
| 659 | w.ep0setup().set(); | ||
| 660 | w.ep0datadone().set() | ||
| 661 | }); | ||
| 662 | poll_fn(|cx| { | ||
| 663 | EP0_WAKER.register(cx.waker()); | ||
| 664 | let regs = T::regs(); | ||
| 665 | if regs.events_ep0datadone.read().bits() != 0 { | ||
| 666 | Poll::Ready(Ok(())) | ||
| 667 | } else if regs.events_usbreset.read().bits() != 0 { | ||
| 668 | trace!("aborted control data_out: usb reset"); | ||
| 669 | Poll::Ready(Err(ReadError::Disabled)) | ||
| 670 | } else if regs.events_ep0setup.read().bits() != 0 { | ||
| 671 | trace!("aborted control data_out: received another SETUP"); | ||
| 672 | Poll::Ready(Err(ReadError::Disabled)) | ||
| 673 | } else { | ||
| 674 | Poll::Pending | ||
| 675 | } | ||
| 676 | }) | ||
| 677 | .await?; | ||
| 678 | |||
| 679 | unsafe { read_dma::<T>(0, buf) } | ||
| 680 | } | ||
| 681 | } | ||
| 682 | |||
| 683 | fn data_in<'a>(&'a mut self, buf: &'a [u8], last_packet: bool) -> Self::DataInFuture<'a> { | ||
| 684 | async move { | ||
| 685 | let regs = T::regs(); | ||
| 686 | regs.events_ep0datadone.reset(); | ||
| 687 | unsafe { | ||
| 688 | write_dma::<T>(0, buf); | ||
| 689 | } | ||
| 690 | |||
| 691 | regs.shorts | ||
| 692 | .modify(|_, w| w.ep0datadone_ep0status().bit(last_packet)); | ||
| 693 | |||
| 694 | regs.intenset.write(|w| { | ||
| 695 | w.usbreset().set(); | ||
| 696 | w.ep0setup().set(); | ||
| 697 | w.ep0datadone().set() | ||
| 698 | }); | ||
| 699 | |||
| 700 | poll_fn(|cx| { | ||
| 701 | cx.waker().wake_by_ref(); | ||
| 702 | EP0_WAKER.register(cx.waker()); | ||
| 703 | let regs = T::regs(); | ||
| 704 | if regs.events_ep0datadone.read().bits() != 0 { | ||
| 705 | Poll::Ready(Ok(())) | ||
| 706 | } else if regs.events_usbreset.read().bits() != 0 { | ||
| 707 | trace!("aborted control data_in: usb reset"); | ||
| 708 | Poll::Ready(Err(WriteError::Disabled)) | ||
| 709 | } else if regs.events_ep0setup.read().bits() != 0 { | ||
| 710 | trace!("aborted control data_in: received another SETUP"); | ||
| 711 | Poll::Ready(Err(WriteError::Disabled)) | ||
| 712 | } else { | ||
| 713 | Poll::Pending | ||
| 714 | } | ||
| 715 | }) | ||
| 716 | .await | ||
| 717 | } | ||
| 718 | } | ||
| 719 | |||
| 720 | fn accept(&mut self) { | ||
| 721 | let regs = T::regs(); | ||
| 722 | regs.tasks_ep0status | ||
| 723 | .write(|w| w.tasks_ep0status().bit(true)); | ||
| 724 | } | ||
| 725 | |||
| 726 | fn reject(&mut self) { | ||
| 727 | let regs = T::regs(); | ||
| 728 | regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(true)); | ||
| 729 | } | ||
| 730 | } | ||
| 731 | |||
| 732 | fn dma_start() { | ||
| 733 | compiler_fence(Ordering::Release); | ||
| 734 | } | ||
| 735 | |||
| 736 | fn dma_end() { | ||
| 737 | compiler_fence(Ordering::Acquire); | ||
| 738 | } | ||
| 739 | |||
| 740 | struct Allocator { | ||
| 741 | used: u16, | ||
| 742 | // Buffers can be up to 64 Bytes since this is a Full-Speed implementation. | ||
| 743 | lens: [u8; 9], | ||
| 744 | } | ||
| 745 | |||
| 746 | impl Allocator { | ||
| 747 | fn new() -> Self { | ||
| 748 | Self { | ||
| 749 | used: 0, | ||
| 750 | lens: [0; 9], | ||
| 751 | } | ||
| 752 | } | ||
| 753 | |||
| 754 | fn allocate( | ||
| 755 | &mut self, | ||
| 756 | ep_addr: Option<EndpointAddress>, | ||
| 757 | ep_type: EndpointType, | ||
| 758 | max_packet_size: u16, | ||
| 759 | _interval: u8, | ||
| 760 | ) -> Result<usize, driver::EndpointAllocError> { | ||
| 761 | // Endpoint addresses are fixed in hardware: | ||
| 762 | // - 0x80 / 0x00 - Control EP0 | ||
| 763 | // - 0x81 / 0x01 - Bulk/Interrupt EP1 | ||
| 764 | // - 0x82 / 0x02 - Bulk/Interrupt EP2 | ||
| 765 | // - 0x83 / 0x03 - Bulk/Interrupt EP3 | ||
| 766 | // - 0x84 / 0x04 - Bulk/Interrupt EP4 | ||
| 767 | // - 0x85 / 0x05 - Bulk/Interrupt EP5 | ||
| 768 | // - 0x86 / 0x06 - Bulk/Interrupt EP6 | ||
| 769 | // - 0x87 / 0x07 - Bulk/Interrupt EP7 | ||
| 770 | // - 0x88 / 0x08 - Isochronous | ||
| 771 | |||
| 772 | // Endpoint directions are allocated individually. | ||
| 773 | |||
| 774 | let alloc_index = if let Some(ep_addr) = ep_addr { | ||
| 775 | match (ep_addr.index(), ep_type) { | ||
| 776 | (0, EndpointType::Control) => {} | ||
| 777 | (8, EndpointType::Isochronous) => {} | ||
| 778 | (n, EndpointType::Bulk) | (n, EndpointType::Interrupt) if n >= 1 && n <= 7 => {} | ||
| 779 | _ => return Err(driver::EndpointAllocError), | ||
| 780 | } | ||
| 781 | |||
| 782 | ep_addr.index() | ||
| 783 | } else { | ||
| 784 | match ep_type { | ||
| 785 | EndpointType::Isochronous => 8, | ||
| 786 | EndpointType::Control => 0, | ||
| 787 | EndpointType::Interrupt | EndpointType::Bulk => { | ||
| 788 | // Find rightmost zero bit in 1..=7 | ||
| 789 | let ones = (self.used >> 1).trailing_ones() as usize; | ||
| 790 | if ones >= 7 { | ||
| 791 | return Err(driver::EndpointAllocError); | ||
| 792 | } | ||
| 793 | ones + 1 | ||
| 794 | } | ||
| 795 | } | ||
| 796 | }; | ||
| 797 | |||
| 798 | if self.used & (1 << alloc_index) != 0 { | ||
| 799 | return Err(driver::EndpointAllocError); | ||
| 800 | } | ||
| 801 | |||
| 802 | self.used |= 1 << alloc_index; | ||
| 803 | self.lens[alloc_index] = max_packet_size as u8; | ||
| 804 | |||
| 805 | Ok(alloc_index) | ||
| 806 | } | ||
| 807 | } | ||
| 41 | 808 | ||
| 42 | pub(crate) mod sealed { | 809 | pub(crate) mod sealed { |
| 43 | use super::*; | 810 | use super::*; |
| @@ -63,3 +830,64 @@ macro_rules! impl_usb { | |||
| 63 | } | 830 | } |
| 64 | }; | 831 | }; |
| 65 | } | 832 | } |
| 833 | |||
| 834 | mod errata { | ||
| 835 | |||
| 836 | /// Writes `val` to `addr`. Used to apply Errata workarounds. | ||
| 837 | unsafe fn poke(addr: u32, val: u32) { | ||
| 838 | (addr as *mut u32).write_volatile(val); | ||
| 839 | } | ||
| 840 | |||
| 841 | /// Reads 32 bits from `addr`. | ||
| 842 | unsafe fn peek(addr: u32) -> u32 { | ||
| 843 | (addr as *mut u32).read_volatile() | ||
| 844 | } | ||
| 845 | |||
| 846 | pub fn pre_enable() { | ||
| 847 | // Works around Erratum 187 on chip revisions 1 and 2. | ||
| 848 | unsafe { | ||
| 849 | poke(0x4006EC00, 0x00009375); | ||
| 850 | poke(0x4006ED14, 0x00000003); | ||
| 851 | poke(0x4006EC00, 0x00009375); | ||
| 852 | } | ||
| 853 | |||
| 854 | pre_wakeup(); | ||
| 855 | } | ||
| 856 | |||
| 857 | pub fn post_enable() { | ||
| 858 | post_wakeup(); | ||
| 859 | |||
| 860 | // Works around Erratum 187 on chip revisions 1 and 2. | ||
| 861 | unsafe { | ||
| 862 | poke(0x4006EC00, 0x00009375); | ||
| 863 | poke(0x4006ED14, 0x00000000); | ||
| 864 | poke(0x4006EC00, 0x00009375); | ||
| 865 | } | ||
| 866 | } | ||
| 867 | |||
| 868 | pub fn pre_wakeup() { | ||
| 869 | // Works around Erratum 171 on chip revisions 1 and 2. | ||
| 870 | |||
| 871 | unsafe { | ||
| 872 | if peek(0x4006EC00) == 0x00000000 { | ||
| 873 | poke(0x4006EC00, 0x00009375); | ||
| 874 | } | ||
| 875 | |||
| 876 | poke(0x4006EC14, 0x000000C0); | ||
| 877 | poke(0x4006EC00, 0x00009375); | ||
| 878 | } | ||
| 879 | } | ||
| 880 | |||
| 881 | pub fn post_wakeup() { | ||
| 882 | // Works around Erratum 171 on chip revisions 1 and 2. | ||
| 883 | |||
| 884 | unsafe { | ||
| 885 | if peek(0x4006EC00) == 0x00000000 { | ||
| 886 | poke(0x4006EC00, 0x00009375); | ||
| 887 | } | ||
| 888 | |||
| 889 | poke(0x4006EC14, 0x00000000); | ||
| 890 | poke(0x4006EC00, 0x00009375); | ||
| 891 | } | ||
| 892 | } | ||
| 893 | } | ||
diff --git a/embassy-usb-hid/Cargo.toml b/embassy-usb-hid/Cargo.toml new file mode 100644 index 000000000..dc3d3cd88 --- /dev/null +++ b/embassy-usb-hid/Cargo.toml | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | [package] | ||
| 2 | name = "embassy-usb-hid" | ||
| 3 | version = "0.1.0" | ||
| 4 | edition = "2021" | ||
| 5 | |||
| 6 | [features] | ||
| 7 | default = ["usbd-hid"] | ||
| 8 | usbd-hid = ["dep:usbd-hid", "ssmarshal"] | ||
| 9 | |||
| 10 | [dependencies] | ||
| 11 | embassy = { version = "0.1.0", path = "../embassy" } | ||
| 12 | embassy-usb = { version = "0.1.0", path = "../embassy-usb" } | ||
| 13 | |||
| 14 | defmt = { version = "0.3", optional = true } | ||
| 15 | log = { version = "0.4.14", optional = true } | ||
| 16 | usbd-hid = { version = "0.5.2", optional = true } | ||
| 17 | ssmarshal = { version = "1.0", default-features = false, optional = true } | ||
| 18 | futures-util = { version = "0.3.21", default-features = false } | ||
diff --git a/embassy-usb-hid/src/fmt.rs b/embassy-usb-hid/src/fmt.rs new file mode 100644 index 000000000..066970813 --- /dev/null +++ b/embassy-usb-hid/src/fmt.rs | |||
| @@ -0,0 +1,225 @@ | |||
| 1 | #![macro_use] | ||
| 2 | #![allow(unused_macros)] | ||
| 3 | |||
| 4 | #[cfg(all(feature = "defmt", feature = "log"))] | ||
| 5 | compile_error!("You may not enable both `defmt` and `log` features."); | ||
| 6 | |||
| 7 | macro_rules! assert { | ||
| 8 | ($($x:tt)*) => { | ||
| 9 | { | ||
| 10 | #[cfg(not(feature = "defmt"))] | ||
| 11 | ::core::assert!($($x)*); | ||
| 12 | #[cfg(feature = "defmt")] | ||
| 13 | ::defmt::assert!($($x)*); | ||
| 14 | } | ||
| 15 | }; | ||
| 16 | } | ||
| 17 | |||
| 18 | macro_rules! assert_eq { | ||
| 19 | ($($x:tt)*) => { | ||
| 20 | { | ||
| 21 | #[cfg(not(feature = "defmt"))] | ||
| 22 | ::core::assert_eq!($($x)*); | ||
| 23 | #[cfg(feature = "defmt")] | ||
| 24 | ::defmt::assert_eq!($($x)*); | ||
| 25 | } | ||
| 26 | }; | ||
| 27 | } | ||
| 28 | |||
| 29 | macro_rules! assert_ne { | ||
| 30 | ($($x:tt)*) => { | ||
| 31 | { | ||
| 32 | #[cfg(not(feature = "defmt"))] | ||
| 33 | ::core::assert_ne!($($x)*); | ||
| 34 | #[cfg(feature = "defmt")] | ||
| 35 | ::defmt::assert_ne!($($x)*); | ||
| 36 | } | ||
| 37 | }; | ||
| 38 | } | ||
| 39 | |||
| 40 | macro_rules! debug_assert { | ||
| 41 | ($($x:tt)*) => { | ||
| 42 | { | ||
| 43 | #[cfg(not(feature = "defmt"))] | ||
| 44 | ::core::debug_assert!($($x)*); | ||
| 45 | #[cfg(feature = "defmt")] | ||
| 46 | ::defmt::debug_assert!($($x)*); | ||
| 47 | } | ||
| 48 | }; | ||
| 49 | } | ||
| 50 | |||
| 51 | macro_rules! debug_assert_eq { | ||
| 52 | ($($x:tt)*) => { | ||
| 53 | { | ||
| 54 | #[cfg(not(feature = "defmt"))] | ||
| 55 | ::core::debug_assert_eq!($($x)*); | ||
| 56 | #[cfg(feature = "defmt")] | ||
| 57 | ::defmt::debug_assert_eq!($($x)*); | ||
| 58 | } | ||
| 59 | }; | ||
| 60 | } | ||
| 61 | |||
| 62 | macro_rules! debug_assert_ne { | ||
| 63 | ($($x:tt)*) => { | ||
| 64 | { | ||
| 65 | #[cfg(not(feature = "defmt"))] | ||
| 66 | ::core::debug_assert_ne!($($x)*); | ||
| 67 | #[cfg(feature = "defmt")] | ||
| 68 | ::defmt::debug_assert_ne!($($x)*); | ||
| 69 | } | ||
| 70 | }; | ||
| 71 | } | ||
| 72 | |||
| 73 | macro_rules! todo { | ||
| 74 | ($($x:tt)*) => { | ||
| 75 | { | ||
| 76 | #[cfg(not(feature = "defmt"))] | ||
| 77 | ::core::todo!($($x)*); | ||
| 78 | #[cfg(feature = "defmt")] | ||
| 79 | ::defmt::todo!($($x)*); | ||
| 80 | } | ||
| 81 | }; | ||
| 82 | } | ||
| 83 | |||
| 84 | macro_rules! unreachable { | ||
| 85 | ($($x:tt)*) => { | ||
| 86 | { | ||
| 87 | #[cfg(not(feature = "defmt"))] | ||
| 88 | ::core::unreachable!($($x)*); | ||
| 89 | #[cfg(feature = "defmt")] | ||
| 90 | ::defmt::unreachable!($($x)*); | ||
| 91 | } | ||
| 92 | }; | ||
| 93 | } | ||
| 94 | |||
| 95 | macro_rules! panic { | ||
| 96 | ($($x:tt)*) => { | ||
| 97 | { | ||
| 98 | #[cfg(not(feature = "defmt"))] | ||
| 99 | ::core::panic!($($x)*); | ||
| 100 | #[cfg(feature = "defmt")] | ||
| 101 | ::defmt::panic!($($x)*); | ||
| 102 | } | ||
| 103 | }; | ||
| 104 | } | ||
| 105 | |||
| 106 | macro_rules! trace { | ||
| 107 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 108 | { | ||
| 109 | #[cfg(feature = "log")] | ||
| 110 | ::log::trace!($s $(, $x)*); | ||
| 111 | #[cfg(feature = "defmt")] | ||
| 112 | ::defmt::trace!($s $(, $x)*); | ||
| 113 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 114 | let _ = ($( & $x ),*); | ||
| 115 | } | ||
| 116 | }; | ||
| 117 | } | ||
| 118 | |||
| 119 | macro_rules! debug { | ||
| 120 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 121 | { | ||
| 122 | #[cfg(feature = "log")] | ||
| 123 | ::log::debug!($s $(, $x)*); | ||
| 124 | #[cfg(feature = "defmt")] | ||
| 125 | ::defmt::debug!($s $(, $x)*); | ||
| 126 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 127 | let _ = ($( & $x ),*); | ||
| 128 | } | ||
| 129 | }; | ||
| 130 | } | ||
| 131 | |||
| 132 | macro_rules! info { | ||
| 133 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 134 | { | ||
| 135 | #[cfg(feature = "log")] | ||
| 136 | ::log::info!($s $(, $x)*); | ||
| 137 | #[cfg(feature = "defmt")] | ||
| 138 | ::defmt::info!($s $(, $x)*); | ||
| 139 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 140 | let _ = ($( & $x ),*); | ||
| 141 | } | ||
| 142 | }; | ||
| 143 | } | ||
| 144 | |||
| 145 | macro_rules! warn { | ||
| 146 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 147 | { | ||
| 148 | #[cfg(feature = "log")] | ||
| 149 | ::log::warn!($s $(, $x)*); | ||
| 150 | #[cfg(feature = "defmt")] | ||
| 151 | ::defmt::warn!($s $(, $x)*); | ||
| 152 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 153 | let _ = ($( & $x ),*); | ||
| 154 | } | ||
| 155 | }; | ||
| 156 | } | ||
| 157 | |||
| 158 | macro_rules! error { | ||
| 159 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 160 | { | ||
| 161 | #[cfg(feature = "log")] | ||
| 162 | ::log::error!($s $(, $x)*); | ||
| 163 | #[cfg(feature = "defmt")] | ||
| 164 | ::defmt::error!($s $(, $x)*); | ||
| 165 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 166 | let _ = ($( & $x ),*); | ||
| 167 | } | ||
| 168 | }; | ||
| 169 | } | ||
| 170 | |||
| 171 | #[cfg(feature = "defmt")] | ||
| 172 | macro_rules! unwrap { | ||
| 173 | ($($x:tt)*) => { | ||
| 174 | ::defmt::unwrap!($($x)*) | ||
| 175 | }; | ||
| 176 | } | ||
| 177 | |||
| 178 | #[cfg(not(feature = "defmt"))] | ||
| 179 | macro_rules! unwrap { | ||
| 180 | ($arg:expr) => { | ||
| 181 | match $crate::fmt::Try::into_result($arg) { | ||
| 182 | ::core::result::Result::Ok(t) => t, | ||
| 183 | ::core::result::Result::Err(e) => { | ||
| 184 | ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | }; | ||
| 188 | ($arg:expr, $($msg:expr),+ $(,)? ) => { | ||
| 189 | match $crate::fmt::Try::into_result($arg) { | ||
| 190 | ::core::result::Result::Ok(t) => t, | ||
| 191 | ::core::result::Result::Err(e) => { | ||
| 192 | ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 199 | pub struct NoneError; | ||
| 200 | |||
| 201 | pub trait Try { | ||
| 202 | type Ok; | ||
| 203 | type Error; | ||
| 204 | fn into_result(self) -> Result<Self::Ok, Self::Error>; | ||
| 205 | } | ||
| 206 | |||
| 207 | impl<T> Try for Option<T> { | ||
| 208 | type Ok = T; | ||
| 209 | type Error = NoneError; | ||
| 210 | |||
| 211 | #[inline] | ||
| 212 | fn into_result(self) -> Result<T, NoneError> { | ||
| 213 | self.ok_or(NoneError) | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | impl<T, E> Try for Result<T, E> { | ||
| 218 | type Ok = T; | ||
| 219 | type Error = E; | ||
| 220 | |||
| 221 | #[inline] | ||
| 222 | fn into_result(self) -> Self { | ||
| 223 | self | ||
| 224 | } | ||
| 225 | } | ||
diff --git a/embassy-usb-hid/src/lib.rs b/embassy-usb-hid/src/lib.rs new file mode 100644 index 000000000..f50c5f8cb --- /dev/null +++ b/embassy-usb-hid/src/lib.rs | |||
| @@ -0,0 +1,522 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![feature(generic_associated_types)] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | //! Implements HID functionality for a usb-device device. | ||
| 6 | |||
| 7 | // This mod MUST go first, so that the others see its macros. | ||
| 8 | pub(crate) mod fmt; | ||
| 9 | |||
| 10 | use core::mem::MaybeUninit; | ||
| 11 | use core::ops::Range; | ||
| 12 | use core::sync::atomic::{AtomicUsize, Ordering}; | ||
| 13 | |||
| 14 | use embassy::time::Duration; | ||
| 15 | use embassy_usb::driver::EndpointOut; | ||
| 16 | use embassy_usb::{ | ||
| 17 | control::{ControlHandler, InResponse, OutResponse, Request, RequestType}, | ||
| 18 | driver::{Driver, Endpoint, EndpointIn, WriteError}, | ||
| 19 | UsbDeviceBuilder, | ||
| 20 | }; | ||
| 21 | |||
| 22 | #[cfg(feature = "usbd-hid")] | ||
| 23 | use ssmarshal::serialize; | ||
| 24 | #[cfg(feature = "usbd-hid")] | ||
| 25 | use usbd_hid::descriptor::AsInputReport; | ||
| 26 | |||
| 27 | const USB_CLASS_HID: u8 = 0x03; | ||
| 28 | const USB_SUBCLASS_NONE: u8 = 0x00; | ||
| 29 | const USB_PROTOCOL_NONE: u8 = 0x00; | ||
| 30 | |||
| 31 | // HID | ||
| 32 | const HID_DESC_DESCTYPE_HID: u8 = 0x21; | ||
| 33 | const HID_DESC_DESCTYPE_HID_REPORT: u8 = 0x22; | ||
| 34 | const HID_DESC_SPEC_1_10: [u8; 2] = [0x10, 0x01]; | ||
| 35 | const HID_DESC_COUNTRY_UNSPEC: u8 = 0x00; | ||
| 36 | |||
| 37 | const HID_REQ_SET_IDLE: u8 = 0x0a; | ||
| 38 | const HID_REQ_GET_IDLE: u8 = 0x02; | ||
| 39 | const HID_REQ_GET_REPORT: u8 = 0x01; | ||
| 40 | const HID_REQ_SET_REPORT: u8 = 0x09; | ||
| 41 | const HID_REQ_GET_PROTOCOL: u8 = 0x03; | ||
| 42 | const HID_REQ_SET_PROTOCOL: u8 = 0x0b; | ||
| 43 | |||
| 44 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 45 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 46 | pub enum ReportId { | ||
| 47 | In(u8), | ||
| 48 | Out(u8), | ||
| 49 | Feature(u8), | ||
| 50 | } | ||
| 51 | |||
| 52 | impl ReportId { | ||
| 53 | fn try_from(value: u16) -> Result<Self, ()> { | ||
| 54 | match value >> 8 { | ||
| 55 | 1 => Ok(ReportId::In(value as u8)), | ||
| 56 | 2 => Ok(ReportId::Out(value as u8)), | ||
| 57 | 3 => Ok(ReportId::Feature(value as u8)), | ||
| 58 | _ => Err(()), | ||
| 59 | } | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | pub struct State<'a, const IN_N: usize, const OUT_N: usize> { | ||
| 64 | control: MaybeUninit<Control<'a>>, | ||
| 65 | out_report_offset: AtomicUsize, | ||
| 66 | } | ||
| 67 | |||
| 68 | impl<'a, const IN_N: usize, const OUT_N: usize> State<'a, IN_N, OUT_N> { | ||
| 69 | pub fn new() -> Self { | ||
| 70 | State { | ||
| 71 | control: MaybeUninit::uninit(), | ||
| 72 | out_report_offset: AtomicUsize::new(0), | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | pub struct HidClass<'d, D: Driver<'d>, T, const IN_N: usize> { | ||
| 78 | input: ReportWriter<'d, D, IN_N>, | ||
| 79 | output: T, | ||
| 80 | } | ||
| 81 | |||
| 82 | impl<'d, D: Driver<'d>, const IN_N: usize> HidClass<'d, D, (), IN_N> { | ||
| 83 | /// Creates a new HidClass. | ||
| 84 | /// | ||
| 85 | /// poll_ms configures how frequently the host should poll for reading/writing | ||
| 86 | /// HID reports. A lower value means better throughput & latency, at the expense | ||
| 87 | /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for | ||
| 88 | /// high performance uses, and a value of 255 is good for best-effort usecases. | ||
| 89 | /// | ||
| 90 | /// This allocates an IN endpoint only. | ||
| 91 | pub fn new<const OUT_N: usize>( | ||
| 92 | builder: &mut UsbDeviceBuilder<'d, D>, | ||
| 93 | state: &'d mut State<'d, IN_N, OUT_N>, | ||
| 94 | report_descriptor: &'static [u8], | ||
| 95 | request_handler: Option<&'d dyn RequestHandler>, | ||
| 96 | poll_ms: u8, | ||
| 97 | max_packet_size: u16, | ||
| 98 | ) -> Self { | ||
| 99 | let ep_in = builder.alloc_interrupt_endpoint_in(max_packet_size, poll_ms); | ||
| 100 | let control = state.control.write(Control::new( | ||
| 101 | report_descriptor, | ||
| 102 | request_handler, | ||
| 103 | &state.out_report_offset, | ||
| 104 | )); | ||
| 105 | control.build(builder, None, &ep_in); | ||
| 106 | |||
| 107 | Self { | ||
| 108 | input: ReportWriter { ep_in }, | ||
| 109 | output: (), | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | impl<'d, D: Driver<'d>, T, const IN_N: usize> HidClass<'d, D, T, IN_N> { | ||
| 115 | /// Gets the [`ReportWriter`] for input reports. | ||
| 116 | /// | ||
| 117 | /// **Note:** If the `HidClass` was created with [`new_ep_out()`](Self::new_ep_out) | ||
| 118 | /// this writer will be useless as no endpoint is availabe to send reports. | ||
| 119 | pub fn input(&mut self) -> &mut ReportWriter<'d, D, IN_N> { | ||
| 120 | &mut self.input | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | impl<'d, D: Driver<'d>, const IN_N: usize, const OUT_N: usize> | ||
| 125 | HidClass<'d, D, ReportReader<'d, D, OUT_N>, IN_N> | ||
| 126 | { | ||
| 127 | /// Creates a new HidClass. | ||
| 128 | /// | ||
| 129 | /// poll_ms configures how frequently the host should poll for reading/writing | ||
| 130 | /// HID reports. A lower value means better throughput & latency, at the expense | ||
| 131 | /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for | ||
| 132 | /// high performance uses, and a value of 255 is good for best-effort usecases. | ||
| 133 | /// | ||
| 134 | /// This allocates two endpoints (IN and OUT). | ||
| 135 | pub fn with_output_ep( | ||
| 136 | builder: &mut UsbDeviceBuilder<'d, D>, | ||
| 137 | state: &'d mut State<'d, IN_N, OUT_N>, | ||
| 138 | report_descriptor: &'static [u8], | ||
| 139 | request_handler: Option<&'d dyn RequestHandler>, | ||
| 140 | poll_ms: u8, | ||
| 141 | max_packet_size: u16, | ||
| 142 | ) -> Self { | ||
| 143 | let ep_out = builder.alloc_interrupt_endpoint_out(max_packet_size, poll_ms); | ||
| 144 | let ep_in = builder.alloc_interrupt_endpoint_in(max_packet_size, poll_ms); | ||
| 145 | |||
| 146 | let control = state.control.write(Control::new( | ||
| 147 | report_descriptor, | ||
| 148 | request_handler, | ||
| 149 | &state.out_report_offset, | ||
| 150 | )); | ||
| 151 | control.build(builder, Some(&ep_out), &ep_in); | ||
| 152 | |||
| 153 | Self { | ||
| 154 | input: ReportWriter { ep_in }, | ||
| 155 | output: ReportReader { | ||
| 156 | ep_out, | ||
| 157 | offset: &state.out_report_offset, | ||
| 158 | }, | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | /// Gets the [`ReportReader`] for output reports. | ||
| 163 | pub fn output(&mut self) -> &mut ReportReader<'d, D, OUT_N> { | ||
| 164 | &mut self.output | ||
| 165 | } | ||
| 166 | |||
| 167 | /// Splits this `HidClass` into seperate readers/writers for input and output reports. | ||
| 168 | pub fn split(self) -> (ReportWriter<'d, D, IN_N>, ReportReader<'d, D, OUT_N>) { | ||
| 169 | (self.input, self.output) | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | pub struct ReportWriter<'d, D: Driver<'d>, const N: usize> { | ||
| 174 | ep_in: D::EndpointIn, | ||
| 175 | } | ||
| 176 | |||
| 177 | pub struct ReportReader<'d, D: Driver<'d>, const N: usize> { | ||
| 178 | ep_out: D::EndpointOut, | ||
| 179 | offset: &'d AtomicUsize, | ||
| 180 | } | ||
| 181 | |||
| 182 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 183 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 184 | pub enum ReadError { | ||
| 185 | BufferOverflow, | ||
| 186 | Disabled, | ||
| 187 | Sync(Range<usize>), | ||
| 188 | } | ||
| 189 | |||
| 190 | impl From<embassy_usb::driver::ReadError> for ReadError { | ||
| 191 | fn from(val: embassy_usb::driver::ReadError) -> Self { | ||
| 192 | use embassy_usb::driver::ReadError::*; | ||
| 193 | match val { | ||
| 194 | BufferOverflow => ReadError::BufferOverflow, | ||
| 195 | Disabled => ReadError::Disabled, | ||
| 196 | } | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | impl<'d, D: Driver<'d>, const N: usize> ReportWriter<'d, D, N> { | ||
| 201 | /// Waits for the interrupt in endpoint to be enabled. | ||
| 202 | pub async fn ready(&mut self) -> () { | ||
| 203 | self.ep_in.wait_enabled().await | ||
| 204 | } | ||
| 205 | |||
| 206 | /// Tries to write an input report by serializing the given report structure. | ||
| 207 | /// | ||
| 208 | /// Panics if no endpoint is available. | ||
| 209 | #[cfg(feature = "usbd-hid")] | ||
| 210 | pub async fn serialize<IR: AsInputReport>(&mut self, r: &IR) -> Result<(), WriteError> { | ||
| 211 | let mut buf: [u8; N] = [0; N]; | ||
| 212 | let size = match serialize(&mut buf, r) { | ||
| 213 | Ok(size) => size, | ||
| 214 | Err(_) => return Err(WriteError::BufferOverflow), | ||
| 215 | }; | ||
| 216 | self.write(&buf[0..size]).await | ||
| 217 | } | ||
| 218 | |||
| 219 | /// Writes `report` to its interrupt endpoint. | ||
| 220 | /// | ||
| 221 | /// Panics if no endpoint is available. | ||
| 222 | pub async fn write(&mut self, report: &[u8]) -> Result<(), WriteError> { | ||
| 223 | assert!(report.len() <= N); | ||
| 224 | |||
| 225 | let max_packet_size = usize::from(self.ep_in.info().max_packet_size); | ||
| 226 | let zlp_needed = report.len() < N && (report.len() % max_packet_size == 0); | ||
| 227 | for chunk in report.chunks(max_packet_size) { | ||
| 228 | self.ep_in.write(chunk).await?; | ||
| 229 | } | ||
| 230 | |||
| 231 | if zlp_needed { | ||
| 232 | self.ep_in.write(&[]).await?; | ||
| 233 | } | ||
| 234 | |||
| 235 | Ok(()) | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | impl<'d, D: Driver<'d>, const N: usize> ReportReader<'d, D, N> { | ||
| 240 | /// Waits for the interrupt out endpoint to be enabled. | ||
| 241 | pub async fn ready(&mut self) -> () { | ||
| 242 | self.ep_out.wait_enabled().await | ||
| 243 | } | ||
| 244 | |||
| 245 | /// Starts a task to deliver output reports from the Interrupt Out pipe to | ||
| 246 | /// `handler`. | ||
| 247 | /// | ||
| 248 | /// Terminates when the interface becomes disabled. | ||
| 249 | /// | ||
| 250 | /// If `use_report_ids` is true, the first byte of the report will be used as | ||
| 251 | /// the `ReportId` value. Otherwise the `ReportId` value will be 0. | ||
| 252 | pub async fn run<T: RequestHandler>(mut self, use_report_ids: bool, handler: &T) -> ! { | ||
| 253 | let offset = self.offset.load(Ordering::Acquire); | ||
| 254 | assert!(offset == 0); | ||
| 255 | let mut buf = [0; N]; | ||
| 256 | loop { | ||
| 257 | match self.read(&mut buf).await { | ||
| 258 | Ok(len) => { | ||
| 259 | let id = if use_report_ids { buf[0] } else { 0 }; | ||
| 260 | handler.set_report(ReportId::Out(id), &buf[..len]); } | ||
| 261 | Err(ReadError::BufferOverflow) => warn!("Host sent output report larger than the configured maximum output report length ({})", N), | ||
| 262 | Err(ReadError::Disabled) => self.ep_out.wait_enabled().await, | ||
| 263 | Err(ReadError::Sync(_)) => unreachable!(), | ||
| 264 | } | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | /// Reads an output report from the Interrupt Out pipe. | ||
| 269 | /// | ||
| 270 | /// **Note:** Any reports sent from the host over the control pipe will be | ||
| 271 | /// passed to [`RequestHandler::set_report()`] for handling. The application | ||
| 272 | /// is responsible for ensuring output reports from both pipes are handled | ||
| 273 | /// correctly. | ||
| 274 | /// | ||
| 275 | /// **Note:** If `N` > the maximum packet size of the endpoint (i.e. output | ||
| 276 | /// reports may be split across multiple packets) and this method's future | ||
| 277 | /// is dropped after some packets have been read, the next call to `read()` | ||
| 278 | /// will return a [`ReadError::SyncError()`]. The range in the sync error | ||
| 279 | /// indicates the portion `buf` that was filled by the current call to | ||
| 280 | /// `read()`. If the dropped future used the same `buf`, then `buf` will | ||
| 281 | /// contain the full report. | ||
| 282 | pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, ReadError> { | ||
| 283 | assert!(N != 0); | ||
| 284 | assert!(buf.len() >= N); | ||
| 285 | |||
| 286 | // Read packets from the endpoint | ||
| 287 | let max_packet_size = usize::from(self.ep_out.info().max_packet_size); | ||
| 288 | let starting_offset = self.offset.load(Ordering::Acquire); | ||
| 289 | let mut total = starting_offset; | ||
| 290 | loop { | ||
| 291 | for chunk in buf[starting_offset..N].chunks_mut(max_packet_size) { | ||
| 292 | match self.ep_out.read(chunk).await { | ||
| 293 | Ok(size) => { | ||
| 294 | total += size; | ||
| 295 | if size < max_packet_size || total == N { | ||
| 296 | self.offset.store(0, Ordering::Release); | ||
| 297 | break; | ||
| 298 | } else { | ||
| 299 | self.offset.store(total, Ordering::Release); | ||
| 300 | } | ||
| 301 | } | ||
| 302 | Err(err) => { | ||
| 303 | self.offset.store(0, Ordering::Release); | ||
| 304 | return Err(err.into()); | ||
| 305 | } | ||
| 306 | } | ||
| 307 | } | ||
| 308 | |||
| 309 | // Some hosts may send ZLPs even when not required by the HID spec, so we'll loop as long as total == 0. | ||
| 310 | if total > 0 { | ||
| 311 | break; | ||
| 312 | } | ||
| 313 | } | ||
| 314 | |||
| 315 | if starting_offset > 0 { | ||
| 316 | Err(ReadError::Sync(starting_offset..total)) | ||
| 317 | } else { | ||
| 318 | Ok(total) | ||
| 319 | } | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | pub trait RequestHandler { | ||
| 324 | /// Reads the value of report `id` into `buf` returning the size. | ||
| 325 | /// | ||
| 326 | /// Returns `None` if `id` is invalid or no data is available. | ||
| 327 | fn get_report(&self, id: ReportId, buf: &mut [u8]) -> Option<usize> { | ||
| 328 | let _ = (id, buf); | ||
| 329 | None | ||
| 330 | } | ||
| 331 | |||
| 332 | /// Sets the value of report `id` to `data`. | ||
| 333 | fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse { | ||
| 334 | let _ = (id, data); | ||
| 335 | OutResponse::Rejected | ||
| 336 | } | ||
| 337 | |||
| 338 | /// Get the idle rate for `id`. | ||
| 339 | /// | ||
| 340 | /// If `id` is `None`, get the idle rate for all reports. Returning `None` | ||
| 341 | /// will reject the control request. Any duration at or above 1.024 seconds | ||
| 342 | /// or below 4ms will be returned as an indefinite idle rate. | ||
| 343 | fn get_idle(&self, id: Option<ReportId>) -> Option<Duration> { | ||
| 344 | let _ = id; | ||
| 345 | None | ||
| 346 | } | ||
| 347 | |||
| 348 | /// Set the idle rate for `id` to `dur`. | ||
| 349 | /// | ||
| 350 | /// If `id` is `None`, set the idle rate of all input reports to `dur`. If | ||
| 351 | /// an indefinite duration is requested, `dur` will be set to `Duration::MAX`. | ||
| 352 | fn set_idle(&self, id: Option<ReportId>, dur: Duration) { | ||
| 353 | let _ = (id, dur); | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | struct Control<'d> { | ||
| 358 | report_descriptor: &'static [u8], | ||
| 359 | request_handler: Option<&'d dyn RequestHandler>, | ||
| 360 | out_report_offset: &'d AtomicUsize, | ||
| 361 | hid_descriptor: [u8; 9], | ||
| 362 | } | ||
| 363 | |||
| 364 | impl<'a> Control<'a> { | ||
| 365 | fn new( | ||
| 366 | report_descriptor: &'static [u8], | ||
| 367 | request_handler: Option<&'a dyn RequestHandler>, | ||
| 368 | out_report_offset: &'a AtomicUsize, | ||
| 369 | ) -> Self { | ||
| 370 | Control { | ||
| 371 | report_descriptor, | ||
| 372 | request_handler, | ||
| 373 | out_report_offset, | ||
| 374 | hid_descriptor: [ | ||
| 375 | // Length of buf inclusive of size prefix | ||
| 376 | 9, | ||
| 377 | // Descriptor type | ||
| 378 | HID_DESC_DESCTYPE_HID, | ||
| 379 | // HID Class spec version | ||
| 380 | HID_DESC_SPEC_1_10[0], | ||
| 381 | HID_DESC_SPEC_1_10[1], | ||
| 382 | // Country code not supported | ||
| 383 | HID_DESC_COUNTRY_UNSPEC, | ||
| 384 | // Number of following descriptors | ||
| 385 | 1, | ||
| 386 | // We have a HID report descriptor the host should read | ||
| 387 | HID_DESC_DESCTYPE_HID_REPORT, | ||
| 388 | // HID report descriptor size, | ||
| 389 | (report_descriptor.len() & 0xFF) as u8, | ||
| 390 | (report_descriptor.len() >> 8 & 0xFF) as u8, | ||
| 391 | ], | ||
| 392 | } | ||
| 393 | } | ||
| 394 | |||
| 395 | fn build<'d, D: Driver<'d>>( | ||
| 396 | &'d mut self, | ||
| 397 | builder: &mut UsbDeviceBuilder<'d, D>, | ||
| 398 | ep_out: Option<&D::EndpointOut>, | ||
| 399 | ep_in: &D::EndpointIn, | ||
| 400 | ) { | ||
| 401 | let len = self.report_descriptor.len(); | ||
| 402 | let if_num = builder.alloc_interface_with_handler(self); | ||
| 403 | |||
| 404 | builder.config_descriptor.interface( | ||
| 405 | if_num, | ||
| 406 | USB_CLASS_HID, | ||
| 407 | USB_SUBCLASS_NONE, | ||
| 408 | USB_PROTOCOL_NONE, | ||
| 409 | ); | ||
| 410 | |||
| 411 | // HID descriptor | ||
| 412 | builder.config_descriptor.write( | ||
| 413 | HID_DESC_DESCTYPE_HID, | ||
| 414 | &[ | ||
| 415 | // HID Class spec version | ||
| 416 | HID_DESC_SPEC_1_10[0], | ||
| 417 | HID_DESC_SPEC_1_10[1], | ||
| 418 | // Country code not supported | ||
| 419 | HID_DESC_COUNTRY_UNSPEC, | ||
| 420 | // Number of following descriptors | ||
| 421 | 1, | ||
| 422 | // We have a HID report descriptor the host should read | ||
| 423 | HID_DESC_DESCTYPE_HID_REPORT, | ||
| 424 | // HID report descriptor size, | ||
| 425 | (len & 0xFF) as u8, | ||
| 426 | (len >> 8 & 0xFF) as u8, | ||
| 427 | ], | ||
| 428 | ); | ||
| 429 | |||
| 430 | builder.config_descriptor.endpoint(ep_in.info()); | ||
| 431 | if let Some(ep) = ep_out { | ||
| 432 | builder.config_descriptor.endpoint(ep.info()); | ||
| 433 | } | ||
| 434 | } | ||
| 435 | } | ||
| 436 | |||
| 437 | impl<'d> ControlHandler for Control<'d> { | ||
| 438 | fn reset(&mut self) { | ||
| 439 | self.out_report_offset.store(0, Ordering::Release); | ||
| 440 | } | ||
| 441 | |||
| 442 | fn control_out(&mut self, req: embassy_usb::control::Request, data: &[u8]) -> OutResponse { | ||
| 443 | trace!("HID control_out {:?} {=[u8]:x}", req, data); | ||
| 444 | if let RequestType::Class = req.request_type { | ||
| 445 | match req.request { | ||
| 446 | HID_REQ_SET_IDLE => { | ||
| 447 | if let Some(handler) = self.request_handler { | ||
| 448 | let id = req.value as u8; | ||
| 449 | let id = (id != 0).then(|| ReportId::In(id)); | ||
| 450 | let dur = u64::from(req.value >> 8); | ||
| 451 | let dur = if dur == 0 { | ||
| 452 | Duration::MAX | ||
| 453 | } else { | ||
| 454 | Duration::from_millis(4 * dur) | ||
| 455 | }; | ||
| 456 | handler.set_idle(id, dur); | ||
| 457 | } | ||
| 458 | OutResponse::Accepted | ||
| 459 | } | ||
| 460 | HID_REQ_SET_REPORT => match (ReportId::try_from(req.value), self.request_handler) { | ||
| 461 | (Ok(id), Some(handler)) => handler.set_report(id, data), | ||
| 462 | _ => OutResponse::Rejected, | ||
| 463 | }, | ||
| 464 | HID_REQ_SET_PROTOCOL => { | ||
| 465 | if req.value == 1 { | ||
| 466 | OutResponse::Accepted | ||
| 467 | } else { | ||
| 468 | warn!("HID Boot Protocol is unsupported."); | ||
| 469 | OutResponse::Rejected // UNSUPPORTED: Boot Protocol | ||
| 470 | } | ||
| 471 | } | ||
| 472 | _ => OutResponse::Rejected, | ||
| 473 | } | ||
| 474 | } else { | ||
| 475 | OutResponse::Rejected // UNSUPPORTED: SET_DESCRIPTOR | ||
| 476 | } | ||
| 477 | } | ||
| 478 | |||
| 479 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { | ||
| 480 | trace!("HID control_in {:?}", req); | ||
| 481 | match (req.request_type, req.request) { | ||
| 482 | (RequestType::Standard, Request::GET_DESCRIPTOR) => match (req.value >> 8) as u8 { | ||
| 483 | HID_DESC_DESCTYPE_HID_REPORT => InResponse::Accepted(self.report_descriptor), | ||
| 484 | HID_DESC_DESCTYPE_HID => InResponse::Accepted(&self.hid_descriptor), | ||
| 485 | _ => InResponse::Rejected, | ||
| 486 | }, | ||
| 487 | (RequestType::Class, HID_REQ_GET_REPORT) => { | ||
| 488 | let size = match ReportId::try_from(req.value) { | ||
| 489 | Ok(id) => self.request_handler.and_then(|x| x.get_report(id, buf)), | ||
| 490 | Err(_) => None, | ||
| 491 | }; | ||
| 492 | |||
| 493 | if let Some(size) = size { | ||
| 494 | InResponse::Accepted(&buf[0..size]) | ||
| 495 | } else { | ||
| 496 | InResponse::Rejected | ||
| 497 | } | ||
| 498 | } | ||
| 499 | (RequestType::Class, HID_REQ_GET_IDLE) => { | ||
| 500 | if let Some(handler) = self.request_handler { | ||
| 501 | let id = req.value as u8; | ||
| 502 | let id = (id != 0).then(|| ReportId::In(id)); | ||
| 503 | if let Some(dur) = handler.get_idle(id) { | ||
| 504 | let dur = u8::try_from(dur.as_millis() / 4).unwrap_or(0); | ||
| 505 | buf[0] = dur; | ||
| 506 | InResponse::Accepted(&buf[0..1]) | ||
| 507 | } else { | ||
| 508 | InResponse::Rejected | ||
| 509 | } | ||
| 510 | } else { | ||
| 511 | InResponse::Rejected | ||
| 512 | } | ||
| 513 | } | ||
| 514 | (RequestType::Class, HID_REQ_GET_PROTOCOL) => { | ||
| 515 | // UNSUPPORTED: Boot Protocol | ||
| 516 | buf[0] = 1; | ||
| 517 | InResponse::Accepted(&buf[0..1]) | ||
| 518 | } | ||
| 519 | _ => InResponse::Rejected, | ||
| 520 | } | ||
| 521 | } | ||
| 522 | } | ||
diff --git a/embassy-usb-serial/Cargo.toml b/embassy-usb-serial/Cargo.toml new file mode 100644 index 000000000..553f2a13c --- /dev/null +++ b/embassy-usb-serial/Cargo.toml | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | [package] | ||
| 2 | name = "embassy-usb-serial" | ||
| 3 | version = "0.1.0" | ||
| 4 | edition = "2021" | ||
| 5 | |||
| 6 | [package.metadata.embassy_docs] | ||
| 7 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-serial-v$VERSION/embassy-usb-serial/src/" | ||
| 8 | src_base_git = "https://github.com/embassy-rs/embassy/blob/master/embassy-usb-serial/src/" | ||
| 9 | features = ["defmt"] | ||
| 10 | flavors = [ | ||
| 11 | { name = "default", target = "thumbv7em-none-eabihf" }, | ||
| 12 | ] | ||
| 13 | |||
| 14 | [dependencies] | ||
| 15 | embassy = { version = "0.1.0", path = "../embassy" } | ||
| 16 | embassy-usb = { version = "0.1.0", path = "../embassy-usb" } | ||
| 17 | |||
| 18 | defmt = { version = "0.3", optional = true } | ||
| 19 | log = { version = "0.4.14", optional = true } | ||
diff --git a/embassy-usb-serial/src/fmt.rs b/embassy-usb-serial/src/fmt.rs new file mode 100644 index 000000000..066970813 --- /dev/null +++ b/embassy-usb-serial/src/fmt.rs | |||
| @@ -0,0 +1,225 @@ | |||
| 1 | #![macro_use] | ||
| 2 | #![allow(unused_macros)] | ||
| 3 | |||
| 4 | #[cfg(all(feature = "defmt", feature = "log"))] | ||
| 5 | compile_error!("You may not enable both `defmt` and `log` features."); | ||
| 6 | |||
| 7 | macro_rules! assert { | ||
| 8 | ($($x:tt)*) => { | ||
| 9 | { | ||
| 10 | #[cfg(not(feature = "defmt"))] | ||
| 11 | ::core::assert!($($x)*); | ||
| 12 | #[cfg(feature = "defmt")] | ||
| 13 | ::defmt::assert!($($x)*); | ||
| 14 | } | ||
| 15 | }; | ||
| 16 | } | ||
| 17 | |||
| 18 | macro_rules! assert_eq { | ||
| 19 | ($($x:tt)*) => { | ||
| 20 | { | ||
| 21 | #[cfg(not(feature = "defmt"))] | ||
| 22 | ::core::assert_eq!($($x)*); | ||
| 23 | #[cfg(feature = "defmt")] | ||
| 24 | ::defmt::assert_eq!($($x)*); | ||
| 25 | } | ||
| 26 | }; | ||
| 27 | } | ||
| 28 | |||
| 29 | macro_rules! assert_ne { | ||
| 30 | ($($x:tt)*) => { | ||
| 31 | { | ||
| 32 | #[cfg(not(feature = "defmt"))] | ||
| 33 | ::core::assert_ne!($($x)*); | ||
| 34 | #[cfg(feature = "defmt")] | ||
| 35 | ::defmt::assert_ne!($($x)*); | ||
| 36 | } | ||
| 37 | }; | ||
| 38 | } | ||
| 39 | |||
| 40 | macro_rules! debug_assert { | ||
| 41 | ($($x:tt)*) => { | ||
| 42 | { | ||
| 43 | #[cfg(not(feature = "defmt"))] | ||
| 44 | ::core::debug_assert!($($x)*); | ||
| 45 | #[cfg(feature = "defmt")] | ||
| 46 | ::defmt::debug_assert!($($x)*); | ||
| 47 | } | ||
| 48 | }; | ||
| 49 | } | ||
| 50 | |||
| 51 | macro_rules! debug_assert_eq { | ||
| 52 | ($($x:tt)*) => { | ||
| 53 | { | ||
| 54 | #[cfg(not(feature = "defmt"))] | ||
| 55 | ::core::debug_assert_eq!($($x)*); | ||
| 56 | #[cfg(feature = "defmt")] | ||
| 57 | ::defmt::debug_assert_eq!($($x)*); | ||
| 58 | } | ||
| 59 | }; | ||
| 60 | } | ||
| 61 | |||
| 62 | macro_rules! debug_assert_ne { | ||
| 63 | ($($x:tt)*) => { | ||
| 64 | { | ||
| 65 | #[cfg(not(feature = "defmt"))] | ||
| 66 | ::core::debug_assert_ne!($($x)*); | ||
| 67 | #[cfg(feature = "defmt")] | ||
| 68 | ::defmt::debug_assert_ne!($($x)*); | ||
| 69 | } | ||
| 70 | }; | ||
| 71 | } | ||
| 72 | |||
| 73 | macro_rules! todo { | ||
| 74 | ($($x:tt)*) => { | ||
| 75 | { | ||
| 76 | #[cfg(not(feature = "defmt"))] | ||
| 77 | ::core::todo!($($x)*); | ||
| 78 | #[cfg(feature = "defmt")] | ||
| 79 | ::defmt::todo!($($x)*); | ||
| 80 | } | ||
| 81 | }; | ||
| 82 | } | ||
| 83 | |||
| 84 | macro_rules! unreachable { | ||
| 85 | ($($x:tt)*) => { | ||
| 86 | { | ||
| 87 | #[cfg(not(feature = "defmt"))] | ||
| 88 | ::core::unreachable!($($x)*); | ||
| 89 | #[cfg(feature = "defmt")] | ||
| 90 | ::defmt::unreachable!($($x)*); | ||
| 91 | } | ||
| 92 | }; | ||
| 93 | } | ||
| 94 | |||
| 95 | macro_rules! panic { | ||
| 96 | ($($x:tt)*) => { | ||
| 97 | { | ||
| 98 | #[cfg(not(feature = "defmt"))] | ||
| 99 | ::core::panic!($($x)*); | ||
| 100 | #[cfg(feature = "defmt")] | ||
| 101 | ::defmt::panic!($($x)*); | ||
| 102 | } | ||
| 103 | }; | ||
| 104 | } | ||
| 105 | |||
| 106 | macro_rules! trace { | ||
| 107 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 108 | { | ||
| 109 | #[cfg(feature = "log")] | ||
| 110 | ::log::trace!($s $(, $x)*); | ||
| 111 | #[cfg(feature = "defmt")] | ||
| 112 | ::defmt::trace!($s $(, $x)*); | ||
| 113 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 114 | let _ = ($( & $x ),*); | ||
| 115 | } | ||
| 116 | }; | ||
| 117 | } | ||
| 118 | |||
| 119 | macro_rules! debug { | ||
| 120 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 121 | { | ||
| 122 | #[cfg(feature = "log")] | ||
| 123 | ::log::debug!($s $(, $x)*); | ||
| 124 | #[cfg(feature = "defmt")] | ||
| 125 | ::defmt::debug!($s $(, $x)*); | ||
| 126 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 127 | let _ = ($( & $x ),*); | ||
| 128 | } | ||
| 129 | }; | ||
| 130 | } | ||
| 131 | |||
| 132 | macro_rules! info { | ||
| 133 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 134 | { | ||
| 135 | #[cfg(feature = "log")] | ||
| 136 | ::log::info!($s $(, $x)*); | ||
| 137 | #[cfg(feature = "defmt")] | ||
| 138 | ::defmt::info!($s $(, $x)*); | ||
| 139 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 140 | let _ = ($( & $x ),*); | ||
| 141 | } | ||
| 142 | }; | ||
| 143 | } | ||
| 144 | |||
| 145 | macro_rules! warn { | ||
| 146 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 147 | { | ||
| 148 | #[cfg(feature = "log")] | ||
| 149 | ::log::warn!($s $(, $x)*); | ||
| 150 | #[cfg(feature = "defmt")] | ||
| 151 | ::defmt::warn!($s $(, $x)*); | ||
| 152 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 153 | let _ = ($( & $x ),*); | ||
| 154 | } | ||
| 155 | }; | ||
| 156 | } | ||
| 157 | |||
| 158 | macro_rules! error { | ||
| 159 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 160 | { | ||
| 161 | #[cfg(feature = "log")] | ||
| 162 | ::log::error!($s $(, $x)*); | ||
| 163 | #[cfg(feature = "defmt")] | ||
| 164 | ::defmt::error!($s $(, $x)*); | ||
| 165 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 166 | let _ = ($( & $x ),*); | ||
| 167 | } | ||
| 168 | }; | ||
| 169 | } | ||
| 170 | |||
| 171 | #[cfg(feature = "defmt")] | ||
| 172 | macro_rules! unwrap { | ||
| 173 | ($($x:tt)*) => { | ||
| 174 | ::defmt::unwrap!($($x)*) | ||
| 175 | }; | ||
| 176 | } | ||
| 177 | |||
| 178 | #[cfg(not(feature = "defmt"))] | ||
| 179 | macro_rules! unwrap { | ||
| 180 | ($arg:expr) => { | ||
| 181 | match $crate::fmt::Try::into_result($arg) { | ||
| 182 | ::core::result::Result::Ok(t) => t, | ||
| 183 | ::core::result::Result::Err(e) => { | ||
| 184 | ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | }; | ||
| 188 | ($arg:expr, $($msg:expr),+ $(,)? ) => { | ||
| 189 | match $crate::fmt::Try::into_result($arg) { | ||
| 190 | ::core::result::Result::Ok(t) => t, | ||
| 191 | ::core::result::Result::Err(e) => { | ||
| 192 | ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 199 | pub struct NoneError; | ||
| 200 | |||
| 201 | pub trait Try { | ||
| 202 | type Ok; | ||
| 203 | type Error; | ||
| 204 | fn into_result(self) -> Result<Self::Ok, Self::Error>; | ||
| 205 | } | ||
| 206 | |||
| 207 | impl<T> Try for Option<T> { | ||
| 208 | type Ok = T; | ||
| 209 | type Error = NoneError; | ||
| 210 | |||
| 211 | #[inline] | ||
| 212 | fn into_result(self) -> Result<T, NoneError> { | ||
| 213 | self.ok_or(NoneError) | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | impl<T, E> Try for Result<T, E> { | ||
| 218 | type Ok = T; | ||
| 219 | type Error = E; | ||
| 220 | |||
| 221 | #[inline] | ||
| 222 | fn into_result(self) -> Self { | ||
| 223 | self | ||
| 224 | } | ||
| 225 | } | ||
diff --git a/embassy-usb-serial/src/lib.rs b/embassy-usb-serial/src/lib.rs new file mode 100644 index 000000000..07352fac5 --- /dev/null +++ b/embassy-usb-serial/src/lib.rs | |||
| @@ -0,0 +1,369 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![feature(generic_associated_types)] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | // This mod MUST go first, so that the others see its macros. | ||
| 6 | pub(crate) mod fmt; | ||
| 7 | |||
| 8 | use core::cell::Cell; | ||
| 9 | use core::mem::{self, MaybeUninit}; | ||
| 10 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 11 | use embassy::blocking_mutex::CriticalSectionMutex; | ||
| 12 | use embassy_usb::control::{self, ControlHandler, InResponse, OutResponse, Request}; | ||
| 13 | use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut, ReadError, WriteError}; | ||
| 14 | use embassy_usb::{driver::Driver, types::*, UsbDeviceBuilder}; | ||
| 15 | |||
| 16 | /// This should be used as `device_class` when building the `UsbDevice`. | ||
| 17 | pub const USB_CLASS_CDC: u8 = 0x02; | ||
| 18 | |||
| 19 | const USB_CLASS_CDC_DATA: u8 = 0x0a; | ||
| 20 | const CDC_SUBCLASS_ACM: u8 = 0x02; | ||
| 21 | const CDC_PROTOCOL_NONE: u8 = 0x00; | ||
| 22 | |||
| 23 | const CS_INTERFACE: u8 = 0x24; | ||
| 24 | const CDC_TYPE_HEADER: u8 = 0x00; | ||
| 25 | const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01; | ||
| 26 | const CDC_TYPE_ACM: u8 = 0x02; | ||
| 27 | const CDC_TYPE_UNION: u8 = 0x06; | ||
| 28 | |||
| 29 | const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00; | ||
| 30 | #[allow(unused)] | ||
| 31 | const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01; | ||
| 32 | const REQ_SET_LINE_CODING: u8 = 0x20; | ||
| 33 | const REQ_GET_LINE_CODING: u8 = 0x21; | ||
| 34 | const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; | ||
| 35 | |||
| 36 | pub struct State<'a> { | ||
| 37 | control: MaybeUninit<Control<'a>>, | ||
| 38 | shared: ControlShared, | ||
| 39 | } | ||
| 40 | |||
| 41 | impl<'a> State<'a> { | ||
| 42 | pub fn new() -> Self { | ||
| 43 | Self { | ||
| 44 | control: MaybeUninit::uninit(), | ||
| 45 | shared: Default::default(), | ||
| 46 | } | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | /// Packet level implementation of a CDC-ACM serial port. | ||
| 51 | /// | ||
| 52 | /// This class can be used directly and it has the least overhead due to directly reading and | ||
| 53 | /// writing USB packets with no intermediate buffers, but it will not act like a stream-like serial | ||
| 54 | /// port. The following constraints must be followed if you use this class directly: | ||
| 55 | /// | ||
| 56 | /// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes. | ||
| 57 | /// - `write_packet` must not be called with a buffer larger than max_packet_size bytes. | ||
| 58 | /// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the | ||
| 59 | /// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP) | ||
| 60 | /// can be sent if there is no other data to send. This is because USB bulk transactions must be | ||
| 61 | /// terminated with a short packet, even if the bulk endpoint is used for stream-like data. | ||
| 62 | pub struct CdcAcmClass<'d, D: Driver<'d>> { | ||
| 63 | _comm_ep: D::EndpointIn, | ||
| 64 | _data_if: InterfaceNumber, | ||
| 65 | read_ep: D::EndpointOut, | ||
| 66 | write_ep: D::EndpointIn, | ||
| 67 | control: &'d ControlShared, | ||
| 68 | } | ||
| 69 | |||
| 70 | struct Control<'a> { | ||
| 71 | shared: &'a ControlShared, | ||
| 72 | } | ||
| 73 | |||
| 74 | /// Shared data between Control and CdcAcmClass | ||
| 75 | struct ControlShared { | ||
| 76 | line_coding: CriticalSectionMutex<Cell<LineCoding>>, | ||
| 77 | dtr: AtomicBool, | ||
| 78 | rts: AtomicBool, | ||
| 79 | } | ||
| 80 | |||
| 81 | impl Default for ControlShared { | ||
| 82 | fn default() -> Self { | ||
| 83 | ControlShared { | ||
| 84 | dtr: AtomicBool::new(false), | ||
| 85 | rts: AtomicBool::new(false), | ||
| 86 | line_coding: CriticalSectionMutex::new(Cell::new(LineCoding { | ||
| 87 | stop_bits: StopBits::One, | ||
| 88 | data_bits: 8, | ||
| 89 | parity_type: ParityType::None, | ||
| 90 | data_rate: 8_000, | ||
| 91 | })), | ||
| 92 | } | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | impl<'a> Control<'a> { | ||
| 97 | fn shared(&mut self) -> &'a ControlShared { | ||
| 98 | self.shared | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | impl<'d> ControlHandler for Control<'d> { | ||
| 103 | fn reset(&mut self) { | ||
| 104 | let shared = self.shared(); | ||
| 105 | shared.line_coding.lock(|x| x.set(LineCoding::default())); | ||
| 106 | shared.dtr.store(false, Ordering::Relaxed); | ||
| 107 | shared.rts.store(false, Ordering::Relaxed); | ||
| 108 | } | ||
| 109 | |||
| 110 | fn control_out(&mut self, req: control::Request, data: &[u8]) -> OutResponse { | ||
| 111 | match req.request { | ||
| 112 | REQ_SEND_ENCAPSULATED_COMMAND => { | ||
| 113 | // We don't actually support encapsulated commands but pretend we do for standards | ||
| 114 | // compatibility. | ||
| 115 | OutResponse::Accepted | ||
| 116 | } | ||
| 117 | REQ_SET_LINE_CODING if data.len() >= 7 => { | ||
| 118 | let coding = LineCoding { | ||
| 119 | data_rate: u32::from_le_bytes(data[0..4].try_into().unwrap()), | ||
| 120 | stop_bits: data[4].into(), | ||
| 121 | parity_type: data[5].into(), | ||
| 122 | data_bits: data[6], | ||
| 123 | }; | ||
| 124 | self.shared().line_coding.lock(|x| x.set(coding)); | ||
| 125 | debug!("Set line coding to: {:?}", coding); | ||
| 126 | |||
| 127 | OutResponse::Accepted | ||
| 128 | } | ||
| 129 | REQ_SET_CONTROL_LINE_STATE => { | ||
| 130 | let dtr = (req.value & 0x0001) != 0; | ||
| 131 | let rts = (req.value & 0x0002) != 0; | ||
| 132 | |||
| 133 | let shared = self.shared(); | ||
| 134 | shared.dtr.store(dtr, Ordering::Relaxed); | ||
| 135 | shared.rts.store(rts, Ordering::Relaxed); | ||
| 136 | debug!("Set dtr {}, rts {}", dtr, rts); | ||
| 137 | |||
| 138 | OutResponse::Accepted | ||
| 139 | } | ||
| 140 | _ => OutResponse::Rejected, | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { | ||
| 145 | match req.request { | ||
| 146 | // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. | ||
| 147 | REQ_GET_LINE_CODING if req.length == 7 => { | ||
| 148 | debug!("Sending line coding"); | ||
| 149 | let coding = self.shared().line_coding.lock(|x| x.get()); | ||
| 150 | assert!(buf.len() >= 7); | ||
| 151 | buf[0..4].copy_from_slice(&coding.data_rate.to_le_bytes()); | ||
| 152 | buf[4] = coding.stop_bits as u8; | ||
| 153 | buf[5] = coding.parity_type as u8; | ||
| 154 | buf[6] = coding.data_bits; | ||
| 155 | InResponse::Accepted(&buf[0..7]) | ||
| 156 | } | ||
| 157 | _ => InResponse::Rejected, | ||
| 158 | } | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> { | ||
| 163 | /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For | ||
| 164 | /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. | ||
| 165 | pub fn new( | ||
| 166 | builder: &mut UsbDeviceBuilder<'d, D>, | ||
| 167 | state: &'d mut State<'d>, | ||
| 168 | max_packet_size: u16, | ||
| 169 | ) -> Self { | ||
| 170 | let control = state.control.write(Control { | ||
| 171 | shared: &state.shared, | ||
| 172 | }); | ||
| 173 | |||
| 174 | let control_shared = &state.shared; | ||
| 175 | |||
| 176 | assert!(builder.control_buf_len() >= 7); | ||
| 177 | |||
| 178 | let comm_if = builder.alloc_interface_with_handler(control); | ||
| 179 | let comm_ep = builder.alloc_interrupt_endpoint_in(8, 255); | ||
| 180 | let data_if = builder.alloc_interface(); | ||
| 181 | let read_ep = builder.alloc_bulk_endpoint_out(max_packet_size); | ||
| 182 | let write_ep = builder.alloc_bulk_endpoint_in(max_packet_size); | ||
| 183 | |||
| 184 | builder.config_descriptor.iad( | ||
| 185 | comm_if, | ||
| 186 | 2, | ||
| 187 | USB_CLASS_CDC, | ||
| 188 | CDC_SUBCLASS_ACM, | ||
| 189 | CDC_PROTOCOL_NONE, | ||
| 190 | ); | ||
| 191 | builder.config_descriptor.interface( | ||
| 192 | comm_if, | ||
| 193 | USB_CLASS_CDC, | ||
| 194 | CDC_SUBCLASS_ACM, | ||
| 195 | CDC_PROTOCOL_NONE, | ||
| 196 | ); | ||
| 197 | builder.config_descriptor.write( | ||
| 198 | CS_INTERFACE, | ||
| 199 | &[ | ||
| 200 | CDC_TYPE_HEADER, // bDescriptorSubtype | ||
| 201 | 0x10, | ||
| 202 | 0x01, // bcdCDC (1.10) | ||
| 203 | ], | ||
| 204 | ); | ||
| 205 | builder.config_descriptor.write( | ||
| 206 | CS_INTERFACE, | ||
| 207 | &[ | ||
| 208 | CDC_TYPE_ACM, // bDescriptorSubtype | ||
| 209 | 0x00, // bmCapabilities | ||
| 210 | ], | ||
| 211 | ); | ||
| 212 | builder.config_descriptor.write( | ||
| 213 | CS_INTERFACE, | ||
| 214 | &[ | ||
| 215 | CDC_TYPE_UNION, // bDescriptorSubtype | ||
| 216 | comm_if.into(), // bControlInterface | ||
| 217 | data_if.into(), // bSubordinateInterface | ||
| 218 | ], | ||
| 219 | ); | ||
| 220 | builder.config_descriptor.write( | ||
| 221 | CS_INTERFACE, | ||
| 222 | &[ | ||
| 223 | CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype | ||
| 224 | 0x00, // bmCapabilities | ||
| 225 | data_if.into(), // bDataInterface | ||
| 226 | ], | ||
| 227 | ); | ||
| 228 | builder.config_descriptor.endpoint(comm_ep.info()); | ||
| 229 | |||
| 230 | builder | ||
| 231 | .config_descriptor | ||
| 232 | .interface(data_if, USB_CLASS_CDC_DATA, 0x00, 0x00); | ||
| 233 | builder.config_descriptor.endpoint(write_ep.info()); | ||
| 234 | builder.config_descriptor.endpoint(read_ep.info()); | ||
| 235 | |||
| 236 | CdcAcmClass { | ||
| 237 | _comm_ep: comm_ep, | ||
| 238 | _data_if: data_if, | ||
| 239 | read_ep, | ||
| 240 | write_ep, | ||
| 241 | control: control_shared, | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | /// Gets the maximum packet size in bytes. | ||
| 246 | pub fn max_packet_size(&self) -> u16 { | ||
| 247 | // The size is the same for both endpoints. | ||
| 248 | self.read_ep.info().max_packet_size | ||
| 249 | } | ||
| 250 | |||
| 251 | /// Gets the current line coding. The line coding contains information that's mainly relevant | ||
| 252 | /// for USB to UART serial port emulators, and can be ignored if not relevant. | ||
| 253 | pub fn line_coding(&self) -> LineCoding { | ||
| 254 | self.control.line_coding.lock(|x| x.get()) | ||
| 255 | } | ||
| 256 | |||
| 257 | /// Gets the DTR (data terminal ready) state | ||
| 258 | pub fn dtr(&self) -> bool { | ||
| 259 | self.control.dtr.load(Ordering::Relaxed) | ||
| 260 | } | ||
| 261 | |||
| 262 | /// Gets the RTS (request to send) state | ||
| 263 | pub fn rts(&self) -> bool { | ||
| 264 | self.control.rts.load(Ordering::Relaxed) | ||
| 265 | } | ||
| 266 | |||
| 267 | /// Writes a single packet into the IN endpoint. | ||
| 268 | pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), WriteError> { | ||
| 269 | self.write_ep.write(data).await | ||
| 270 | } | ||
| 271 | |||
| 272 | /// Reads a single packet from the OUT endpoint. | ||
| 273 | pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, ReadError> { | ||
| 274 | self.read_ep.read(data).await | ||
| 275 | } | ||
| 276 | |||
| 277 | /// Waits for the USB host to enable this interface | ||
| 278 | pub async fn wait_connection(&mut self) { | ||
| 279 | self.read_ep.wait_enabled().await | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | /// Number of stop bits for LineCoding | ||
| 284 | #[derive(Copy, Clone, PartialEq, Eq, defmt::Format)] | ||
| 285 | pub enum StopBits { | ||
| 286 | /// 1 stop bit | ||
| 287 | One = 0, | ||
| 288 | |||
| 289 | /// 1.5 stop bits | ||
| 290 | OnePointFive = 1, | ||
| 291 | |||
| 292 | /// 2 stop bits | ||
| 293 | Two = 2, | ||
| 294 | } | ||
| 295 | |||
| 296 | impl From<u8> for StopBits { | ||
| 297 | fn from(value: u8) -> Self { | ||
| 298 | if value <= 2 { | ||
| 299 | unsafe { mem::transmute(value) } | ||
| 300 | } else { | ||
| 301 | StopBits::One | ||
| 302 | } | ||
| 303 | } | ||
| 304 | } | ||
| 305 | |||
| 306 | /// Parity for LineCoding | ||
| 307 | #[derive(Copy, Clone, PartialEq, Eq, defmt::Format)] | ||
| 308 | pub enum ParityType { | ||
| 309 | None = 0, | ||
| 310 | Odd = 1, | ||
| 311 | Event = 2, | ||
| 312 | Mark = 3, | ||
| 313 | Space = 4, | ||
| 314 | } | ||
| 315 | |||
| 316 | impl From<u8> for ParityType { | ||
| 317 | fn from(value: u8) -> Self { | ||
| 318 | if value <= 4 { | ||
| 319 | unsafe { mem::transmute(value) } | ||
| 320 | } else { | ||
| 321 | ParityType::None | ||
| 322 | } | ||
| 323 | } | ||
| 324 | } | ||
| 325 | |||
| 326 | /// Line coding parameters | ||
| 327 | /// | ||
| 328 | /// This is provided by the host for specifying the standard UART parameters such as baud rate. Can | ||
| 329 | /// be ignored if you don't plan to interface with a physical UART. | ||
| 330 | #[derive(Clone, Copy, defmt::Format)] | ||
| 331 | pub struct LineCoding { | ||
| 332 | stop_bits: StopBits, | ||
| 333 | data_bits: u8, | ||
| 334 | parity_type: ParityType, | ||
| 335 | data_rate: u32, | ||
| 336 | } | ||
| 337 | |||
| 338 | impl LineCoding { | ||
| 339 | /// Gets the number of stop bits for UART communication. | ||
| 340 | pub fn stop_bits(&self) -> StopBits { | ||
| 341 | self.stop_bits | ||
| 342 | } | ||
| 343 | |||
| 344 | /// Gets the number of data bits for UART communication. | ||
| 345 | pub fn data_bits(&self) -> u8 { | ||
| 346 | self.data_bits | ||
| 347 | } | ||
| 348 | |||
| 349 | /// Gets the parity type for UART communication. | ||
| 350 | pub fn parity_type(&self) -> ParityType { | ||
| 351 | self.parity_type | ||
| 352 | } | ||
| 353 | |||
| 354 | /// Gets the data rate in bits per second for UART communication. | ||
| 355 | pub fn data_rate(&self) -> u32 { | ||
| 356 | self.data_rate | ||
| 357 | } | ||
| 358 | } | ||
| 359 | |||
| 360 | impl Default for LineCoding { | ||
| 361 | fn default() -> Self { | ||
| 362 | LineCoding { | ||
| 363 | stop_bits: StopBits::One, | ||
| 364 | data_bits: 8, | ||
| 365 | parity_type: ParityType::None, | ||
| 366 | data_rate: 8_000, | ||
| 367 | } | ||
| 368 | } | ||
| 369 | } | ||
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml new file mode 100644 index 000000000..48c205064 --- /dev/null +++ b/embassy-usb/Cargo.toml | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | [package] | ||
| 2 | name = "embassy-usb" | ||
| 3 | version = "0.1.0" | ||
| 4 | edition = "2021" | ||
| 5 | |||
| 6 | [package.metadata.embassy_docs] | ||
| 7 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-v$VERSION/embassy-usb/src/" | ||
| 8 | src_base_git = "https://github.com/embassy-rs/embassy/blob/master/embassy-usb/src/" | ||
| 9 | features = ["defmt"] | ||
| 10 | flavors = [ | ||
| 11 | { name = "default", target = "thumbv7em-none-eabihf" }, | ||
| 12 | ] | ||
| 13 | |||
| 14 | [dependencies] | ||
| 15 | embassy = { version = "0.1.0", path = "../embassy" } | ||
| 16 | |||
| 17 | defmt = { version = "0.3", optional = true } | ||
| 18 | log = { version = "0.4.14", optional = true } | ||
| 19 | heapless = "0.7.10" \ No newline at end of file | ||
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs new file mode 100644 index 000000000..4bbcd3e56 --- /dev/null +++ b/embassy-usb/src/builder.rs | |||
| @@ -0,0 +1,386 @@ | |||
| 1 | use heapless::Vec; | ||
| 2 | |||
| 3 | use super::control::ControlHandler; | ||
| 4 | use super::descriptor::{BosWriter, DescriptorWriter}; | ||
| 5 | use super::driver::{Driver, EndpointAllocError}; | ||
| 6 | use super::types::*; | ||
| 7 | use super::UsbDevice; | ||
| 8 | use super::MAX_INTERFACE_COUNT; | ||
| 9 | |||
| 10 | #[derive(Debug, Copy, Clone)] | ||
| 11 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 12 | #[non_exhaustive] | ||
| 13 | pub struct Config<'a> { | ||
| 14 | pub(crate) vendor_id: u16, | ||
| 15 | pub(crate) product_id: u16, | ||
| 16 | |||
| 17 | /// Device class code assigned by USB.org. Set to `0xff` for vendor-specific | ||
| 18 | /// devices that do not conform to any class. | ||
| 19 | /// | ||
| 20 | /// Default: `0x00` (class code specified by interfaces) | ||
| 21 | pub device_class: u8, | ||
| 22 | |||
| 23 | /// Device sub-class code. Depends on class. | ||
| 24 | /// | ||
| 25 | /// Default: `0x00` | ||
| 26 | pub device_sub_class: u8, | ||
| 27 | |||
| 28 | /// Device protocol code. Depends on class and sub-class. | ||
| 29 | /// | ||
| 30 | /// Default: `0x00` | ||
| 31 | pub device_protocol: u8, | ||
| 32 | |||
| 33 | /// Device release version in BCD. | ||
| 34 | /// | ||
| 35 | /// Default: `0x0010` ("0.1") | ||
| 36 | pub device_release: u16, | ||
| 37 | |||
| 38 | /// Maximum packet size in bytes for the control endpoint 0. | ||
| 39 | /// | ||
| 40 | /// Valid values are 8, 16, 32 and 64. There's generally no need to change this from the default | ||
| 41 | /// value of 8 bytes unless a class uses control transfers for sending large amounts of data, in | ||
| 42 | /// which case using a larger packet size may be more efficient. | ||
| 43 | /// | ||
| 44 | /// Default: 8 bytes | ||
| 45 | pub max_packet_size_0: u8, | ||
| 46 | |||
| 47 | /// Manufacturer name string descriptor. | ||
| 48 | /// | ||
| 49 | /// Default: (none) | ||
| 50 | pub manufacturer: Option<&'a str>, | ||
| 51 | |||
| 52 | /// Product name string descriptor. | ||
| 53 | /// | ||
| 54 | /// Default: (none) | ||
| 55 | pub product: Option<&'a str>, | ||
| 56 | |||
| 57 | /// Serial number string descriptor. | ||
| 58 | /// | ||
| 59 | /// Default: (none) | ||
| 60 | pub serial_number: Option<&'a str>, | ||
| 61 | |||
| 62 | /// Whether the device supports remotely waking up the host is requested. | ||
| 63 | /// | ||
| 64 | /// Default: `false` | ||
| 65 | pub supports_remote_wakeup: bool, | ||
| 66 | |||
| 67 | /// Configures the device as a composite device with interface association descriptors. | ||
| 68 | /// | ||
| 69 | /// If set to `true`, the following fields should have the given values: | ||
| 70 | /// | ||
| 71 | /// - `device_class` = `0xEF` | ||
| 72 | /// - `device_sub_class` = `0x02` | ||
| 73 | /// - `device_protocol` = `0x01` | ||
| 74 | pub composite_with_iads: bool, | ||
| 75 | |||
| 76 | /// Whether the device has its own power source. | ||
| 77 | /// | ||
| 78 | /// This should be set to `true` even if the device is sometimes self-powered and may not | ||
| 79 | /// always draw power from the USB bus. | ||
| 80 | /// | ||
| 81 | /// Default: `false` | ||
| 82 | /// | ||
| 83 | /// See also: `max_power` | ||
| 84 | pub self_powered: bool, | ||
| 85 | |||
| 86 | /// Maximum current drawn from the USB bus by the device, in milliamps. | ||
| 87 | /// | ||
| 88 | /// The default is 100 mA. If your device always uses an external power source and never draws | ||
| 89 | /// power from the USB bus, this can be set to 0. | ||
| 90 | /// | ||
| 91 | /// See also: `self_powered` | ||
| 92 | /// | ||
| 93 | /// Default: 100mA | ||
| 94 | /// Max: 500mA | ||
| 95 | pub max_power: u16, | ||
| 96 | } | ||
| 97 | |||
| 98 | impl<'a> Config<'a> { | ||
| 99 | pub fn new(vid: u16, pid: u16) -> Self { | ||
| 100 | Self { | ||
| 101 | device_class: 0x00, | ||
| 102 | device_sub_class: 0x00, | ||
| 103 | device_protocol: 0x00, | ||
| 104 | max_packet_size_0: 8, | ||
| 105 | vendor_id: vid, | ||
| 106 | product_id: pid, | ||
| 107 | device_release: 0x0010, | ||
| 108 | manufacturer: None, | ||
| 109 | product: None, | ||
| 110 | serial_number: None, | ||
| 111 | self_powered: false, | ||
| 112 | supports_remote_wakeup: false, | ||
| 113 | composite_with_iads: false, | ||
| 114 | max_power: 100, | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | /// Used to build new [`UsbDevice`]s. | ||
| 120 | pub struct UsbDeviceBuilder<'d, D: Driver<'d>> { | ||
| 121 | config: Config<'d>, | ||
| 122 | interfaces: Vec<(u8, &'d mut dyn ControlHandler), MAX_INTERFACE_COUNT>, | ||
| 123 | control_buf: &'d mut [u8], | ||
| 124 | |||
| 125 | driver: D, | ||
| 126 | next_interface_number: u8, | ||
| 127 | next_string_index: u8, | ||
| 128 | |||
| 129 | // TODO make not pub? | ||
| 130 | pub device_descriptor: DescriptorWriter<'d>, | ||
| 131 | pub config_descriptor: DescriptorWriter<'d>, | ||
| 132 | pub bos_descriptor: BosWriter<'d>, | ||
| 133 | } | ||
| 134 | |||
| 135 | impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> { | ||
| 136 | /// Creates a builder for constructing a new [`UsbDevice`]. | ||
| 137 | /// | ||
| 138 | /// `control_buf` is a buffer used for USB control request data. It should be sized | ||
| 139 | /// large enough for the length of the largest control request (in or out) | ||
| 140 | /// anticipated by any class added to the device. | ||
| 141 | pub fn new( | ||
| 142 | driver: D, | ||
| 143 | config: Config<'d>, | ||
| 144 | device_descriptor_buf: &'d mut [u8], | ||
| 145 | config_descriptor_buf: &'d mut [u8], | ||
| 146 | bos_descriptor_buf: &'d mut [u8], | ||
| 147 | control_buf: &'d mut [u8], | ||
| 148 | ) -> Self { | ||
| 149 | // Magic values specified in USB-IF ECN on IADs. | ||
| 150 | if config.composite_with_iads | ||
| 151 | && (config.device_class != 0xEF | ||
| 152 | || config.device_sub_class != 0x02 | ||
| 153 | || config.device_protocol != 0x01) | ||
| 154 | { | ||
| 155 | panic!("if composite_with_iads is set, you must set device_class = 0xEF, device_sub_class = 0x02, device_protocol = 0x01"); | ||
| 156 | } | ||
| 157 | |||
| 158 | if config.max_power > 500 { | ||
| 159 | panic!("The maximum allowed value for `max_power` is 500mA"); | ||
| 160 | } | ||
| 161 | |||
| 162 | match config.max_packet_size_0 { | ||
| 163 | 8 | 16 | 32 | 64 => {} | ||
| 164 | _ => panic!("invalid max_packet_size_0, the allowed values are 8, 16, 32 or 64"), | ||
| 165 | } | ||
| 166 | |||
| 167 | let mut device_descriptor = DescriptorWriter::new(device_descriptor_buf); | ||
| 168 | let mut config_descriptor = DescriptorWriter::new(config_descriptor_buf); | ||
| 169 | let mut bos_descriptor = BosWriter::new(DescriptorWriter::new(bos_descriptor_buf)); | ||
| 170 | |||
| 171 | device_descriptor.device(&config); | ||
| 172 | config_descriptor.configuration(&config); | ||
| 173 | bos_descriptor.bos(); | ||
| 174 | |||
| 175 | UsbDeviceBuilder { | ||
| 176 | driver, | ||
| 177 | config, | ||
| 178 | interfaces: Vec::new(), | ||
| 179 | control_buf, | ||
| 180 | next_interface_number: 0, | ||
| 181 | next_string_index: 4, | ||
| 182 | |||
| 183 | device_descriptor, | ||
| 184 | config_descriptor, | ||
| 185 | bos_descriptor, | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | /// Creates the [`UsbDevice`] instance with the configuration in this builder. | ||
| 190 | pub async fn build(mut self) -> UsbDevice<'d, D> { | ||
| 191 | self.config_descriptor.end_configuration(); | ||
| 192 | self.bos_descriptor.end_bos(); | ||
| 193 | |||
| 194 | UsbDevice::build( | ||
| 195 | self.driver, | ||
| 196 | self.config, | ||
| 197 | self.device_descriptor.into_buf(), | ||
| 198 | self.config_descriptor.into_buf(), | ||
| 199 | self.bos_descriptor.writer.into_buf(), | ||
| 200 | self.interfaces, | ||
| 201 | self.control_buf, | ||
| 202 | ) | ||
| 203 | .await | ||
| 204 | } | ||
| 205 | |||
| 206 | /// Allocates a new interface number. | ||
| 207 | pub fn alloc_interface(&mut self) -> InterfaceNumber { | ||
| 208 | let number = self.next_interface_number; | ||
| 209 | self.next_interface_number += 1; | ||
| 210 | |||
| 211 | InterfaceNumber::new(number) | ||
| 212 | } | ||
| 213 | |||
| 214 | /// Returns the size of the control request data buffer. Can be used by | ||
| 215 | /// classes to validate the buffer is large enough for their needs. | ||
| 216 | pub fn control_buf_len(&self) -> usize { | ||
| 217 | self.control_buf.len() | ||
| 218 | } | ||
| 219 | |||
| 220 | /// Allocates a new interface number, with a handler that will be called | ||
| 221 | /// for all the control requests directed to it. | ||
| 222 | pub fn alloc_interface_with_handler( | ||
| 223 | &mut self, | ||
| 224 | handler: &'d mut dyn ControlHandler, | ||
| 225 | ) -> InterfaceNumber { | ||
| 226 | let number = self.next_interface_number; | ||
| 227 | self.next_interface_number += 1; | ||
| 228 | |||
| 229 | if self.interfaces.push((number, handler)).is_err() { | ||
| 230 | panic!("max class count reached") | ||
| 231 | } | ||
| 232 | |||
| 233 | InterfaceNumber::new(number) | ||
| 234 | } | ||
| 235 | |||
| 236 | /// Allocates a new string index. | ||
| 237 | pub fn alloc_string(&mut self) -> StringIndex { | ||
| 238 | let index = self.next_string_index; | ||
| 239 | self.next_string_index += 1; | ||
| 240 | |||
| 241 | StringIndex::new(index) | ||
| 242 | } | ||
| 243 | |||
| 244 | /// Allocates an in endpoint. | ||
| 245 | /// | ||
| 246 | /// This directly delegates to [`Driver::alloc_endpoint_in`], so see that method for details. In most | ||
| 247 | /// cases classes should call the endpoint type specific methods instead. | ||
| 248 | pub fn alloc_endpoint_in( | ||
| 249 | &mut self, | ||
| 250 | ep_addr: Option<EndpointAddress>, | ||
| 251 | ep_type: EndpointType, | ||
| 252 | max_packet_size: u16, | ||
| 253 | interval: u8, | ||
| 254 | ) -> Result<D::EndpointIn, EndpointAllocError> { | ||
| 255 | self.driver | ||
| 256 | .alloc_endpoint_in(ep_addr, ep_type, max_packet_size, interval) | ||
| 257 | } | ||
| 258 | |||
| 259 | /// Allocates an out endpoint. | ||
| 260 | /// | ||
| 261 | /// This directly delegates to [`Driver::alloc_endpoint_out`], so see that method for details. In most | ||
| 262 | /// cases classes should call the endpoint type specific methods instead. | ||
| 263 | pub fn alloc_endpoint_out( | ||
| 264 | &mut self, | ||
| 265 | ep_addr: Option<EndpointAddress>, | ||
| 266 | ep_type: EndpointType, | ||
| 267 | max_packet_size: u16, | ||
| 268 | interval: u8, | ||
| 269 | ) -> Result<D::EndpointOut, EndpointAllocError> { | ||
| 270 | self.driver | ||
| 271 | .alloc_endpoint_out(ep_addr, ep_type, max_packet_size, interval) | ||
| 272 | } | ||
| 273 | |||
| 274 | /// Allocates a control in endpoint. | ||
| 275 | /// | ||
| 276 | /// This crate implements the control state machine only for endpoint 0. If classes want to | ||
| 277 | /// support control requests in other endpoints, the state machine must be implemented manually. | ||
| 278 | /// This should rarely be needed by classes. | ||
| 279 | /// | ||
| 280 | /// # Arguments | ||
| 281 | /// | ||
| 282 | /// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64. | ||
| 283 | /// | ||
| 284 | /// # Panics | ||
| 285 | /// | ||
| 286 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | ||
| 287 | /// feasibly recoverable. | ||
| 288 | #[inline] | ||
| 289 | pub fn alloc_control_endpoint_in(&mut self, max_packet_size: u16) -> D::EndpointIn { | ||
| 290 | self.alloc_endpoint_in(None, EndpointType::Control, max_packet_size, 0) | ||
| 291 | .expect("alloc_ep failed") | ||
| 292 | } | ||
| 293 | |||
| 294 | /// Allocates a control out endpoint. | ||
| 295 | /// | ||
| 296 | /// This crate implements the control state machine only for endpoint 0. If classes want to | ||
| 297 | /// support control requests in other endpoints, the state machine must be implemented manually. | ||
| 298 | /// This should rarely be needed by classes. | ||
| 299 | /// | ||
| 300 | /// # Arguments | ||
| 301 | /// | ||
| 302 | /// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64. | ||
| 303 | /// | ||
| 304 | /// # Panics | ||
| 305 | /// | ||
| 306 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | ||
| 307 | /// feasibly recoverable. | ||
| 308 | #[inline] | ||
| 309 | pub fn alloc_control_pipe(&mut self, max_packet_size: u16) -> D::ControlPipe { | ||
| 310 | self.driver | ||
| 311 | .alloc_control_pipe(max_packet_size) | ||
| 312 | .expect("alloc_control_pipe failed") | ||
| 313 | } | ||
| 314 | |||
| 315 | /// Allocates a bulk in endpoint. | ||
| 316 | /// | ||
| 317 | /// # Arguments | ||
| 318 | /// | ||
| 319 | /// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64. | ||
| 320 | /// | ||
| 321 | /// # Panics | ||
| 322 | /// | ||
| 323 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | ||
| 324 | /// feasibly recoverable. | ||
| 325 | #[inline] | ||
| 326 | pub fn alloc_bulk_endpoint_in(&mut self, max_packet_size: u16) -> D::EndpointIn { | ||
| 327 | self.alloc_endpoint_in(None, EndpointType::Bulk, max_packet_size, 0) | ||
| 328 | .expect("alloc_ep failed") | ||
| 329 | } | ||
| 330 | |||
| 331 | /// Allocates a bulk out endpoint. | ||
| 332 | /// | ||
| 333 | /// # Arguments | ||
| 334 | /// | ||
| 335 | /// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64. | ||
| 336 | /// | ||
| 337 | /// # Panics | ||
| 338 | /// | ||
| 339 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | ||
| 340 | /// feasibly recoverable. | ||
| 341 | #[inline] | ||
| 342 | pub fn alloc_bulk_endpoint_out(&mut self, max_packet_size: u16) -> D::EndpointOut { | ||
| 343 | self.alloc_endpoint_out(None, EndpointType::Bulk, max_packet_size, 0) | ||
| 344 | .expect("alloc_ep failed") | ||
| 345 | } | ||
| 346 | |||
| 347 | /// Allocates a bulk in endpoint. | ||
| 348 | /// | ||
| 349 | /// # Arguments | ||
| 350 | /// | ||
| 351 | /// * `max_packet_size` - Maximum packet size in bytes. Cannot exceed 64 bytes. | ||
| 352 | /// | ||
| 353 | /// # Panics | ||
| 354 | /// | ||
| 355 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | ||
| 356 | /// feasibly recoverable. | ||
| 357 | #[inline] | ||
| 358 | pub fn alloc_interrupt_endpoint_in( | ||
| 359 | &mut self, | ||
| 360 | max_packet_size: u16, | ||
| 361 | interval: u8, | ||
| 362 | ) -> D::EndpointIn { | ||
| 363 | self.alloc_endpoint_in(None, EndpointType::Interrupt, max_packet_size, interval) | ||
| 364 | .expect("alloc_ep failed") | ||
| 365 | } | ||
| 366 | |||
| 367 | /// Allocates a bulk in endpoint. | ||
| 368 | /// | ||
| 369 | /// # Arguments | ||
| 370 | /// | ||
| 371 | /// * `max_packet_size` - Maximum packet size in bytes. Cannot exceed 64 bytes. | ||
| 372 | /// | ||
| 373 | /// # Panics | ||
| 374 | /// | ||
| 375 | /// Panics if endpoint allocation fails, because running out of endpoints or memory is not | ||
| 376 | /// feasibly recoverable. | ||
| 377 | #[inline] | ||
| 378 | pub fn alloc_interrupt_endpoint_out( | ||
| 379 | &mut self, | ||
| 380 | max_packet_size: u16, | ||
| 381 | interval: u8, | ||
| 382 | ) -> D::EndpointOut { | ||
| 383 | self.alloc_endpoint_out(None, EndpointType::Interrupt, max_packet_size, interval) | ||
| 384 | .expect("alloc_ep failed") | ||
| 385 | } | ||
| 386 | } | ||
diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs new file mode 100644 index 000000000..7c46812bd --- /dev/null +++ b/embassy-usb/src/control.rs | |||
| @@ -0,0 +1,330 @@ | |||
| 1 | use core::mem; | ||
| 2 | |||
| 3 | use crate::descriptor::DescriptorWriter; | ||
| 4 | use crate::driver::{self, ReadError}; | ||
| 5 | use crate::DEFAULT_ALTERNATE_SETTING; | ||
| 6 | |||
| 7 | use super::types::*; | ||
| 8 | |||
| 9 | /// Control request type. | ||
| 10 | #[repr(u8)] | ||
| 11 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 12 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 13 | pub enum RequestType { | ||
| 14 | /// Request is a USB standard request. Usually handled by | ||
| 15 | /// [`UsbDevice`](crate::device::UsbDevice). | ||
| 16 | Standard = 0, | ||
| 17 | /// Request is intended for a USB class. | ||
| 18 | Class = 1, | ||
| 19 | /// Request is vendor-specific. | ||
| 20 | Vendor = 2, | ||
| 21 | /// Reserved. | ||
| 22 | Reserved = 3, | ||
| 23 | } | ||
| 24 | |||
| 25 | /// Control request recipient. | ||
| 26 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 27 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 28 | pub enum Recipient { | ||
| 29 | /// Request is intended for the entire device. | ||
| 30 | Device = 0, | ||
| 31 | /// Request is intended for an interface. Generally, the `index` field of the request specifies | ||
| 32 | /// the interface number. | ||
| 33 | Interface = 1, | ||
| 34 | /// Request is intended for an endpoint. Generally, the `index` field of the request specifies | ||
| 35 | /// the endpoint address. | ||
| 36 | Endpoint = 2, | ||
| 37 | /// None of the above. | ||
| 38 | Other = 3, | ||
| 39 | /// Reserved. | ||
| 40 | Reserved = 4, | ||
| 41 | } | ||
| 42 | |||
| 43 | /// A control request read from a SETUP packet. | ||
| 44 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 45 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 46 | pub struct Request { | ||
| 47 | /// Direction of the request. | ||
| 48 | pub direction: UsbDirection, | ||
| 49 | /// Type of the request. | ||
| 50 | pub request_type: RequestType, | ||
| 51 | /// Recipient of the request. | ||
| 52 | pub recipient: Recipient, | ||
| 53 | /// Request code. The meaning of the value depends on the previous fields. | ||
| 54 | pub request: u8, | ||
| 55 | /// Request value. The meaning of the value depends on the previous fields. | ||
| 56 | pub value: u16, | ||
| 57 | /// Request index. The meaning of the value depends on the previous fields. | ||
| 58 | pub index: u16, | ||
| 59 | /// Length of the DATA stage. For control OUT transfers this is the exact length of the data the | ||
| 60 | /// host sent. For control IN transfers this is the maximum length of data the device should | ||
| 61 | /// return. | ||
| 62 | pub length: u16, | ||
| 63 | } | ||
| 64 | |||
| 65 | impl Request { | ||
| 66 | /// Standard USB control request Get Status | ||
| 67 | pub const GET_STATUS: u8 = 0; | ||
| 68 | |||
| 69 | /// Standard USB control request Clear Feature | ||
| 70 | pub const CLEAR_FEATURE: u8 = 1; | ||
| 71 | |||
| 72 | /// Standard USB control request Set Feature | ||
| 73 | pub const SET_FEATURE: u8 = 3; | ||
| 74 | |||
| 75 | /// Standard USB control request Set Address | ||
| 76 | pub const SET_ADDRESS: u8 = 5; | ||
| 77 | |||
| 78 | /// Standard USB control request Get Descriptor | ||
| 79 | pub const GET_DESCRIPTOR: u8 = 6; | ||
| 80 | |||
| 81 | /// Standard USB control request Set Descriptor | ||
| 82 | pub const SET_DESCRIPTOR: u8 = 7; | ||
| 83 | |||
| 84 | /// Standard USB control request Get Configuration | ||
| 85 | pub const GET_CONFIGURATION: u8 = 8; | ||
| 86 | |||
| 87 | /// Standard USB control request Set Configuration | ||
| 88 | pub const SET_CONFIGURATION: u8 = 9; | ||
| 89 | |||
| 90 | /// Standard USB control request Get Interface | ||
| 91 | pub const GET_INTERFACE: u8 = 10; | ||
| 92 | |||
| 93 | /// Standard USB control request Set Interface | ||
| 94 | pub const SET_INTERFACE: u8 = 11; | ||
| 95 | |||
| 96 | /// Standard USB control request Synch Frame | ||
| 97 | pub const SYNCH_FRAME: u8 = 12; | ||
| 98 | |||
| 99 | /// Standard USB feature Endpoint Halt for Set/Clear Feature | ||
| 100 | pub const FEATURE_ENDPOINT_HALT: u16 = 0; | ||
| 101 | |||
| 102 | /// Standard USB feature Device Remote Wakeup for Set/Clear Feature | ||
| 103 | pub const FEATURE_DEVICE_REMOTE_WAKEUP: u16 = 1; | ||
| 104 | |||
| 105 | /// Parses a USB control request from a byte array. | ||
| 106 | pub fn parse(buf: &[u8; 8]) -> Request { | ||
| 107 | let rt = buf[0]; | ||
| 108 | let recipient = rt & 0b11111; | ||
| 109 | |||
| 110 | Request { | ||
| 111 | direction: rt.into(), | ||
| 112 | request_type: unsafe { mem::transmute((rt >> 5) & 0b11) }, | ||
| 113 | recipient: if recipient <= 3 { | ||
| 114 | unsafe { mem::transmute(recipient) } | ||
| 115 | } else { | ||
| 116 | Recipient::Reserved | ||
| 117 | }, | ||
| 118 | request: buf[1], | ||
| 119 | value: (buf[2] as u16) | ((buf[3] as u16) << 8), | ||
| 120 | index: (buf[4] as u16) | ((buf[5] as u16) << 8), | ||
| 121 | length: (buf[6] as u16) | ((buf[7] as u16) << 8), | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | /// Gets the descriptor type and index from the value field of a GET_DESCRIPTOR request. | ||
| 126 | pub fn descriptor_type_index(&self) -> (u8, u8) { | ||
| 127 | ((self.value >> 8) as u8, self.value as u8) | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 132 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 133 | pub enum OutResponse { | ||
| 134 | Accepted, | ||
| 135 | Rejected, | ||
| 136 | } | ||
| 137 | |||
| 138 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 139 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 140 | pub enum InResponse<'a> { | ||
| 141 | Accepted(&'a [u8]), | ||
| 142 | Rejected, | ||
| 143 | } | ||
| 144 | |||
| 145 | /// Handler for control requests. | ||
| 146 | /// | ||
| 147 | /// All methods are optional callbacks that will be called by | ||
| 148 | /// [`UsbDevice::run()`](crate::UsbDevice::run) | ||
| 149 | pub trait ControlHandler { | ||
| 150 | /// Called after a USB reset after the bus reset sequence is complete. | ||
| 151 | fn reset(&mut self) {} | ||
| 152 | |||
| 153 | /// Called when a control request is received with direction HostToDevice. | ||
| 154 | /// | ||
| 155 | /// # Arguments | ||
| 156 | /// | ||
| 157 | /// * `req` - The request from the SETUP packet. | ||
| 158 | /// * `data` - The data from the request. | ||
| 159 | fn control_out(&mut self, req: Request, data: &[u8]) -> OutResponse { | ||
| 160 | let _ = (req, data); | ||
| 161 | OutResponse::Rejected | ||
| 162 | } | ||
| 163 | |||
| 164 | /// Called when a control request is received with direction DeviceToHost. | ||
| 165 | /// | ||
| 166 | /// You should write the response to `resp`, then return `InResponse::Accepted(len)` | ||
| 167 | /// with the length of the response. | ||
| 168 | /// | ||
| 169 | /// # Arguments | ||
| 170 | /// | ||
| 171 | /// * `req` - The request from the SETUP packet. | ||
| 172 | fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> { | ||
| 173 | let _ = (req, buf); | ||
| 174 | InResponse::Rejected | ||
| 175 | } | ||
| 176 | |||
| 177 | fn set_interface(&mut self, alternate_setting: u16) -> OutResponse { | ||
| 178 | if alternate_setting == u16::from(DEFAULT_ALTERNATE_SETTING) { | ||
| 179 | OutResponse::Accepted | ||
| 180 | } else { | ||
| 181 | OutResponse::Rejected | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | fn get_interface<'a>(&'a mut self, buf: &'a mut [u8]) -> InResponse<'a> { | ||
| 186 | buf[0] = DEFAULT_ALTERNATE_SETTING; | ||
| 187 | InResponse::Accepted(&buf[0..1]) | ||
| 188 | } | ||
| 189 | |||
| 190 | fn get_status<'a>(&'a mut self, buf: &'a mut [u8]) -> InResponse { | ||
| 191 | let status: u16 = 0; | ||
| 192 | buf[0..2].copy_from_slice(&status.to_le_bytes()); | ||
| 193 | InResponse::Accepted(&buf[0..2]) | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | /// Typestate representing a ControlPipe in the DATA IN stage | ||
| 198 | #[derive(Debug)] | ||
| 199 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 200 | pub(crate) struct DataInStage { | ||
| 201 | pub(crate) length: usize, | ||
| 202 | } | ||
| 203 | |||
| 204 | /// Typestate representing a ControlPipe in the DATA OUT stage | ||
| 205 | #[derive(Debug)] | ||
| 206 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 207 | pub(crate) struct DataOutStage { | ||
| 208 | length: usize, | ||
| 209 | } | ||
| 210 | |||
| 211 | /// Typestate representing a ControlPipe in the STATUS stage | ||
| 212 | #[derive(Debug)] | ||
| 213 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 214 | pub(crate) struct StatusStage {} | ||
| 215 | |||
| 216 | #[derive(Debug)] | ||
| 217 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 218 | pub(crate) enum Setup { | ||
| 219 | DataIn(Request, DataInStage), | ||
| 220 | DataOut(Request, DataOutStage), | ||
| 221 | } | ||
| 222 | |||
| 223 | pub(crate) struct ControlPipe<C: driver::ControlPipe> { | ||
| 224 | control: C, | ||
| 225 | } | ||
| 226 | |||
| 227 | impl<C: driver::ControlPipe> ControlPipe<C> { | ||
| 228 | pub(crate) fn new(control: C) -> Self { | ||
| 229 | ControlPipe { control } | ||
| 230 | } | ||
| 231 | |||
| 232 | pub(crate) async fn setup(&mut self) -> Setup { | ||
| 233 | let req = self.control.setup().await; | ||
| 234 | trace!("control request: {:02x}", req); | ||
| 235 | |||
| 236 | match (req.direction, req.length) { | ||
| 237 | (UsbDirection::Out, n) => Setup::DataOut( | ||
| 238 | req, | ||
| 239 | DataOutStage { | ||
| 240 | length: usize::from(n), | ||
| 241 | }, | ||
| 242 | ), | ||
| 243 | (UsbDirection::In, n) => Setup::DataIn( | ||
| 244 | req, | ||
| 245 | DataInStage { | ||
| 246 | length: usize::from(n), | ||
| 247 | }, | ||
| 248 | ), | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | pub(crate) async fn data_out<'a>( | ||
| 253 | &mut self, | ||
| 254 | buf: &'a mut [u8], | ||
| 255 | stage: DataOutStage, | ||
| 256 | ) -> Result<(&'a [u8], StatusStage), ReadError> { | ||
| 257 | if stage.length == 0 { | ||
| 258 | Ok((&[], StatusStage {})) | ||
| 259 | } else { | ||
| 260 | let req_length = stage.length; | ||
| 261 | let max_packet_size = self.control.max_packet_size(); | ||
| 262 | let mut total = 0; | ||
| 263 | |||
| 264 | for chunk in buf.chunks_mut(max_packet_size) { | ||
| 265 | let size = self.control.data_out(chunk).await?; | ||
| 266 | total += size; | ||
| 267 | if size < max_packet_size || total == req_length { | ||
| 268 | break; | ||
| 269 | } | ||
| 270 | } | ||
| 271 | |||
| 272 | let res = &buf[0..total]; | ||
| 273 | #[cfg(feature = "defmt")] | ||
| 274 | trace!(" control out data: {:02x}", buf); | ||
| 275 | #[cfg(not(feature = "defmt"))] | ||
| 276 | trace!(" control out data: {:02x?}", buf); | ||
| 277 | |||
| 278 | Ok((res, StatusStage {})) | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | pub(crate) async fn accept_in(&mut self, buf: &[u8], stage: DataInStage) { | ||
| 283 | #[cfg(feature = "defmt")] | ||
| 284 | trace!(" control in accept {:02x}", buf); | ||
| 285 | #[cfg(not(feature = "defmt"))] | ||
| 286 | trace!(" control in accept {:02x?}", buf); | ||
| 287 | |||
| 288 | let req_len = stage.length; | ||
| 289 | let len = buf.len().min(req_len); | ||
| 290 | let max_packet_size = self.control.max_packet_size(); | ||
| 291 | let need_zlp = len != req_len && (len % usize::from(max_packet_size)) == 0; | ||
| 292 | |||
| 293 | let mut chunks = buf[0..len] | ||
| 294 | .chunks(max_packet_size) | ||
| 295 | .chain(need_zlp.then(|| -> &[u8] { &[] })); | ||
| 296 | |||
| 297 | while let Some(chunk) = chunks.next() { | ||
| 298 | match self.control.data_in(chunk, chunks.size_hint().0 == 0).await { | ||
| 299 | Ok(()) => {} | ||
| 300 | Err(e) => { | ||
| 301 | warn!("control accept_in failed: {:?}", e); | ||
| 302 | return; | ||
| 303 | } | ||
| 304 | } | ||
| 305 | } | ||
| 306 | } | ||
| 307 | |||
| 308 | pub(crate) async fn accept_in_writer( | ||
| 309 | &mut self, | ||
| 310 | req: Request, | ||
| 311 | stage: DataInStage, | ||
| 312 | f: impl FnOnce(&mut DescriptorWriter), | ||
| 313 | ) { | ||
| 314 | let mut buf = [0; 256]; | ||
| 315 | let mut w = DescriptorWriter::new(&mut buf); | ||
| 316 | f(&mut w); | ||
| 317 | let pos = w.position().min(usize::from(req.length)); | ||
| 318 | self.accept_in(&buf[..pos], stage).await | ||
| 319 | } | ||
| 320 | |||
| 321 | pub(crate) fn accept(&mut self, _: StatusStage) { | ||
| 322 | trace!(" control accept"); | ||
| 323 | self.control.accept(); | ||
| 324 | } | ||
| 325 | |||
| 326 | pub(crate) fn reject(&mut self) { | ||
| 327 | trace!(" control reject"); | ||
| 328 | self.control.reject(); | ||
| 329 | } | ||
| 330 | } | ||
diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs new file mode 100644 index 000000000..ff971e127 --- /dev/null +++ b/embassy-usb/src/descriptor.rs | |||
| @@ -0,0 +1,369 @@ | |||
| 1 | use super::builder::Config; | ||
| 2 | use super::{types::*, CONFIGURATION_VALUE, DEFAULT_ALTERNATE_SETTING}; | ||
| 3 | |||
| 4 | /// Standard descriptor types | ||
| 5 | #[allow(missing_docs)] | ||
| 6 | pub mod descriptor_type { | ||
| 7 | pub const DEVICE: u8 = 1; | ||
| 8 | pub const CONFIGURATION: u8 = 2; | ||
| 9 | pub const STRING: u8 = 3; | ||
| 10 | pub const INTERFACE: u8 = 4; | ||
| 11 | pub const ENDPOINT: u8 = 5; | ||
| 12 | pub const IAD: u8 = 11; | ||
| 13 | pub const BOS: u8 = 15; | ||
| 14 | pub const CAPABILITY: u8 = 16; | ||
| 15 | } | ||
| 16 | |||
| 17 | /// String descriptor language IDs. | ||
| 18 | pub mod lang_id { | ||
| 19 | /// English (US) | ||
| 20 | /// | ||
| 21 | /// Recommended for use as the first language ID for compatibility. | ||
| 22 | pub const ENGLISH_US: u16 = 0x0409; | ||
| 23 | } | ||
| 24 | |||
| 25 | /// Standard capability descriptor types | ||
| 26 | #[allow(missing_docs)] | ||
| 27 | pub mod capability_type { | ||
| 28 | pub const WIRELESS_USB: u8 = 1; | ||
| 29 | pub const USB_2_0_EXTENSION: u8 = 2; | ||
| 30 | pub const SS_USB_DEVICE: u8 = 3; | ||
| 31 | pub const CONTAINER_ID: u8 = 4; | ||
| 32 | pub const PLATFORM: u8 = 5; | ||
| 33 | } | ||
| 34 | |||
| 35 | /// A writer for USB descriptors. | ||
| 36 | pub struct DescriptorWriter<'a> { | ||
| 37 | buf: &'a mut [u8], | ||
| 38 | position: usize, | ||
| 39 | num_interfaces_mark: Option<usize>, | ||
| 40 | num_endpoints_mark: Option<usize>, | ||
| 41 | write_iads: bool, | ||
| 42 | } | ||
| 43 | |||
| 44 | impl<'a> DescriptorWriter<'a> { | ||
| 45 | pub(crate) fn new(buf: &'a mut [u8]) -> Self { | ||
| 46 | DescriptorWriter { | ||
| 47 | buf, | ||
| 48 | position: 0, | ||
| 49 | num_interfaces_mark: None, | ||
| 50 | num_endpoints_mark: None, | ||
| 51 | write_iads: false, | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | pub fn into_buf(self) -> &'a mut [u8] { | ||
| 56 | &mut self.buf[..self.position] | ||
| 57 | } | ||
| 58 | |||
| 59 | /// Gets the current position in the buffer, i.e. the number of bytes written so far. | ||
| 60 | pub fn position(&self) -> usize { | ||
| 61 | self.position | ||
| 62 | } | ||
| 63 | |||
| 64 | /// Writes an arbitrary (usually class-specific) descriptor. | ||
| 65 | pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8]) { | ||
| 66 | let length = descriptor.len(); | ||
| 67 | |||
| 68 | if (self.position + 2 + length) > self.buf.len() || (length + 2) > 255 { | ||
| 69 | panic!("Descriptor buffer full"); | ||
| 70 | } | ||
| 71 | |||
| 72 | self.buf[self.position] = (length + 2) as u8; | ||
| 73 | self.buf[self.position + 1] = descriptor_type; | ||
| 74 | |||
| 75 | let start = self.position + 2; | ||
| 76 | |||
| 77 | self.buf[start..start + length].copy_from_slice(descriptor); | ||
| 78 | |||
| 79 | self.position = start + length; | ||
| 80 | } | ||
| 81 | |||
| 82 | pub(crate) fn device(&mut self, config: &Config) { | ||
| 83 | self.write( | ||
| 84 | descriptor_type::DEVICE, | ||
| 85 | &[ | ||
| 86 | 0x10, | ||
| 87 | 0x02, // bcdUSB 2.1 | ||
| 88 | config.device_class, // bDeviceClass | ||
| 89 | config.device_sub_class, // bDeviceSubClass | ||
| 90 | config.device_protocol, // bDeviceProtocol | ||
| 91 | config.max_packet_size_0, // bMaxPacketSize0 | ||
| 92 | config.vendor_id as u8, | ||
| 93 | (config.vendor_id >> 8) as u8, // idVendor | ||
| 94 | config.product_id as u8, | ||
| 95 | (config.product_id >> 8) as u8, // idProduct | ||
| 96 | config.device_release as u8, | ||
| 97 | (config.device_release >> 8) as u8, // bcdDevice | ||
| 98 | config.manufacturer.map_or(0, |_| 1), // iManufacturer | ||
| 99 | config.product.map_or(0, |_| 2), // iProduct | ||
| 100 | config.serial_number.map_or(0, |_| 3), // iSerialNumber | ||
| 101 | 1, // bNumConfigurations | ||
| 102 | ], | ||
| 103 | ) | ||
| 104 | } | ||
| 105 | |||
| 106 | pub(crate) fn configuration(&mut self, config: &Config) { | ||
| 107 | self.num_interfaces_mark = Some(self.position + 4); | ||
| 108 | |||
| 109 | self.write_iads = config.composite_with_iads; | ||
| 110 | |||
| 111 | self.write( | ||
| 112 | descriptor_type::CONFIGURATION, | ||
| 113 | &[ | ||
| 114 | 0, | ||
| 115 | 0, // wTotalLength | ||
| 116 | 0, // bNumInterfaces | ||
| 117 | CONFIGURATION_VALUE, // bConfigurationValue | ||
| 118 | 0, // iConfiguration | ||
| 119 | 0x80 | if config.self_powered { 0x40 } else { 0x00 } | ||
| 120 | | if config.supports_remote_wakeup { | ||
| 121 | 0x20 | ||
| 122 | } else { | ||
| 123 | 0x00 | ||
| 124 | }, // bmAttributes | ||
| 125 | (config.max_power / 2) as u8, // bMaxPower | ||
| 126 | ], | ||
| 127 | ) | ||
| 128 | } | ||
| 129 | |||
| 130 | #[allow(unused)] | ||
| 131 | pub(crate) fn end_class(&mut self) { | ||
| 132 | self.num_endpoints_mark = None; | ||
| 133 | } | ||
| 134 | |||
| 135 | pub(crate) fn end_configuration(&mut self) { | ||
| 136 | let position = self.position as u16; | ||
| 137 | self.buf[2..4].copy_from_slice(&position.to_le_bytes()); | ||
| 138 | } | ||
| 139 | |||
| 140 | /// Writes a interface association descriptor. Call from `UsbClass::get_configuration_descriptors` | ||
| 141 | /// before writing the USB class or function's interface descriptors if your class has more than | ||
| 142 | /// one interface and wants to play nicely with composite devices on Windows. If the USB device | ||
| 143 | /// hosting the class was not configured as composite with IADs enabled, calling this function | ||
| 144 | /// does nothing, so it is safe to call from libraries. | ||
| 145 | /// | ||
| 146 | /// # Arguments | ||
| 147 | /// | ||
| 148 | /// * `first_interface` - Number of the function's first interface, previously allocated with | ||
| 149 | /// [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::interface). | ||
| 150 | /// * `interface_count` - Number of interfaces in the function. | ||
| 151 | /// * `function_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices | ||
| 152 | /// that do not conform to any class. | ||
| 153 | /// * `function_sub_class` - Sub-class code. Depends on class. | ||
| 154 | /// * `function_protocol` - Protocol code. Depends on class and sub-class. | ||
| 155 | pub fn iad( | ||
| 156 | &mut self, | ||
| 157 | first_interface: InterfaceNumber, | ||
| 158 | interface_count: u8, | ||
| 159 | function_class: u8, | ||
| 160 | function_sub_class: u8, | ||
| 161 | function_protocol: u8, | ||
| 162 | ) { | ||
| 163 | if !self.write_iads { | ||
| 164 | return; | ||
| 165 | } | ||
| 166 | |||
| 167 | self.write( | ||
| 168 | descriptor_type::IAD, | ||
| 169 | &[ | ||
| 170 | first_interface.into(), // bFirstInterface | ||
| 171 | interface_count, // bInterfaceCount | ||
| 172 | function_class, | ||
| 173 | function_sub_class, | ||
| 174 | function_protocol, | ||
| 175 | 0, | ||
| 176 | ], | ||
| 177 | ); | ||
| 178 | } | ||
| 179 | |||
| 180 | /// Writes a interface descriptor. | ||
| 181 | /// | ||
| 182 | /// # Arguments | ||
| 183 | /// | ||
| 184 | /// * `number` - Interface number previously allocated with | ||
| 185 | /// [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::interface). | ||
| 186 | /// * `interface_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices | ||
| 187 | /// that do not conform to any class. | ||
| 188 | /// * `interface_sub_class` - Sub-class code. Depends on class. | ||
| 189 | /// * `interface_protocol` - Protocol code. Depends on class and sub-class. | ||
| 190 | pub fn interface( | ||
| 191 | &mut self, | ||
| 192 | number: InterfaceNumber, | ||
| 193 | interface_class: u8, | ||
| 194 | interface_sub_class: u8, | ||
| 195 | interface_protocol: u8, | ||
| 196 | ) { | ||
| 197 | self.interface_alt( | ||
| 198 | number, | ||
| 199 | DEFAULT_ALTERNATE_SETTING, | ||
| 200 | interface_class, | ||
| 201 | interface_sub_class, | ||
| 202 | interface_protocol, | ||
| 203 | None, | ||
| 204 | ) | ||
| 205 | } | ||
| 206 | |||
| 207 | /// Writes a interface descriptor with a specific alternate setting and | ||
| 208 | /// interface string identifier. | ||
| 209 | /// | ||
| 210 | /// # Arguments | ||
| 211 | /// | ||
| 212 | /// * `number` - Interface number previously allocated with | ||
| 213 | /// [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::interface). | ||
| 214 | /// * `alternate_setting` - Number of the alternate setting | ||
| 215 | /// * `interface_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices | ||
| 216 | /// that do not conform to any class. | ||
| 217 | /// * `interface_sub_class` - Sub-class code. Depends on class. | ||
| 218 | /// * `interface_protocol` - Protocol code. Depends on class and sub-class. | ||
| 219 | /// * `interface_string` - Index of string descriptor describing this interface | ||
| 220 | |||
| 221 | pub fn interface_alt( | ||
| 222 | &mut self, | ||
| 223 | number: InterfaceNumber, | ||
| 224 | alternate_setting: u8, | ||
| 225 | interface_class: u8, | ||
| 226 | interface_sub_class: u8, | ||
| 227 | interface_protocol: u8, | ||
| 228 | interface_string: Option<StringIndex>, | ||
| 229 | ) { | ||
| 230 | if alternate_setting == DEFAULT_ALTERNATE_SETTING { | ||
| 231 | match self.num_interfaces_mark { | ||
| 232 | Some(mark) => self.buf[mark] += 1, | ||
| 233 | None => { | ||
| 234 | panic!("you can only call `interface/interface_alt` after `configuration`.") | ||
| 235 | } | ||
| 236 | }; | ||
| 237 | } | ||
| 238 | |||
| 239 | let str_index = interface_string.map_or(0, Into::into); | ||
| 240 | |||
| 241 | self.num_endpoints_mark = Some(self.position + 4); | ||
| 242 | |||
| 243 | self.write( | ||
| 244 | descriptor_type::INTERFACE, | ||
| 245 | &[ | ||
| 246 | number.into(), // bInterfaceNumber | ||
| 247 | alternate_setting, // bAlternateSetting | ||
| 248 | 0, // bNumEndpoints | ||
| 249 | interface_class, // bInterfaceClass | ||
| 250 | interface_sub_class, // bInterfaceSubClass | ||
| 251 | interface_protocol, // bInterfaceProtocol | ||
| 252 | str_index, // iInterface | ||
| 253 | ], | ||
| 254 | ); | ||
| 255 | } | ||
| 256 | |||
| 257 | /// Writes an endpoint descriptor. | ||
| 258 | /// | ||
| 259 | /// # Arguments | ||
| 260 | /// | ||
| 261 | /// * `endpoint` - Endpoint previously allocated with | ||
| 262 | /// [`UsbDeviceBuilder`](crate::bus::UsbDeviceBuilder). | ||
| 263 | pub fn endpoint(&mut self, endpoint: &EndpointInfo) { | ||
| 264 | match self.num_endpoints_mark { | ||
| 265 | Some(mark) => self.buf[mark] += 1, | ||
| 266 | None => panic!("you can only call `endpoint` after `interface/interface_alt`."), | ||
| 267 | }; | ||
| 268 | |||
| 269 | self.write( | ||
| 270 | descriptor_type::ENDPOINT, | ||
| 271 | &[ | ||
| 272 | endpoint.addr.into(), // bEndpointAddress | ||
| 273 | endpoint.ep_type as u8, // bmAttributes | ||
| 274 | endpoint.max_packet_size as u8, | ||
| 275 | (endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize | ||
| 276 | endpoint.interval, // bInterval | ||
| 277 | ], | ||
| 278 | ); | ||
| 279 | } | ||
| 280 | |||
| 281 | /// Writes a string descriptor. | ||
| 282 | pub(crate) fn string(&mut self, string: &str) { | ||
| 283 | let mut pos = self.position; | ||
| 284 | |||
| 285 | if pos + 2 > self.buf.len() { | ||
| 286 | panic!("Descriptor buffer full"); | ||
| 287 | } | ||
| 288 | |||
| 289 | self.buf[pos] = 0; // length placeholder | ||
| 290 | self.buf[pos + 1] = descriptor_type::STRING; | ||
| 291 | |||
| 292 | pos += 2; | ||
| 293 | |||
| 294 | for c in string.encode_utf16() { | ||
| 295 | if pos >= self.buf.len() { | ||
| 296 | panic!("Descriptor buffer full"); | ||
| 297 | } | ||
| 298 | |||
| 299 | self.buf[pos..pos + 2].copy_from_slice(&c.to_le_bytes()); | ||
| 300 | pos += 2; | ||
| 301 | } | ||
| 302 | |||
| 303 | self.buf[self.position] = (pos - self.position) as u8; | ||
| 304 | |||
| 305 | self.position = pos; | ||
| 306 | } | ||
| 307 | } | ||
| 308 | |||
| 309 | /// A writer for Binary Object Store descriptor. | ||
| 310 | pub struct BosWriter<'a> { | ||
| 311 | pub(crate) writer: DescriptorWriter<'a>, | ||
| 312 | num_caps_mark: Option<usize>, | ||
| 313 | } | ||
| 314 | |||
| 315 | impl<'a> BosWriter<'a> { | ||
| 316 | pub(crate) fn new(writer: DescriptorWriter<'a>) -> Self { | ||
| 317 | Self { | ||
| 318 | writer: writer, | ||
| 319 | num_caps_mark: None, | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | pub(crate) fn bos(&mut self) { | ||
| 324 | self.num_caps_mark = Some(self.writer.position + 4); | ||
| 325 | self.writer.write( | ||
| 326 | descriptor_type::BOS, | ||
| 327 | &[ | ||
| 328 | 0x00, 0x00, // wTotalLength | ||
| 329 | 0x00, // bNumDeviceCaps | ||
| 330 | ], | ||
| 331 | ); | ||
| 332 | |||
| 333 | self.capability(capability_type::USB_2_0_EXTENSION, &[0; 4]); | ||
| 334 | } | ||
| 335 | |||
| 336 | /// Writes capability descriptor to a BOS | ||
| 337 | /// | ||
| 338 | /// # Arguments | ||
| 339 | /// | ||
| 340 | /// * `capability_type` - Type of a capability | ||
| 341 | /// * `data` - Binary data of the descriptor | ||
| 342 | pub fn capability(&mut self, capability_type: u8, data: &[u8]) { | ||
| 343 | match self.num_caps_mark { | ||
| 344 | Some(mark) => self.writer.buf[mark] += 1, | ||
| 345 | None => panic!("called `capability` not between `bos` and `end_bos`."), | ||
| 346 | } | ||
| 347 | |||
| 348 | let mut start = self.writer.position; | ||
| 349 | let blen = data.len(); | ||
| 350 | |||
| 351 | if (start + blen + 3) > self.writer.buf.len() || (blen + 3) > 255 { | ||
| 352 | panic!("Descriptor buffer full"); | ||
| 353 | } | ||
| 354 | |||
| 355 | self.writer.buf[start] = (blen + 3) as u8; | ||
| 356 | self.writer.buf[start + 1] = descriptor_type::CAPABILITY; | ||
| 357 | self.writer.buf[start + 2] = capability_type; | ||
| 358 | |||
| 359 | start += 3; | ||
| 360 | self.writer.buf[start..start + blen].copy_from_slice(data); | ||
| 361 | self.writer.position = start + blen; | ||
| 362 | } | ||
| 363 | |||
| 364 | pub(crate) fn end_bos(&mut self) { | ||
| 365 | self.num_caps_mark = None; | ||
| 366 | let position = self.writer.position as u16; | ||
| 367 | self.writer.buf[2..4].copy_from_slice(&position.to_le_bytes()); | ||
| 368 | } | ||
| 369 | } | ||
diff --git a/embassy-usb/src/driver.rs b/embassy-usb/src/driver.rs new file mode 100644 index 000000000..01eb3d577 --- /dev/null +++ b/embassy-usb/src/driver.rs | |||
| @@ -0,0 +1,239 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | |||
| 3 | use crate::control::Request; | ||
| 4 | |||
| 5 | use super::types::*; | ||
| 6 | |||
| 7 | /// Driver for a specific USB peripheral. Implement this to add support for a new hardware | ||
| 8 | /// platform. | ||
| 9 | pub trait Driver<'a> { | ||
| 10 | type EndpointOut: EndpointOut + 'a; | ||
| 11 | type EndpointIn: EndpointIn + 'a; | ||
| 12 | type ControlPipe: ControlPipe + 'a; | ||
| 13 | type Bus: Bus + 'a; | ||
| 14 | type EnableFuture: Future<Output = Self::Bus> + 'a; | ||
| 15 | |||
| 16 | /// Allocates an endpoint and specified endpoint parameters. This method is called by the device | ||
| 17 | /// and class implementations to allocate endpoints, and can only be called before | ||
| 18 | /// [`enable`](UsbBus::enable) is called. | ||
| 19 | /// | ||
| 20 | /// # Arguments | ||
| 21 | /// | ||
| 22 | /// * `ep_addr` - A static endpoint address to allocate. If Some, the implementation should | ||
| 23 | /// attempt to return an endpoint with the specified address. If None, the implementation | ||
| 24 | /// should return the next available one. | ||
| 25 | /// * `max_packet_size` - Maximum packet size in bytes. | ||
| 26 | /// * `interval` - Polling interval parameter for interrupt endpoints. | ||
| 27 | fn alloc_endpoint_out( | ||
| 28 | &mut self, | ||
| 29 | ep_addr: Option<EndpointAddress>, | ||
| 30 | ep_type: EndpointType, | ||
| 31 | max_packet_size: u16, | ||
| 32 | interval: u8, | ||
| 33 | ) -> Result<Self::EndpointOut, EndpointAllocError>; | ||
| 34 | |||
| 35 | fn alloc_endpoint_in( | ||
| 36 | &mut self, | ||
| 37 | ep_addr: Option<EndpointAddress>, | ||
| 38 | ep_type: EndpointType, | ||
| 39 | max_packet_size: u16, | ||
| 40 | interval: u8, | ||
| 41 | ) -> Result<Self::EndpointIn, EndpointAllocError>; | ||
| 42 | |||
| 43 | fn alloc_control_pipe( | ||
| 44 | &mut self, | ||
| 45 | max_packet_size: u16, | ||
| 46 | ) -> Result<Self::ControlPipe, EndpointAllocError>; | ||
| 47 | |||
| 48 | /// Enables and initializes the USB peripheral. Soon after enabling the device will be reset, so | ||
| 49 | /// there is no need to perform a USB reset in this method. | ||
| 50 | fn enable(self) -> Self::EnableFuture; | ||
| 51 | |||
| 52 | /// Indicates that `set_device_address` must be called before accepting the corresponding | ||
| 53 | /// control transfer, not after. | ||
| 54 | /// | ||
| 55 | /// The default value for this constant is `false`, which corresponds to the USB 2.0 spec, 9.4.6 | ||
| 56 | const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = false; | ||
| 57 | } | ||
| 58 | |||
| 59 | pub trait Bus { | ||
| 60 | type PollFuture<'a>: Future<Output = Event> + 'a | ||
| 61 | where | ||
| 62 | Self: 'a; | ||
| 63 | |||
| 64 | fn poll<'a>(&'a mut self) -> Self::PollFuture<'a>; | ||
| 65 | |||
| 66 | /// Called when the host resets the device. This will be soon called after | ||
| 67 | /// [`poll`](crate::device::UsbDevice::poll) returns [`PollResult::Reset`]. This method should | ||
| 68 | /// reset the state of all endpoints and peripheral flags back to a state suitable for | ||
| 69 | /// enumeration, as well as ensure that all endpoints previously allocated with alloc_ep are | ||
| 70 | /// initialized as specified. | ||
| 71 | fn reset(&mut self); | ||
| 72 | |||
| 73 | /// Sets the device USB address to `addr`. | ||
| 74 | fn set_device_address(&mut self, addr: u8); | ||
| 75 | |||
| 76 | /// Sets the device configured state. | ||
| 77 | fn set_configured(&mut self, configured: bool); | ||
| 78 | |||
| 79 | /// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it | ||
| 80 | /// should be prepared to receive data again. Only used during control transfers. | ||
| 81 | fn set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool); | ||
| 82 | |||
| 83 | /// Gets whether the STALL condition is set for an endpoint. Only used during control transfers. | ||
| 84 | fn is_stalled(&mut self, ep_addr: EndpointAddress) -> bool; | ||
| 85 | |||
| 86 | /// Causes the USB peripheral to enter USB suspend mode, lowering power consumption and | ||
| 87 | /// preparing to detect a USB wakeup event. This will be called after | ||
| 88 | /// [`poll`](crate::device::UsbDevice::poll) returns [`PollResult::Suspend`]. The device will | ||
| 89 | /// continue be polled, and it shall return a value other than `Suspend` from `poll` when it no | ||
| 90 | /// longer detects the suspend condition. | ||
| 91 | fn suspend(&mut self); | ||
| 92 | |||
| 93 | /// Resumes from suspend mode. This may only be called after the peripheral has been previously | ||
| 94 | /// suspended. | ||
| 95 | fn resume(&mut self); | ||
| 96 | |||
| 97 | /// Simulates a disconnect from the USB bus, causing the host to reset and re-enumerate the | ||
| 98 | /// device. | ||
| 99 | /// | ||
| 100 | /// The default implementation just returns `Unsupported`. | ||
| 101 | /// | ||
| 102 | /// # Errors | ||
| 103 | /// | ||
| 104 | /// * [`Unsupported`](crate::UsbError::Unsupported) - This UsbBus implementation doesn't support | ||
| 105 | /// simulating a disconnect or it has not been enabled at creation time. | ||
| 106 | fn force_reset(&mut self) -> Result<(), Unsupported> { | ||
| 107 | Err(Unsupported) | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | pub trait Endpoint { | ||
| 112 | type WaitEnabledFuture<'a>: Future<Output = ()> + 'a | ||
| 113 | where | ||
| 114 | Self: 'a; | ||
| 115 | |||
| 116 | /// Get the endpoint address | ||
| 117 | fn info(&self) -> &EndpointInfo; | ||
| 118 | |||
| 119 | /// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it | ||
| 120 | /// should be prepared to receive data again. | ||
| 121 | fn set_stalled(&self, stalled: bool); | ||
| 122 | |||
| 123 | /// Gets whether the STALL condition is set for an endpoint. | ||
| 124 | fn is_stalled(&self) -> bool; | ||
| 125 | |||
| 126 | /// Waits for the endpoint to be enabled. | ||
| 127 | fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_>; | ||
| 128 | |||
| 129 | // TODO enable/disable? | ||
| 130 | } | ||
| 131 | |||
| 132 | pub trait EndpointOut: Endpoint { | ||
| 133 | type ReadFuture<'a>: Future<Output = Result<usize, ReadError>> + 'a | ||
| 134 | where | ||
| 135 | Self: 'a; | ||
| 136 | |||
| 137 | /// Reads a single packet of data from the endpoint, and returns the actual length of | ||
| 138 | /// the packet. | ||
| 139 | /// | ||
| 140 | /// This should also clear any NAK flags and prepare the endpoint to receive the next packet. | ||
| 141 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a>; | ||
| 142 | } | ||
| 143 | |||
| 144 | pub trait ControlPipe { | ||
| 145 | type SetupFuture<'a>: Future<Output = Request> + 'a | ||
| 146 | where | ||
| 147 | Self: 'a; | ||
| 148 | type DataOutFuture<'a>: Future<Output = Result<usize, ReadError>> + 'a | ||
| 149 | where | ||
| 150 | Self: 'a; | ||
| 151 | type DataInFuture<'a>: Future<Output = Result<(), WriteError>> + 'a | ||
| 152 | where | ||
| 153 | Self: 'a; | ||
| 154 | |||
| 155 | /// Maximum packet size for the control pipe | ||
| 156 | fn max_packet_size(&self) -> usize; | ||
| 157 | |||
| 158 | /// Reads a single setup packet from the endpoint. | ||
| 159 | fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a>; | ||
| 160 | |||
| 161 | /// Reads a DATA OUT packet into `buf` in response to a control write request. | ||
| 162 | /// | ||
| 163 | /// Must be called after `setup()` for requests with `direction` of `Out` | ||
| 164 | /// and `length` greater than zero. | ||
| 165 | fn data_out<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::DataOutFuture<'a>; | ||
| 166 | |||
| 167 | /// Sends a DATA IN packet with `data` in response to a control read request. | ||
| 168 | /// | ||
| 169 | /// If `last_packet` is true, the STATUS packet will be ACKed following the transfer of `data`. | ||
| 170 | fn data_in<'a>(&'a mut self, data: &'a [u8], last_packet: bool) -> Self::DataInFuture<'a>; | ||
| 171 | |||
| 172 | /// Accepts a control request. | ||
| 173 | /// | ||
| 174 | /// Causes the STATUS packet for the current request to be ACKed. | ||
| 175 | fn accept(&mut self); | ||
| 176 | |||
| 177 | /// Rejects a control request. | ||
| 178 | /// | ||
| 179 | /// Sets a STALL condition on the pipe to indicate an error. | ||
| 180 | fn reject(&mut self); | ||
| 181 | } | ||
| 182 | |||
| 183 | pub trait EndpointIn: Endpoint { | ||
| 184 | type WriteFuture<'a>: Future<Output = Result<(), WriteError>> + 'a | ||
| 185 | where | ||
| 186 | Self: 'a; | ||
| 187 | |||
| 188 | /// Writes a single packet of data to the endpoint. | ||
| 189 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a>; | ||
| 190 | } | ||
| 191 | |||
| 192 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 193 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 194 | /// Event returned by [`Bus::poll`]. | ||
| 195 | pub enum Event { | ||
| 196 | /// The USB reset condition has been detected. | ||
| 197 | Reset, | ||
| 198 | |||
| 199 | /// A USB suspend request has been detected or, in the case of self-powered devices, the device | ||
| 200 | /// has been disconnected from the USB bus. | ||
| 201 | Suspend, | ||
| 202 | |||
| 203 | /// A USB resume request has been detected after being suspended or, in the case of self-powered | ||
| 204 | /// devices, the device has been connected to the USB bus. | ||
| 205 | Resume, | ||
| 206 | } | ||
| 207 | |||
| 208 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 209 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 210 | pub struct EndpointAllocError; | ||
| 211 | |||
| 212 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 213 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 214 | /// Operation is unsupported by the driver. | ||
| 215 | pub struct Unsupported; | ||
| 216 | |||
| 217 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 218 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 219 | /// Errors returned by [`EndpointIn::write`] | ||
| 220 | pub enum WriteError { | ||
| 221 | /// The packet is too long to fit in the | ||
| 222 | /// transmission buffer. This is generally an error in the class implementation, because the | ||
| 223 | /// class shouldn't provide more data than the `max_packet_size` it specified when allocating | ||
| 224 | /// the endpoint. | ||
| 225 | BufferOverflow, | ||
| 226 | Disabled, | ||
| 227 | } | ||
| 228 | |||
| 229 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 230 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 231 | /// Errors returned by [`EndpointOut::read`] | ||
| 232 | pub enum ReadError { | ||
| 233 | /// The received packet is too long to | ||
| 234 | /// fit in `buf`. This is generally an error in the class implementation, because the class | ||
| 235 | /// should use a buffer that is large enough for the `max_packet_size` it specified when | ||
| 236 | /// allocating the endpoint. | ||
| 237 | BufferOverflow, | ||
| 238 | Disabled, | ||
| 239 | } | ||
diff --git a/embassy-usb/src/fmt.rs b/embassy-usb/src/fmt.rs new file mode 100644 index 000000000..066970813 --- /dev/null +++ b/embassy-usb/src/fmt.rs | |||
| @@ -0,0 +1,225 @@ | |||
| 1 | #![macro_use] | ||
| 2 | #![allow(unused_macros)] | ||
| 3 | |||
| 4 | #[cfg(all(feature = "defmt", feature = "log"))] | ||
| 5 | compile_error!("You may not enable both `defmt` and `log` features."); | ||
| 6 | |||
| 7 | macro_rules! assert { | ||
| 8 | ($($x:tt)*) => { | ||
| 9 | { | ||
| 10 | #[cfg(not(feature = "defmt"))] | ||
| 11 | ::core::assert!($($x)*); | ||
| 12 | #[cfg(feature = "defmt")] | ||
| 13 | ::defmt::assert!($($x)*); | ||
| 14 | } | ||
| 15 | }; | ||
| 16 | } | ||
| 17 | |||
| 18 | macro_rules! assert_eq { | ||
| 19 | ($($x:tt)*) => { | ||
| 20 | { | ||
| 21 | #[cfg(not(feature = "defmt"))] | ||
| 22 | ::core::assert_eq!($($x)*); | ||
| 23 | #[cfg(feature = "defmt")] | ||
| 24 | ::defmt::assert_eq!($($x)*); | ||
| 25 | } | ||
| 26 | }; | ||
| 27 | } | ||
| 28 | |||
| 29 | macro_rules! assert_ne { | ||
| 30 | ($($x:tt)*) => { | ||
| 31 | { | ||
| 32 | #[cfg(not(feature = "defmt"))] | ||
| 33 | ::core::assert_ne!($($x)*); | ||
| 34 | #[cfg(feature = "defmt")] | ||
| 35 | ::defmt::assert_ne!($($x)*); | ||
| 36 | } | ||
| 37 | }; | ||
| 38 | } | ||
| 39 | |||
| 40 | macro_rules! debug_assert { | ||
| 41 | ($($x:tt)*) => { | ||
| 42 | { | ||
| 43 | #[cfg(not(feature = "defmt"))] | ||
| 44 | ::core::debug_assert!($($x)*); | ||
| 45 | #[cfg(feature = "defmt")] | ||
| 46 | ::defmt::debug_assert!($($x)*); | ||
| 47 | } | ||
| 48 | }; | ||
| 49 | } | ||
| 50 | |||
| 51 | macro_rules! debug_assert_eq { | ||
| 52 | ($($x:tt)*) => { | ||
| 53 | { | ||
| 54 | #[cfg(not(feature = "defmt"))] | ||
| 55 | ::core::debug_assert_eq!($($x)*); | ||
| 56 | #[cfg(feature = "defmt")] | ||
| 57 | ::defmt::debug_assert_eq!($($x)*); | ||
| 58 | } | ||
| 59 | }; | ||
| 60 | } | ||
| 61 | |||
| 62 | macro_rules! debug_assert_ne { | ||
| 63 | ($($x:tt)*) => { | ||
| 64 | { | ||
| 65 | #[cfg(not(feature = "defmt"))] | ||
| 66 | ::core::debug_assert_ne!($($x)*); | ||
| 67 | #[cfg(feature = "defmt")] | ||
| 68 | ::defmt::debug_assert_ne!($($x)*); | ||
| 69 | } | ||
| 70 | }; | ||
| 71 | } | ||
| 72 | |||
| 73 | macro_rules! todo { | ||
| 74 | ($($x:tt)*) => { | ||
| 75 | { | ||
| 76 | #[cfg(not(feature = "defmt"))] | ||
| 77 | ::core::todo!($($x)*); | ||
| 78 | #[cfg(feature = "defmt")] | ||
| 79 | ::defmt::todo!($($x)*); | ||
| 80 | } | ||
| 81 | }; | ||
| 82 | } | ||
| 83 | |||
| 84 | macro_rules! unreachable { | ||
| 85 | ($($x:tt)*) => { | ||
| 86 | { | ||
| 87 | #[cfg(not(feature = "defmt"))] | ||
| 88 | ::core::unreachable!($($x)*); | ||
| 89 | #[cfg(feature = "defmt")] | ||
| 90 | ::defmt::unreachable!($($x)*); | ||
| 91 | } | ||
| 92 | }; | ||
| 93 | } | ||
| 94 | |||
| 95 | macro_rules! panic { | ||
| 96 | ($($x:tt)*) => { | ||
| 97 | { | ||
| 98 | #[cfg(not(feature = "defmt"))] | ||
| 99 | ::core::panic!($($x)*); | ||
| 100 | #[cfg(feature = "defmt")] | ||
| 101 | ::defmt::panic!($($x)*); | ||
| 102 | } | ||
| 103 | }; | ||
| 104 | } | ||
| 105 | |||
| 106 | macro_rules! trace { | ||
| 107 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 108 | { | ||
| 109 | #[cfg(feature = "log")] | ||
| 110 | ::log::trace!($s $(, $x)*); | ||
| 111 | #[cfg(feature = "defmt")] | ||
| 112 | ::defmt::trace!($s $(, $x)*); | ||
| 113 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 114 | let _ = ($( & $x ),*); | ||
| 115 | } | ||
| 116 | }; | ||
| 117 | } | ||
| 118 | |||
| 119 | macro_rules! debug { | ||
| 120 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 121 | { | ||
| 122 | #[cfg(feature = "log")] | ||
| 123 | ::log::debug!($s $(, $x)*); | ||
| 124 | #[cfg(feature = "defmt")] | ||
| 125 | ::defmt::debug!($s $(, $x)*); | ||
| 126 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 127 | let _ = ($( & $x ),*); | ||
| 128 | } | ||
| 129 | }; | ||
| 130 | } | ||
| 131 | |||
| 132 | macro_rules! info { | ||
| 133 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 134 | { | ||
| 135 | #[cfg(feature = "log")] | ||
| 136 | ::log::info!($s $(, $x)*); | ||
| 137 | #[cfg(feature = "defmt")] | ||
| 138 | ::defmt::info!($s $(, $x)*); | ||
| 139 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 140 | let _ = ($( & $x ),*); | ||
| 141 | } | ||
| 142 | }; | ||
| 143 | } | ||
| 144 | |||
| 145 | macro_rules! warn { | ||
| 146 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 147 | { | ||
| 148 | #[cfg(feature = "log")] | ||
| 149 | ::log::warn!($s $(, $x)*); | ||
| 150 | #[cfg(feature = "defmt")] | ||
| 151 | ::defmt::warn!($s $(, $x)*); | ||
| 152 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 153 | let _ = ($( & $x ),*); | ||
| 154 | } | ||
| 155 | }; | ||
| 156 | } | ||
| 157 | |||
| 158 | macro_rules! error { | ||
| 159 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 160 | { | ||
| 161 | #[cfg(feature = "log")] | ||
| 162 | ::log::error!($s $(, $x)*); | ||
| 163 | #[cfg(feature = "defmt")] | ||
| 164 | ::defmt::error!($s $(, $x)*); | ||
| 165 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 166 | let _ = ($( & $x ),*); | ||
| 167 | } | ||
| 168 | }; | ||
| 169 | } | ||
| 170 | |||
| 171 | #[cfg(feature = "defmt")] | ||
| 172 | macro_rules! unwrap { | ||
| 173 | ($($x:tt)*) => { | ||
| 174 | ::defmt::unwrap!($($x)*) | ||
| 175 | }; | ||
| 176 | } | ||
| 177 | |||
| 178 | #[cfg(not(feature = "defmt"))] | ||
| 179 | macro_rules! unwrap { | ||
| 180 | ($arg:expr) => { | ||
| 181 | match $crate::fmt::Try::into_result($arg) { | ||
| 182 | ::core::result::Result::Ok(t) => t, | ||
| 183 | ::core::result::Result::Err(e) => { | ||
| 184 | ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | }; | ||
| 188 | ($arg:expr, $($msg:expr),+ $(,)? ) => { | ||
| 189 | match $crate::fmt::Try::into_result($arg) { | ||
| 190 | ::core::result::Result::Ok(t) => t, | ||
| 191 | ::core::result::Result::Err(e) => { | ||
| 192 | ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 199 | pub struct NoneError; | ||
| 200 | |||
| 201 | pub trait Try { | ||
| 202 | type Ok; | ||
| 203 | type Error; | ||
| 204 | fn into_result(self) -> Result<Self::Ok, Self::Error>; | ||
| 205 | } | ||
| 206 | |||
| 207 | impl<T> Try for Option<T> { | ||
| 208 | type Ok = T; | ||
| 209 | type Error = NoneError; | ||
| 210 | |||
| 211 | #[inline] | ||
| 212 | fn into_result(self) -> Result<T, NoneError> { | ||
| 213 | self.ok_or(NoneError) | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | impl<T, E> Try for Result<T, E> { | ||
| 218 | type Ok = T; | ||
| 219 | type Error = E; | ||
| 220 | |||
| 221 | #[inline] | ||
| 222 | fn into_result(self) -> Self { | ||
| 223 | self | ||
| 224 | } | ||
| 225 | } | ||
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs new file mode 100644 index 000000000..e98cbdee3 --- /dev/null +++ b/embassy-usb/src/lib.rs | |||
| @@ -0,0 +1,344 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![feature(generic_associated_types)] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | // This mod MUST go first, so that the others see its macros. | ||
| 6 | pub(crate) mod fmt; | ||
| 7 | |||
| 8 | mod builder; | ||
| 9 | pub mod control; | ||
| 10 | pub mod descriptor; | ||
| 11 | pub mod driver; | ||
| 12 | pub mod types; | ||
| 13 | mod util; | ||
| 14 | |||
| 15 | use heapless::Vec; | ||
| 16 | |||
| 17 | use self::control::*; | ||
| 18 | use self::descriptor::*; | ||
| 19 | use self::driver::{Bus, Driver, Event}; | ||
| 20 | use self::types::*; | ||
| 21 | use self::util::*; | ||
| 22 | |||
| 23 | pub use self::builder::Config; | ||
| 24 | pub use self::builder::UsbDeviceBuilder; | ||
| 25 | |||
| 26 | /// The global state of the USB device. | ||
| 27 | /// | ||
| 28 | /// In general class traffic is only possible in the `Configured` state. | ||
| 29 | #[repr(u8)] | ||
| 30 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] | ||
| 31 | pub enum UsbDeviceState { | ||
| 32 | /// The USB device has just been created or reset. | ||
| 33 | Default, | ||
| 34 | |||
| 35 | /// The USB device has received an address from the host. | ||
| 36 | Addressed, | ||
| 37 | |||
| 38 | /// The USB device has been configured and is fully functional. | ||
| 39 | Configured, | ||
| 40 | |||
| 41 | /// The USB device has been suspended by the host or it has been unplugged from the USB bus. | ||
| 42 | Suspend, | ||
| 43 | } | ||
| 44 | |||
| 45 | /// The bConfiguration value for the not configured state. | ||
| 46 | pub const CONFIGURATION_NONE: u8 = 0; | ||
| 47 | |||
| 48 | /// The bConfiguration value for the single configuration supported by this device. | ||
| 49 | pub const CONFIGURATION_VALUE: u8 = 1; | ||
| 50 | |||
| 51 | /// The default value for bAlternateSetting for all interfaces. | ||
| 52 | pub const DEFAULT_ALTERNATE_SETTING: u8 = 0; | ||
| 53 | |||
| 54 | pub const MAX_INTERFACE_COUNT: usize = 4; | ||
| 55 | |||
| 56 | pub struct UsbDevice<'d, D: Driver<'d>> { | ||
| 57 | bus: D::Bus, | ||
| 58 | control: ControlPipe<D::ControlPipe>, | ||
| 59 | |||
| 60 | config: Config<'d>, | ||
| 61 | device_descriptor: &'d [u8], | ||
| 62 | config_descriptor: &'d [u8], | ||
| 63 | bos_descriptor: &'d [u8], | ||
| 64 | control_buf: &'d mut [u8], | ||
| 65 | |||
| 66 | device_state: UsbDeviceState, | ||
| 67 | remote_wakeup_enabled: bool, | ||
| 68 | self_powered: bool, | ||
| 69 | pending_address: u8, | ||
| 70 | |||
| 71 | interfaces: Vec<(u8, &'d mut dyn ControlHandler), MAX_INTERFACE_COUNT>, | ||
| 72 | } | ||
| 73 | |||
| 74 | impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | ||
| 75 | pub(crate) async fn build( | ||
| 76 | mut driver: D, | ||
| 77 | config: Config<'d>, | ||
| 78 | device_descriptor: &'d [u8], | ||
| 79 | config_descriptor: &'d [u8], | ||
| 80 | bos_descriptor: &'d [u8], | ||
| 81 | interfaces: Vec<(u8, &'d mut dyn ControlHandler), MAX_INTERFACE_COUNT>, | ||
| 82 | control_buf: &'d mut [u8], | ||
| 83 | ) -> UsbDevice<'d, D> { | ||
| 84 | let control = driver | ||
| 85 | .alloc_control_pipe(config.max_packet_size_0 as u16) | ||
| 86 | .expect("failed to alloc control endpoint"); | ||
| 87 | |||
| 88 | // Enable the USB bus. | ||
| 89 | // This prevent further allocation by consuming the driver. | ||
| 90 | let bus = driver.enable().await; | ||
| 91 | |||
| 92 | Self { | ||
| 93 | bus, | ||
| 94 | config, | ||
| 95 | control: ControlPipe::new(control), | ||
| 96 | device_descriptor, | ||
| 97 | config_descriptor, | ||
| 98 | bos_descriptor, | ||
| 99 | control_buf, | ||
| 100 | device_state: UsbDeviceState::Default, | ||
| 101 | remote_wakeup_enabled: false, | ||
| 102 | self_powered: false, | ||
| 103 | pending_address: 0, | ||
| 104 | interfaces, | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | pub async fn run(&mut self) { | ||
| 109 | loop { | ||
| 110 | let control_fut = self.control.setup(); | ||
| 111 | let bus_fut = self.bus.poll(); | ||
| 112 | match select(bus_fut, control_fut).await { | ||
| 113 | Either::Left(evt) => match evt { | ||
| 114 | Event::Reset => { | ||
| 115 | trace!("usb: reset"); | ||
| 116 | self.bus.reset(); | ||
| 117 | |||
| 118 | self.device_state = UsbDeviceState::Default; | ||
| 119 | self.remote_wakeup_enabled = false; | ||
| 120 | self.pending_address = 0; | ||
| 121 | |||
| 122 | for (_, h) in self.interfaces.iter_mut() { | ||
| 123 | h.reset(); | ||
| 124 | } | ||
| 125 | } | ||
| 126 | Event::Resume => { | ||
| 127 | trace!("usb: resume"); | ||
| 128 | } | ||
| 129 | Event::Suspend => { | ||
| 130 | trace!("usb: suspend"); | ||
| 131 | self.bus.suspend(); | ||
| 132 | self.device_state = UsbDeviceState::Suspend; | ||
| 133 | } | ||
| 134 | }, | ||
| 135 | Either::Right(req) => match req { | ||
| 136 | Setup::DataIn(req, stage) => self.handle_control_in(req, stage).await, | ||
| 137 | Setup::DataOut(req, stage) => self.handle_control_out(req, stage).await, | ||
| 138 | }, | ||
| 139 | } | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | async fn handle_control_out(&mut self, req: Request, stage: DataOutStage) { | ||
| 144 | const CONFIGURATION_NONE_U16: u16 = CONFIGURATION_NONE as u16; | ||
| 145 | const CONFIGURATION_VALUE_U16: u16 = CONFIGURATION_VALUE as u16; | ||
| 146 | |||
| 147 | let (data, stage) = match self.control.data_out(self.control_buf, stage).await { | ||
| 148 | Ok(data) => data, | ||
| 149 | Err(_) => { | ||
| 150 | warn!("usb: failed to read CONTROL OUT data stage."); | ||
| 151 | return; | ||
| 152 | } | ||
| 153 | }; | ||
| 154 | |||
| 155 | match (req.request_type, req.recipient) { | ||
| 156 | (RequestType::Standard, Recipient::Device) => match (req.request, req.value) { | ||
| 157 | (Request::CLEAR_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => { | ||
| 158 | self.remote_wakeup_enabled = false; | ||
| 159 | self.control.accept(stage) | ||
| 160 | } | ||
| 161 | (Request::SET_FEATURE, Request::FEATURE_DEVICE_REMOTE_WAKEUP) => { | ||
| 162 | self.remote_wakeup_enabled = true; | ||
| 163 | self.control.accept(stage) | ||
| 164 | } | ||
| 165 | (Request::SET_ADDRESS, addr @ 1..=127) => { | ||
| 166 | self.pending_address = addr as u8; | ||
| 167 | self.bus.set_device_address(self.pending_address); | ||
| 168 | self.control.accept(stage) | ||
| 169 | } | ||
| 170 | (Request::SET_CONFIGURATION, CONFIGURATION_VALUE_U16) => { | ||
| 171 | self.device_state = UsbDeviceState::Configured; | ||
| 172 | self.bus.set_configured(true); | ||
| 173 | self.control.accept(stage) | ||
| 174 | } | ||
| 175 | (Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => match self.device_state { | ||
| 176 | UsbDeviceState::Default => self.control.accept(stage), | ||
| 177 | _ => { | ||
| 178 | self.device_state = UsbDeviceState::Addressed; | ||
| 179 | self.bus.set_configured(false); | ||
| 180 | self.control.accept(stage) | ||
| 181 | } | ||
| 182 | }, | ||
| 183 | _ => self.control.reject(), | ||
| 184 | }, | ||
| 185 | (RequestType::Standard, Recipient::Endpoint) => match (req.request, req.value) { | ||
| 186 | (Request::SET_FEATURE, Request::FEATURE_ENDPOINT_HALT) => { | ||
| 187 | let ep_addr = ((req.index as u8) & 0x8f).into(); | ||
| 188 | self.bus.set_stalled(ep_addr, true); | ||
| 189 | self.control.accept(stage) | ||
| 190 | } | ||
| 191 | (Request::CLEAR_FEATURE, Request::FEATURE_ENDPOINT_HALT) => { | ||
| 192 | let ep_addr = ((req.index as u8) & 0x8f).into(); | ||
| 193 | self.bus.set_stalled(ep_addr, false); | ||
| 194 | self.control.accept(stage) | ||
| 195 | } | ||
| 196 | _ => self.control.reject(), | ||
| 197 | }, | ||
| 198 | (_, Recipient::Interface) => { | ||
| 199 | let handler = self | ||
| 200 | .interfaces | ||
| 201 | .iter_mut() | ||
| 202 | .find(|(i, _)| req.index == *i as _) | ||
| 203 | .map(|(_, h)| h); | ||
| 204 | |||
| 205 | match handler { | ||
| 206 | Some(handler) => { | ||
| 207 | let response = match (req.request_type, req.request) { | ||
| 208 | (RequestType::Standard, Request::SET_INTERFACE) => { | ||
| 209 | handler.set_interface(req.value) | ||
| 210 | } | ||
| 211 | _ => handler.control_out(req, data), | ||
| 212 | }; | ||
| 213 | match response { | ||
| 214 | OutResponse::Accepted => self.control.accept(stage), | ||
| 215 | OutResponse::Rejected => self.control.reject(), | ||
| 216 | } | ||
| 217 | } | ||
| 218 | None => self.control.reject(), | ||
| 219 | } | ||
| 220 | } | ||
| 221 | _ => self.control.reject(), | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | async fn handle_control_in(&mut self, req: Request, mut stage: DataInStage) { | ||
| 226 | // If we don't have an address yet, respond with max 1 packet. | ||
| 227 | // The host doesn't know our EP0 max packet size yet, and might assume | ||
| 228 | // a full-length packet is a short packet, thinking we're done sending data. | ||
| 229 | // See https://github.com/hathach/tinyusb/issues/184 | ||
| 230 | const DEVICE_DESCRIPTOR_LEN: u8 = 18; | ||
| 231 | if self.pending_address == 0 | ||
| 232 | && self.config.max_packet_size_0 < DEVICE_DESCRIPTOR_LEN | ||
| 233 | && (self.config.max_packet_size_0 as usize) < stage.length | ||
| 234 | { | ||
| 235 | trace!("received control req while not addressed: capping response to 1 packet."); | ||
| 236 | stage.length = self.config.max_packet_size_0 as _; | ||
| 237 | } | ||
| 238 | |||
| 239 | match (req.request_type, req.recipient) { | ||
| 240 | (RequestType::Standard, Recipient::Device) => match req.request { | ||
| 241 | Request::GET_STATUS => { | ||
| 242 | let mut status: u16 = 0x0000; | ||
| 243 | if self.self_powered { | ||
| 244 | status |= 0x0001; | ||
| 245 | } | ||
| 246 | if self.remote_wakeup_enabled { | ||
| 247 | status |= 0x0002; | ||
| 248 | } | ||
| 249 | self.control.accept_in(&status.to_le_bytes(), stage).await | ||
| 250 | } | ||
| 251 | Request::GET_DESCRIPTOR => self.handle_get_descriptor(req, stage).await, | ||
| 252 | Request::GET_CONFIGURATION => { | ||
| 253 | let status = match self.device_state { | ||
| 254 | UsbDeviceState::Configured => CONFIGURATION_VALUE, | ||
| 255 | _ => CONFIGURATION_NONE, | ||
| 256 | }; | ||
| 257 | self.control.accept_in(&status.to_le_bytes(), stage).await | ||
| 258 | } | ||
| 259 | _ => self.control.reject(), | ||
| 260 | }, | ||
| 261 | (RequestType::Standard, Recipient::Endpoint) => match req.request { | ||
| 262 | Request::GET_STATUS => { | ||
| 263 | let ep_addr: EndpointAddress = ((req.index as u8) & 0x8f).into(); | ||
| 264 | let mut status: u16 = 0x0000; | ||
| 265 | if self.bus.is_stalled(ep_addr) { | ||
| 266 | status |= 0x0001; | ||
| 267 | } | ||
| 268 | self.control.accept_in(&status.to_le_bytes(), stage).await | ||
| 269 | } | ||
| 270 | _ => self.control.reject(), | ||
| 271 | }, | ||
| 272 | (_, Recipient::Interface) => { | ||
| 273 | let handler = self | ||
| 274 | .interfaces | ||
| 275 | .iter_mut() | ||
| 276 | .find(|(i, _)| req.index == *i as _) | ||
| 277 | .map(|(_, h)| h); | ||
| 278 | |||
| 279 | match handler { | ||
| 280 | Some(handler) => { | ||
| 281 | let response = match (req.request_type, req.request) { | ||
| 282 | (RequestType::Standard, Request::GET_STATUS) => { | ||
| 283 | handler.get_status(self.control_buf) | ||
| 284 | } | ||
| 285 | (RequestType::Standard, Request::GET_INTERFACE) => { | ||
| 286 | handler.get_interface(self.control_buf) | ||
| 287 | } | ||
| 288 | _ => handler.control_in(req, self.control_buf), | ||
| 289 | }; | ||
| 290 | |||
| 291 | match response { | ||
| 292 | InResponse::Accepted(data) => self.control.accept_in(data, stage).await, | ||
| 293 | InResponse::Rejected => self.control.reject(), | ||
| 294 | } | ||
| 295 | } | ||
| 296 | None => self.control.reject(), | ||
| 297 | } | ||
| 298 | } | ||
| 299 | _ => self.control.reject(), | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 303 | async fn handle_get_descriptor(&mut self, req: Request, stage: DataInStage) { | ||
| 304 | let (dtype, index) = req.descriptor_type_index(); | ||
| 305 | |||
| 306 | match dtype { | ||
| 307 | descriptor_type::BOS => self.control.accept_in(self.bos_descriptor, stage).await, | ||
| 308 | descriptor_type::DEVICE => self.control.accept_in(self.device_descriptor, stage).await, | ||
| 309 | descriptor_type::CONFIGURATION => { | ||
| 310 | self.control.accept_in(self.config_descriptor, stage).await | ||
| 311 | } | ||
| 312 | descriptor_type::STRING => { | ||
| 313 | if index == 0 { | ||
| 314 | self.control | ||
| 315 | .accept_in_writer(req, stage, |w| { | ||
| 316 | w.write(descriptor_type::STRING, &lang_id::ENGLISH_US.to_le_bytes()); | ||
| 317 | }) | ||
| 318 | .await | ||
| 319 | } else { | ||
| 320 | let s = match index { | ||
| 321 | 1 => self.config.manufacturer, | ||
| 322 | 2 => self.config.product, | ||
| 323 | 3 => self.config.serial_number, | ||
| 324 | _ => { | ||
| 325 | let _index = StringIndex::new(index); | ||
| 326 | let _lang_id = req.index; | ||
| 327 | // TODO | ||
| 328 | None | ||
| 329 | } | ||
| 330 | }; | ||
| 331 | |||
| 332 | if let Some(s) = s { | ||
| 333 | self.control | ||
| 334 | .accept_in_writer(req, stage, |w| w.string(s)) | ||
| 335 | .await | ||
| 336 | } else { | ||
| 337 | self.control.reject() | ||
| 338 | } | ||
| 339 | } | ||
| 340 | } | ||
| 341 | _ => self.control.reject(), | ||
| 342 | } | ||
| 343 | } | ||
| 344 | } | ||
diff --git a/embassy-usb/src/types.rs b/embassy-usb/src/types.rs new file mode 100644 index 000000000..9d00e46cb --- /dev/null +++ b/embassy-usb/src/types.rs | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | /// Direction of USB traffic. Note that in the USB standard the direction is always indicated from | ||
| 2 | /// the perspective of the host, which is backward for devices, but the standard directions are used | ||
| 3 | /// for consistency. | ||
| 4 | /// | ||
| 5 | /// The values of the enum also match the direction bit used in endpoint addresses and control | ||
| 6 | /// request types. | ||
| 7 | #[repr(u8)] | ||
| 8 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 9 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 10 | pub enum UsbDirection { | ||
| 11 | /// Host to device (OUT) | ||
| 12 | Out = 0x00, | ||
| 13 | /// Device to host (IN) | ||
| 14 | In = 0x80, | ||
| 15 | } | ||
| 16 | |||
| 17 | impl From<u8> for UsbDirection { | ||
| 18 | fn from(value: u8) -> Self { | ||
| 19 | unsafe { core::mem::transmute(value & 0x80) } | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | /// USB endpoint transfer type. The values of this enum can be directly cast into `u8` to get the | ||
| 24 | /// transfer bmAttributes transfer type bits. | ||
| 25 | #[repr(u8)] | ||
| 26 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 27 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 28 | pub enum EndpointType { | ||
| 29 | /// Control endpoint. Used for device management. Only the host can initiate requests. Usually | ||
| 30 | /// used only endpoint 0. | ||
| 31 | Control = 0b00, | ||
| 32 | /// Isochronous endpoint. Used for time-critical unreliable data. Not implemented yet. | ||
| 33 | Isochronous = 0b01, | ||
| 34 | /// Bulk endpoint. Used for large amounts of best-effort reliable data. | ||
| 35 | Bulk = 0b10, | ||
| 36 | /// Interrupt endpoint. Used for small amounts of time-critical reliable data. | ||
| 37 | Interrupt = 0b11, | ||
| 38 | } | ||
| 39 | |||
| 40 | /// Type-safe endpoint address. | ||
| 41 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] | ||
| 42 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 43 | pub struct EndpointAddress(u8); | ||
| 44 | |||
| 45 | impl From<u8> for EndpointAddress { | ||
| 46 | #[inline] | ||
| 47 | fn from(addr: u8) -> EndpointAddress { | ||
| 48 | EndpointAddress(addr) | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | impl From<EndpointAddress> for u8 { | ||
| 53 | #[inline] | ||
| 54 | fn from(addr: EndpointAddress) -> u8 { | ||
| 55 | addr.0 | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | impl EndpointAddress { | ||
| 60 | const INBITS: u8 = UsbDirection::In as u8; | ||
| 61 | |||
| 62 | /// Constructs a new EndpointAddress with the given index and direction. | ||
| 63 | #[inline] | ||
| 64 | pub fn from_parts(index: usize, dir: UsbDirection) -> Self { | ||
| 65 | EndpointAddress(index as u8 | dir as u8) | ||
| 66 | } | ||
| 67 | |||
| 68 | /// Gets the direction part of the address. | ||
| 69 | #[inline] | ||
| 70 | pub fn direction(&self) -> UsbDirection { | ||
| 71 | if (self.0 & Self::INBITS) != 0 { | ||
| 72 | UsbDirection::In | ||
| 73 | } else { | ||
| 74 | UsbDirection::Out | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | /// Returns true if the direction is IN, otherwise false. | ||
| 79 | #[inline] | ||
| 80 | pub fn is_in(&self) -> bool { | ||
| 81 | (self.0 & Self::INBITS) != 0 | ||
| 82 | } | ||
| 83 | |||
| 84 | /// Returns true if the direction is OUT, otherwise false. | ||
| 85 | #[inline] | ||
| 86 | pub fn is_out(&self) -> bool { | ||
| 87 | (self.0 & Self::INBITS) == 0 | ||
| 88 | } | ||
| 89 | |||
| 90 | /// Gets the index part of the endpoint address. | ||
| 91 | #[inline] | ||
| 92 | pub fn index(&self) -> usize { | ||
| 93 | (self.0 & !Self::INBITS) as usize | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 98 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 99 | pub struct EndpointInfo { | ||
| 100 | pub addr: EndpointAddress, | ||
| 101 | pub ep_type: EndpointType, | ||
| 102 | pub max_packet_size: u16, | ||
| 103 | pub interval: u8, | ||
| 104 | } | ||
| 105 | |||
| 106 | /// A handle for a USB interface that contains its number. | ||
| 107 | #[derive(Copy, Clone, Eq, PartialEq)] | ||
| 108 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 109 | pub struct InterfaceNumber(u8); | ||
| 110 | |||
| 111 | impl InterfaceNumber { | ||
| 112 | pub(crate) fn new(index: u8) -> InterfaceNumber { | ||
| 113 | InterfaceNumber(index) | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | impl From<InterfaceNumber> for u8 { | ||
| 118 | fn from(n: InterfaceNumber) -> u8 { | ||
| 119 | n.0 | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | /// A handle for a USB string descriptor that contains its index. | ||
| 124 | #[derive(Copy, Clone, Eq, PartialEq)] | ||
| 125 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 126 | pub struct StringIndex(u8); | ||
| 127 | |||
| 128 | impl StringIndex { | ||
| 129 | pub(crate) fn new(index: u8) -> StringIndex { | ||
| 130 | StringIndex(index) | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | impl From<StringIndex> for u8 { | ||
| 135 | fn from(i: StringIndex) -> u8 { | ||
| 136 | i.0 | ||
| 137 | } | ||
| 138 | } | ||
diff --git a/embassy-usb/src/util.rs b/embassy-usb/src/util.rs new file mode 100644 index 000000000..18cc875c6 --- /dev/null +++ b/embassy-usb/src/util.rs | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use core::task::{Context, Poll}; | ||
| 4 | |||
| 5 | #[derive(Debug, Clone)] | ||
| 6 | pub enum Either<A, B> { | ||
| 7 | Left(A), | ||
| 8 | Right(B), | ||
| 9 | } | ||
| 10 | |||
| 11 | pub fn select<A, B>(a: A, b: B) -> Select<A, B> | ||
| 12 | where | ||
| 13 | A: Future, | ||
| 14 | B: Future, | ||
| 15 | { | ||
| 16 | Select { a, b } | ||
| 17 | } | ||
| 18 | |||
| 19 | pub struct Select<A, B> { | ||
| 20 | a: A, | ||
| 21 | b: B, | ||
| 22 | } | ||
| 23 | |||
| 24 | impl<A: Unpin, B: Unpin> Unpin for Select<A, B> {} | ||
| 25 | |||
| 26 | impl<A, B> Future for Select<A, B> | ||
| 27 | where | ||
| 28 | A: Future, | ||
| 29 | B: Future, | ||
| 30 | { | ||
| 31 | type Output = Either<A::Output, B::Output>; | ||
| 32 | |||
| 33 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 34 | let this = unsafe { self.get_unchecked_mut() }; | ||
| 35 | let a = unsafe { Pin::new_unchecked(&mut this.a) }; | ||
| 36 | let b = unsafe { Pin::new_unchecked(&mut this.b) }; | ||
| 37 | match a.poll(cx) { | ||
| 38 | Poll::Ready(x) => Poll::Ready(Either::Left(x)), | ||
| 39 | Poll::Pending => match b.poll(cx) { | ||
| 40 | Poll::Ready(x) => Poll::Ready(Either::Right(x)), | ||
| 41 | Poll::Pending => Poll::Pending, | ||
| 42 | }, | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index a704eb3bc..e944c171a 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml | |||
| @@ -1,16 +1,19 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | authors = ["Dario Nieuwenhuis <[email protected]>"] | 2 | authors = ["Dario Nieuwenhuis <[email protected]>"] |
| 3 | edition = "2018" | 3 | edition = "2021" |
| 4 | name = "embassy-nrf-examples" | 4 | name = "embassy-nrf-examples" |
| 5 | version = "0.1.0" | 5 | version = "0.1.0" |
| 6 | 6 | ||
| 7 | [features] | 7 | [features] |
| 8 | default = ["nightly"] | 8 | default = ["nightly"] |
| 9 | nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits"] | 9 | nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid"] |
| 10 | 10 | ||
| 11 | [dependencies] | 11 | [dependencies] |
| 12 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } | 12 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } |
| 13 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } | 13 | embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } |
| 14 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } | ||
| 15 | embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true } | ||
| 16 | embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true } | ||
| 14 | 17 | ||
| 15 | defmt = "0.3" | 18 | defmt = "0.3" |
| 16 | defmt-rtt = "0.3" | 19 | defmt-rtt = "0.3" |
| @@ -21,6 +24,5 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } | |||
| 21 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } | 24 | futures = { version = "0.3.17", default-features = false, features = ["async-await"] } |
| 22 | rand = { version = "0.8.4", default-features = false } | 25 | rand = { version = "0.8.4", default-features = false } |
| 23 | embedded-storage = "0.3.0" | 26 | embedded-storage = "0.3.0" |
| 24 | 27 | usbd-hid = "0.5.2" | |
| 25 | usb-device = "0.2" | 28 | serde = { version = "1.0.136", default-features = false } |
| 26 | usbd-serial = "0.1.1" | ||
diff --git a/examples/nrf/src/bin/usb_hid_keyboard.rs b/examples/nrf/src/bin/usb_hid_keyboard.rs new file mode 100644 index 000000000..0812697e4 --- /dev/null +++ b/examples/nrf/src/bin/usb_hid_keyboard.rs | |||
| @@ -0,0 +1,148 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(generic_associated_types)] | ||
| 4 | #![feature(type_alias_impl_trait)] | ||
| 5 | |||
| 6 | use core::mem; | ||
| 7 | use defmt::*; | ||
| 8 | use embassy::executor::Spawner; | ||
| 9 | use embassy::time::Duration; | ||
| 10 | use embassy_nrf::gpio::{Input, Pin, Pull}; | ||
| 11 | use embassy_nrf::interrupt; | ||
| 12 | use embassy_nrf::pac; | ||
| 13 | use embassy_nrf::usb::Driver; | ||
| 14 | use embassy_nrf::Peripherals; | ||
| 15 | use embassy_usb::control::OutResponse; | ||
| 16 | use embassy_usb::{Config, UsbDeviceBuilder}; | ||
| 17 | use embassy_usb_hid::{HidClass, ReportId, RequestHandler, State}; | ||
| 18 | use futures::future::join; | ||
| 19 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | ||
| 20 | |||
| 21 | use defmt_rtt as _; // global logger | ||
| 22 | use panic_probe as _; | ||
| 23 | |||
| 24 | #[embassy::main] | ||
| 25 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 26 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 27 | let power: pac::POWER = unsafe { mem::transmute(()) }; | ||
| 28 | |||
| 29 | info!("Enabling ext hfosc..."); | ||
| 30 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 31 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 32 | |||
| 33 | info!("Waiting for vbus..."); | ||
| 34 | while !power.usbregstatus.read().vbusdetect().is_vbus_present() {} | ||
| 35 | info!("vbus OK"); | ||
| 36 | |||
| 37 | // Create the driver, from the HAL. | ||
| 38 | let irq = interrupt::take!(USBD); | ||
| 39 | let driver = Driver::new(p.USBD, irq); | ||
| 40 | |||
| 41 | // Create embassy-usb Config | ||
| 42 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 43 | config.manufacturer = Some("Tactile Engineering"); | ||
| 44 | config.product = Some("Testy"); | ||
| 45 | config.serial_number = Some("12345678"); | ||
| 46 | config.max_power = 100; | ||
| 47 | config.max_packet_size_0 = 64; | ||
| 48 | |||
| 49 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 50 | // It needs some buffers for building the descriptors. | ||
| 51 | let mut device_descriptor = [0; 256]; | ||
| 52 | let mut config_descriptor = [0; 256]; | ||
| 53 | let mut bos_descriptor = [0; 256]; | ||
| 54 | let mut control_buf = [0; 16]; | ||
| 55 | let request_handler = MyRequestHandler {}; | ||
| 56 | |||
| 57 | let mut state = State::<8, 1>::new(); | ||
| 58 | |||
| 59 | let mut builder = UsbDeviceBuilder::new( | ||
| 60 | driver, | ||
| 61 | config, | ||
| 62 | &mut device_descriptor, | ||
| 63 | &mut config_descriptor, | ||
| 64 | &mut bos_descriptor, | ||
| 65 | &mut control_buf, | ||
| 66 | ); | ||
| 67 | |||
| 68 | // Create classes on the builder. | ||
| 69 | let hid = HidClass::with_output_ep( | ||
| 70 | &mut builder, | ||
| 71 | &mut state, | ||
| 72 | KeyboardReport::desc(), | ||
| 73 | Some(&request_handler), | ||
| 74 | 60, | ||
| 75 | 64, | ||
| 76 | ); | ||
| 77 | |||
| 78 | // Build the builder. | ||
| 79 | let mut usb = builder.build().await; | ||
| 80 | |||
| 81 | // Run the USB device. | ||
| 82 | let usb_fut = usb.run(); | ||
| 83 | |||
| 84 | let mut button = Input::new(p.P0_11.degrade(), Pull::Up); | ||
| 85 | |||
| 86 | let (mut hid_in, hid_out) = hid.split(); | ||
| 87 | |||
| 88 | // Do stuff with the class! | ||
| 89 | let in_fut = async { | ||
| 90 | loop { | ||
| 91 | button.wait_for_low().await; | ||
| 92 | info!("PRESSED"); | ||
| 93 | let report = KeyboardReport { | ||
| 94 | keycodes: [4, 0, 0, 0, 0, 0], | ||
| 95 | leds: 0, | ||
| 96 | modifier: 0, | ||
| 97 | reserved: 0, | ||
| 98 | }; | ||
| 99 | match hid_in.serialize(&report).await { | ||
| 100 | Ok(()) => {} | ||
| 101 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 102 | }; | ||
| 103 | |||
| 104 | button.wait_for_high().await; | ||
| 105 | info!("RELEASED"); | ||
| 106 | let report = KeyboardReport { | ||
| 107 | keycodes: [0, 0, 0, 0, 0, 0], | ||
| 108 | leds: 0, | ||
| 109 | modifier: 0, | ||
| 110 | reserved: 0, | ||
| 111 | }; | ||
| 112 | match hid_in.serialize(&report).await { | ||
| 113 | Ok(()) => {} | ||
| 114 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 115 | }; | ||
| 116 | } | ||
| 117 | }; | ||
| 118 | |||
| 119 | let out_fut = async { | ||
| 120 | hid_out.run(false, &request_handler).await; | ||
| 121 | }; | ||
| 122 | // Run everything concurrently. | ||
| 123 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 124 | join(usb_fut, join(in_fut, out_fut)).await; | ||
| 125 | } | ||
| 126 | |||
| 127 | struct MyRequestHandler {} | ||
| 128 | |||
| 129 | impl RequestHandler for MyRequestHandler { | ||
| 130 | fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option<usize> { | ||
| 131 | info!("Get report for {:?}", id); | ||
| 132 | None | ||
| 133 | } | ||
| 134 | |||
| 135 | fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse { | ||
| 136 | info!("Set report for {:?}: {=[u8]}", id, data); | ||
| 137 | OutResponse::Accepted | ||
| 138 | } | ||
| 139 | |||
| 140 | fn set_idle(&self, id: Option<ReportId>, dur: Duration) { | ||
| 141 | info!("Set idle rate for {:?} to {:?}", id, dur); | ||
| 142 | } | ||
| 143 | |||
| 144 | fn get_idle(&self, id: Option<ReportId>) -> Option<Duration> { | ||
| 145 | info!("Get idle rate for {:?}", id); | ||
| 146 | None | ||
| 147 | } | ||
| 148 | } | ||
diff --git a/examples/nrf/src/bin/usb_hid_mouse.rs b/examples/nrf/src/bin/usb_hid_mouse.rs new file mode 100644 index 000000000..ca9383827 --- /dev/null +++ b/examples/nrf/src/bin/usb_hid_mouse.rs | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(generic_associated_types)] | ||
| 4 | #![feature(type_alias_impl_trait)] | ||
| 5 | |||
| 6 | use core::mem; | ||
| 7 | use defmt::*; | ||
| 8 | use embassy::executor::Spawner; | ||
| 9 | use embassy::time::{Duration, Timer}; | ||
| 10 | use embassy_nrf::interrupt; | ||
| 11 | use embassy_nrf::pac; | ||
| 12 | use embassy_nrf::usb::Driver; | ||
| 13 | use embassy_nrf::Peripherals; | ||
| 14 | use embassy_usb::control::OutResponse; | ||
| 15 | use embassy_usb::{Config, UsbDeviceBuilder}; | ||
| 16 | use embassy_usb_hid::{HidClass, 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 | #[embassy::main] | ||
| 24 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 25 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 26 | let power: pac::POWER = unsafe { mem::transmute(()) }; | ||
| 27 | |||
| 28 | info!("Enabling ext hfosc..."); | ||
| 29 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 30 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 31 | |||
| 32 | info!("Waiting for vbus..."); | ||
| 33 | while !power.usbregstatus.read().vbusdetect().is_vbus_present() {} | ||
| 34 | info!("vbus OK"); | ||
| 35 | |||
| 36 | // Create the driver, from the HAL. | ||
| 37 | let irq = interrupt::take!(USBD); | ||
| 38 | let driver = Driver::new(p.USBD, irq); | ||
| 39 | |||
| 40 | // Create embassy-usb Config | ||
| 41 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 42 | config.manufacturer = Some("Tactile Engineering"); | ||
| 43 | config.product = Some("Testy"); | ||
| 44 | config.serial_number = Some("12345678"); | ||
| 45 | config.max_power = 100; | ||
| 46 | |||
| 47 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 48 | // It needs some buffers for building the descriptors. | ||
| 49 | let mut device_descriptor = [0; 256]; | ||
| 50 | let mut config_descriptor = [0; 256]; | ||
| 51 | let mut bos_descriptor = [0; 256]; | ||
| 52 | let mut control_buf = [0; 16]; | ||
| 53 | let request_handler = MyRequestHandler {}; | ||
| 54 | |||
| 55 | let mut control = State::<5, 0>::new(); | ||
| 56 | |||
| 57 | let mut builder = UsbDeviceBuilder::new( | ||
| 58 | driver, | ||
| 59 | config, | ||
| 60 | &mut device_descriptor, | ||
| 61 | &mut config_descriptor, | ||
| 62 | &mut bos_descriptor, | ||
| 63 | &mut control_buf, | ||
| 64 | ); | ||
| 65 | |||
| 66 | // Create classes on the builder. | ||
| 67 | let mut hid = HidClass::new( | ||
| 68 | &mut builder, | ||
| 69 | &mut control, | ||
| 70 | MouseReport::desc(), | ||
| 71 | Some(&request_handler), | ||
| 72 | 60, | ||
| 73 | 8, | ||
| 74 | ); | ||
| 75 | |||
| 76 | // Build the builder. | ||
| 77 | let mut usb = builder.build().await; | ||
| 78 | |||
| 79 | // Run the USB device. | ||
| 80 | let usb_fut = usb.run(); | ||
| 81 | |||
| 82 | // Do stuff with the class! | ||
| 83 | let hid_fut = async { | ||
| 84 | let mut y: i8 = 5; | ||
| 85 | loop { | ||
| 86 | Timer::after(Duration::from_millis(500)).await; | ||
| 87 | |||
| 88 | y = -y; | ||
| 89 | let report = MouseReport { | ||
| 90 | buttons: 0, | ||
| 91 | x: 0, | ||
| 92 | y, | ||
| 93 | wheel: 0, | ||
| 94 | pan: 0, | ||
| 95 | }; | ||
| 96 | match hid.input().serialize(&report).await { | ||
| 97 | Ok(()) => {} | ||
| 98 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 99 | } | ||
| 100 | } | ||
| 101 | }; | ||
| 102 | |||
| 103 | // Run everything concurrently. | ||
| 104 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 105 | join(usb_fut, hid_fut).await; | ||
| 106 | } | ||
| 107 | |||
| 108 | struct MyRequestHandler {} | ||
| 109 | |||
| 110 | impl RequestHandler for MyRequestHandler { | ||
| 111 | fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option<usize> { | ||
| 112 | info!("Get report for {:?}", id); | ||
| 113 | None | ||
| 114 | } | ||
| 115 | |||
| 116 | fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse { | ||
| 117 | info!("Set report for {:?}: {=[u8]}", id, data); | ||
| 118 | OutResponse::Accepted | ||
| 119 | } | ||
| 120 | |||
| 121 | fn set_idle(&self, id: Option<ReportId>, dur: Duration) { | ||
| 122 | info!("Set idle rate for {:?} to {:?}", id, dur); | ||
| 123 | } | ||
| 124 | |||
| 125 | fn get_idle(&self, id: Option<ReportId>) -> Option<Duration> { | ||
| 126 | info!("Get idle rate for {:?}", id); | ||
| 127 | None | ||
| 128 | } | ||
| 129 | } | ||
diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs new file mode 100644 index 000000000..500be2ce8 --- /dev/null +++ b/examples/nrf/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,113 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(generic_associated_types)] | ||
| 4 | #![feature(type_alias_impl_trait)] | ||
| 5 | |||
| 6 | use core::mem; | ||
| 7 | use defmt::{info, panic}; | ||
| 8 | use embassy::executor::Spawner; | ||
| 9 | use embassy_nrf::interrupt; | ||
| 10 | use embassy_nrf::pac; | ||
| 11 | use embassy_nrf::usb::{Driver, Instance}; | ||
| 12 | use embassy_nrf::Peripherals; | ||
| 13 | use embassy_usb::driver::{ReadError, WriteError}; | ||
| 14 | use embassy_usb::{Config, UsbDeviceBuilder}; | ||
| 15 | use embassy_usb_serial::{CdcAcmClass, State}; | ||
| 16 | use futures::future::join; | ||
| 17 | |||
| 18 | use defmt_rtt as _; // global logger | ||
| 19 | use panic_probe as _; | ||
| 20 | |||
| 21 | #[embassy::main] | ||
| 22 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 23 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 24 | let power: pac::POWER = unsafe { mem::transmute(()) }; | ||
| 25 | |||
| 26 | info!("Enabling ext hfosc..."); | ||
| 27 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 28 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 29 | |||
| 30 | info!("Waiting for vbus..."); | ||
| 31 | while !power.usbregstatus.read().vbusdetect().is_vbus_present() {} | ||
| 32 | info!("vbus OK"); | ||
| 33 | |||
| 34 | // Create the driver, from the HAL. | ||
| 35 | let irq = interrupt::take!(USBD); | ||
| 36 | let driver = Driver::new(p.USBD, irq); | ||
| 37 | |||
| 38 | // Create embassy-usb Config | ||
| 39 | let config = Config::new(0xc0de, 0xcafe); | ||
| 40 | |||
| 41 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 42 | // It needs some buffers for building the descriptors. | ||
| 43 | let mut device_descriptor = [0; 256]; | ||
| 44 | let mut config_descriptor = [0; 256]; | ||
| 45 | let mut bos_descriptor = [0; 256]; | ||
| 46 | let mut control_buf = [0; 7]; | ||
| 47 | |||
| 48 | let mut state = State::new(); | ||
| 49 | |||
| 50 | let mut builder = UsbDeviceBuilder::new( | ||
| 51 | driver, | ||
| 52 | config, | ||
| 53 | &mut device_descriptor, | ||
| 54 | &mut config_descriptor, | ||
| 55 | &mut bos_descriptor, | ||
| 56 | &mut control_buf, | ||
| 57 | ); | ||
| 58 | |||
| 59 | // Create classes on the builder. | ||
| 60 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 61 | |||
| 62 | // Build the builder. | ||
| 63 | let mut usb = builder.build().await; | ||
| 64 | |||
| 65 | // Run the USB device. | ||
| 66 | let usb_fut = usb.run(); | ||
| 67 | |||
| 68 | // Do stuff with the class! | ||
| 69 | let echo_fut = async { | ||
| 70 | loop { | ||
| 71 | class.wait_connection().await; | ||
| 72 | info!("Connected"); | ||
| 73 | let _ = echo(&mut class).await; | ||
| 74 | info!("Disconnected"); | ||
| 75 | } | ||
| 76 | }; | ||
| 77 | |||
| 78 | // Run everything concurrently. | ||
| 79 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 80 | join(usb_fut, echo_fut).await; | ||
| 81 | } | ||
| 82 | |||
| 83 | struct Disconnected {} | ||
| 84 | |||
| 85 | impl From<ReadError> for Disconnected { | ||
| 86 | fn from(val: ReadError) -> Self { | ||
| 87 | match val { | ||
| 88 | ReadError::BufferOverflow => panic!("Buffer overflow"), | ||
| 89 | ReadError::Disabled => Disconnected {}, | ||
| 90 | } | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | impl From<WriteError> for Disconnected { | ||
| 95 | fn from(val: WriteError) -> Self { | ||
| 96 | match val { | ||
| 97 | WriteError::BufferOverflow => panic!("Buffer overflow"), | ||
| 98 | WriteError::Disabled => Disconnected {}, | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | async fn echo<'d, T: Instance + 'd>( | ||
| 104 | class: &mut CdcAcmClass<'d, Driver<'d, T>>, | ||
| 105 | ) -> Result<(), Disconnected> { | ||
| 106 | let mut buf = [0; 64]; | ||
| 107 | loop { | ||
| 108 | let n = class.read_packet(&mut buf).await?; | ||
| 109 | let data = &buf[..n]; | ||
| 110 | info!("data: {:x}", data); | ||
| 111 | class.write_packet(data).await?; | ||
| 112 | } | ||
| 113 | } | ||
diff --git a/examples/nrf/src/bin/usb_serial_multitask.rs b/examples/nrf/src/bin/usb_serial_multitask.rs new file mode 100644 index 000000000..1258bc53d --- /dev/null +++ b/examples/nrf/src/bin/usb_serial_multitask.rs | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(generic_associated_types)] | ||
| 4 | #![feature(type_alias_impl_trait)] | ||
| 5 | |||
| 6 | use core::mem; | ||
| 7 | use defmt::{info, panic, unwrap}; | ||
| 8 | use embassy::executor::Spawner; | ||
| 9 | use embassy::util::Forever; | ||
| 10 | use embassy_nrf::pac; | ||
| 11 | use embassy_nrf::usb::Driver; | ||
| 12 | use embassy_nrf::Peripherals; | ||
| 13 | use embassy_nrf::{interrupt, peripherals}; | ||
| 14 | use embassy_usb::driver::{ReadError, WriteError}; | ||
| 15 | use embassy_usb::{Config, UsbDevice, UsbDeviceBuilder}; | ||
| 16 | use embassy_usb_serial::{CdcAcmClass, State}; | ||
| 17 | |||
| 18 | use defmt_rtt as _; // global logger | ||
| 19 | use panic_probe as _; | ||
| 20 | |||
| 21 | type MyDriver = Driver<'static, peripherals::USBD>; | ||
| 22 | |||
| 23 | #[embassy::task] | ||
| 24 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) { | ||
| 25 | device.run().await; | ||
| 26 | } | ||
| 27 | |||
| 28 | #[embassy::task] | ||
| 29 | async fn echo_task(mut class: CdcAcmClass<'static, MyDriver>) { | ||
| 30 | loop { | ||
| 31 | class.wait_connection().await; | ||
| 32 | info!("Connected"); | ||
| 33 | let _ = echo(&mut class).await; | ||
| 34 | info!("Disconnected"); | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | #[embassy::main] | ||
| 39 | async fn main(spawner: Spawner, p: Peripherals) { | ||
| 40 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 41 | let power: pac::POWER = unsafe { mem::transmute(()) }; | ||
| 42 | |||
| 43 | info!("Enabling ext hfosc..."); | ||
| 44 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 45 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 46 | |||
| 47 | info!("Waiting for vbus..."); | ||
| 48 | while !power.usbregstatus.read().vbusdetect().is_vbus_present() {} | ||
| 49 | info!("vbus OK"); | ||
| 50 | |||
| 51 | // Create the driver, from the HAL. | ||
| 52 | let irq = interrupt::take!(USBD); | ||
| 53 | let driver = Driver::new(p.USBD, irq); | ||
| 54 | |||
| 55 | // Create embassy-usb Config | ||
| 56 | let config = Config::new(0xc0de, 0xcafe); | ||
| 57 | |||
| 58 | struct Resources { | ||
| 59 | device_descriptor: [u8; 256], | ||
| 60 | config_descriptor: [u8; 256], | ||
| 61 | bos_descriptor: [u8; 256], | ||
| 62 | control_buf: [u8; 7], | ||
| 63 | serial_state: State<'static>, | ||
| 64 | } | ||
| 65 | static RESOURCES: Forever<Resources> = Forever::new(); | ||
| 66 | let res = RESOURCES.put(Resources { | ||
| 67 | device_descriptor: [0; 256], | ||
| 68 | config_descriptor: [0; 256], | ||
| 69 | bos_descriptor: [0; 256], | ||
| 70 | control_buf: [0; 7], | ||
| 71 | serial_state: State::new(), | ||
| 72 | }); | ||
| 73 | |||
| 74 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 75 | let mut builder = UsbDeviceBuilder::new( | ||
| 76 | driver, | ||
| 77 | config, | ||
| 78 | &mut res.device_descriptor, | ||
| 79 | &mut res.config_descriptor, | ||
| 80 | &mut res.bos_descriptor, | ||
| 81 | &mut res.control_buf, | ||
| 82 | ); | ||
| 83 | |||
| 84 | // Create classes on the builder. | ||
| 85 | let class = CdcAcmClass::new(&mut builder, &mut res.serial_state, 64); | ||
| 86 | |||
| 87 | // Build the builder. | ||
| 88 | let usb = builder.build().await; | ||
| 89 | |||
| 90 | unwrap!(spawner.spawn(usb_task(usb))); | ||
| 91 | unwrap!(spawner.spawn(echo_task(class))); | ||
| 92 | } | ||
| 93 | |||
| 94 | struct Disconnected {} | ||
| 95 | |||
| 96 | impl From<ReadError> for Disconnected { | ||
| 97 | fn from(val: ReadError) -> Self { | ||
| 98 | match val { | ||
| 99 | ReadError::BufferOverflow => panic!("Buffer overflow"), | ||
| 100 | ReadError::Disabled => Disconnected {}, | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | impl From<WriteError> for Disconnected { | ||
| 106 | fn from(val: WriteError) -> Self { | ||
| 107 | match val { | ||
| 108 | WriteError::BufferOverflow => panic!("Buffer overflow"), | ||
| 109 | WriteError::Disabled => Disconnected {}, | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | async fn echo(class: &mut CdcAcmClass<'static, MyDriver>) -> Result<(), Disconnected> { | ||
| 115 | let mut buf = [0; 64]; | ||
| 116 | loop { | ||
| 117 | let n = class.read_packet(&mut buf).await?; | ||
| 118 | let data = &buf[..n]; | ||
| 119 | info!("data: {:x}", data); | ||
| 120 | class.write_packet(data).await?; | ||
| 121 | } | ||
| 122 | } | ||
diff --git a/examples/nrf/src/bin/usb_uart.rs b/examples/nrf/src/bin/usb_uart.rs deleted file mode 100644 index d283dccd1..000000000 --- a/examples/nrf/src/bin/usb_uart.rs +++ /dev/null | |||
| @@ -1,89 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use embassy::executor::Spawner; | ||
| 7 | use embassy::interrupt::InterruptExt; | ||
| 8 | use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; | ||
| 9 | use embassy_nrf::usb::{State, Usb, UsbBus, UsbSerial}; | ||
| 10 | use embassy_nrf::{interrupt, Peripherals}; | ||
| 11 | use futures::pin_mut; | ||
| 12 | use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; | ||
| 13 | |||
| 14 | use defmt_rtt as _; // global logger | ||
| 15 | use panic_probe as _; // print out panic messages | ||
| 16 | |||
| 17 | #[embassy::main] | ||
| 18 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 19 | let mut rx_buffer = [0u8; 64]; | ||
| 20 | // we send back input + cr + lf | ||
| 21 | let mut tx_buffer = [0u8; 66]; | ||
| 22 | |||
| 23 | let usb_bus = UsbBus::new(p.USBD); | ||
| 24 | |||
| 25 | let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer); | ||
| 26 | |||
| 27 | let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) | ||
| 28 | .manufacturer("Fake company") | ||
| 29 | .product("Serial port") | ||
| 30 | .serial_number("TEST") | ||
| 31 | .device_class(0x02) | ||
| 32 | .build(); | ||
| 33 | |||
| 34 | let irq = interrupt::take!(USBD); | ||
| 35 | irq.set_priority(interrupt::Priority::P3); | ||
| 36 | |||
| 37 | let mut state = State::new(); | ||
| 38 | let usb = unsafe { Usb::new(&mut state, device, serial, irq) }; | ||
| 39 | pin_mut!(usb); | ||
| 40 | |||
| 41 | let (mut reader, mut writer) = usb.as_ref().take_serial_0(); | ||
| 42 | |||
| 43 | info!("usb initialized!"); | ||
| 44 | |||
| 45 | unwrap!( | ||
| 46 | writer | ||
| 47 | .write_all(b"\r\nInput returned upper cased on CR+LF\r\n") | ||
| 48 | .await | ||
| 49 | ); | ||
| 50 | |||
| 51 | let mut buf = [0u8; 64]; | ||
| 52 | loop { | ||
| 53 | let mut n = 0; | ||
| 54 | |||
| 55 | async { | ||
| 56 | loop { | ||
| 57 | let char = unwrap!(reader.read_byte().await); | ||
| 58 | |||
| 59 | // throw away, read more on cr, exit on lf | ||
| 60 | if char == b'\r' { | ||
| 61 | continue; | ||
| 62 | } else if char == b'\n' { | ||
| 63 | break; | ||
| 64 | } | ||
| 65 | |||
| 66 | buf[n] = char; | ||
| 67 | n += 1; | ||
| 68 | |||
| 69 | // stop if we're out of room | ||
| 70 | if n == buf.len() { | ||
| 71 | break; | ||
| 72 | } | ||
| 73 | } | ||
| 74 | } | ||
| 75 | .await; | ||
| 76 | |||
| 77 | if n > 0 { | ||
| 78 | for char in buf[..n].iter_mut() { | ||
| 79 | // upper case | ||
| 80 | if 0x61 <= *char && *char <= 0x7a { | ||
| 81 | *char &= !0x20; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | unwrap!(writer.write_all(&buf[..n]).await); | ||
| 85 | unwrap!(writer.write_all(b"\r\n").await); | ||
| 86 | unwrap!(writer.flush().await); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | } | ||
diff --git a/examples/nrf/src/bin/usb_uart_io.rs b/examples/nrf/src/bin/usb_uart_io.rs deleted file mode 100644 index ef2629844..000000000 --- a/examples/nrf/src/bin/usb_uart_io.rs +++ /dev/null | |||
| @@ -1,66 +0,0 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use embassy::executor::Spawner; | ||
| 7 | use embassy::interrupt::InterruptExt; | ||
| 8 | use embassy::io::{read_line, AsyncWriteExt}; | ||
| 9 | use embassy_nrf::usb::{State, Usb, UsbBus, UsbSerial}; | ||
| 10 | use embassy_nrf::{interrupt, Peripherals}; | ||
| 11 | use futures::pin_mut; | ||
| 12 | use usb_device::device::{UsbDeviceBuilder, UsbVidPid}; | ||
| 13 | |||
| 14 | use defmt_rtt as _; // global logger | ||
| 15 | use panic_probe as _; // print out panic messages | ||
| 16 | |||
| 17 | #[embassy::main] | ||
| 18 | async fn main(_spawner: Spawner, p: Peripherals) { | ||
| 19 | let mut rx_buffer = [0u8; 64]; | ||
| 20 | // we send back input + cr + lf | ||
| 21 | let mut tx_buffer = [0u8; 66]; | ||
| 22 | |||
| 23 | let usb_bus = UsbBus::new(p.USBD); | ||
| 24 | |||
| 25 | let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer); | ||
| 26 | |||
| 27 | let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) | ||
| 28 | .manufacturer("Fake company") | ||
| 29 | .product("Serial port") | ||
| 30 | .serial_number("TEST") | ||
| 31 | .device_class(0x02) | ||
| 32 | .build(); | ||
| 33 | |||
| 34 | let irq = interrupt::take!(USBD); | ||
| 35 | irq.set_priority(interrupt::Priority::P3); | ||
| 36 | |||
| 37 | let mut state = State::new(); | ||
| 38 | let usb = unsafe { Usb::new(&mut state, device, serial, irq) }; | ||
| 39 | pin_mut!(usb); | ||
| 40 | |||
| 41 | let (mut reader, mut writer) = usb.as_ref().take_serial_0(); | ||
| 42 | |||
| 43 | info!("usb initialized!"); | ||
| 44 | |||
| 45 | unwrap!( | ||
| 46 | writer | ||
| 47 | .write_all(b"\r\nInput returned upper cased on CR+LF\r\n") | ||
| 48 | .await | ||
| 49 | ); | ||
| 50 | |||
| 51 | let mut buf = [0u8; 64]; | ||
| 52 | loop { | ||
| 53 | let n = unwrap!(read_line(&mut reader, &mut buf).await); | ||
| 54 | |||
| 55 | for char in buf[..n].iter_mut() { | ||
| 56 | // upper case | ||
| 57 | if 0x61 <= *char && *char <= 0x7a { | ||
| 58 | *char &= !0x20; | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | unwrap!(writer.write_all(&buf[..n]).await); | ||
| 63 | unwrap!(writer.write_all(b"\r\n").await); | ||
| 64 | unwrap!(writer.flush().await); | ||
| 65 | } | ||
| 66 | } | ||
