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.rs259
1 files changed, 259 insertions, 0 deletions
diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs
new file mode 100644
index 000000000..e436beb1e
--- /dev/null
+++ b/embassy-net/src/stack.rs
@@ -0,0 +1,259 @@
1use core::cell::RefCell;
2use core::future::Future;
3use core::task::Context;
4use core::task::Poll;
5use embassy::time::{Instant, Timer};
6use embassy::util::ThreadModeMutex;
7use embassy::util::{Forever, WakerRegistration};
8use futures::pin_mut;
9use smoltcp::iface::InterfaceBuilder;
10#[cfg(feature = "medium-ethernet")]
11use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes};
12use smoltcp::phy::Device as _;
13use smoltcp::phy::Medium;
14use smoltcp::socket::SocketSetItem;
15use smoltcp::time::Instant as SmolInstant;
16#[cfg(feature = "medium-ethernet")]
17use smoltcp::wire::EthernetAddress;
18use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr};
19
20use crate::config::Configurator;
21use crate::config::Event;
22use crate::device::{Device, DeviceAdapter, LinkState};
23use crate::fmt::*;
24use crate::{Interface, SocketSet};
25
26const ADDRESSES_LEN: usize = 1;
27const NEIGHBOR_CACHE_LEN: usize = 8;
28const SOCKETS_LEN: usize = 2;
29const LOCAL_PORT_MIN: u16 = 1025;
30const LOCAL_PORT_MAX: u16 = 65535;
31
32struct StackResources {
33 addresses: [IpCidr; ADDRESSES_LEN],
34 sockets: [Option<SocketSetItem<'static>>; SOCKETS_LEN],
35
36 #[cfg(feature = "medium-ethernet")]
37 routes: [Option<(IpCidr, Route)>; 1],
38 #[cfg(feature = "medium-ethernet")]
39 neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR_CACHE_LEN],
40}
41
42static STACK_RESOURCES: Forever<StackResources> = Forever::new();
43static STACK: ThreadModeMutex<RefCell<Option<Stack>>> = ThreadModeMutex::new(RefCell::new(None));
44
45pub(crate) struct Stack {
46 iface: Interface,
47 pub sockets: SocketSet,
48 link_up: bool,
49 config_up: bool,
50 next_local_port: u16,
51 configurator: &'static mut dyn Configurator,
52 waker: WakerRegistration,
53}
54
55impl Stack {
56 pub(crate) fn with<R>(f: impl FnOnce(&mut Stack) -> R) -> R {
57 let mut stack = STACK.borrow().borrow_mut();
58 let stack = stack.as_mut().unwrap();
59 f(stack)
60 }
61
62 pub fn get_local_port(&mut self) -> u16 {
63 let res = self.next_local_port;
64 self.next_local_port = if res >= LOCAL_PORT_MAX {
65 LOCAL_PORT_MIN
66 } else {
67 res + 1
68 };
69 res
70 }
71
72 pub(crate) fn wake(&mut self) {
73 self.waker.wake()
74 }
75
76 fn poll_configurator(&mut self, timestamp: SmolInstant) {
77 let medium = self.iface.device().capabilities().medium;
78
79 match self
80 .configurator
81 .poll(&mut self.iface, &mut self.sockets, timestamp)
82 {
83 Event::NoChange => {}
84 Event::Configured(config) => {
85 debug!("Acquired IP configuration:");
86
87 debug!(" IP address: {}", config.address);
88 set_ipv4_addr(&mut self.iface, config.address);
89
90 #[cfg(feature = "medium-ethernet")]
91 if medium == Medium::Ethernet {
92 if let Some(gateway) = config.gateway {
93 debug!(" Default gateway: {}", gateway);
94 self.iface
95 .routes_mut()
96 .add_default_ipv4_route(gateway)
97 .unwrap();
98 } else {
99 debug!(" Default gateway: None");
100 self.iface.routes_mut().remove_default_ipv4_route();
101 }
102 }
103 for (i, s) in config.dns_servers.iter().enumerate() {
104 debug!(" DNS server {}: {}", i, s);
105 }
106
107 self.config_up = true;
108 }
109 Event::Deconfigured => {
110 debug!("Lost IP configuration");
111 set_ipv4_addr(&mut self.iface, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0));
112 #[cfg(feature = "medium-ethernet")]
113 if medium == Medium::Ethernet {
114 self.iface.routes_mut().remove_default_ipv4_route();
115 }
116 self.config_up = false;
117 }
118 }
119 }
120
121 fn poll(&mut self, cx: &mut Context<'_>) {
122 self.iface.device_mut().device.register_waker(cx.waker());
123 self.waker.register(cx.waker());
124
125 let timestamp = instant_to_smoltcp(Instant::now());
126 if let Err(_) = self.iface.poll(&mut self.sockets, timestamp) {
127 // If poll() returns error, it may not be done yet, so poll again later.
128 cx.waker().wake_by_ref();
129 return;
130 }
131
132 // Update link up
133 let old_link_up = self.link_up;
134 self.link_up = self.iface.device_mut().device.link_state() == LinkState::Up;
135
136 // Print when changed
137 if old_link_up != self.link_up {
138 if self.link_up {
139 info!("Link up!");
140 } else {
141 info!("Link down!");
142 }
143 }
144
145 if old_link_up || self.link_up {
146 self.poll_configurator(timestamp)
147 }
148
149 if let Some(poll_at) = self.iface.poll_at(&mut self.sockets, timestamp) {
150 let t = Timer::at(instant_from_smoltcp(poll_at));
151 pin_mut!(t);
152 if t.poll(cx).is_ready() {
153 cx.waker().wake_by_ref();
154 }
155 }
156 }
157}
158
159fn set_ipv4_addr(iface: &mut Interface, cidr: Ipv4Cidr) {
160 iface.update_ip_addrs(|addrs| {
161 let dest = addrs.iter_mut().next().unwrap();
162 *dest = IpCidr::Ipv4(cidr);
163 });
164}
165
166/// Initialize embassy_net.
167/// This function must be called from thread mode.
168pub fn init(device: &'static mut dyn Device, configurator: &'static mut dyn Configurator) {
169 const NONE_SOCKET: Option<SocketSetItem<'static>> = None;
170 let res = STACK_RESOURCES.put(StackResources {
171 addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32)],
172 sockets: [NONE_SOCKET; SOCKETS_LEN],
173
174 #[cfg(feature = "medium-ethernet")]
175 routes: [None; 1],
176 #[cfg(feature = "medium-ethernet")]
177 neighbor_cache: [None; NEIGHBOR_CACHE_LEN],
178 });
179
180 let medium = device.capabilities().medium;
181
182 #[cfg(feature = "medium-ethernet")]
183 let ethernet_addr = if medium == Medium::Ethernet {
184 device.ethernet_address()
185 } else {
186 [0, 0, 0, 0, 0, 0]
187 };
188
189 let mut b = InterfaceBuilder::new(DeviceAdapter::new(device));
190 b = b.ip_addrs(&mut res.addresses[..]);
191
192 #[cfg(feature = "medium-ethernet")]
193 if medium == Medium::Ethernet {
194 b = b.ethernet_addr(EthernetAddress(ethernet_addr));
195 b = b.neighbor_cache(NeighborCache::new(&mut res.neighbor_cache[..]));
196 b = b.routes(Routes::new(&mut res.routes[..]));
197 }
198
199 let iface = b.finalize();
200
201 let sockets = SocketSet::new(&mut res.sockets[..]);
202
203 let local_port = loop {
204 let mut res = [0u8; 2];
205 rand(&mut res);
206 let port = u16::from_le_bytes(res);
207 if port >= LOCAL_PORT_MIN && port <= LOCAL_PORT_MAX {
208 break port;
209 }
210 };
211
212 let stack = Stack {
213 iface,
214 sockets,
215 link_up: false,
216 config_up: false,
217 configurator,
218 next_local_port: local_port,
219 waker: WakerRegistration::new(),
220 };
221
222 *STACK.borrow().borrow_mut() = Some(stack);
223}
224
225pub fn is_init() -> bool {
226 STACK.borrow().borrow().is_some()
227}
228
229pub fn is_link_up() -> bool {
230 STACK.borrow().borrow().as_ref().unwrap().link_up
231}
232
233pub fn is_config_up() -> bool {
234 STACK.borrow().borrow().as_ref().unwrap().config_up
235}
236
237pub async fn run() {
238 futures::future::poll_fn(|cx| {
239 Stack::with(|stack| stack.poll(cx));
240 Poll::<()>::Pending
241 })
242 .await
243}
244
245fn instant_to_smoltcp(instant: Instant) -> SmolInstant {
246 SmolInstant::from_millis(instant.as_millis() as i64)
247}
248
249fn instant_from_smoltcp(instant: SmolInstant) -> Instant {
250 Instant::from_millis(instant.total_millis() as u64)
251}
252
253extern "Rust" {
254 fn _embassy_rand(buf: &mut [u8]);
255}
256
257fn rand(buf: &mut [u8]) {
258 unsafe { _embassy_rand(buf) }
259}