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.rs302
1 files changed, 0 insertions, 302 deletions
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}