aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-nrf/src/usb.rs180
-rw-r--r--embassy-usb-serial/src/lib.rs5
-rw-r--r--embassy-usb/src/driver.rs12
-rw-r--r--embassy-usb/src/lib.rs7
-rw-r--r--examples/nrf/src/bin/usb_serial.rs46
5 files changed, 190 insertions, 60 deletions
diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs
index 124316a29..c16e1be00 100644
--- a/embassy-nrf/src/usb.rs
+++ b/embassy-nrf/src/usb.rs
@@ -17,6 +17,7 @@ use futures::future::poll_fn;
17use futures::Future; 17use futures::Future;
18 18
19pub use embassy_usb; 19pub use embassy_usb;
20use pac::usbd::RegisterBlock;
20 21
21use crate::interrupt::Interrupt; 22use crate::interrupt::Interrupt;
22use crate::pac; 23use crate::pac;
@@ -92,11 +93,11 @@ impl<'d, T: Instance> Driver<'d, T> {
92 regs.epdatastatus.write(|w| unsafe { w.bits(r) }); 93 regs.epdatastatus.write(|w| unsafe { w.bits(r) });
93 READY_ENDPOINTS.fetch_or(r, Ordering::AcqRel); 94 READY_ENDPOINTS.fetch_or(r, Ordering::AcqRel);
94 for i in 1..=7 { 95 for i in 1..=7 {
95 if r & (1 << i) != 0 { 96 if r & In::mask(i) != 0 {
96 EP_IN_WAKERS[i - 1].wake(); 97 In::waker(i).wake();
97 } 98 }
98 if r & (1 << (i + 16)) != 0 { 99 if r & Out::mask(i) != 0 {
99 EP_OUT_WAKERS[i - 1].wake(); 100 Out::waker(i).wake();
100 } 101 }
101 } 102 }
102 } 103 }
@@ -272,32 +273,48 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
272 273
273 #[inline] 274 #[inline]
274 fn reset(&mut self) { 275 fn reset(&mut self) {
275 let regs = T::regs(); 276 self.set_configured(false);
277 }
276 278
277 // TODO: Initialize ISO buffers 279 #[inline]
280 fn set_configured(&mut self, configured: bool) {
281 let regs = T::regs();
278 282
279 // XXX this is not spec compliant; the endpoints should only be enabled after the device
280 // has been put in the Configured state. However, usb-device provides no hook to do that
281 unsafe { 283 unsafe {
282 regs.epinen.write(|w| w.bits(self.alloc_in.used.into())); 284 if configured {
283 regs.epouten.write(|w| w.bits(self.alloc_out.used.into())); 285 // TODO: Initialize ISO buffers
284 }
285 286
286 for i in 1..8 { 287 regs.epinen.write(|w| w.bits(self.alloc_in.used.into()));
287 let out_enabled = self.alloc_out.used & (1 << i) != 0; 288 regs.epouten.write(|w| w.bits(self.alloc_out.used.into()));
289
290 for i in 1..8 {
291 let out_enabled = self.alloc_out.used & (1 << i) != 0;
292
293 // when first enabled, bulk/interrupt OUT endpoints will *not* receive data (the
294 // peripheral will NAK all incoming packets) until we write a zero to the SIZE
295 // register (see figure 203 of the 52840 manual). To avoid that we write a 0 to the
296 // SIZE register
297 if out_enabled {
298 regs.size.epout[i].reset();
299 }
300 }
288 301
289 // when first enabled, bulk/interrupt OUT endpoints will *not* receive data (the 302 // IN endpoints (low bits) default to ready.
290 // peripheral will NAK all incoming packets) until we write a zero to the SIZE 303 // OUT endpoints (high bits) default to NOT ready, they become ready when data comes in.
291 // register (see figure 203 of the 52840 manual). To avoid that we write a 0 to the 304 READY_ENDPOINTS.store(0x0000FFFF, Ordering::Release);
292 // SIZE register 305 } else {
293 if out_enabled { 306 // Disable all endpoints except EP0
294 regs.size.epout[i].reset(); 307 regs.epinen.write(|w| w.bits(0x01));
308 regs.epouten.write(|w| w.bits(0x01));
309
310 READY_ENDPOINTS.store(In::mask(0), Ordering::Release);
295 } 311 }
296 } 312 }
297 313
298 // IN endpoints (low bits) default to ready. 314 for i in 1..=7 {
299 // OUT endpoints (high bits) default to NOT ready, they become ready when data comes in. 315 In::waker(i).wake();
300 READY_ENDPOINTS.store(0x0000FFFF, Ordering::Release); 316 Out::waker(i).wake();
317 }
301 } 318 }
302 319
303 #[inline] 320 #[inline]
@@ -332,6 +349,46 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
332pub enum Out {} 349pub enum Out {}
333pub enum In {} 350pub enum In {}
334 351
352trait EndpointDir {
353 fn waker(i: usize) -> &'static AtomicWaker;
354 fn mask(i: usize) -> u32;
355 fn is_enabled(regs: &RegisterBlock, i: usize) -> bool;
356}
357
358impl EndpointDir for In {
359 #[inline]
360 fn waker(i: usize) -> &'static AtomicWaker {
361 &EP_IN_WAKERS[i - 1]
362 }
363
364 #[inline]
365 fn mask(i: usize) -> u32 {
366 1 << i
367 }
368
369 #[inline]
370 fn is_enabled(regs: &RegisterBlock, i: usize) -> bool {
371 (regs.epinen.read().bits() & (1 << i)) != 0
372 }
373}
374
375impl EndpointDir for Out {
376 #[inline]
377 fn waker(i: usize) -> &'static AtomicWaker {
378 &EP_OUT_WAKERS[i - 1]
379 }
380
381 #[inline]
382 fn mask(i: usize) -> u32 {
383 1 << (i + 16)
384 }
385
386 #[inline]
387 fn is_enabled(regs: &RegisterBlock, i: usize) -> bool {
388 (regs.epouten.read().bits() & (1 << i)) != 0
389 }
390}
391
335pub struct Endpoint<'d, T: Instance, Dir> { 392pub struct Endpoint<'d, T: Instance, Dir> {
336 _phantom: PhantomData<(&'d mut T, Dir)>, 393 _phantom: PhantomData<(&'d mut T, Dir)>,
337 info: EndpointInfo, 394 info: EndpointInfo,
@@ -346,7 +403,7 @@ impl<'d, T: Instance, Dir> Endpoint<'d, T, Dir> {
346 } 403 }
347} 404}
348 405
349impl<'d, T: Instance, Dir> driver::Endpoint for Endpoint<'d, T, Dir> { 406impl<'d, T: Instance, Dir: EndpointDir> driver::Endpoint for Endpoint<'d, T, Dir> {
350 fn info(&self) -> &EndpointInfo { 407 fn info(&self) -> &EndpointInfo {
351 &self.info 408 &self.info
352 } 409 }
@@ -358,6 +415,49 @@ impl<'d, T: Instance, Dir> driver::Endpoint for Endpoint<'d, T, Dir> {
358 fn is_stalled(&self) -> bool { 415 fn is_stalled(&self) -> bool {
359 Driver::<T>::is_stalled(self.info.addr) 416 Driver::<T>::is_stalled(self.info.addr)
360 } 417 }
418
419 type WaitEnabledFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
420
421 fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> {
422 let i = self.info.addr.index();
423 assert!(i != 0);
424
425 poll_fn(move |cx| {
426 Dir::waker(i).register(cx.waker());
427 if Dir::is_enabled(T::regs(), i) {
428 Poll::Ready(())
429 } else {
430 Poll::Pending
431 }
432 })
433 }
434}
435
436impl<'d, T: Instance, Dir> Endpoint<'d, T, Dir> {
437 async fn wait_data_ready(&mut self) -> Result<(), ()>
438 where
439 Dir: EndpointDir,
440 {
441 let i = self.info.addr.index();
442 assert!(i != 0);
443 poll_fn(|cx| {
444 Dir::waker(i).register(cx.waker());
445 let r = READY_ENDPOINTS.load(Ordering::Acquire);
446 if !Dir::is_enabled(T::regs(), i) {
447 Poll::Ready(Err(()))
448 } else if r & Dir::mask(i) != 0 {
449 Poll::Ready(Ok(()))
450 } else {
451 Poll::Pending
452 }
453 })
454 .await?;
455
456 // Mark as not ready
457 READY_ENDPOINTS.fetch_and(!Dir::mask(i), Ordering::AcqRel);
458
459 Ok(())
460 }
361} 461}
362 462
363unsafe fn read_dma<T: Instance>(i: usize, buf: &mut [u8]) -> Result<usize, ReadError> { 463unsafe fn read_dma<T: Instance>(i: usize, buf: &mut [u8]) -> Result<usize, ReadError> {
@@ -449,20 +549,9 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {
449 let i = self.info.addr.index(); 549 let i = self.info.addr.index();
450 assert!(i != 0); 550 assert!(i != 0);
451 551
452 // Wait until ready 552 self.wait_data_ready()
453 poll_fn(|cx| { 553 .await
454 EP_OUT_WAKERS[i - 1].register(cx.waker()); 554 .map_err(|_| ReadError::Disabled)?;
455 let r = READY_ENDPOINTS.load(Ordering::Acquire);
456 if r & (1 << (i + 16)) != 0 {
457 Poll::Ready(())
458 } else {
459 Poll::Pending
460 }
461 })
462 .await;
463
464 // Mark as not ready
465 READY_ENDPOINTS.fetch_and(!(1 << (i + 16)), Ordering::AcqRel);
466 555
467 unsafe { read_dma::<T>(i, buf) } 556 unsafe { read_dma::<T>(i, buf) }
468 } 557 }
@@ -477,20 +566,9 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {
477 let i = self.info.addr.index(); 566 let i = self.info.addr.index();
478 assert!(i != 0); 567 assert!(i != 0);
479 568
480 // Wait until ready. 569 self.wait_data_ready()
481 poll_fn(|cx| { 570 .await
482 EP_IN_WAKERS[i - 1].register(cx.waker()); 571 .map_err(|_| WriteError::Disabled)?;
483 let r = READY_ENDPOINTS.load(Ordering::Acquire);
484 if r & (1 << i) != 0 {
485 Poll::Ready(())
486 } else {
487 Poll::Pending
488 }
489 })
490 .await;
491
492 // Mark as not ready
493 READY_ENDPOINTS.fetch_and(!(1 << i), Ordering::AcqRel);
494 572
495 unsafe { write_dma::<T>(i, buf) } 573 unsafe { write_dma::<T>(i, buf) }
496 574
diff --git a/embassy-usb-serial/src/lib.rs b/embassy-usb-serial/src/lib.rs
index 8418de0f0..07352fac5 100644
--- a/embassy-usb-serial/src/lib.rs
+++ b/embassy-usb-serial/src/lib.rs
@@ -273,6 +273,11 @@ impl<'d, D: Driver<'d>> CdcAcmClass<'d, D> {
273 pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, ReadError> { 273 pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, ReadError> {
274 self.read_ep.read(data).await 274 self.read_ep.read(data).await
275 } 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 }
276} 281}
277 282
278/// Number of stop bits for LineCoding 283/// Number of stop bits for LineCoding
diff --git a/embassy-usb/src/driver.rs b/embassy-usb/src/driver.rs
index 82b59bd1e..6eaa40b0d 100644
--- a/embassy-usb/src/driver.rs
+++ b/embassy-usb/src/driver.rs
@@ -72,6 +72,9 @@ pub trait Bus {
72 /// Sets the device USB address to `addr`. 72 /// Sets the device USB address to `addr`.
73 fn set_device_address(&mut self, addr: u8); 73 fn set_device_address(&mut self, addr: u8);
74 74
75 /// Sets the device configured state.
76 fn set_configured(&mut self, configured: bool);
77
75 /// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it 78 /// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it
76 /// should be prepared to receive data again. Only used during control transfers. 79 /// should be prepared to receive data again. Only used during control transfers.
77 fn set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool); 80 fn set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool);
@@ -105,6 +108,10 @@ pub trait Bus {
105} 108}
106 109
107pub trait Endpoint { 110pub trait Endpoint {
111 type WaitEnabledFuture<'a>: Future<Output = ()> + 'a
112 where
113 Self: 'a;
114
108 /// Get the endpoint address 115 /// Get the endpoint address
109 fn info(&self) -> &EndpointInfo; 116 fn info(&self) -> &EndpointInfo;
110 117
@@ -115,6 +122,9 @@ pub trait Endpoint {
115 /// Gets whether the STALL condition is set for an endpoint. 122 /// Gets whether the STALL condition is set for an endpoint.
116 fn is_stalled(&self) -> bool; 123 fn is_stalled(&self) -> bool;
117 124
125 /// Waits for the endpoint to be enabled.
126 fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_>;
127
118 // TODO enable/disable? 128 // TODO enable/disable?
119} 129}
120 130
@@ -212,6 +222,7 @@ pub enum WriteError {
212 /// class shouldn't provide more data than the `max_packet_size` it specified when allocating 222 /// class shouldn't provide more data than the `max_packet_size` it specified when allocating
213 /// the endpoint. 223 /// the endpoint.
214 BufferOverflow, 224 BufferOverflow,
225 Disabled,
215} 226}
216 227
217#[derive(Copy, Clone, Eq, PartialEq, Debug)] 228#[derive(Copy, Clone, Eq, PartialEq, Debug)]
@@ -223,4 +234,5 @@ pub enum ReadError {
223 /// should use a buffer that is large enough for the `max_packet_size` it specified when 234 /// should use a buffer that is large enough for the `max_packet_size` it specified when
224 /// allocating the endpoint. 235 /// allocating the endpoint.
225 BufferOverflow, 236 BufferOverflow,
237 Disabled,
226} 238}
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs
index d2d3e5e0a..cf8d12539 100644
--- a/embassy-usb/src/lib.rs
+++ b/embassy-usb/src/lib.rs
@@ -162,18 +162,21 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
162 self.remote_wakeup_enabled = true; 162 self.remote_wakeup_enabled = true;
163 self.control.accept(stage) 163 self.control.accept(stage)
164 } 164 }
165 (Request::SET_ADDRESS, 1..=127) => { 165 (Request::SET_ADDRESS, addr @ 1..=127) => {
166 self.pending_address = req.value as u8; 166 self.pending_address = addr as u8;
167 self.bus.set_device_address(self.pending_address);
167 self.control.accept(stage) 168 self.control.accept(stage)
168 } 169 }
169 (Request::SET_CONFIGURATION, CONFIGURATION_VALUE_U16) => { 170 (Request::SET_CONFIGURATION, CONFIGURATION_VALUE_U16) => {
170 self.device_state = UsbDeviceState::Configured; 171 self.device_state = UsbDeviceState::Configured;
172 self.bus.set_configured(true);
171 self.control.accept(stage) 173 self.control.accept(stage)
172 } 174 }
173 (Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => match self.device_state { 175 (Request::SET_CONFIGURATION, CONFIGURATION_NONE_U16) => match self.device_state {
174 UsbDeviceState::Default => self.control.accept(stage), 176 UsbDeviceState::Default => self.control.accept(stage),
175 _ => { 177 _ => {
176 self.device_state = UsbDeviceState::Addressed; 178 self.device_state = UsbDeviceState::Addressed;
179 self.bus.set_configured(false);
177 self.control.accept(stage) 180 self.control.accept(stage)
178 } 181 }
179 }, 182 },
diff --git a/examples/nrf/src/bin/usb_serial.rs b/examples/nrf/src/bin/usb_serial.rs
index cd681c5ce..9437e835f 100644
--- a/examples/nrf/src/bin/usb_serial.rs
+++ b/examples/nrf/src/bin/usb_serial.rs
@@ -4,12 +4,13 @@
4#![feature(type_alias_impl_trait)] 4#![feature(type_alias_impl_trait)]
5 5
6use core::mem; 6use core::mem;
7use defmt::*; 7use defmt::{info, panic};
8use embassy::executor::Spawner; 8use embassy::executor::Spawner;
9use embassy_nrf::interrupt; 9use embassy_nrf::interrupt;
10use embassy_nrf::pac; 10use embassy_nrf::pac;
11use embassy_nrf::usb::Driver; 11use embassy_nrf::usb::{Driver, Instance};
12use embassy_nrf::Peripherals; 12use embassy_nrf::Peripherals;
13use embassy_usb::driver::{ReadError, WriteError};
13use embassy_usb::{Config, UsbDeviceBuilder}; 14use embassy_usb::{Config, UsbDeviceBuilder};
14use embassy_usb_serial::{CdcAcmClass, State}; 15use embassy_usb_serial::{CdcAcmClass, State};
15use futures::future::join; 16use futures::future::join;
@@ -66,12 +67,11 @@ async fn main(_spawner: Spawner, p: Peripherals) {
66 67
67 // Do stuff with the class! 68 // Do stuff with the class!
68 let echo_fut = async { 69 let echo_fut = async {
69 let mut buf = [0; 64];
70 loop { 70 loop {
71 let n = class.read_packet(&mut buf).await.unwrap(); 71 class.wait_connection().await;
72 let data = &buf[..n]; 72 info!("Connected");
73 info!("data: {:x}", data); 73 let _ = echo(&mut class).await;
74 class.write_packet(data).await.unwrap(); 74 info!("Disconnected");
75 } 75 }
76 }; 76 };
77 77
@@ -79,3 +79,35 @@ async fn main(_spawner: Spawner, p: Peripherals) {
79 // If we had made everything `'static` above instead, we could do this using separate tasks instead. 79 // If we had made everything `'static` above instead, we could do this using separate tasks instead.
80 join(usb_fut, echo_fut).await; 80 join(usb_fut, echo_fut).await;
81} 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}