aboutsummaryrefslogtreecommitdiff
path: root/embassy-net/src/lib.rs
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-09-11 22:06:26 +0200
committerDario Nieuwenhuis <[email protected]>2024-09-16 21:17:11 +0200
commitbe0d9775e3bcc3c1bd1448e357d7c6cd67b68991 (patch)
tree5e8c444b233a86ade113b096ab1e2934b7bbc7bd /embassy-net/src/lib.rs
parent7648d42b7f23a2caad29ed6e16123b088ccdc8b5 (diff)
net: refactor to simplify lifetimes/generics.
Diffstat (limited to 'embassy-net/src/lib.rs')
-rw-r--r--embassy-net/src/lib.rs445
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.
13pub(crate) mod fmt; 13pub(crate) mod fmt;
14 14
15mod device;
16#[cfg(feature = "dns")] 15#[cfg(feature = "dns")]
17pub mod dns; 16pub mod dns;
17mod driver_util;
18#[cfg(feature = "raw")] 18#[cfg(feature = "raw")]
19pub mod raw; 19pub mod raw;
20#[cfg(feature = "tcp")] 20#[cfg(feature = "tcp")]
@@ -25,6 +25,7 @@ pub mod udp;
25 25
26use core::cell::RefCell; 26use core::cell::RefCell;
27use core::future::{poll_fn, Future}; 27use core::future::{poll_fn, Future};
28use core::mem::MaybeUninit;
28use core::pin::pin; 29use core::pin::pin;
29use core::task::{Context, Poll}; 30use core::task::{Context, Poll};
30 31
@@ -36,7 +37,7 @@ use embassy_time::{Instant, Timer};
36use heapless::Vec; 37use heapless::Vec;
37#[cfg(feature = "dns")] 38#[cfg(feature = "dns")]
38pub use smoltcp::config::DNS_MAX_SERVER_COUNT; 39pub use smoltcp::config::DNS_MAX_SERVER_COUNT;
39#[cfg(feature = "igmp")] 40#[cfg(feature = "multicast")]
40pub use smoltcp::iface::MulticastError; 41pub use smoltcp::iface::MulticastError;
41#[allow(unused_imports)] 42#[allow(unused_imports)]
42use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; 43use 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")]
58pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr}; 59pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr};
59 60
60use crate::device::DriverAdapter; 61use crate::driver_util::DriverAdapter;
61use crate::time::{instant_from_smoltcp, instant_to_smoltcp}; 62use crate::time::{instant_from_smoltcp, instant_to_smoltcp};
62 63
63const LOCAL_PORT_MIN: u16 = 1025; 64const 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.
71pub struct StackResources<const SOCK: usize> { 72pub 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")]
80struct HostnameResources { 82struct 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
85impl<const SOCK: usize> StackResources<SOCK> { 87impl<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.
246pub 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
245pub 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>>, 256pub struct Stack<'d> {
257 inner: &'d RefCell<Inner>,
248} 258}
249 259
250struct Inner<D: Driver> { 260pub(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
268pub(crate) struct SocketStack { 282fn _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.
287pub 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
275fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> (HardwareAddress, Medium) { 358fn 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
295impl<D: Driver> Stack<D> { 378impl<'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")]
608impl<D: Driver> Stack<D> { 612impl<'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
693impl SocketStack { 629impl 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
702impl<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
864impl<'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}