Working CDC-ACM device->host

This commit is contained in:
Dario Nieuwenhuis 2022-03-10 01:05:33 +01:00
parent 77ceced036
commit 0320500f0f
4 changed files with 116 additions and 63 deletions

View File

@ -1,7 +1,8 @@
#![macro_use] #![macro_use]
use core::marker::PhantomData; use core::marker::PhantomData;
use core::sync::atomic::{compiler_fence, Ordering}; use core::mem::MaybeUninit;
use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
use core::task::Poll; use core::task::Poll;
use embassy::interrupt::InterruptExt; use embassy::interrupt::InterruptExt;
use embassy::time::{with_timeout, Duration}; use embassy::time::{with_timeout, Duration};
@ -23,6 +24,7 @@ const NEW_AW: AtomicWaker = AtomicWaker::new();
static BUS_WAKER: AtomicWaker = NEW_AW; static BUS_WAKER: AtomicWaker = NEW_AW;
static EP_IN_WAKERS: [AtomicWaker; 9] = [NEW_AW; 9]; static EP_IN_WAKERS: [AtomicWaker; 9] = [NEW_AW; 9];
static EP_OUT_WAKERS: [AtomicWaker; 9] = [NEW_AW; 9]; static EP_OUT_WAKERS: [AtomicWaker; 9] = [NEW_AW; 9];
static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0);
pub struct Driver<'d, T: Instance> { pub struct Driver<'d, T: Instance> {
phantom: PhantomData<&'d mut T>, phantom: PhantomData<&'d mut T>,
@ -84,6 +86,8 @@ impl<'d, T: Instance> Driver<'d, T> {
regs.events_epdata.reset(); regs.events_epdata.reset();
let r = regs.epdatastatus.read().bits(); let r = regs.epdatastatus.read().bits();
regs.epdatastatus.write(|w| unsafe { w.bits(r) });
READY_ENDPOINTS.fetch_or(r, Ordering::AcqRel);
for i in 1..=7 { for i in 1..=7 {
if r & (1 << i) != 0 { if r & (1 << i) != 0 {
EP_IN_WAKERS[i].wake(); EP_IN_WAKERS[i].wake();
@ -143,15 +147,12 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
.alloc_in .alloc_in
.allocate(ep_addr, ep_type, max_packet_size, interval)?; .allocate(ep_addr, ep_type, max_packet_size, interval)?;
let ep_addr = EndpointAddress::from_parts(index, UsbDirection::In); let ep_addr = EndpointAddress::from_parts(index, UsbDirection::In);
Ok(Endpoint { Ok(Endpoint::new(EndpointInfo {
_phantom: PhantomData, addr: ep_addr,
info: EndpointInfo { ep_type,
addr: ep_addr, max_packet_size,
ep_type, interval,
max_packet_size, }))
interval,
},
})
} }
fn alloc_endpoint_out( fn alloc_endpoint_out(
@ -165,15 +166,12 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
.alloc_out .alloc_out
.allocate(ep_addr, ep_type, max_packet_size, interval)?; .allocate(ep_addr, ep_type, max_packet_size, interval)?;
let ep_addr = EndpointAddress::from_parts(index, UsbDirection::Out); let ep_addr = EndpointAddress::from_parts(index, UsbDirection::Out);
Ok(Endpoint { Ok(Endpoint::new(EndpointInfo {
_phantom: PhantomData, addr: ep_addr,
info: EndpointInfo { ep_type,
addr: ep_addr, max_packet_size,
ep_type, interval,
max_packet_size, }))
interval,
},
})
} }
fn enable(self) -> Self::Bus { fn enable(self) -> Self::Bus {
@ -284,7 +282,9 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
} }
} }
//self.busy_in_endpoints = 0; // IN endpoints (low bits) default to ready.
// OUT endpoints (high bits) default to NOT ready, they become ready when data comes in.
READY_ENDPOINTS.store(0x0000FFFF, Ordering::Release);
} }
#[inline] #[inline]
@ -324,6 +324,15 @@ pub struct Endpoint<'d, T: Instance, Dir> {
info: EndpointInfo, info: EndpointInfo,
} }
impl<'d, T: Instance, Dir> Endpoint<'d, T, Dir> {
fn new(info: EndpointInfo) -> Self {
Self {
info,
_phantom: PhantomData,
}
}
}
impl<'d, T: Instance, Dir> driver::Endpoint for Endpoint<'d, T, Dir> { impl<'d, T: Instance, Dir> driver::Endpoint for Endpoint<'d, T, Dir> {
fn info(&self) -> &EndpointInfo { fn info(&self) -> &EndpointInfo {
&self.info &self.info
@ -368,7 +377,6 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {
} }
}) })
.await; .await;
info!("got SETUP");
if buf.len() < 8 { if buf.len() < 8 {
return Err(ReadError::BufferOverflow); return Err(ReadError::BufferOverflow);
@ -385,10 +393,10 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {
Ok(8) Ok(8)
} else { } else {
// Wait until ready
poll_fn(|cx| { poll_fn(|cx| {
EP_OUT_WAKERS[i].register(cx.waker()); EP_OUT_WAKERS[i].register(cx.waker());
let regs = T::regs(); let r = READY_ENDPOINTS.load(Ordering::Acquire);
let r = regs.epdatastatus.read().bits();
if r & (1 << (i + 16)) != 0 { if r & (1 << (i + 16)) != 0 {
Poll::Ready(()) Poll::Ready(())
} else { } else {
@ -397,9 +405,8 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {
}) })
.await; .await;
// Clear status // Mark as not ready
regs.epdatastatus READY_ENDPOINTS.fetch_and(!(1 << (i + 16)), Ordering::AcqRel);
.write(|w| unsafe { w.bits(1 << (i + 16)) });
// Check that the packet fits into the buffer // Check that the packet fits into the buffer
let size = regs.size.epout[i].read().bits(); let size = regs.size.epout[i].read().bits();
@ -448,48 +455,83 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
async move { async move {
info!("write: {:x}", buf);
let regs = T::regs(); let regs = T::regs();
let i = self.info.addr.index();
let ptr = buf.as_ptr() as u32; // Wait until ready.
let len = buf.len() as u32; if i != 0 {
regs.epin0.ptr.write(|w| unsafe { w.bits(ptr) });
regs.epin0.maxcnt.write(|w| unsafe { w.bits(len) });
regs.events_ep0datadone.reset();
regs.events_endepin[0].reset();
dma_start();
regs.tasks_startepin[0].write(|w| unsafe { w.bits(1) });
info!("write: waiting for endepin...");
while regs.events_endepin[0].read().bits() == 0 {}
dma_end();
info!("write: waiting for ep0datadone...");
regs.intenset.write(|w| w.ep0datadone().set());
let res = with_timeout(
Duration::from_millis(10),
poll_fn(|cx| { poll_fn(|cx| {
EP_IN_WAKERS[0].register(cx.waker()); EP_IN_WAKERS[i].register(cx.waker());
let regs = T::regs(); let r = READY_ENDPOINTS.load(Ordering::Acquire);
if regs.events_ep0datadone.read().bits() != 0 { if r & (1 << i) != 0 {
Poll::Ready(()) Poll::Ready(())
} else { } else {
Poll::Pending Poll::Pending
} }
}), })
) .await;
.await;
if res.is_err() { // Mark as not ready
// todo wrong error READY_ENDPOINTS.fetch_and(!(1 << i), Ordering::AcqRel);
return Err(driver::WriteError::BufferOverflow);
} }
info!("write done"); if i == 0 {
regs.events_ep0datadone.reset();
}
assert!(buf.len() <= 64);
// EasyDMA can't read FLASH, so we copy through RAM
let mut ram_buf: MaybeUninit<[u8; 64]> = MaybeUninit::uninit();
let ptr = ram_buf.as_mut_ptr() as *mut u8;
unsafe { core::ptr::copy_nonoverlapping(buf.as_ptr(), ptr, buf.len()) };
let epin = [
&regs.epin0,
&regs.epin1,
&regs.epin2,
&regs.epin3,
&regs.epin4,
&regs.epin5,
&regs.epin6,
&regs.epin7,
];
// Set the buffer length so the right number of bytes are transmitted.
// Safety: `buf.len()` has been checked to be <= the max buffer length.
unsafe {
epin[i].ptr.write(|w| w.bits(ptr as u32));
epin[i].maxcnt.write(|w| w.maxcnt().bits(buf.len() as u8));
}
regs.events_endepin[i].reset();
dma_start();
regs.tasks_startepin[i].write(|w| unsafe { w.bits(1) });
while regs.events_endepin[i].read().bits() == 0 {}
dma_end();
if i == 0 {
regs.intenset.write(|w| w.ep0datadone().set());
let res = with_timeout(
Duration::from_millis(10),
poll_fn(|cx| {
EP_IN_WAKERS[0].register(cx.waker());
let regs = T::regs();
if regs.events_ep0datadone.read().bits() != 0 {
Poll::Ready(())
} else {
Poll::Pending
}
}),
)
.await;
if res.is_err() {
// todo wrong error
return Err(driver::WriteError::BufferOverflow);
}
}
Ok(()) Ok(())
} }

View File

@ -198,6 +198,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
} }
fn control_reject(&mut self, req: Request) { fn control_reject(&mut self, req: Request) {
info!("control reject");
self.control_out.set_stalled(true); self.control_out.set_stalled(true);
} }

View File

@ -1,6 +1,6 @@
[package] [package]
authors = ["Dario Nieuwenhuis <dirbaio@dirbaio.net>"] authors = ["Dario Nieuwenhuis <dirbaio@dirbaio.net>"]
edition = "2018" edition = "2021"
name = "embassy-nrf-examples" name = "embassy-nrf-examples"
version = "0.1.0" version = "0.1.0"

View File

@ -10,13 +10,14 @@ mod cdc_acm;
use core::mem; use core::mem;
use defmt::*; use defmt::*;
use embassy::executor::Spawner; use embassy::executor::Spawner;
use embassy::time::{Duration, Timer};
use embassy_nrf::interrupt; use embassy_nrf::interrupt;
use embassy_nrf::pac; use embassy_nrf::pac;
use embassy_nrf::usb::{self, Driver}; use embassy_nrf::usb::Driver;
use embassy_nrf::Peripherals; use embassy_nrf::Peripherals;
use embassy_usb::driver::EndpointOut; use embassy_usb::driver::{EndpointIn, EndpointOut};
use embassy_usb::{Config, UsbDeviceBuilder}; use embassy_usb::{Config, UsbDeviceBuilder};
use futures::future::{join, select}; use futures::future::join3;
use crate::cdc_acm::CdcAcmClass; use crate::cdc_acm::CdcAcmClass;
@ -61,6 +62,15 @@ async fn main(_spawner: Spawner, p: Peripherals) {
info!("data: {:x}", data); info!("data: {:x}", data);
} }
}; };
let fut3 = async {
loop {
info!("writing...");
class.write_ep.write(b"Hello World!\r\n").await.unwrap();
info!("written");
join(fut1, fut2).await; Timer::after(Duration::from_secs(1)).await;
}
};
join3(fut1, fut2, fut3).await;
} }