diff options
| -rw-r--r-- | embassy-nrf/src/usb.rs | 158 | ||||
| -rw-r--r-- | embassy-usb/src/lib.rs | 1 | ||||
| -rw-r--r-- | examples/nrf/Cargo.toml | 2 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb/main.rs | 18 |
4 files changed, 116 insertions, 63 deletions
diff --git a/embassy-nrf/src/usb.rs b/embassy-nrf/src/usb.rs index 163b2c794..874bfe841 100644 --- a/embassy-nrf/src/usb.rs +++ b/embassy-nrf/src/usb.rs | |||
| @@ -1,7 +1,8 @@ | |||
| 1 | #![macro_use] | 1 | #![macro_use] |
| 2 | 2 | ||
| 3 | use core::marker::PhantomData; | 3 | use core::marker::PhantomData; |
| 4 | use core::sync::atomic::{compiler_fence, Ordering}; | 4 | use core::mem::MaybeUninit; |
| 5 | use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; | ||
| 5 | use core::task::Poll; | 6 | use core::task::Poll; |
| 6 | use embassy::interrupt::InterruptExt; | 7 | use embassy::interrupt::InterruptExt; |
| 7 | use embassy::time::{with_timeout, Duration}; | 8 | use embassy::time::{with_timeout, Duration}; |
| @@ -23,6 +24,7 @@ const NEW_AW: AtomicWaker = AtomicWaker::new(); | |||
| 23 | static BUS_WAKER: AtomicWaker = NEW_AW; | 24 | static BUS_WAKER: AtomicWaker = NEW_AW; |
| 24 | static EP_IN_WAKERS: [AtomicWaker; 9] = [NEW_AW; 9]; | 25 | static EP_IN_WAKERS: [AtomicWaker; 9] = [NEW_AW; 9]; |
| 25 | static EP_OUT_WAKERS: [AtomicWaker; 9] = [NEW_AW; 9]; | 26 | static EP_OUT_WAKERS: [AtomicWaker; 9] = [NEW_AW; 9]; |
| 27 | static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0); | ||
| 26 | 28 | ||
| 27 | pub struct Driver<'d, T: Instance> { | 29 | pub struct Driver<'d, T: Instance> { |
| 28 | phantom: PhantomData<&'d mut T>, | 30 | phantom: PhantomData<&'d mut T>, |
| @@ -84,6 +86,8 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 84 | regs.events_epdata.reset(); | 86 | regs.events_epdata.reset(); |
| 85 | 87 | ||
| 86 | let r = regs.epdatastatus.read().bits(); | 88 | let r = regs.epdatastatus.read().bits(); |
| 89 | regs.epdatastatus.write(|w| unsafe { w.bits(r) }); | ||
| 90 | READY_ENDPOINTS.fetch_or(r, Ordering::AcqRel); | ||
| 87 | for i in 1..=7 { | 91 | for i in 1..=7 { |
| 88 | if r & (1 << i) != 0 { | 92 | if r & (1 << i) != 0 { |
| 89 | EP_IN_WAKERS[i].wake(); | 93 | EP_IN_WAKERS[i].wake(); |
| @@ -143,15 +147,12 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { | |||
| 143 | .alloc_in | 147 | .alloc_in |
| 144 | .allocate(ep_addr, ep_type, max_packet_size, interval)?; | 148 | .allocate(ep_addr, ep_type, max_packet_size, interval)?; |
| 145 | let ep_addr = EndpointAddress::from_parts(index, UsbDirection::In); | 149 | let ep_addr = EndpointAddress::from_parts(index, UsbDirection::In); |
| 146 | Ok(Endpoint { | 150 | Ok(Endpoint::new(EndpointInfo { |
| 147 | _phantom: PhantomData, | 151 | addr: ep_addr, |
| 148 | info: EndpointInfo { | 152 | ep_type, |
| 149 | addr: ep_addr, | 153 | max_packet_size, |
| 150 | ep_type, | 154 | interval, |
| 151 | max_packet_size, | 155 | })) |
| 152 | interval, | ||
| 153 | }, | ||
| 154 | }) | ||
| 155 | } | 156 | } |
| 156 | 157 | ||
| 157 | fn alloc_endpoint_out( | 158 | fn alloc_endpoint_out( |
| @@ -165,15 +166,12 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { | |||
| 165 | .alloc_out | 166 | .alloc_out |
| 166 | .allocate(ep_addr, ep_type, max_packet_size, interval)?; | 167 | .allocate(ep_addr, ep_type, max_packet_size, interval)?; |
| 167 | let ep_addr = EndpointAddress::from_parts(index, UsbDirection::Out); | 168 | let ep_addr = EndpointAddress::from_parts(index, UsbDirection::Out); |
| 168 | Ok(Endpoint { | 169 | Ok(Endpoint::new(EndpointInfo { |
| 169 | _phantom: PhantomData, | 170 | addr: ep_addr, |
| 170 | info: EndpointInfo { | 171 | ep_type, |
| 171 | addr: ep_addr, | 172 | max_packet_size, |
| 172 | ep_type, | 173 | interval, |
| 173 | max_packet_size, | 174 | })) |
| 174 | interval, | ||
| 175 | }, | ||
| 176 | }) | ||
| 177 | } | 175 | } |
| 178 | 176 | ||
| 179 | fn enable(self) -> Self::Bus { | 177 | fn enable(self) -> Self::Bus { |
| @@ -284,7 +282,9 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { | |||
| 284 | } | 282 | } |
| 285 | } | 283 | } |
| 286 | 284 | ||
| 287 | //self.busy_in_endpoints = 0; | 285 | // IN endpoints (low bits) default to ready. |
| 286 | // OUT endpoints (high bits) default to NOT ready, they become ready when data comes in. | ||
| 287 | READY_ENDPOINTS.store(0x0000FFFF, Ordering::Release); | ||
| 288 | } | 288 | } |
| 289 | 289 | ||
| 290 | #[inline] | 290 | #[inline] |
| @@ -324,6 +324,15 @@ pub struct Endpoint<'d, T: Instance, Dir> { | |||
| 324 | info: EndpointInfo, | 324 | info: EndpointInfo, |
| 325 | } | 325 | } |
| 326 | 326 | ||
| 327 | impl<'d, T: Instance, Dir> Endpoint<'d, T, Dir> { | ||
| 328 | fn new(info: EndpointInfo) -> Self { | ||
| 329 | Self { | ||
| 330 | info, | ||
| 331 | _phantom: PhantomData, | ||
| 332 | } | ||
| 333 | } | ||
| 334 | } | ||
| 335 | |||
| 327 | impl<'d, T: Instance, Dir> driver::Endpoint for Endpoint<'d, T, Dir> { | 336 | impl<'d, T: Instance, Dir> driver::Endpoint for Endpoint<'d, T, Dir> { |
| 328 | fn info(&self) -> &EndpointInfo { | 337 | fn info(&self) -> &EndpointInfo { |
| 329 | &self.info | 338 | &self.info |
| @@ -368,7 +377,6 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { | |||
| 368 | } | 377 | } |
| 369 | }) | 378 | }) |
| 370 | .await; | 379 | .await; |
| 371 | info!("got SETUP"); | ||
| 372 | 380 | ||
| 373 | if buf.len() < 8 { | 381 | if buf.len() < 8 { |
| 374 | return Err(ReadError::BufferOverflow); | 382 | return Err(ReadError::BufferOverflow); |
| @@ -385,10 +393,10 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { | |||
| 385 | 393 | ||
| 386 | Ok(8) | 394 | Ok(8) |
| 387 | } else { | 395 | } else { |
| 396 | // Wait until ready | ||
| 388 | poll_fn(|cx| { | 397 | poll_fn(|cx| { |
| 389 | EP_OUT_WAKERS[i].register(cx.waker()); | 398 | EP_OUT_WAKERS[i].register(cx.waker()); |
| 390 | let regs = T::regs(); | 399 | let r = READY_ENDPOINTS.load(Ordering::Acquire); |
| 391 | let r = regs.epdatastatus.read().bits(); | ||
| 392 | if r & (1 << (i + 16)) != 0 { | 400 | if r & (1 << (i + 16)) != 0 { |
| 393 | Poll::Ready(()) | 401 | Poll::Ready(()) |
| 394 | } else { | 402 | } else { |
| @@ -397,9 +405,8 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { | |||
| 397 | }) | 405 | }) |
| 398 | .await; | 406 | .await; |
| 399 | 407 | ||
| 400 | // Clear status | 408 | // Mark as not ready |
| 401 | regs.epdatastatus | 409 | READY_ENDPOINTS.fetch_and(!(1 << (i + 16)), Ordering::AcqRel); |
| 402 | .write(|w| unsafe { w.bits(1 << (i + 16)) }); | ||
| 403 | 410 | ||
| 404 | // Check that the packet fits into the buffer | 411 | // Check that the packet fits into the buffer |
| 405 | let size = regs.size.epout[i].read().bits(); | 412 | let size = regs.size.epout[i].read().bits(); |
| @@ -448,48 +455,83 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { | |||
| 448 | 455 | ||
| 449 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { | 456 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { |
| 450 | async move { | 457 | async move { |
| 451 | info!("write: {:x}", buf); | ||
| 452 | |||
| 453 | let regs = T::regs(); | 458 | let regs = T::regs(); |
| 459 | let i = self.info.addr.index(); | ||
| 454 | 460 | ||
| 455 | let ptr = buf.as_ptr() as u32; | 461 | // Wait until ready. |
| 456 | let len = buf.len() as u32; | 462 | if i != 0 { |
| 457 | regs.epin0.ptr.write(|w| unsafe { w.bits(ptr) }); | ||
| 458 | regs.epin0.maxcnt.write(|w| unsafe { w.bits(len) }); | ||
| 459 | |||
| 460 | regs.events_ep0datadone.reset(); | ||
| 461 | regs.events_endepin[0].reset(); | ||
| 462 | |||
| 463 | dma_start(); | ||
| 464 | |||
| 465 | regs.tasks_startepin[0].write(|w| unsafe { w.bits(1) }); | ||
| 466 | info!("write: waiting for endepin..."); | ||
| 467 | while regs.events_endepin[0].read().bits() == 0 {} | ||
| 468 | |||
| 469 | dma_end(); | ||
| 470 | |||
| 471 | info!("write: waiting for ep0datadone..."); | ||
| 472 | regs.intenset.write(|w| w.ep0datadone().set()); | ||
| 473 | let res = with_timeout( | ||
| 474 | Duration::from_millis(10), | ||
| 475 | poll_fn(|cx| { | 463 | poll_fn(|cx| { |
| 476 | EP_IN_WAKERS[0].register(cx.waker()); | 464 | EP_IN_WAKERS[i].register(cx.waker()); |
| 477 | let regs = T::regs(); | 465 | let r = READY_ENDPOINTS.load(Ordering::Acquire); |
| 478 | if regs.events_ep0datadone.read().bits() != 0 { | 466 | if r & (1 << i) != 0 { |
| 479 | Poll::Ready(()) | 467 | Poll::Ready(()) |
| 480 | } else { | 468 | } else { |
| 481 | Poll::Pending | 469 | Poll::Pending |
| 482 | } | 470 | } |
| 483 | }), | 471 | }) |
| 484 | ) | 472 | .await; |
| 485 | .await; | 473 | |
| 474 | // Mark as not ready | ||
| 475 | READY_ENDPOINTS.fetch_and(!(1 << i), Ordering::AcqRel); | ||
| 476 | } | ||
| 477 | |||
| 478 | if i == 0 { | ||
| 479 | regs.events_ep0datadone.reset(); | ||
| 480 | } | ||
| 486 | 481 | ||
| 487 | if res.is_err() { | 482 | assert!(buf.len() <= 64); |
| 488 | // todo wrong error | 483 | |
| 489 | return Err(driver::WriteError::BufferOverflow); | 484 | // EasyDMA can't read FLASH, so we copy through RAM |
| 485 | let mut ram_buf: MaybeUninit<[u8; 64]> = MaybeUninit::uninit(); | ||
| 486 | let ptr = ram_buf.as_mut_ptr() as *mut u8; | ||
| 487 | unsafe { core::ptr::copy_nonoverlapping(buf.as_ptr(), ptr, buf.len()) }; | ||
| 488 | |||
| 489 | let epin = [ | ||
| 490 | ®s.epin0, | ||
| 491 | ®s.epin1, | ||
| 492 | ®s.epin2, | ||
| 493 | ®s.epin3, | ||
| 494 | ®s.epin4, | ||
| 495 | ®s.epin5, | ||
| 496 | ®s.epin6, | ||
| 497 | ®s.epin7, | ||
| 498 | ]; | ||
| 499 | |||
| 500 | // Set the buffer length so the right number of bytes are transmitted. | ||
| 501 | // Safety: `buf.len()` has been checked to be <= the max buffer length. | ||
| 502 | unsafe { | ||
| 503 | epin[i].ptr.write(|w| w.bits(ptr as u32)); | ||
| 504 | epin[i].maxcnt.write(|w| w.maxcnt().bits(buf.len() as u8)); | ||
| 490 | } | 505 | } |
| 491 | 506 | ||
| 492 | info!("write done"); | 507 | regs.events_endepin[i].reset(); |
| 508 | |||
| 509 | dma_start(); | ||
| 510 | regs.tasks_startepin[i].write(|w| unsafe { w.bits(1) }); | ||
| 511 | while regs.events_endepin[i].read().bits() == 0 {} | ||
| 512 | dma_end(); | ||
| 513 | |||
| 514 | if i == 0 { | ||
| 515 | regs.intenset.write(|w| w.ep0datadone().set()); | ||
| 516 | let res = with_timeout( | ||
| 517 | Duration::from_millis(10), | ||
| 518 | poll_fn(|cx| { | ||
| 519 | EP_IN_WAKERS[0].register(cx.waker()); | ||
| 520 | let regs = T::regs(); | ||
| 521 | if regs.events_ep0datadone.read().bits() != 0 { | ||
| 522 | Poll::Ready(()) | ||
| 523 | } else { | ||
| 524 | Poll::Pending | ||
| 525 | } | ||
| 526 | }), | ||
| 527 | ) | ||
| 528 | .await; | ||
| 529 | |||
| 530 | if res.is_err() { | ||
| 531 | // todo wrong error | ||
| 532 | return Err(driver::WriteError::BufferOverflow); | ||
| 533 | } | ||
| 534 | } | ||
| 493 | 535 | ||
| 494 | Ok(()) | 536 | Ok(()) |
| 495 | } | 537 | } |
diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 33f3d4712..95f78804d 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs | |||
| @@ -198,6 +198,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { | |||
| 198 | } | 198 | } |
| 199 | 199 | ||
| 200 | fn control_reject(&mut self, req: Request) { | 200 | fn control_reject(&mut self, req: Request) { |
| 201 | info!("control reject"); | ||
| 201 | self.control_out.set_stalled(true); | 202 | self.control_out.set_stalled(true); |
| 202 | } | 203 | } |
| 203 | 204 | ||
diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index fb846b3a9..59e5de026 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | [package] | 1 | [package] |
| 2 | authors = ["Dario Nieuwenhuis <[email protected]>"] | 2 | authors = ["Dario Nieuwenhuis <[email protected]>"] |
| 3 | edition = "2018" | 3 | edition = "2021" |
| 4 | name = "embassy-nrf-examples" | 4 | name = "embassy-nrf-examples" |
| 5 | version = "0.1.0" | 5 | version = "0.1.0" |
| 6 | 6 | ||
diff --git a/examples/nrf/src/bin/usb/main.rs b/examples/nrf/src/bin/usb/main.rs index d175766bb..014ad5c6e 100644 --- a/examples/nrf/src/bin/usb/main.rs +++ b/examples/nrf/src/bin/usb/main.rs | |||
| @@ -10,13 +10,14 @@ mod cdc_acm; | |||
| 10 | use core::mem; | 10 | use core::mem; |
| 11 | use defmt::*; | 11 | use defmt::*; |
| 12 | use embassy::executor::Spawner; | 12 | use embassy::executor::Spawner; |
| 13 | use embassy::time::{Duration, Timer}; | ||
| 13 | use embassy_nrf::interrupt; | 14 | use embassy_nrf::interrupt; |
| 14 | use embassy_nrf::pac; | 15 | use embassy_nrf::pac; |
| 15 | use embassy_nrf::usb::{self, Driver}; | 16 | use embassy_nrf::usb::Driver; |
| 16 | use embassy_nrf::Peripherals; | 17 | use embassy_nrf::Peripherals; |
| 17 | use embassy_usb::driver::EndpointOut; | 18 | use embassy_usb::driver::{EndpointIn, EndpointOut}; |
| 18 | use embassy_usb::{Config, UsbDeviceBuilder}; | 19 | use embassy_usb::{Config, UsbDeviceBuilder}; |
| 19 | use futures::future::{join, select}; | 20 | use futures::future::join3; |
| 20 | 21 | ||
| 21 | use crate::cdc_acm::CdcAcmClass; | 22 | use crate::cdc_acm::CdcAcmClass; |
| 22 | 23 | ||
| @@ -61,6 +62,15 @@ async fn main(_spawner: Spawner, p: Peripherals) { | |||
| 61 | info!("data: {:x}", data); | 62 | info!("data: {:x}", data); |
| 62 | } | 63 | } |
| 63 | }; | 64 | }; |
| 65 | let fut3 = async { | ||
| 66 | loop { | ||
| 67 | info!("writing..."); | ||
| 68 | class.write_ep.write(b"Hello World!\r\n").await.unwrap(); | ||
| 69 | info!("written"); | ||
| 70 | |||
| 71 | Timer::after(Duration::from_secs(1)).await; | ||
| 72 | } | ||
| 73 | }; | ||
| 64 | 74 | ||
| 65 | join(fut1, fut2).await; | 75 | join3(fut1, fut2, fut3).await; |
| 66 | } | 76 | } |
