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