Merge pull request #1129 from embassy-rs/net-driver
net: driver crate split
This commit is contained in:
		
							
								
								
									
										1
									
								
								.github/workflows/doc.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/doc.yml
									
									
									
									
										vendored
									
									
								
							| @@ -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 | ||||
|   | ||||
							
								
								
									
										23
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										23
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							| @@ -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", | ||||
|   | ||||
							
								
								
									
										8
									
								
								ci.sh
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								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 \ | ||||
|   | ||||
| @@ -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 \ | ||||
|   | ||||
							
								
								
									
										12
									
								
								embassy-net-driver-channel/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								embassy-net-driver-channel/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -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" } | ||||
							
								
								
									
										225
									
								
								embassy-net-driver-channel/src/fmt.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								embassy-net-driver-channel/src/fmt.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<Self::Ok, Self::Error>; | ||||
| } | ||||
|  | ||||
| impl<T> Try for Option<T> { | ||||
|     type Ok = T; | ||||
|     type Error = NoneError; | ||||
|  | ||||
|     #[inline] | ||||
|     fn into_result(self) -> Result<T, NoneError> { | ||||
|         self.ok_or(NoneError) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<T, E> Try for Result<T, E> { | ||||
|     type Ok = T; | ||||
|     type Error = E; | ||||
|  | ||||
|     #[inline] | ||||
|     fn into_result(self) -> Self { | ||||
|         self | ||||
|     } | ||||
| } | ||||
							
								
								
									
										525
									
								
								embassy-net-driver-channel/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										525
									
								
								embassy-net-driver-channel/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<const MTU: usize, const N_RX: usize, const N_TX: usize> { | ||||
|     rx: [PacketBuf<MTU>; N_RX], | ||||
|     tx: [PacketBuf<MTU>; N_TX], | ||||
|     inner: MaybeUninit<StateInner<'static, MTU>>, | ||||
| } | ||||
|  | ||||
| impl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_TX> { | ||||
|     const NEW_PACKET: PacketBuf<MTU> = 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<MTU>>, | ||||
|     tx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf<MTU>>, | ||||
|     link_state: Mutex<NoopRawMutex, RefCell<LinkStateState>>, | ||||
| } | ||||
|  | ||||
| /// 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<MTU>>, | ||||
|     rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>, | ||||
|     link_state: &'d Mutex<NoopRawMutex, RefCell<LinkStateState>>, | ||||
| } | ||||
|  | ||||
| pub struct RxRunner<'d, const MTU: usize> { | ||||
|     rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>, | ||||
|     link_state: &'d Mutex<NoopRawMutex, RefCell<LinkStateState>>, | ||||
| } | ||||
|  | ||||
| pub struct TxRunner<'d, const MTU: usize> { | ||||
|     tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf<MTU>>, | ||||
| } | ||||
|  | ||||
| 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<MTU, N_RX, N_TX>, | ||||
|     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<StateInner<'d, MTU>> = | ||||
|         (&mut state.inner as *mut MaybeUninit<StateInner<'static, MTU>>).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<const MTU: usize> { | ||||
|     len: usize, | ||||
|     buf: [u8; MTU], | ||||
| } | ||||
|  | ||||
| impl<const MTU: usize> PacketBuf<MTU> { | ||||
|     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<MTU>>, | ||||
|     tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>, | ||||
|     link_state: &'d Mutex<NoopRawMutex, RefCell<LinkStateState>>, | ||||
|     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<Self::TxToken<'_>> { | ||||
|         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<MTU>>, | ||||
| } | ||||
|  | ||||
| impl<'a, const MTU: usize> embassy_net_driver::RxToken for RxToken<'a, MTU> { | ||||
|     fn consume<R, F>(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<MTU>>, | ||||
| } | ||||
|  | ||||
| impl<'a, const MTU: usize> embassy_net_driver::TxToken for TxToken<'a, MTU> { | ||||
|     fn consume<R, F>(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<M, RefCell<State>>, | ||||
|     } | ||||
|  | ||||
|     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<usize> { | ||||
|             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<usize> { | ||||
|             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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										15
									
								
								embassy-net-driver/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								embassy-net-driver/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -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 } | ||||
							
								
								
									
										175
									
								
								embassy-net-driver/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								embassy-net-driver/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<Self::TxToken<'_>>; | ||||
|     fn link_state(&mut self, cx: &mut Context) -> LinkState; | ||||
|  | ||||
|     fn capabilities(&self) -> Capabilities; | ||||
|     fn ethernet_address(&self) -> [u8; 6]; | ||||
| } | ||||
|  | ||||
| impl<T: ?Sized + Driver> 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<Self::TxToken<'_>> { | ||||
|         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<R, F>(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<R, F>(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<usize>, | ||||
|  | ||||
|     /// 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, | ||||
| } | ||||
| @@ -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" | ||||
|   | ||||
| @@ -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<Self::TxToken<'_>>; | ||||
|     fn link_state(&mut self, cx: &mut Context) -> LinkState; | ||||
|  | ||||
|     fn capabilities(&self) -> phy::DeviceCapabilities; | ||||
|     fn ethernet_address(&self) -> [u8; 6]; | ||||
| } | ||||
|  | ||||
| impl<T: ?Sized + Device> 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<Self::TxToken<'_>> { | ||||
|         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<R, F>(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<R, F>(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<T::RxToken<'a>> where Self: 'a; | ||||
|     type TxToken<'a> = TxTokenAdapter<T::TxToken<'a>> 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 | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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<D: Device> { | ||||
| pub struct Stack<D: Driver> { | ||||
|     pub(crate) socket: RefCell<SocketStack>, | ||||
|     inner: RefCell<Inner<D>>, | ||||
| } | ||||
|  | ||||
| struct Inner<D: Device> { | ||||
| struct Inner<D: Driver> { | ||||
|     device: D, | ||||
|     link_up: bool, | ||||
|     config: Option<Config>, | ||||
| @@ -102,7 +101,7 @@ pub(crate) struct SocketStack { | ||||
|     next_local_port: u16, | ||||
| } | ||||
|  | ||||
| impl<D: Device + 'static> Stack<D> { | ||||
| impl<D: Driver + 'static> Stack<D> { | ||||
|     pub fn new<const ADDR: usize, const SOCK: usize, const NEIGH: usize>( | ||||
|         mut device: D, | ||||
|         config: ConfigStrategy, | ||||
| @@ -130,7 +129,7 @@ impl<D: Device + 'static> Stack<D> { | ||||
|             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<D: Device + 'static> Inner<D> { | ||||
| impl<D: Driver + 'static> Inner<D> { | ||||
|     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<D: Device + 'static> Inner<D> { | ||||
|         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, | ||||
|         }; | ||||
|   | ||||
| @@ -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<D: Device>(stack: &'a Stack<D>, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { | ||||
|     pub fn new<D: Driver>(stack: &'a Stack<D>, 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<D>, | ||||
|         state: &'d TcpClientState<N, TX_SZ, RX_SZ>, | ||||
|     } | ||||
|  | ||||
|     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<D>, state: &'d TcpClientState<N, TX_SZ, RX_SZ>) -> 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<D: Device>(stack: &'d Stack<D>, state: &'d TcpClientState<N, TX_SZ, RX_SZ>) -> Result<Self, Error> { | ||||
|         fn new<D: Driver>(stack: &'d Stack<D>, state: &'d TcpClientState<N, TX_SZ, RX_SZ>) -> Result<Self, Error> { | ||||
|             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) }, | ||||
|   | ||||
| @@ -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<D: Device>( | ||||
|     pub fn new<D: Driver>( | ||||
|         stack: &'a Stack<D>, | ||||
|         rx_meta: &'a mut [PacketMetadata], | ||||
|         rx_buffer: &'a mut [u8], | ||||
|   | ||||
| @@ -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 = [] | ||||
|   | ||||
| @@ -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<const TX: usize, const RX: usize> PacketQueue<TX, RX> { | ||||
|  | ||||
| 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<Self::TxToken<'_>> { | ||||
|             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<R, F>(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<Self::TxToken<'_>> { | ||||
|         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<R, F>(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<R, F>(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<R, F>(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 | ||||
|   | ||||
| @@ -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); | ||||
|             )* | ||||
|         }) | ||||
|     }; | ||||
|   | ||||
| @@ -14,4 +14,3 @@ target = "thumbv7em-none-eabi" | ||||
|  | ||||
| [dependencies] | ||||
| defmt = { version = "0.3", optional = true } | ||||
| log = { version = "0.4.14", optional = true } | ||||
|   | ||||
| @@ -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 } | ||||
|   | ||||
| @@ -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<MTU>; N_RX], | ||||
|     tx: [PacketBuf<MTU>; N_TX], | ||||
|     inner: MaybeUninit<StateInner<'d, MTU>>, | ||||
| pub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> { | ||||
|     ch_state: ch::State<MTU, N_RX, N_TX>, | ||||
| } | ||||
|  | ||||
| impl<'d, const MTU: usize, const N_RX: usize, const N_TX: usize> State<'d, MTU, N_RX, N_TX> { | ||||
|     const NEW_PACKET: PacketBuf<MTU> = PacketBuf::new(); | ||||
|  | ||||
| impl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_TX> { | ||||
|     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<MTU>>, | ||||
|     tx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf<MTU>>, | ||||
|     link_state: Mutex<NoopRawMutex, RefCell<LinkStateState>>, | ||||
| } | ||||
|  | ||||
| /// 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<MTU>>, | ||||
|     rx_usb: Receiver<'d, D>, | ||||
|     rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>, | ||||
|     link_state: &'d Mutex<NoopRawMutex, RefCell<LinkStateState>>, | ||||
|     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<const MTU: usize, const N_RX: usize, const N_TX: usize>( | ||||
|         self, | ||||
|         state: &'d mut State<'d, MTU, N_RX, N_TX>, | ||||
|         state: &'d mut State<MTU, N_RX, N_TX>, | ||||
|         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<const MTU: usize> { | ||||
|     len: usize, | ||||
|     buf: [u8; MTU], | ||||
| } | ||||
|  | ||||
| impl<const MTU: usize> PacketBuf<MTU> { | ||||
|     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<MTU>>, | ||||
|     tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>, | ||||
|     link_state: &'d Mutex<NoopRawMutex, RefCell<LinkStateState>>, | ||||
|     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<Self::TxToken<'_>> { | ||||
|         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<MTU>>, | ||||
| } | ||||
|  | ||||
| impl<'a, const MTU: usize> embassy_net::device::RxToken for RxToken<'a, MTU> { | ||||
|     fn consume<R, F>(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<MTU>>, | ||||
| } | ||||
|  | ||||
| impl<'a, const MTU: usize> embassy_net::device::TxToken for TxToken<'a, MTU> { | ||||
|     fn consume<R, F>(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<M, RefCell<State>>, | ||||
|     } | ||||
|  | ||||
|     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<usize> { | ||||
|             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<usize> { | ||||
|             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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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`. | ||||
|   | ||||
| @@ -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 } | ||||
|  | ||||
|   | ||||
| @@ -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" } | ||||
|  | ||||
|   | ||||
| @@ -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"] } | ||||
|  | ||||
|   | ||||
| @@ -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<u8>, | ||||
| } | ||||
|  | ||||
| impl device::RxToken for RxToken { | ||||
| impl embassy_net_driver::RxToken for RxToken { | ||||
|     fn consume<R, F>(mut self, f: F) -> R | ||||
|     where | ||||
|         F: FnOnce(&mut [u8]) -> R, | ||||
| @@ -204,7 +204,7 @@ pub struct TxToken<'a> { | ||||
|     device: &'a mut Async<TunTap>, | ||||
| } | ||||
|  | ||||
| impl<'a> device::TxToken for TxToken<'a> { | ||||
| impl<'a> embassy_net_driver::TxToken for TxToken<'a> { | ||||
|     fn consume<R, F>(self, len: usize, f: F) -> R | ||||
|     where | ||||
|         F: FnOnce(&mut [u8]) -> R, | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
| @@ -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" | ||||
|   | ||||
| @@ -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" | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user