diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index eb460e738..8a341b8f7 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -69,6 +69,7 @@ jobs: builder ./embassy-futures crates/embassy-futures/git.zup builder ./embassy-lora crates/embassy-lora/git.zup builder ./embassy-net crates/embassy-net/git.zup + builder ./embassy-net-driver crates/embassy-net-driver/git.zup builder ./embassy-nrf crates/embassy-nrf/git.zup builder ./embassy-rp crates/embassy-rp/git.zup builder ./embassy-sync crates/embassy-sync/git.zup diff --git a/.vscode/settings.json b/.vscode/settings.json index fd2e4afb1..086f435da 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,28 +4,21 @@ "rust-analyzer.checkOnSave.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true, "rust-analyzer.procMacro.enable": true, - //"rust-analyzer.cargo.target": "thumbv7em-none-eabi", - "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.cargo.target": "thumbv7em-none-eabi", + //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", "rust-analyzer.cargo.features": [ - // These are needed to prevent embassy-net from failing to build - //"embassy-net/medium-ethernet", - //"embassy-net/tcp", - //"embassy-net/pool-16", - //"time-tick-16mhz", - //"defmt-timestamp-uptime", - //"nightly", - //"unstable-traits", + "nightly", ], "rust-analyzer.linkedProjects": [ // Declare for the target you wish to develop - //"embassy-executor/Cargo.toml", - //"embassy-sync/Cargo.toml", - //"examples/nrf/Cargo.toml", + // "embassy-executor/Cargo.toml", + // "embassy-sync/Cargo.toml", + "examples/nrf/Cargo.toml", // "examples/nrf-rtos-trace/Cargo.toml", // "examples/rp/Cargo.toml", // "examples/std/Cargo.toml", // "examples/stm32f0/Cargo.toml", - //"examples/stm32f1/Cargo.toml", + // "examples/stm32f1/Cargo.toml", // "examples/stm32f2/Cargo.toml", // "examples/stm32f3/Cargo.toml", // "examples/stm32f4/Cargo.toml", @@ -36,7 +29,7 @@ // "examples/stm32l0/Cargo.toml", // "examples/stm32l1/Cargo.toml", // "examples/stm32l4/Cargo.toml", - "examples/stm32l5/Cargo.toml", + // "examples/stm32l5/Cargo.toml", // "examples/stm32u5/Cargo.toml", // "examples/stm32wb/Cargo.toml", // "examples/stm32wb55/Cargo.toml", diff --git a/ci.sh b/ci.sh index 9f79c87b9..14a48454d 100755 --- a/ci.sh +++ b/ci.sh @@ -38,10 +38,10 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16 \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,unstable-traits \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,nightly \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,unstable-traits,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits,nightly \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \ diff --git a/ci_stable.sh b/ci_stable.sh index 0332c3faf..a1d507d71 100755 --- a/ci_stable.sh +++ b/ci_stable.sh @@ -13,8 +13,8 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16 \ - --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,unstable-traits \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \ diff --git a/embassy-net-driver-channel/Cargo.toml b/embassy-net-driver-channel/Cargo.toml new file mode 100644 index 000000000..700a4e8a0 --- /dev/null +++ b/embassy-net-driver-channel/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "embassy-net-driver-channel" +version = "0.1.0" +edition = "2021" + +[dependencies] +defmt = { version = "0.3", optional = true } +log = { version = "0.4.14", optional = true } + +embassy-sync = { version = "0.1.0", path = "../embassy-sync" } +embassy-futures = { version = "0.1.0", path = "../embassy-futures" } +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } diff --git a/embassy-net-driver-channel/src/fmt.rs b/embassy-net-driver-channel/src/fmt.rs new file mode 100644 index 000000000..066970813 --- /dev/null +++ b/embassy-net-driver-channel/src/fmt.rs @@ -0,0 +1,225 @@ +#![macro_use] +#![allow(unused_macros)] + +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You may not enable both `defmt` and `log` features."); + +macro_rules! assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert!($($x)*); + } + }; +} + +macro_rules! assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_eq!($($x)*); + } + }; +} + +macro_rules! assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_ne!($($x)*); + } + }; +} + +macro_rules! debug_assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert!($($x)*); + } + }; +} + +macro_rules! debug_assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_eq!($($x)*); + } + }; +} + +macro_rules! debug_assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_ne!($($x)*); + } + }; +} + +macro_rules! todo { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::todo!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::todo!($($x)*); + } + }; +} + +macro_rules! unreachable { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::unreachable!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::unreachable!($($x)*); + } + }; +} + +macro_rules! panic { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::panic!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::panic!($($x)*); + } + }; +} + +macro_rules! trace { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::trace!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::trace!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! debug { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::debug!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::debug!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! info { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::info!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::info!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! warn { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::warn!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::warn!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! error { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::error!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::error!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unwrap { + ($($x:tt)*) => { + ::defmt::unwrap!($($x)*) + }; +} + +#[cfg(not(feature = "defmt"))] +macro_rules! unwrap { + ($arg:expr) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); + } + } + }; + ($arg:expr, $($msg:expr),+ $(,)? ) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); + } + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct NoneError; + +pub trait Try { + type Ok; + type Error; + fn into_result(self) -> Result; +} + +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } +} + +impl Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } +} diff --git a/embassy-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs new file mode 100644 index 000000000..369dc5a9d --- /dev/null +++ b/embassy-net-driver-channel/src/lib.rs @@ -0,0 +1,525 @@ +#![no_std] + +// must go first! +mod fmt; + +use core::cell::RefCell; +use core::mem::MaybeUninit; +use core::task::{Context, Poll}; + +pub use embassy_net_driver as driver; +use embassy_net_driver::{Capabilities, LinkState, Medium}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::waitqueue::WakerRegistration; + +pub struct State { + rx: [PacketBuf; N_RX], + tx: [PacketBuf; N_TX], + inner: MaybeUninit>, +} + +impl State { + const NEW_PACKET: PacketBuf = PacketBuf::new(); + + pub const fn new() -> Self { + Self { + rx: [Self::NEW_PACKET; N_RX], + tx: [Self::NEW_PACKET; N_TX], + inner: MaybeUninit::uninit(), + } + } +} + +struct StateInner<'d, const MTU: usize> { + rx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, + tx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, + link_state: Mutex>, +} + +/// State of the LinkState +struct LinkStateState { + state: LinkState, + waker: WakerRegistration, +} + +pub struct Runner<'d, const MTU: usize> { + tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, + rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, + link_state: &'d Mutex>, +} + +pub struct RxRunner<'d, const MTU: usize> { + rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, + link_state: &'d Mutex>, +} + +pub struct TxRunner<'d, const MTU: usize> { + tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, +} + +impl<'d, const MTU: usize> Runner<'d, MTU> { + pub fn split(self) -> (RxRunner<'d, MTU>, TxRunner<'d, MTU>) { + ( + RxRunner { + link_state: self.link_state, + rx_chan: self.rx_chan, + }, + TxRunner { tx_chan: self.tx_chan }, + ) + } + + pub fn set_link_state(&mut self, state: LinkState) { + self.link_state.lock(|s| { + let s = &mut *s.borrow_mut(); + s.state = state; + s.waker.wake(); + }); + } + + pub async fn rx_buf(&mut self) -> &mut [u8] { + let p = self.rx_chan.send().await; + &mut p.buf + } + + pub fn try_rx_buf(&mut self) -> Option<&mut [u8]> { + let p = self.rx_chan.try_send()?; + Some(&mut p.buf) + } + + pub fn poll_rx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { + match self.rx_chan.poll_send(cx) { + Poll::Ready(p) => Poll::Ready(&mut p.buf), + Poll::Pending => Poll::Pending, + } + } + + pub fn rx_done(&mut self, len: usize) { + let p = self.rx_chan.try_send().unwrap(); + p.len = len; + self.rx_chan.send_done(); + } + + pub async fn tx_buf(&mut self) -> &mut [u8] { + let p = self.tx_chan.recv().await; + &mut p.buf[..p.len] + } + + pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> { + let p = self.tx_chan.try_recv()?; + Some(&mut p.buf[..p.len]) + } + + pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { + match self.tx_chan.poll_recv(cx) { + Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]), + Poll::Pending => Poll::Pending, + } + } + + pub fn tx_done(&mut self) { + self.tx_chan.recv_done(); + } +} + +impl<'d, const MTU: usize> RxRunner<'d, MTU> { + pub fn set_link_state(&mut self, state: LinkState) { + self.link_state.lock(|s| { + let s = &mut *s.borrow_mut(); + s.state = state; + s.waker.wake(); + }); + } + + pub async fn rx_buf(&mut self) -> &mut [u8] { + let p = self.rx_chan.send().await; + &mut p.buf + } + + pub fn try_rx_buf(&mut self) -> Option<&mut [u8]> { + let p = self.rx_chan.try_send()?; + Some(&mut p.buf) + } + + pub fn poll_rx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { + match self.rx_chan.poll_send(cx) { + Poll::Ready(p) => Poll::Ready(&mut p.buf), + Poll::Pending => Poll::Pending, + } + } + + pub fn rx_done(&mut self, len: usize) { + let p = self.rx_chan.try_send().unwrap(); + p.len = len; + self.rx_chan.send_done(); + } +} + +impl<'d, const MTU: usize> TxRunner<'d, MTU> { + pub async fn tx_buf(&mut self) -> &mut [u8] { + let p = self.tx_chan.recv().await; + &mut p.buf[..p.len] + } + + pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> { + let p = self.tx_chan.try_recv()?; + Some(&mut p.buf[..p.len]) + } + + pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> { + match self.tx_chan.poll_recv(cx) { + Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]), + Poll::Pending => Poll::Pending, + } + } + + pub fn tx_done(&mut self) { + self.tx_chan.recv_done(); + } +} + +pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>( + state: &'d mut State, + ethernet_address: [u8; 6], +) -> (Runner<'d, MTU>, Device<'d, MTU>) { + let mut caps = Capabilities::default(); + caps.max_transmission_unit = MTU; + caps.medium = Medium::Ethernet; + + // safety: this is a self-referential struct, however: + // - it can't move while the `'d` borrow is active. + // - when the borrow ends, the dangling references inside the MaybeUninit will never be used again. + let state_uninit: *mut MaybeUninit> = + (&mut state.inner as *mut MaybeUninit>).cast(); + let state = unsafe { &mut *state_uninit }.write(StateInner { + rx: zerocopy_channel::Channel::new(&mut state.rx[..]), + tx: zerocopy_channel::Channel::new(&mut state.tx[..]), + link_state: Mutex::new(RefCell::new(LinkStateState { + state: LinkState::Down, + waker: WakerRegistration::new(), + })), + }); + + let (rx_sender, rx_receiver) = state.rx.split(); + let (tx_sender, tx_receiver) = state.tx.split(); + + ( + Runner { + tx_chan: tx_receiver, + rx_chan: rx_sender, + link_state: &state.link_state, + }, + Device { + caps, + ethernet_address, + link_state: &state.link_state, + rx: rx_receiver, + tx: tx_sender, + }, + ) +} + +pub struct PacketBuf { + len: usize, + buf: [u8; MTU], +} + +impl PacketBuf { + pub const fn new() -> Self { + Self { len: 0, buf: [0; MTU] } + } +} + +pub struct Device<'d, const MTU: usize> { + rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, + tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, + link_state: &'d Mutex>, + caps: Capabilities, + ethernet_address: [u8; 6], +} + +impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { + type RxToken<'a> = RxToken<'a, MTU> where Self: 'a ; + type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ; + + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + if self.rx.poll_recv(cx).is_ready() && self.tx.poll_send(cx).is_ready() { + Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() })) + } else { + None + } + } + + /// Construct a transmit token. + fn transmit(&mut self, cx: &mut Context) -> Option> { + if self.tx.poll_send(cx).is_ready() { + Some(TxToken { tx: self.tx.borrow() }) + } else { + None + } + } + + /// Get a description of device capabilities. + fn capabilities(&self) -> Capabilities { + self.caps.clone() + } + + fn ethernet_address(&self) -> [u8; 6] { + self.ethernet_address + } + + fn link_state(&mut self, cx: &mut Context) -> LinkState { + self.link_state.lock(|s| { + let s = &mut *s.borrow_mut(); + s.waker.register(cx.waker()); + s.state + }) + } +} + +pub struct RxToken<'a, const MTU: usize> { + rx: zerocopy_channel::Receiver<'a, NoopRawMutex, PacketBuf>, +} + +impl<'a, const MTU: usize> embassy_net_driver::RxToken for RxToken<'a, MTU> { + fn consume(mut self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.rx.try_recv()); + let r = f(&mut pkt.buf[..pkt.len]); + self.rx.recv_done(); + r + } +} + +pub struct TxToken<'a, const MTU: usize> { + tx: zerocopy_channel::Sender<'a, NoopRawMutex, PacketBuf>, +} + +impl<'a, const MTU: usize> embassy_net_driver::TxToken for TxToken<'a, MTU> { + fn consume(mut self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.tx.try_send()); + let r = f(&mut pkt.buf[..len]); + pkt.len = len; + self.tx.send_done(); + r + } +} + +mod zerocopy_channel { + use core::cell::RefCell; + use core::future::poll_fn; + use core::marker::PhantomData; + use core::task::{Context, Poll}; + + use embassy_sync::blocking_mutex::raw::RawMutex; + use embassy_sync::blocking_mutex::Mutex; + use embassy_sync::waitqueue::WakerRegistration; + + pub struct Channel<'a, M: RawMutex, T> { + buf: *mut T, + phantom: PhantomData<&'a mut T>, + state: Mutex>, + } + + impl<'a, M: RawMutex, T> Channel<'a, M, T> { + pub fn new(buf: &'a mut [T]) -> Self { + let len = buf.len(); + assert!(len != 0); + + Self { + buf: buf.as_mut_ptr(), + phantom: PhantomData, + state: Mutex::new(RefCell::new(State { + len, + front: 0, + back: 0, + full: false, + send_waker: WakerRegistration::new(), + recv_waker: WakerRegistration::new(), + })), + } + } + + pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) { + (Sender { channel: self }, Receiver { channel: self }) + } + } + + pub struct Sender<'a, M: RawMutex, T> { + channel: &'a Channel<'a, M, T>, + } + + impl<'a, M: RawMutex, T> Sender<'a, M, T> { + pub fn borrow(&mut self) -> Sender<'_, M, T> { + Sender { channel: self.channel } + } + + pub fn try_send(&mut self) -> Option<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), + None => None, + } + }) + } + + pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), + None => { + s.recv_waker.register(cx.waker()); + Poll::Pending + } + } + }) + } + + pub async fn send(&mut self) -> &mut T { + let i = poll_fn(|cx| { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.push_index() { + Some(i) => Poll::Ready(i), + None => { + s.recv_waker.register(cx.waker()); + Poll::Pending + } + } + }) + }) + .await; + unsafe { &mut *self.channel.buf.add(i) } + } + + pub fn send_done(&mut self) { + self.channel.state.lock(|s| s.borrow_mut().push_done()) + } + } + pub struct Receiver<'a, M: RawMutex, T> { + channel: &'a Channel<'a, M, T>, + } + + impl<'a, M: RawMutex, T> Receiver<'a, M, T> { + pub fn borrow(&mut self) -> Receiver<'_, M, T> { + Receiver { channel: self.channel } + } + + pub fn try_recv(&mut self) -> Option<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), + None => None, + } + }) + } + + pub fn poll_recv(&mut self, cx: &mut Context) -> Poll<&mut T> { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), + None => { + s.send_waker.register(cx.waker()); + Poll::Pending + } + } + }) + } + + pub async fn recv(&mut self) -> &mut T { + let i = poll_fn(|cx| { + self.channel.state.lock(|s| { + let s = &mut *s.borrow_mut(); + match s.pop_index() { + Some(i) => Poll::Ready(i), + None => { + s.send_waker.register(cx.waker()); + Poll::Pending + } + } + }) + }) + .await; + unsafe { &mut *self.channel.buf.add(i) } + } + + pub fn recv_done(&mut self) { + self.channel.state.lock(|s| s.borrow_mut().pop_done()) + } + } + + struct State { + len: usize, + + /// Front index. Always 0..=(N-1) + front: usize, + /// Back index. Always 0..=(N-1). + back: usize, + + /// Used to distinguish "empty" and "full" cases when `front == back`. + /// May only be `true` if `front == back`, always `false` otherwise. + full: bool, + + send_waker: WakerRegistration, + recv_waker: WakerRegistration, + } + + impl State { + fn increment(&self, i: usize) -> usize { + if i + 1 == self.len { + 0 + } else { + i + 1 + } + } + + fn is_full(&self) -> bool { + self.full + } + + fn is_empty(&self) -> bool { + self.front == self.back && !self.full + } + + fn push_index(&mut self) -> Option { + match self.is_full() { + true => None, + false => Some(self.back), + } + } + + fn push_done(&mut self) { + assert!(!self.is_full()); + self.back = self.increment(self.back); + if self.back == self.front { + self.full = true; + } + self.send_waker.wake(); + } + + fn pop_index(&mut self) -> Option { + match self.is_empty() { + true => None, + false => Some(self.front), + } + } + + fn pop_done(&mut self) { + assert!(!self.is_empty()); + self.front = self.increment(self.front); + self.full = false; + self.recv_waker.wake(); + } + } +} diff --git a/embassy-net-driver/Cargo.toml b/embassy-net-driver/Cargo.toml new file mode 100644 index 000000000..7ab9d1194 --- /dev/null +++ b/embassy-net-driver/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "embassy-net-driver" +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" + + +[package.metadata.embassy_docs] +src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net/src/" +src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-driver/src/" +features = ["defmt"] +target = "thumbv7em-none-eabi" + +[dependencies] +defmt = { version = "0.3", optional = true } \ No newline at end of file diff --git a/embassy-net-driver/src/lib.rs b/embassy-net-driver/src/lib.rs new file mode 100644 index 000000000..a39cfecc1 --- /dev/null +++ b/embassy-net-driver/src/lib.rs @@ -0,0 +1,175 @@ +#![no_std] + +use core::task::Context; + +pub trait Driver { + type RxToken<'a>: RxToken + where + Self: 'a; + type TxToken<'a>: TxToken + where + Self: 'a; + + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>; + fn transmit(&mut self, cx: &mut Context) -> Option>; + fn link_state(&mut self, cx: &mut Context) -> LinkState; + + fn capabilities(&self) -> Capabilities; + fn ethernet_address(&self) -> [u8; 6]; +} + +impl Driver for &mut T { + type RxToken<'a> = T::RxToken<'a> + where + Self: 'a; + type TxToken<'a> = T::TxToken<'a> + where + Self: 'a; + + fn transmit(&mut self, cx: &mut Context) -> Option> { + T::transmit(self, cx) + } + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + T::receive(self, cx) + } + fn capabilities(&self) -> Capabilities { + T::capabilities(self) + } + fn link_state(&mut self, cx: &mut Context) -> LinkState { + T::link_state(self, cx) + } + fn ethernet_address(&self) -> [u8; 6] { + T::ethernet_address(self) + } +} + +/// A token to receive a single network packet. +pub trait RxToken { + /// Consumes the token to receive a single network packet. + /// + /// This method receives a packet and then calls the given closure `f` with the raw + /// packet bytes as argument. + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R; +} + +/// A token to transmit a single network packet. +pub trait TxToken { + /// Consumes the token to send a single network packet. + /// + /// This method constructs a transmit buffer of size `len` and calls the passed + /// closure `f` with a mutable reference to that buffer. The closure should construct + /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure + /// returns, the transmit buffer is sent out. + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R; +} + +/// A description of device capabilities. +/// +/// Higher-level protocols may achieve higher throughput or lower latency if they consider +/// the bandwidth or packet size limitations. +#[derive(Debug, Clone, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct Capabilities { + /// Medium of the device. + /// + /// This indicates what kind of packet the sent/received bytes are, and determines + /// some behaviors of Interface. For example, ARP/NDISC address resolution is only done + /// for Ethernet mediums. + pub medium: Medium, + + /// Maximum transmission unit. + /// + /// The network device is unable to send or receive frames larger than the value returned + /// by this function. + /// + /// For Ethernet devices, this is the maximum Ethernet frame size, including the Ethernet header (14 octets), but + /// *not* including the Ethernet FCS (4 octets). Therefore, Ethernet MTU = IP MTU + 14. + /// + /// Note that in Linux and other OSes, "MTU" is the IP MTU, not the Ethernet MTU, even for Ethernet + /// devices. This is a common source of confusion. + /// + /// Most common IP MTU is 1500. Minimum is 576 (for IPv4) or 1280 (for IPv6). Maximum is 9216 octets. + pub max_transmission_unit: usize, + + /// Maximum burst size, in terms of MTU. + /// + /// The network device is unable to send or receive bursts large than the value returned + /// by this function. + /// + /// If `None`, there is no fixed limit on burst size, e.g. if network buffers are + /// dynamically allocated. + pub max_burst_size: Option, + + /// Checksum behavior. + /// + /// If the network device is capable of verifying or computing checksums for some protocols, + /// it can request that the stack not do so in software to improve performance. + pub checksum: ChecksumCapabilities, +} + +/// Type of medium of a device. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Medium { + /// Ethernet medium. Devices of this type send and receive Ethernet frames, + /// and interfaces using it must do neighbor discovery via ARP or NDISC. + /// + /// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode. + Ethernet, + + /// IP medium. Devices of this type send and receive IP frames, without an + /// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done. + /// + /// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode. + Ip, +} + +impl Default for Medium { + fn default() -> Medium { + Medium::Ethernet + } +} + +/// A description of checksum behavior for every supported protocol. +#[derive(Debug, Clone, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct ChecksumCapabilities { + pub ipv4: Checksum, + pub udp: Checksum, + pub tcp: Checksum, + pub icmpv4: Checksum, + pub icmpv6: Checksum, +} + +/// A description of checksum behavior for a particular protocol. +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Checksum { + /// Verify checksum when receiving and compute checksum when sending. + Both, + /// Verify checksum when receiving. + Rx, + /// Compute checksum before sending. + Tx, + /// Ignore checksum completely. + None, +} + +impl Default for Checksum { + fn default() -> Checksum { + Checksum::Both + } +} + +#[derive(PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum LinkState { + Down, + Up, +} diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 2d853762e..9214fd17e 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -8,14 +8,14 @@ license = "MIT OR Apache-2.0" [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/" -features = ["pool-4", "defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip"] +features = ["defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip"] target = "thumbv7em-none-eabi" [features] default = [] std = [] -defmt = ["dep:defmt", "smoltcp/defmt"] +defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"] nightly = ["dep:embedded-io", "embedded-io?/async", "dep:embedded-nal-async"] unstable-traits = [] @@ -28,18 +28,12 @@ proto-ipv6 = ["smoltcp/proto-ipv6"] medium-ethernet = ["smoltcp/medium-ethernet"] medium-ip = ["smoltcp/medium-ip"] -pool-4 = [] -pool-8 = [] -pool-16 = [] -pool-32 = [] -pool-64 = [] -pool-128 = [] - [dependencies] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embedded-io = { version = "0.4.0", optional = true } @@ -52,6 +46,7 @@ stable_deref_trait = { version = "1.2.0", default-features = false } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } atomic-pool = "1.0" embedded-nal-async = { version = "0.3.0", optional = true } +atomic-polyfill = { version = "1.0" } [dependencies.smoltcp] version = "0.8.0" diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 5d86ca91e..44f7dc7bd 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -1,93 +1,20 @@ use core::task::Context; +use embassy_net_driver::{Capabilities, Checksum, Driver, Medium, RxToken, TxToken}; use smoltcp::phy; -pub use smoltcp::phy::{Checksum, ChecksumCapabilities, DeviceCapabilities, Medium}; -#[derive(PartialEq, Eq, Clone, Copy)] -pub enum LinkState { - Down, - Up, -} - -pub trait Device { - type RxToken<'a>: RxToken - where - Self: 'a; - type TxToken<'a>: TxToken - where - Self: 'a; - - fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>; - fn transmit(&mut self, cx: &mut Context) -> Option>; - fn link_state(&mut self, cx: &mut Context) -> LinkState; - - fn capabilities(&self) -> phy::DeviceCapabilities; - fn ethernet_address(&self) -> [u8; 6]; -} - -impl Device for &mut T { - type RxToken<'a> = T::RxToken<'a> - where - Self: 'a; - type TxToken<'a> = T::TxToken<'a> - where - Self: 'a; - - fn transmit(&mut self, cx: &mut Context) -> Option> { - T::transmit(self, cx) - } - fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - T::receive(self, cx) - } - fn capabilities(&self) -> phy::DeviceCapabilities { - T::capabilities(self) - } - fn link_state(&mut self, cx: &mut Context) -> LinkState { - T::link_state(self, cx) - } - fn ethernet_address(&self) -> [u8; 6] { - T::ethernet_address(self) - } -} - -/// A token to receive a single network packet. -pub trait RxToken { - /// Consumes the token to receive a single network packet. - /// - /// This method receives a packet and then calls the given closure `f` with the raw - /// packet bytes as argument. - fn consume(self, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R; -} - -/// A token to transmit a single network packet. -pub trait TxToken { - /// Consumes the token to send a single network packet. - /// - /// This method constructs a transmit buffer of size `len` and calls the passed - /// closure `f` with a mutable reference to that buffer. The closure should construct - /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure - /// returns, the transmit buffer is sent out. - fn consume(self, len: usize, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R; -} - -/////////////////////////// - -pub(crate) struct DeviceAdapter<'d, 'c, T> +pub(crate) struct DriverAdapter<'d, 'c, T> where - T: Device, + T: Driver, { // must be Some when actually using this to rx/tx pub cx: Option<&'d mut Context<'c>>, pub inner: &'d mut T, } -impl<'d, 'c, T> phy::Device for DeviceAdapter<'d, 'c, T> +impl<'d, 'c, T> phy::Device for DriverAdapter<'d, 'c, T> where - T: Device, + T: Driver, { type RxToken<'a> = RxTokenAdapter> where Self: 'a; type TxToken<'a> = TxTokenAdapter> where Self: 'a; @@ -105,7 +32,39 @@ where /// Get a description of device capabilities. fn capabilities(&self) -> phy::DeviceCapabilities { - self.inner.capabilities() + fn convert(c: Checksum) -> phy::Checksum { + match c { + Checksum::Both => phy::Checksum::Both, + Checksum::Tx => phy::Checksum::Tx, + Checksum::Rx => phy::Checksum::Rx, + Checksum::None => phy::Checksum::None, + } + } + let caps: Capabilities = self.inner.capabilities(); + let mut smolcaps = phy::DeviceCapabilities::default(); + + smolcaps.max_transmission_unit = caps.max_transmission_unit; + smolcaps.max_burst_size = caps.max_burst_size; + smolcaps.medium = match caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => phy::Medium::Ethernet, + #[cfg(feature = "medium-ip")] + Medium::Ip => phy::Medium::Ip, + _ => panic!( + "Unsupported medium {:?}. MAke sure to enable it in embassy-net's Cargo features.", + caps.medium + ), + }; + smolcaps.checksum.ipv4 = convert(caps.checksum.ipv4); + #[cfg(feature = "proto-ipv6")] + { + smolcaps.checksum.ipv6 = convert(caps.checksum.ipv6); + } + smolcaps.checksum.tcp = convert(caps.checksum.tcp); + smolcaps.checksum.udp = convert(caps.checksum.udp); + smolcaps.checksum.icmpv4 = convert(caps.checksum.icmpv4); + + smolcaps } } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index afe0d6da0..b58c9cf36 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -18,6 +18,7 @@ use core::cell::RefCell; use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; +use embassy_net_driver::{Driver, LinkState, Medium}; use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; use futures::pin_mut; @@ -27,8 +28,6 @@ use smoltcp::iface::SocketHandle; use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; #[cfg(feature = "medium-ethernet")] use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes}; -#[cfg(feature = "medium-ethernet")] -use smoltcp::phy::Medium; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4; // smoltcp reexports @@ -41,7 +40,7 @@ pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr}; #[cfg(feature = "udp")] pub use smoltcp::{socket::udp::PacketMetadata, wire::IpListenEndpoint}; -use crate::device::{Device, DeviceAdapter, LinkState}; +use crate::device::DriverAdapter; const LOCAL_PORT_MIN: u16 = 1025; const LOCAL_PORT_MAX: u16 = 65535; @@ -82,12 +81,12 @@ pub enum ConfigStrategy { Dhcp, } -pub struct Stack { +pub struct Stack { pub(crate) socket: RefCell, inner: RefCell>, } -struct Inner { +struct Inner { device: D, link_up: bool, config: Option, @@ -102,7 +101,7 @@ pub(crate) struct SocketStack { next_local_port: u16, } -impl Stack { +impl Stack { pub fn new( mut device: D, config: ConfigStrategy, @@ -130,7 +129,7 @@ impl Stack { b = b.routes(Routes::new(&mut resources.routes[..])); } - let iface = b.finalize(&mut DeviceAdapter { + let iface = b.finalize(&mut DriverAdapter { inner: &mut device, cx: None, }); @@ -211,7 +210,7 @@ impl SocketStack { } } -impl Inner { +impl Inner { fn apply_config(&mut self, s: &mut SocketStack, config: Config) { #[cfg(feature = "medium-ethernet")] let medium = self.device.capabilities().medium; @@ -263,7 +262,7 @@ impl Inner { s.waker.register(cx.waker()); let timestamp = instant_to_smoltcp(Instant::now()); - let mut smoldev = DeviceAdapter { + let mut smoldev = DriverAdapter { cx: Some(cx), inner: &mut self.device, }; diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 55cbda455..0fbf0c91b 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -3,12 +3,12 @@ use core::future::poll_fn; use core::mem; use core::task::Poll; +use embassy_net_driver::Driver; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::tcp; use smoltcp::time::Duration; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; -use crate::device::Device; use crate::{SocketStack, Stack}; #[derive(PartialEq, Eq, Clone, Copy, Debug)] @@ -66,7 +66,7 @@ impl<'a> TcpWriter<'a> { } impl<'a> TcpSocket<'a> { - pub fn new(stack: &'a Stack, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { + pub fn new(stack: &'a Stack, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { let s = &mut *stack.socket.borrow_mut(); let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; @@ -329,26 +329,26 @@ pub mod client { use core::cell::UnsafeCell; use core::mem::MaybeUninit; use core::ptr::NonNull; - use core::sync::atomic::{AtomicBool, Ordering}; + use atomic_polyfill::{AtomicBool, Ordering}; use embedded_nal_async::IpAddr; use super::*; /// TCP client capable of creating up to N multiple connections with tx and rx buffers according to TX_SZ and RX_SZ. - pub struct TcpClient<'d, D: Device, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> { + pub struct TcpClient<'d, D: Driver, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> { stack: &'d Stack, state: &'d TcpClientState, } - impl<'d, D: Device, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, D, N, TX_SZ, RX_SZ> { + impl<'d, D: Driver, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, D, N, TX_SZ, RX_SZ> { /// Create a new TcpClient pub fn new(stack: &'d Stack, state: &'d TcpClientState) -> Self { Self { stack, state } } } - impl<'d, D: Device, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_nal_async::TcpConnect + impl<'d, D: Driver, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_nal_async::TcpConnect for TcpClient<'d, D, N, TX_SZ, RX_SZ> { type Error = Error; @@ -386,7 +386,7 @@ pub mod client { } impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpConnection<'d, N, TX_SZ, RX_SZ> { - fn new(stack: &'d Stack, state: &'d TcpClientState) -> Result { + fn new(stack: &'d Stack, state: &'d TcpClientState) -> Result { let mut bufs = state.pool.alloc().ok_or(Error::ConnectionReset)?; Ok(Self { socket: unsafe { TcpSocket::new(stack, &mut bufs.as_mut().0, &mut bufs.as_mut().1) }, diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 2f5334df3..0ee8c6e19 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -3,11 +3,12 @@ use core::future::poll_fn; use core::mem; use core::task::Poll; +use embassy_net_driver::Driver; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::udp::{self, PacketMetadata}; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; -use crate::{Device, SocketStack, Stack}; +use crate::{SocketStack, Stack}; #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -31,7 +32,7 @@ pub struct UdpSocket<'a> { } impl<'a> UdpSocket<'a> { - pub fn new( + pub fn new( stack: &'a Stack, rx_meta: &'a mut [PacketMetadata], rx_buffer: &'a mut [u8], diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 0c491ee46..67996cca4 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -39,7 +39,7 @@ embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]} embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } -embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } +embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } @@ -75,7 +75,7 @@ quote = "1.0.15" stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]} [features] -defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt"] +defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-executor/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] sdmmc-rs = ["embedded-sdmmc"] memory-x = ["stm32-metapac/memory-x"] subghz = [] diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index fd1b48530..9f62b61ee 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -1,14 +1,17 @@ #![macro_use] -#![cfg_attr(not(feature = "embassy-net"), allow(unused))] #[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")] #[cfg_attr(eth_v2, path = "v2/mod.rs")] mod _version; pub mod generic_smi; -pub use _version::*; +use core::task::Context; + +use embassy_net_driver::{Capabilities, LinkState}; use embassy_sync::waitqueue::AtomicWaker; +pub use self::_version::*; + #[allow(unused)] const MTU: usize = 1514; const TX_BUFFER_SIZE: usize = 1514; @@ -40,92 +43,84 @@ impl PacketQueue { static WAKER: AtomicWaker = AtomicWaker::new(); -#[cfg(feature = "embassy-net")] -mod embassy_net_impl { - use core::task::Context; +impl<'d, T: Instance, P: PHY> embassy_net_driver::Driver for Ethernet<'d, T, P> { + type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; + type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; - use embassy_net::device::{Device, DeviceCapabilities, LinkState}; - - use super::*; - - impl<'d, T: Instance, P: PHY> Device for Ethernet<'d, T, P> { - type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; - type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; - - fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - WAKER.register(cx.waker()); - if self.rx.available().is_some() && self.tx.available().is_some() { - Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx })) - } else { - None - } - } - - fn transmit(&mut self, cx: &mut Context) -> Option> { - WAKER.register(cx.waker()); - if self.tx.available().is_some() { - Some(TxToken { tx: &mut self.tx }) - } else { - None - } - } - - fn capabilities(&self) -> DeviceCapabilities { - let mut caps = DeviceCapabilities::default(); - caps.max_transmission_unit = MTU; - caps.max_burst_size = Some(self.tx.len()); - caps - } - - fn link_state(&mut self, cx: &mut Context) -> LinkState { - // TODO: wake cx.waker on link state change - cx.waker().wake_by_ref(); - if P::poll_link(self) { - LinkState::Up - } else { - LinkState::Down - } - } - - fn ethernet_address(&self) -> [u8; 6] { - self.mac_addr + fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + WAKER.register(cx.waker()); + if self.rx.available().is_some() && self.tx.available().is_some() { + Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx })) + } else { + None } } - pub struct RxToken<'a, 'd> { - rx: &'a mut RDesRing<'d>, - } - - impl<'a, 'd> embassy_net::device::RxToken for RxToken<'a, 'd> { - fn consume(self, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - // NOTE(unwrap): we checked the queue wasn't full when creating the token. - let pkt = unwrap!(self.rx.available()); - let r = f(pkt); - self.rx.pop_packet(); - r + fn transmit(&mut self, cx: &mut Context) -> Option> { + WAKER.register(cx.waker()); + if self.tx.available().is_some() { + Some(TxToken { tx: &mut self.tx }) + } else { + None } } - pub struct TxToken<'a, 'd> { - tx: &'a mut TDesRing<'d>, + fn capabilities(&self) -> Capabilities { + let mut caps = Capabilities::default(); + caps.max_transmission_unit = MTU; + caps.max_burst_size = Some(self.tx.len()); + caps } - impl<'a, 'd> embassy_net::device::TxToken for TxToken<'a, 'd> { - fn consume(self, len: usize, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - // NOTE(unwrap): we checked the queue wasn't full when creating the token. - let pkt = unwrap!(self.tx.available()); - let r = f(&mut pkt[..len]); - self.tx.transmit(len); - r + fn link_state(&mut self, cx: &mut Context) -> LinkState { + // TODO: wake cx.waker on link state change + cx.waker().wake_by_ref(); + if P::poll_link(self) { + LinkState::Up + } else { + LinkState::Down } } + + fn ethernet_address(&self) -> [u8; 6] { + self.mac_addr + } } + +pub struct RxToken<'a, 'd> { + rx: &'a mut RDesRing<'d>, +} + +impl<'a, 'd> embassy_net_driver::RxToken for RxToken<'a, 'd> { + fn consume(self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.rx.available()); + let r = f(pkt); + self.rx.pop_packet(); + r + } +} + +pub struct TxToken<'a, 'd> { + tx: &'a mut TDesRing<'d>, +} + +impl<'a, 'd> embassy_net_driver::TxToken for TxToken<'a, 'd> { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + // NOTE(unwrap): we checked the queue wasn't full when creating the token. + let pkt = unwrap!(self.tx.available()); + let r = f(&mut pkt[..len]); + self.tx.transmit(len); + r + } +} + /// Station Management Interface (SMI) on an ethernet PHY /// /// # Safety diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index de36d3da1..9c0f4d66d 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -13,7 +13,7 @@ pub(crate) use self::rx_desc::{RDes, RDesRing}; pub(crate) use self::tx_desc::{TDes, TDesRing}; use super::*; use crate::gpio::sealed::{AFType, Pin as __GpioPin}; -use crate::gpio::{AnyPin, Speed}; +use crate::gpio::AnyPin; #[cfg(eth_v1a)] use crate::pac::AFIO; #[cfg(any(eth_v1b, eth_v1c))] @@ -66,7 +66,7 @@ macro_rules! config_pins { critical_section::with(|_| { $( $pin.set_as_af($pin.af_num(), AFType::OutputPushPull); - $pin.set_speed(Speed::VeryHigh); + $pin.set_speed(crate::gpio::Speed::VeryHigh); )* }) }; diff --git a/embassy-usb-driver/Cargo.toml b/embassy-usb-driver/Cargo.toml index d22bf7d72..d658f9ec7 100644 --- a/embassy-usb-driver/Cargo.toml +++ b/embassy-usb-driver/Cargo.toml @@ -14,4 +14,3 @@ target = "thumbv7em-none-eabi" [dependencies] defmt = { version = "0.3", optional = true } -log = { version = "0.4.14", optional = true } diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index 1e72ce682..1e567bb94 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -19,7 +19,7 @@ default = ["usbd-hid"] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } -embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } +embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs index 60bbfd8d4..7ecf693d2 100644 --- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs +++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs @@ -1,81 +1,45 @@ -use core::cell::RefCell; -use core::mem::MaybeUninit; -use core::task::Context; - use embassy_futures::select::{select, Either}; -use embassy_net::device::{Device as DeviceTrait, DeviceCapabilities, LinkState, Medium}; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embassy_sync::blocking_mutex::Mutex; -use embassy_sync::waitqueue::WakerRegistration; +use embassy_net_driver_channel as ch; +use embassy_net_driver_channel::driver::LinkState; use embassy_usb_driver::Driver; use super::{CdcNcmClass, Receiver, Sender}; -pub struct State<'d, const MTU: usize, const N_RX: usize, const N_TX: usize> { - rx: [PacketBuf; N_RX], - tx: [PacketBuf; N_TX], - inner: MaybeUninit>, +pub struct State { + ch_state: ch::State, } -impl<'d, const MTU: usize, const N_RX: usize, const N_TX: usize> State<'d, MTU, N_RX, N_TX> { - const NEW_PACKET: PacketBuf = PacketBuf::new(); - +impl State { pub const fn new() -> Self { Self { - rx: [Self::NEW_PACKET; N_RX], - tx: [Self::NEW_PACKET; N_TX], - inner: MaybeUninit::uninit(), + ch_state: ch::State::new(), } } } -struct StateInner<'d, const MTU: usize> { - rx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, - tx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf>, - link_state: Mutex>, -} - -/// State of the LinkState -struct LinkStateState { - state: LinkState, - waker: WakerRegistration, -} - pub struct Runner<'d, D: Driver<'d>, const MTU: usize> { tx_usb: Sender<'d, D>, - tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, rx_usb: Receiver<'d, D>, - rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, - link_state: &'d Mutex>, + ch: ch::Runner<'d, MTU>, } impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { pub async fn run(mut self) -> ! { + let (mut rx_chan, mut tx_chan) = self.ch.split(); let rx_fut = async move { loop { trace!("WAITING for connection"); - self.link_state.lock(|s| { - let s = &mut *s.borrow_mut(); - s.state = LinkState::Down; - s.waker.wake(); - }); + rx_chan.set_link_state(LinkState::Down); self.rx_usb.wait_connection().await.unwrap(); trace!("Connected"); - self.link_state.lock(|s| { - let s = &mut *s.borrow_mut(); - s.state = LinkState::Up; - s.waker.wake(); - }); + rx_chan.set_link_state(LinkState::Up); loop { - let p = self.rx_chan.send().await; - match self.rx_usb.read_packet(&mut p.buf).await { - Ok(n) => { - p.len = n; - self.rx_chan.send_done(); - } + let p = rx_chan.rx_buf().await; + match self.rx_usb.read_packet(p).await { + Ok(n) => rx_chan.rx_done(n), Err(e) => { warn!("error reading packet: {:?}", e); break; @@ -86,11 +50,11 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { }; let tx_fut = async move { loop { - let p = self.tx_chan.recv().await; - if let Err(e) = self.tx_usb.write_packet(&p.buf[..p.len]).await { + let p = tx_chan.tx_buf().await; + if let Err(e) = self.tx_usb.write_packet(p).await { warn!("Failed to TX packet: {:?}", e); } - self.tx_chan.recv_done(); + tx_chan.tx_done(); } }; match select(rx_fut, tx_fut).await { @@ -100,350 +64,26 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { } } +// would be cool to use a TAIT here, but it gives a "may not live long enough". rustc bug? +//pub type Device<'d, const MTU: usize> = impl embassy_net_driver_channel::driver::Driver + 'd; +pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>; + impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { pub fn into_embassy_net_device( self, - state: &'d mut State<'d, MTU, N_RX, N_TX>, + state: &'d mut State, ethernet_address: [u8; 6], ) -> (Runner<'d, D, MTU>, Device<'d, MTU>) { let (tx_usb, rx_usb) = self.split(); - - let mut caps = DeviceCapabilities::default(); - caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header - caps.medium = Medium::Ethernet; - - let state = state.inner.write(StateInner { - rx: zerocopy_channel::Channel::new(&mut state.rx[..]), - tx: zerocopy_channel::Channel::new(&mut state.tx[..]), - link_state: Mutex::new(RefCell::new(LinkStateState { - state: LinkState::Down, - waker: WakerRegistration::new(), - })), - }); - - let (rx_sender, rx_receiver) = state.rx.split(); - let (tx_sender, tx_receiver) = state.tx.split(); + let (runner, device) = ch::new(&mut state.ch_state, ethernet_address); ( Runner { tx_usb, - tx_chan: tx_receiver, rx_usb, - rx_chan: rx_sender, - link_state: &state.link_state, - }, - Device { - caps, - ethernet_address, - link_state: &state.link_state, - rx: rx_receiver, - tx: tx_sender, + ch: runner, }, + device, ) } } - -pub struct PacketBuf { - len: usize, - buf: [u8; MTU], -} - -impl PacketBuf { - pub const fn new() -> Self { - Self { len: 0, buf: [0; MTU] } - } -} - -pub struct Device<'d, const MTU: usize> { - rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf>, - tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf>, - link_state: &'d Mutex>, - caps: DeviceCapabilities, - ethernet_address: [u8; 6], -} - -impl<'d, const MTU: usize> DeviceTrait for Device<'d, MTU> { - type RxToken<'a> = RxToken<'a, MTU> where Self: 'a ; - type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ; - - fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - if self.rx.poll_recv(cx).is_ready() && self.tx.poll_send(cx).is_ready() { - Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() })) - } else { - None - } - } - - /// Construct a transmit token. - fn transmit(&mut self, cx: &mut Context) -> Option> { - if self.tx.poll_send(cx).is_ready() { - Some(TxToken { tx: self.tx.borrow() }) - } else { - None - } - } - - /// Get a description of device capabilities. - fn capabilities(&self) -> DeviceCapabilities { - self.caps.clone() - } - - fn ethernet_address(&self) -> [u8; 6] { - self.ethernet_address - } - - fn link_state(&mut self, cx: &mut Context) -> LinkState { - self.link_state.lock(|s| { - let s = &mut *s.borrow_mut(); - s.waker.register(cx.waker()); - s.state - }) - } -} - -pub struct RxToken<'a, const MTU: usize> { - rx: zerocopy_channel::Receiver<'a, NoopRawMutex, PacketBuf>, -} - -impl<'a, const MTU: usize> embassy_net::device::RxToken for RxToken<'a, MTU> { - fn consume(mut self, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - // NOTE(unwrap): we checked the queue wasn't full when creating the token. - let pkt = unwrap!(self.rx.try_recv()); - let r = f(&mut pkt.buf[..pkt.len]); - self.rx.recv_done(); - r - } -} - -pub struct TxToken<'a, const MTU: usize> { - tx: zerocopy_channel::Sender<'a, NoopRawMutex, PacketBuf>, -} - -impl<'a, const MTU: usize> embassy_net::device::TxToken for TxToken<'a, MTU> { - fn consume(mut self, len: usize, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - // NOTE(unwrap): we checked the queue wasn't full when creating the token. - let pkt = unwrap!(self.tx.try_send()); - let r = f(&mut pkt.buf[..len]); - pkt.len = len; - self.tx.send_done(); - r - } -} - -mod zerocopy_channel { - use core::cell::RefCell; - use core::future::poll_fn; - use core::marker::PhantomData; - use core::task::{Context, Poll}; - - use embassy_sync::blocking_mutex::raw::RawMutex; - use embassy_sync::blocking_mutex::Mutex; - use embassy_sync::waitqueue::WakerRegistration; - - pub struct Channel<'a, M: RawMutex, T> { - buf: *mut T, - phantom: PhantomData<&'a mut T>, - state: Mutex>, - } - - impl<'a, M: RawMutex, T> Channel<'a, M, T> { - pub fn new(buf: &'a mut [T]) -> Self { - let len = buf.len(); - assert!(len != 0); - - Self { - buf: buf.as_mut_ptr(), - phantom: PhantomData, - state: Mutex::new(RefCell::new(State { - len, - front: 0, - back: 0, - full: false, - send_waker: WakerRegistration::new(), - recv_waker: WakerRegistration::new(), - })), - } - } - - pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) { - (Sender { channel: self }, Receiver { channel: self }) - } - } - - pub struct Sender<'a, M: RawMutex, T> { - channel: &'a Channel<'a, M, T>, - } - - impl<'a, M: RawMutex, T> Sender<'a, M, T> { - pub fn borrow(&mut self) -> Sender<'_, M, T> { - Sender { channel: self.channel } - } - - pub fn try_send(&mut self) -> Option<&mut T> { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.push_index() { - Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), - None => None, - } - }) - } - - pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.push_index() { - Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), - None => { - s.recv_waker.register(cx.waker()); - Poll::Pending - } - } - }) - } - - pub async fn send(&mut self) -> &mut T { - let i = poll_fn(|cx| { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.push_index() { - Some(i) => Poll::Ready(i), - None => { - s.recv_waker.register(cx.waker()); - Poll::Pending - } - } - }) - }) - .await; - unsafe { &mut *self.channel.buf.add(i) } - } - - pub fn send_done(&mut self) { - self.channel.state.lock(|s| s.borrow_mut().push_done()) - } - } - pub struct Receiver<'a, M: RawMutex, T> { - channel: &'a Channel<'a, M, T>, - } - - impl<'a, M: RawMutex, T> Receiver<'a, M, T> { - pub fn borrow(&mut self) -> Receiver<'_, M, T> { - Receiver { channel: self.channel } - } - - pub fn try_recv(&mut self) -> Option<&mut T> { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.pop_index() { - Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }), - None => None, - } - }) - } - - pub fn poll_recv(&mut self, cx: &mut Context) -> Poll<&mut T> { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.pop_index() { - Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }), - None => { - s.send_waker.register(cx.waker()); - Poll::Pending - } - } - }) - } - - pub async fn recv(&mut self) -> &mut T { - let i = poll_fn(|cx| { - self.channel.state.lock(|s| { - let s = &mut *s.borrow_mut(); - match s.pop_index() { - Some(i) => Poll::Ready(i), - None => { - s.send_waker.register(cx.waker()); - Poll::Pending - } - } - }) - }) - .await; - unsafe { &mut *self.channel.buf.add(i) } - } - - pub fn recv_done(&mut self) { - self.channel.state.lock(|s| s.borrow_mut().pop_done()) - } - } - - struct State { - len: usize, - - /// Front index. Always 0..=(N-1) - front: usize, - /// Back index. Always 0..=(N-1). - back: usize, - - /// Used to distinguish "empty" and "full" cases when `front == back`. - /// May only be `true` if `front == back`, always `false` otherwise. - full: bool, - - send_waker: WakerRegistration, - recv_waker: WakerRegistration, - } - - impl State { - fn increment(&self, i: usize) -> usize { - if i + 1 == self.len { - 0 - } else { - i + 1 - } - } - - fn is_full(&self) -> bool { - self.full - } - - fn is_empty(&self) -> bool { - self.front == self.back && !self.full - } - - fn push_index(&mut self) -> Option { - match self.is_full() { - true => None, - false => Some(self.back), - } - } - - fn push_done(&mut self) { - assert!(!self.is_full()); - self.back = self.increment(self.back); - if self.back == self.front { - self.full = true; - } - self.send_waker.wake(); - } - - fn pop_index(&mut self) -> Option { - match self.is_empty() { - true => None, - false => Some(self.front), - } - } - - fn pop_done(&mut self) { - assert!(!self.is_empty()); - self.front = self.increment(self.front); - self.full = false; - self.recv_waker.wake(); - } - } -} diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index 2ee47f68c..4954a65bc 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -21,7 +21,6 @@ use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; use crate::types::*; use crate::Builder; -#[cfg(feature = "embassy-net")] pub mod embassy_net; /// This should be used as `device_class` when building the `UsbDevice`. diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml index a980a505c..994823a9e 100644 --- a/examples/nrf/Cargo.toml +++ b/examples/nrf/Cargo.toml @@ -15,8 +15,8 @@ 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-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"], optional = true } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true } embedded-io = "0.4.0" embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 31a08cfb6..afd1042a1 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -10,8 +10,8 @@ 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-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", "time-driver", "pio", "critical-section-impl"] } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 41680f8f4..45b2a4a4f 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -8,7 +8,8 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dhcpv4", "pool-16"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dhcpv4"] } +embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } critical-section = { version = "1.1", features = ["std"] } diff --git a/examples/std/src/tuntap.rs b/examples/std/src/tuntap.rs index bb3e194cc..d918a2e62 100644 --- a/examples/std/src/tuntap.rs +++ b/examples/std/src/tuntap.rs @@ -4,7 +4,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::task::Context; use async_io::Async; -use embassy_net::device::{self, Device, DeviceCapabilities, LinkState}; +use embassy_net_driver::{self, Capabilities, Driver, LinkState}; use log::*; pub const SIOCGIFMTU: libc::c_ulong = 0x8921; @@ -137,7 +137,7 @@ impl TunTapDevice { } } -impl Device for TunTapDevice { +impl Driver for TunTapDevice { type RxToken<'a> = RxToken where Self: 'a; type TxToken<'a> = TxToken<'a> where Self: 'a; @@ -170,8 +170,8 @@ impl Device for TunTapDevice { }) } - fn capabilities(&self) -> DeviceCapabilities { - let mut caps = DeviceCapabilities::default(); + fn capabilities(&self) -> Capabilities { + let mut caps = Capabilities::default(); caps.max_transmission_unit = self.device.get_ref().mtu; caps } @@ -190,7 +190,7 @@ pub struct RxToken { buffer: Vec, } -impl device::RxToken for RxToken { +impl embassy_net_driver::RxToken for RxToken { fn consume(mut self, f: F) -> R where F: FnOnce(&mut [u8]) -> R, @@ -204,7 +204,7 @@ pub struct TxToken<'a> { device: &'a mut Async, } -impl<'a> device::TxToken for TxToken<'a> { +impl<'a> embassy_net_driver::TxToken for TxToken<'a> { fn consume(self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R, diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index afdf87000..b80dbbf9c 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 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", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "embassy-net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } -embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } +embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embedded-io = { version = "0.4.0", features = ["async"] } defmt = "0.3" diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 5acf0035d..d30c42b1f 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 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", "unstable-traits", "tick-hz-32_768"] } -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "embassy-net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } -embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16", "unstable-traits"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } +embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits"] } embedded-io = { version = "0.4.0", features = ["async"] } defmt = "0.3" diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index bb8515312..c0accb0d6 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -11,8 +11,8 @@ 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-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } -embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } usbd-hid = "0.6.0"