aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2022-12-26 05:06:15 +0100
committerGitHub <[email protected]>2022-12-26 05:06:15 +0100
commit147609d3bde445b663d0e75c8bccdb152b9c7e1e (patch)
tree451d75b390e751e2ef6401b0b60c137bcaf92b29
parentc29657f95a835dd0893333be409ded1efde9a9b9 (diff)
parent007246b16036c3aa874e78d0665567a27ab35fa9 (diff)
Merge pull request #1129 from embassy-rs/net-driver
net: driver crate split
-rw-r--r--.github/workflows/doc.yml1
-rw-r--r--.vscode/settings.json23
-rwxr-xr-xci.sh8
-rwxr-xr-xci_stable.sh4
-rw-r--r--embassy-net-driver-channel/Cargo.toml12
-rw-r--r--embassy-net-driver-channel/src/fmt.rs225
-rw-r--r--embassy-net-driver-channel/src/lib.rs525
-rw-r--r--embassy-net-driver/Cargo.toml15
-rw-r--r--embassy-net-driver/src/lib.rs175
-rw-r--r--embassy-net/Cargo.toml13
-rw-r--r--embassy-net/src/device.rs117
-rw-r--r--embassy-net/src/lib.rs17
-rw-r--r--embassy-net/src/tcp.rs14
-rw-r--r--embassy-net/src/udp.rs5
-rw-r--r--embassy-stm32/Cargo.toml4
-rw-r--r--embassy-stm32/src/eth/mod.rs139
-rw-r--r--embassy-stm32/src/eth/v1/mod.rs4
-rw-r--r--embassy-usb-driver/Cargo.toml1
-rw-r--r--embassy-usb/Cargo.toml2
-rw-r--r--embassy-usb/src/class/cdc_ncm/embassy_net.rs408
-rw-r--r--embassy-usb/src/class/cdc_ncm/mod.rs1
-rw-r--r--examples/nrf/Cargo.toml4
-rw-r--r--examples/rp/Cargo.toml4
-rw-r--r--examples/std/Cargo.toml3
-rw-r--r--examples/std/src/tuntap.rs12
-rw-r--r--examples/stm32f7/Cargo.toml4
-rw-r--r--examples/stm32h7/Cargo.toml4
-rw-r--r--examples/stm32l5/Cargo.toml4
28 files changed, 1141 insertions, 607 deletions
diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml
index eb460e738..8a341b8f7 100644
--- a/.github/workflows/doc.yml
+++ b/.github/workflows/doc.yml
@@ -69,6 +69,7 @@ jobs:
69 builder ./embassy-futures crates/embassy-futures/git.zup 69 builder ./embassy-futures crates/embassy-futures/git.zup
70 builder ./embassy-lora crates/embassy-lora/git.zup 70 builder ./embassy-lora crates/embassy-lora/git.zup
71 builder ./embassy-net crates/embassy-net/git.zup 71 builder ./embassy-net crates/embassy-net/git.zup
72 builder ./embassy-net-driver crates/embassy-net-driver/git.zup
72 builder ./embassy-nrf crates/embassy-nrf/git.zup 73 builder ./embassy-nrf crates/embassy-nrf/git.zup
73 builder ./embassy-rp crates/embassy-rp/git.zup 74 builder ./embassy-rp crates/embassy-rp/git.zup
74 builder ./embassy-sync crates/embassy-sync/git.zup 75 builder ./embassy-sync crates/embassy-sync/git.zup
diff --git a/.vscode/settings.json b/.vscode/settings.json
index fd2e4afb1..086f435da 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -4,28 +4,21 @@
4 "rust-analyzer.checkOnSave.noDefaultFeatures": true, 4 "rust-analyzer.checkOnSave.noDefaultFeatures": true,
5 "rust-analyzer.cargo.noDefaultFeatures": true, 5 "rust-analyzer.cargo.noDefaultFeatures": true,
6 "rust-analyzer.procMacro.enable": true, 6 "rust-analyzer.procMacro.enable": true,
7 //"rust-analyzer.cargo.target": "thumbv7em-none-eabi", 7 "rust-analyzer.cargo.target": "thumbv7em-none-eabi",
8 "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", 8 //"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
9 "rust-analyzer.cargo.features": [ 9 "rust-analyzer.cargo.features": [
10 // These are needed to prevent embassy-net from failing to build 10 "nightly",
11 //"embassy-net/medium-ethernet",
12 //"embassy-net/tcp",
13 //"embassy-net/pool-16",
14 //"time-tick-16mhz",
15 //"defmt-timestamp-uptime",
16 //"nightly",
17 //"unstable-traits",
18 ], 11 ],
19 "rust-analyzer.linkedProjects": [ 12 "rust-analyzer.linkedProjects": [
20 // Declare for the target you wish to develop 13 // Declare for the target you wish to develop
21 //"embassy-executor/Cargo.toml", 14 // "embassy-executor/Cargo.toml",
22 //"embassy-sync/Cargo.toml", 15 // "embassy-sync/Cargo.toml",
23 //"examples/nrf/Cargo.toml", 16 "examples/nrf/Cargo.toml",
24 // "examples/nrf-rtos-trace/Cargo.toml", 17 // "examples/nrf-rtos-trace/Cargo.toml",
25 // "examples/rp/Cargo.toml", 18 // "examples/rp/Cargo.toml",
26 // "examples/std/Cargo.toml", 19 // "examples/std/Cargo.toml",
27 // "examples/stm32f0/Cargo.toml", 20 // "examples/stm32f0/Cargo.toml",
28 //"examples/stm32f1/Cargo.toml", 21 // "examples/stm32f1/Cargo.toml",
29 // "examples/stm32f2/Cargo.toml", 22 // "examples/stm32f2/Cargo.toml",
30 // "examples/stm32f3/Cargo.toml", 23 // "examples/stm32f3/Cargo.toml",
31 // "examples/stm32f4/Cargo.toml", 24 // "examples/stm32f4/Cargo.toml",
@@ -36,7 +29,7 @@
36 // "examples/stm32l0/Cargo.toml", 29 // "examples/stm32l0/Cargo.toml",
37 // "examples/stm32l1/Cargo.toml", 30 // "examples/stm32l1/Cargo.toml",
38 // "examples/stm32l4/Cargo.toml", 31 // "examples/stm32l4/Cargo.toml",
39 "examples/stm32l5/Cargo.toml", 32 // "examples/stm32l5/Cargo.toml",
40 // "examples/stm32u5/Cargo.toml", 33 // "examples/stm32u5/Cargo.toml",
41 // "examples/stm32wb/Cargo.toml", 34 // "examples/stm32wb/Cargo.toml",
42 // "examples/stm32wb55/Cargo.toml", 35 // "examples/stm32wb55/Cargo.toml",
diff --git a/ci.sh b/ci.sh
index 9f79c87b9..14a48454d 100755
--- a/ci.sh
+++ b/ci.sh
@@ -38,10 +38,10 @@ cargo batch \
38 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ 38 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
39 --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ 39 --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
40 --- 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 \ 40 --- 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 \
41 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16 \ 41 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
42 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,unstable-traits \ 42 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \
43 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,nightly \ 43 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \
44 --- 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 \ 44 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits,nightly \
45 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \ 45 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \
46 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \ 46 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \
47 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \ 47 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \
diff --git a/ci_stable.sh b/ci_stable.sh
index 0332c3faf..a1d507d71 100755
--- a/ci_stable.sh
+++ b/ci_stable.sh
@@ -13,8 +13,8 @@ cargo batch \
13 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \ 13 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \
14 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \ 14 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \
15 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \ 15 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
16 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16 \ 16 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
17 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,pool-16,unstable-traits \ 17 --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \
18 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \ 18 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \
19 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \ 19 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \
20 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \ 20 --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \
diff --git a/embassy-net-driver-channel/Cargo.toml b/embassy-net-driver-channel/Cargo.toml
new file mode 100644
index 000000000..700a4e8a0
--- /dev/null
+++ b/embassy-net-driver-channel/Cargo.toml
@@ -0,0 +1,12 @@
1[package]
2name = "embassy-net-driver-channel"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7defmt = { version = "0.3", optional = true }
8log = { version = "0.4.14", optional = true }
9
10embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
11embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
12embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
diff --git a/embassy-net-driver-channel/src/fmt.rs b/embassy-net-driver-channel/src/fmt.rs
new file mode 100644
index 000000000..066970813
--- /dev/null
+++ b/embassy-net-driver-channel/src/fmt.rs
@@ -0,0 +1,225 @@
1#![macro_use]
2#![allow(unused_macros)]
3
4#[cfg(all(feature = "defmt", feature = "log"))]
5compile_error!("You may not enable both `defmt` and `log` features.");
6
7macro_rules! assert {
8 ($($x:tt)*) => {
9 {
10 #[cfg(not(feature = "defmt"))]
11 ::core::assert!($($x)*);
12 #[cfg(feature = "defmt")]
13 ::defmt::assert!($($x)*);
14 }
15 };
16}
17
18macro_rules! assert_eq {
19 ($($x:tt)*) => {
20 {
21 #[cfg(not(feature = "defmt"))]
22 ::core::assert_eq!($($x)*);
23 #[cfg(feature = "defmt")]
24 ::defmt::assert_eq!($($x)*);
25 }
26 };
27}
28
29macro_rules! assert_ne {
30 ($($x:tt)*) => {
31 {
32 #[cfg(not(feature = "defmt"))]
33 ::core::assert_ne!($($x)*);
34 #[cfg(feature = "defmt")]
35 ::defmt::assert_ne!($($x)*);
36 }
37 };
38}
39
40macro_rules! debug_assert {
41 ($($x:tt)*) => {
42 {
43 #[cfg(not(feature = "defmt"))]
44 ::core::debug_assert!($($x)*);
45 #[cfg(feature = "defmt")]
46 ::defmt::debug_assert!($($x)*);
47 }
48 };
49}
50
51macro_rules! debug_assert_eq {
52 ($($x:tt)*) => {
53 {
54 #[cfg(not(feature = "defmt"))]
55 ::core::debug_assert_eq!($($x)*);
56 #[cfg(feature = "defmt")]
57 ::defmt::debug_assert_eq!($($x)*);
58 }
59 };
60}
61
62macro_rules! debug_assert_ne {
63 ($($x:tt)*) => {
64 {
65 #[cfg(not(feature = "defmt"))]
66 ::core::debug_assert_ne!($($x)*);
67 #[cfg(feature = "defmt")]
68 ::defmt::debug_assert_ne!($($x)*);
69 }
70 };
71}
72
73macro_rules! todo {
74 ($($x:tt)*) => {
75 {
76 #[cfg(not(feature = "defmt"))]
77 ::core::todo!($($x)*);
78 #[cfg(feature = "defmt")]
79 ::defmt::todo!($($x)*);
80 }
81 };
82}
83
84macro_rules! unreachable {
85 ($($x:tt)*) => {
86 {
87 #[cfg(not(feature = "defmt"))]
88 ::core::unreachable!($($x)*);
89 #[cfg(feature = "defmt")]
90 ::defmt::unreachable!($($x)*);
91 }
92 };
93}
94
95macro_rules! panic {
96 ($($x:tt)*) => {
97 {
98 #[cfg(not(feature = "defmt"))]
99 ::core::panic!($($x)*);
100 #[cfg(feature = "defmt")]
101 ::defmt::panic!($($x)*);
102 }
103 };
104}
105
106macro_rules! trace {
107 ($s:literal $(, $x:expr)* $(,)?) => {
108 {
109 #[cfg(feature = "log")]
110 ::log::trace!($s $(, $x)*);
111 #[cfg(feature = "defmt")]
112 ::defmt::trace!($s $(, $x)*);
113 #[cfg(not(any(feature = "log", feature="defmt")))]
114 let _ = ($( & $x ),*);
115 }
116 };
117}
118
119macro_rules! debug {
120 ($s:literal $(, $x:expr)* $(,)?) => {
121 {
122 #[cfg(feature = "log")]
123 ::log::debug!($s $(, $x)*);
124 #[cfg(feature = "defmt")]
125 ::defmt::debug!($s $(, $x)*);
126 #[cfg(not(any(feature = "log", feature="defmt")))]
127 let _ = ($( & $x ),*);
128 }
129 };
130}
131
132macro_rules! info {
133 ($s:literal $(, $x:expr)* $(,)?) => {
134 {
135 #[cfg(feature = "log")]
136 ::log::info!($s $(, $x)*);
137 #[cfg(feature = "defmt")]
138 ::defmt::info!($s $(, $x)*);
139 #[cfg(not(any(feature = "log", feature="defmt")))]
140 let _ = ($( & $x ),*);
141 }
142 };
143}
144
145macro_rules! warn {
146 ($s:literal $(, $x:expr)* $(,)?) => {
147 {
148 #[cfg(feature = "log")]
149 ::log::warn!($s $(, $x)*);
150 #[cfg(feature = "defmt")]
151 ::defmt::warn!($s $(, $x)*);
152 #[cfg(not(any(feature = "log", feature="defmt")))]
153 let _ = ($( & $x ),*);
154 }
155 };
156}
157
158macro_rules! error {
159 ($s:literal $(, $x:expr)* $(,)?) => {
160 {
161 #[cfg(feature = "log")]
162 ::log::error!($s $(, $x)*);
163 #[cfg(feature = "defmt")]
164 ::defmt::error!($s $(, $x)*);
165 #[cfg(not(any(feature = "log", feature="defmt")))]
166 let _ = ($( & $x ),*);
167 }
168 };
169}
170
171#[cfg(feature = "defmt")]
172macro_rules! unwrap {
173 ($($x:tt)*) => {
174 ::defmt::unwrap!($($x)*)
175 };
176}
177
178#[cfg(not(feature = "defmt"))]
179macro_rules! unwrap {
180 ($arg:expr) => {
181 match $crate::fmt::Try::into_result($arg) {
182 ::core::result::Result::Ok(t) => t,
183 ::core::result::Result::Err(e) => {
184 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
185 }
186 }
187 };
188 ($arg:expr, $($msg:expr),+ $(,)? ) => {
189 match $crate::fmt::Try::into_result($arg) {
190 ::core::result::Result::Ok(t) => t,
191 ::core::result::Result::Err(e) => {
192 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
193 }
194 }
195 }
196}
197
198#[derive(Debug, Copy, Clone, Eq, PartialEq)]
199pub struct NoneError;
200
201pub trait Try {
202 type Ok;
203 type Error;
204 fn into_result(self) -> Result<Self::Ok, Self::Error>;
205}
206
207impl<T> Try for Option<T> {
208 type Ok = T;
209 type Error = NoneError;
210
211 #[inline]
212 fn into_result(self) -> Result<T, NoneError> {
213 self.ok_or(NoneError)
214 }
215}
216
217impl<T, E> Try for Result<T, E> {
218 type Ok = T;
219 type Error = E;
220
221 #[inline]
222 fn into_result(self) -> Self {
223 self
224 }
225}
diff --git a/embassy-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs
new file mode 100644
index 000000000..369dc5a9d
--- /dev/null
+++ b/embassy-net-driver-channel/src/lib.rs
@@ -0,0 +1,525 @@
1#![no_std]
2
3// must go first!
4mod fmt;
5
6use core::cell::RefCell;
7use core::mem::MaybeUninit;
8use core::task::{Context, Poll};
9
10pub use embassy_net_driver as driver;
11use embassy_net_driver::{Capabilities, LinkState, Medium};
12use embassy_sync::blocking_mutex::raw::NoopRawMutex;
13use embassy_sync::blocking_mutex::Mutex;
14use embassy_sync::waitqueue::WakerRegistration;
15
16pub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> {
17 rx: [PacketBuf<MTU>; N_RX],
18 tx: [PacketBuf<MTU>; N_TX],
19 inner: MaybeUninit<StateInner<'static, MTU>>,
20}
21
22impl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_TX> {
23 const NEW_PACKET: PacketBuf<MTU> = PacketBuf::new();
24
25 pub const fn new() -> Self {
26 Self {
27 rx: [Self::NEW_PACKET; N_RX],
28 tx: [Self::NEW_PACKET; N_TX],
29 inner: MaybeUninit::uninit(),
30 }
31 }
32}
33
34struct StateInner<'d, const MTU: usize> {
35 rx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf<MTU>>,
36 tx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf<MTU>>,
37 link_state: Mutex<NoopRawMutex, RefCell<LinkStateState>>,
38}
39
40/// State of the LinkState
41struct LinkStateState {
42 state: LinkState,
43 waker: WakerRegistration,
44}
45
46pub struct Runner<'d, const MTU: usize> {
47 tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf<MTU>>,
48 rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>,
49 link_state: &'d Mutex<NoopRawMutex, RefCell<LinkStateState>>,
50}
51
52pub struct RxRunner<'d, const MTU: usize> {
53 rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>,
54 link_state: &'d Mutex<NoopRawMutex, RefCell<LinkStateState>>,
55}
56
57pub struct TxRunner<'d, const MTU: usize> {
58 tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf<MTU>>,
59}
60
61impl<'d, const MTU: usize> Runner<'d, MTU> {
62 pub fn split(self) -> (RxRunner<'d, MTU>, TxRunner<'d, MTU>) {
63 (
64 RxRunner {
65 link_state: self.link_state,
66 rx_chan: self.rx_chan,
67 },
68 TxRunner { tx_chan: self.tx_chan },
69 )
70 }
71
72 pub fn set_link_state(&mut self, state: LinkState) {
73 self.link_state.lock(|s| {
74 let s = &mut *s.borrow_mut();
75 s.state = state;
76 s.waker.wake();
77 });
78 }
79
80 pub async fn rx_buf(&mut self) -> &mut [u8] {
81 let p = self.rx_chan.send().await;
82 &mut p.buf
83 }
84
85 pub fn try_rx_buf(&mut self) -> Option<&mut [u8]> {
86 let p = self.rx_chan.try_send()?;
87 Some(&mut p.buf)
88 }
89
90 pub fn poll_rx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> {
91 match self.rx_chan.poll_send(cx) {
92 Poll::Ready(p) => Poll::Ready(&mut p.buf),
93 Poll::Pending => Poll::Pending,
94 }
95 }
96
97 pub fn rx_done(&mut self, len: usize) {
98 let p = self.rx_chan.try_send().unwrap();
99 p.len = len;
100 self.rx_chan.send_done();
101 }
102
103 pub async fn tx_buf(&mut self) -> &mut [u8] {
104 let p = self.tx_chan.recv().await;
105 &mut p.buf[..p.len]
106 }
107
108 pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> {
109 let p = self.tx_chan.try_recv()?;
110 Some(&mut p.buf[..p.len])
111 }
112
113 pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> {
114 match self.tx_chan.poll_recv(cx) {
115 Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]),
116 Poll::Pending => Poll::Pending,
117 }
118 }
119
120 pub fn tx_done(&mut self) {
121 self.tx_chan.recv_done();
122 }
123}
124
125impl<'d, const MTU: usize> RxRunner<'d, MTU> {
126 pub fn set_link_state(&mut self, state: LinkState) {
127 self.link_state.lock(|s| {
128 let s = &mut *s.borrow_mut();
129 s.state = state;
130 s.waker.wake();
131 });
132 }
133
134 pub async fn rx_buf(&mut self) -> &mut [u8] {
135 let p = self.rx_chan.send().await;
136 &mut p.buf
137 }
138
139 pub fn try_rx_buf(&mut self) -> Option<&mut [u8]> {
140 let p = self.rx_chan.try_send()?;
141 Some(&mut p.buf)
142 }
143
144 pub fn poll_rx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> {
145 match self.rx_chan.poll_send(cx) {
146 Poll::Ready(p) => Poll::Ready(&mut p.buf),
147 Poll::Pending => Poll::Pending,
148 }
149 }
150
151 pub fn rx_done(&mut self, len: usize) {
152 let p = self.rx_chan.try_send().unwrap();
153 p.len = len;
154 self.rx_chan.send_done();
155 }
156}
157
158impl<'d, const MTU: usize> TxRunner<'d, MTU> {
159 pub async fn tx_buf(&mut self) -> &mut [u8] {
160 let p = self.tx_chan.recv().await;
161 &mut p.buf[..p.len]
162 }
163
164 pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> {
165 let p = self.tx_chan.try_recv()?;
166 Some(&mut p.buf[..p.len])
167 }
168
169 pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> {
170 match self.tx_chan.poll_recv(cx) {
171 Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]),
172 Poll::Pending => Poll::Pending,
173 }
174 }
175
176 pub fn tx_done(&mut self) {
177 self.tx_chan.recv_done();
178 }
179}
180
181pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>(
182 state: &'d mut State<MTU, N_RX, N_TX>,
183 ethernet_address: [u8; 6],
184) -> (Runner<'d, MTU>, Device<'d, MTU>) {
185 let mut caps = Capabilities::default();
186 caps.max_transmission_unit = MTU;
187 caps.medium = Medium::Ethernet;
188
189 // safety: this is a self-referential struct, however:
190 // - it can't move while the `'d` borrow is active.
191 // - when the borrow ends, the dangling references inside the MaybeUninit will never be used again.
192 let state_uninit: *mut MaybeUninit<StateInner<'d, MTU>> =
193 (&mut state.inner as *mut MaybeUninit<StateInner<'static, MTU>>).cast();
194 let state = unsafe { &mut *state_uninit }.write(StateInner {
195 rx: zerocopy_channel::Channel::new(&mut state.rx[..]),
196 tx: zerocopy_channel::Channel::new(&mut state.tx[..]),
197 link_state: Mutex::new(RefCell::new(LinkStateState {
198 state: LinkState::Down,
199 waker: WakerRegistration::new(),
200 })),
201 });
202
203 let (rx_sender, rx_receiver) = state.rx.split();
204 let (tx_sender, tx_receiver) = state.tx.split();
205
206 (
207 Runner {
208 tx_chan: tx_receiver,
209 rx_chan: rx_sender,
210 link_state: &state.link_state,
211 },
212 Device {
213 caps,
214 ethernet_address,
215 link_state: &state.link_state,
216 rx: rx_receiver,
217 tx: tx_sender,
218 },
219 )
220}
221
222pub struct PacketBuf<const MTU: usize> {
223 len: usize,
224 buf: [u8; MTU],
225}
226
227impl<const MTU: usize> PacketBuf<MTU> {
228 pub const fn new() -> Self {
229 Self { len: 0, buf: [0; MTU] }
230 }
231}
232
233pub struct Device<'d, const MTU: usize> {
234 rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf<MTU>>,
235 tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>,
236 link_state: &'d Mutex<NoopRawMutex, RefCell<LinkStateState>>,
237 caps: Capabilities,
238 ethernet_address: [u8; 6],
239}
240
241impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> {
242 type RxToken<'a> = RxToken<'a, MTU> where Self: 'a ;
243 type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ;
244
245 fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
246 if self.rx.poll_recv(cx).is_ready() && self.tx.poll_send(cx).is_ready() {
247 Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() }))
248 } else {
249 None
250 }
251 }
252
253 /// Construct a transmit token.
254 fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
255 if self.tx.poll_send(cx).is_ready() {
256 Some(TxToken { tx: self.tx.borrow() })
257 } else {
258 None
259 }
260 }
261
262 /// Get a description of device capabilities.
263 fn capabilities(&self) -> Capabilities {
264 self.caps.clone()
265 }
266
267 fn ethernet_address(&self) -> [u8; 6] {
268 self.ethernet_address
269 }
270
271 fn link_state(&mut self, cx: &mut Context) -> LinkState {
272 self.link_state.lock(|s| {
273 let s = &mut *s.borrow_mut();
274 s.waker.register(cx.waker());
275 s.state
276 })
277 }
278}
279
280pub struct RxToken<'a, const MTU: usize> {
281 rx: zerocopy_channel::Receiver<'a, NoopRawMutex, PacketBuf<MTU>>,
282}
283
284impl<'a, const MTU: usize> embassy_net_driver::RxToken for RxToken<'a, MTU> {
285 fn consume<R, F>(mut self, f: F) -> R
286 where
287 F: FnOnce(&mut [u8]) -> R,
288 {
289 // NOTE(unwrap): we checked the queue wasn't full when creating the token.
290 let pkt = unwrap!(self.rx.try_recv());
291 let r = f(&mut pkt.buf[..pkt.len]);
292 self.rx.recv_done();
293 r
294 }
295}
296
297pub struct TxToken<'a, const MTU: usize> {
298 tx: zerocopy_channel::Sender<'a, NoopRawMutex, PacketBuf<MTU>>,
299}
300
301impl<'a, const MTU: usize> embassy_net_driver::TxToken for TxToken<'a, MTU> {
302 fn consume<R, F>(mut self, len: usize, f: F) -> R
303 where
304 F: FnOnce(&mut [u8]) -> R,
305 {
306 // NOTE(unwrap): we checked the queue wasn't full when creating the token.
307 let pkt = unwrap!(self.tx.try_send());
308 let r = f(&mut pkt.buf[..len]);
309 pkt.len = len;
310 self.tx.send_done();
311 r
312 }
313}
314
315mod zerocopy_channel {
316 use core::cell::RefCell;
317 use core::future::poll_fn;
318 use core::marker::PhantomData;
319 use core::task::{Context, Poll};
320
321 use embassy_sync::blocking_mutex::raw::RawMutex;
322 use embassy_sync::blocking_mutex::Mutex;
323 use embassy_sync::waitqueue::WakerRegistration;
324
325 pub struct Channel<'a, M: RawMutex, T> {
326 buf: *mut T,
327 phantom: PhantomData<&'a mut T>,
328 state: Mutex<M, RefCell<State>>,
329 }
330
331 impl<'a, M: RawMutex, T> Channel<'a, M, T> {
332 pub fn new(buf: &'a mut [T]) -> Self {
333 let len = buf.len();
334 assert!(len != 0);
335
336 Self {
337 buf: buf.as_mut_ptr(),
338 phantom: PhantomData,
339 state: Mutex::new(RefCell::new(State {
340 len,
341 front: 0,
342 back: 0,
343 full: false,
344 send_waker: WakerRegistration::new(),
345 recv_waker: WakerRegistration::new(),
346 })),
347 }
348 }
349
350 pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) {
351 (Sender { channel: self }, Receiver { channel: self })
352 }
353 }
354
355 pub struct Sender<'a, M: RawMutex, T> {
356 channel: &'a Channel<'a, M, T>,
357 }
358
359 impl<'a, M: RawMutex, T> Sender<'a, M, T> {
360 pub fn borrow(&mut self) -> Sender<'_, M, T> {
361 Sender { channel: self.channel }
362 }
363
364 pub fn try_send(&mut self) -> Option<&mut T> {
365 self.channel.state.lock(|s| {
366 let s = &mut *s.borrow_mut();
367 match s.push_index() {
368 Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }),
369 None => None,
370 }
371 })
372 }
373
374 pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> {
375 self.channel.state.lock(|s| {
376 let s = &mut *s.borrow_mut();
377 match s.push_index() {
378 Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }),
379 None => {
380 s.recv_waker.register(cx.waker());
381 Poll::Pending
382 }
383 }
384 })
385 }
386
387 pub async fn send(&mut self) -> &mut T {
388 let i = poll_fn(|cx| {
389 self.channel.state.lock(|s| {
390 let s = &mut *s.borrow_mut();
391 match s.push_index() {
392 Some(i) => Poll::Ready(i),
393 None => {
394 s.recv_waker.register(cx.waker());
395 Poll::Pending
396 }
397 }
398 })
399 })
400 .await;
401 unsafe { &mut *self.channel.buf.add(i) }
402 }
403
404 pub fn send_done(&mut self) {
405 self.channel.state.lock(|s| s.borrow_mut().push_done())
406 }
407 }
408 pub struct Receiver<'a, M: RawMutex, T> {
409 channel: &'a Channel<'a, M, T>,
410 }
411
412 impl<'a, M: RawMutex, T> Receiver<'a, M, T> {
413 pub fn borrow(&mut self) -> Receiver<'_, M, T> {
414 Receiver { channel: self.channel }
415 }
416
417 pub fn try_recv(&mut self) -> Option<&mut T> {
418 self.channel.state.lock(|s| {
419 let s = &mut *s.borrow_mut();
420 match s.pop_index() {
421 Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }),
422 None => None,
423 }
424 })
425 }
426
427 pub fn poll_recv(&mut self, cx: &mut Context) -> Poll<&mut T> {
428 self.channel.state.lock(|s| {
429 let s = &mut *s.borrow_mut();
430 match s.pop_index() {
431 Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }),
432 None => {
433 s.send_waker.register(cx.waker());
434 Poll::Pending
435 }
436 }
437 })
438 }
439
440 pub async fn recv(&mut self) -> &mut T {
441 let i = poll_fn(|cx| {
442 self.channel.state.lock(|s| {
443 let s = &mut *s.borrow_mut();
444 match s.pop_index() {
445 Some(i) => Poll::Ready(i),
446 None => {
447 s.send_waker.register(cx.waker());
448 Poll::Pending
449 }
450 }
451 })
452 })
453 .await;
454 unsafe { &mut *self.channel.buf.add(i) }
455 }
456
457 pub fn recv_done(&mut self) {
458 self.channel.state.lock(|s| s.borrow_mut().pop_done())
459 }
460 }
461
462 struct State {
463 len: usize,
464
465 /// Front index. Always 0..=(N-1)
466 front: usize,
467 /// Back index. Always 0..=(N-1).
468 back: usize,
469
470 /// Used to distinguish "empty" and "full" cases when `front == back`.
471 /// May only be `true` if `front == back`, always `false` otherwise.
472 full: bool,
473
474 send_waker: WakerRegistration,
475 recv_waker: WakerRegistration,
476 }
477
478 impl State {
479 fn increment(&self, i: usize) -> usize {
480 if i + 1 == self.len {
481 0
482 } else {
483 i + 1
484 }
485 }
486
487 fn is_full(&self) -> bool {
488 self.full
489 }
490
491 fn is_empty(&self) -> bool {
492 self.front == self.back && !self.full
493 }
494
495 fn push_index(&mut self) -> Option<usize> {
496 match self.is_full() {
497 true => None,
498 false => Some(self.back),
499 }
500 }
501
502 fn push_done(&mut self) {
503 assert!(!self.is_full());
504 self.back = self.increment(self.back);
505 if self.back == self.front {
506 self.full = true;
507 }
508 self.send_waker.wake();
509 }
510
511 fn pop_index(&mut self) -> Option<usize> {
512 match self.is_empty() {
513 true => None,
514 false => Some(self.front),
515 }
516 }
517
518 fn pop_done(&mut self) {
519 assert!(!self.is_empty());
520 self.front = self.increment(self.front);
521 self.full = false;
522 self.recv_waker.wake();
523 }
524 }
525}
diff --git a/embassy-net-driver/Cargo.toml b/embassy-net-driver/Cargo.toml
new file mode 100644
index 000000000..7ab9d1194
--- /dev/null
+++ b/embassy-net-driver/Cargo.toml
@@ -0,0 +1,15 @@
1[package]
2name = "embassy-net-driver"
3version = "0.1.0"
4edition = "2021"
5license = "MIT OR Apache-2.0"
6
7
8[package.metadata.embassy_docs]
9src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-driver-v$VERSION/embassy-net/src/"
10src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-driver/src/"
11features = ["defmt"]
12target = "thumbv7em-none-eabi"
13
14[dependencies]
15defmt = { version = "0.3", optional = true } \ No newline at end of file
diff --git a/embassy-net-driver/src/lib.rs b/embassy-net-driver/src/lib.rs
new file mode 100644
index 000000000..a39cfecc1
--- /dev/null
+++ b/embassy-net-driver/src/lib.rs
@@ -0,0 +1,175 @@
1#![no_std]
2
3use core::task::Context;
4
5pub trait Driver {
6 type RxToken<'a>: RxToken
7 where
8 Self: 'a;
9 type TxToken<'a>: TxToken
10 where
11 Self: 'a;
12
13 fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>;
14 fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>>;
15 fn link_state(&mut self, cx: &mut Context) -> LinkState;
16
17 fn capabilities(&self) -> Capabilities;
18 fn ethernet_address(&self) -> [u8; 6];
19}
20
21impl<T: ?Sized + Driver> Driver for &mut T {
22 type RxToken<'a> = T::RxToken<'a>
23 where
24 Self: 'a;
25 type TxToken<'a> = T::TxToken<'a>
26 where
27 Self: 'a;
28
29 fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
30 T::transmit(self, cx)
31 }
32 fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
33 T::receive(self, cx)
34 }
35 fn capabilities(&self) -> Capabilities {
36 T::capabilities(self)
37 }
38 fn link_state(&mut self, cx: &mut Context) -> LinkState {
39 T::link_state(self, cx)
40 }
41 fn ethernet_address(&self) -> [u8; 6] {
42 T::ethernet_address(self)
43 }
44}
45
46/// A token to receive a single network packet.
47pub trait RxToken {
48 /// Consumes the token to receive a single network packet.
49 ///
50 /// This method receives a packet and then calls the given closure `f` with the raw
51 /// packet bytes as argument.
52 fn consume<R, F>(self, f: F) -> R
53 where
54 F: FnOnce(&mut [u8]) -> R;
55}
56
57/// A token to transmit a single network packet.
58pub trait TxToken {
59 /// Consumes the token to send a single network packet.
60 ///
61 /// This method constructs a transmit buffer of size `len` and calls the passed
62 /// closure `f` with a mutable reference to that buffer. The closure should construct
63 /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure
64 /// returns, the transmit buffer is sent out.
65 fn consume<R, F>(self, len: usize, f: F) -> R
66 where
67 F: FnOnce(&mut [u8]) -> R;
68}
69
70/// A description of device capabilities.
71///
72/// Higher-level protocols may achieve higher throughput or lower latency if they consider
73/// the bandwidth or packet size limitations.
74#[derive(Debug, Clone, Default)]
75#[cfg_attr(feature = "defmt", derive(defmt::Format))]
76#[non_exhaustive]
77pub struct Capabilities {
78 /// Medium of the device.
79 ///
80 /// This indicates what kind of packet the sent/received bytes are, and determines
81 /// some behaviors of Interface. For example, ARP/NDISC address resolution is only done
82 /// for Ethernet mediums.
83 pub medium: Medium,
84
85 /// Maximum transmission unit.
86 ///
87 /// The network device is unable to send or receive frames larger than the value returned
88 /// by this function.
89 ///
90 /// For Ethernet devices, this is the maximum Ethernet frame size, including the Ethernet header (14 octets), but
91 /// *not* including the Ethernet FCS (4 octets). Therefore, Ethernet MTU = IP MTU + 14.
92 ///
93 /// Note that in Linux and other OSes, "MTU" is the IP MTU, not the Ethernet MTU, even for Ethernet
94 /// devices. This is a common source of confusion.
95 ///
96 /// Most common IP MTU is 1500. Minimum is 576 (for IPv4) or 1280 (for IPv6). Maximum is 9216 octets.
97 pub max_transmission_unit: usize,
98
99 /// Maximum burst size, in terms of MTU.
100 ///
101 /// The network device is unable to send or receive bursts large than the value returned
102 /// by this function.
103 ///
104 /// If `None`, there is no fixed limit on burst size, e.g. if network buffers are
105 /// dynamically allocated.
106 pub max_burst_size: Option<usize>,
107
108 /// Checksum behavior.
109 ///
110 /// If the network device is capable of verifying or computing checksums for some protocols,
111 /// it can request that the stack not do so in software to improve performance.
112 pub checksum: ChecksumCapabilities,
113}
114
115/// Type of medium of a device.
116#[derive(Debug, Eq, PartialEq, Copy, Clone)]
117#[cfg_attr(feature = "defmt", derive(defmt::Format))]
118pub enum Medium {
119 /// Ethernet medium. Devices of this type send and receive Ethernet frames,
120 /// and interfaces using it must do neighbor discovery via ARP or NDISC.
121 ///
122 /// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode.
123 Ethernet,
124
125 /// IP medium. Devices of this type send and receive IP frames, without an
126 /// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done.
127 ///
128 /// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
129 Ip,
130}
131
132impl Default for Medium {
133 fn default() -> Medium {
134 Medium::Ethernet
135 }
136}
137
138/// A description of checksum behavior for every supported protocol.
139#[derive(Debug, Clone, Default)]
140#[cfg_attr(feature = "defmt", derive(defmt::Format))]
141#[non_exhaustive]
142pub struct ChecksumCapabilities {
143 pub ipv4: Checksum,
144 pub udp: Checksum,
145 pub tcp: Checksum,
146 pub icmpv4: Checksum,
147 pub icmpv6: Checksum,
148}
149
150/// A description of checksum behavior for a particular protocol.
151#[derive(Debug, Clone, Copy)]
152#[cfg_attr(feature = "defmt", derive(defmt::Format))]
153pub enum Checksum {
154 /// Verify checksum when receiving and compute checksum when sending.
155 Both,
156 /// Verify checksum when receiving.
157 Rx,
158 /// Compute checksum before sending.
159 Tx,
160 /// Ignore checksum completely.
161 None,
162}
163
164impl Default for Checksum {
165 fn default() -> Checksum {
166 Checksum::Both
167 }
168}
169
170#[derive(PartialEq, Eq, Clone, Copy)]
171#[cfg_attr(feature = "defmt", derive(defmt::Format))]
172pub enum LinkState {
173 Down,
174 Up,
175}
diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml
index 2d853762e..9214fd17e 100644
--- a/embassy-net/Cargo.toml
+++ b/embassy-net/Cargo.toml
@@ -8,14 +8,14 @@ license = "MIT OR Apache-2.0"
8[package.metadata.embassy_docs] 8[package.metadata.embassy_docs]
9src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/" 9src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-v$VERSION/embassy-net/src/"
10src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/" 10src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/src/"
11features = ["pool-4", "defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip"] 11features = ["defmt", "tcp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip"]
12target = "thumbv7em-none-eabi" 12target = "thumbv7em-none-eabi"
13 13
14[features] 14[features]
15default = [] 15default = []
16std = [] 16std = []
17 17
18defmt = ["dep:defmt", "smoltcp/defmt"] 18defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"]
19 19
20nightly = ["dep:embedded-io", "embedded-io?/async", "dep:embedded-nal-async"] 20nightly = ["dep:embedded-io", "embedded-io?/async", "dep:embedded-nal-async"]
21unstable-traits = [] 21unstable-traits = []
@@ -28,18 +28,12 @@ proto-ipv6 = ["smoltcp/proto-ipv6"]
28medium-ethernet = ["smoltcp/medium-ethernet"] 28medium-ethernet = ["smoltcp/medium-ethernet"]
29medium-ip = ["smoltcp/medium-ip"] 29medium-ip = ["smoltcp/medium-ip"]
30 30
31pool-4 = []
32pool-8 = []
33pool-16 = []
34pool-32 = []
35pool-64 = []
36pool-128 = []
37
38[dependencies] 31[dependencies]
39 32
40defmt = { version = "0.3", optional = true } 33defmt = { version = "0.3", optional = true }
41log = { version = "0.4.14", optional = true } 34log = { version = "0.4.14", optional = true }
42 35
36embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
43embassy-time = { version = "0.1.0", path = "../embassy-time" } 37embassy-time = { version = "0.1.0", path = "../embassy-time" }
44embassy-sync = { version = "0.1.0", path = "../embassy-sync" } 38embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
45embedded-io = { version = "0.4.0", optional = true } 39embedded-io = { version = "0.4.0", optional = true }
@@ -52,6 +46,7 @@ stable_deref_trait = { version = "1.2.0", default-features = false }
52futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } 46futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
53atomic-pool = "1.0" 47atomic-pool = "1.0"
54embedded-nal-async = { version = "0.3.0", optional = true } 48embedded-nal-async = { version = "0.3.0", optional = true }
49atomic-polyfill = { version = "1.0" }
55 50
56[dependencies.smoltcp] 51[dependencies.smoltcp]
57version = "0.8.0" 52version = "0.8.0"
diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs
index 5d86ca91e..44f7dc7bd 100644
--- a/embassy-net/src/device.rs
+++ b/embassy-net/src/device.rs
@@ -1,93 +1,20 @@
1use core::task::Context; 1use core::task::Context;
2 2
3use embassy_net_driver::{Capabilities, Checksum, Driver, Medium, RxToken, TxToken};
3use smoltcp::phy; 4use smoltcp::phy;
4pub use smoltcp::phy::{Checksum, ChecksumCapabilities, DeviceCapabilities, Medium};
5 5
6#[derive(PartialEq, Eq, Clone, Copy)] 6pub(crate) struct DriverAdapter<'d, 'c, T>
7pub enum LinkState {
8 Down,
9 Up,
10}
11
12pub trait Device {
13 type RxToken<'a>: RxToken
14 where
15 Self: 'a;
16 type TxToken<'a>: TxToken
17 where
18 Self: 'a;
19
20 fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>;
21 fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>>;
22 fn link_state(&mut self, cx: &mut Context) -> LinkState;
23
24 fn capabilities(&self) -> phy::DeviceCapabilities;
25 fn ethernet_address(&self) -> [u8; 6];
26}
27
28impl<T: ?Sized + Device> Device for &mut T {
29 type RxToken<'a> = T::RxToken<'a>
30 where
31 Self: 'a;
32 type TxToken<'a> = T::TxToken<'a>
33 where
34 Self: 'a;
35
36 fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
37 T::transmit(self, cx)
38 }
39 fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
40 T::receive(self, cx)
41 }
42 fn capabilities(&self) -> phy::DeviceCapabilities {
43 T::capabilities(self)
44 }
45 fn link_state(&mut self, cx: &mut Context) -> LinkState {
46 T::link_state(self, cx)
47 }
48 fn ethernet_address(&self) -> [u8; 6] {
49 T::ethernet_address(self)
50 }
51}
52
53/// A token to receive a single network packet.
54pub trait RxToken {
55 /// Consumes the token to receive a single network packet.
56 ///
57 /// This method receives a packet and then calls the given closure `f` with the raw
58 /// packet bytes as argument.
59 fn consume<R, F>(self, f: F) -> R
60 where
61 F: FnOnce(&mut [u8]) -> R;
62}
63
64/// A token to transmit a single network packet.
65pub trait TxToken {
66 /// Consumes the token to send a single network packet.
67 ///
68 /// This method constructs a transmit buffer of size `len` and calls the passed
69 /// closure `f` with a mutable reference to that buffer. The closure should construct
70 /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure
71 /// returns, the transmit buffer is sent out.
72 fn consume<R, F>(self, len: usize, f: F) -> R
73 where
74 F: FnOnce(&mut [u8]) -> R;
75}
76
77///////////////////////////
78
79pub(crate) struct DeviceAdapter<'d, 'c, T>
80where 7where
81 T: Device, 8 T: Driver,
82{ 9{
83 // must be Some when actually using this to rx/tx 10 // must be Some when actually using this to rx/tx
84 pub cx: Option<&'d mut Context<'c>>, 11 pub cx: Option<&'d mut Context<'c>>,
85 pub inner: &'d mut T, 12 pub inner: &'d mut T,
86} 13}
87 14
88impl<'d, 'c, T> phy::Device for DeviceAdapter<'d, 'c, T> 15impl<'d, 'c, T> phy::Device for DriverAdapter<'d, 'c, T>
89where 16where
90 T: Device, 17 T: Driver,
91{ 18{
92 type RxToken<'a> = RxTokenAdapter<T::RxToken<'a>> where Self: 'a; 19 type RxToken<'a> = RxTokenAdapter<T::RxToken<'a>> where Self: 'a;
93 type TxToken<'a> = TxTokenAdapter<T::TxToken<'a>> where Self: 'a; 20 type TxToken<'a> = TxTokenAdapter<T::TxToken<'a>> where Self: 'a;
@@ -105,7 +32,39 @@ where
105 32
106 /// Get a description of device capabilities. 33 /// Get a description of device capabilities.
107 fn capabilities(&self) -> phy::DeviceCapabilities { 34 fn capabilities(&self) -> phy::DeviceCapabilities {
108 self.inner.capabilities() 35 fn convert(c: Checksum) -> phy::Checksum {
36 match c {
37 Checksum::Both => phy::Checksum::Both,
38 Checksum::Tx => phy::Checksum::Tx,
39 Checksum::Rx => phy::Checksum::Rx,
40 Checksum::None => phy::Checksum::None,
41 }
42 }
43 let caps: Capabilities = self.inner.capabilities();
44 let mut smolcaps = phy::DeviceCapabilities::default();
45
46 smolcaps.max_transmission_unit = caps.max_transmission_unit;
47 smolcaps.max_burst_size = caps.max_burst_size;
48 smolcaps.medium = match caps.medium {
49 #[cfg(feature = "medium-ethernet")]
50 Medium::Ethernet => phy::Medium::Ethernet,
51 #[cfg(feature = "medium-ip")]
52 Medium::Ip => phy::Medium::Ip,
53 _ => panic!(
54 "Unsupported medium {:?}. MAke sure to enable it in embassy-net's Cargo features.",
55 caps.medium
56 ),
57 };
58 smolcaps.checksum.ipv4 = convert(caps.checksum.ipv4);
59 #[cfg(feature = "proto-ipv6")]
60 {
61 smolcaps.checksum.ipv6 = convert(caps.checksum.ipv6);
62 }
63 smolcaps.checksum.tcp = convert(caps.checksum.tcp);
64 smolcaps.checksum.udp = convert(caps.checksum.udp);
65 smolcaps.checksum.icmpv4 = convert(caps.checksum.icmpv4);
66
67 smolcaps
109 } 68 }
110} 69}
111 70
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs
index afe0d6da0..b58c9cf36 100644
--- a/embassy-net/src/lib.rs
+++ b/embassy-net/src/lib.rs
@@ -18,6 +18,7 @@ use core::cell::RefCell;
18use core::future::{poll_fn, Future}; 18use core::future::{poll_fn, Future};
19use core::task::{Context, Poll}; 19use core::task::{Context, Poll};
20 20
21use embassy_net_driver::{Driver, LinkState, Medium};
21use embassy_sync::waitqueue::WakerRegistration; 22use embassy_sync::waitqueue::WakerRegistration;
22use embassy_time::{Instant, Timer}; 23use embassy_time::{Instant, Timer};
23use futures::pin_mut; 24use futures::pin_mut;
@@ -27,8 +28,6 @@ use smoltcp::iface::SocketHandle;
27use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage}; 28use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage};
28#[cfg(feature = "medium-ethernet")] 29#[cfg(feature = "medium-ethernet")]
29use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes}; 30use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes};
30#[cfg(feature = "medium-ethernet")]
31use smoltcp::phy::Medium;
32#[cfg(feature = "dhcpv4")] 31#[cfg(feature = "dhcpv4")]
33use smoltcp::socket::dhcpv4; 32use smoltcp::socket::dhcpv4;
34// smoltcp reexports 33// smoltcp reexports
@@ -41,7 +40,7 @@ pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr};
41#[cfg(feature = "udp")] 40#[cfg(feature = "udp")]
42pub use smoltcp::{socket::udp::PacketMetadata, wire::IpListenEndpoint}; 41pub use smoltcp::{socket::udp::PacketMetadata, wire::IpListenEndpoint};
43 42
44use crate::device::{Device, DeviceAdapter, LinkState}; 43use crate::device::DriverAdapter;
45 44
46const LOCAL_PORT_MIN: u16 = 1025; 45const LOCAL_PORT_MIN: u16 = 1025;
47const LOCAL_PORT_MAX: u16 = 65535; 46const LOCAL_PORT_MAX: u16 = 65535;
@@ -82,12 +81,12 @@ pub enum ConfigStrategy {
82 Dhcp, 81 Dhcp,
83} 82}
84 83
85pub struct Stack<D: Device> { 84pub struct Stack<D: Driver> {
86 pub(crate) socket: RefCell<SocketStack>, 85 pub(crate) socket: RefCell<SocketStack>,
87 inner: RefCell<Inner<D>>, 86 inner: RefCell<Inner<D>>,
88} 87}
89 88
90struct Inner<D: Device> { 89struct Inner<D: Driver> {
91 device: D, 90 device: D,
92 link_up: bool, 91 link_up: bool,
93 config: Option<Config>, 92 config: Option<Config>,
@@ -102,7 +101,7 @@ pub(crate) struct SocketStack {
102 next_local_port: u16, 101 next_local_port: u16,
103} 102}
104 103
105impl<D: Device + 'static> Stack<D> { 104impl<D: Driver + 'static> Stack<D> {
106 pub fn new<const ADDR: usize, const SOCK: usize, const NEIGH: usize>( 105 pub fn new<const ADDR: usize, const SOCK: usize, const NEIGH: usize>(
107 mut device: D, 106 mut device: D,
108 config: ConfigStrategy, 107 config: ConfigStrategy,
@@ -130,7 +129,7 @@ impl<D: Device + 'static> Stack<D> {
130 b = b.routes(Routes::new(&mut resources.routes[..])); 129 b = b.routes(Routes::new(&mut resources.routes[..]));
131 } 130 }
132 131
133 let iface = b.finalize(&mut DeviceAdapter { 132 let iface = b.finalize(&mut DriverAdapter {
134 inner: &mut device, 133 inner: &mut device,
135 cx: None, 134 cx: None,
136 }); 135 });
@@ -211,7 +210,7 @@ impl SocketStack {
211 } 210 }
212} 211}
213 212
214impl<D: Device + 'static> Inner<D> { 213impl<D: Driver + 'static> Inner<D> {
215 fn apply_config(&mut self, s: &mut SocketStack, config: Config) { 214 fn apply_config(&mut self, s: &mut SocketStack, config: Config) {
216 #[cfg(feature = "medium-ethernet")] 215 #[cfg(feature = "medium-ethernet")]
217 let medium = self.device.capabilities().medium; 216 let medium = self.device.capabilities().medium;
@@ -263,7 +262,7 @@ impl<D: Device + 'static> Inner<D> {
263 s.waker.register(cx.waker()); 262 s.waker.register(cx.waker());
264 263
265 let timestamp = instant_to_smoltcp(Instant::now()); 264 let timestamp = instant_to_smoltcp(Instant::now());
266 let mut smoldev = DeviceAdapter { 265 let mut smoldev = DriverAdapter {
267 cx: Some(cx), 266 cx: Some(cx),
268 inner: &mut self.device, 267 inner: &mut self.device,
269 }; 268 };
diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs
index 55cbda455..0fbf0c91b 100644
--- a/embassy-net/src/tcp.rs
+++ b/embassy-net/src/tcp.rs
@@ -3,12 +3,12 @@ use core::future::poll_fn;
3use core::mem; 3use core::mem;
4use core::task::Poll; 4use core::task::Poll;
5 5
6use embassy_net_driver::Driver;
6use smoltcp::iface::{Interface, SocketHandle}; 7use smoltcp::iface::{Interface, SocketHandle};
7use smoltcp::socket::tcp; 8use smoltcp::socket::tcp;
8use smoltcp::time::Duration; 9use smoltcp::time::Duration;
9use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; 10use smoltcp::wire::{IpEndpoint, IpListenEndpoint};
10 11
11use crate::device::Device;
12use crate::{SocketStack, Stack}; 12use crate::{SocketStack, Stack};
13 13
14#[derive(PartialEq, Eq, Clone, Copy, Debug)] 14#[derive(PartialEq, Eq, Clone, Copy, Debug)]
@@ -66,7 +66,7 @@ impl<'a> TcpWriter<'a> {
66} 66}
67 67
68impl<'a> TcpSocket<'a> { 68impl<'a> TcpSocket<'a> {
69 pub fn new<D: Device>(stack: &'a Stack<D>, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { 69 pub fn new<D: Driver>(stack: &'a Stack<D>, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self {
70 let s = &mut *stack.socket.borrow_mut(); 70 let s = &mut *stack.socket.borrow_mut();
71 let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; 71 let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) };
72 let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; 72 let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) };
@@ -329,26 +329,26 @@ pub mod client {
329 use core::cell::UnsafeCell; 329 use core::cell::UnsafeCell;
330 use core::mem::MaybeUninit; 330 use core::mem::MaybeUninit;
331 use core::ptr::NonNull; 331 use core::ptr::NonNull;
332 use core::sync::atomic::{AtomicBool, Ordering};
333 332
333 use atomic_polyfill::{AtomicBool, Ordering};
334 use embedded_nal_async::IpAddr; 334 use embedded_nal_async::IpAddr;
335 335
336 use super::*; 336 use super::*;
337 337
338 /// TCP client capable of creating up to N multiple connections with tx and rx buffers according to TX_SZ and RX_SZ. 338 /// TCP client capable of creating up to N multiple connections with tx and rx buffers according to TX_SZ and RX_SZ.
339 pub struct TcpClient<'d, D: Device, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> { 339 pub struct TcpClient<'d, D: Driver, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> {
340 stack: &'d Stack<D>, 340 stack: &'d Stack<D>,
341 state: &'d TcpClientState<N, TX_SZ, RX_SZ>, 341 state: &'d TcpClientState<N, TX_SZ, RX_SZ>,
342 } 342 }
343 343
344 impl<'d, D: Device, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, D, N, TX_SZ, RX_SZ> { 344 impl<'d, D: Driver, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, D, N, TX_SZ, RX_SZ> {
345 /// Create a new TcpClient 345 /// Create a new TcpClient
346 pub fn new(stack: &'d Stack<D>, state: &'d TcpClientState<N, TX_SZ, RX_SZ>) -> Self { 346 pub fn new(stack: &'d Stack<D>, state: &'d TcpClientState<N, TX_SZ, RX_SZ>) -> Self {
347 Self { stack, state } 347 Self { stack, state }
348 } 348 }
349 } 349 }
350 350
351 impl<'d, D: Device, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_nal_async::TcpConnect 351 impl<'d, D: Driver, const N: usize, const TX_SZ: usize, const RX_SZ: usize> embedded_nal_async::TcpConnect
352 for TcpClient<'d, D, N, TX_SZ, RX_SZ> 352 for TcpClient<'d, D, N, TX_SZ, RX_SZ>
353 { 353 {
354 type Error = Error; 354 type Error = Error;
@@ -386,7 +386,7 @@ pub mod client {
386 } 386 }
387 387
388 impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpConnection<'d, N, TX_SZ, RX_SZ> { 388 impl<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpConnection<'d, N, TX_SZ, RX_SZ> {
389 fn new<D: Device>(stack: &'d Stack<D>, state: &'d TcpClientState<N, TX_SZ, RX_SZ>) -> Result<Self, Error> { 389 fn new<D: Driver>(stack: &'d Stack<D>, state: &'d TcpClientState<N, TX_SZ, RX_SZ>) -> Result<Self, Error> {
390 let mut bufs = state.pool.alloc().ok_or(Error::ConnectionReset)?; 390 let mut bufs = state.pool.alloc().ok_or(Error::ConnectionReset)?;
391 Ok(Self { 391 Ok(Self {
392 socket: unsafe { TcpSocket::new(stack, &mut bufs.as_mut().0, &mut bufs.as_mut().1) }, 392 socket: unsafe { TcpSocket::new(stack, &mut bufs.as_mut().0, &mut bufs.as_mut().1) },
diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs
index 2f5334df3..0ee8c6e19 100644
--- a/embassy-net/src/udp.rs
+++ b/embassy-net/src/udp.rs
@@ -3,11 +3,12 @@ use core::future::poll_fn;
3use core::mem; 3use core::mem;
4use core::task::Poll; 4use core::task::Poll;
5 5
6use embassy_net_driver::Driver;
6use smoltcp::iface::{Interface, SocketHandle}; 7use smoltcp::iface::{Interface, SocketHandle};
7use smoltcp::socket::udp::{self, PacketMetadata}; 8use smoltcp::socket::udp::{self, PacketMetadata};
8use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; 9use smoltcp::wire::{IpEndpoint, IpListenEndpoint};
9 10
10use crate::{Device, SocketStack, Stack}; 11use crate::{SocketStack, Stack};
11 12
12#[derive(PartialEq, Eq, Clone, Copy, Debug)] 13#[derive(PartialEq, Eq, Clone, Copy, Debug)]
13#[cfg_attr(feature = "defmt", derive(defmt::Format))] 14#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -31,7 +32,7 @@ pub struct UdpSocket<'a> {
31} 32}
32 33
33impl<'a> UdpSocket<'a> { 34impl<'a> UdpSocket<'a> {
34 pub fn new<D: Device>( 35 pub fn new<D: Driver>(
35 stack: &'a Stack<D>, 36 stack: &'a Stack<D>,
36 rx_meta: &'a mut [PacketMetadata], 37 rx_meta: &'a mut [PacketMetadata],
37 rx_buffer: &'a mut [u8], 38 rx_buffer: &'a mut [u8],
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 0c491ee46..67996cca4 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -39,7 +39,7 @@ embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
39embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]} 39embassy-cortex-m = { version = "0.1.0", path = "../embassy-cortex-m", features = ["prio-bits-4"]}
40embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" } 40embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common" }
41embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } 41embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" }
42embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } 42embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
43embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } 43embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true }
44 44
45embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } 45embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] }
@@ -75,7 +75,7 @@ quote = "1.0.15"
75stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]} 75stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", default-features = false, features = ["metadata"]}
76 76
77[features] 77[features]
78defmt = ["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"] 78defmt = ["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"]
79sdmmc-rs = ["embedded-sdmmc"] 79sdmmc-rs = ["embedded-sdmmc"]
80memory-x = ["stm32-metapac/memory-x"] 80memory-x = ["stm32-metapac/memory-x"]
81subghz = [] 81subghz = []
diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs
index fd1b48530..9f62b61ee 100644
--- a/embassy-stm32/src/eth/mod.rs
+++ b/embassy-stm32/src/eth/mod.rs
@@ -1,14 +1,17 @@
1#![macro_use] 1#![macro_use]
2#![cfg_attr(not(feature = "embassy-net"), allow(unused))]
3 2
4#[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")] 3#[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1/mod.rs")]
5#[cfg_attr(eth_v2, path = "v2/mod.rs")] 4#[cfg_attr(eth_v2, path = "v2/mod.rs")]
6mod _version; 5mod _version;
7pub mod generic_smi; 6pub mod generic_smi;
8 7
9pub use _version::*; 8use core::task::Context;
9
10use embassy_net_driver::{Capabilities, LinkState};
10use embassy_sync::waitqueue::AtomicWaker; 11use embassy_sync::waitqueue::AtomicWaker;
11 12
13pub use self::_version::*;
14
12#[allow(unused)] 15#[allow(unused)]
13const MTU: usize = 1514; 16const MTU: usize = 1514;
14const TX_BUFFER_SIZE: usize = 1514; 17const TX_BUFFER_SIZE: usize = 1514;
@@ -40,92 +43,84 @@ impl<const TX: usize, const RX: usize> PacketQueue<TX, RX> {
40 43
41static WAKER: AtomicWaker = AtomicWaker::new(); 44static WAKER: AtomicWaker = AtomicWaker::new();
42 45
43#[cfg(feature = "embassy-net")] 46impl<'d, T: Instance, P: PHY> embassy_net_driver::Driver for Ethernet<'d, T, P> {
44mod embassy_net_impl { 47 type RxToken<'a> = RxToken<'a, 'd> where Self: 'a;
45 use core::task::Context; 48 type TxToken<'a> = TxToken<'a, 'd> where Self: 'a;
46
47 use embassy_net::device::{Device, DeviceCapabilities, LinkState};
48
49 use super::*;
50
51 impl<'d, T: Instance, P: PHY> Device for Ethernet<'d, T, P> {
52 type RxToken<'a> = RxToken<'a, 'd> where Self: 'a;
53 type TxToken<'a> = TxToken<'a, 'd> where Self: 'a;
54 49
55 fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { 50 fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
56 WAKER.register(cx.waker()); 51 WAKER.register(cx.waker());
57 if self.rx.available().is_some() && self.tx.available().is_some() { 52 if self.rx.available().is_some() && self.tx.available().is_some() {
58 Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx })) 53 Some((RxToken { rx: &mut self.rx }, TxToken { tx: &mut self.tx }))
59 } else { 54 } else {
60 None 55 None
61 }
62 }
63
64 fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
65 WAKER.register(cx.waker());
66 if self.tx.available().is_some() {
67 Some(TxToken { tx: &mut self.tx })
68 } else {
69 None
70 }
71 } 56 }
57 }
72 58
73 fn capabilities(&self) -> DeviceCapabilities { 59 fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
74 let mut caps = DeviceCapabilities::default(); 60 WAKER.register(cx.waker());
75 caps.max_transmission_unit = MTU; 61 if self.tx.available().is_some() {
76 caps.max_burst_size = Some(self.tx.len()); 62 Some(TxToken { tx: &mut self.tx })
77 caps 63 } else {
64 None
78 } 65 }
66 }
79 67
80 fn link_state(&mut self, cx: &mut Context) -> LinkState { 68 fn capabilities(&self) -> Capabilities {
81 // TODO: wake cx.waker on link state change 69 let mut caps = Capabilities::default();
82 cx.waker().wake_by_ref(); 70 caps.max_transmission_unit = MTU;
83 if P::poll_link(self) { 71 caps.max_burst_size = Some(self.tx.len());
84 LinkState::Up 72 caps
85 } else { 73 }
86 LinkState::Down
87 }
88 }
89 74
90 fn ethernet_address(&self) -> [u8; 6] { 75 fn link_state(&mut self, cx: &mut Context) -> LinkState {
91 self.mac_addr 76 // TODO: wake cx.waker on link state change
77 cx.waker().wake_by_ref();
78 if P::poll_link(self) {
79 LinkState::Up
80 } else {
81 LinkState::Down
92 } 82 }
93 } 83 }
94 84
95 pub struct RxToken<'a, 'd> { 85 fn ethernet_address(&self) -> [u8; 6] {
96 rx: &'a mut RDesRing<'d>, 86 self.mac_addr
97 } 87 }
88}
98 89
99 impl<'a, 'd> embassy_net::device::RxToken for RxToken<'a, 'd> { 90pub struct RxToken<'a, 'd> {
100 fn consume<R, F>(self, f: F) -> R 91 rx: &'a mut RDesRing<'d>,
101 where 92}
102 F: FnOnce(&mut [u8]) -> R,
103 {
104 // NOTE(unwrap): we checked the queue wasn't full when creating the token.
105 let pkt = unwrap!(self.rx.available());
106 let r = f(pkt);
107 self.rx.pop_packet();
108 r
109 }
110 }
111 93
112 pub struct TxToken<'a, 'd> { 94impl<'a, 'd> embassy_net_driver::RxToken for RxToken<'a, 'd> {
113 tx: &'a mut TDesRing<'d>, 95 fn consume<R, F>(self, f: F) -> R
96 where
97 F: FnOnce(&mut [u8]) -> R,
98 {
99 // NOTE(unwrap): we checked the queue wasn't full when creating the token.
100 let pkt = unwrap!(self.rx.available());
101 let r = f(pkt);
102 self.rx.pop_packet();
103 r
114 } 104 }
105}
115 106
116 impl<'a, 'd> embassy_net::device::TxToken for TxToken<'a, 'd> { 107pub struct TxToken<'a, 'd> {
117 fn consume<R, F>(self, len: usize, f: F) -> R 108 tx: &'a mut TDesRing<'d>,
118 where 109}
119 F: FnOnce(&mut [u8]) -> R, 110
120 { 111impl<'a, 'd> embassy_net_driver::TxToken for TxToken<'a, 'd> {
121 // NOTE(unwrap): we checked the queue wasn't full when creating the token. 112 fn consume<R, F>(self, len: usize, f: F) -> R
122 let pkt = unwrap!(self.tx.available()); 113 where
123 let r = f(&mut pkt[..len]); 114 F: FnOnce(&mut [u8]) -> R,
124 self.tx.transmit(len); 115 {
125 r 116 // NOTE(unwrap): we checked the queue wasn't full when creating the token.
126 } 117 let pkt = unwrap!(self.tx.available());
118 let r = f(&mut pkt[..len]);
119 self.tx.transmit(len);
120 r
127 } 121 }
128} 122}
123
129/// Station Management Interface (SMI) on an ethernet PHY 124/// Station Management Interface (SMI) on an ethernet PHY
130/// 125///
131/// # Safety 126/// # Safety
diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs
index de36d3da1..9c0f4d66d 100644
--- a/embassy-stm32/src/eth/v1/mod.rs
+++ b/embassy-stm32/src/eth/v1/mod.rs
@@ -13,7 +13,7 @@ pub(crate) use self::rx_desc::{RDes, RDesRing};
13pub(crate) use self::tx_desc::{TDes, TDesRing}; 13pub(crate) use self::tx_desc::{TDes, TDesRing};
14use super::*; 14use super::*;
15use crate::gpio::sealed::{AFType, Pin as __GpioPin}; 15use crate::gpio::sealed::{AFType, Pin as __GpioPin};
16use crate::gpio::{AnyPin, Speed}; 16use crate::gpio::AnyPin;
17#[cfg(eth_v1a)] 17#[cfg(eth_v1a)]
18use crate::pac::AFIO; 18use crate::pac::AFIO;
19#[cfg(any(eth_v1b, eth_v1c))] 19#[cfg(any(eth_v1b, eth_v1c))]
@@ -66,7 +66,7 @@ macro_rules! config_pins {
66 critical_section::with(|_| { 66 critical_section::with(|_| {
67 $( 67 $(
68 $pin.set_as_af($pin.af_num(), AFType::OutputPushPull); 68 $pin.set_as_af($pin.af_num(), AFType::OutputPushPull);
69 $pin.set_speed(Speed::VeryHigh); 69 $pin.set_speed(crate::gpio::Speed::VeryHigh);
70 )* 70 )*
71 }) 71 })
72 }; 72 };
diff --git a/embassy-usb-driver/Cargo.toml b/embassy-usb-driver/Cargo.toml
index d22bf7d72..d658f9ec7 100644
--- a/embassy-usb-driver/Cargo.toml
+++ b/embassy-usb-driver/Cargo.toml
@@ -14,4 +14,3 @@ target = "thumbv7em-none-eabi"
14 14
15[dependencies] 15[dependencies]
16defmt = { version = "0.3", optional = true } 16defmt = { version = "0.3", optional = true }
17log = { version = "0.4.14", optional = true }
diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml
index 1e72ce682..1e567bb94 100644
--- a/embassy-usb/Cargo.toml
+++ b/embassy-usb/Cargo.toml
@@ -19,7 +19,7 @@ default = ["usbd-hid"]
19embassy-futures = { version = "0.1.0", path = "../embassy-futures" } 19embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
20embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } 20embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" }
21embassy-sync = { version = "0.1.0", path = "../embassy-sync" } 21embassy-sync = { version = "0.1.0", path = "../embassy-sync" }
22embassy-net = { version = "0.1.0", path = "../embassy-net", optional = true } 22embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
23 23
24defmt = { version = "0.3", optional = true } 24defmt = { version = "0.3", optional = true }
25log = { version = "0.4.14", optional = true } 25log = { version = "0.4.14", optional = true }
diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs
index 60bbfd8d4..7ecf693d2 100644
--- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs
+++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs
@@ -1,81 +1,45 @@
1use core::cell::RefCell;
2use core::mem::MaybeUninit;
3use core::task::Context;
4
5use embassy_futures::select::{select, Either}; 1use embassy_futures::select::{select, Either};
6use embassy_net::device::{Device as DeviceTrait, DeviceCapabilities, LinkState, Medium}; 2use embassy_net_driver_channel as ch;
7use embassy_sync::blocking_mutex::raw::NoopRawMutex; 3use embassy_net_driver_channel::driver::LinkState;
8use embassy_sync::blocking_mutex::Mutex;
9use embassy_sync::waitqueue::WakerRegistration;
10use embassy_usb_driver::Driver; 4use embassy_usb_driver::Driver;
11 5
12use super::{CdcNcmClass, Receiver, Sender}; 6use super::{CdcNcmClass, Receiver, Sender};
13 7
14pub struct State<'d, const MTU: usize, const N_RX: usize, const N_TX: usize> { 8pub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> {
15 rx: [PacketBuf<MTU>; N_RX], 9 ch_state: ch::State<MTU, N_RX, N_TX>,
16 tx: [PacketBuf<MTU>; N_TX],
17 inner: MaybeUninit<StateInner<'d, MTU>>,
18} 10}
19 11
20impl<'d, const MTU: usize, const N_RX: usize, const N_TX: usize> State<'d, MTU, N_RX, N_TX> { 12impl<const MTU: usize, const N_RX: usize, const N_TX: usize> State<MTU, N_RX, N_TX> {
21 const NEW_PACKET: PacketBuf<MTU> = PacketBuf::new();
22
23 pub const fn new() -> Self { 13 pub const fn new() -> Self {
24 Self { 14 Self {
25 rx: [Self::NEW_PACKET; N_RX], 15 ch_state: ch::State::new(),
26 tx: [Self::NEW_PACKET; N_TX],
27 inner: MaybeUninit::uninit(),
28 } 16 }
29 } 17 }
30} 18}
31 19
32struct StateInner<'d, const MTU: usize> {
33 rx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf<MTU>>,
34 tx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf<MTU>>,
35 link_state: Mutex<NoopRawMutex, RefCell<LinkStateState>>,
36}
37
38/// State of the LinkState
39struct LinkStateState {
40 state: LinkState,
41 waker: WakerRegistration,
42}
43
44pub struct Runner<'d, D: Driver<'d>, const MTU: usize> { 20pub struct Runner<'d, D: Driver<'d>, const MTU: usize> {
45 tx_usb: Sender<'d, D>, 21 tx_usb: Sender<'d, D>,
46 tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf<MTU>>,
47 rx_usb: Receiver<'d, D>, 22 rx_usb: Receiver<'d, D>,
48 rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>, 23 ch: ch::Runner<'d, MTU>,
49 link_state: &'d Mutex<NoopRawMutex, RefCell<LinkStateState>>,
50} 24}
51 25
52impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> { 26impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> {
53 pub async fn run(mut self) -> ! { 27 pub async fn run(mut self) -> ! {
28 let (mut rx_chan, mut tx_chan) = self.ch.split();
54 let rx_fut = async move { 29 let rx_fut = async move {
55 loop { 30 loop {
56 trace!("WAITING for connection"); 31 trace!("WAITING for connection");
57 self.link_state.lock(|s| { 32 rx_chan.set_link_state(LinkState::Down);
58 let s = &mut *s.borrow_mut();
59 s.state = LinkState::Down;
60 s.waker.wake();
61 });
62 33
63 self.rx_usb.wait_connection().await.unwrap(); 34 self.rx_usb.wait_connection().await.unwrap();
64 35
65 trace!("Connected"); 36 trace!("Connected");
66 self.link_state.lock(|s| { 37 rx_chan.set_link_state(LinkState::Up);
67 let s = &mut *s.borrow_mut();
68 s.state = LinkState::Up;
69 s.waker.wake();
70 });
71 38
72 loop { 39 loop {
73 let p = self.rx_chan.send().await; 40 let p = rx_chan.rx_buf().await;
74 match self.rx_usb.read_packet(&mut p.buf).await { 41 match self.rx_usb.read_packet(p).await {
75 Ok(n) => { 42 Ok(n) => rx_chan.rx_done(n),
76 p.len = n;
77 self.rx_chan.send_done();
78 }
79 Err(e) => { 43 Err(e) => {
80 warn!("error reading packet: {:?}", e); 44 warn!("error reading packet: {:?}", e);
81 break; 45 break;
@@ -86,11 +50,11 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> {
86 }; 50 };
87 let tx_fut = async move { 51 let tx_fut = async move {
88 loop { 52 loop {
89 let p = self.tx_chan.recv().await; 53 let p = tx_chan.tx_buf().await;
90 if let Err(e) = self.tx_usb.write_packet(&p.buf[..p.len]).await { 54 if let Err(e) = self.tx_usb.write_packet(p).await {
91 warn!("Failed to TX packet: {:?}", e); 55 warn!("Failed to TX packet: {:?}", e);
92 } 56 }
93 self.tx_chan.recv_done(); 57 tx_chan.tx_done();
94 } 58 }
95 }; 59 };
96 match select(rx_fut, tx_fut).await { 60 match select(rx_fut, tx_fut).await {
@@ -100,350 +64,26 @@ impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> {
100 } 64 }
101} 65}
102 66
67// would be cool to use a TAIT here, but it gives a "may not live long enough". rustc bug?
68//pub type Device<'d, const MTU: usize> = impl embassy_net_driver_channel::driver::Driver + 'd;
69pub type Device<'d, const MTU: usize> = embassy_net_driver_channel::Device<'d, MTU>;
70
103impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> { 71impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
104 pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>( 72 pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>(
105 self, 73 self,
106 state: &'d mut State<'d, MTU, N_RX, N_TX>, 74 state: &'d mut State<MTU, N_RX, N_TX>,
107 ethernet_address: [u8; 6], 75 ethernet_address: [u8; 6],
108 ) -> (Runner<'d, D, MTU>, Device<'d, MTU>) { 76 ) -> (Runner<'d, D, MTU>, Device<'d, MTU>) {
109 let (tx_usb, rx_usb) = self.split(); 77 let (tx_usb, rx_usb) = self.split();
110 78 let (runner, device) = ch::new(&mut state.ch_state, ethernet_address);
111 let mut caps = DeviceCapabilities::default();
112 caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header
113 caps.medium = Medium::Ethernet;
114
115 let state = state.inner.write(StateInner {
116 rx: zerocopy_channel::Channel::new(&mut state.rx[..]),
117 tx: zerocopy_channel::Channel::new(&mut state.tx[..]),
118 link_state: Mutex::new(RefCell::new(LinkStateState {
119 state: LinkState::Down,
120 waker: WakerRegistration::new(),
121 })),
122 });
123
124 let (rx_sender, rx_receiver) = state.rx.split();
125 let (tx_sender, tx_receiver) = state.tx.split();
126 79
127 ( 80 (
128 Runner { 81 Runner {
129 tx_usb, 82 tx_usb,
130 tx_chan: tx_receiver,
131 rx_usb, 83 rx_usb,
132 rx_chan: rx_sender, 84 ch: runner,
133 link_state: &state.link_state,
134 },
135 Device {
136 caps,
137 ethernet_address,
138 link_state: &state.link_state,
139 rx: rx_receiver,
140 tx: tx_sender,
141 }, 85 },
86 device,
142 ) 87 )
143 } 88 }
144} 89}
145
146pub struct PacketBuf<const MTU: usize> {
147 len: usize,
148 buf: [u8; MTU],
149}
150
151impl<const MTU: usize> PacketBuf<MTU> {
152 pub const fn new() -> Self {
153 Self { len: 0, buf: [0; MTU] }
154 }
155}
156
157pub struct Device<'d, const MTU: usize> {
158 rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf<MTU>>,
159 tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>,
160 link_state: &'d Mutex<NoopRawMutex, RefCell<LinkStateState>>,
161 caps: DeviceCapabilities,
162 ethernet_address: [u8; 6],
163}
164
165impl<'d, const MTU: usize> DeviceTrait for Device<'d, MTU> {
166 type RxToken<'a> = RxToken<'a, MTU> where Self: 'a ;
167 type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ;
168
169 fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
170 if self.rx.poll_recv(cx).is_ready() && self.tx.poll_send(cx).is_ready() {
171 Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() }))
172 } else {
173 None
174 }
175 }
176
177 /// Construct a transmit token.
178 fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
179 if self.tx.poll_send(cx).is_ready() {
180 Some(TxToken { tx: self.tx.borrow() })
181 } else {
182 None
183 }
184 }
185
186 /// Get a description of device capabilities.
187 fn capabilities(&self) -> DeviceCapabilities {
188 self.caps.clone()
189 }
190
191 fn ethernet_address(&self) -> [u8; 6] {
192 self.ethernet_address
193 }
194
195 fn link_state(&mut self, cx: &mut Context) -> LinkState {
196 self.link_state.lock(|s| {
197 let s = &mut *s.borrow_mut();
198 s.waker.register(cx.waker());
199 s.state
200 })
201 }
202}
203
204pub struct RxToken<'a, const MTU: usize> {
205 rx: zerocopy_channel::Receiver<'a, NoopRawMutex, PacketBuf<MTU>>,
206}
207
208impl<'a, const MTU: usize> embassy_net::device::RxToken for RxToken<'a, MTU> {
209 fn consume<R, F>(mut self, f: F) -> R
210 where
211 F: FnOnce(&mut [u8]) -> R,
212 {
213 // NOTE(unwrap): we checked the queue wasn't full when creating the token.
214 let pkt = unwrap!(self.rx.try_recv());
215 let r = f(&mut pkt.buf[..pkt.len]);
216 self.rx.recv_done();
217 r
218 }
219}
220
221pub struct TxToken<'a, const MTU: usize> {
222 tx: zerocopy_channel::Sender<'a, NoopRawMutex, PacketBuf<MTU>>,
223}
224
225impl<'a, const MTU: usize> embassy_net::device::TxToken for TxToken<'a, MTU> {
226 fn consume<R, F>(mut self, len: usize, f: F) -> R
227 where
228 F: FnOnce(&mut [u8]) -> R,
229 {
230 // NOTE(unwrap): we checked the queue wasn't full when creating the token.
231 let pkt = unwrap!(self.tx.try_send());
232 let r = f(&mut pkt.buf[..len]);
233 pkt.len = len;
234 self.tx.send_done();
235 r
236 }
237}
238
239mod zerocopy_channel {
240 use core::cell::RefCell;
241 use core::future::poll_fn;
242 use core::marker::PhantomData;
243 use core::task::{Context, Poll};
244
245 use embassy_sync::blocking_mutex::raw::RawMutex;
246 use embassy_sync::blocking_mutex::Mutex;
247 use embassy_sync::waitqueue::WakerRegistration;
248
249 pub struct Channel<'a, M: RawMutex, T> {
250 buf: *mut T,
251 phantom: PhantomData<&'a mut T>,
252 state: Mutex<M, RefCell<State>>,
253 }
254
255 impl<'a, M: RawMutex, T> Channel<'a, M, T> {
256 pub fn new(buf: &'a mut [T]) -> Self {
257 let len = buf.len();
258 assert!(len != 0);
259
260 Self {
261 buf: buf.as_mut_ptr(),
262 phantom: PhantomData,
263 state: Mutex::new(RefCell::new(State {
264 len,
265 front: 0,
266 back: 0,
267 full: false,
268 send_waker: WakerRegistration::new(),
269 recv_waker: WakerRegistration::new(),
270 })),
271 }
272 }
273
274 pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) {
275 (Sender { channel: self }, Receiver { channel: self })
276 }
277 }
278
279 pub struct Sender<'a, M: RawMutex, T> {
280 channel: &'a Channel<'a, M, T>,
281 }
282
283 impl<'a, M: RawMutex, T> Sender<'a, M, T> {
284 pub fn borrow(&mut self) -> Sender<'_, M, T> {
285 Sender { channel: self.channel }
286 }
287
288 pub fn try_send(&mut self) -> Option<&mut T> {
289 self.channel.state.lock(|s| {
290 let s = &mut *s.borrow_mut();
291 match s.push_index() {
292 Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }),
293 None => None,
294 }
295 })
296 }
297
298 pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> {
299 self.channel.state.lock(|s| {
300 let s = &mut *s.borrow_mut();
301 match s.push_index() {
302 Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }),
303 None => {
304 s.recv_waker.register(cx.waker());
305 Poll::Pending
306 }
307 }
308 })
309 }
310
311 pub async fn send(&mut self) -> &mut T {
312 let i = poll_fn(|cx| {
313 self.channel.state.lock(|s| {
314 let s = &mut *s.borrow_mut();
315 match s.push_index() {
316 Some(i) => Poll::Ready(i),
317 None => {
318 s.recv_waker.register(cx.waker());
319 Poll::Pending
320 }
321 }
322 })
323 })
324 .await;
325 unsafe { &mut *self.channel.buf.add(i) }
326 }
327
328 pub fn send_done(&mut self) {
329 self.channel.state.lock(|s| s.borrow_mut().push_done())
330 }
331 }
332 pub struct Receiver<'a, M: RawMutex, T> {
333 channel: &'a Channel<'a, M, T>,
334 }
335
336 impl<'a, M: RawMutex, T> Receiver<'a, M, T> {
337 pub fn borrow(&mut self) -> Receiver<'_, M, T> {
338 Receiver { channel: self.channel }
339 }
340
341 pub fn try_recv(&mut self) -> Option<&mut T> {
342 self.channel.state.lock(|s| {
343 let s = &mut *s.borrow_mut();
344 match s.pop_index() {
345 Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }),
346 None => None,
347 }
348 })
349 }
350
351 pub fn poll_recv(&mut self, cx: &mut Context) -> Poll<&mut T> {
352 self.channel.state.lock(|s| {
353 let s = &mut *s.borrow_mut();
354 match s.pop_index() {
355 Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }),
356 None => {
357 s.send_waker.register(cx.waker());
358 Poll::Pending
359 }
360 }
361 })
362 }
363
364 pub async fn recv(&mut self) -> &mut T {
365 let i = poll_fn(|cx| {
366 self.channel.state.lock(|s| {
367 let s = &mut *s.borrow_mut();
368 match s.pop_index() {
369 Some(i) => Poll::Ready(i),
370 None => {
371 s.send_waker.register(cx.waker());
372 Poll::Pending
373 }
374 }
375 })
376 })
377 .await;
378 unsafe { &mut *self.channel.buf.add(i) }
379 }
380
381 pub fn recv_done(&mut self) {
382 self.channel.state.lock(|s| s.borrow_mut().pop_done())
383 }
384 }
385
386 struct State {
387 len: usize,
388
389 /// Front index. Always 0..=(N-1)
390 front: usize,
391 /// Back index. Always 0..=(N-1).
392 back: usize,
393
394 /// Used to distinguish "empty" and "full" cases when `front == back`.
395 /// May only be `true` if `front == back`, always `false` otherwise.
396 full: bool,
397
398 send_waker: WakerRegistration,
399 recv_waker: WakerRegistration,
400 }
401
402 impl State {
403 fn increment(&self, i: usize) -> usize {
404 if i + 1 == self.len {
405 0
406 } else {
407 i + 1
408 }
409 }
410
411 fn is_full(&self) -> bool {
412 self.full
413 }
414
415 fn is_empty(&self) -> bool {
416 self.front == self.back && !self.full
417 }
418
419 fn push_index(&mut self) -> Option<usize> {
420 match self.is_full() {
421 true => None,
422 false => Some(self.back),
423 }
424 }
425
426 fn push_done(&mut self) {
427 assert!(!self.is_full());
428 self.back = self.increment(self.back);
429 if self.back == self.front {
430 self.full = true;
431 }
432 self.send_waker.wake();
433 }
434
435 fn pop_index(&mut self) -> Option<usize> {
436 match self.is_empty() {
437 true => None,
438 false => Some(self.front),
439 }
440 }
441
442 fn pop_done(&mut self) {
443 assert!(!self.is_empty());
444 self.front = self.increment(self.front);
445 self.full = false;
446 self.recv_waker.wake();
447 }
448 }
449}
diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs
index 2ee47f68c..4954a65bc 100644
--- a/embassy-usb/src/class/cdc_ncm/mod.rs
+++ b/embassy-usb/src/class/cdc_ncm/mod.rs
@@ -21,7 +21,6 @@ use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
21use crate::types::*; 21use crate::types::*;
22use crate::Builder; 22use crate::Builder;
23 23
24#[cfg(feature = "embassy-net")]
25pub mod embassy_net; 24pub mod embassy_net;
26 25
27/// This should be used as `device_class` when building the `UsbDevice`. 26/// This should be used as `device_class` when building the `UsbDevice`.
diff --git a/examples/nrf/Cargo.toml b/examples/nrf/Cargo.toml
index a980a505c..994823a9e 100644
--- a/examples/nrf/Cargo.toml
+++ b/examples/nrf/Cargo.toml
@@ -15,8 +15,8 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de
15embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 15embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
16embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 16embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
17embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } 17embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
18embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"], optional = true } 18embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"], optional = true }
19embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"], optional = true } 19embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"], optional = true }
20embedded-io = "0.4.0" 20embedded-io = "0.4.0"
21embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true } 21embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["sx126x", "time", "defmt"], optional = true }
22 22
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml
index 31a08cfb6..afd1042a1 100644
--- a/examples/rp/Cargo.toml
+++ b/examples/rp/Cargo.toml
@@ -10,8 +10,8 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de
10embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 10embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
11embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } 11embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
12embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } 12embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio", "critical-section-impl"] }
13embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] } 13embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
14embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } 14embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
15embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 15embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
16embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" } 16embassy-usb-logger = { version = "0.1.0", path = "../../embassy-usb-logger" }
17 17
diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml
index 41680f8f4..45b2a4a4f 100644
--- a/examples/std/Cargo.toml
+++ b/examples/std/Cargo.toml
@@ -8,7 +8,8 @@ license = "MIT OR Apache-2.0"
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] }
11embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dhcpv4", "pool-16"] } 11embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dhcpv4"] }
12embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" }
12embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } 13embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] }
13critical-section = { version = "1.1", features = ["std"] } 14critical-section = { version = "1.1", features = ["std"] }
14 15
diff --git a/examples/std/src/tuntap.rs b/examples/std/src/tuntap.rs
index bb3e194cc..d918a2e62 100644
--- a/examples/std/src/tuntap.rs
+++ b/examples/std/src/tuntap.rs
@@ -4,7 +4,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
4use std::task::Context; 4use std::task::Context;
5 5
6use async_io::Async; 6use async_io::Async;
7use embassy_net::device::{self, Device, DeviceCapabilities, LinkState}; 7use embassy_net_driver::{self, Capabilities, Driver, LinkState};
8use log::*; 8use log::*;
9 9
10pub const SIOCGIFMTU: libc::c_ulong = 0x8921; 10pub const SIOCGIFMTU: libc::c_ulong = 0x8921;
@@ -137,7 +137,7 @@ impl TunTapDevice {
137 } 137 }
138} 138}
139 139
140impl Device for TunTapDevice { 140impl Driver for TunTapDevice {
141 type RxToken<'a> = RxToken where Self: 'a; 141 type RxToken<'a> = RxToken where Self: 'a;
142 type TxToken<'a> = TxToken<'a> where Self: 'a; 142 type TxToken<'a> = TxToken<'a> where Self: 'a;
143 143
@@ -170,8 +170,8 @@ impl Device for TunTapDevice {
170 }) 170 })
171 } 171 }
172 172
173 fn capabilities(&self) -> DeviceCapabilities { 173 fn capabilities(&self) -> Capabilities {
174 let mut caps = DeviceCapabilities::default(); 174 let mut caps = Capabilities::default();
175 caps.max_transmission_unit = self.device.get_ref().mtu; 175 caps.max_transmission_unit = self.device.get_ref().mtu;
176 caps 176 caps
177 } 177 }
@@ -190,7 +190,7 @@ pub struct RxToken {
190 buffer: Vec<u8>, 190 buffer: Vec<u8>,
191} 191}
192 192
193impl device::RxToken for RxToken { 193impl embassy_net_driver::RxToken for RxToken {
194 fn consume<R, F>(mut self, f: F) -> R 194 fn consume<R, F>(mut self, f: F) -> R
195 where 195 where
196 F: FnOnce(&mut [u8]) -> R, 196 F: FnOnce(&mut [u8]) -> R,
@@ -204,7 +204,7 @@ pub struct TxToken<'a> {
204 device: &'a mut Async<TunTap>, 204 device: &'a mut Async<TunTap>,
205} 205}
206 206
207impl<'a> device::TxToken for TxToken<'a> { 207impl<'a> embassy_net_driver::TxToken for TxToken<'a> {
208 fn consume<R, F>(self, len: usize, f: F) -> R 208 fn consume<R, F>(self, len: usize, f: F) -> R
209 where 209 where
210 F: FnOnce(&mut [u8]) -> R, 210 F: FnOnce(&mut [u8]) -> R,
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml
index afdf87000..b80dbbf9c 100644
--- a/examples/stm32f7/Cargo.toml
+++ b/examples/stm32f7/Cargo.toml
@@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0"
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "embassy-net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] }
12embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } 12embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
13embedded-io = { version = "0.4.0", features = ["async"] } 13embedded-io = { version = "0.4.0", features = ["async"] }
14 14
15defmt = "0.3" 15defmt = "0.3"
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml
index 5acf0035d..d30c42b1f 100644
--- a/examples/stm32h7/Cargo.toml
+++ b/examples/stm32h7/Cargo.toml
@@ -8,8 +8,8 @@ license = "MIT OR Apache-2.0"
8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } 8embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 9embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } 10embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "embassy-net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] }
12embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16", "unstable-traits"] } 12embassy-net = { path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "unstable-traits"] }
13embedded-io = { version = "0.4.0", features = ["async"] } 13embedded-io = { version = "0.4.0", features = ["async"] }
14 14
15defmt = "0.3" 15defmt = "0.3"
diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml
index bb8515312..c0accb0d6 100644
--- a/examples/stm32l5/Cargo.toml
+++ b/examples/stm32l5/Cargo.toml
@@ -11,8 +11,8 @@ embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["de
11embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } 11embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
12embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 12embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
13embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] } 13embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "unstable-traits", "memory-x"] }
14embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt", "embassy-net"] } 14embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
15embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } 15embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet"] }
16embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } 16embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
17usbd-hid = "0.6.0" 17usbd-hid = "0.6.0"
18 18