aboutsummaryrefslogtreecommitdiff
path: root/embassy-net/src
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2022-12-13 17:03:51 +0100
committerGitHub <[email protected]>2022-12-13 17:03:51 +0100
commit5b65b0e84377338b615ba3fc16d0a96d2db5c206 (patch)
tree627cc0abdfdf95f504728fceaaf9816575d05e84 /embassy-net/src
parent47747d3b73f392e53ead8ff49cd09fd017df3215 (diff)
parent790e4e1594d455d06c7303a628172244e78af0da (diff)
Merge pull request #1096 from embassy-rs/net-refactor2
net: remove packet pool
Diffstat (limited to 'embassy-net/src')
-rw-r--r--embassy-net/src/device.rs163
-rw-r--r--embassy-net/src/lib.rs313
-rw-r--r--embassy-net/src/packet_pool.rs107
-rw-r--r--embassy-net/src/stack.rs302
-rw-r--r--embassy-net/src/tcp.rs5
-rw-r--r--embassy-net/src/udp.rs3
6 files changed, 395 insertions, 498 deletions
diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs
index 4bdfd7720..5d86ca91e 100644
--- a/embassy-net/src/device.rs
+++ b/embassy-net/src/device.rs
@@ -1,10 +1,7 @@
1use core::task::Waker; 1use core::task::Context;
2 2
3use smoltcp::phy::{Device as SmolDevice, DeviceCapabilities}; 3use smoltcp::phy;
4use smoltcp::time::Instant as SmolInstant; 4pub use smoltcp::phy::{Checksum, ChecksumCapabilities, DeviceCapabilities, Medium};
5
6use crate::packet_pool::PacketBoxExt;
7use crate::{Packet, PacketBox, PacketBuf};
8 5
9#[derive(PartialEq, Eq, Clone, Copy)] 6#[derive(PartialEq, Eq, Clone, Copy)]
10pub enum LinkState { 7pub enum LinkState {
@@ -13,115 +10,133 @@ pub enum LinkState {
13} 10}
14 11
15pub trait Device { 12pub trait Device {
16 fn is_transmit_ready(&mut self) -> bool; 13 type RxToken<'a>: RxToken
17 fn transmit(&mut self, pkt: PacketBuf); 14 where
18 fn receive(&mut self) -> Option<PacketBuf>; 15 Self: 'a;
16 type TxToken<'a>: TxToken
17 where
18 Self: 'a;
19 19
20 fn register_waker(&mut self, waker: &Waker); 20 fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>;
21 fn capabilities(&self) -> DeviceCapabilities; 21 fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>>;
22 fn link_state(&mut self) -> LinkState; 22 fn link_state(&mut self, cx: &mut Context) -> LinkState;
23
24 fn capabilities(&self) -> phy::DeviceCapabilities;
23 fn ethernet_address(&self) -> [u8; 6]; 25 fn ethernet_address(&self) -> [u8; 6];
24} 26}
25 27
26impl<T: ?Sized + Device> Device for &mut T { 28impl<T: ?Sized + Device> Device for &mut T {
27 fn is_transmit_ready(&mut self) -> bool { 29 type RxToken<'a> = T::RxToken<'a>
28 T::is_transmit_ready(self) 30 where
29 } 31 Self: 'a;
30 fn transmit(&mut self, pkt: PacketBuf) { 32 type TxToken<'a> = T::TxToken<'a>
31 T::transmit(self, pkt) 33 where
32 } 34 Self: 'a;
33 fn receive(&mut self) -> Option<PacketBuf> { 35
34 T::receive(self) 36 fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
37 T::transmit(self, cx)
35 } 38 }
36 fn register_waker(&mut self, waker: &Waker) { 39 fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
37 T::register_waker(self, waker) 40 T::receive(self, cx)
38 } 41 }
39 fn capabilities(&self) -> DeviceCapabilities { 42 fn capabilities(&self) -> phy::DeviceCapabilities {
40 T::capabilities(self) 43 T::capabilities(self)
41 } 44 }
42 fn link_state(&mut self) -> LinkState { 45 fn link_state(&mut self, cx: &mut Context) -> LinkState {
43 T::link_state(self) 46 T::link_state(self, cx)
44 } 47 }
45 fn ethernet_address(&self) -> [u8; 6] { 48 fn ethernet_address(&self) -> [u8; 6] {
46 T::ethernet_address(self) 49 T::ethernet_address(self)
47 } 50 }
48} 51}
49 52
50pub struct DeviceAdapter<D: Device> { 53/// A token to receive a single network packet.
51 pub device: D, 54pub trait RxToken {
52 caps: DeviceCapabilities, 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;
53} 62}
54 63
55impl<D: Device> DeviceAdapter<D> { 64/// A token to transmit a single network packet.
56 pub(crate) fn new(device: D) -> Self { 65pub trait TxToken {
57 Self { 66 /// Consumes the token to send a single network packet.
58 caps: device.capabilities(), 67 ///
59 device, 68 /// This method constructs a transmit buffer of size `len` and calls the passed
60 } 69 /// closure `f` with a mutable reference to that buffer. The closure should construct
61 } 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
81 T: Device,
82{
83 // must be Some when actually using this to rx/tx
84 pub cx: Option<&'d mut Context<'c>>,
85 pub inner: &'d mut T,
62} 86}
63 87
64impl<D: Device> SmolDevice for DeviceAdapter<D> { 88impl<'d, 'c, T> phy::Device for DeviceAdapter<'d, 'c, T>
65 type RxToken<'a> = RxToken where Self: 'a; 89where
66 type TxToken<'a> = TxToken<'a, D> where Self: 'a; 90 T: Device,
91{
92 type RxToken<'a> = RxTokenAdapter<T::RxToken<'a>> where Self: 'a;
93 type TxToken<'a> = TxTokenAdapter<T::TxToken<'a>> where Self: 'a;
67 94
68 fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { 95 fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
69 let tx_pkt = PacketBox::new(Packet::new())?; 96 self.inner
70 let rx_pkt = self.device.receive()?; 97 .receive(self.cx.as_deref_mut().unwrap())
71 let rx_token = RxToken { pkt: rx_pkt }; 98 .map(|(rx, tx)| (RxTokenAdapter(rx), TxTokenAdapter(tx)))
72 let tx_token = TxToken {
73 device: &mut self.device,
74 pkt: tx_pkt,
75 };
76
77 Some((rx_token, tx_token))
78 } 99 }
79 100
80 /// Construct a transmit token. 101 /// Construct a transmit token.
81 fn transmit(&mut self) -> Option<Self::TxToken<'_>> { 102 fn transmit(&mut self) -> Option<Self::TxToken<'_>> {
82 if !self.device.is_transmit_ready() { 103 self.inner.transmit(self.cx.as_deref_mut().unwrap()).map(TxTokenAdapter)
83 return None;
84 }
85
86 let tx_pkt = PacketBox::new(Packet::new())?;
87 Some(TxToken {
88 device: &mut self.device,
89 pkt: tx_pkt,
90 })
91 } 104 }
92 105
93 /// Get a description of device capabilities. 106 /// Get a description of device capabilities.
94 fn capabilities(&self) -> DeviceCapabilities { 107 fn capabilities(&self) -> phy::DeviceCapabilities {
95 self.caps.clone() 108 self.inner.capabilities()
96 } 109 }
97} 110}
98 111
99pub struct RxToken { 112pub(crate) struct RxTokenAdapter<T>(T)
100 pkt: PacketBuf, 113where
101} 114 T: RxToken;
102 115
103impl smoltcp::phy::RxToken for RxToken { 116impl<T> phy::RxToken for RxTokenAdapter<T>
104 fn consume<R, F>(mut self, _timestamp: SmolInstant, f: F) -> smoltcp::Result<R> 117where
118 T: RxToken,
119{
120 fn consume<R, F>(self, _timestamp: smoltcp::time::Instant, f: F) -> smoltcp::Result<R>
105 where 121 where
106 F: FnOnce(&mut [u8]) -> smoltcp::Result<R>, 122 F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
107 { 123 {
108 f(&mut self.pkt) 124 self.0.consume(|buf| f(buf))
109 } 125 }
110} 126}
111 127
112pub struct TxToken<'a, D: Device> { 128pub(crate) struct TxTokenAdapter<T>(T)
113 device: &'a mut D, 129where
114 pkt: PacketBox, 130 T: TxToken;
115}
116 131
117impl<'a, D: Device> smoltcp::phy::TxToken for TxToken<'a, D> { 132impl<T> phy::TxToken for TxTokenAdapter<T>
118 fn consume<R, F>(self, _timestamp: SmolInstant, len: usize, f: F) -> smoltcp::Result<R> 133where
134 T: TxToken,
135{
136 fn consume<R, F>(self, _timestamp: smoltcp::time::Instant, len: usize, f: F) -> smoltcp::Result<R>
119 where 137 where
120 F: FnOnce(&mut [u8]) -> smoltcp::Result<R>, 138 F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
121 { 139 {
122 let mut buf = self.pkt.slice(0..len); 140 self.0.consume(len, |buf| f(buf))
123 let r = f(&mut buf)?;
124 self.device.transmit(buf);
125 Ok(r)
126 } 141 }
127} 142}
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs
index edb969842..afe0d6da0 100644
--- a/embassy-net/src/lib.rs
+++ b/embassy-net/src/lib.rs
@@ -8,22 +8,30 @@
8// This mod MUST go first, so that the others see its macros. 8// This mod MUST go first, so that the others see its macros.
9pub(crate) mod fmt; 9pub(crate) mod fmt;
10 10
11mod device; 11pub mod device;
12mod packet_pool;
13mod stack;
14
15pub use device::{Device, LinkState};
16pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf, MTU};
17pub use stack::{Config, ConfigStrategy, Stack, StackResources};
18
19#[cfg(feature = "tcp")] 12#[cfg(feature = "tcp")]
20pub mod tcp; 13pub mod tcp;
21
22#[cfg(feature = "udp")] 14#[cfg(feature = "udp")]
23pub mod udp; 15pub mod udp;
24 16
17use core::cell::RefCell;
18use core::future::{poll_fn, Future};
19use core::task::{Context, Poll};
20
21use embassy_sync::waitqueue::WakerRegistration;
22use embassy_time::{Instant, Timer};
23use futures::pin_mut;
24use heapless::Vec;
25#[cfg(feature = "dhcpv4")]
26use smoltcp::iface::SocketHandle;
27use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage};
28#[cfg(feature = "medium-ethernet")]
29use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes};
30#[cfg(feature = "medium-ethernet")]
31use smoltcp::phy::Medium;
32#[cfg(feature = "dhcpv4")]
33use smoltcp::socket::dhcpv4;
25// smoltcp reexports 34// smoltcp reexports
26pub use smoltcp::phy::{DeviceCapabilities, Medium};
27pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant}; 35pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant};
28#[cfg(feature = "medium-ethernet")] 36#[cfg(feature = "medium-ethernet")]
29pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; 37pub use smoltcp::wire::{EthernetAddress, HardwareAddress};
@@ -32,3 +40,288 @@ pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr};
32pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr}; 40pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr};
33#[cfg(feature = "udp")] 41#[cfg(feature = "udp")]
34pub use smoltcp::{socket::udp::PacketMetadata, wire::IpListenEndpoint}; 42pub use smoltcp::{socket::udp::PacketMetadata, wire::IpListenEndpoint};
43
44use crate::device::{Device, DeviceAdapter, LinkState};
45
46const LOCAL_PORT_MIN: u16 = 1025;
47const LOCAL_PORT_MAX: u16 = 65535;
48
49pub struct StackResources<const ADDR: usize, const SOCK: usize, const NEIGHBOR: usize> {
50 addresses: [IpCidr; ADDR],
51 sockets: [SocketStorage<'static>; SOCK],
52
53 #[cfg(feature = "medium-ethernet")]
54 routes: [Option<(IpCidr, Route)>; 1],
55 #[cfg(feature = "medium-ethernet")]
56 neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR],
57}
58
59impl<const ADDR: usize, const SOCK: usize, const NEIGHBOR: usize> StackResources<ADDR, SOCK, NEIGHBOR> {
60 pub fn new() -> Self {
61 Self {
62 addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32); ADDR],
63 sockets: [SocketStorage::EMPTY; SOCK],
64 #[cfg(feature = "medium-ethernet")]
65 routes: [None; 1],
66 #[cfg(feature = "medium-ethernet")]
67 neighbor_cache: [None; NEIGHBOR],
68 }
69 }
70}
71
72#[derive(Debug, Clone, PartialEq, Eq)]
73pub struct Config {
74 pub address: Ipv4Cidr,
75 pub gateway: Option<Ipv4Address>,
76 pub dns_servers: Vec<Ipv4Address, 3>,
77}
78
79pub enum ConfigStrategy {
80 Static(Config),
81 #[cfg(feature = "dhcpv4")]
82 Dhcp,
83}
84
85pub struct Stack<D: Device> {
86 pub(crate) socket: RefCell<SocketStack>,
87 inner: RefCell<Inner<D>>,
88}
89
90struct Inner<D: Device> {
91 device: D,
92 link_up: bool,
93 config: Option<Config>,
94 #[cfg(feature = "dhcpv4")]
95 dhcp_socket: Option<SocketHandle>,
96}
97
98pub(crate) struct SocketStack {
99 pub(crate) sockets: SocketSet<'static>,
100 pub(crate) iface: Interface<'static>,
101 pub(crate) waker: WakerRegistration,
102 next_local_port: u16,
103}
104
105impl<D: Device + 'static> Stack<D> {
106 pub fn new<const ADDR: usize, const SOCK: usize, const NEIGH: usize>(
107 mut device: D,
108 config: ConfigStrategy,
109 resources: &'static mut StackResources<ADDR, SOCK, NEIGH>,
110 random_seed: u64,
111 ) -> Self {
112 #[cfg(feature = "medium-ethernet")]
113 let medium = device.capabilities().medium;
114
115 #[cfg(feature = "medium-ethernet")]
116 let ethernet_addr = if medium == Medium::Ethernet {
117 device.ethernet_address()
118 } else {
119 [0, 0, 0, 0, 0, 0]
120 };
121
122 let mut b = InterfaceBuilder::new();
123 b = b.ip_addrs(&mut resources.addresses[..]);
124 b = b.random_seed(random_seed);
125
126 #[cfg(feature = "medium-ethernet")]
127 if medium == Medium::Ethernet {
128 b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(ethernet_addr)));
129 b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..]));
130 b = b.routes(Routes::new(&mut resources.routes[..]));
131 }
132
133 let iface = b.finalize(&mut DeviceAdapter {
134 inner: &mut device,
135 cx: None,
136 });
137
138 let sockets = SocketSet::new(&mut resources.sockets[..]);
139
140 let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN;
141
142 let mut inner = Inner {
143 device,
144 link_up: false,
145 config: None,
146 #[cfg(feature = "dhcpv4")]
147 dhcp_socket: None,
148 };
149 let mut socket = SocketStack {
150 sockets,
151 iface,
152 waker: WakerRegistration::new(),
153 next_local_port,
154 };
155
156 match config {
157 ConfigStrategy::Static(config) => inner.apply_config(&mut socket, config),
158 #[cfg(feature = "dhcpv4")]
159 ConfigStrategy::Dhcp => {
160 let handle = socket.sockets.add(smoltcp::socket::dhcpv4::Socket::new());
161 inner.dhcp_socket = Some(handle);
162 }
163 }
164
165 Self {
166 socket: RefCell::new(socket),
167 inner: RefCell::new(inner),
168 }
169 }
170
171 fn with<R>(&self, f: impl FnOnce(&SocketStack, &Inner<D>) -> R) -> R {
172 f(&*self.socket.borrow(), &*self.inner.borrow())
173 }
174
175 fn with_mut<R>(&self, f: impl FnOnce(&mut SocketStack, &mut Inner<D>) -> R) -> R {
176 f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut())
177 }
178
179 pub fn ethernet_address(&self) -> [u8; 6] {
180 self.with(|_s, i| i.device.ethernet_address())
181 }
182
183 pub fn is_link_up(&self) -> bool {
184 self.with(|_s, i| i.link_up)
185 }
186
187 pub fn is_config_up(&self) -> bool {
188 self.with(|_s, i| i.config.is_some())
189 }
190
191 pub fn config(&self) -> Option<Config> {
192 self.with(|_s, i| i.config.clone())
193 }
194
195 pub async fn run(&self) -> ! {
196 poll_fn(|cx| {
197 self.with_mut(|s, i| i.poll(cx, s));
198 Poll::<()>::Pending
199 })
200 .await;
201 unreachable!()
202 }
203}
204
205impl SocketStack {
206 #[allow(clippy::absurd_extreme_comparisons)]
207 pub fn get_local_port(&mut self) -> u16 {
208 let res = self.next_local_port;
209 self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 };
210 res
211 }
212}
213
214impl<D: Device + 'static> Inner<D> {
215 fn apply_config(&mut self, s: &mut SocketStack, config: Config) {
216 #[cfg(feature = "medium-ethernet")]
217 let medium = self.device.capabilities().medium;
218
219 debug!("Acquired IP configuration:");
220
221 debug!(" IP address: {}", config.address);
222 self.set_ipv4_addr(s, config.address);
223
224 #[cfg(feature = "medium-ethernet")]
225 if medium == Medium::Ethernet {
226 if let Some(gateway) = config.gateway {
227 debug!(" Default gateway: {}", gateway);
228 s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap();
229 } else {
230 debug!(" Default gateway: None");
231 s.iface.routes_mut().remove_default_ipv4_route();
232 }
233 }
234 for (i, s) in config.dns_servers.iter().enumerate() {
235 debug!(" DNS server {}: {}", i, s);
236 }
237
238 self.config = Some(config)
239 }
240
241 #[allow(unused)] // used only with dhcp
242 fn unapply_config(&mut self, s: &mut SocketStack) {
243 #[cfg(feature = "medium-ethernet")]
244 let medium = self.device.capabilities().medium;
245
246 debug!("Lost IP configuration");
247 self.set_ipv4_addr(s, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0));
248 #[cfg(feature = "medium-ethernet")]
249 if medium == Medium::Ethernet {
250 s.iface.routes_mut().remove_default_ipv4_route();
251 }
252 self.config = None
253 }
254
255 fn set_ipv4_addr(&mut self, s: &mut SocketStack, cidr: Ipv4Cidr) {
256 s.iface.update_ip_addrs(|addrs| {
257 let dest = addrs.iter_mut().next().unwrap();
258 *dest = IpCidr::Ipv4(cidr);
259 });
260 }
261
262 fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) {
263 s.waker.register(cx.waker());
264
265 let timestamp = instant_to_smoltcp(Instant::now());
266 let mut smoldev = DeviceAdapter {
267 cx: Some(cx),
268 inner: &mut self.device,
269 };
270 if s.iface.poll(timestamp, &mut smoldev, &mut s.sockets).is_err() {
271 // If poll() returns error, it may not be done yet, so poll again later.
272 cx.waker().wake_by_ref();
273 return;
274 }
275
276 // Update link up
277 let old_link_up = self.link_up;
278 self.link_up = self.device.link_state(cx) == LinkState::Up;
279
280 // Print when changed
281 if old_link_up != self.link_up {
282 info!("link_up = {:?}", self.link_up);
283 }
284
285 #[cfg(feature = "dhcpv4")]
286 if let Some(dhcp_handle) = self.dhcp_socket {
287 let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle);
288
289 if self.link_up {
290 match socket.poll() {
291 None => {}
292 Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s),
293 Some(dhcpv4::Event::Configured(config)) => {
294 let config = Config {
295 address: config.address,
296 gateway: config.router,
297 dns_servers: config.dns_servers,
298 };
299 self.apply_config(s, config)
300 }
301 }
302 } else if old_link_up {
303 socket.reset();
304 self.unapply_config(s);
305 }
306 }
307 //if old_link_up || self.link_up {
308 // self.poll_configurator(timestamp)
309 //}
310
311 if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) {
312 let t = Timer::at(instant_from_smoltcp(poll_at));
313 pin_mut!(t);
314 if t.poll(cx).is_ready() {
315 cx.waker().wake_by_ref();
316 }
317 }
318 }
319}
320
321fn instant_to_smoltcp(instant: Instant) -> SmolInstant {
322 SmolInstant::from_millis(instant.as_millis() as i64)
323}
324
325fn instant_from_smoltcp(instant: SmolInstant) -> Instant {
326 Instant::from_millis(instant.total_millis() as u64)
327}
diff --git a/embassy-net/src/packet_pool.rs b/embassy-net/src/packet_pool.rs
deleted file mode 100644
index cb8a1316c..000000000
--- a/embassy-net/src/packet_pool.rs
+++ /dev/null
@@ -1,107 +0,0 @@
1use core::ops::{Deref, DerefMut, Range};
2
3use as_slice::{AsMutSlice, AsSlice};
4use atomic_pool::{pool, Box};
5
6pub const MTU: usize = 1516;
7
8#[cfg(feature = "pool-4")]
9pub const PACKET_POOL_SIZE: usize = 4;
10
11#[cfg(feature = "pool-8")]
12pub const PACKET_POOL_SIZE: usize = 8;
13
14#[cfg(feature = "pool-16")]
15pub const PACKET_POOL_SIZE: usize = 16;
16
17#[cfg(feature = "pool-32")]
18pub const PACKET_POOL_SIZE: usize = 32;
19
20#[cfg(feature = "pool-64")]
21pub const PACKET_POOL_SIZE: usize = 64;
22
23#[cfg(feature = "pool-128")]
24pub const PACKET_POOL_SIZE: usize = 128;
25
26pool!(pub PacketPool: [Packet; PACKET_POOL_SIZE]);
27pub type PacketBox = Box<PacketPool>;
28
29#[repr(align(4))]
30pub struct Packet(pub [u8; MTU]);
31
32impl Packet {
33 pub const fn new() -> Self {
34 Self([0; MTU])
35 }
36}
37
38pub trait PacketBoxExt {
39 fn slice(self, range: Range<usize>) -> PacketBuf;
40}
41
42impl PacketBoxExt for PacketBox {
43 fn slice(self, range: Range<usize>) -> PacketBuf {
44 PacketBuf { packet: self, range }
45 }
46}
47
48impl AsSlice for Packet {
49 type Element = u8;
50
51 fn as_slice(&self) -> &[Self::Element] {
52 &self.deref()[..]
53 }
54}
55
56impl AsMutSlice for Packet {
57 fn as_mut_slice(&mut self) -> &mut [Self::Element] {
58 &mut self.deref_mut()[..]
59 }
60}
61
62impl Deref for Packet {
63 type Target = [u8; MTU];
64
65 fn deref(&self) -> &[u8; MTU] {
66 &self.0
67 }
68}
69
70impl DerefMut for Packet {
71 fn deref_mut(&mut self) -> &mut [u8; MTU] {
72 &mut self.0
73 }
74}
75
76pub struct PacketBuf {
77 packet: PacketBox,
78 range: Range<usize>,
79}
80
81impl AsSlice for PacketBuf {
82 type Element = u8;
83
84 fn as_slice(&self) -> &[Self::Element] {
85 &self.packet[self.range.clone()]
86 }
87}
88
89impl AsMutSlice for PacketBuf {
90 fn as_mut_slice(&mut self) -> &mut [Self::Element] {
91 &mut self.packet[self.range.clone()]
92 }
93}
94
95impl Deref for PacketBuf {
96 type Target = [u8];
97
98 fn deref(&self) -> &[u8] {
99 &self.packet[self.range.clone()]
100 }
101}
102
103impl DerefMut for PacketBuf {
104 fn deref_mut(&mut self) -> &mut [u8] {
105 &mut self.packet[self.range.clone()]
106 }
107}
diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs
deleted file mode 100644
index 5c4fb0442..000000000
--- a/embassy-net/src/stack.rs
+++ /dev/null
@@ -1,302 +0,0 @@
1use core::cell::RefCell;
2use core::future::{poll_fn, Future};
3use core::task::{Context, Poll};
4
5use embassy_sync::waitqueue::WakerRegistration;
6use embassy_time::{Instant, Timer};
7use futures::pin_mut;
8use heapless::Vec;
9#[cfg(feature = "dhcpv4")]
10use smoltcp::iface::SocketHandle;
11use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage};
12#[cfg(feature = "medium-ethernet")]
13use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes};
14#[cfg(feature = "medium-ethernet")]
15use smoltcp::phy::{Device as _, Medium};
16#[cfg(feature = "dhcpv4")]
17use smoltcp::socket::dhcpv4;
18use smoltcp::time::Instant as SmolInstant;
19#[cfg(feature = "medium-ethernet")]
20use smoltcp::wire::{EthernetAddress, HardwareAddress, IpAddress};
21use smoltcp::wire::{IpCidr, Ipv4Address, Ipv4Cidr};
22
23use crate::device::{Device, DeviceAdapter, LinkState};
24
25const LOCAL_PORT_MIN: u16 = 1025;
26const LOCAL_PORT_MAX: u16 = 65535;
27
28pub struct StackResources<const ADDR: usize, const SOCK: usize, const NEIGHBOR: usize> {
29 addresses: [IpCidr; ADDR],
30 sockets: [SocketStorage<'static>; SOCK],
31
32 #[cfg(feature = "medium-ethernet")]
33 routes: [Option<(IpCidr, Route)>; 1],
34 #[cfg(feature = "medium-ethernet")]
35 neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR],
36}
37
38impl<const ADDR: usize, const SOCK: usize, const NEIGHBOR: usize> StackResources<ADDR, SOCK, NEIGHBOR> {
39 pub fn new() -> Self {
40 Self {
41 addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32); ADDR],
42 sockets: [SocketStorage::EMPTY; SOCK],
43 #[cfg(feature = "medium-ethernet")]
44 routes: [None; 1],
45 #[cfg(feature = "medium-ethernet")]
46 neighbor_cache: [None; NEIGHBOR],
47 }
48 }
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct Config {
53 pub address: Ipv4Cidr,
54 pub gateway: Option<Ipv4Address>,
55 pub dns_servers: Vec<Ipv4Address, 3>,
56}
57
58pub enum ConfigStrategy {
59 Static(Config),
60 #[cfg(feature = "dhcpv4")]
61 Dhcp,
62}
63
64pub struct Stack<D: Device> {
65 pub(crate) socket: RefCell<SocketStack>,
66 inner: RefCell<Inner<D>>,
67}
68
69struct Inner<D: Device> {
70 device: DeviceAdapter<D>,
71 link_up: bool,
72 config: Option<Config>,
73 #[cfg(feature = "dhcpv4")]
74 dhcp_socket: Option<SocketHandle>,
75}
76
77pub(crate) struct SocketStack {
78 pub(crate) sockets: SocketSet<'static>,
79 pub(crate) iface: Interface<'static>,
80 pub(crate) waker: WakerRegistration,
81 next_local_port: u16,
82}
83
84impl<D: Device + 'static> Stack<D> {
85 pub fn new<const ADDR: usize, const SOCK: usize, const NEIGH: usize>(
86 device: D,
87 config: ConfigStrategy,
88 resources: &'static mut StackResources<ADDR, SOCK, NEIGH>,
89 random_seed: u64,
90 ) -> Self {
91 #[cfg(feature = "medium-ethernet")]
92 let medium = device.capabilities().medium;
93
94 #[cfg(feature = "medium-ethernet")]
95 let ethernet_addr = if medium == Medium::Ethernet {
96 device.ethernet_address()
97 } else {
98 [0, 0, 0, 0, 0, 0]
99 };
100
101 let mut device = DeviceAdapter::new(device);
102
103 let mut b = InterfaceBuilder::new();
104 b = b.ip_addrs(&mut resources.addresses[..]);
105 b = b.random_seed(random_seed);
106
107 #[cfg(feature = "medium-ethernet")]
108 if medium == Medium::Ethernet {
109 b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(ethernet_addr)));
110 b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..]));
111 b = b.routes(Routes::new(&mut resources.routes[..]));
112 }
113
114 let iface = b.finalize(&mut device);
115
116 let sockets = SocketSet::new(&mut resources.sockets[..]);
117
118 let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN;
119
120 let mut inner = Inner {
121 device,
122 link_up: false,
123 config: None,
124 #[cfg(feature = "dhcpv4")]
125 dhcp_socket: None,
126 };
127 let mut socket = SocketStack {
128 sockets,
129 iface,
130 waker: WakerRegistration::new(),
131 next_local_port,
132 };
133
134 match config {
135 ConfigStrategy::Static(config) => inner.apply_config(&mut socket, config),
136 #[cfg(feature = "dhcpv4")]
137 ConfigStrategy::Dhcp => {
138 let handle = socket.sockets.add(smoltcp::socket::dhcpv4::Socket::new());
139 inner.dhcp_socket = Some(handle);
140 }
141 }
142
143 Self {
144 socket: RefCell::new(socket),
145 inner: RefCell::new(inner),
146 }
147 }
148
149 fn with<R>(&self, f: impl FnOnce(&SocketStack, &Inner<D>) -> R) -> R {
150 f(&*self.socket.borrow(), &*self.inner.borrow())
151 }
152
153 fn with_mut<R>(&self, f: impl FnOnce(&mut SocketStack, &mut Inner<D>) -> R) -> R {
154 f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut())
155 }
156
157 pub fn ethernet_address(&self) -> [u8; 6] {
158 self.with(|_s, i| i.device.device.ethernet_address())
159 }
160
161 pub fn is_link_up(&self) -> bool {
162 self.with(|_s, i| i.link_up)
163 }
164
165 pub fn is_config_up(&self) -> bool {
166 self.with(|_s, i| i.config.is_some())
167 }
168
169 pub fn config(&self) -> Option<Config> {
170 self.with(|_s, i| i.config.clone())
171 }
172
173 pub async fn run(&self) -> ! {
174 poll_fn(|cx| {
175 self.with_mut(|s, i| i.poll(cx, s));
176 Poll::<()>::Pending
177 })
178 .await;
179 unreachable!()
180 }
181}
182
183impl SocketStack {
184 #[allow(clippy::absurd_extreme_comparisons)]
185 pub fn get_local_port(&mut self) -> u16 {
186 let res = self.next_local_port;
187 self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 };
188 res
189 }
190}
191
192impl<D: Device + 'static> Inner<D> {
193 fn apply_config(&mut self, s: &mut SocketStack, config: Config) {
194 #[cfg(feature = "medium-ethernet")]
195 let medium = self.device.capabilities().medium;
196
197 debug!("Acquired IP configuration:");
198
199 debug!(" IP address: {}", config.address);
200 self.set_ipv4_addr(s, config.address);
201
202 #[cfg(feature = "medium-ethernet")]
203 if medium == Medium::Ethernet {
204 if let Some(gateway) = config.gateway {
205 debug!(" Default gateway: {}", gateway);
206 s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap();
207 } else {
208 debug!(" Default gateway: None");
209 s.iface.routes_mut().remove_default_ipv4_route();
210 }
211 }
212 for (i, s) in config.dns_servers.iter().enumerate() {
213 debug!(" DNS server {}: {}", i, s);
214 }
215
216 self.config = Some(config)
217 }
218
219 #[allow(unused)] // used only with dhcp
220 fn unapply_config(&mut self, s: &mut SocketStack) {
221 #[cfg(feature = "medium-ethernet")]
222 let medium = self.device.capabilities().medium;
223
224 debug!("Lost IP configuration");
225 self.set_ipv4_addr(s, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0));
226 #[cfg(feature = "medium-ethernet")]
227 if medium == Medium::Ethernet {
228 s.iface.routes_mut().remove_default_ipv4_route();
229 }
230 self.config = None
231 }
232
233 fn set_ipv4_addr(&mut self, s: &mut SocketStack, cidr: Ipv4Cidr) {
234 s.iface.update_ip_addrs(|addrs| {
235 let dest = addrs.iter_mut().next().unwrap();
236 *dest = IpCidr::Ipv4(cidr);
237 });
238 }
239
240 fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) {
241 self.device.device.register_waker(cx.waker());
242 s.waker.register(cx.waker());
243
244 let timestamp = instant_to_smoltcp(Instant::now());
245 if s.iface.poll(timestamp, &mut self.device, &mut s.sockets).is_err() {
246 // If poll() returns error, it may not be done yet, so poll again later.
247 cx.waker().wake_by_ref();
248 return;
249 }
250
251 // Update link up
252 let old_link_up = self.link_up;
253 self.link_up = self.device.device.link_state() == LinkState::Up;
254
255 // Print when changed
256 if old_link_up != self.link_up {
257 info!("link_up = {:?}", self.link_up);
258 }
259
260 #[cfg(feature = "dhcpv4")]
261 if let Some(dhcp_handle) = self.dhcp_socket {
262 let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle);
263
264 if self.link_up {
265 match socket.poll() {
266 None => {}
267 Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s),
268 Some(dhcpv4::Event::Configured(config)) => {
269 let config = Config {
270 address: config.address,
271 gateway: config.router,
272 dns_servers: config.dns_servers,
273 };
274 self.apply_config(s, config)
275 }
276 }
277 } else if old_link_up {
278 socket.reset();
279 self.unapply_config(s);
280 }
281 }
282 //if old_link_up || self.link_up {
283 // self.poll_configurator(timestamp)
284 //}
285
286 if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) {
287 let t = Timer::at(instant_from_smoltcp(poll_at));
288 pin_mut!(t);
289 if t.poll(cx).is_ready() {
290 cx.waker().wake_by_ref();
291 }
292 }
293 }
294}
295
296fn instant_to_smoltcp(instant: Instant) -> SmolInstant {
297 SmolInstant::from_millis(instant.as_millis() as i64)
298}
299
300fn instant_from_smoltcp(instant: SmolInstant) -> Instant {
301 Instant::from_millis(instant.total_millis() as u64)
302}
diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs
index 73cf2d4e4..0dc8da73a 100644
--- a/embassy-net/src/tcp.rs
+++ b/embassy-net/src/tcp.rs
@@ -8,9 +8,8 @@ use smoltcp::socket::tcp;
8use smoltcp::time::Duration; 8use smoltcp::time::Duration;
9use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; 9use smoltcp::wire::{IpEndpoint, IpListenEndpoint};
10 10
11use super::stack::Stack; 11use crate::device::Device;
12use crate::stack::SocketStack; 12use crate::{SocketStack, Stack};
13use crate::Device;
14 13
15#[derive(PartialEq, Eq, Clone, Copy, Debug)] 14#[derive(PartialEq, Eq, Clone, Copy, Debug)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))] 15#[cfg_attr(feature = "defmt", derive(defmt::Format))]
diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs
index 4ddad77d4..2f5334df3 100644
--- a/embassy-net/src/udp.rs
+++ b/embassy-net/src/udp.rs
@@ -7,8 +7,7 @@ use smoltcp::iface::{Interface, SocketHandle};
7use smoltcp::socket::udp::{self, PacketMetadata}; 7use smoltcp::socket::udp::{self, PacketMetadata};
8use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; 8use smoltcp::wire::{IpEndpoint, IpListenEndpoint};
9 9
10use super::stack::SocketStack; 10use crate::{Device, SocketStack, Stack};
11use crate::{Device, Stack};
12 11
13#[derive(PartialEq, Eq, Clone, Copy, Debug)] 12#[derive(PartialEq, Eq, Clone, Copy, Debug)]
14#[cfg_attr(feature = "defmt", derive(defmt::Format))] 13#[cfg_attr(feature = "defmt", derive(defmt::Format))]