aboutsummaryrefslogtreecommitdiff
path: root/embassy-net/src/stack.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-net/src/stack.rs')
-rw-r--r--embassy-net/src/stack.rs316
1 files changed, 0 insertions, 316 deletions
diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs
deleted file mode 100644
index 8d2dd4bca..000000000
--- a/embassy-net/src/stack.rs
+++ /dev/null
@@ -1,316 +0,0 @@
1use core::cell::UnsafeCell;
2use core::future::Future;
3use core::task::{Context, Poll};
4
5use embassy_sync::waitqueue::WakerRegistration;
6use embassy_time::{Instant, Timer};
7use futures::future::poll_fn;
8use futures::pin_mut;
9use heapless::Vec;
10#[cfg(feature = "dhcpv4")]
11use smoltcp::iface::SocketHandle;
12use smoltcp::iface::{Interface, InterfaceBuilder, SocketSet, SocketStorage};
13#[cfg(feature = "medium-ethernet")]
14use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes};
15#[cfg(feature = "medium-ethernet")]
16use smoltcp::phy::{Device as _, Medium};
17#[cfg(feature = "dhcpv4")]
18use smoltcp::socket::dhcpv4;
19use smoltcp::time::Instant as SmolInstant;
20#[cfg(feature = "medium-ethernet")]
21use smoltcp::wire::{EthernetAddress, HardwareAddress, IpAddress};
22use smoltcp::wire::{IpCidr, Ipv4Address, Ipv4Cidr};
23
24use crate::device::{Device, DeviceAdapter, LinkState};
25
26const LOCAL_PORT_MIN: u16 = 1025;
27const LOCAL_PORT_MAX: u16 = 65535;
28
29pub struct StackResources<const ADDR: usize, const SOCK: usize, const NEIGHBOR: usize> {
30 addresses: [IpCidr; ADDR],
31 sockets: [SocketStorage<'static>; SOCK],
32
33 #[cfg(feature = "medium-ethernet")]
34 routes: [Option<(IpCidr, Route)>; 1],
35 #[cfg(feature = "medium-ethernet")]
36 neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR],
37}
38
39impl<const ADDR: usize, const SOCK: usize, const NEIGHBOR: usize> StackResources<ADDR, SOCK, NEIGHBOR> {
40 pub fn new() -> Self {
41 Self {
42 addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32); ADDR],
43 sockets: [SocketStorage::EMPTY; SOCK],
44 #[cfg(feature = "medium-ethernet")]
45 routes: [None; 1],
46 #[cfg(feature = "medium-ethernet")]
47 neighbor_cache: [None; NEIGHBOR],
48 }
49 }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct Config {
54 pub address: Ipv4Cidr,
55 pub gateway: Option<Ipv4Address>,
56 pub dns_servers: Vec<Ipv4Address, 3>,
57}
58
59pub enum ConfigStrategy {
60 Static(Config),
61 #[cfg(feature = "dhcpv4")]
62 Dhcp,
63}
64
65pub struct Stack<D: Device> {
66 pub(crate) socket: UnsafeCell<SocketStack>,
67 inner: UnsafeCell<Inner<D>>,
68}
69
70struct Inner<D: Device> {
71 device: DeviceAdapter<D>,
72 link_up: bool,
73 config: Option<Config>,
74 #[cfg(feature = "dhcpv4")]
75 dhcp_socket: Option<SocketHandle>,
76}
77
78pub(crate) struct SocketStack {
79 pub(crate) sockets: SocketSet<'static>,
80 pub(crate) iface: Interface<'static>,
81 pub(crate) waker: WakerRegistration,
82 next_local_port: u16,
83}
84
85unsafe impl<D: Device> Send for Stack<D> {}
86
87impl<D: Device + 'static> Stack<D> {
88 pub fn new<const ADDR: usize, const SOCK: usize, const NEIGH: usize>(
89 device: D,
90 config: ConfigStrategy,
91 resources: &'static mut StackResources<ADDR, SOCK, NEIGH>,
92 random_seed: u64,
93 ) -> Self {
94 #[cfg(feature = "medium-ethernet")]
95 let medium = device.capabilities().medium;
96
97 #[cfg(feature = "medium-ethernet")]
98 let ethernet_addr = if medium == Medium::Ethernet {
99 device.ethernet_address()
100 } else {
101 [0, 0, 0, 0, 0, 0]
102 };
103
104 let mut device = DeviceAdapter::new(device);
105
106 let mut b = InterfaceBuilder::new();
107 b = b.ip_addrs(&mut resources.addresses[..]);
108 b = b.random_seed(random_seed);
109
110 #[cfg(feature = "medium-ethernet")]
111 if medium == Medium::Ethernet {
112 b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(ethernet_addr)));
113 b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..]));
114 b = b.routes(Routes::new(&mut resources.routes[..]));
115 }
116
117 let iface = b.finalize(&mut device);
118
119 let sockets = SocketSet::new(&mut resources.sockets[..]);
120
121 let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN;
122
123 let mut inner = Inner {
124 device,
125 link_up: false,
126 config: None,
127 #[cfg(feature = "dhcpv4")]
128 dhcp_socket: None,
129 };
130 let mut socket = SocketStack {
131 sockets,
132 iface,
133 waker: WakerRegistration::new(),
134 next_local_port,
135 };
136
137 match config {
138 ConfigStrategy::Static(config) => inner.apply_config(&mut socket, config),
139 #[cfg(feature = "dhcpv4")]
140 ConfigStrategy::Dhcp => {
141 let handle = socket.sockets.add(smoltcp::socket::dhcpv4::Socket::new());
142 inner.dhcp_socket = Some(handle);
143 }
144 }
145
146 Self {
147 socket: UnsafeCell::new(socket),
148 inner: UnsafeCell::new(inner),
149 }
150 }
151
152 /// SAFETY: must not call reentrantly.
153 unsafe fn with<R>(&self, f: impl FnOnce(&SocketStack, &Inner<D>) -> R) -> R {
154 f(&*self.socket.get(), &*self.inner.get())
155 }
156
157 /// SAFETY: must not call reentrantly.
158 unsafe fn with_mut<R>(&self, f: impl FnOnce(&mut SocketStack, &mut Inner<D>) -> R) -> R {
159 f(&mut *self.socket.get(), &mut *self.inner.get())
160 }
161
162 pub fn ethernet_address(&self) -> [u8; 6] {
163 unsafe { self.with(|_s, i| i.device.device.ethernet_address()) }
164 }
165
166 pub fn is_link_up(&self) -> bool {
167 unsafe { self.with(|_s, i| i.link_up) }
168 }
169
170 pub fn is_config_up(&self) -> bool {
171 unsafe { self.with(|_s, i| i.config.is_some()) }
172 }
173
174 pub fn config(&self) -> Option<Config> {
175 unsafe { self.with(|_s, i| i.config.clone()) }
176 }
177
178 pub async fn run(&self) -> ! {
179 poll_fn(|cx| {
180 unsafe { self.with_mut(|s, i| i.poll(cx, s)) }
181 Poll::<()>::Pending
182 })
183 .await;
184 unreachable!()
185 }
186}
187
188impl SocketStack {
189 #[allow(clippy::absurd_extreme_comparisons)]
190 pub fn get_local_port(&mut self) -> u16 {
191 let res = self.next_local_port;
192 self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 };
193 res
194 }
195}
196
197impl<D: Device + 'static> Inner<D> {
198 fn apply_config(&mut self, s: &mut SocketStack, config: Config) {
199 #[cfg(feature = "medium-ethernet")]
200 let medium = self.device.capabilities().medium;
201
202 debug!("Acquired IP configuration:");
203
204 debug!(" IP address: {}", config.address);
205 self.set_ipv4_addr(s, config.address);
206
207 #[cfg(feature = "medium-ethernet")]
208 if medium == Medium::Ethernet {
209 if let Some(gateway) = config.gateway {
210 debug!(" Default gateway: {}", gateway);
211 s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap();
212 } else {
213 debug!(" Default gateway: None");
214 s.iface.routes_mut().remove_default_ipv4_route();
215 }
216 }
217 for (i, s) in config.dns_servers.iter().enumerate() {
218 debug!(" DNS server {}: {}", i, s);
219 }
220
221 self.config = Some(config)
222 }
223
224 #[allow(unused)] // used only with dhcp
225 fn unapply_config(&mut self, s: &mut SocketStack) {
226 #[cfg(feature = "medium-ethernet")]
227 let medium = self.device.capabilities().medium;
228
229 debug!("Lost IP configuration");
230 self.set_ipv4_addr(s, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0));
231 #[cfg(feature = "medium-ethernet")]
232 if medium == Medium::Ethernet {
233 s.iface.routes_mut().remove_default_ipv4_route();
234 }
235 self.config = None
236 }
237
238 fn set_ipv4_addr(&mut self, s: &mut SocketStack, cidr: Ipv4Cidr) {
239 s.iface.update_ip_addrs(|addrs| {
240 let dest = addrs.iter_mut().next().unwrap();
241 *dest = IpCidr::Ipv4(cidr);
242 });
243 }
244
245 fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) {
246 self.device.device.register_waker(cx.waker());
247 s.waker.register(cx.waker());
248
249 let timestamp = instant_to_smoltcp(Instant::now());
250 if s.iface.poll(timestamp, &mut self.device, &mut s.sockets).is_err() {
251 // If poll() returns error, it may not be done yet, so poll again later.
252 cx.waker().wake_by_ref();
253 return;
254 }
255
256 // Update link up
257 let old_link_up = self.link_up;
258 self.link_up = self.device.device.link_state() == LinkState::Up;
259
260 // Print when changed
261 if old_link_up != self.link_up {
262 info!("link_up = {:?}", self.link_up);
263 }
264
265 #[cfg(feature = "dhcpv4")]
266 if let Some(dhcp_handle) = self.dhcp_socket {
267 let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle);
268
269 if self.link_up {
270 match socket.poll() {
271 None => {}
272 Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s),
273 Some(dhcpv4::Event::Configured(config)) => {
274 let mut dns_servers = Vec::new();
275 for s in &config.dns_servers {
276 if let Some(addr) = s {
277 dns_servers.push(addr.clone()).unwrap();
278 }
279 }
280
281 self.apply_config(
282 s,
283 Config {
284 address: config.address,
285 gateway: config.router,
286 dns_servers,
287 },
288 )
289 }
290 }
291 } else if old_link_up {
292 socket.reset();
293 self.unapply_config(s);
294 }
295 }
296 //if old_link_up || self.link_up {
297 // self.poll_configurator(timestamp)
298 //}
299
300 if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) {
301 let t = Timer::at(instant_from_smoltcp(poll_at));
302 pin_mut!(t);
303 if t.poll(cx).is_ready() {
304 cx.waker().wake_by_ref();
305 }
306 }
307 }
308}
309
310fn instant_to_smoltcp(instant: Instant) -> SmolInstant {
311 SmolInstant::from_millis(instant.as_millis() as i64)
312}
313
314fn instant_from_smoltcp(instant: SmolInstant) -> Instant {
315 Instant::from_millis(instant.total_millis() as u64)
316}