aboutsummaryrefslogtreecommitdiff
path: root/embassy-net/src/stack.rs
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2022-12-07 16:02:28 +0100
committerDario Nieuwenhuis <[email protected]>2022-12-13 16:18:39 +0100
commitaaaf5f23a8a3a0df1ad2186802e2afc061a74b72 (patch)
tree05fa84ad745c1b9d3e712ef65e79076c37d92601 /embassy-net/src/stack.rs
parentac74613b5a7be72acd8d5259055963f8b4aba7fd (diff)
net: move stack into lib.rs
Diffstat (limited to 'embassy-net/src/stack.rs')
-rw-r--r--embassy-net/src/stack.rs306
1 files changed, 0 insertions, 306 deletions
diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs
deleted file mode 100644
index 21316e485..000000000
--- a/embassy-net/src/stack.rs
+++ /dev/null
@@ -1,306 +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::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: 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 mut 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 b = InterfaceBuilder::new();
102 b = b.ip_addrs(&mut resources.addresses[..]);
103 b = b.random_seed(random_seed);
104
105 #[cfg(feature = "medium-ethernet")]
106 if medium == Medium::Ethernet {
107 b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(ethernet_addr)));
108 b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..]));
109 b = b.routes(Routes::new(&mut resources.routes[..]));
110 }
111
112 let iface = b.finalize(&mut DeviceAdapter {
113 inner: &mut device,
114 cx: None,
115 });
116
117 let sockets = SocketSet::new(&mut resources.sockets[..]);
118
119 let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN;
120
121 let mut inner = Inner {
122 device,
123 link_up: false,
124 config: None,
125 #[cfg(feature = "dhcpv4")]
126 dhcp_socket: None,
127 };
128 let mut socket = SocketStack {
129 sockets,
130 iface,
131 waker: WakerRegistration::new(),
132 next_local_port,
133 };
134
135 match config {
136 ConfigStrategy::Static(config) => inner.apply_config(&mut socket, config),
137 #[cfg(feature = "dhcpv4")]
138 ConfigStrategy::Dhcp => {
139 let handle = socket.sockets.add(smoltcp::socket::dhcpv4::Socket::new());
140 inner.dhcp_socket = Some(handle);
141 }
142 }
143
144 Self {
145 socket: RefCell::new(socket),
146 inner: RefCell::new(inner),
147 }
148 }
149
150 fn with<R>(&self, f: impl FnOnce(&SocketStack, &Inner<D>) -> R) -> R {
151 f(&*self.socket.borrow(), &*self.inner.borrow())
152 }
153
154 fn with_mut<R>(&self, f: impl FnOnce(&mut SocketStack, &mut Inner<D>) -> R) -> R {
155 f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut())
156 }
157
158 pub fn ethernet_address(&self) -> [u8; 6] {
159 self.with(|_s, i| i.device.ethernet_address())
160 }
161
162 pub fn is_link_up(&self) -> bool {
163 self.with(|_s, i| i.link_up)
164 }
165
166 pub fn is_config_up(&self) -> bool {
167 self.with(|_s, i| i.config.is_some())
168 }
169
170 pub fn config(&self) -> Option<Config> {
171 self.with(|_s, i| i.config.clone())
172 }
173
174 pub async fn run(&self) -> ! {
175 poll_fn(|cx| {
176 self.with_mut(|s, i| i.poll(cx, s));
177 Poll::<()>::Pending
178 })
179 .await;
180 unreachable!()
181 }
182}
183
184impl SocketStack {
185 #[allow(clippy::absurd_extreme_comparisons)]
186 pub fn get_local_port(&mut self) -> u16 {
187 let res = self.next_local_port;
188 self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 };
189 res
190 }
191}
192
193impl<D: Device + 'static> Inner<D> {
194 fn apply_config(&mut self, s: &mut SocketStack, config: Config) {
195 #[cfg(feature = "medium-ethernet")]
196 let medium = self.device.capabilities().medium;
197
198 debug!("Acquired IP configuration:");
199
200 debug!(" IP address: {}", config.address);
201 self.set_ipv4_addr(s, config.address);
202
203 #[cfg(feature = "medium-ethernet")]
204 if medium == Medium::Ethernet {
205 if let Some(gateway) = config.gateway {
206 debug!(" Default gateway: {}", gateway);
207 s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap();
208 } else {
209 debug!(" Default gateway: None");
210 s.iface.routes_mut().remove_default_ipv4_route();
211 }
212 }
213 for (i, s) in config.dns_servers.iter().enumerate() {
214 debug!(" DNS server {}: {}", i, s);
215 }
216
217 self.config = Some(config)
218 }
219
220 #[allow(unused)] // used only with dhcp
221 fn unapply_config(&mut self, s: &mut SocketStack) {
222 #[cfg(feature = "medium-ethernet")]
223 let medium = self.device.capabilities().medium;
224
225 debug!("Lost IP configuration");
226 self.set_ipv4_addr(s, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0));
227 #[cfg(feature = "medium-ethernet")]
228 if medium == Medium::Ethernet {
229 s.iface.routes_mut().remove_default_ipv4_route();
230 }
231 self.config = None
232 }
233
234 fn set_ipv4_addr(&mut self, s: &mut SocketStack, cidr: Ipv4Cidr) {
235 s.iface.update_ip_addrs(|addrs| {
236 let dest = addrs.iter_mut().next().unwrap();
237 *dest = IpCidr::Ipv4(cidr);
238 });
239 }
240
241 fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) {
242 s.waker.register(cx.waker());
243
244 let timestamp = instant_to_smoltcp(Instant::now());
245 let mut smoldev = DeviceAdapter {
246 cx: Some(cx),
247 inner: &mut self.device,
248 };
249 if s.iface.poll(timestamp, &mut smoldev, &mut s.sockets).is_err() {
250 // If poll() returns error, it may not be done yet, so poll again later.
251 cx.waker().wake_by_ref();
252 return;
253 }
254
255 // Update link up
256 let old_link_up = self.link_up;
257 self.link_up = self.device.link_state(cx) == LinkState::Up;
258
259 // Print when changed
260 if old_link_up != self.link_up {
261 info!("link_up = {:?}", self.link_up);
262 }
263
264 #[cfg(feature = "dhcpv4")]
265 if let Some(dhcp_handle) = self.dhcp_socket {
266 let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle);
267
268 if self.link_up {
269 match socket.poll() {
270 None => {}
271 Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s),
272 Some(dhcpv4::Event::Configured(config)) => {
273 let config = Config {
274 address: config.address,
275 gateway: config.router,
276 dns_servers: config.dns_servers,
277 };
278 self.apply_config(s, config)
279 }
280 }
281 } else if old_link_up {
282 socket.reset();
283 self.unapply_config(s);
284 }
285 }
286 //if old_link_up || self.link_up {
287 // self.poll_configurator(timestamp)
288 //}
289
290 if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) {
291 let t = Timer::at(instant_from_smoltcp(poll_at));
292 pin_mut!(t);
293 if t.poll(cx).is_ready() {
294 cx.waker().wake_by_ref();
295 }
296 }
297 }
298}
299
300fn instant_to_smoltcp(instant: Instant) -> SmolInstant {
301 SmolInstant::from_millis(instant.as_millis() as i64)
302}
303
304fn instant_from_smoltcp(instant: SmolInstant) -> Instant {
305 Instant::from_millis(instant.total_millis() as u64)
306}