diff options
Diffstat (limited to 'embassy-net/src/lib.rs')
| -rw-r--r-- | embassy-net/src/lib.rs | 445 |
1 files changed, 197 insertions, 248 deletions
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 56321cec9..ef53fb905 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs | |||
| @@ -12,9 +12,9 @@ compile_error!("You must enable at least one of the following features: proto-ip | |||
| 12 | // This mod MUST go first, so that the others see its macros. | 12 | // This mod MUST go first, so that the others see its macros. |
| 13 | pub(crate) mod fmt; | 13 | pub(crate) mod fmt; |
| 14 | 14 | ||
| 15 | mod device; | ||
| 16 | #[cfg(feature = "dns")] | 15 | #[cfg(feature = "dns")] |
| 17 | pub mod dns; | 16 | pub mod dns; |
| 17 | mod driver_util; | ||
| 18 | #[cfg(feature = "raw")] | 18 | #[cfg(feature = "raw")] |
| 19 | pub mod raw; | 19 | pub mod raw; |
| 20 | #[cfg(feature = "tcp")] | 20 | #[cfg(feature = "tcp")] |
| @@ -25,6 +25,7 @@ pub mod udp; | |||
| 25 | 25 | ||
| 26 | use core::cell::RefCell; | 26 | use core::cell::RefCell; |
| 27 | use core::future::{poll_fn, Future}; | 27 | use core::future::{poll_fn, Future}; |
| 28 | use core::mem::MaybeUninit; | ||
| 28 | use core::pin::pin; | 29 | use core::pin::pin; |
| 29 | use core::task::{Context, Poll}; | 30 | use core::task::{Context, Poll}; |
| 30 | 31 | ||
| @@ -36,7 +37,7 @@ use embassy_time::{Instant, Timer}; | |||
| 36 | use heapless::Vec; | 37 | use heapless::Vec; |
| 37 | #[cfg(feature = "dns")] | 38 | #[cfg(feature = "dns")] |
| 38 | pub use smoltcp::config::DNS_MAX_SERVER_COUNT; | 39 | pub use smoltcp::config::DNS_MAX_SERVER_COUNT; |
| 39 | #[cfg(feature = "igmp")] | 40 | #[cfg(feature = "multicast")] |
| 40 | pub use smoltcp::iface::MulticastError; | 41 | pub use smoltcp::iface::MulticastError; |
| 41 | #[allow(unused_imports)] | 42 | #[allow(unused_imports)] |
| 42 | use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; | 43 | use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; |
| @@ -57,7 +58,7 @@ pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr}; | |||
| 57 | #[cfg(feature = "proto-ipv6")] | 58 | #[cfg(feature = "proto-ipv6")] |
| 58 | pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr}; | 59 | pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr}; |
| 59 | 60 | ||
| 60 | use crate::device::DriverAdapter; | 61 | use crate::driver_util::DriverAdapter; |
| 61 | use crate::time::{instant_from_smoltcp, instant_to_smoltcp}; | 62 | use crate::time::{instant_from_smoltcp, instant_to_smoltcp}; |
| 62 | 63 | ||
| 63 | const LOCAL_PORT_MIN: u16 = 1025; | 64 | const LOCAL_PORT_MIN: u16 = 1025; |
| @@ -69,33 +70,33 @@ const MAX_HOSTNAME_LEN: usize = 32; | |||
| 69 | 70 | ||
| 70 | /// Memory resources needed for a network stack. | 71 | /// Memory resources needed for a network stack. |
| 71 | pub struct StackResources<const SOCK: usize> { | 72 | pub struct StackResources<const SOCK: usize> { |
| 72 | sockets: [SocketStorage<'static>; SOCK], | 73 | sockets: MaybeUninit<[SocketStorage<'static>; SOCK]>, |
| 74 | inner: MaybeUninit<RefCell<Inner>>, | ||
| 73 | #[cfg(feature = "dns")] | 75 | #[cfg(feature = "dns")] |
| 74 | queries: [Option<dns::DnsQuery>; MAX_QUERIES], | 76 | queries: MaybeUninit<[Option<dns::DnsQuery>; MAX_QUERIES]>, |
| 75 | #[cfg(feature = "dhcpv4-hostname")] | 77 | #[cfg(feature = "dhcpv4-hostname")] |
| 76 | hostname: core::cell::UnsafeCell<HostnameResources>, | 78 | hostname: HostnameResources, |
| 77 | } | 79 | } |
| 78 | 80 | ||
| 79 | #[cfg(feature = "dhcpv4-hostname")] | 81 | #[cfg(feature = "dhcpv4-hostname")] |
| 80 | struct HostnameResources { | 82 | struct HostnameResources { |
| 81 | option: smoltcp::wire::DhcpOption<'static>, | 83 | option: MaybeUninit<smoltcp::wire::DhcpOption<'static>>, |
| 82 | data: [u8; MAX_HOSTNAME_LEN], | 84 | data: MaybeUninit<[u8; MAX_HOSTNAME_LEN]>, |
| 83 | } | 85 | } |
| 84 | 86 | ||
| 85 | impl<const SOCK: usize> StackResources<SOCK> { | 87 | impl<const SOCK: usize> StackResources<SOCK> { |
| 86 | /// Create a new set of stack resources. | 88 | /// Create a new set of stack resources. |
| 87 | pub const fn new() -> Self { | 89 | pub const fn new() -> Self { |
| 88 | #[cfg(feature = "dns")] | ||
| 89 | const INIT: Option<dns::DnsQuery> = None; | ||
| 90 | Self { | 90 | Self { |
| 91 | sockets: [SocketStorage::EMPTY; SOCK], | 91 | sockets: MaybeUninit::uninit(), |
| 92 | inner: MaybeUninit::uninit(), | ||
| 92 | #[cfg(feature = "dns")] | 93 | #[cfg(feature = "dns")] |
| 93 | queries: [INIT; MAX_QUERIES], | 94 | queries: MaybeUninit::uninit(), |
| 94 | #[cfg(feature = "dhcpv4-hostname")] | 95 | #[cfg(feature = "dhcpv4-hostname")] |
| 95 | hostname: core::cell::UnsafeCell::new(HostnameResources { | 96 | hostname: HostnameResources { |
| 96 | option: smoltcp::wire::DhcpOption { kind: 0, data: &[] }, | 97 | option: MaybeUninit::uninit(), |
| 97 | data: [0; MAX_HOSTNAME_LEN], | 98 | data: MaybeUninit::uninit(), |
| 98 | }), | 99 | }, |
| 99 | } | 100 | } |
| 100 | } | 101 | } |
| 101 | } | 102 | } |
| @@ -239,16 +240,29 @@ pub enum ConfigV6 { | |||
| 239 | Static(StaticConfigV6), | 240 | Static(StaticConfigV6), |
| 240 | } | 241 | } |
| 241 | 242 | ||
| 242 | /// A network stack. | 243 | /// Network stack runner. |
| 244 | /// | ||
| 245 | /// You must call [`Runner::run()`] in a background task for the network stack to work. | ||
| 246 | pub struct Runner<'d, D: Driver> { | ||
| 247 | driver: D, | ||
| 248 | stack: Stack<'d>, | ||
| 249 | } | ||
| 250 | |||
| 251 | /// Network stack handle | ||
| 243 | /// | 252 | /// |
| 244 | /// This is the main entry point for the network stack. | 253 | /// Use this to create sockets. It's `Copy`, so you can pass |
| 245 | pub struct Stack<D: Driver> { | 254 | /// it by value instead of by reference. |
| 246 | pub(crate) socket: RefCell<SocketStack>, | 255 | #[derive(Copy, Clone)] |
| 247 | inner: RefCell<Inner<D>>, | 256 | pub struct Stack<'d> { |
| 257 | inner: &'d RefCell<Inner>, | ||
| 248 | } | 258 | } |
| 249 | 259 | ||
| 250 | struct Inner<D: Driver> { | 260 | pub(crate) struct Inner { |
| 251 | device: D, | 261 | pub(crate) sockets: SocketSet<'static>, // Lifetime type-erased. |
| 262 | pub(crate) iface: Interface, | ||
| 263 | pub(crate) waker: WakerRegistration, | ||
| 264 | hardware_address: HardwareAddress, | ||
| 265 | next_local_port: u16, | ||
| 252 | link_up: bool, | 266 | link_up: bool, |
| 253 | #[cfg(feature = "proto-ipv4")] | 267 | #[cfg(feature = "proto-ipv4")] |
| 254 | static_v4: Option<StaticConfigV4>, | 268 | static_v4: Option<StaticConfigV4>, |
| @@ -262,14 +276,83 @@ struct Inner<D: Driver> { | |||
| 262 | #[cfg(feature = "dns")] | 276 | #[cfg(feature = "dns")] |
| 263 | dns_waker: WakerRegistration, | 277 | dns_waker: WakerRegistration, |
| 264 | #[cfg(feature = "dhcpv4-hostname")] | 278 | #[cfg(feature = "dhcpv4-hostname")] |
| 265 | hostname: &'static mut core::cell::UnsafeCell<HostnameResources>, | 279 | hostname: *mut HostnameResources, |
| 266 | } | 280 | } |
| 267 | 281 | ||
| 268 | pub(crate) struct SocketStack { | 282 | fn _assert_covariant<'a, 'b: 'a>(x: Stack<'b>) -> Stack<'a> { |
| 269 | pub(crate) sockets: SocketSet<'static>, | 283 | x |
| 270 | pub(crate) iface: Interface, | 284 | } |
| 271 | pub(crate) waker: WakerRegistration, | 285 | |
| 272 | next_local_port: u16, | 286 | /// Create a new network stack. |
| 287 | pub fn new<'d, D: Driver, const SOCK: usize>( | ||
| 288 | mut driver: D, | ||
| 289 | config: Config, | ||
| 290 | resources: &'d mut StackResources<SOCK>, | ||
| 291 | random_seed: u64, | ||
| 292 | ) -> (Stack<'d>, Runner<'d, D>) { | ||
| 293 | let (hardware_address, medium) = to_smoltcp_hardware_address(driver.hardware_address()); | ||
| 294 | let mut iface_cfg = smoltcp::iface::Config::new(hardware_address); | ||
| 295 | iface_cfg.random_seed = random_seed; | ||
| 296 | |||
| 297 | let iface = Interface::new( | ||
| 298 | iface_cfg, | ||
| 299 | &mut DriverAdapter { | ||
| 300 | inner: &mut driver, | ||
| 301 | cx: None, | ||
| 302 | medium, | ||
| 303 | }, | ||
| 304 | instant_to_smoltcp(Instant::now()), | ||
| 305 | ); | ||
| 306 | |||
| 307 | unsafe fn transmute_slice<T>(x: &mut [T]) -> &'static mut [T] { | ||
| 308 | core::mem::transmute(x) | ||
| 309 | } | ||
| 310 | |||
| 311 | let sockets = resources.sockets.write([SocketStorage::EMPTY; SOCK]); | ||
| 312 | #[allow(unused_mut)] | ||
| 313 | let mut sockets: SocketSet<'static> = SocketSet::new(unsafe { transmute_slice(sockets) }); | ||
| 314 | |||
| 315 | let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; | ||
| 316 | |||
| 317 | #[cfg(feature = "dns")] | ||
| 318 | let dns_socket = sockets.add(dns::Socket::new( | ||
| 319 | &[], | ||
| 320 | managed::ManagedSlice::Borrowed(unsafe { | ||
| 321 | transmute_slice(resources.queries.write([const { None }; MAX_QUERIES])) | ||
| 322 | }), | ||
| 323 | )); | ||
| 324 | |||
| 325 | let mut inner = Inner { | ||
| 326 | sockets, | ||
| 327 | iface, | ||
| 328 | waker: WakerRegistration::new(), | ||
| 329 | next_local_port, | ||
| 330 | hardware_address, | ||
| 331 | link_up: false, | ||
| 332 | #[cfg(feature = "proto-ipv4")] | ||
| 333 | static_v4: None, | ||
| 334 | #[cfg(feature = "proto-ipv6")] | ||
| 335 | static_v6: None, | ||
| 336 | #[cfg(feature = "dhcpv4")] | ||
| 337 | dhcp_socket: None, | ||
| 338 | config_waker: WakerRegistration::new(), | ||
| 339 | #[cfg(feature = "dns")] | ||
| 340 | dns_socket, | ||
| 341 | #[cfg(feature = "dns")] | ||
| 342 | dns_waker: WakerRegistration::new(), | ||
| 343 | #[cfg(feature = "dhcpv4-hostname")] | ||
| 344 | hostname: &mut resources.hostname, | ||
| 345 | }; | ||
| 346 | |||
| 347 | #[cfg(feature = "proto-ipv4")] | ||
| 348 | inner.set_config_v4(config.ipv4); | ||
| 349 | #[cfg(feature = "proto-ipv6")] | ||
| 350 | inner.set_config_v6(config.ipv6); | ||
| 351 | inner.apply_static_config(); | ||
| 352 | |||
| 353 | let inner = &*resources.inner.write(RefCell::new(inner)); | ||
| 354 | let stack = Stack { inner }; | ||
| 355 | (stack, Runner { driver, stack }) | ||
| 273 | } | 356 | } |
| 274 | 357 | ||
| 275 | fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> (HardwareAddress, Medium) { | 358 | fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> (HardwareAddress, Medium) { |
| @@ -292,89 +375,23 @@ fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> (HardwareAddres | |||
| 292 | } | 375 | } |
| 293 | } | 376 | } |
| 294 | 377 | ||
| 295 | impl<D: Driver> Stack<D> { | 378 | impl<'d> Stack<'d> { |
| 296 | /// Create a new network stack. | 379 | fn with<R>(&self, f: impl FnOnce(&Inner) -> R) -> R { |
| 297 | pub fn new<const SOCK: usize>( | 380 | f(&*self.inner.borrow()) |
| 298 | mut device: D, | ||
| 299 | config: Config, | ||
| 300 | resources: &'static mut StackResources<SOCK>, | ||
| 301 | random_seed: u64, | ||
| 302 | ) -> Self { | ||
| 303 | let (hardware_addr, medium) = to_smoltcp_hardware_address(device.hardware_address()); | ||
| 304 | let mut iface_cfg = smoltcp::iface::Config::new(hardware_addr); | ||
| 305 | iface_cfg.random_seed = random_seed; | ||
| 306 | |||
| 307 | let iface = Interface::new( | ||
| 308 | iface_cfg, | ||
| 309 | &mut DriverAdapter { | ||
| 310 | inner: &mut device, | ||
| 311 | cx: None, | ||
| 312 | medium, | ||
| 313 | }, | ||
| 314 | instant_to_smoltcp(Instant::now()), | ||
| 315 | ); | ||
| 316 | |||
| 317 | let sockets = SocketSet::new(&mut resources.sockets[..]); | ||
| 318 | |||
| 319 | let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; | ||
| 320 | |||
| 321 | #[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))] | ||
| 322 | let mut socket = SocketStack { | ||
| 323 | sockets, | ||
| 324 | iface, | ||
| 325 | waker: WakerRegistration::new(), | ||
| 326 | next_local_port, | ||
| 327 | }; | ||
| 328 | |||
| 329 | let mut inner = Inner { | ||
| 330 | device, | ||
| 331 | link_up: false, | ||
| 332 | #[cfg(feature = "proto-ipv4")] | ||
| 333 | static_v4: None, | ||
| 334 | #[cfg(feature = "proto-ipv6")] | ||
| 335 | static_v6: None, | ||
| 336 | #[cfg(feature = "dhcpv4")] | ||
| 337 | dhcp_socket: None, | ||
| 338 | config_waker: WakerRegistration::new(), | ||
| 339 | #[cfg(feature = "dns")] | ||
| 340 | dns_socket: socket.sockets.add(dns::Socket::new( | ||
| 341 | &[], | ||
| 342 | managed::ManagedSlice::Borrowed(&mut resources.queries), | ||
| 343 | )), | ||
| 344 | #[cfg(feature = "dns")] | ||
| 345 | dns_waker: WakerRegistration::new(), | ||
| 346 | #[cfg(feature = "dhcpv4-hostname")] | ||
| 347 | hostname: &mut resources.hostname, | ||
| 348 | }; | ||
| 349 | |||
| 350 | #[cfg(feature = "proto-ipv4")] | ||
| 351 | inner.set_config_v4(&mut socket, config.ipv4); | ||
| 352 | #[cfg(feature = "proto-ipv6")] | ||
| 353 | inner.set_config_v6(&mut socket, config.ipv6); | ||
| 354 | inner.apply_static_config(&mut socket); | ||
| 355 | |||
| 356 | Self { | ||
| 357 | socket: RefCell::new(socket), | ||
| 358 | inner: RefCell::new(inner), | ||
| 359 | } | ||
| 360 | } | ||
| 361 | |||
| 362 | fn with<R>(&self, f: impl FnOnce(&SocketStack, &Inner<D>) -> R) -> R { | ||
| 363 | f(&*self.socket.borrow(), &*self.inner.borrow()) | ||
| 364 | } | 381 | } |
| 365 | 382 | ||
| 366 | fn with_mut<R>(&self, f: impl FnOnce(&mut SocketStack, &mut Inner<D>) -> R) -> R { | 383 | fn with_mut<R>(&self, f: impl FnOnce(&mut Inner) -> R) -> R { |
| 367 | f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut()) | 384 | f(&mut *self.inner.borrow_mut()) |
| 368 | } | 385 | } |
| 369 | 386 | ||
| 370 | /// Get the hardware address of the network interface. | 387 | /// Get the hardware address of the network interface. |
| 371 | pub fn hardware_address(&self) -> HardwareAddress { | 388 | pub fn hardware_address(&self) -> HardwareAddress { |
| 372 | self.with(|_s, i| to_smoltcp_hardware_address(i.device.hardware_address()).0) | 389 | self.with(|i| i.hardware_address) |
| 373 | } | 390 | } |
| 374 | 391 | ||
| 375 | /// Get whether the link is up. | 392 | /// Get whether the link is up. |
| 376 | pub fn is_link_up(&self) -> bool { | 393 | pub fn is_link_up(&self) -> bool { |
| 377 | self.with(|_s, i| i.link_up) | 394 | self.with(|i| i.link_up) |
| 378 | } | 395 | } |
| 379 | 396 | ||
| 380 | /// Get whether the network stack has a valid IP configuration. | 397 | /// Get whether the network stack has a valid IP configuration. |
| @@ -420,15 +437,14 @@ impl<D: Driver> Stack<D> { | |||
| 420 | /// // provisioning space for 3 sockets here: one for DHCP, one for DNS, and one for your code (e.g. TCP). | 437 | /// // provisioning space for 3 sockets here: one for DHCP, one for DNS, and one for your code (e.g. TCP). |
| 421 | /// // If you use more sockets you must increase this. If you don't enable DHCP or DNS you can decrease it. | 438 | /// // If you use more sockets you must increase this. If you don't enable DHCP or DNS you can decrease it. |
| 422 | /// static RESOURCES: StaticCell<embassy_net::StackResources<3>> = StaticCell::new(); | 439 | /// static RESOURCES: StaticCell<embassy_net::StackResources<3>> = StaticCell::new(); |
| 423 | /// static STACK: StaticCell<embassy_net::Stack> = StaticCell::new(); | 440 | /// let (stack, runner) = embassy_net::new( |
| 424 | /// let stack = &*STACK.init(embassy_net::Stack::new( | 441 | /// driver, |
| 425 | /// device, | ||
| 426 | /// config, | 442 | /// config, |
| 427 | /// RESOURCES.init(embassy_net::StackResources::new()), | 443 | /// RESOURCES.init(embassy_net::StackResources::new()), |
| 428 | /// seed | 444 | /// seed |
| 429 | /// )); | 445 | /// ); |
| 430 | /// // Launch network task that runs `stack.run().await` | 446 | /// // Launch network task that runs `runner.run().await` |
| 431 | /// spawner.spawn(net_task(stack)).unwrap(); | 447 | /// spawner.spawn(net_task(runner)).unwrap(); |
| 432 | /// // Wait for DHCP config | 448 | /// // Wait for DHCP config |
| 433 | /// stack.wait_config_up().await; | 449 | /// stack.wait_config_up().await; |
| 434 | /// // use the network stack | 450 | /// // use the network stack |
| @@ -448,7 +464,7 @@ impl<D: Driver> Stack<D> { | |||
| 448 | // when a config is applied (static or DHCP). | 464 | // when a config is applied (static or DHCP). |
| 449 | trace!("Waiting for config up"); | 465 | trace!("Waiting for config up"); |
| 450 | 466 | ||
| 451 | self.with_mut(|_, i| { | 467 | self.with_mut(|i| { |
| 452 | i.config_waker.register(cx.waker()); | 468 | i.config_waker.register(cx.waker()); |
| 453 | }); | 469 | }); |
| 454 | 470 | ||
| @@ -464,45 +480,33 @@ impl<D: Driver> Stack<D> { | |||
| 464 | /// acquire an IP address, or Some if it has. | 480 | /// acquire an IP address, or Some if it has. |
| 465 | #[cfg(feature = "proto-ipv4")] | 481 | #[cfg(feature = "proto-ipv4")] |
| 466 | pub fn config_v4(&self) -> Option<StaticConfigV4> { | 482 | pub fn config_v4(&self) -> Option<StaticConfigV4> { |
| 467 | self.with(|_, i| i.static_v4.clone()) | 483 | self.with(|i| i.static_v4.clone()) |
| 468 | } | 484 | } |
| 469 | 485 | ||
| 470 | /// Get the current IPv6 configuration. | 486 | /// Get the current IPv6 configuration. |
| 471 | #[cfg(feature = "proto-ipv6")] | 487 | #[cfg(feature = "proto-ipv6")] |
| 472 | pub fn config_v6(&self) -> Option<StaticConfigV6> { | 488 | pub fn config_v6(&self) -> Option<StaticConfigV6> { |
| 473 | self.with(|_, i| i.static_v6.clone()) | 489 | self.with(|i| i.static_v6.clone()) |
| 474 | } | 490 | } |
| 475 | 491 | ||
| 476 | /// Set the IPv4 configuration. | 492 | /// Set the IPv4 configuration. |
| 477 | #[cfg(feature = "proto-ipv4")] | 493 | #[cfg(feature = "proto-ipv4")] |
| 478 | pub fn set_config_v4(&self, config: ConfigV4) { | 494 | pub fn set_config_v4(&self, config: ConfigV4) { |
| 479 | self.with_mut(|s, i| { | 495 | self.with_mut(|i| { |
| 480 | i.set_config_v4(s, config); | 496 | i.set_config_v4(config); |
| 481 | i.apply_static_config(s); | 497 | i.apply_static_config(); |
| 482 | }) | 498 | }) |
| 483 | } | 499 | } |
| 484 | 500 | ||
| 485 | /// Set the IPv6 configuration. | 501 | /// Set the IPv6 configuration. |
| 486 | #[cfg(feature = "proto-ipv6")] | 502 | #[cfg(feature = "proto-ipv6")] |
| 487 | pub fn set_config_v6(&self, config: ConfigV6) { | 503 | pub fn set_config_v6(&self, config: ConfigV6) { |
| 488 | self.with_mut(|s, i| { | 504 | self.with_mut(|i| { |
| 489 | i.set_config_v6(s, config); | 505 | i.set_config_v6(config); |
| 490 | i.apply_static_config(s); | 506 | i.apply_static_config(); |
| 491 | }) | 507 | }) |
| 492 | } | 508 | } |
| 493 | 509 | ||
| 494 | /// Run the network stack. | ||
| 495 | /// | ||
| 496 | /// You must call this in a background task, to process network events. | ||
| 497 | pub async fn run(&self) -> ! { | ||
| 498 | poll_fn(|cx| { | ||
| 499 | self.with_mut(|s, i| i.poll(cx, s)); | ||
| 500 | Poll::<()>::Pending | ||
| 501 | }) | ||
| 502 | .await; | ||
| 503 | unreachable!() | ||
| 504 | } | ||
| 505 | |||
| 506 | /// Make a query for a given name and return the corresponding IP addresses. | 510 | /// Make a query for a given name and return the corresponding IP addresses. |
| 507 | #[cfg(feature = "dns")] | 511 | #[cfg(feature = "dns")] |
| 508 | pub async fn dns_query( | 512 | pub async fn dns_query( |
| @@ -528,11 +532,11 @@ impl<D: Driver> Stack<D> { | |||
| 528 | } | 532 | } |
| 529 | 533 | ||
| 530 | let query = poll_fn(|cx| { | 534 | let query = poll_fn(|cx| { |
| 531 | self.with_mut(|s, i| { | 535 | self.with_mut(|i| { |
| 532 | let socket = s.sockets.get_mut::<dns::Socket>(i.dns_socket); | 536 | let socket = i.sockets.get_mut::<dns::Socket>(i.dns_socket); |
| 533 | match socket.start_query(s.iface.context(), name, qtype) { | 537 | match socket.start_query(i.iface.context(), name, qtype) { |
| 534 | Ok(handle) => { | 538 | Ok(handle) => { |
| 535 | s.waker.wake(); | 539 | i.waker.wake(); |
| 536 | Poll::Ready(Ok(handle)) | 540 | Poll::Ready(Ok(handle)) |
| 537 | } | 541 | } |
| 538 | Err(dns::StartQueryError::NoFreeSlot) => { | 542 | Err(dns::StartQueryError::NoFreeSlot) => { |
| @@ -569,17 +573,17 @@ impl<D: Driver> Stack<D> { | |||
| 569 | } | 573 | } |
| 570 | 574 | ||
| 571 | let drop = OnDrop::new(|| { | 575 | let drop = OnDrop::new(|| { |
| 572 | self.with_mut(|s, i| { | 576 | self.with_mut(|i| { |
| 573 | let socket = s.sockets.get_mut::<dns::Socket>(i.dns_socket); | 577 | let socket = i.sockets.get_mut::<dns::Socket>(i.dns_socket); |
| 574 | socket.cancel_query(query); | 578 | socket.cancel_query(query); |
| 575 | s.waker.wake(); | 579 | i.waker.wake(); |
| 576 | i.dns_waker.wake(); | 580 | i.dns_waker.wake(); |
| 577 | }) | 581 | }) |
| 578 | }); | 582 | }); |
| 579 | 583 | ||
| 580 | let res = poll_fn(|cx| { | 584 | let res = poll_fn(|cx| { |
| 581 | self.with_mut(|s, i| { | 585 | self.with_mut(|i| { |
| 582 | let socket = s.sockets.get_mut::<dns::Socket>(i.dns_socket); | 586 | let socket = i.sockets.get_mut::<dns::Socket>(i.dns_socket); |
| 583 | match socket.get_query_result(query) { | 587 | match socket.get_query_result(query) { |
| 584 | Ok(addrs) => { | 588 | Ok(addrs) => { |
| 585 | i.dns_waker.wake(); | 589 | i.dns_waker.wake(); |
| @@ -604,104 +608,34 @@ impl<D: Driver> Stack<D> { | |||
| 604 | } | 608 | } |
| 605 | } | 609 | } |
| 606 | 610 | ||
| 607 | #[cfg(feature = "igmp")] | 611 | #[cfg(feature = "multicast")] |
| 608 | impl<D: Driver> Stack<D> { | 612 | impl<'d> Stack<'d> { |
| 609 | /// Join a multicast group. | 613 | /// Join a multicast group. |
| 610 | pub async fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError> | 614 | pub fn join_multicast_group(&self, addr: impl Into<IpAddress>) -> Result<(), MulticastError> { |
| 611 | where | 615 | self.with_mut(|i| i.iface.join_multicast_group(addr)) |
| 612 | T: Into<IpAddress>, | ||
| 613 | { | ||
| 614 | let addr = addr.into(); | ||
| 615 | |||
| 616 | poll_fn(move |cx| self.poll_join_multicast_group(addr, cx)).await | ||
| 617 | } | ||
| 618 | |||
| 619 | /// Join a multicast group. | ||
| 620 | /// | ||
| 621 | /// When the send queue is full, this method will return `Poll::Pending` | ||
| 622 | /// and register the current task to be notified when the queue has space available. | ||
| 623 | pub fn poll_join_multicast_group<T>(&self, addr: T, cx: &mut Context<'_>) -> Poll<Result<bool, MulticastError>> | ||
| 624 | where | ||
| 625 | T: Into<IpAddress>, | ||
| 626 | { | ||
| 627 | let addr = addr.into(); | ||
| 628 | |||
| 629 | self.with_mut(|s, i| { | ||
| 630 | let (_hardware_addr, medium) = to_smoltcp_hardware_address(i.device.hardware_address()); | ||
| 631 | let mut smoldev = DriverAdapter { | ||
| 632 | cx: Some(cx), | ||
| 633 | inner: &mut i.device, | ||
| 634 | medium, | ||
| 635 | }; | ||
| 636 | |||
| 637 | match s | ||
| 638 | .iface | ||
| 639 | .join_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now())) | ||
| 640 | { | ||
| 641 | Ok(announce_sent) => Poll::Ready(Ok(announce_sent)), | ||
| 642 | Err(MulticastError::Exhausted) => Poll::Pending, | ||
| 643 | Err(other) => Poll::Ready(Err(other)), | ||
| 644 | } | ||
| 645 | }) | ||
| 646 | } | 616 | } |
| 647 | 617 | ||
| 648 | /// Leave a multicast group. | 618 | /// Leave a multicast group. |
| 649 | pub async fn leave_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError> | 619 | pub fn leave_multicast_group(&self, addr: impl Into<IpAddress>) -> Result<(), MulticastError> { |
| 650 | where | 620 | self.with_mut(|i| i.iface.leave_multicast_group(addr)) |
| 651 | T: Into<IpAddress>, | ||
| 652 | { | ||
| 653 | let addr = addr.into(); | ||
| 654 | |||
| 655 | poll_fn(move |cx| self.poll_leave_multicast_group(addr, cx)).await | ||
| 656 | } | ||
| 657 | |||
| 658 | /// Leave a multicast group. | ||
| 659 | /// | ||
| 660 | /// When the send queue is full, this method will return `Poll::Pending` | ||
| 661 | /// and register the current task to be notified when the queue has space available. | ||
| 662 | pub fn poll_leave_multicast_group<T>(&self, addr: T, cx: &mut Context<'_>) -> Poll<Result<bool, MulticastError>> | ||
| 663 | where | ||
| 664 | T: Into<IpAddress>, | ||
| 665 | { | ||
| 666 | let addr = addr.into(); | ||
| 667 | |||
| 668 | self.with_mut(|s, i| { | ||
| 669 | let (_hardware_addr, medium) = to_smoltcp_hardware_address(i.device.hardware_address()); | ||
| 670 | let mut smoldev = DriverAdapter { | ||
| 671 | cx: Some(cx), | ||
| 672 | inner: &mut i.device, | ||
| 673 | medium, | ||
| 674 | }; | ||
| 675 | |||
| 676 | match s | ||
| 677 | .iface | ||
| 678 | .leave_multicast_group(&mut smoldev, addr, instant_to_smoltcp(Instant::now())) | ||
| 679 | { | ||
| 680 | Ok(leave_sent) => Poll::Ready(Ok(leave_sent)), | ||
| 681 | Err(MulticastError::Exhausted) => Poll::Pending, | ||
| 682 | Err(other) => Poll::Ready(Err(other)), | ||
| 683 | } | ||
| 684 | }) | ||
| 685 | } | 621 | } |
| 686 | 622 | ||
| 687 | /// Get whether the network stack has joined the given multicast group. | 623 | /// Get whether the network stack has joined the given multicast group. |
| 688 | pub fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool { | 624 | pub fn has_multicast_group(&self, addr: impl Into<IpAddress>) -> bool { |
| 689 | self.socket.borrow().iface.has_multicast_group(addr) | 625 | self.with(|i| i.iface.has_multicast_group(addr)) |
| 690 | } | 626 | } |
| 691 | } | 627 | } |
| 692 | 628 | ||
| 693 | impl SocketStack { | 629 | impl Inner { |
| 694 | #[allow(clippy::absurd_extreme_comparisons, dead_code)] | 630 | #[allow(clippy::absurd_extreme_comparisons, dead_code)] |
| 695 | pub fn get_local_port(&mut self) -> u16 { | 631 | pub fn get_local_port(&mut self) -> u16 { |
| 696 | let res = self.next_local_port; | 632 | let res = self.next_local_port; |
| 697 | self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 }; | 633 | self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 }; |
| 698 | res | 634 | res |
| 699 | } | 635 | } |
| 700 | } | ||
| 701 | 636 | ||
| 702 | impl<D: Driver> Inner<D> { | ||
| 703 | #[cfg(feature = "proto-ipv4")] | 637 | #[cfg(feature = "proto-ipv4")] |
| 704 | pub fn set_config_v4(&mut self, _s: &mut SocketStack, config: ConfigV4) { | 638 | pub fn set_config_v4(&mut self, config: ConfigV4) { |
| 705 | // Handle static config. | 639 | // Handle static config. |
| 706 | self.static_v4 = match config.clone() { | 640 | self.static_v4 = match config.clone() { |
| 707 | ConfigV4::None => None, | 641 | ConfigV4::None => None, |
| @@ -717,12 +651,12 @@ impl<D: Driver> Inner<D> { | |||
| 717 | // Create the socket if it doesn't exist. | 651 | // Create the socket if it doesn't exist. |
| 718 | if self.dhcp_socket.is_none() { | 652 | if self.dhcp_socket.is_none() { |
| 719 | let socket = smoltcp::socket::dhcpv4::Socket::new(); | 653 | let socket = smoltcp::socket::dhcpv4::Socket::new(); |
| 720 | let handle = _s.sockets.add(socket); | 654 | let handle = self.sockets.add(socket); |
| 721 | self.dhcp_socket = Some(handle); | 655 | self.dhcp_socket = Some(handle); |
| 722 | } | 656 | } |
| 723 | 657 | ||
| 724 | // Configure it | 658 | // Configure it |
| 725 | let socket = _s.sockets.get_mut::<dhcpv4::Socket>(unwrap!(self.dhcp_socket)); | 659 | let socket = self.sockets.get_mut::<dhcpv4::Socket>(unwrap!(self.dhcp_socket)); |
| 726 | socket.set_ignore_naks(c.ignore_naks); | 660 | socket.set_ignore_naks(c.ignore_naks); |
| 727 | socket.set_max_lease_duration(c.max_lease_duration.map(crate::time::duration_to_smoltcp)); | 661 | socket.set_max_lease_duration(c.max_lease_duration.map(crate::time::duration_to_smoltcp)); |
| 728 | socket.set_ports(c.server_port, c.client_port); | 662 | socket.set_ports(c.server_port, c.client_port); |
| @@ -731,19 +665,20 @@ impl<D: Driver> Inner<D> { | |||
| 731 | socket.set_outgoing_options(&[]); | 665 | socket.set_outgoing_options(&[]); |
| 732 | #[cfg(feature = "dhcpv4-hostname")] | 666 | #[cfg(feature = "dhcpv4-hostname")] |
| 733 | if let Some(h) = c.hostname { | 667 | if let Some(h) = c.hostname { |
| 734 | // safety: we just did set_outgoing_options([]) so we know the socket is no longer holding a reference. | 668 | // safety: |
| 735 | let hostname = unsafe { &mut *self.hostname.get() }; | 669 | // - we just did set_outgoing_options([]) so we know the socket is no longer holding a reference. |
| 670 | // - we know this pointer lives for as long as the stack exists, because `new()` borrows | ||
| 671 | // the resources for `'d`. Therefore it's OK to pass a reference to this to smoltcp. | ||
| 672 | let hostname = unsafe { &mut *self.hostname }; | ||
| 736 | 673 | ||
| 737 | // create data | 674 | // create data |
| 738 | // safety: we know the buffer lives forever, new borrows the StackResources for 'static. | 675 | let data = hostname.data.write([0; MAX_HOSTNAME_LEN]); |
| 739 | // also we won't modify it until next call to this function. | 676 | data[..h.len()].copy_from_slice(h.as_bytes()); |
| 740 | hostname.data[..h.len()].copy_from_slice(h.as_bytes()); | 677 | let data: &[u8] = &data[..h.len()]; |
| 741 | let data: &[u8] = &hostname.data[..h.len()]; | ||
| 742 | let data: &'static [u8] = unsafe { core::mem::transmute(data) }; | ||
| 743 | 678 | ||
| 744 | // set the option. | 679 | // set the option. |
| 745 | hostname.option = smoltcp::wire::DhcpOption { data, kind: 12 }; | 680 | let option = hostname.option.write(smoltcp::wire::DhcpOption { data, kind: 12 }); |
| 746 | socket.set_outgoing_options(core::slice::from_ref(&hostname.option)); | 681 | socket.set_outgoing_options(core::slice::from_ref(option)); |
| 747 | } | 682 | } |
| 748 | 683 | ||
| 749 | socket.reset(); | 684 | socket.reset(); |
| @@ -751,7 +686,7 @@ impl<D: Driver> Inner<D> { | |||
| 751 | _ => { | 686 | _ => { |
| 752 | // Remove DHCP socket if any. | 687 | // Remove DHCP socket if any. |
| 753 | if let Some(socket) = self.dhcp_socket { | 688 | if let Some(socket) = self.dhcp_socket { |
| 754 | _s.sockets.remove(socket); | 689 | self.sockets.remove(socket); |
| 755 | self.dhcp_socket = None; | 690 | self.dhcp_socket = None; |
| 756 | } | 691 | } |
| 757 | } | 692 | } |
| @@ -759,14 +694,14 @@ impl<D: Driver> Inner<D> { | |||
| 759 | } | 694 | } |
| 760 | 695 | ||
| 761 | #[cfg(feature = "proto-ipv6")] | 696 | #[cfg(feature = "proto-ipv6")] |
| 762 | pub fn set_config_v6(&mut self, _s: &mut SocketStack, config: ConfigV6) { | 697 | pub fn set_config_v6(&mut self, config: ConfigV6) { |
| 763 | self.static_v6 = match config { | 698 | self.static_v6 = match config { |
| 764 | ConfigV6::None => None, | 699 | ConfigV6::None => None, |
| 765 | ConfigV6::Static(c) => Some(c), | 700 | ConfigV6::Static(c) => Some(c), |
| 766 | }; | 701 | }; |
| 767 | } | 702 | } |
| 768 | 703 | ||
| 769 | fn apply_static_config(&mut self, s: &mut SocketStack) { | 704 | fn apply_static_config(&mut self) { |
| 770 | let mut addrs = Vec::new(); | 705 | let mut addrs = Vec::new(); |
| 771 | #[cfg(feature = "dns")] | 706 | #[cfg(feature = "dns")] |
| 772 | let mut dns_servers: Vec<_, 6> = Vec::new(); | 707 | let mut dns_servers: Vec<_, 6> = Vec::new(); |
| @@ -810,20 +745,20 @@ impl<D: Driver> Inner<D> { | |||
| 810 | } | 745 | } |
| 811 | 746 | ||
| 812 | // Apply addresses | 747 | // Apply addresses |
| 813 | s.iface.update_ip_addrs(|a| *a = addrs); | 748 | self.iface.update_ip_addrs(|a| *a = addrs); |
| 814 | 749 | ||
| 815 | // Apply gateways | 750 | // Apply gateways |
| 816 | #[cfg(feature = "proto-ipv4")] | 751 | #[cfg(feature = "proto-ipv4")] |
| 817 | if let Some(gateway) = gateway_v4 { | 752 | if let Some(gateway) = gateway_v4 { |
| 818 | unwrap!(s.iface.routes_mut().add_default_ipv4_route(gateway)); | 753 | unwrap!(self.iface.routes_mut().add_default_ipv4_route(gateway)); |
| 819 | } else { | 754 | } else { |
| 820 | s.iface.routes_mut().remove_default_ipv4_route(); | 755 | self.iface.routes_mut().remove_default_ipv4_route(); |
| 821 | } | 756 | } |
| 822 | #[cfg(feature = "proto-ipv6")] | 757 | #[cfg(feature = "proto-ipv6")] |
| 823 | if let Some(gateway) = gateway_v6 { | 758 | if let Some(gateway) = gateway_v6 { |
| 824 | unwrap!(s.iface.routes_mut().add_default_ipv6_route(gateway)); | 759 | unwrap!(self.iface.routes_mut().add_default_ipv6_route(gateway)); |
| 825 | } else { | 760 | } else { |
| 826 | s.iface.routes_mut().remove_default_ipv6_route(); | 761 | self.iface.routes_mut().remove_default_ipv6_route(); |
| 827 | } | 762 | } |
| 828 | 763 | ||
| 829 | // Apply DNS servers | 764 | // Apply DNS servers |
| @@ -835,7 +770,7 @@ impl<D: Driver> Inner<D> { | |||
| 835 | } else { | 770 | } else { |
| 836 | dns_servers.len() | 771 | dns_servers.len() |
| 837 | }; | 772 | }; |
| 838 | s.sockets | 773 | self.sockets |
| 839 | .get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket) | 774 | .get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket) |
| 840 | .update_servers(&dns_servers[..count]); | 775 | .update_servers(&dns_servers[..count]); |
| 841 | } | 776 | } |
| @@ -843,10 +778,10 @@ impl<D: Driver> Inner<D> { | |||
| 843 | self.config_waker.wake(); | 778 | self.config_waker.wake(); |
| 844 | } | 779 | } |
| 845 | 780 | ||
| 846 | fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { | 781 | fn poll<D: Driver>(&mut self, cx: &mut Context<'_>, driver: &mut D) { |
| 847 | s.waker.register(cx.waker()); | 782 | self.waker.register(cx.waker()); |
| 848 | 783 | ||
| 849 | let (_hardware_addr, medium) = to_smoltcp_hardware_address(self.device.hardware_address()); | 784 | let (_hardware_addr, medium) = to_smoltcp_hardware_address(driver.hardware_address()); |
| 850 | 785 | ||
| 851 | #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] | 786 | #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] |
| 852 | { | 787 | { |
| @@ -859,21 +794,21 @@ impl<D: Driver> Inner<D> { | |||
| 859 | _ => false, | 794 | _ => false, |
| 860 | }; | 795 | }; |
| 861 | if do_set { | 796 | if do_set { |
| 862 | s.iface.set_hardware_addr(_hardware_addr); | 797 | self.iface.set_hardware_addr(_hardware_addr); |
| 863 | } | 798 | } |
| 864 | } | 799 | } |
| 865 | 800 | ||
| 866 | let timestamp = instant_to_smoltcp(Instant::now()); | 801 | let timestamp = instant_to_smoltcp(Instant::now()); |
| 867 | let mut smoldev = DriverAdapter { | 802 | let mut smoldev = DriverAdapter { |
| 868 | cx: Some(cx), | 803 | cx: Some(cx), |
| 869 | inner: &mut self.device, | 804 | inner: driver, |
| 870 | medium, | 805 | medium, |
| 871 | }; | 806 | }; |
| 872 | s.iface.poll(timestamp, &mut smoldev, &mut s.sockets); | 807 | self.iface.poll(timestamp, &mut smoldev, &mut self.sockets); |
| 873 | 808 | ||
| 874 | // Update link up | 809 | // Update link up |
| 875 | let old_link_up = self.link_up; | 810 | let old_link_up = self.link_up; |
| 876 | self.link_up = self.device.link_state(cx) == LinkState::Up; | 811 | self.link_up = driver.link_state(cx) == LinkState::Up; |
| 877 | 812 | ||
| 878 | // Print when changed | 813 | // Print when changed |
| 879 | if old_link_up != self.link_up { | 814 | if old_link_up != self.link_up { |
| @@ -885,7 +820,7 @@ impl<D: Driver> Inner<D> { | |||
| 885 | 820 | ||
| 886 | #[cfg(feature = "dhcpv4")] | 821 | #[cfg(feature = "dhcpv4")] |
| 887 | if let Some(dhcp_handle) = self.dhcp_socket { | 822 | if let Some(dhcp_handle) = self.dhcp_socket { |
| 888 | let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle); | 823 | let socket = self.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle); |
| 889 | 824 | ||
| 890 | if self.link_up { | 825 | if self.link_up { |
| 891 | if old_link_up != self.link_up { | 826 | if old_link_up != self.link_up { |
| @@ -914,10 +849,10 @@ impl<D: Driver> Inner<D> { | |||
| 914 | } | 849 | } |
| 915 | 850 | ||
| 916 | if apply_config { | 851 | if apply_config { |
| 917 | self.apply_static_config(s); | 852 | self.apply_static_config(); |
| 918 | } | 853 | } |
| 919 | 854 | ||
| 920 | if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) { | 855 | if let Some(poll_at) = self.iface.poll_at(timestamp, &mut self.sockets) { |
| 921 | let t = pin!(Timer::at(instant_from_smoltcp(poll_at))); | 856 | let t = pin!(Timer::at(instant_from_smoltcp(poll_at))); |
| 922 | if t.poll(cx).is_ready() { | 857 | if t.poll(cx).is_ready() { |
| 923 | cx.waker().wake_by_ref(); | 858 | cx.waker().wake_by_ref(); |
| @@ -925,3 +860,17 @@ impl<D: Driver> Inner<D> { | |||
| 925 | } | 860 | } |
| 926 | } | 861 | } |
| 927 | } | 862 | } |
| 863 | |||
| 864 | impl<'d, D: Driver> Runner<'d, D> { | ||
| 865 | /// Run the network stack. | ||
| 866 | /// | ||
| 867 | /// You must call this in a background task, to process network events. | ||
| 868 | pub async fn run(&mut self) -> ! { | ||
| 869 | poll_fn(|cx| { | ||
| 870 | self.stack.with_mut(|i| i.poll(cx, &mut self.driver)); | ||
| 871 | Poll::<()>::Pending | ||
| 872 | }) | ||
| 873 | .await; | ||
| 874 | unreachable!() | ||
| 875 | } | ||
| 876 | } | ||
