aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-04-07 23:03:39 +0000
committerGitHub <[email protected]>2022-04-07 23:03:39 +0000
commit37da84129da12280cfb0a4f016639d517a0f637e (patch)
treea638b15c2129c5fd4d7475be57574c27f445c652
parent637ec36f9c619c8aa1f844d1499492420aa36b29 (diff)
parent9252e8bb88236946597575918991b55bc10596b3 (diff)
Merge #657
657: Async usb stack r=Dirbaio a=Dirbaio TODO - [x] Make it work on nRF - [x] Add a way for classes to handle their own EP0 control requests - thanks `@alexmoon!` - [x] Handle CONTROL OUT requests with data. - [ ] Impl AsyncRead/AsyncWrite for CDC ACM -- will do later, it's not trivial - [x] Cleanup unwraps/asserts/panics - [x] Cleanup logs (make everything trace/debug, not info) - [ ] Port synopsys-usb-otg - [ ] Port stm32-usbd - [ ] Add more classes? HID, MSD? Co-authored-by: Dario Nieuwenhuis <[email protected]> Co-authored-by: alexmoon <[email protected]>
-rw-r--r--.github/workflows/rust.yml4
-rw-r--r--embassy-nrf/Cargo.toml5
-rw-r--r--embassy-nrf/src/chips/nrf52820.rs1
-rw-r--r--embassy-nrf/src/chips/nrf52833.rs1
-rw-r--r--embassy-nrf/src/chips/nrf52840.rs1
-rw-r--r--embassy-nrf/src/chips/nrf5340_app.rs1
-rw-r--r--embassy-nrf/src/lib.rs1
-rw-r--r--embassy-nrf/src/usb.rs872
-rw-r--r--embassy-usb-hid/Cargo.toml18
-rw-r--r--embassy-usb-hid/src/fmt.rs225
-rw-r--r--embassy-usb-hid/src/lib.rs522
-rw-r--r--embassy-usb-serial/Cargo.toml19
-rw-r--r--embassy-usb-serial/src/fmt.rs225
-rw-r--r--embassy-usb-serial/src/lib.rs369
-rw-r--r--embassy-usb/Cargo.toml19
-rw-r--r--embassy-usb/src/builder.rs386
-rw-r--r--embassy-usb/src/control.rs330
-rw-r--r--embassy-usb/src/descriptor.rs369
-rw-r--r--embassy-usb/src/driver.rs239
-rw-r--r--embassy-usb/src/fmt.rs225
-rw-r--r--embassy-usb/src/lib.rs344
-rw-r--r--embassy-usb/src/types.rs138
-rw-r--r--embassy-usb/src/util.rs45
-rw-r--r--examples/nrf/Cargo.toml14
-rw-r--r--examples/nrf/src/bin/usb_hid_keyboard.rs148
-rw-r--r--examples/nrf/src/bin/usb_hid_mouse.rs129
-rw-r--r--examples/nrf/src/bin/usb_serial.rs113
-rw-r--r--examples/nrf/src/bin/usb_serial_multitask.rs122
-rw-r--r--examples/nrf/src/bin/usb_uart.rs89
-rw-r--r--examples/nrf/src/bin/usb_uart_io.rs66
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
21nightly = ["embassy/nightly", "embedded-hal-1", "embedded-hal-async"] 21nightly = ["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 = []
64embassy = { version = "0.1.0", path = "../embassy" } 64embassy = { version = "0.1.0", path = "../embassy" }
65embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["nrf"]} 65embassy-macros = { version = "0.1.0", path = "../embassy-macros", features = ["nrf"]}
66embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } 66embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
67embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional=true }
67 68
68embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 69embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
69embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.7", git = "https://github.com/embassy-rs/embedded-hal", branch = "embassy2", optional = true} 70embedded-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"
80fixed = "1.10.0" 81fixed = "1.10.0"
81embedded-storage = "0.3.0" 82embedded-storage = "0.3.0"
82cfg-if = "1.0.0" 83cfg-if = "1.0.0"
83nrf-usbd = {version = "0.1.1"}
84usb-device = "0.2.8"
85 84
86nrf52805-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } 85nrf52805-pac = { version = "0.11.0", optional = true, features = [ "rt" ] }
87nrf52810-pac = { version = "0.11.0", optional = true, features = [ "rt" ] } 86nrf52810-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")]
128impl_usb!(USBD, USBD, USBD); 129impl_usb!(USBD, USBD, USBD);
129 130
130impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 131impl_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")]
160impl_usb!(USBD, USBD, USBD); 161impl_usb!(USBD, USBD, USBD);
161 162
162impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 163impl_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")]
163impl_usb!(USBD, USBD, USBD); 164impl_usb!(USBD, USBD, USBD);
164 165
165impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); 166impl_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")]
351impl_usb!(USBD, USBD, USBD); 352impl_usb!(USBD, USBD, USBD);
352 353
353impl_uarte!(UARTETWISPI0, UARTE0, SERIAL0); 354impl_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")]
94pub mod usb; 95pub mod usb;
95#[cfg(not(feature = "_nrf5340"))] 96#[cfg(not(feature = "_nrf5340"))]
96pub mod wdt; 97pub 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
3use core::marker::PhantomData;
4use core::mem::MaybeUninit;
5use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
6use core::task::Poll;
7use cortex_m::peripheral::NVIC;
8use embassy::interrupt::InterruptExt;
9use embassy::util::Unborrow;
10use embassy::waitqueue::AtomicWaker;
11use embassy_hal_common::unborrow;
12use embassy_usb::control::Request;
13use embassy_usb::driver::{self, Event, ReadError, WriteError};
14use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection};
15use futures::future::poll_fn;
16use futures::Future;
17
18pub use embassy_usb;
19use pac::usbd::RegisterBlock;
20
3use crate::interrupt::Interrupt; 21use crate::interrupt::Interrupt;
4use crate::pac; 22use crate::pac;
23use crate::util::slice_in_ram;
5 24
6use core::marker::PhantomData; 25const NEW_AW: AtomicWaker = AtomicWaker::new();
7use embassy::util::Unborrow; 26static BUS_WAKER: AtomicWaker = NEW_AW;
8use nrf_usbd::{UsbPeripheral, Usbd}; 27static EP0_WAKER: AtomicWaker = NEW_AW;
9use usb_device::bus::UsbBusAllocator; 28static EP_IN_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8];
29static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8];
30static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0);
31
32pub struct Driver<'d, T: Instance> {
33 phantom: PhantomData<&'d mut T>,
34 alloc_in: Allocator,
35 alloc_out: Allocator,
36}
37
38impl<'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
138impl<'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
11pub use embassy_hal_common::usb::*; 195 fn enable(self) -> Self::EnableFuture {
196 async move {
197 let regs = T::regs();
12 198
13pub 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
239pub 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
17unsafe impl<'d, T: Instance> UsbPeripheral for UsbBus<'d, T> { 245impl<'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
22impl<'d, T: Instance> UsbBus<'d, T> { 361pub enum Out {}
23 pub fn new(_usb: impl Unborrow<Target = T> + 'd) -> UsbBusAllocator<Usbd<UsbBus<'d, T>>> { 362pub enum In {}
24 let r = T::regs();
25 363
26 r.intenset.write(|w| { 364trait 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 { 370impl 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
387impl 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
404pub struct Endpoint<'d, T: Instance, Dir> {
405 _phantom: PhantomData<(&'d mut T, Dir)>,
406 info: EndpointInfo,
407}
408
409impl<'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
418impl<'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
448impl<'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
475unsafe 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 &regs.epout0,
490 &regs.epout1,
491 &regs.epout2,
492 &regs.epout3,
493 &regs.epout4,
494 &regs.epout5,
495 &regs.epout6,
496 &regs.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
518unsafe 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 &regs.epin0,
534 &regs.epin1,
535 &regs.epin2,
536 &regs.epin3,
537 &regs.epin4,
538 &regs.epin5,
539 &regs.epin6,
540 &regs.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
556impl<'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
573impl<'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
40unsafe impl embassy_hal_common::usb::USBInterrupt for crate::interrupt::USBD {} 592pub struct ControlPipe<'d, T: Instance> {
593 _phantom: PhantomData<&'d mut T>,
594 max_packet_size: u16,
595}
596
597impl<'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
732fn dma_start() {
733 compiler_fence(Ordering::Release);
734}
735
736fn dma_end() {
737 compiler_fence(Ordering::Acquire);
738}
739
740struct 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
746impl 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
42pub(crate) mod sealed { 809pub(crate) mod sealed {
43 use super::*; 810 use super::*;
@@ -63,3 +830,64 @@ macro_rules! impl_usb {
63 } 830 }
64 }; 831 };
65} 832}
833
834mod 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]
2name = "embassy-usb-hid"
3version = "0.1.0"
4edition = "2021"
5
6[features]
7default = ["usbd-hid"]
8usbd-hid = ["dep:usbd-hid", "ssmarshal"]
9
10[dependencies]
11embassy = { version = "0.1.0", path = "../embassy" }
12embassy-usb = { version = "0.1.0", path = "../embassy-usb" }
13
14defmt = { version = "0.3", optional = true }
15log = { version = "0.4.14", optional = true }
16usbd-hid = { version = "0.5.2", optional = true }
17ssmarshal = { version = "1.0", default-features = false, optional = true }
18futures-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"))]
5compile_error!("You may not enable both `defmt` and `log` features.");
6
7macro_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
18macro_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
29macro_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
40macro_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
51macro_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
62macro_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
73macro_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
84macro_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
95macro_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
106macro_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
119macro_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
132macro_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
145macro_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
158macro_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")]
172macro_rules! unwrap {
173 ($($x:tt)*) => {
174 ::defmt::unwrap!($($x)*)
175 };
176}
177
178#[cfg(not(feature = "defmt"))]
179macro_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)]
199pub struct NoneError;
200
201pub trait Try {
202 type Ok;
203 type Error;
204 fn into_result(self) -> Result<Self::Ok, Self::Error>;
205}
206
207impl<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
217impl<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.
8pub(crate) mod fmt;
9
10use core::mem::MaybeUninit;
11use core::ops::Range;
12use core::sync::atomic::{AtomicUsize, Ordering};
13
14use embassy::time::Duration;
15use embassy_usb::driver::EndpointOut;
16use embassy_usb::{
17 control::{ControlHandler, InResponse, OutResponse, Request, RequestType},
18 driver::{Driver, Endpoint, EndpointIn, WriteError},
19 UsbDeviceBuilder,
20};
21
22#[cfg(feature = "usbd-hid")]
23use ssmarshal::serialize;
24#[cfg(feature = "usbd-hid")]
25use usbd_hid::descriptor::AsInputReport;
26
27const USB_CLASS_HID: u8 = 0x03;
28const USB_SUBCLASS_NONE: u8 = 0x00;
29const USB_PROTOCOL_NONE: u8 = 0x00;
30
31// HID
32const HID_DESC_DESCTYPE_HID: u8 = 0x21;
33const HID_DESC_DESCTYPE_HID_REPORT: u8 = 0x22;
34const HID_DESC_SPEC_1_10: [u8; 2] = [0x10, 0x01];
35const HID_DESC_COUNTRY_UNSPEC: u8 = 0x00;
36
37const HID_REQ_SET_IDLE: u8 = 0x0a;
38const HID_REQ_GET_IDLE: u8 = 0x02;
39const HID_REQ_GET_REPORT: u8 = 0x01;
40const HID_REQ_SET_REPORT: u8 = 0x09;
41const HID_REQ_GET_PROTOCOL: u8 = 0x03;
42const HID_REQ_SET_PROTOCOL: u8 = 0x0b;
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45#[cfg_attr(feature = "defmt", derive(defmt::Format))]
46pub enum ReportId {
47 In(u8),
48 Out(u8),
49 Feature(u8),
50}
51
52impl 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
63pub struct State<'a, const IN_N: usize, const OUT_N: usize> {
64 control: MaybeUninit<Control<'a>>,
65 out_report_offset: AtomicUsize,
66}
67
68impl<'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
77pub struct HidClass<'d, D: Driver<'d>, T, const IN_N: usize> {
78 input: ReportWriter<'d, D, IN_N>,
79 output: T,
80}
81
82impl<'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
114impl<'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
124impl<'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
173pub struct ReportWriter<'d, D: Driver<'d>, const N: usize> {
174 ep_in: D::EndpointIn,
175}
176
177pub 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))]
184pub enum ReadError {
185 BufferOverflow,
186 Disabled,
187 Sync(Range<usize>),
188}
189
190impl 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
200impl<'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
239impl<'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
323pub 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
357struct 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
364impl<'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
437impl<'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]
2name = "embassy-usb-serial"
3version = "0.1.0"
4edition = "2021"
5
6[package.metadata.embassy_docs]
7src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-serial-v$VERSION/embassy-usb-serial/src/"
8src_base_git = "https://github.com/embassy-rs/embassy/blob/master/embassy-usb-serial/src/"
9features = ["defmt"]
10flavors = [
11 { name = "default", target = "thumbv7em-none-eabihf" },
12]
13
14[dependencies]
15embassy = { version = "0.1.0", path = "../embassy" }
16embassy-usb = { version = "0.1.0", path = "../embassy-usb" }
17
18defmt = { version = "0.3", optional = true }
19log = { 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"))]
5compile_error!("You may not enable both `defmt` and `log` features.");
6
7macro_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
18macro_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
29macro_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
40macro_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
51macro_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
62macro_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
73macro_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
84macro_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
95macro_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
106macro_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
119macro_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
132macro_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
145macro_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
158macro_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")]
172macro_rules! unwrap {
173 ($($x:tt)*) => {
174 ::defmt::unwrap!($($x)*)
175 };
176}
177
178#[cfg(not(feature = "defmt"))]
179macro_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)]
199pub struct NoneError;
200
201pub trait Try {
202 type Ok;
203 type Error;
204 fn into_result(self) -> Result<Self::Ok, Self::Error>;
205}
206
207impl<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
217impl<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.
6pub(crate) mod fmt;
7
8use core::cell::Cell;
9use core::mem::{self, MaybeUninit};
10use core::sync::atomic::{AtomicBool, Ordering};
11use embassy::blocking_mutex::CriticalSectionMutex;
12use embassy_usb::control::{self, ControlHandler, InResponse, OutResponse, Request};
13use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut, ReadError, WriteError};
14use embassy_usb::{driver::Driver, types::*, UsbDeviceBuilder};
15
16/// This should be used as `device_class` when building the `UsbDevice`.
17pub const USB_CLASS_CDC: u8 = 0x02;
18
19const USB_CLASS_CDC_DATA: u8 = 0x0a;
20const CDC_SUBCLASS_ACM: u8 = 0x02;
21const CDC_PROTOCOL_NONE: u8 = 0x00;
22
23const CS_INTERFACE: u8 = 0x24;
24const CDC_TYPE_HEADER: u8 = 0x00;
25const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01;
26const CDC_TYPE_ACM: u8 = 0x02;
27const CDC_TYPE_UNION: u8 = 0x06;
28
29const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00;
30#[allow(unused)]
31const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01;
32const REQ_SET_LINE_CODING: u8 = 0x20;
33const REQ_GET_LINE_CODING: u8 = 0x21;
34const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22;
35
36pub struct State<'a> {
37 control: MaybeUninit<Control<'a>>,
38 shared: ControlShared,
39}
40
41impl<'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.
62pub 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
70struct Control<'a> {
71 shared: &'a ControlShared,
72}
73
74/// Shared data between Control and CdcAcmClass
75struct ControlShared {
76 line_coding: CriticalSectionMutex<Cell<LineCoding>>,
77 dtr: AtomicBool,
78 rts: AtomicBool,
79}
80
81impl 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
96impl<'a> Control<'a> {
97 fn shared(&mut self) -> &'a ControlShared {
98 self.shared
99 }
100}
101
102impl<'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
162impl<'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)]
285pub 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
296impl 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)]
308pub enum ParityType {
309 None = 0,
310 Odd = 1,
311 Event = 2,
312 Mark = 3,
313 Space = 4,
314}
315
316impl 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)]
331pub struct LineCoding {
332 stop_bits: StopBits,
333 data_bits: u8,
334 parity_type: ParityType,
335 data_rate: u32,
336}
337
338impl 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
360impl 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]
2name = "embassy-usb"
3version = "0.1.0"
4edition = "2021"
5
6[package.metadata.embassy_docs]
7src_base = "https://github.com/embassy-rs/embassy/blob/embassy-usb-v$VERSION/embassy-usb/src/"
8src_base_git = "https://github.com/embassy-rs/embassy/blob/master/embassy-usb/src/"
9features = ["defmt"]
10flavors = [
11 { name = "default", target = "thumbv7em-none-eabihf" },
12]
13
14[dependencies]
15embassy = { version = "0.1.0", path = "../embassy" }
16
17defmt = { version = "0.3", optional = true }
18log = { version = "0.4.14", optional = true }
19heapless = "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 @@
1use heapless::Vec;
2
3use super::control::ControlHandler;
4use super::descriptor::{BosWriter, DescriptorWriter};
5use super::driver::{Driver, EndpointAllocError};
6use super::types::*;
7use super::UsbDevice;
8use super::MAX_INTERFACE_COUNT;
9
10#[derive(Debug, Copy, Clone)]
11#[cfg_attr(feature = "defmt", derive(defmt::Format))]
12#[non_exhaustive]
13pub 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
98impl<'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.
120pub 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
135impl<'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 @@
1use core::mem;
2
3use crate::descriptor::DescriptorWriter;
4use crate::driver::{self, ReadError};
5use crate::DEFAULT_ALTERNATE_SETTING;
6
7use 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))]
13pub 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))]
28pub 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))]
46pub 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
65impl 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))]
133pub enum OutResponse {
134 Accepted,
135 Rejected,
136}
137
138#[derive(Copy, Clone, Eq, PartialEq, Debug)]
139#[cfg_attr(feature = "defmt", derive(defmt::Format))]
140pub 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)
149pub 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))]
200pub(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))]
207pub(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))]
214pub(crate) struct StatusStage {}
215
216#[derive(Debug)]
217#[cfg_attr(feature = "defmt", derive(defmt::Format))]
218pub(crate) enum Setup {
219 DataIn(Request, DataInStage),
220 DataOut(Request, DataOutStage),
221}
222
223pub(crate) struct ControlPipe<C: driver::ControlPipe> {
224 control: C,
225}
226
227impl<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 @@
1use super::builder::Config;
2use super::{types::*, CONFIGURATION_VALUE, DEFAULT_ALTERNATE_SETTING};
3
4/// Standard descriptor types
5#[allow(missing_docs)]
6pub 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.
18pub 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)]
27pub 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.
36pub 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
44impl<'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.
310pub struct BosWriter<'a> {
311 pub(crate) writer: DescriptorWriter<'a>,
312 num_caps_mark: Option<usize>,
313}
314
315impl<'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 @@
1use core::future::Future;
2
3use crate::control::Request;
4
5use super::types::*;
6
7/// Driver for a specific USB peripheral. Implement this to add support for a new hardware
8/// platform.
9pub 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
59pub 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
111pub 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
132pub 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
144pub 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
183pub 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`].
195pub 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))]
210pub 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.
215pub struct Unsupported;
216
217#[derive(Copy, Clone, Eq, PartialEq, Debug)]
218#[cfg_attr(feature = "defmt", derive(defmt::Format))]
219/// Errors returned by [`EndpointIn::write`]
220pub 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`]
232pub 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"))]
5compile_error!("You may not enable both `defmt` and `log` features.");
6
7macro_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
18macro_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
29macro_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
40macro_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
51macro_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
62macro_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
73macro_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
84macro_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
95macro_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
106macro_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
119macro_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
132macro_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
145macro_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
158macro_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")]
172macro_rules! unwrap {
173 ($($x:tt)*) => {
174 ::defmt::unwrap!($($x)*)
175 };
176}
177
178#[cfg(not(feature = "defmt"))]
179macro_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)]
199pub struct NoneError;
200
201pub trait Try {
202 type Ok;
203 type Error;
204 fn into_result(self) -> Result<Self::Ok, Self::Error>;
205}
206
207impl<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
217impl<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.
6pub(crate) mod fmt;
7
8mod builder;
9pub mod control;
10pub mod descriptor;
11pub mod driver;
12pub mod types;
13mod util;
14
15use heapless::Vec;
16
17use self::control::*;
18use self::descriptor::*;
19use self::driver::{Bus, Driver, Event};
20use self::types::*;
21use self::util::*;
22
23pub use self::builder::Config;
24pub 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)]
31pub 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.
46pub const CONFIGURATION_NONE: u8 = 0;
47
48/// The bConfiguration value for the single configuration supported by this device.
49pub const CONFIGURATION_VALUE: u8 = 1;
50
51/// The default value for bAlternateSetting for all interfaces.
52pub const DEFAULT_ALTERNATE_SETTING: u8 = 0;
53
54pub const MAX_INTERFACE_COUNT: usize = 4;
55
56pub 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
74impl<'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))]
10pub enum UsbDirection {
11 /// Host to device (OUT)
12 Out = 0x00,
13 /// Device to host (IN)
14 In = 0x80,
15}
16
17impl 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))]
28pub 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))]
43pub struct EndpointAddress(u8);
44
45impl From<u8> for EndpointAddress {
46 #[inline]
47 fn from(addr: u8) -> EndpointAddress {
48 EndpointAddress(addr)
49 }
50}
51
52impl From<EndpointAddress> for u8 {
53 #[inline]
54 fn from(addr: EndpointAddress) -> u8 {
55 addr.0
56 }
57}
58
59impl 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))]
99pub 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))]
109pub struct InterfaceNumber(u8);
110
111impl InterfaceNumber {
112 pub(crate) fn new(index: u8) -> InterfaceNumber {
113 InterfaceNumber(index)
114 }
115}
116
117impl 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))]
126pub struct StringIndex(u8);
127
128impl StringIndex {
129 pub(crate) fn new(index: u8) -> StringIndex {
130 StringIndex(index)
131 }
132}
133
134impl 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 @@
1use core::future::Future;
2use core::pin::Pin;
3use core::task::{Context, Poll};
4
5#[derive(Debug, Clone)]
6pub enum Either<A, B> {
7 Left(A),
8 Right(B),
9}
10
11pub fn select<A, B>(a: A, b: B) -> Select<A, B>
12where
13 A: Future,
14 B: Future,
15{
16 Select { a, b }
17}
18
19pub struct Select<A, B> {
20 a: A,
21 b: B,
22}
23
24impl<A: Unpin, B: Unpin> Unpin for Select<A, B> {}
25
26impl<A, B> Future for Select<A, B>
27where
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]
2authors = ["Dario Nieuwenhuis <[email protected]>"] 2authors = ["Dario Nieuwenhuis <[email protected]>"]
3edition = "2018" 3edition = "2021"
4name = "embassy-nrf-examples" 4name = "embassy-nrf-examples"
5version = "0.1.0" 5version = "0.1.0"
6 6
7[features] 7[features]
8default = ["nightly"] 8default = ["nightly"]
9nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits"] 9nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits", "embassy-usb", "embassy-usb-serial", "embassy-usb-hid"]
10 10
11[dependencies] 11[dependencies]
12embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } 12embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
13embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } 13embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
14embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true }
15embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"], optional = true }
16embassy-usb-hid = { version = "0.1.0", path = "../../embassy-usb-hid", features = ["defmt"], optional = true }
14 17
15defmt = "0.3" 18defmt = "0.3"
16defmt-rtt = "0.3" 19defmt-rtt = "0.3"
@@ -21,6 +24,5 @@ panic-probe = { version = "0.3", features = ["print-defmt"] }
21futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 24futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
22rand = { version = "0.8.4", default-features = false } 25rand = { version = "0.8.4", default-features = false }
23embedded-storage = "0.3.0" 26embedded-storage = "0.3.0"
24 27usbd-hid = "0.5.2"
25usb-device = "0.2" 28serde = { version = "1.0.136", default-features = false }
26usbd-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
6use core::mem;
7use defmt::*;
8use embassy::executor::Spawner;
9use embassy::time::Duration;
10use embassy_nrf::gpio::{Input, Pin, Pull};
11use embassy_nrf::interrupt;
12use embassy_nrf::pac;
13use embassy_nrf::usb::Driver;
14use embassy_nrf::Peripherals;
15use embassy_usb::control::OutResponse;
16use embassy_usb::{Config, UsbDeviceBuilder};
17use embassy_usb_hid::{HidClass, ReportId, RequestHandler, State};
18use futures::future::join;
19use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};
20
21use defmt_rtt as _; // global logger
22use panic_probe as _;
23
24#[embassy::main]
25async 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
127struct MyRequestHandler {}
128
129impl 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
6use core::mem;
7use defmt::*;
8use embassy::executor::Spawner;
9use embassy::time::{Duration, Timer};
10use embassy_nrf::interrupt;
11use embassy_nrf::pac;
12use embassy_nrf::usb::Driver;
13use embassy_nrf::Peripherals;
14use embassy_usb::control::OutResponse;
15use embassy_usb::{Config, UsbDeviceBuilder};
16use embassy_usb_hid::{HidClass, ReportId, RequestHandler, State};
17use futures::future::join;
18use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
19
20use defmt_rtt as _; // global logger
21use panic_probe as _;
22
23#[embassy::main]
24async 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
108struct MyRequestHandler {}
109
110impl 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
6use core::mem;
7use defmt::{info, panic};
8use embassy::executor::Spawner;
9use embassy_nrf::interrupt;
10use embassy_nrf::pac;
11use embassy_nrf::usb::{Driver, Instance};
12use embassy_nrf::Peripherals;
13use embassy_usb::driver::{ReadError, WriteError};
14use embassy_usb::{Config, UsbDeviceBuilder};
15use embassy_usb_serial::{CdcAcmClass, State};
16use futures::future::join;
17
18use defmt_rtt as _; // global logger
19use panic_probe as _;
20
21#[embassy::main]
22async 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
83struct Disconnected {}
84
85impl 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
94impl 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
103async 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
6use core::mem;
7use defmt::{info, panic, unwrap};
8use embassy::executor::Spawner;
9use embassy::util::Forever;
10use embassy_nrf::pac;
11use embassy_nrf::usb::Driver;
12use embassy_nrf::Peripherals;
13use embassy_nrf::{interrupt, peripherals};
14use embassy_usb::driver::{ReadError, WriteError};
15use embassy_usb::{Config, UsbDevice, UsbDeviceBuilder};
16use embassy_usb_serial::{CdcAcmClass, State};
17
18use defmt_rtt as _; // global logger
19use panic_probe as _;
20
21type MyDriver = Driver<'static, peripherals::USBD>;
22
23#[embassy::task]
24async fn usb_task(mut device: UsbDevice<'static, MyDriver>) {
25 device.run().await;
26}
27
28#[embassy::task]
29async 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]
39async 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
94struct Disconnected {}
95
96impl 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
105impl 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
114async 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
5use defmt::{info, unwrap};
6use embassy::executor::Spawner;
7use embassy::interrupt::InterruptExt;
8use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
9use embassy_nrf::usb::{State, Usb, UsbBus, UsbSerial};
10use embassy_nrf::{interrupt, Peripherals};
11use futures::pin_mut;
12use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
13
14use defmt_rtt as _; // global logger
15use panic_probe as _; // print out panic messages
16
17#[embassy::main]
18async 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
5use defmt::{info, unwrap};
6use embassy::executor::Spawner;
7use embassy::interrupt::InterruptExt;
8use embassy::io::{read_line, AsyncWriteExt};
9use embassy_nrf::usb::{State, Usb, UsbBus, UsbSerial};
10use embassy_nrf::{interrupt, Peripherals};
11use futures::pin_mut;
12use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
13
14use defmt_rtt as _; // global logger
15use panic_probe as _; // print out panic messages
16
17#[embassy::main]
18async 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}