aboutsummaryrefslogtreecommitdiff
path: root/embassy-net/src/stack.rs
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2021-02-03 05:09:37 +0100
committerDario Nieuwenhuis <[email protected]>2021-02-03 05:09:37 +0100
commitcb5931d583d283dda3a1b5ed2014c086bb8f98ae (patch)
tree19a669238e0d562bf74616fe38485388ec40b02a /embassy-net/src/stack.rs
:rainbow:
Diffstat (limited to 'embassy-net/src/stack.rs')
-rw-r--r--embassy-net/src/stack.rs212
1 files changed, 212 insertions, 0 deletions
diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs
new file mode 100644
index 000000000..c353f1bb1
--- /dev/null
+++ b/embassy-net/src/stack.rs
@@ -0,0 +1,212 @@
1use core::future::Future;
2use core::task::Context;
3use core::task::Poll;
4use core::{cell::RefCell, future};
5use embassy::time::{Instant, Timer};
6use embassy::util::ThreadModeMutex;
7use embassy::util::{Forever, WakerRegistration};
8use futures::pin_mut;
9use smoltcp::iface::{InterfaceBuilder, Neighbor, NeighborCache, Route, Routes};
10use smoltcp::phy::Device as _;
11use smoltcp::phy::Medium;
12use smoltcp::socket::SocketSetItem;
13use smoltcp::time::Instant as SmolInstant;
14use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address};
15
16use crate::device::{Device, DeviceAdapter};
17use crate::fmt::*;
18use crate::{
19 config::{Config, Configurator},
20 device::LinkState,
21};
22use crate::{Interface, SocketSet};
23
24const ADDRESSES_LEN: usize = 1;
25const NEIGHBOR_CACHE_LEN: usize = 8;
26const SOCKETS_LEN: usize = 2;
27const LOCAL_PORT_MIN: u16 = 1025;
28const LOCAL_PORT_MAX: u16 = 65535;
29
30struct StackResources {
31 addresses: [IpCidr; ADDRESSES_LEN],
32 neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR_CACHE_LEN],
33 sockets: [Option<SocketSetItem<'static>>; SOCKETS_LEN],
34 routes: [Option<(IpCidr, Route)>; 1],
35}
36
37static STACK_RESOURCES: Forever<StackResources> = Forever::new();
38static STACK: ThreadModeMutex<RefCell<Option<Stack>>> = ThreadModeMutex::new(RefCell::new(None));
39
40pub(crate) struct Stack {
41 iface: Interface,
42 pub sockets: SocketSet,
43 link_up: bool,
44 next_local_port: u16,
45 configurator: &'static mut dyn Configurator,
46 waker: WakerRegistration,
47}
48
49impl Stack {
50 pub(crate) fn with<R>(f: impl FnOnce(&mut Stack) -> R) -> R {
51 let mut stack = STACK.borrow().borrow_mut();
52 let stack = stack.as_mut().unwrap();
53 f(stack)
54 }
55
56 pub fn get_local_port(&mut self) -> u16 {
57 let res = self.next_local_port;
58 self.next_local_port = if res >= LOCAL_PORT_MAX {
59 LOCAL_PORT_MIN
60 } else {
61 res + 1
62 };
63 res
64 }
65
66 pub(crate) fn wake(&mut self) {
67 self.waker.wake()
68 }
69
70 fn poll_configurator(&mut self, timestamp: SmolInstant) {
71 if let Some(config) = self
72 .configurator
73 .poll(&mut self.iface, &mut self.sockets, timestamp)
74 {
75 let medium = self.iface.device().capabilities().medium;
76
77 let (addr, gateway) = match config {
78 Config::Up(config) => (config.address.into(), Some(config.gateway)),
79 Config::Down => (IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32), None),
80 };
81
82 self.iface.update_ip_addrs(|addrs| {
83 let curr_addr = &mut addrs[0];
84 if *curr_addr != addr {
85 info!("IPv4 address: {:?} -> {:?}", *curr_addr, addr);
86 *curr_addr = addr;
87 }
88 });
89
90 if medium == Medium::Ethernet {
91 self.iface.routes_mut().update(|r| {
92 let cidr = IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0);
93 let curr_gateway = r.get(&cidr).map(|r| r.via_router);
94
95 if curr_gateway != gateway.map(|a| a.into()) {
96 info!("IPv4 gateway: {:?} -> {:?}", curr_gateway, gateway);
97 if let Some(gateway) = gateway {
98 r.insert(cidr, Route::new_ipv4_gateway(gateway)).unwrap();
99 } else {
100 r.remove(&cidr);
101 }
102 }
103 });
104 }
105 }
106 }
107
108 fn poll(&mut self, cx: &mut Context<'_>) {
109 self.iface.device_mut().device.register_waker(cx.waker());
110 self.waker.register(cx.waker());
111
112 let timestamp = instant_to_smoltcp(Instant::now());
113 if let Err(e) = self.iface.poll(&mut self.sockets, timestamp) {
114 // If poll() returns error, it may not be done yet, so poll again later.
115 cx.waker().wake_by_ref();
116 return;
117 }
118
119 // Update link up
120 let old_link_up = self.link_up;
121 self.link_up = self.iface.device_mut().device.link_state() == LinkState::Up;
122
123 // Print when changed
124 if old_link_up != self.link_up {
125 if self.link_up {
126 info!("Link up!");
127 } else {
128 info!("Link down!");
129 }
130 }
131
132 if old_link_up || self.link_up {
133 self.poll_configurator(timestamp)
134 }
135
136 if let Some(poll_at) = self.iface.poll_at(&mut self.sockets, timestamp) {
137 let t = Timer::at(instant_from_smoltcp(poll_at));
138 pin_mut!(t);
139 if t.poll(cx).is_ready() {
140 cx.waker().wake_by_ref();
141 }
142 }
143 }
144}
145
146/// Initialize embassy_net.
147/// This function must be called from thread mode.
148pub fn init(device: &'static mut dyn Device, configurator: &'static mut dyn Configurator) {
149 let res = STACK_RESOURCES.put(StackResources {
150 addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32)],
151 neighbor_cache: [None; NEIGHBOR_CACHE_LEN],
152 sockets: [None; SOCKETS_LEN],
153 routes: [None; 1],
154 });
155
156 let ethernet_addr = EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]);
157
158 let medium = device.capabilities().medium;
159
160 let mut b = InterfaceBuilder::new(DeviceAdapter::new(device));
161 b = b.ip_addrs(&mut res.addresses[..]);
162
163 if medium == Medium::Ethernet {
164 b = b.ethernet_addr(ethernet_addr);
165 b = b.neighbor_cache(NeighborCache::new(&mut res.neighbor_cache[..]));
166 b = b.routes(Routes::new(&mut res.routes[..]));
167 }
168
169 let iface = b.finalize();
170
171 let sockets = SocketSet::new(&mut res.sockets[..]);
172
173 let local_port = loop {
174 let mut res = [0u8; 2];
175 embassy::rand::rand(&mut res);
176 let port = u16::from_le_bytes(res);
177 if port >= LOCAL_PORT_MIN && port <= LOCAL_PORT_MAX {
178 break port;
179 }
180 };
181
182 let stack = Stack {
183 iface,
184 sockets,
185 link_up: false,
186 configurator,
187 next_local_port: local_port,
188 waker: WakerRegistration::new(),
189 };
190
191 *STACK.borrow().borrow_mut() = Some(stack);
192}
193
194pub fn is_init() -> bool {
195 STACK.borrow().borrow().is_some()
196}
197
198pub async fn run() {
199 futures::future::poll_fn(|cx| {
200 Stack::with(|stack| stack.poll(cx));
201 Poll::<()>::Pending
202 })
203 .await
204}
205
206fn instant_to_smoltcp(instant: Instant) -> SmolInstant {
207 SmolInstant::from_millis(instant.as_millis() as i64)
208}
209
210fn instant_from_smoltcp(instant: SmolInstant) -> Instant {
211 Instant::from_millis(instant.total_millis() as u64)
212}