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