aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2022-03-09 01:34:35 +0100
committerDario Nieuwenhuis <[email protected]>2022-04-06 05:38:11 +0200
commit37598a5b3792ec1b763b5c16fe422c9e1347d7d6 (patch)
tree558d06652cd95998e2d7b4eebc8e85329aa17a6b
parentc1b382296434e762d16a36d658d2f308358e3f87 (diff)
wip: experimental async usb stack
-rw-r--r--embassy-nrf/Cargo.toml3
-rw-r--r--embassy-nrf/src/usb.rs487
-rw-r--r--embassy-usb/Cargo.toml12
-rw-r--r--embassy-usb/src/builder.rs347
-rw-r--r--embassy-usb/src/control.rs134
-rw-r--r--embassy-usb/src/descriptor.rs387
-rw-r--r--embassy-usb/src/driver.rs160
-rw-r--r--embassy-usb/src/fmt.rs225
-rw-r--r--embassy-usb/src/lib.rs342
-rw-r--r--embassy-usb/src/types.rs138
-rw-r--r--examples/nrf/Cargo.toml5
-rw-r--r--examples/nrf/src/bin/usb/cdc_acm.rs356
-rw-r--r--examples/nrf/src/bin/usb/main.rs53
-rw-r--r--examples/nrf/src/bin/usb_uart.rs89
-rw-r--r--examples/nrf/src/bin/usb_uart_io.rs66
15 files changed, 2621 insertions, 183 deletions
diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml
index 5e69a8878..36c61c651 100644
--- a/embassy-nrf/Cargo.toml
+++ b/embassy-nrf/Cargo.toml
@@ -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" }
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/usb.rs b/embassy-nrf/src/usb.rs
index deab94544..0f7d68d8c 100644
--- a/embassy-nrf/src/usb.rs
+++ b/embassy-nrf/src/usb.rs
@@ -1,43 +1,423 @@
1#![macro_use] 1#![macro_use]
2 2
3use crate::interrupt::Interrupt;
4use crate::pac;
5
6use core::marker::PhantomData; 3use core::marker::PhantomData;
4use core::sync::atomic::{compiler_fence, Ordering};
5use core::task::Poll;
6use embassy::interrupt::InterruptExt;
7use embassy::time::{with_timeout, Duration};
7use embassy::util::Unborrow; 8use embassy::util::Unborrow;
8use nrf_usbd::{UsbPeripheral, Usbd}; 9use embassy::waitqueue::AtomicWaker;
9use usb_device::bus::UsbBusAllocator; 10use embassy_hal_common::unborrow;
11use embassy_usb::driver::{self, ReadError, WriteError};
12use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection};
13use futures::future::poll_fn;
14use futures::Future;
15use pac::NVIC;
16
17pub use embassy_usb;
18
19use crate::interrupt::Interrupt;
20use crate::pac;
10 21
11pub use embassy_hal_common::usb::*; 22static EP0_WAKER: AtomicWaker = AtomicWaker::new();
12 23
13pub struct UsbBus<'d, T: Instance> { 24pub struct Driver<'d, T: Instance> {
14 phantom: PhantomData<&'d mut T>, 25 phantom: PhantomData<&'d mut T>,
26 alloc_in: Allocator,
27 alloc_out: Allocator,
15} 28}
16 29
17unsafe impl<'d, T: Instance> UsbPeripheral for UsbBus<'d, T> { 30impl<'d, T: Instance> Driver<'d, T> {
18 // todo how to use T::regs 31 pub fn new(
19 const REGISTERS: *const () = pac::USBD::ptr() as *const (); 32 _usb: impl Unborrow<Target = T> + 'd,
33 irq: impl Unborrow<Target = T::Interrupt> + 'd,
34 ) -> Self {
35 unborrow!(irq);
36 irq.set_handler(Self::on_interrupt);
37 irq.unpend();
38 irq.enable();
39
40 Self {
41 phantom: PhantomData,
42 alloc_in: Allocator::new(),
43 alloc_out: Allocator::new(),
44 }
45 }
46
47 fn on_interrupt(_: *mut ()) {
48 let regs = T::regs();
49
50 if regs.events_ep0setup.read().bits() != 0 {
51 regs.intenclr.write(|w| w.ep0setup().clear());
52 EP0_WAKER.wake();
53 }
54 if regs.events_ep0datadone.read().bits() != 0 {
55 regs.intenclr.write(|w| w.ep0datadone().clear());
56 EP0_WAKER.wake();
57 }
58 }
59
60 fn set_stalled(ep_addr: EndpointAddress, stalled: bool) {
61 let regs = T::regs();
62
63 unsafe {
64 if ep_addr.index() == 0 {
65 regs.tasks_ep0stall
66 .write(|w| w.tasks_ep0stall().bit(stalled));
67 } else {
68 regs.epstall.write(|w| {
69 w.ep().bits(ep_addr.index() as u8 & 0b111);
70 w.io().bit(ep_addr.is_in());
71 w.stall().bit(stalled)
72 });
73 }
74 }
75
76 //if stalled {
77 // self.busy_in_endpoints &= !(1 << ep_addr.index());
78 //}
79 }
80
81 fn is_stalled(ep_addr: EndpointAddress) -> bool {
82 let regs = T::regs();
83
84 let i = ep_addr.index();
85 match ep_addr.direction() {
86 UsbDirection::Out => regs.halted.epout[i].read().getstatus().is_halted(),
87 UsbDirection::In => regs.halted.epin[i].read().getstatus().is_halted(),
88 }
89 }
20} 90}
21 91
22impl<'d, T: Instance> UsbBus<'d, T> { 92impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
23 pub fn new(_usb: impl Unborrow<Target = T> + 'd) -> UsbBusAllocator<Usbd<UsbBus<'d, T>>> { 93 type EndpointOut = Endpoint<'d, T, Out>;
24 let r = T::regs(); 94 type EndpointIn = Endpoint<'d, T, In>;
95 type Bus = Bus<'d, T>;
25 96
26 r.intenset.write(|w| { 97 fn alloc_endpoint_in(
27 w.sof().set_bit(); 98 &mut self,
28 w.usbevent().set_bit(); 99 ep_addr: Option<EndpointAddress>,
29 w.ep0datadone().set_bit(); 100 ep_type: EndpointType,
30 w.ep0setup().set_bit(); 101 max_packet_size: u16,
31 w.usbreset().set_bit() 102 interval: u8,
32 }); 103 ) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
104 let index = self
105 .alloc_in
106 .allocate(ep_addr, ep_type, max_packet_size, interval)?;
107 let ep_addr = EndpointAddress::from_parts(index, UsbDirection::In);
108 Ok(Endpoint {
109 _phantom: PhantomData,
110 info: EndpointInfo {
111 addr: ep_addr,
112 ep_type,
113 max_packet_size,
114 interval,
115 },
116 })
117 }
33 118
34 Usbd::new(UsbBus { 119 fn alloc_endpoint_out(
35 phantom: PhantomData, 120 &mut self,
121 ep_addr: Option<EndpointAddress>,
122 ep_type: EndpointType,
123 max_packet_size: u16,
124 interval: u8,
125 ) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
126 let index = self
127 .alloc_out
128 .allocate(ep_addr, ep_type, max_packet_size, interval)?;
129 let ep_addr = EndpointAddress::from_parts(index, UsbDirection::Out);
130 Ok(Endpoint {
131 _phantom: PhantomData,
132 info: EndpointInfo {
133 addr: ep_addr,
134 ep_type,
135 max_packet_size,
136 interval,
137 },
36 }) 138 })
37 } 139 }
140
141 fn enable(self) -> Self::Bus {
142 let regs = T::regs();
143
144 errata::pre_enable();
145
146 regs.enable.write(|w| w.enable().enabled());
147
148 // Wait until the peripheral is ready.
149 while !regs.eventcause.read().ready().is_ready() {}
150 regs.eventcause.write(|w| w.ready().set_bit()); // Write 1 to clear.
151
152 errata::post_enable();
153
154 unsafe { NVIC::unmask(pac::Interrupt::USBD) };
155
156 // Enable the USB pullup, allowing enumeration.
157 regs.usbpullup.write(|w| w.connect().enabled());
158 info!("enabled");
159
160 Bus {
161 phantom: PhantomData,
162 alloc_in: self.alloc_in,
163 alloc_out: self.alloc_out,
164 }
165 }
166}
167
168pub struct Bus<'d, T: Instance> {
169 phantom: PhantomData<&'d mut T>,
170 alloc_in: Allocator,
171 alloc_out: Allocator,
172}
173
174impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
175 #[inline]
176 fn reset(&mut self) {
177 let regs = T::regs();
178
179 // TODO: Initialize ISO buffers
180
181 // XXX this is not spec compliant; the endpoints should only be enabled after the device
182 // has been put in the Configured state. However, usb-device provides no hook to do that
183 unsafe {
184 regs.epinen.write(|w| w.bits(self.alloc_in.used.into()));
185 regs.epouten.write(|w| w.bits(self.alloc_out.used.into()));
186 }
187
188 for i in 1..8 {
189 let out_enabled = self.alloc_out.used & (1 << i) != 0;
190
191 // when first enabled, bulk/interrupt OUT endpoints will *not* receive data (the
192 // peripheral will NAK all incoming packets) until we write a zero to the SIZE
193 // register (see figure 203 of the 52840 manual). To avoid that we write a 0 to the
194 // SIZE register
195 if out_enabled {
196 regs.size.epout[i].reset();
197 }
198 }
199
200 //self.busy_in_endpoints = 0;
201 }
202
203 #[inline]
204 fn set_device_address(&mut self, _addr: u8) {
205 // Nothing to do, the peripheral handles this.
206 }
207
208 fn set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
209 Driver::<T>::set_stalled(ep_addr, stalled)
210 }
211
212 fn is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {
213 Driver::<T>::is_stalled(ep_addr)
214 }
215
216 #[inline]
217 fn suspend(&mut self) {
218 let regs = T::regs();
219 regs.lowpower.write(|w| w.lowpower().low_power());
220 }
221
222 #[inline]
223 fn resume(&mut self) {
224 let regs = T::regs();
225
226 errata::pre_wakeup();
227
228 regs.lowpower.write(|w| w.lowpower().force_normal());
229 }
230}
231
232pub enum Out {}
233pub enum In {}
234
235pub struct Endpoint<'d, T: Instance, Dir> {
236 _phantom: PhantomData<(&'d mut T, Dir)>,
237 info: EndpointInfo,
238}
239
240impl<'d, T: Instance, Dir> driver::Endpoint for Endpoint<'d, T, Dir> {
241 fn info(&self) -> &EndpointInfo {
242 &self.info
243 }
244
245 fn set_stalled(&self, stalled: bool) {
246 Driver::<T>::set_stalled(self.info.addr, stalled)
247 }
248
249 fn is_stalled(&self) -> bool {
250 Driver::<T>::is_stalled(self.info.addr)
251 }
38} 252}
39 253
40unsafe impl embassy_hal_common::usb::USBInterrupt for crate::interrupt::USBD {} 254impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {
255 type ReadFuture<'a>
256 where
257 Self: 'a,
258 = impl Future<Output = Result<usize, ReadError>> + 'a;
259
260 fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
261 async move {
262 let regs = T::regs();
263
264 if buf.len() == 0 {
265 regs.tasks_ep0status.write(|w| unsafe { w.bits(1) });
266 return Ok(0);
267 }
268
269 // Wait for SETUP packet
270 regs.events_ep0setup.reset();
271 regs.intenset.write(|w| w.ep0setup().set());
272 poll_fn(|cx| {
273 EP0_WAKER.register(cx.waker());
274 if regs.events_ep0setup.read().bits() != 0 {
275 Poll::Ready(())
276 } else {
277 Poll::Pending
278 }
279 })
280 .await;
281 info!("got SETUP");
282
283 if buf.len() < 8 {
284 return Err(ReadError::BufferOverflow);
285 }
286
287 buf[0] = regs.bmrequesttype.read().bits() as u8;
288 buf[1] = regs.brequest.read().brequest().bits();
289 buf[2] = regs.wvaluel.read().wvaluel().bits();
290 buf[3] = regs.wvalueh.read().wvalueh().bits();
291 buf[4] = regs.windexl.read().windexl().bits();
292 buf[5] = regs.windexh.read().windexh().bits();
293 buf[6] = regs.wlengthl.read().wlengthl().bits();
294 buf[7] = regs.wlengthh.read().wlengthh().bits();
295
296 Ok(8)
297 }
298 }
299}
300
301impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {
302 type WriteFuture<'a>
303 where
304 Self: 'a,
305 = impl Future<Output = Result<(), WriteError>> + 'a;
306
307 fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
308 async move {
309 info!("write: {:x}", buf);
310
311 let regs = T::regs();
312
313 let ptr = buf.as_ptr() as u32;
314 let len = buf.len() as u32;
315 regs.epin0.ptr.write(|w| unsafe { w.bits(ptr) });
316 regs.epin0.maxcnt.write(|w| unsafe { w.bits(len) });
317
318 regs.events_ep0datadone.reset();
319 regs.events_endepin[0].reset();
320
321 dma_start();
322
323 regs.tasks_startepin[0].write(|w| unsafe { w.bits(1) });
324 info!("write: waiting for endepin...");
325 while regs.events_endepin[0].read().bits() == 0 {}
326
327 dma_end();
328
329 info!("write: waiting for ep0datadone...");
330 regs.intenset.write(|w| w.ep0datadone().set());
331 let res = with_timeout(
332 Duration::from_millis(10),
333 poll_fn(|cx| {
334 EP0_WAKER.register(cx.waker());
335 if regs.events_ep0datadone.read().bits() != 0 {
336 Poll::Ready(())
337 } else {
338 Poll::Pending
339 }
340 }),
341 )
342 .await;
343
344 if res.is_err() {
345 // todo wrong error
346 return Err(driver::WriteError::BufferOverflow);
347 }
348
349 info!("write done");
350
351 Ok(())
352 }
353 }
354}
355
356fn dma_start() {
357 compiler_fence(Ordering::Release);
358}
359
360fn dma_end() {
361 compiler_fence(Ordering::Acquire);
362}
363
364struct Allocator {
365 used: u16,
366 // Buffers can be up to 64 Bytes since this is a Full-Speed implementation.
367 lens: [u8; 9],
368}
369
370impl Allocator {
371 fn new() -> Self {
372 Self {
373 used: 0,
374 lens: [0; 9],
375 }
376 }
377
378 fn allocate(
379 &mut self,
380 ep_addr: Option<EndpointAddress>,
381 ep_type: EndpointType,
382 max_packet_size: u16,
383 _interval: u8,
384 ) -> Result<usize, driver::EndpointAllocError> {
385 // Endpoint addresses are fixed in hardware:
386 // - 0x80 / 0x00 - Control EP0
387 // - 0x81 / 0x01 - Bulk/Interrupt EP1
388 // - 0x82 / 0x02 - Bulk/Interrupt EP2
389 // - 0x83 / 0x03 - Bulk/Interrupt EP3
390 // - 0x84 / 0x04 - Bulk/Interrupt EP4
391 // - 0x85 / 0x05 - Bulk/Interrupt EP5
392 // - 0x86 / 0x06 - Bulk/Interrupt EP6
393 // - 0x87 / 0x07 - Bulk/Interrupt EP7
394 // - 0x88 / 0x08 - Isochronous
395
396 // Endpoint directions are allocated individually.
397
398 let alloc_index = match ep_type {
399 EndpointType::Isochronous => 8,
400 EndpointType::Control => 0,
401 EndpointType::Interrupt | EndpointType::Bulk => {
402 // Find rightmost zero bit in 1..=7
403 let ones = (self.used >> 1).trailing_ones() as usize;
404 if ones >= 7 {
405 return Err(driver::EndpointAllocError);
406 }
407 ones + 1
408 }
409 };
410
411 if self.used & (1 << alloc_index) != 0 {
412 return Err(driver::EndpointAllocError);
413 }
414
415 self.used |= 1 << alloc_index;
416 self.lens[alloc_index] = max_packet_size as u8;
417
418 Ok(alloc_index)
419 }
420}
41 421
42pub(crate) mod sealed { 422pub(crate) mod sealed {
43 use super::*; 423 use super::*;
@@ -63,3 +443,64 @@ macro_rules! impl_usb {
63 } 443 }
64 }; 444 };
65} 445}
446
447mod errata {
448
449 /// Writes `val` to `addr`. Used to apply Errata workarounds.
450 unsafe fn poke(addr: u32, val: u32) {
451 (addr as *mut u32).write_volatile(val);
452 }
453
454 /// Reads 32 bits from `addr`.
455 unsafe fn peek(addr: u32) -> u32 {
456 (addr as *mut u32).read_volatile()
457 }
458
459 pub fn pre_enable() {
460 // Works around Erratum 187 on chip revisions 1 and 2.
461 unsafe {
462 poke(0x4006EC00, 0x00009375);
463 poke(0x4006ED14, 0x00000003);
464 poke(0x4006EC00, 0x00009375);
465 }
466
467 pre_wakeup();
468 }
469
470 pub fn post_enable() {
471 post_wakeup();
472
473 // Works around Erratum 187 on chip revisions 1 and 2.
474 unsafe {
475 poke(0x4006EC00, 0x00009375);
476 poke(0x4006ED14, 0x00000000);
477 poke(0x4006EC00, 0x00009375);
478 }
479 }
480
481 pub fn pre_wakeup() {
482 // Works around Erratum 171 on chip revisions 1 and 2.
483
484 unsafe {
485 if peek(0x4006EC00) == 0x00000000 {
486 poke(0x4006EC00, 0x00009375);
487 }
488
489 poke(0x4006EC14, 0x000000C0);
490 poke(0x4006EC00, 0x00009375);
491 }
492 }
493
494 pub fn post_wakeup() {
495 // Works around Erratum 171 on chip revisions 1 and 2.
496
497 unsafe {
498 if peek(0x4006EC00) == 0x00000000 {
499 poke(0x4006EC00, 0x00009375);
500 }
501
502 poke(0x4006EC14, 0x00000000);
503 poke(0x4006EC00, 0x00009375);
504 }
505 }
506}
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml
new file mode 100644
index 000000000..dfdc8fbac
--- /dev/null
+++ b/embassy-usb/Cargo.toml
@@ -0,0 +1,12 @@
1[package]
2name = "embassy-usb"
3version = "0.1.0"
4edition = "2018"
5
6[dependencies]
7embassy = { version = "0.1.0", path = "../embassy" }
8
9defmt = { version = "0.3", optional = true }
10log = { version = "0.4.14", optional = true }
11cortex-m = "0.7.3"
12num-traits = { version = "0.2.14", default-features = false }
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs
new file mode 100644
index 000000000..e92cc8ef2
--- /dev/null
+++ b/embassy-usb/src/builder.rs
@@ -0,0 +1,347 @@
1use super::descriptor::{BosWriter, DescriptorWriter};
2use super::driver::{Driver, EndpointAllocError};
3use super::types::*;
4use super::UsbDevice;
5
6#[derive(Debug, Copy, Clone)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8#[non_exhaustive]
9pub struct Config<'a> {
10 pub(crate) vendor_id: u16,
11 pub(crate) product_id: u16,
12
13 /// Device class code assigned by USB.org. Set to `0xff` for vendor-specific
14 /// devices that do not conform to any class.
15 ///
16 /// Default: `0x00` (class code specified by interfaces)
17 pub device_class: u8,
18
19 /// Device sub-class code. Depends on class.
20 ///
21 /// Default: `0x00`
22 pub device_sub_class: u8,
23
24 /// Device protocol code. Depends on class and sub-class.
25 ///
26 /// Default: `0x00`
27 pub device_protocol: u8,
28
29 /// Device release version in BCD.
30 ///
31 /// Default: `0x0010` ("0.1")
32 pub device_release: u16,
33
34 /// Maximum packet size in bytes for the control endpoint 0.
35 ///
36 /// Valid values are 8, 16, 32 and 64. There's generally no need to change this from the default
37 /// value of 8 bytes unless a class uses control transfers for sending large amounts of data, in
38 /// which case using a larger packet size may be more efficient.
39 ///
40 /// Default: 8 bytes
41 pub max_packet_size_0: u8,
42
43 /// Manufacturer name string descriptor.
44 ///
45 /// Default: (none)
46 pub manufacturer: Option<&'a str>,
47
48 /// Product name string descriptor.
49 ///
50 /// Default: (none)
51 pub product: Option<&'a str>,
52
53 /// Serial number string descriptor.
54 ///
55 /// Default: (none)
56 pub serial_number: Option<&'a str>,
57
58 /// Whether the device supports remotely waking up the host is requested.
59 ///
60 /// Default: `false`
61 pub supports_remote_wakeup: bool,
62
63 /// Configures the device as a composite device with interface association descriptors.
64 ///
65 /// If set to `true`, the following fields should have the given values:
66 ///
67 /// - `device_class` = `0xEF`
68 /// - `device_sub_class` = `0x02`
69 /// - `device_protocol` = `0x01`
70 pub composite_with_iads: bool,
71
72 /// Whether the device has its own power source.
73 ///
74 /// This should be set to `true` even if the device is sometimes self-powered and may not
75 /// always draw power from the USB bus.
76 ///
77 /// Default: `false`
78 ///
79 /// See also: `max_power`
80 pub self_powered: bool,
81
82 /// Maximum current drawn from the USB bus by the device, in milliamps.
83 ///
84 /// The default is 100 mA. If your device always uses an external power source and never draws
85 /// power from the USB bus, this can be set to 0.
86 ///
87 /// See also: `self_powered`
88 ///
89 /// Default: 100mA
90 /// Max: 500mA
91 pub max_power: u16,
92}
93
94impl<'a> Config<'a> {
95 pub fn new(vid: u16, pid: u16) -> Self {
96 Self {
97 device_class: 0x00,
98 device_sub_class: 0x00,
99 device_protocol: 0x00,
100 max_packet_size_0: 8,
101 vendor_id: vid,
102 product_id: pid,
103 device_release: 0x0010,
104 manufacturer: None,
105 product: None,
106 serial_number: None,
107 self_powered: false,
108 supports_remote_wakeup: false,
109 composite_with_iads: false,
110 max_power: 100,
111 }
112 }
113}
114
115/// Used to build new [`UsbDevice`]s.
116pub struct UsbDeviceBuilder<'d, D: Driver<'d>> {
117 config: Config<'d>,
118
119 bus: D,
120 next_interface_number: u8,
121 next_string_index: u8,
122
123 // TODO make not pub?
124 pub device_descriptor: DescriptorWriter<'d>,
125 pub config_descriptor: DescriptorWriter<'d>,
126 pub bos_descriptor: BosWriter<'d>,
127}
128
129impl<'d, D: Driver<'d>> UsbDeviceBuilder<'d, D> {
130 /// Creates a builder for constructing a new [`UsbDevice`].
131 pub fn new(
132 bus: D,
133 config: Config<'d>,
134 device_descriptor_buf: &'d mut [u8],
135 config_descriptor_buf: &'d mut [u8],
136 bos_descriptor_buf: &'d mut [u8],
137 ) -> Self {
138 // Magic values specified in USB-IF ECN on IADs.
139 if config.composite_with_iads
140 && (config.device_class != 0xEF
141 || config.device_sub_class != 0x02
142 || config.device_protocol != 0x01)
143 {
144 panic!("if composite_with_iads is set, you must set device_class = 0xEF, device_sub_class = 0x02, device_protocol = 0x01");
145 }
146
147 if config.max_power > 500 {
148 panic!("The maximum allowed value for `max_power` is 500mA");
149 }
150
151 match config.max_packet_size_0 {
152 8 | 16 | 32 | 64 => {}
153 _ => panic!("invalid max_packet_size_0, the allowed values are 8, 16, 32 or 64"),
154 }
155
156 let mut device_descriptor = DescriptorWriter::new(device_descriptor_buf);
157 let mut config_descriptor = DescriptorWriter::new(config_descriptor_buf);
158 let mut bos_descriptor = BosWriter::new(DescriptorWriter::new(bos_descriptor_buf));
159
160 device_descriptor.device(&config).unwrap();
161 config_descriptor.configuration(&config).unwrap();
162 bos_descriptor.bos().unwrap();
163
164 UsbDeviceBuilder {
165 bus,
166 config,
167 next_interface_number: 0,
168 next_string_index: 4,
169
170 device_descriptor,
171 config_descriptor,
172 bos_descriptor,
173 }
174 }
175
176 /// Creates the [`UsbDevice`] instance with the configuration in this builder.
177 pub fn build(mut self) -> UsbDevice<'d, D> {
178 self.config_descriptor.end_configuration();
179 self.bos_descriptor.end_bos();
180
181 UsbDevice::build(
182 self.bus,
183 self.config,
184 self.device_descriptor.into_buf(),
185 self.config_descriptor.into_buf(),
186 self.bos_descriptor.writer.into_buf(),
187 )
188 }
189
190 /// Allocates a new interface number.
191 pub fn alloc_interface(&mut self) -> InterfaceNumber {
192 let number = self.next_interface_number;
193 self.next_interface_number += 1;
194
195 InterfaceNumber::new(number)
196 }
197
198 /// Allocates a new string index.
199 pub fn alloc_string(&mut self) -> StringIndex {
200 let index = self.next_string_index;
201 self.next_string_index += 1;
202
203 StringIndex::new(index)
204 }
205
206 /// Allocates an in endpoint.
207 ///
208 /// This directly delegates to [`Driver::alloc_endpoint_in`], so see that method for details. In most
209 /// cases classes should call the endpoint type specific methods instead.
210 pub fn alloc_endpoint_in(
211 &mut self,
212 ep_addr: Option<EndpointAddress>,
213 ep_type: EndpointType,
214 max_packet_size: u16,
215 interval: u8,
216 ) -> Result<D::EndpointIn, EndpointAllocError> {
217 self.bus
218 .alloc_endpoint_in(ep_addr, ep_type, max_packet_size, interval)
219 }
220
221 /// Allocates an out endpoint.
222 ///
223 /// This directly delegates to [`Driver::alloc_endpoint_out`], so see that method for details. In most
224 /// cases classes should call the endpoint type specific methods instead.
225 pub fn alloc_endpoint_out(
226 &mut self,
227 ep_addr: Option<EndpointAddress>,
228 ep_type: EndpointType,
229 max_packet_size: u16,
230 interval: u8,
231 ) -> Result<D::EndpointOut, EndpointAllocError> {
232 self.bus
233 .alloc_endpoint_out(ep_addr, ep_type, max_packet_size, interval)
234 }
235
236 /// Allocates a control in endpoint.
237 ///
238 /// This crate implements the control state machine only for endpoint 0. If classes want to
239 /// support control requests in other endpoints, the state machine must be implemented manually.
240 /// This should rarely be needed by classes.
241 ///
242 /// # Arguments
243 ///
244 /// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64.
245 ///
246 /// # Panics
247 ///
248 /// Panics if endpoint allocation fails, because running out of endpoints or memory is not
249 /// feasibly recoverable.
250 #[inline]
251 pub fn alloc_control_endpoint_in(&mut self, max_packet_size: u16) -> D::EndpointIn {
252 self.alloc_endpoint_in(None, EndpointType::Control, max_packet_size, 0)
253 .expect("alloc_ep failed")
254 }
255
256 /// Allocates a control out endpoint.
257 ///
258 /// This crate implements the control state machine only for endpoint 0. If classes want to
259 /// support control requests in other endpoints, the state machine must be implemented manually.
260 /// This should rarely be needed by classes.
261 ///
262 /// # Arguments
263 ///
264 /// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64.
265 ///
266 /// # Panics
267 ///
268 /// Panics if endpoint allocation fails, because running out of endpoints or memory is not
269 /// feasibly recoverable.
270 #[inline]
271 pub fn alloc_control_endpoint_out(&mut self, max_packet_size: u16) -> D::EndpointOut {
272 self.alloc_endpoint_out(None, EndpointType::Control, max_packet_size, 0)
273 .expect("alloc_ep failed")
274 }
275
276 /// Allocates a bulk in endpoint.
277 ///
278 /// # Arguments
279 ///
280 /// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64.
281 ///
282 /// # Panics
283 ///
284 /// Panics if endpoint allocation fails, because running out of endpoints or memory is not
285 /// feasibly recoverable.
286 #[inline]
287 pub fn alloc_bulk_endpoint_in(&mut self, max_packet_size: u16) -> D::EndpointIn {
288 self.alloc_endpoint_in(None, EndpointType::Bulk, max_packet_size, 0)
289 .expect("alloc_ep failed")
290 }
291
292 /// Allocates a bulk out endpoint.
293 ///
294 /// # Arguments
295 ///
296 /// * `max_packet_size` - Maximum packet size in bytes. Must be one of 8, 16, 32 or 64.
297 ///
298 /// # Panics
299 ///
300 /// Panics if endpoint allocation fails, because running out of endpoints or memory is not
301 /// feasibly recoverable.
302 #[inline]
303 pub fn alloc_bulk_endpoint_out(&mut self, max_packet_size: u16) -> D::EndpointOut {
304 self.alloc_endpoint_out(None, EndpointType::Bulk, max_packet_size, 0)
305 .expect("alloc_ep failed")
306 }
307
308 /// Allocates a bulk in endpoint.
309 ///
310 /// # Arguments
311 ///
312 /// * `max_packet_size` - Maximum packet size in bytes. Cannot exceed 64 bytes.
313 ///
314 /// # Panics
315 ///
316 /// Panics if endpoint allocation fails, because running out of endpoints or memory is not
317 /// feasibly recoverable.
318 #[inline]
319 pub fn alloc_interrupt_endpoint_in(
320 &mut self,
321 max_packet_size: u16,
322 interval: u8,
323 ) -> D::EndpointIn {
324 self.alloc_endpoint_in(None, EndpointType::Interrupt, max_packet_size, interval)
325 .expect("alloc_ep failed")
326 }
327
328 /// Allocates a bulk in endpoint.
329 ///
330 /// # Arguments
331 ///
332 /// * `max_packet_size` - Maximum packet size in bytes. Cannot exceed 64 bytes.
333 ///
334 /// # Panics
335 ///
336 /// Panics if endpoint allocation fails, because running out of endpoints or memory is not
337 /// feasibly recoverable.
338 #[inline]
339 pub fn alloc_interrupt_endpoint_out(
340 &mut self,
341 max_packet_size: u16,
342 interval: u8,
343 ) -> D::EndpointOut {
344 self.alloc_endpoint_out(None, EndpointType::Interrupt, max_packet_size, interval)
345 .expect("alloc_ep failed")
346 }
347}
diff --git a/embassy-usb/src/control.rs b/embassy-usb/src/control.rs
new file mode 100644
index 000000000..f1148ac76
--- /dev/null
+++ b/embassy-usb/src/control.rs
@@ -0,0 +1,134 @@
1use core::mem;
2
3use super::types::*;
4
5#[derive(Debug, PartialEq, Eq, Clone, Copy)]
6#[cfg_attr(feature = "defmt", derive(defmt::Format))]
7pub enum ParseError {
8 InvalidLength,
9}
10
11/// Control request type.
12#[repr(u8)]
13#[derive(Copy, Clone, Eq, PartialEq, Debug)]
14#[cfg_attr(feature = "defmt", derive(defmt::Format))]
15pub enum RequestType {
16 /// Request is a USB standard request. Usually handled by
17 /// [`UsbDevice`](crate::device::UsbDevice).
18 Standard = 0,
19 /// Request is intended for a USB class.
20 Class = 1,
21 /// Request is vendor-specific.
22 Vendor = 2,
23 /// Reserved.
24 Reserved = 3,
25}
26
27/// Control request recipient.
28#[derive(Copy, Clone, Eq, PartialEq, Debug)]
29#[cfg_attr(feature = "defmt", derive(defmt::Format))]
30pub enum Recipient {
31 /// Request is intended for the entire device.
32 Device = 0,
33 /// Request is intended for an interface. Generally, the `index` field of the request specifies
34 /// the interface number.
35 Interface = 1,
36 /// Request is intended for an endpoint. Generally, the `index` field of the request specifies
37 /// the endpoint address.
38 Endpoint = 2,
39 /// None of the above.
40 Other = 3,
41 /// Reserved.
42 Reserved = 4,
43}
44
45/// A control request read from a SETUP packet.
46#[derive(Copy, Clone, Eq, PartialEq, Debug)]
47#[cfg_attr(feature = "defmt", derive(defmt::Format))]
48pub struct Request {
49 /// Direction of the request.
50 pub direction: UsbDirection,
51 /// Type of the request.
52 pub request_type: RequestType,
53 /// Recipient of the request.
54 pub recipient: Recipient,
55 /// Request code. The meaning of the value depends on the previous fields.
56 pub request: u8,
57 /// Request value. The meaning of the value depends on the previous fields.
58 pub value: u16,
59 /// Request index. The meaning of the value depends on the previous fields.
60 pub index: u16,
61 /// Length of the DATA stage. For control OUT transfers this is the exact length of the data the
62 /// host sent. For control IN transfers this is the maximum length of data the device should
63 /// return.
64 pub length: u16,
65}
66
67impl Request {
68 /// Standard USB control request Get Status
69 pub const GET_STATUS: u8 = 0;
70
71 /// Standard USB control request Clear Feature
72 pub const CLEAR_FEATURE: u8 = 1;
73
74 /// Standard USB control request Set Feature
75 pub const SET_FEATURE: u8 = 3;
76
77 /// Standard USB control request Set Address
78 pub const SET_ADDRESS: u8 = 5;
79
80 /// Standard USB control request Get Descriptor
81 pub const GET_DESCRIPTOR: u8 = 6;
82
83 /// Standard USB control request Set Descriptor
84 pub const SET_DESCRIPTOR: u8 = 7;
85
86 /// Standard USB control request Get Configuration
87 pub const GET_CONFIGURATION: u8 = 8;
88
89 /// Standard USB control request Set Configuration
90 pub const SET_CONFIGURATION: u8 = 9;
91
92 /// Standard USB control request Get Interface
93 pub const GET_INTERFACE: u8 = 10;
94
95 /// Standard USB control request Set Interface
96 pub const SET_INTERFACE: u8 = 11;
97
98 /// Standard USB control request Synch Frame
99 pub const SYNCH_FRAME: u8 = 12;
100
101 /// Standard USB feature Endpoint Halt for Set/Clear Feature
102 pub const FEATURE_ENDPOINT_HALT: u16 = 0;
103
104 /// Standard USB feature Device Remote Wakeup for Set/Clear Feature
105 pub const FEATURE_DEVICE_REMOTE_WAKEUP: u16 = 1;
106
107 pub(crate) fn parse(buf: &[u8]) -> Result<Request, ParseError> {
108 if buf.len() != 8 {
109 return Err(ParseError::InvalidLength);
110 }
111
112 let rt = buf[0];
113 let recipient = rt & 0b11111;
114
115 Ok(Request {
116 direction: rt.into(),
117 request_type: unsafe { mem::transmute((rt >> 5) & 0b11) },
118 recipient: if recipient <= 3 {
119 unsafe { mem::transmute(recipient) }
120 } else {
121 Recipient::Reserved
122 },
123 request: buf[1],
124 value: (buf[2] as u16) | ((buf[3] as u16) << 8),
125 index: (buf[4] as u16) | ((buf[5] as u16) << 8),
126 length: (buf[6] as u16) | ((buf[7] as u16) << 8),
127 })
128 }
129
130 /// Gets the descriptor type and index from the value field of a GET_DESCRIPTOR request.
131 pub fn descriptor_type_index(&self) -> (u8, u8) {
132 ((self.value >> 8) as u8, self.value as u8)
133 }
134}
diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs
new file mode 100644
index 000000000..746c6b828
--- /dev/null
+++ b/embassy-usb/src/descriptor.rs
@@ -0,0 +1,387 @@
1use super::builder::Config;
2use super::{types::*, CONFIGURATION_VALUE, DEFAULT_ALTERNATE_SETTING};
3
4#[derive(Debug, PartialEq, Eq, Clone, Copy)]
5#[cfg_attr(feature = "defmt", derive(defmt::Format))]
6pub enum Error {
7 BufferFull,
8 InvalidState,
9}
10
11/// Standard descriptor types
12#[allow(missing_docs)]
13pub mod descriptor_type {
14 pub const DEVICE: u8 = 1;
15 pub const CONFIGURATION: u8 = 2;
16 pub const STRING: u8 = 3;
17 pub const INTERFACE: u8 = 4;
18 pub const ENDPOINT: u8 = 5;
19 pub const IAD: u8 = 11;
20 pub const BOS: u8 = 15;
21 pub const CAPABILITY: u8 = 16;
22}
23
24/// String descriptor language IDs.
25pub mod lang_id {
26 /// English (US)
27 ///
28 /// Recommended for use as the first language ID for compatibility.
29 pub const ENGLISH_US: u16 = 0x0409;
30}
31
32/// Standard capability descriptor types
33#[allow(missing_docs)]
34pub mod capability_type {
35 pub const WIRELESS_USB: u8 = 1;
36 pub const USB_2_0_EXTENSION: u8 = 2;
37 pub const SS_USB_DEVICE: u8 = 3;
38 pub const CONTAINER_ID: u8 = 4;
39 pub const PLATFORM: u8 = 5;
40}
41
42/// A writer for USB descriptors.
43pub struct DescriptorWriter<'a> {
44 buf: &'a mut [u8],
45 position: usize,
46 num_interfaces_mark: Option<usize>,
47 num_endpoints_mark: Option<usize>,
48 write_iads: bool,
49}
50
51impl<'a> DescriptorWriter<'a> {
52 pub(crate) fn new(buf: &'a mut [u8]) -> Self {
53 DescriptorWriter {
54 buf,
55 position: 0,
56 num_interfaces_mark: None,
57 num_endpoints_mark: None,
58 write_iads: false,
59 }
60 }
61
62 pub fn into_buf(self) -> &'a mut [u8] {
63 &mut self.buf[..self.position]
64 }
65
66 /// Gets the current position in the buffer, i.e. the number of bytes written so far.
67 pub fn position(&self) -> usize {
68 self.position
69 }
70
71 /// Writes an arbitrary (usually class-specific) descriptor.
72 pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8]) -> Result<(), Error> {
73 let length = descriptor.len();
74
75 if (self.position + 2 + length) > self.buf.len() || (length + 2) > 255 {
76 return Err(Error::BufferFull);
77 }
78
79 self.buf[self.position] = (length + 2) as u8;
80 self.buf[self.position + 1] = descriptor_type;
81
82 let start = self.position + 2;
83
84 self.buf[start..start + length].copy_from_slice(descriptor);
85
86 self.position = start + length;
87
88 Ok(())
89 }
90
91 pub(crate) fn device(&mut self, config: &Config) -> Result<(), Error> {
92 self.write(
93 descriptor_type::DEVICE,
94 &[
95 0x10,
96 0x02, // bcdUSB 2.1
97 config.device_class, // bDeviceClass
98 config.device_sub_class, // bDeviceSubClass
99 config.device_protocol, // bDeviceProtocol
100 config.max_packet_size_0, // bMaxPacketSize0
101 config.vendor_id as u8,
102 (config.vendor_id >> 8) as u8, // idVendor
103 config.product_id as u8,
104 (config.product_id >> 8) as u8, // idProduct
105 config.device_release as u8,
106 (config.device_release >> 8) as u8, // bcdDevice
107 config.manufacturer.map_or(0, |_| 1), // iManufacturer
108 config.product.map_or(0, |_| 2), // iProduct
109 config.serial_number.map_or(0, |_| 3), // iSerialNumber
110 1, // bNumConfigurations
111 ],
112 )
113 }
114
115 pub(crate) fn configuration(&mut self, config: &Config) -> Result<(), Error> {
116 self.num_interfaces_mark = Some(self.position + 4);
117
118 self.write_iads = config.composite_with_iads;
119
120 self.write(
121 descriptor_type::CONFIGURATION,
122 &[
123 0,
124 0, // wTotalLength
125 0, // bNumInterfaces
126 CONFIGURATION_VALUE, // bConfigurationValue
127 0, // iConfiguration
128 0x80 | if config.self_powered { 0x40 } else { 0x00 }
129 | if config.supports_remote_wakeup {
130 0x20
131 } else {
132 0x00
133 }, // bmAttributes
134 (config.max_power / 2) as u8, // bMaxPower
135 ],
136 )
137 }
138
139 pub(crate) fn end_class(&mut self) {
140 self.num_endpoints_mark = None;
141 }
142
143 pub(crate) fn end_configuration(&mut self) {
144 let position = self.position as u16;
145 self.buf[2..4].copy_from_slice(&position.to_le_bytes());
146 }
147
148 /// Writes a interface association descriptor. Call from `UsbClass::get_configuration_descriptors`
149 /// before writing the USB class or function's interface descriptors if your class has more than
150 /// one interface and wants to play nicely with composite devices on Windows. If the USB device
151 /// hosting the class was not configured as composite with IADs enabled, calling this function
152 /// does nothing, so it is safe to call from libraries.
153 ///
154 /// # Arguments
155 ///
156 /// * `first_interface` - Number of the function's first interface, previously allocated with
157 /// [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::interface).
158 /// * `interface_count` - Number of interfaces in the function.
159 /// * `function_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices
160 /// that do not conform to any class.
161 /// * `function_sub_class` - Sub-class code. Depends on class.
162 /// * `function_protocol` - Protocol code. Depends on class and sub-class.
163 pub fn iad(
164 &mut self,
165 first_interface: InterfaceNumber,
166 interface_count: u8,
167 function_class: u8,
168 function_sub_class: u8,
169 function_protocol: u8,
170 ) -> Result<(), Error> {
171 if !self.write_iads {
172 return Ok(());
173 }
174
175 self.write(
176 descriptor_type::IAD,
177 &[
178 first_interface.into(), // bFirstInterface
179 interface_count, // bInterfaceCount
180 function_class,
181 function_sub_class,
182 function_protocol,
183 0,
184 ],
185 )?;
186
187 Ok(())
188 }
189
190 /// Writes a interface descriptor.
191 ///
192 /// # Arguments
193 ///
194 /// * `number` - Interface number previously allocated with
195 /// [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::interface).
196 /// * `interface_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices
197 /// that do not conform to any class.
198 /// * `interface_sub_class` - Sub-class code. Depends on class.
199 /// * `interface_protocol` - Protocol code. Depends on class and sub-class.
200 pub fn interface(
201 &mut self,
202 number: InterfaceNumber,
203 interface_class: u8,
204 interface_sub_class: u8,
205 interface_protocol: u8,
206 ) -> Result<(), Error> {
207 self.interface_alt(
208 number,
209 DEFAULT_ALTERNATE_SETTING,
210 interface_class,
211 interface_sub_class,
212 interface_protocol,
213 None,
214 )
215 }
216
217 /// Writes a interface descriptor with a specific alternate setting and
218 /// interface string identifier.
219 ///
220 /// # Arguments
221 ///
222 /// * `number` - Interface number previously allocated with
223 /// [`UsbDeviceBuilder::interface`](crate::bus::UsbDeviceBuilder::interface).
224 /// * `alternate_setting` - Number of the alternate setting
225 /// * `interface_class` - Class code assigned by USB.org. Use `0xff` for vendor-specific devices
226 /// that do not conform to any class.
227 /// * `interface_sub_class` - Sub-class code. Depends on class.
228 /// * `interface_protocol` - Protocol code. Depends on class and sub-class.
229 /// * `interface_string` - Index of string descriptor describing this interface
230
231 pub fn interface_alt(
232 &mut self,
233 number: InterfaceNumber,
234 alternate_setting: u8,
235 interface_class: u8,
236 interface_sub_class: u8,
237 interface_protocol: u8,
238 interface_string: Option<StringIndex>,
239 ) -> Result<(), Error> {
240 if alternate_setting == DEFAULT_ALTERNATE_SETTING {
241 match self.num_interfaces_mark {
242 Some(mark) => self.buf[mark] += 1,
243 None => return Err(Error::InvalidState),
244 };
245 }
246
247 let str_index = interface_string.map_or(0, Into::into);
248
249 self.num_endpoints_mark = Some(self.position + 4);
250
251 self.write(
252 descriptor_type::INTERFACE,
253 &[
254 number.into(), // bInterfaceNumber
255 alternate_setting, // bAlternateSetting
256 0, // bNumEndpoints
257 interface_class, // bInterfaceClass
258 interface_sub_class, // bInterfaceSubClass
259 interface_protocol, // bInterfaceProtocol
260 str_index, // iInterface
261 ],
262 )?;
263
264 Ok(())
265 }
266
267 /// Writes an endpoint descriptor.
268 ///
269 /// # Arguments
270 ///
271 /// * `endpoint` - Endpoint previously allocated with
272 /// [`UsbDeviceBuilder`](crate::bus::UsbDeviceBuilder).
273 pub fn endpoint(&mut self, endpoint: &EndpointInfo) -> Result<(), Error> {
274 match self.num_endpoints_mark {
275 Some(mark) => self.buf[mark] += 1,
276 None => return Err(Error::InvalidState),
277 };
278
279 self.write(
280 descriptor_type::ENDPOINT,
281 &[
282 endpoint.addr.into(), // bEndpointAddress
283 endpoint.ep_type as u8, // bmAttributes
284 endpoint.max_packet_size as u8,
285 (endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize
286 endpoint.interval, // bInterval
287 ],
288 )?;
289
290 Ok(())
291 }
292
293 /// Writes a string descriptor.
294 pub(crate) fn string(&mut self, string: &str) -> Result<(), Error> {
295 let mut pos = self.position;
296
297 if pos + 2 > self.buf.len() {
298 return Err(Error::BufferFull);
299 }
300
301 self.buf[pos] = 0; // length placeholder
302 self.buf[pos + 1] = descriptor_type::STRING;
303
304 pos += 2;
305
306 for c in string.encode_utf16() {
307 if pos >= self.buf.len() {
308 return Err(Error::BufferFull);
309 }
310
311 self.buf[pos..pos + 2].copy_from_slice(&c.to_le_bytes());
312 pos += 2;
313 }
314
315 self.buf[self.position] = (pos - self.position) as u8;
316
317 self.position = pos;
318
319 Ok(())
320 }
321}
322
323/// A writer for Binary Object Store descriptor.
324pub struct BosWriter<'a> {
325 pub(crate) writer: DescriptorWriter<'a>,
326 num_caps_mark: Option<usize>,
327}
328
329impl<'a> BosWriter<'a> {
330 pub(crate) fn new(writer: DescriptorWriter<'a>) -> Self {
331 Self {
332 writer: writer,
333 num_caps_mark: None,
334 }
335 }
336
337 pub(crate) fn bos(&mut self) -> Result<(), Error> {
338 self.num_caps_mark = Some(self.writer.position + 4);
339 self.writer.write(
340 descriptor_type::BOS,
341 &[
342 0x00, 0x00, // wTotalLength
343 0x00, // bNumDeviceCaps
344 ],
345 )?;
346
347 self.capability(capability_type::USB_2_0_EXTENSION, &[0; 4])?;
348
349 Ok(())
350 }
351
352 /// Writes capability descriptor to a BOS
353 ///
354 /// # Arguments
355 ///
356 /// * `capability_type` - Type of a capability
357 /// * `data` - Binary data of the descriptor
358 pub fn capability(&mut self, capability_type: u8, data: &[u8]) -> Result<(), Error> {
359 match self.num_caps_mark {
360 Some(mark) => self.writer.buf[mark] += 1,
361 None => return Err(Error::InvalidState),
362 }
363
364 let mut start = self.writer.position;
365 let blen = data.len();
366
367 if (start + blen + 3) > self.writer.buf.len() || (blen + 3) > 255 {
368 return Err(Error::BufferFull);
369 }
370
371 self.writer.buf[start] = (blen + 3) as u8;
372 self.writer.buf[start + 1] = descriptor_type::CAPABILITY;
373 self.writer.buf[start + 2] = capability_type;
374
375 start += 3;
376 self.writer.buf[start..start + blen].copy_from_slice(data);
377 self.writer.position = start + blen;
378
379 Ok(())
380 }
381
382 pub(crate) fn end_bos(&mut self) {
383 self.num_caps_mark = None;
384 let position = self.writer.position as u16;
385 self.writer.buf[2..4].copy_from_slice(&position.to_le_bytes());
386 }
387}
diff --git a/embassy-usb/src/driver.rs b/embassy-usb/src/driver.rs
new file mode 100644
index 000000000..ed4edb576
--- /dev/null
+++ b/embassy-usb/src/driver.rs
@@ -0,0 +1,160 @@
1use core::future::Future;
2
3use super::types::*;
4
5#[derive(Copy, Clone, Eq, PartialEq, Debug)]
6#[cfg_attr(feature = "defmt", derive(defmt::Format))]
7pub struct EndpointAllocError;
8
9#[derive(Copy, Clone, Eq, PartialEq, Debug)]
10#[cfg_attr(feature = "defmt", derive(defmt::Format))]
11
12/// Operation is unsupported by the driver.
13pub struct Unsupported;
14
15#[derive(Copy, Clone, Eq, PartialEq, Debug)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17
18/// Errors returned by [`EndpointIn::write`]
19pub enum WriteError {
20 /// The packet is too long to fit in the
21 /// transmission buffer. This is generally an error in the class implementation, because the
22 /// class shouldn't provide more data than the `max_packet_size` it specified when allocating
23 /// the endpoint.
24 BufferOverflow,
25}
26
27#[derive(Copy, Clone, Eq, PartialEq, Debug)]
28#[cfg_attr(feature = "defmt", derive(defmt::Format))]
29
30/// Errors returned by [`EndpointOut::read`]
31pub enum ReadError {
32 /// The received packet is too long to
33 /// fit in `buf`. This is generally an error in the class implementation, because the class
34 /// should use a buffer that is large enough for the `max_packet_size` it specified when
35 /// allocating the endpoint.
36 BufferOverflow,
37}
38
39/// Driver for a specific USB peripheral. Implement this to add support for a new hardware
40/// platform.
41pub trait Driver<'a> {
42 type EndpointOut: EndpointOut + 'a;
43 type EndpointIn: EndpointIn + 'a;
44 type Bus: Bus + 'a;
45
46 /// Allocates an endpoint and specified endpoint parameters. This method is called by the device
47 /// and class implementations to allocate endpoints, and can only be called before
48 /// [`enable`](UsbBus::enable) is called.
49 ///
50 /// # Arguments
51 ///
52 /// * `ep_addr` - A static endpoint address to allocate. If Some, the implementation should
53 /// attempt to return an endpoint with the specified address. If None, the implementation
54 /// should return the next available one.
55 /// * `max_packet_size` - Maximum packet size in bytes.
56 /// * `interval` - Polling interval parameter for interrupt endpoints.
57 fn alloc_endpoint_out(
58 &mut self,
59 ep_addr: Option<EndpointAddress>,
60 ep_type: EndpointType,
61 max_packet_size: u16,
62 interval: u8,
63 ) -> Result<Self::EndpointOut, EndpointAllocError>;
64
65 fn alloc_endpoint_in(
66 &mut self,
67 ep_addr: Option<EndpointAddress>,
68 ep_type: EndpointType,
69 max_packet_size: u16,
70 interval: u8,
71 ) -> Result<Self::EndpointIn, EndpointAllocError>;
72
73 /// Enables and initializes the USB peripheral. Soon after enabling the device will be reset, so
74 /// there is no need to perform a USB reset in this method.
75 fn enable(self) -> Self::Bus;
76
77 /// Indicates that `set_device_address` must be called before accepting the corresponding
78 /// control transfer, not after.
79 ///
80 /// The default value for this constant is `false`, which corresponds to the USB 2.0 spec, 9.4.6
81 const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = false;
82}
83
84pub trait Bus {
85 /// Called when the host resets the device. This will be soon called after
86 /// [`poll`](crate::device::UsbDevice::poll) returns [`PollResult::Reset`]. This method should
87 /// reset the state of all endpoints and peripheral flags back to a state suitable for
88 /// enumeration, as well as ensure that all endpoints previously allocated with alloc_ep are
89 /// initialized as specified.
90 fn reset(&mut self);
91
92 /// Sets the device USB address to `addr`.
93 fn set_device_address(&mut self, addr: u8);
94
95 /// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it
96 /// should be prepared to receive data again. Only used during control transfers.
97 fn set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool);
98
99 /// Gets whether the STALL condition is set for an endpoint. Only used during control transfers.
100 fn is_stalled(&mut self, ep_addr: EndpointAddress) -> bool;
101
102 /// Causes the USB peripheral to enter USB suspend mode, lowering power consumption and
103 /// preparing to detect a USB wakeup event. This will be called after
104 /// [`poll`](crate::device::UsbDevice::poll) returns [`PollResult::Suspend`]. The device will
105 /// continue be polled, and it shall return a value other than `Suspend` from `poll` when it no
106 /// longer detects the suspend condition.
107 fn suspend(&mut self);
108
109 /// Resumes from suspend mode. This may only be called after the peripheral has been previously
110 /// suspended.
111 fn resume(&mut self);
112
113 /// Simulates a disconnect from the USB bus, causing the host to reset and re-enumerate the
114 /// device.
115 ///
116 /// The default implementation just returns `Unsupported`.
117 ///
118 /// # Errors
119 ///
120 /// * [`Unsupported`](crate::UsbError::Unsupported) - This UsbBus implementation doesn't support
121 /// simulating a disconnect or it has not been enabled at creation time.
122 fn force_reset(&mut self) -> Result<(), Unsupported> {
123 Err(Unsupported)
124 }
125}
126
127pub trait Endpoint {
128 /// Get the endpoint address
129 fn info(&self) -> &EndpointInfo;
130
131 /// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it
132 /// should be prepared to receive data again.
133 fn set_stalled(&self, stalled: bool);
134
135 /// Gets whether the STALL condition is set for an endpoint.
136 fn is_stalled(&self) -> bool;
137
138 // TODO enable/disable?
139}
140
141pub trait EndpointOut: Endpoint {
142 type ReadFuture<'a>: Future<Output = Result<usize, ReadError>> + 'a
143 where
144 Self: 'a;
145
146 /// Reads a single packet of data from the endpoint, and returns the actual length of
147 /// the packet.
148 ///
149 /// This should also clear any NAK flags and prepare the endpoint to receive the next packet.
150 fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a>;
151}
152
153pub trait EndpointIn: Endpoint {
154 type WriteFuture<'a>: Future<Output = Result<(), WriteError>> + 'a
155 where
156 Self: 'a;
157
158 /// Writes a single packet of data to the endpoint.
159 fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a>;
160}
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..397db96c4
--- /dev/null
+++ b/embassy-usb/src/lib.rs
@@ -0,0 +1,342 @@
1#![no_std]
2#![feature(generic_associated_types)]
3
4// This mod MUST go first, so that the others see its macros.
5pub(crate) mod fmt;
6
7mod builder;
8mod control;
9pub mod descriptor;
10pub mod driver;
11pub mod types;
12
13use self::control::*;
14use self::descriptor::*;
15use self::driver::*;
16use self::types::*;
17
18pub use self::builder::Config;
19pub use self::builder::UsbDeviceBuilder;
20
21/// The global state of the USB device.
22///
23/// In general class traffic is only possible in the `Configured` state.
24#[repr(u8)]
25#[derive(PartialEq, Eq, Copy, Clone, Debug)]
26pub enum UsbDeviceState {
27 /// The USB device has just been created or reset.
28 Default,
29
30 /// The USB device has received an address from the host.
31 Addressed,
32
33 /// The USB device has been configured and is fully functional.
34 Configured,
35
36 /// The USB device has been suspended by the host or it has been unplugged from the USB bus.
37 Suspend,
38}
39
40/// The bConfiguration value for the not configured state.
41pub const CONFIGURATION_NONE: u8 = 0;
42
43/// The bConfiguration value for the single configuration supported by this device.
44pub const CONFIGURATION_VALUE: u8 = 1;
45
46/// The default value for bAlternateSetting for all interfaces.
47pub const DEFAULT_ALTERNATE_SETTING: u8 = 0;
48
49pub struct UsbDevice<'d, D: Driver<'d>> {
50 driver: D::Bus,
51 control_in: D::EndpointIn,
52 control_out: D::EndpointOut,
53
54 config: Config<'d>,
55 device_descriptor: &'d [u8],
56 config_descriptor: &'d [u8],
57 bos_descriptor: &'d [u8],
58
59 device_state: UsbDeviceState,
60 remote_wakeup_enabled: bool,
61 self_powered: bool,
62 pending_address: u8,
63}
64
65impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
66 pub(crate) fn build(
67 mut driver: D,
68 config: Config<'d>,
69 device_descriptor: &'d [u8],
70 config_descriptor: &'d [u8],
71 bos_descriptor: &'d [u8],
72 ) -> Self {
73 let control_out = driver
74 .alloc_endpoint_out(
75 Some(0x00.into()),
76 EndpointType::Control,
77 config.max_packet_size_0 as u16,
78 0,
79 )
80 .expect("failed to alloc control endpoint");
81
82 let control_in = driver
83 .alloc_endpoint_in(
84 Some(0x80.into()),
85 EndpointType::Control,
86 config.max_packet_size_0 as u16,
87 0,
88 )
89 .expect("failed to alloc control endpoint");
90
91 // Enable the USB bus.
92 // This prevent further allocation by consuming the driver.
93 let driver = driver.enable();
94
95 Self {
96 driver,
97 config,
98 control_in,
99 control_out,
100 device_descriptor,
101 config_descriptor,
102 bos_descriptor,
103 device_state: UsbDeviceState::Default,
104 remote_wakeup_enabled: false,
105 self_powered: false,
106 pending_address: 0,
107 }
108 }
109
110 pub async fn run(&mut self) {
111 loop {
112 let mut buf = [0; 8];
113 let n = self.control_out.read(&mut buf).await.unwrap();
114 assert_eq!(n, 8);
115 let req = Request::parse(&buf).unwrap();
116 info!("setup request: {:x}", req);
117
118 // Now that we have properly parsed the setup packet, ensure the end-point is no longer in
119 // a stalled state.
120 self.control_out.set_stalled(false);
121
122 match req.direction {
123 UsbDirection::In => self.handle_control_in(req).await,
124 UsbDirection::Out => self.handle_control_out(req).await,
125 }
126 }
127 }
128
129 async fn write_chunked(&mut self, data: &[u8]) -> Result<(), driver::WriteError> {
130 for c in data.chunks(8) {
131 self.control_in.write(c).await?;
132 }
133 if data.len() % 8 == 0 {
134 self.control_in.write(&[]).await?;
135 }
136 Ok(())
137 }
138
139 async fn control_out_accept(&mut self, req: Request) {
140 info!("control out accept");
141 // status phase
142 // todo: cleanup
143 self.control_out.read(&mut []).await.unwrap();
144 }
145
146 async fn control_in_accept(&mut self, req: Request, data: &[u8]) {
147 info!("control accept {:x}", data);
148
149 let len = data.len().min(req.length as _);
150 if let Err(e) = self.write_chunked(&data[..len]).await {
151 info!("write_chunked failed: {:?}", e);
152 }
153
154 // status phase
155 // todo: cleanup
156 self.control_out.read(&mut []).await.unwrap();
157 }
158
159 async fn control_in_accept_writer(
160 &mut self,
161 req: Request,
162 f: impl FnOnce(&mut DescriptorWriter),
163 ) {
164 let mut buf = [0; 256];
165 let mut w = DescriptorWriter::new(&mut buf);
166 f(&mut w);
167 let pos = w.position();
168 self.control_in_accept(req, &buf[..pos]).await;
169 }
170
171 fn control_reject(&mut self, req: Request) {
172 self.control_out.set_stalled(true);
173 }
174
175 async fn handle_control_out(&mut self, req: Request) {
176 // TODO actually read the data if there's an OUT data phase.
177
178 const CONFIGURATION_NONE_U16: u16 = CONFIGURATION_NONE as u16;
179 const CONFIGURATION_VALUE_U16: u16 = CONFIGURATION_VALUE as u16;
180 const DEFAULT_ALTERNATE_SETTING_U16: u16 = DEFAULT_ALTERNATE_SETTING as u16;
181
182 match req.request_type {
183 RequestType::Standard => match (req.recipient, req.request, req.value) {
184 (
185 Recipient::Device,
186 Request::CLEAR_FEATURE,
187 Request::FEATURE_DEVICE_REMOTE_WAKEUP,
188 ) => {
189 self.remote_wakeup_enabled = false;
190 self.control_out_accept(req).await;
191 }
192
193 (Recipient::Endpoint, Request::CLEAR_FEATURE, Request::FEATURE_ENDPOINT_HALT) => {
194 //self.bus.set_stalled(((req.index as u8) & 0x8f).into(), false);
195 self.control_out_accept(req).await;
196 }
197
198 (
199 Recipient::Device,
200 Request::SET_FEATURE,
201 Request::FEATURE_DEVICE_REMOTE_WAKEUP,
202 ) => {
203 self.remote_wakeup_enabled = true;
204 self.control_out_accept(req).await;
205 }
206
207 (Recipient::Endpoint, Request::SET_FEATURE, Request::FEATURE_ENDPOINT_HALT) => {
208 //self.bus.set_stalled(((req.index as u8) & 0x8f).into(), true);
209 self.control_out_accept(req).await;
210 }
211
212 (Recipient::Device, Request::SET_ADDRESS, 1..=127) => {
213 self.pending_address = req.value as u8;
214
215 // on NRF the hardware auto-handles SET_ADDRESS.
216 self.control_out_accept(req).await;
217 }
218
219 (Recipient::Device, Request::SET_CONFIGURATION, CONFIGURATION_VALUE_U16) => {
220 self.device_state = UsbDeviceState::Configured;
221 self.control_out_accept(req).await;
222 }
223
224 (Recipient::Device, Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => {
225 match self.device_state {
226 UsbDeviceState::Default => {
227 self.control_out_accept(req).await;
228 }
229 _ => {
230 self.device_state = UsbDeviceState::Addressed;
231 self.control_out_accept(req).await;
232 }
233 }
234 }
235
236 (Recipient::Interface, Request::SET_INTERFACE, DEFAULT_ALTERNATE_SETTING_U16) => {
237 // TODO: do something when alternate settings are implemented
238 self.control_out_accept(req).await;
239 }
240
241 _ => self.control_reject(req),
242 },
243 _ => self.control_reject(req),
244 }
245 }
246
247 async fn handle_control_in(&mut self, req: Request) {
248 match req.request_type {
249 RequestType::Standard => match (req.recipient, req.request) {
250 (Recipient::Device, Request::GET_STATUS) => {
251 let mut status: u16 = 0x0000;
252 if self.self_powered {
253 status |= 0x0001;
254 }
255 if self.remote_wakeup_enabled {
256 status |= 0x0002;
257 }
258 self.control_in_accept(req, &status.to_le_bytes()).await;
259 }
260
261 (Recipient::Interface, Request::GET_STATUS) => {
262 let status: u16 = 0x0000;
263 self.control_in_accept(req, &status.to_le_bytes()).await;
264 }
265
266 (Recipient::Endpoint, Request::GET_STATUS) => {
267 let ep_addr: EndpointAddress = ((req.index as u8) & 0x8f).into();
268 let mut status: u16 = 0x0000;
269 if self.driver.is_stalled(ep_addr) {
270 status |= 0x0001;
271 }
272 self.control_in_accept(req, &status.to_le_bytes()).await;
273 }
274
275 (Recipient::Device, Request::GET_DESCRIPTOR) => {
276 self.handle_get_descriptor(req).await;
277 }
278
279 (Recipient::Device, Request::GET_CONFIGURATION) => {
280 let status = match self.device_state {
281 UsbDeviceState::Configured => CONFIGURATION_VALUE,
282 _ => CONFIGURATION_NONE,
283 };
284 self.control_in_accept(req, &status.to_le_bytes()).await;
285 }
286
287 (Recipient::Interface, Request::GET_INTERFACE) => {
288 // TODO: change when alternate settings are implemented
289 let status = DEFAULT_ALTERNATE_SETTING;
290 self.control_in_accept(req, &status.to_le_bytes()).await;
291 }
292 _ => self.control_reject(req),
293 },
294 _ => self.control_reject(req),
295 }
296 }
297
298 async fn handle_get_descriptor(&mut self, req: Request) {
299 let (dtype, index) = req.descriptor_type_index();
300 let config = self.config.clone();
301
302 match dtype {
303 descriptor_type::BOS => self.control_in_accept(req, self.bos_descriptor).await,
304 descriptor_type::DEVICE => self.control_in_accept(req, self.device_descriptor).await,
305 descriptor_type::CONFIGURATION => {
306 self.control_in_accept(req, self.config_descriptor).await
307 }
308 descriptor_type::STRING => {
309 if index == 0 {
310 self.control_in_accept_writer(req, |w| {
311 w.write(descriptor_type::STRING, &lang_id::ENGLISH_US.to_le_bytes())
312 .unwrap();
313 })
314 .await
315 } else {
316 let s = match index {
317 1 => self.config.manufacturer,
318 2 => self.config.product,
319 3 => self.config.serial_number,
320 _ => {
321 let index = StringIndex::new(index);
322 let lang_id = req.index;
323 None
324 //classes
325 // .iter()
326 // .filter_map(|cls| cls.get_string(index, lang_id))
327 // .nth(0)
328 }
329 };
330
331 if let Some(s) = s {
332 self.control_in_accept_writer(req, |w| w.string(s).unwrap())
333 .await;
334 } else {
335 self.control_reject(req)
336 }
337 }
338 }
339 _ => self.control_reject(req),
340 }
341 }
342}
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/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml
index a704eb3bc..fb846b3a9 100644
--- a/examples/nrf/Cargo.toml
+++ b/examples/nrf/Cargo.toml
@@ -10,7 +10,8 @@ nightly = ["embassy-nrf/nightly", "embassy-nrf/unstable-traits"]
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"] }
14 15
15defmt = "0.3" 16defmt = "0.3"
16defmt-rtt = "0.3" 17defmt-rtt = "0.3"
@@ -22,5 +23,3 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa
22rand = { version = "0.8.4", default-features = false } 23rand = { version = "0.8.4", default-features = false }
23embedded-storage = "0.3.0" 24embedded-storage = "0.3.0"
24 25
25usb-device = "0.2"
26usbd-serial = "0.1.1"
diff --git a/examples/nrf/src/bin/usb/cdc_acm.rs b/examples/nrf/src/bin/usb/cdc_acm.rs
new file mode 100644
index 000000000..345d00389
--- /dev/null
+++ b/examples/nrf/src/bin/usb/cdc_acm.rs
@@ -0,0 +1,356 @@
1use core::convert::TryInto;
2use core::mem;
3use embassy_usb::driver::{Endpoint, EndpointIn, EndpointOut, ReadError, WriteError};
4use embassy_usb::{driver::Driver, types::*, UsbDeviceBuilder};
5
6/// This should be used as `device_class` when building the `UsbDevice`.
7pub const USB_CLASS_CDC: u8 = 0x02;
8
9const USB_CLASS_CDC_DATA: u8 = 0x0a;
10const CDC_SUBCLASS_ACM: u8 = 0x02;
11const CDC_PROTOCOL_NONE: u8 = 0x00;
12
13const CS_INTERFACE: u8 = 0x24;
14const CDC_TYPE_HEADER: u8 = 0x00;
15const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01;
16const CDC_TYPE_ACM: u8 = 0x02;
17const CDC_TYPE_UNION: u8 = 0x06;
18
19const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00;
20#[allow(unused)]
21const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01;
22const REQ_SET_LINE_CODING: u8 = 0x20;
23const REQ_GET_LINE_CODING: u8 = 0x21;
24const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22;
25
26/// Packet level implementation of a CDC-ACM serial port.
27///
28/// This class can be used directly and it has the least overhead due to directly reading and
29/// writing USB packets with no intermediate buffers, but it will not act like a stream-like serial
30/// port. The following constraints must be followed if you use this class directly:
31///
32/// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes, and the
33/// method will return a `WouldBlock` error if there is no packet to be read.
34/// - `write_packet` must not be called with a buffer larger than max_packet_size bytes, and the
35/// method will return a `WouldBlock` error if the previous packet has not been sent yet.
36/// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the
37/// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP)
38/// can be sent if there is no other data to send. This is because USB bulk transactions must be
39/// terminated with a short packet, even if the bulk endpoint is used for stream-like data.
40pub struct CdcAcmClass<'d, D: Driver<'d>> {
41 comm_if: InterfaceNumber,
42 comm_ep: D::EndpointIn,
43 data_if: InterfaceNumber,
44 read_ep: D::EndpointOut,
45 write_ep: D::EndpointIn,
46 line_coding: LineCoding,
47 dtr: bool,
48 rts: bool,
49}
50
51impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
52 /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For
53 /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64.
54 pub fn new(builder: &mut UsbDeviceBuilder<'d, D>, max_packet_size: u16) -> Self {
55 let comm_if = builder.alloc_interface();
56 let comm_ep = builder.alloc_interrupt_endpoint_in(8, 255);
57 let data_if = builder.alloc_interface();
58 let read_ep = builder.alloc_bulk_endpoint_out(max_packet_size);
59 let write_ep = builder.alloc_bulk_endpoint_in(max_packet_size);
60
61 builder
62 .config_descriptor
63 .iad(
64 comm_if,
65 2,
66 USB_CLASS_CDC,
67 CDC_SUBCLASS_ACM,
68 CDC_PROTOCOL_NONE,
69 )
70 .unwrap();
71
72 builder
73 .config_descriptor
74 .interface(comm_if, USB_CLASS_CDC, CDC_SUBCLASS_ACM, CDC_PROTOCOL_NONE)
75 .unwrap();
76
77 builder
78 .config_descriptor
79 .write(
80 CS_INTERFACE,
81 &[
82 CDC_TYPE_HEADER, // bDescriptorSubtype
83 0x10,
84 0x01, // bcdCDC (1.10)
85 ],
86 )
87 .unwrap();
88
89 builder
90 .config_descriptor
91 .write(
92 CS_INTERFACE,
93 &[
94 CDC_TYPE_ACM, // bDescriptorSubtype
95 0x00, // bmCapabilities
96 ],
97 )
98 .unwrap();
99
100 builder
101 .config_descriptor
102 .write(
103 CS_INTERFACE,
104 &[
105 CDC_TYPE_UNION, // bDescriptorSubtype
106 comm_if.into(), // bControlInterface
107 data_if.into(), // bSubordinateInterface
108 ],
109 )
110 .unwrap();
111
112 builder
113 .config_descriptor
114 .write(
115 CS_INTERFACE,
116 &[
117 CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype
118 0x00, // bmCapabilities
119 data_if.into(), // bDataInterface
120 ],
121 )
122 .unwrap();
123
124 builder.config_descriptor.endpoint(comm_ep.info()).unwrap();
125
126 builder
127 .config_descriptor
128 .interface(data_if, USB_CLASS_CDC_DATA, 0x00, 0x00)
129 .unwrap();
130
131 builder.config_descriptor.endpoint(write_ep.info()).unwrap();
132 builder.config_descriptor.endpoint(read_ep.info()).unwrap();
133
134 CdcAcmClass {
135 comm_if,
136 comm_ep,
137 data_if,
138 read_ep,
139 write_ep,
140 line_coding: LineCoding {
141 stop_bits: StopBits::One,
142 data_bits: 8,
143 parity_type: ParityType::None,
144 data_rate: 8_000,
145 },
146 dtr: false,
147 rts: false,
148 }
149 }
150
151 /// Gets the maximum packet size in bytes.
152 pub fn max_packet_size(&self) -> u16 {
153 // The size is the same for both endpoints.
154 self.read_ep.info().max_packet_size
155 }
156
157 /// Gets the current line coding. The line coding contains information that's mainly relevant
158 /// for USB to UART serial port emulators, and can be ignored if not relevant.
159 pub fn line_coding(&self) -> &LineCoding {
160 &self.line_coding
161 }
162
163 /// Gets the DTR (data terminal ready) state
164 pub fn dtr(&self) -> bool {
165 self.dtr
166 }
167
168 /// Gets the RTS (request to send) state
169 pub fn rts(&self) -> bool {
170 self.rts
171 }
172
173 /// Writes a single packet into the IN endpoint.
174 pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), WriteError> {
175 self.write_ep.write(data).await
176 }
177
178 /// Reads a single packet from the OUT endpoint.
179 pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, ReadError> {
180 self.read_ep.read(data).await
181 }
182
183 /// Gets the address of the IN endpoint.
184 pub(crate) fn write_ep_address(&self) -> EndpointAddress {
185 self.write_ep.info().addr
186 }
187}
188
189/*
190impl<B: UsbBus> UsbClass<B> for CdcAcmClass<'_, B> {
191 fn get_configuration_descriptors(&self, builder.config_descriptor: &mut Descriptorbuilder.config_descriptor) -> Result<()> {
192
193 Ok(())
194 }
195
196 fn reset(&mut self) {
197 self.line_coding = LineCoding::default();
198 self.dtr = false;
199 self.rts = false;
200 }
201
202 fn control_in(&mut self, xfer: ControlIn<B>) {
203 let req = xfer.request();
204
205 if !(req.request_type == control::RequestType::Class
206 && req.recipient == control::Recipient::Interface
207 && req.index == u8::from(self.comm_if) as u16)
208 {
209 return;
210 }
211
212 match req.request {
213 // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
214 REQ_GET_LINE_CODING if req.length == 7 => {
215 xfer.accept(|data| {
216 data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes());
217 data[4] = self.line_coding.stop_bits as u8;
218 data[5] = self.line_coding.parity_type as u8;
219 data[6] = self.line_coding.data_bits;
220
221 Ok(7)
222 })
223 .ok();
224 }
225 _ => {
226 xfer.reject().ok();
227 }
228 }
229 }
230
231 fn control_out(&mut self, xfer: ControlOut<B>) {
232 let req = xfer.request();
233
234 if !(req.request_type == control::RequestType::Class
235 && req.recipient == control::Recipient::Interface
236 && req.index == u8::from(self.comm_if) as u16)
237 {
238 return;
239 }
240
241 match req.request {
242 REQ_SEND_ENCAPSULATED_COMMAND => {
243 // We don't actually support encapsulated commands but pretend we do for standards
244 // compatibility.
245 xfer.accept().ok();
246 }
247 REQ_SET_LINE_CODING if xfer.data().len() >= 7 => {
248 self.line_coding.data_rate =
249 u32::from_le_bytes(xfer.data()[0..4].try_into().unwrap());
250 self.line_coding.stop_bits = xfer.data()[4].into();
251 self.line_coding.parity_type = xfer.data()[5].into();
252 self.line_coding.data_bits = xfer.data()[6];
253
254 xfer.accept().ok();
255 }
256 REQ_SET_CONTROL_LINE_STATE => {
257 self.dtr = (req.value & 0x0001) != 0;
258 self.rts = (req.value & 0x0002) != 0;
259
260 xfer.accept().ok();
261 }
262 _ => {
263 xfer.reject().ok();
264 }
265 };
266 }
267}
268
269 */
270
271/// Number of stop bits for LineCoding
272#[derive(Copy, Clone, PartialEq, Eq)]
273pub enum StopBits {
274 /// 1 stop bit
275 One = 0,
276
277 /// 1.5 stop bits
278 OnePointFive = 1,
279
280 /// 2 stop bits
281 Two = 2,
282}
283
284impl From<u8> for StopBits {
285 fn from(value: u8) -> Self {
286 if value <= 2 {
287 unsafe { mem::transmute(value) }
288 } else {
289 StopBits::One
290 }
291 }
292}
293
294/// Parity for LineCoding
295#[derive(Copy, Clone, PartialEq, Eq)]
296pub enum ParityType {
297 None = 0,
298 Odd = 1,
299 Event = 2,
300 Mark = 3,
301 Space = 4,
302}
303
304impl From<u8> for ParityType {
305 fn from(value: u8) -> Self {
306 if value <= 4 {
307 unsafe { mem::transmute(value) }
308 } else {
309 ParityType::None
310 }
311 }
312}
313
314/// Line coding parameters
315///
316/// This is provided by the host for specifying the standard UART parameters such as baud rate. Can
317/// be ignored if you don't plan to interface with a physical UART.
318pub struct LineCoding {
319 stop_bits: StopBits,
320 data_bits: u8,
321 parity_type: ParityType,
322 data_rate: u32,
323}
324
325impl LineCoding {
326 /// Gets the number of stop bits for UART communication.
327 pub fn stop_bits(&self) -> StopBits {
328 self.stop_bits
329 }
330
331 /// Gets the number of data bits for UART communication.
332 pub fn data_bits(&self) -> u8 {
333 self.data_bits
334 }
335
336 /// Gets the parity type for UART communication.
337 pub fn parity_type(&self) -> ParityType {
338 self.parity_type
339 }
340
341 /// Gets the data rate in bits per second for UART communication.
342 pub fn data_rate(&self) -> u32 {
343 self.data_rate
344 }
345}
346
347impl Default for LineCoding {
348 fn default() -> Self {
349 LineCoding {
350 stop_bits: StopBits::One,
351 data_bits: 8,
352 parity_type: ParityType::None,
353 data_rate: 8_000,
354 }
355 }
356}
diff --git a/examples/nrf/src/bin/usb/main.rs b/examples/nrf/src/bin/usb/main.rs
new file mode 100644
index 000000000..21ca2ba4f
--- /dev/null
+++ b/examples/nrf/src/bin/usb/main.rs
@@ -0,0 +1,53 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5#[path = "../../example_common.rs"]
6mod example_common;
7
8mod cdc_acm;
9
10use core::mem;
11use defmt::*;
12use embassy::executor::Spawner;
13use embassy_nrf::interrupt;
14use embassy_nrf::pac;
15use embassy_nrf::usb::{self, Driver};
16use embassy_nrf::Peripherals;
17use embassy_usb::{Config, UsbDeviceBuilder};
18
19use crate::cdc_acm::CdcAcmClass;
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 let irq = interrupt::take!(USBD);
35 let driver = Driver::new(p.USBD, irq);
36 let config = Config::new(0xc0de, 0xcafe);
37 let mut device_descriptor = [0; 256];
38 let mut config_descriptor = [0; 256];
39 let mut bos_descriptor = [0; 256];
40
41 let mut builder = UsbDeviceBuilder::new(
42 driver,
43 config,
44 &mut device_descriptor,
45 &mut config_descriptor,
46 &mut bos_descriptor,
47 );
48
49 let mut class = CdcAcmClass::new(&mut builder, 64);
50
51 let mut usb = builder.build();
52 usb.run().await;
53}
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}