Merge pull request #927 from embassy-rs/rp-usb
rp: add usb device support.
This commit is contained in:
commit
e4d4166da2
7 changed files with 1263 additions and 5 deletions
|
@ -12,6 +12,7 @@ flavors = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
defmt = ["dep:defmt", "embassy-usb?/defmt"]
|
||||||
|
|
||||||
# Reexport the PAC for the currently enabled chip at `embassy_rp::pac`.
|
# Reexport the PAC for the currently enabled chip at `embassy_rp::pac`.
|
||||||
# This is unstable because semver-minor (non-breaking) releases of embassy-rp may major-bump (breaking) the PAC version.
|
# This is unstable because semver-minor (non-breaking) releases of embassy-rp may major-bump (breaking) the PAC version.
|
||||||
|
@ -20,7 +21,7 @@ flavors = [
|
||||||
unstable-pac = []
|
unstable-pac = []
|
||||||
|
|
||||||
# Enable nightly-only features
|
# Enable nightly-only features
|
||||||
nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly"]
|
nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb"]
|
||||||
|
|
||||||
# Implement embedded-hal 1.0 alpha traits.
|
# Implement embedded-hal 1.0 alpha traits.
|
||||||
# Implement embedded-hal-async traits if `nightly` is set as well.
|
# Implement embedded-hal-async traits if `nightly` is set as well.
|
||||||
|
@ -33,6 +34,7 @@ embassy-time = { version = "0.1.0", path = "../embassy-time", features = [ "tick
|
||||||
embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]}
|
embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-2"]}
|
||||||
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
|
embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
|
||||||
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
|
||||||
|
embassy-usb = {version = "0.1.0", path = "../embassy-usb", optional = true }
|
||||||
atomic-polyfill = "1.0.1"
|
atomic-polyfill = "1.0.1"
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
|
@ -43,8 +45,8 @@ cortex-m = "0.7.6"
|
||||||
critical-section = "1.1"
|
critical-section = "1.1"
|
||||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||||
|
|
||||||
rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="9ad7223a48a065e612bc7dc7be5bf5bd0b41cfc4", features = ["rt"] }
|
rp2040-pac2 = { git = "https://github.com/embassy-rs/rp2040-pac2", rev="017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364", features = ["rt"] }
|
||||||
#rp2040-pac2 = { path = "../../rp/rp2040-pac2", features = ["rt"] }
|
#rp2040-pac2 = { path = "../../rp2040-pac2", features = ["rt"] }
|
||||||
|
|
||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
|
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
|
||||||
|
|
|
@ -433,7 +433,7 @@ impl<'d, T: Pin> Flex<'d, T> {
|
||||||
});
|
});
|
||||||
|
|
||||||
pin.io().ctrl().write(|w| {
|
pin.io().ctrl().write(|w| {
|
||||||
w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::SIO_0.0);
|
w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::SIO_0.0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -586,7 +586,7 @@ impl<'d, T: Pin> Drop for Flex<'d, T> {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.pin.pad_ctrl().write(|_| {});
|
self.pin.pad_ctrl().write(|_| {});
|
||||||
self.pin.io().ctrl().write(|w| {
|
self.pin.io().ctrl().write(|w| {
|
||||||
w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::NULL.0);
|
w.set_funcsel(pac::io::vals::Gpio0ctrlFuncsel::NULL.0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ pub mod interrupt;
|
||||||
pub mod spi;
|
pub mod spi;
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
pub mod uart;
|
pub mod uart;
|
||||||
|
#[cfg(feature = "nightly")]
|
||||||
|
pub mod usb;
|
||||||
|
|
||||||
mod clocks;
|
mod clocks;
|
||||||
mod reset;
|
mod reset;
|
||||||
|
@ -80,6 +82,8 @@ embassy_hal_common::peripherals! {
|
||||||
DMA_CH9,
|
DMA_CH9,
|
||||||
DMA_CH10,
|
DMA_CH10,
|
||||||
DMA_CH11,
|
DMA_CH11,
|
||||||
|
|
||||||
|
USB,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[link_section = ".boot2"]
|
#[link_section = ".boot2"]
|
||||||
|
@ -110,3 +114,36 @@ pub fn init(_config: config::Config) -> Peripherals {
|
||||||
|
|
||||||
peripherals
|
peripherals
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extension trait for PAC regs, adding atomic xor/bitset/bitclear writes.
|
||||||
|
trait RegExt<T: Copy> {
|
||||||
|
unsafe fn write_xor<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
|
||||||
|
unsafe fn write_set<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
|
||||||
|
unsafe fn write_clear<R>(&self, f: impl FnOnce(&mut T) -> R) -> R;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default + Copy, A: pac::common::Write> RegExt<T> for pac::common::Reg<T, A> {
|
||||||
|
unsafe fn write_xor<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
|
||||||
|
let mut val = Default::default();
|
||||||
|
let res = f(&mut val);
|
||||||
|
let ptr = (self.ptr() as *mut u8).add(0x1000) as *mut T;
|
||||||
|
ptr.write_volatile(val);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write_set<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
|
||||||
|
let mut val = Default::default();
|
||||||
|
let res = f(&mut val);
|
||||||
|
let ptr = (self.ptr() as *mut u8).add(0x2000) as *mut T;
|
||||||
|
ptr.write_volatile(val);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write_clear<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
|
||||||
|
let mut val = Default::default();
|
||||||
|
let res = f(&mut val);
|
||||||
|
let ptr = (self.ptr() as *mut u8).add(0x3000) as *mut T;
|
||||||
|
ptr.write_volatile(val);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
846
embassy-rp/src/usb.rs
Normal file
846
embassy-rp/src/usb.rs
Normal file
|
@ -0,0 +1,846 @@
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use core::slice;
|
||||||
|
use core::sync::atomic::Ordering;
|
||||||
|
use core::task::Poll;
|
||||||
|
|
||||||
|
use atomic_polyfill::compiler_fence;
|
||||||
|
use embassy_hal_common::into_ref;
|
||||||
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
use embassy_usb::driver::{self, EndpointAllocError, EndpointError, Event, Unsupported};
|
||||||
|
use embassy_usb::types::{EndpointAddress, EndpointInfo, EndpointType, UsbDirection};
|
||||||
|
use futures::future::poll_fn;
|
||||||
|
use futures::Future;
|
||||||
|
|
||||||
|
use crate::interrupt::{Interrupt, InterruptExt};
|
||||||
|
use crate::{pac, peripherals, Peripheral, RegExt};
|
||||||
|
|
||||||
|
pub(crate) mod sealed {
|
||||||
|
pub trait Instance {
|
||||||
|
fn regs() -> crate::pac::usb::Usb;
|
||||||
|
fn dpram() -> crate::pac::usb_dpram::UsbDpram;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Instance: sealed::Instance + 'static {
|
||||||
|
type Interrupt: Interrupt;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::usb::sealed::Instance for peripherals::USB {
|
||||||
|
fn regs() -> pac::usb::Usb {
|
||||||
|
pac::USBCTRL_REGS
|
||||||
|
}
|
||||||
|
fn dpram() -> crate::pac::usb_dpram::UsbDpram {
|
||||||
|
pac::USBCTRL_DPRAM
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::usb::Instance for peripherals::USB {
|
||||||
|
type Interrupt = crate::interrupt::USBCTRL_IRQ;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EP_COUNT: usize = 16;
|
||||||
|
const EP_MEMORY_SIZE: usize = 4096;
|
||||||
|
const EP_MEMORY: *mut u8 = pac::USBCTRL_DPRAM.0;
|
||||||
|
|
||||||
|
const NEW_AW: AtomicWaker = AtomicWaker::new();
|
||||||
|
static BUS_WAKER: AtomicWaker = NEW_AW;
|
||||||
|
static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT];
|
||||||
|
static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT];
|
||||||
|
|
||||||
|
struct EndpointBuffer<T: Instance> {
|
||||||
|
addr: u16,
|
||||||
|
len: u16,
|
||||||
|
_phantom: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Instance> EndpointBuffer<T> {
|
||||||
|
const fn new(addr: u16, len: u16) -> Self {
|
||||||
|
Self {
|
||||||
|
addr,
|
||||||
|
len,
|
||||||
|
_phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&mut self, buf: &mut [u8]) {
|
||||||
|
assert!(buf.len() <= self.len as usize);
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
let mem = unsafe { slice::from_raw_parts(EP_MEMORY.add(self.addr as _), buf.len()) };
|
||||||
|
buf.copy_from_slice(mem);
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, buf: &[u8]) {
|
||||||
|
assert!(buf.len() <= self.len as usize);
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
let mem = unsafe { slice::from_raw_parts_mut(EP_MEMORY.add(self.addr as _), buf.len()) };
|
||||||
|
mem.copy_from_slice(buf);
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
struct EndpointData {
|
||||||
|
ep_type: EndpointType, // only valid if used
|
||||||
|
max_packet_size: u16,
|
||||||
|
used: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EndpointData {
|
||||||
|
const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
ep_type: EndpointType::Bulk,
|
||||||
|
max_packet_size: 0,
|
||||||
|
used: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Driver<'d, T: Instance> {
|
||||||
|
phantom: PhantomData<&'d mut T>,
|
||||||
|
ep_in: [EndpointData; EP_COUNT],
|
||||||
|
ep_out: [EndpointData; EP_COUNT],
|
||||||
|
ep_mem_free: u16, // first free address in EP mem, in bytes.
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> Driver<'d, T> {
|
||||||
|
pub fn new(_usb: impl Peripheral<P = T> + 'd, irq: impl Peripheral<P = T::Interrupt> + 'd) -> Self {
|
||||||
|
into_ref!(irq);
|
||||||
|
irq.set_handler(Self::on_interrupt);
|
||||||
|
irq.unpend();
|
||||||
|
irq.enable();
|
||||||
|
|
||||||
|
let regs = T::regs();
|
||||||
|
unsafe {
|
||||||
|
// zero fill regs
|
||||||
|
let p = regs.0 as *mut u32;
|
||||||
|
for i in 0..0x9c / 4 {
|
||||||
|
p.add(i).write_volatile(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// zero fill epmem
|
||||||
|
let p = EP_MEMORY as *mut u32;
|
||||||
|
for i in 0..0x100 / 4 {
|
||||||
|
p.add(i).write_volatile(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
regs.usb_muxing().write(|w| {
|
||||||
|
w.set_to_phy(true);
|
||||||
|
w.set_softcon(true);
|
||||||
|
});
|
||||||
|
regs.usb_pwr().write(|w| {
|
||||||
|
w.set_vbus_detect(true);
|
||||||
|
w.set_vbus_detect_override_en(true);
|
||||||
|
});
|
||||||
|
regs.main_ctrl().write(|w| {
|
||||||
|
w.set_controller_en(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the bus so that it signals that power is available
|
||||||
|
BUS_WAKER.wake();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
phantom: PhantomData,
|
||||||
|
ep_in: [EndpointData::new(); EP_COUNT],
|
||||||
|
ep_out: [EndpointData::new(); EP_COUNT],
|
||||||
|
ep_mem_free: 0x180, // data buffer region
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_interrupt(_: *mut ()) {
|
||||||
|
unsafe {
|
||||||
|
let regs = T::regs();
|
||||||
|
//let x = regs.istr().read().0;
|
||||||
|
//trace!("USB IRQ: {:08x}", x);
|
||||||
|
|
||||||
|
let ints = regs.ints().read();
|
||||||
|
|
||||||
|
if ints.bus_reset() {
|
||||||
|
regs.inte().write_clear(|w| w.set_bus_reset(true));
|
||||||
|
BUS_WAKER.wake();
|
||||||
|
}
|
||||||
|
if ints.dev_resume_from_host() {
|
||||||
|
regs.inte().write_clear(|w| w.set_dev_resume_from_host(true));
|
||||||
|
BUS_WAKER.wake();
|
||||||
|
}
|
||||||
|
if ints.dev_suspend() {
|
||||||
|
regs.inte().write_clear(|w| w.set_dev_suspend(true));
|
||||||
|
BUS_WAKER.wake();
|
||||||
|
}
|
||||||
|
if ints.setup_req() {
|
||||||
|
regs.inte().write_clear(|w| w.set_setup_req(true));
|
||||||
|
EP_OUT_WAKERS[0].wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ints.buff_status() {
|
||||||
|
let s = regs.buff_status().read();
|
||||||
|
regs.buff_status().write_value(s);
|
||||||
|
|
||||||
|
for i in 0..EP_COUNT {
|
||||||
|
if s.ep_in(i) {
|
||||||
|
EP_IN_WAKERS[i].wake();
|
||||||
|
}
|
||||||
|
if s.ep_out(i) {
|
||||||
|
EP_OUT_WAKERS[i].wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_endpoint<D: Dir>(
|
||||||
|
&mut self,
|
||||||
|
ep_type: EndpointType,
|
||||||
|
max_packet_size: u16,
|
||||||
|
interval: u8,
|
||||||
|
) -> Result<Endpoint<'d, T, D>, driver::EndpointAllocError> {
|
||||||
|
trace!(
|
||||||
|
"allocating type={:?} mps={:?} interval={}, dir={:?}",
|
||||||
|
ep_type,
|
||||||
|
max_packet_size,
|
||||||
|
interval,
|
||||||
|
D::dir()
|
||||||
|
);
|
||||||
|
|
||||||
|
let alloc = match D::dir() {
|
||||||
|
UsbDirection::Out => &mut self.ep_out,
|
||||||
|
UsbDirection::In => &mut self.ep_in,
|
||||||
|
};
|
||||||
|
|
||||||
|
let index = alloc.iter_mut().enumerate().find(|(i, ep)| {
|
||||||
|
if *i == 0 {
|
||||||
|
return false; // reserved for control pipe
|
||||||
|
}
|
||||||
|
!ep.used
|
||||||
|
});
|
||||||
|
|
||||||
|
let (index, ep) = index.ok_or(EndpointAllocError)?;
|
||||||
|
assert!(!ep.used);
|
||||||
|
|
||||||
|
if max_packet_size > 64 {
|
||||||
|
warn!("max_packet_size too high: {}", max_packet_size);
|
||||||
|
return Err(EndpointAllocError);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ep mem addrs must be 64-byte aligned, so there's no point in trying
|
||||||
|
// to allocate smaller chunks to save memory.
|
||||||
|
let len = 64;
|
||||||
|
|
||||||
|
let addr = self.ep_mem_free;
|
||||||
|
if addr + len > EP_MEMORY_SIZE as _ {
|
||||||
|
warn!("Endpoint memory full");
|
||||||
|
return Err(EndpointAllocError);
|
||||||
|
}
|
||||||
|
self.ep_mem_free += len;
|
||||||
|
|
||||||
|
let buf = EndpointBuffer {
|
||||||
|
addr,
|
||||||
|
len,
|
||||||
|
_phantom: PhantomData,
|
||||||
|
};
|
||||||
|
|
||||||
|
trace!(" index={} addr={} len={}", index, buf.addr, buf.len);
|
||||||
|
|
||||||
|
ep.ep_type = ep_type;
|
||||||
|
ep.used = true;
|
||||||
|
ep.max_packet_size = max_packet_size;
|
||||||
|
|
||||||
|
let ep_type_reg = match ep_type {
|
||||||
|
EndpointType::Bulk => pac::usb_dpram::vals::EpControlEndpointType::BULK,
|
||||||
|
EndpointType::Control => pac::usb_dpram::vals::EpControlEndpointType::CONTROL,
|
||||||
|
EndpointType::Interrupt => pac::usb_dpram::vals::EpControlEndpointType::INTERRUPT,
|
||||||
|
EndpointType::Isochronous => pac::usb_dpram::vals::EpControlEndpointType::ISOCHRONOUS,
|
||||||
|
};
|
||||||
|
|
||||||
|
match D::dir() {
|
||||||
|
UsbDirection::Out => unsafe {
|
||||||
|
T::dpram().ep_out_control(index - 1).write(|w| {
|
||||||
|
w.set_enable(false);
|
||||||
|
w.set_buffer_address(addr);
|
||||||
|
w.set_interrupt_per_buff(true);
|
||||||
|
w.set_endpoint_type(ep_type_reg);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
UsbDirection::In => unsafe {
|
||||||
|
T::dpram().ep_in_control(index - 1).write(|w| {
|
||||||
|
w.set_enable(false);
|
||||||
|
w.set_buffer_address(addr);
|
||||||
|
w.set_interrupt_per_buff(true);
|
||||||
|
w.set_endpoint_type(ep_type_reg);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Endpoint {
|
||||||
|
_phantom: PhantomData,
|
||||||
|
info: EndpointInfo {
|
||||||
|
addr: EndpointAddress::from_parts(index, D::dir()),
|
||||||
|
ep_type,
|
||||||
|
max_packet_size,
|
||||||
|
interval,
|
||||||
|
},
|
||||||
|
buf,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
|
||||||
|
type EndpointOut = Endpoint<'d, T, Out>;
|
||||||
|
type EndpointIn = Endpoint<'d, T, In>;
|
||||||
|
type ControlPipe = ControlPipe<'d, T>;
|
||||||
|
type Bus = Bus<'d, T>;
|
||||||
|
|
||||||
|
fn alloc_endpoint_in(
|
||||||
|
&mut self,
|
||||||
|
ep_type: EndpointType,
|
||||||
|
max_packet_size: u16,
|
||||||
|
interval: u8,
|
||||||
|
) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
|
||||||
|
self.alloc_endpoint(ep_type, max_packet_size, interval)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_endpoint_out(
|
||||||
|
&mut self,
|
||||||
|
ep_type: EndpointType,
|
||||||
|
max_packet_size: u16,
|
||||||
|
interval: u8,
|
||||||
|
) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
|
||||||
|
self.alloc_endpoint(ep_type, max_packet_size, interval)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
|
||||||
|
let regs = T::regs();
|
||||||
|
unsafe {
|
||||||
|
regs.inte().write(|w| {
|
||||||
|
w.set_bus_reset(true);
|
||||||
|
w.set_buff_status(true);
|
||||||
|
w.set_dev_resume_from_host(true);
|
||||||
|
w.set_dev_suspend(true);
|
||||||
|
w.set_setup_req(true);
|
||||||
|
});
|
||||||
|
regs.int_ep_ctrl().write(|w| {
|
||||||
|
w.set_int_ep_active(0xFFFE); // all EPs
|
||||||
|
});
|
||||||
|
regs.sie_ctrl().write(|w| {
|
||||||
|
w.set_ep0_int_1buf(true);
|
||||||
|
w.set_pullup_en(true);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
trace!("enabled");
|
||||||
|
|
||||||
|
(
|
||||||
|
Bus {
|
||||||
|
phantom: PhantomData,
|
||||||
|
inited: false,
|
||||||
|
ep_out: self.ep_out,
|
||||||
|
},
|
||||||
|
ControlPipe {
|
||||||
|
_phantom: PhantomData,
|
||||||
|
max_packet_size: control_max_packet_size,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Bus<'d, T: Instance> {
|
||||||
|
phantom: PhantomData<&'d mut T>,
|
||||||
|
ep_out: [EndpointData; EP_COUNT],
|
||||||
|
inited: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
|
||||||
|
type PollFuture<'a> = impl Future<Output = Event> + 'a where Self: 'a;
|
||||||
|
|
||||||
|
fn poll<'a>(&'a mut self) -> Self::PollFuture<'a> {
|
||||||
|
poll_fn(move |cx| unsafe {
|
||||||
|
BUS_WAKER.register(cx.waker());
|
||||||
|
|
||||||
|
if !self.inited {
|
||||||
|
self.inited = true;
|
||||||
|
return Poll::Ready(Event::PowerDetected);
|
||||||
|
}
|
||||||
|
|
||||||
|
let regs = T::regs();
|
||||||
|
let siestatus = regs.sie_status().read();
|
||||||
|
|
||||||
|
if siestatus.resume() {
|
||||||
|
regs.sie_status().write(|w| w.set_resume(true));
|
||||||
|
return Poll::Ready(Event::Resume);
|
||||||
|
}
|
||||||
|
|
||||||
|
if siestatus.bus_reset() {
|
||||||
|
regs.sie_status().write(|w| {
|
||||||
|
w.set_bus_reset(true);
|
||||||
|
w.set_setup_rec(true);
|
||||||
|
});
|
||||||
|
regs.buff_status().write(|w| w.0 = 0xFFFF_FFFF);
|
||||||
|
regs.addr_endp().write(|w| w.set_address(0));
|
||||||
|
|
||||||
|
for i in 1..EP_COUNT {
|
||||||
|
T::dpram().ep_in_control(i - 1).modify(|w| w.set_enable(false));
|
||||||
|
T::dpram().ep_out_control(i - 1).modify(|w| w.set_enable(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
for w in &EP_IN_WAKERS {
|
||||||
|
w.wake()
|
||||||
|
}
|
||||||
|
for w in &EP_OUT_WAKERS {
|
||||||
|
w.wake()
|
||||||
|
}
|
||||||
|
return Poll::Ready(Event::Reset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if siestatus.suspended() {
|
||||||
|
regs.sie_status().write(|w| w.set_suspended(true));
|
||||||
|
return Poll::Ready(Event::Suspend);
|
||||||
|
}
|
||||||
|
|
||||||
|
// no pending event. Reenable all irqs.
|
||||||
|
regs.inte().write_set(|w| {
|
||||||
|
w.set_bus_reset(true);
|
||||||
|
w.set_dev_resume_from_host(true);
|
||||||
|
w.set_dev_suspend(true);
|
||||||
|
});
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_address(&mut self, addr: u8) {
|
||||||
|
let regs = T::regs();
|
||||||
|
trace!("setting addr: {}", addr);
|
||||||
|
unsafe { regs.addr_endp().write(|w| w.set_address(addr)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn endpoint_set_stalled(&mut self, _ep_addr: EndpointAddress, _stalled: bool) {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn endpoint_is_stalled(&mut self, _ep_addr: EndpointAddress) -> bool {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {
|
||||||
|
trace!("set_enabled {:?} {}", ep_addr, enabled);
|
||||||
|
if ep_addr.index() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let n = ep_addr.index();
|
||||||
|
match ep_addr.direction() {
|
||||||
|
UsbDirection::In => unsafe {
|
||||||
|
T::dpram().ep_in_control(n - 1).modify(|w| w.set_enable(enabled));
|
||||||
|
T::dpram().ep_in_buffer_control(ep_addr.index()).write(|w| {
|
||||||
|
w.set_pid(0, true); // first packet is DATA0, but PID is flipped before
|
||||||
|
});
|
||||||
|
EP_IN_WAKERS[n].wake();
|
||||||
|
},
|
||||||
|
UsbDirection::Out => unsafe {
|
||||||
|
T::dpram().ep_out_control(n - 1).modify(|w| w.set_enable(enabled));
|
||||||
|
|
||||||
|
T::dpram().ep_out_buffer_control(ep_addr.index()).write(|w| {
|
||||||
|
w.set_pid(0, false);
|
||||||
|
w.set_length(0, self.ep_out[n].max_packet_size);
|
||||||
|
});
|
||||||
|
cortex_m::asm::delay(12);
|
||||||
|
T::dpram().ep_out_buffer_control(ep_addr.index()).write(|w| {
|
||||||
|
w.set_pid(0, false);
|
||||||
|
w.set_length(0, self.ep_out[n].max_packet_size);
|
||||||
|
w.set_available(0, true);
|
||||||
|
});
|
||||||
|
EP_OUT_WAKERS[n].wake();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type EnableFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
|
||||||
|
|
||||||
|
fn enable(&mut self) -> Self::EnableFuture<'_> {
|
||||||
|
async move {}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DisableFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
|
||||||
|
|
||||||
|
fn disable(&mut self) -> Self::DisableFuture<'_> {
|
||||||
|
async move {}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoteWakeupFuture<'a> = impl Future<Output = Result<(), Unsupported>> + 'a where Self: 'a;
|
||||||
|
|
||||||
|
fn remote_wakeup(&mut self) -> Self::RemoteWakeupFuture<'_> {
|
||||||
|
async move { Err(Unsupported) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Dir {
|
||||||
|
fn dir() -> UsbDirection;
|
||||||
|
fn waker(i: usize) -> &'static AtomicWaker;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum In {}
|
||||||
|
impl Dir for In {
|
||||||
|
fn dir() -> UsbDirection {
|
||||||
|
UsbDirection::In
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn waker(i: usize) -> &'static AtomicWaker {
|
||||||
|
&EP_IN_WAKERS[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Out {}
|
||||||
|
impl Dir for Out {
|
||||||
|
fn dir() -> UsbDirection {
|
||||||
|
UsbDirection::Out
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn waker(i: usize) -> &'static AtomicWaker {
|
||||||
|
&EP_OUT_WAKERS[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Endpoint<'d, T: Instance, D> {
|
||||||
|
_phantom: PhantomData<(&'d mut T, D)>,
|
||||||
|
info: EndpointInfo,
|
||||||
|
buf: EndpointBuffer<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> {
|
||||||
|
fn info(&self) -> &EndpointInfo {
|
||||||
|
&self.info
|
||||||
|
}
|
||||||
|
|
||||||
|
type WaitEnabledFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
|
||||||
|
|
||||||
|
fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> {
|
||||||
|
async move {
|
||||||
|
trace!("wait_enabled IN WAITING");
|
||||||
|
let index = self.info.addr.index();
|
||||||
|
poll_fn(|cx| {
|
||||||
|
EP_OUT_WAKERS[index].register(cx.waker());
|
||||||
|
let val = unsafe { T::dpram().ep_in_control(self.info.addr.index() - 1).read() };
|
||||||
|
if val.enable() {
|
||||||
|
Poll::Ready(())
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
trace!("wait_enabled IN OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, Out> {
|
||||||
|
fn info(&self) -> &EndpointInfo {
|
||||||
|
&self.info
|
||||||
|
}
|
||||||
|
|
||||||
|
type WaitEnabledFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
|
||||||
|
|
||||||
|
fn wait_enabled(&mut self) -> Self::WaitEnabledFuture<'_> {
|
||||||
|
async move {
|
||||||
|
trace!("wait_enabled OUT WAITING");
|
||||||
|
let index = self.info.addr.index();
|
||||||
|
poll_fn(|cx| {
|
||||||
|
EP_OUT_WAKERS[index].register(cx.waker());
|
||||||
|
let val = unsafe { T::dpram().ep_out_control(self.info.addr.index() - 1).read() };
|
||||||
|
if val.enable() {
|
||||||
|
Poll::Ready(())
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
trace!("wait_enabled OUT OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {
|
||||||
|
type ReadFuture<'a> = impl Future<Output = Result<usize, EndpointError>> + 'a where Self: 'a;
|
||||||
|
|
||||||
|
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||||
|
async move {
|
||||||
|
trace!("READ WAITING, buf.len() = {}", buf.len());
|
||||||
|
let index = self.info.addr.index();
|
||||||
|
let val = poll_fn(|cx| unsafe {
|
||||||
|
EP_OUT_WAKERS[index].register(cx.waker());
|
||||||
|
let val = T::dpram().ep_out_buffer_control(index).read();
|
||||||
|
if val.available(0) {
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let rx_len = val.length(0) as usize;
|
||||||
|
if rx_len > buf.len() {
|
||||||
|
return Err(EndpointError::BufferOverflow);
|
||||||
|
}
|
||||||
|
self.buf.read(&mut buf[..rx_len]);
|
||||||
|
|
||||||
|
trace!("READ OK, rx_len = {}", rx_len);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let pid = !val.pid(0);
|
||||||
|
T::dpram().ep_out_buffer_control(index).write(|w| {
|
||||||
|
w.set_pid(0, pid);
|
||||||
|
w.set_length(0, self.info.max_packet_size);
|
||||||
|
});
|
||||||
|
cortex_m::asm::delay(12);
|
||||||
|
T::dpram().ep_out_buffer_control(index).write(|w| {
|
||||||
|
w.set_pid(0, pid);
|
||||||
|
w.set_length(0, self.info.max_packet_size);
|
||||||
|
w.set_available(0, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(rx_len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> {
|
||||||
|
type WriteFuture<'a> = impl Future<Output = Result<(), EndpointError>> + 'a where Self: 'a;
|
||||||
|
|
||||||
|
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||||
|
async move {
|
||||||
|
if buf.len() > self.info.max_packet_size as usize {
|
||||||
|
return Err(EndpointError::BufferOverflow);
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!("WRITE WAITING");
|
||||||
|
|
||||||
|
let index = self.info.addr.index();
|
||||||
|
let val = poll_fn(|cx| unsafe {
|
||||||
|
EP_IN_WAKERS[index].register(cx.waker());
|
||||||
|
let val = T::dpram().ep_in_buffer_control(index).read();
|
||||||
|
if val.available(0) {
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
self.buf.write(buf);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let pid = !val.pid(0);
|
||||||
|
T::dpram().ep_in_buffer_control(index).write(|w| {
|
||||||
|
w.set_pid(0, pid);
|
||||||
|
w.set_length(0, buf.len() as _);
|
||||||
|
w.set_full(0, true);
|
||||||
|
});
|
||||||
|
cortex_m::asm::delay(12);
|
||||||
|
T::dpram().ep_in_buffer_control(index).write(|w| {
|
||||||
|
w.set_pid(0, pid);
|
||||||
|
w.set_length(0, buf.len() as _);
|
||||||
|
w.set_full(0, true);
|
||||||
|
w.set_available(0, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!("WRITE OK");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ControlPipe<'d, T: Instance> {
|
||||||
|
_phantom: PhantomData<&'d mut T>,
|
||||||
|
max_packet_size: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> {
|
||||||
|
type SetupFuture<'a> = impl Future<Output = [u8;8]> + 'a where Self: 'a;
|
||||||
|
type DataOutFuture<'a> = impl Future<Output = Result<usize, EndpointError>> + 'a where Self: 'a;
|
||||||
|
type DataInFuture<'a> = impl Future<Output = Result<(), EndpointError>> + 'a where Self: 'a;
|
||||||
|
type AcceptFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
|
||||||
|
type RejectFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
|
||||||
|
|
||||||
|
fn max_packet_size(&self) -> usize {
|
||||||
|
64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup<'a>(&'a mut self) -> Self::SetupFuture<'a> {
|
||||||
|
async move {
|
||||||
|
loop {
|
||||||
|
trace!("SETUP read waiting");
|
||||||
|
let regs = T::regs();
|
||||||
|
unsafe { regs.inte().write_set(|w| w.set_setup_req(true)) };
|
||||||
|
|
||||||
|
poll_fn(|cx| unsafe {
|
||||||
|
EP_OUT_WAKERS[0].register(cx.waker());
|
||||||
|
let regs = T::regs();
|
||||||
|
if regs.sie_status().read().setup_rec() {
|
||||||
|
Poll::Ready(())
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let mut buf = [0; 8];
|
||||||
|
EndpointBuffer::<T>::new(0, 8).read(&mut buf);
|
||||||
|
|
||||||
|
let regs = T::regs();
|
||||||
|
unsafe {
|
||||||
|
regs.sie_status().write(|w| w.set_setup_rec(true));
|
||||||
|
|
||||||
|
// set PID to 0, so (after toggling) first DATA is PID 1
|
||||||
|
T::dpram().ep_in_buffer_control(0).write(|w| w.set_pid(0, false));
|
||||||
|
T::dpram().ep_out_buffer_control(0).write(|w| w.set_pid(0, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!("SETUP read ok");
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_out<'a>(&'a mut self, buf: &'a mut [u8], _first: bool, _last: bool) -> Self::DataOutFuture<'a> {
|
||||||
|
async move {
|
||||||
|
unsafe {
|
||||||
|
let bufcontrol = T::dpram().ep_out_buffer_control(0);
|
||||||
|
let pid = !bufcontrol.read().pid(0);
|
||||||
|
bufcontrol.write(|w| {
|
||||||
|
w.set_length(0, self.max_packet_size);
|
||||||
|
w.set_pid(0, pid);
|
||||||
|
});
|
||||||
|
cortex_m::asm::delay(12);
|
||||||
|
bufcontrol.write(|w| {
|
||||||
|
w.set_length(0, self.max_packet_size);
|
||||||
|
w.set_pid(0, pid);
|
||||||
|
w.set_available(0, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!("control: data_out len={} first={} last={}", buf.len(), _first, _last);
|
||||||
|
let val = poll_fn(|cx| unsafe {
|
||||||
|
EP_OUT_WAKERS[0].register(cx.waker());
|
||||||
|
let val = T::dpram().ep_out_buffer_control(0).read();
|
||||||
|
if val.available(0) {
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let rx_len = val.length(0) as _;
|
||||||
|
trace!("control data_out DONE, rx_len = {}", rx_len);
|
||||||
|
|
||||||
|
if rx_len > buf.len() {
|
||||||
|
return Err(EndpointError::BufferOverflow);
|
||||||
|
}
|
||||||
|
EndpointBuffer::<T>::new(0x100, 64).read(&mut buf[..rx_len]);
|
||||||
|
|
||||||
|
Ok(rx_len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_in<'a>(&'a mut self, buf: &'a [u8], _first: bool, _last: bool) -> Self::DataInFuture<'a> {
|
||||||
|
async move {
|
||||||
|
trace!("control: data_in len={} first={} last={}", buf.len(), _first, _last);
|
||||||
|
|
||||||
|
if buf.len() > 64 {
|
||||||
|
return Err(EndpointError::BufferOverflow);
|
||||||
|
}
|
||||||
|
EndpointBuffer::<T>::new(0x100, 64).write(buf);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let bufcontrol = T::dpram().ep_in_buffer_control(0);
|
||||||
|
let pid = !bufcontrol.read().pid(0);
|
||||||
|
bufcontrol.write(|w| {
|
||||||
|
w.set_length(0, buf.len() as _);
|
||||||
|
w.set_pid(0, pid);
|
||||||
|
w.set_full(0, true);
|
||||||
|
});
|
||||||
|
cortex_m::asm::delay(12);
|
||||||
|
bufcontrol.write(|w| {
|
||||||
|
w.set_length(0, buf.len() as _);
|
||||||
|
w.set_pid(0, pid);
|
||||||
|
w.set_full(0, true);
|
||||||
|
w.set_available(0, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
poll_fn(|cx| unsafe {
|
||||||
|
EP_IN_WAKERS[0].register(cx.waker());
|
||||||
|
let bufcontrol = T::dpram().ep_in_buffer_control(0);
|
||||||
|
if bufcontrol.read().available(0) {
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
trace!("control: data_in DONE");
|
||||||
|
|
||||||
|
if _last {
|
||||||
|
// prepare status phase right away.
|
||||||
|
unsafe {
|
||||||
|
let bufcontrol = T::dpram().ep_out_buffer_control(0);
|
||||||
|
bufcontrol.write(|w| {
|
||||||
|
w.set_length(0, 0);
|
||||||
|
w.set_pid(0, true);
|
||||||
|
});
|
||||||
|
cortex_m::asm::delay(12);
|
||||||
|
bufcontrol.write(|w| {
|
||||||
|
w.set_length(0, 0);
|
||||||
|
w.set_pid(0, true);
|
||||||
|
w.set_available(0, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn accept<'a>(&'a mut self) -> Self::AcceptFuture<'a> {
|
||||||
|
async move {
|
||||||
|
trace!("control: accept");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let bufcontrol = T::dpram().ep_in_buffer_control(0);
|
||||||
|
bufcontrol.write(|w| {
|
||||||
|
w.set_length(0, 0);
|
||||||
|
w.set_pid(0, true);
|
||||||
|
w.set_full(0, true);
|
||||||
|
});
|
||||||
|
cortex_m::asm::delay(12);
|
||||||
|
bufcontrol.write(|w| {
|
||||||
|
w.set_length(0, 0);
|
||||||
|
w.set_pid(0, true);
|
||||||
|
w.set_full(0, true);
|
||||||
|
w.set_available(0, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reject<'a>(&'a mut self) -> Self::RejectFuture<'a> {
|
||||||
|
async move {
|
||||||
|
trace!("control: reject");
|
||||||
|
|
||||||
|
let regs = T::regs();
|
||||||
|
unsafe {
|
||||||
|
regs.ep_stall_arm().write_set(|w| {
|
||||||
|
w.set_ep0_in(true);
|
||||||
|
w.set_ep0_out(true);
|
||||||
|
});
|
||||||
|
T::dpram().ep_out_buffer_control(0).write(|w| w.set_stall(true));
|
||||||
|
T::dpram().ep_in_buffer_control(0).write(|w| w.set_stall(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,10 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de
|
||||||
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
|
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
|
||||||
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||||
embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] }
|
embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] }
|
||||||
|
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||||
|
embassy-usb-serial = { version = "0.1.0", path = "../../embassy-usb-serial", features = ["defmt"] }
|
||||||
|
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] }
|
||||||
|
embassy-usb-ncm = { version = "0.1.0", path = "../../embassy-usb-ncm", features = ["defmt"] }
|
||||||
|
|
||||||
defmt = "0.3"
|
defmt = "0.3"
|
||||||
defmt-rtt = "0.3"
|
defmt-rtt = "0.3"
|
||||||
|
@ -25,3 +29,5 @@ byte-slice-cast = { version = "1.2.0", default-features = false }
|
||||||
|
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" }
|
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" }
|
||||||
embedded-hal-async = { version = "0.1.0-alpha.1" }
|
embedded-hal-async = { version = "0.1.0-alpha.1" }
|
||||||
|
embedded-io = { version = "0.3.0", features = ["async", "defmt"] }
|
||||||
|
static_cell = "1.0.0"
|
||||||
|
|
264
examples/rp/src/bin/usb_ethernet.rs
Normal file
264
examples/rp/src/bin/usb_ethernet.rs
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(generic_associated_types)]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use core::task::Waker;
|
||||||
|
|
||||||
|
use defmt::*;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_net::tcp::TcpSocket;
|
||||||
|
use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources};
|
||||||
|
use embassy_rp::usb::Driver;
|
||||||
|
use embassy_rp::{interrupt, peripherals};
|
||||||
|
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
|
||||||
|
use embassy_sync::channel::Channel;
|
||||||
|
use embassy_usb::{Builder, Config, UsbDevice};
|
||||||
|
use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State};
|
||||||
|
use embedded_io::asynch::{Read, Write};
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
type MyDriver = Driver<'static, peripherals::USB>;
|
||||||
|
|
||||||
|
macro_rules! singleton {
|
||||||
|
($val:expr) => {{
|
||||||
|
type T = impl Sized;
|
||||||
|
static STATIC_CELL: StaticCell<T> = StaticCell::new();
|
||||||
|
STATIC_CELL.init_with(move || $val)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! {
|
||||||
|
device.run().await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn usb_ncm_rx_task(mut class: Receiver<'static, MyDriver>) {
|
||||||
|
loop {
|
||||||
|
warn!("WAITING for connection");
|
||||||
|
LINK_UP.store(false, Ordering::Relaxed);
|
||||||
|
|
||||||
|
class.wait_connection().await.unwrap();
|
||||||
|
|
||||||
|
warn!("Connected");
|
||||||
|
LINK_UP.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut p = unwrap!(PacketBox::new(embassy_net::Packet::new()));
|
||||||
|
let n = match class.read_packet(&mut p[..]).await {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(e) => {
|
||||||
|
warn!("error reading packet: {:?}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let buf = p.slice(0..n);
|
||||||
|
if RX_CHANNEL.try_send(buf).is_err() {
|
||||||
|
warn!("Failed pushing rx'd packet to channel.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) {
|
||||||
|
loop {
|
||||||
|
let pkt = TX_CHANNEL.recv().await;
|
||||||
|
if let Err(e) = class.write_packet(&pkt[..]).await {
|
||||||
|
warn!("Failed to TX packet: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn net_task(stack: &'static Stack<Device>) -> ! {
|
||||||
|
stack.run().await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(spawner: Spawner) {
|
||||||
|
let p = embassy_rp::init(Default::default());
|
||||||
|
|
||||||
|
// Create the driver, from the HAL.
|
||||||
|
let irq = interrupt::take!(USBCTRL_IRQ);
|
||||||
|
let driver = Driver::new(p.USB, irq);
|
||||||
|
|
||||||
|
// Create embassy-usb Config
|
||||||
|
let mut config = Config::new(0xc0de, 0xcafe);
|
||||||
|
config.manufacturer = Some("Embassy");
|
||||||
|
config.product = Some("USB-Ethernet example");
|
||||||
|
config.serial_number = Some("12345678");
|
||||||
|
config.max_power = 100;
|
||||||
|
config.max_packet_size_0 = 64;
|
||||||
|
|
||||||
|
// Required for Windows support.
|
||||||
|
config.composite_with_iads = true;
|
||||||
|
config.device_class = 0xEF;
|
||||||
|
config.device_sub_class = 0x02;
|
||||||
|
config.device_protocol = 0x01;
|
||||||
|
|
||||||
|
struct Resources {
|
||||||
|
device_descriptor: [u8; 256],
|
||||||
|
config_descriptor: [u8; 256],
|
||||||
|
bos_descriptor: [u8; 256],
|
||||||
|
control_buf: [u8; 128],
|
||||||
|
serial_state: State<'static>,
|
||||||
|
}
|
||||||
|
let res: &mut Resources = singleton!(Resources {
|
||||||
|
device_descriptor: [0; 256],
|
||||||
|
config_descriptor: [0; 256],
|
||||||
|
bos_descriptor: [0; 256],
|
||||||
|
control_buf: [0; 128],
|
||||||
|
serial_state: State::new(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create embassy-usb DeviceBuilder using the driver and config.
|
||||||
|
let mut builder = Builder::new(
|
||||||
|
driver,
|
||||||
|
config,
|
||||||
|
&mut res.device_descriptor,
|
||||||
|
&mut res.config_descriptor,
|
||||||
|
&mut res.bos_descriptor,
|
||||||
|
&mut res.control_buf,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
// WARNINGS for Android ethernet tethering:
|
||||||
|
// - On Pixel 4a, it refused to work on Android 11, worked on Android 12.
|
||||||
|
// - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte),
|
||||||
|
// it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled.
|
||||||
|
// This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417
|
||||||
|
// and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757
|
||||||
|
|
||||||
|
// Our MAC addr.
|
||||||
|
let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC];
|
||||||
|
// Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has.
|
||||||
|
let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88];
|
||||||
|
|
||||||
|
// Create classes on the builder.
|
||||||
|
let class = CdcNcmClass::new(&mut builder, &mut res.serial_state, host_mac_addr, 64);
|
||||||
|
|
||||||
|
// Build the builder.
|
||||||
|
let usb = builder.build();
|
||||||
|
|
||||||
|
unwrap!(spawner.spawn(usb_task(usb)));
|
||||||
|
|
||||||
|
let (tx, rx) = class.split();
|
||||||
|
unwrap!(spawner.spawn(usb_ncm_rx_task(rx)));
|
||||||
|
unwrap!(spawner.spawn(usb_ncm_tx_task(tx)));
|
||||||
|
|
||||||
|
let config = embassy_net::ConfigStrategy::Dhcp;
|
||||||
|
//let config = embassy_net::ConfigStrategy::Static(embassy_net::Config {
|
||||||
|
// address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
|
||||||
|
// dns_servers: Vec::new(),
|
||||||
|
// gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
|
||||||
|
//});
|
||||||
|
|
||||||
|
// Generate random seed
|
||||||
|
let seed = 1234; // guaranteed random, chosen by a fair dice roll
|
||||||
|
|
||||||
|
// Init network stack
|
||||||
|
let device = Device { mac_addr: our_mac_addr };
|
||||||
|
let stack = &*singleton!(Stack::new(
|
||||||
|
device,
|
||||||
|
config,
|
||||||
|
singleton!(StackResources::<1, 2, 8>::new()),
|
||||||
|
seed
|
||||||
|
));
|
||||||
|
|
||||||
|
unwrap!(spawner.spawn(net_task(stack)));
|
||||||
|
|
||||||
|
// And now we can use it!
|
||||||
|
|
||||||
|
let mut rx_buffer = [0; 4096];
|
||||||
|
let mut tx_buffer = [0; 4096];
|
||||||
|
let mut buf = [0; 4096];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
|
||||||
|
socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
|
||||||
|
|
||||||
|
info!("Listening on TCP:1234...");
|
||||||
|
if let Err(e) = socket.accept(1234).await {
|
||||||
|
warn!("accept error: {:?}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Received connection from {:?}", socket.remote_endpoint());
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let n = match socket.read(&mut buf).await {
|
||||||
|
Ok(0) => {
|
||||||
|
warn!("read EOF");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(e) => {
|
||||||
|
warn!("read error: {:?}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
info!("rxd {:02x}", &buf[..n]);
|
||||||
|
|
||||||
|
match socket.write_all(&buf[..n]).await {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(e) => {
|
||||||
|
warn!("write error: {:?}", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static TX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new();
|
||||||
|
static RX_CHANNEL: Channel<ThreadModeRawMutex, PacketBuf, 8> = Channel::new();
|
||||||
|
static LINK_UP: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
struct Device {
|
||||||
|
mac_addr: [u8; 6],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl embassy_net::Device for Device {
|
||||||
|
fn register_waker(&mut self, waker: &Waker) {
|
||||||
|
// loopy loopy wakey wakey
|
||||||
|
waker.wake_by_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn link_state(&mut self) -> embassy_net::LinkState {
|
||||||
|
match LINK_UP.load(Ordering::Relaxed) {
|
||||||
|
true => embassy_net::LinkState::Up,
|
||||||
|
false => embassy_net::LinkState::Down,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn capabilities(&self) -> embassy_net::DeviceCapabilities {
|
||||||
|
let mut caps = embassy_net::DeviceCapabilities::default();
|
||||||
|
caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header
|
||||||
|
caps.medium = embassy_net::Medium::Ethernet;
|
||||||
|
caps
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_transmit_ready(&mut self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transmit(&mut self, pkt: PacketBuf) {
|
||||||
|
if TX_CHANNEL.try_send(pkt).is_err() {
|
||||||
|
warn!("TX failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive<'a>(&mut self) -> Option<PacketBuf> {
|
||||||
|
RX_CHANNEL.try_recv().ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ethernet_address(&self) -> [u8; 6] {
|
||||||
|
self.mac_addr
|
||||||
|
}
|
||||||
|
}
|
103
examples/rp/src/bin/usb_serial.rs
Normal file
103
examples/rp/src/bin/usb_serial.rs
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(generic_associated_types)]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
|
use defmt::{info, panic};
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_rp::interrupt;
|
||||||
|
use embassy_rp::usb::{Driver, Instance};
|
||||||
|
use embassy_usb::driver::EndpointError;
|
||||||
|
use embassy_usb::{Builder, Config};
|
||||||
|
use embassy_usb_serial::{CdcAcmClass, State};
|
||||||
|
use futures::future::join;
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
info!("Hello there!");
|
||||||
|
|
||||||
|
let p = embassy_rp::init(Default::default());
|
||||||
|
|
||||||
|
// Create the driver, from the HAL.
|
||||||
|
let irq = interrupt::take!(USBCTRL_IRQ);
|
||||||
|
let driver = Driver::new(p.USB, irq);
|
||||||
|
|
||||||
|
// Create embassy-usb Config
|
||||||
|
let mut config = Config::new(0xc0de, 0xcafe);
|
||||||
|
config.manufacturer = Some("Embassy");
|
||||||
|
config.product = Some("USB-serial example");
|
||||||
|
config.serial_number = Some("12345678");
|
||||||
|
config.max_power = 100;
|
||||||
|
config.max_packet_size_0 = 64;
|
||||||
|
|
||||||
|
// Required for windows compatiblity.
|
||||||
|
// https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
|
||||||
|
config.device_class = 0xEF;
|
||||||
|
config.device_sub_class = 0x02;
|
||||||
|
config.device_protocol = 0x01;
|
||||||
|
config.composite_with_iads = true;
|
||||||
|
|
||||||
|
// Create embassy-usb DeviceBuilder using the driver and config.
|
||||||
|
// It needs some buffers for building the descriptors.
|
||||||
|
let mut device_descriptor = [0; 256];
|
||||||
|
let mut config_descriptor = [0; 256];
|
||||||
|
let mut bos_descriptor = [0; 256];
|
||||||
|
let mut control_buf = [0; 64];
|
||||||
|
|
||||||
|
let mut state = State::new();
|
||||||
|
|
||||||
|
let mut builder = Builder::new(
|
||||||
|
driver,
|
||||||
|
config,
|
||||||
|
&mut device_descriptor,
|
||||||
|
&mut config_descriptor,
|
||||||
|
&mut bos_descriptor,
|
||||||
|
&mut control_buf,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create classes on the builder.
|
||||||
|
let mut class = CdcAcmClass::new(&mut builder, &mut state, 64);
|
||||||
|
|
||||||
|
// Build the builder.
|
||||||
|
let mut usb = builder.build();
|
||||||
|
|
||||||
|
// Run the USB device.
|
||||||
|
let usb_fut = usb.run();
|
||||||
|
|
||||||
|
// Do stuff with the class!
|
||||||
|
let echo_fut = async {
|
||||||
|
loop {
|
||||||
|
class.wait_connection().await;
|
||||||
|
info!("Connected");
|
||||||
|
let _ = echo(&mut class).await;
|
||||||
|
info!("Disconnected");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Run everything concurrently.
|
||||||
|
// If we had made everything `'static` above instead, we could do this using separate tasks instead.
|
||||||
|
join(usb_fut, echo_fut).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Disconnected {}
|
||||||
|
|
||||||
|
impl From<EndpointError> for Disconnected {
|
||||||
|
fn from(val: EndpointError) -> Self {
|
||||||
|
match val {
|
||||||
|
EndpointError::BufferOverflow => panic!("Buffer overflow"),
|
||||||
|
EndpointError::Disabled => Disconnected {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> {
|
||||||
|
let mut buf = [0; 64];
|
||||||
|
loop {
|
||||||
|
let n = class.read_packet(&mut buf).await?;
|
||||||
|
let data = &buf[..n];
|
||||||
|
info!("data: {:x}", data);
|
||||||
|
class.write_packet(data).await?;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue