2021-03-02 20:20:00 +00:00
|
|
|
use core::cell::RefCell;
|
2021-02-03 04:09:37 +00:00
|
|
|
use core::future::Future;
|
|
|
|
use core::task::Context;
|
|
|
|
use core::task::Poll;
|
2021-09-10 23:53:53 +00:00
|
|
|
use embassy::blocking_mutex::ThreadModeMutex;
|
2021-02-03 04:09:37 +00:00
|
|
|
use embassy::time::{Instant, Timer};
|
2021-09-10 23:53:53 +00:00
|
|
|
use embassy::waitqueue::WakerRegistration;
|
2021-02-03 04:09:37 +00:00
|
|
|
use futures::pin_mut;
|
2021-04-12 19:00:23 +00:00
|
|
|
use smoltcp::iface::InterfaceBuilder;
|
2021-11-26 03:12:14 +00:00
|
|
|
use smoltcp::iface::SocketStorage;
|
2021-11-26 19:39:21 +00:00
|
|
|
use smoltcp::time::Instant as SmolInstant;
|
|
|
|
use smoltcp::wire::{IpCidr, Ipv4Address, Ipv4Cidr};
|
|
|
|
|
2021-04-12 19:00:23 +00:00
|
|
|
#[cfg(feature = "medium-ethernet")]
|
|
|
|
use smoltcp::iface::{Neighbor, NeighborCache, Route, Routes};
|
2021-08-20 13:06:06 +00:00
|
|
|
#[cfg(feature = "medium-ethernet")]
|
2021-11-26 19:39:21 +00:00
|
|
|
use smoltcp::phy::{Device as _, Medium};
|
2021-08-20 13:06:06 +00:00
|
|
|
#[cfg(feature = "medium-ethernet")]
|
2021-11-26 19:39:21 +00:00
|
|
|
use smoltcp::wire::{EthernetAddress, HardwareAddress, IpAddress};
|
2021-02-03 04:09:37 +00:00
|
|
|
|
2021-04-07 17:06:45 +00:00
|
|
|
use crate::config::Configurator;
|
|
|
|
use crate::config::Event;
|
|
|
|
use crate::device::{Device, DeviceAdapter, LinkState};
|
2022-05-02 14:15:05 +00:00
|
|
|
use crate::{Config, Interface};
|
2021-02-03 04:09:37 +00:00
|
|
|
|
|
|
|
const LOCAL_PORT_MIN: u16 = 1025;
|
|
|
|
const LOCAL_PORT_MAX: u16 = 65535;
|
|
|
|
|
2021-06-20 19:46:26 +00:00
|
|
|
pub struct StackResources<const ADDR: usize, const SOCK: usize, const NEIGHBOR: usize> {
|
|
|
|
addresses: [IpCidr; ADDR],
|
2021-11-26 03:12:14 +00:00
|
|
|
sockets: [SocketStorage<'static>; SOCK],
|
2021-04-12 19:00:23 +00:00
|
|
|
|
|
|
|
#[cfg(feature = "medium-ethernet")]
|
2021-02-03 04:09:37 +00:00
|
|
|
routes: [Option<(IpCidr, Route)>; 1],
|
2021-04-12 19:00:23 +00:00
|
|
|
#[cfg(feature = "medium-ethernet")]
|
2021-06-20 19:46:26 +00:00
|
|
|
neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR],
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<const ADDR: usize, const SOCK: usize, const NEIGHBOR: usize>
|
|
|
|
StackResources<ADDR, SOCK, NEIGHBOR>
|
|
|
|
{
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32); ADDR],
|
2021-11-26 03:12:14 +00:00
|
|
|
sockets: [SocketStorage::EMPTY; SOCK],
|
2021-08-20 13:06:06 +00:00
|
|
|
#[cfg(feature = "medium-ethernet")]
|
2021-06-20 19:46:26 +00:00
|
|
|
routes: [None; 1],
|
2021-08-20 13:06:06 +00:00
|
|
|
#[cfg(feature = "medium-ethernet")]
|
2021-06-20 19:46:26 +00:00
|
|
|
neighbor_cache: [None; NEIGHBOR],
|
|
|
|
}
|
|
|
|
}
|
2021-02-03 04:09:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static STACK: ThreadModeMutex<RefCell<Option<Stack>>> = ThreadModeMutex::new(RefCell::new(None));
|
|
|
|
|
|
|
|
pub(crate) struct Stack {
|
2021-11-26 03:12:14 +00:00
|
|
|
pub iface: Interface,
|
2021-02-03 04:09:37 +00:00
|
|
|
link_up: bool,
|
2022-05-02 14:15:05 +00:00
|
|
|
config: Option<Config>,
|
2021-02-03 04:09:37 +00:00
|
|
|
next_local_port: u16,
|
|
|
|
configurator: &'static mut dyn Configurator,
|
|
|
|
waker: WakerRegistration,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Stack {
|
|
|
|
pub(crate) fn with<R>(f: impl FnOnce(&mut Stack) -> R) -> R {
|
|
|
|
let mut stack = STACK.borrow().borrow_mut();
|
|
|
|
let stack = stack.as_mut().unwrap();
|
|
|
|
f(stack)
|
|
|
|
}
|
|
|
|
|
2021-10-28 01:07:06 +00:00
|
|
|
#[allow(clippy::absurd_extreme_comparisons)]
|
2021-02-03 04:09:37 +00:00
|
|
|
pub fn get_local_port(&mut self) -> u16 {
|
|
|
|
let res = self.next_local_port;
|
|
|
|
self.next_local_port = if res >= LOCAL_PORT_MAX {
|
|
|
|
LOCAL_PORT_MIN
|
|
|
|
} else {
|
|
|
|
res + 1
|
|
|
|
};
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn wake(&mut self) {
|
|
|
|
self.waker.wake()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn poll_configurator(&mut self, timestamp: SmolInstant) {
|
2021-08-20 13:06:06 +00:00
|
|
|
#[cfg(feature = "medium-ethernet")]
|
2021-04-07 17:06:45 +00:00
|
|
|
let medium = self.iface.device().capabilities().medium;
|
|
|
|
|
2021-11-26 03:12:14 +00:00
|
|
|
match self.configurator.poll(&mut self.iface, timestamp) {
|
2021-04-07 17:06:45 +00:00
|
|
|
Event::NoChange => {}
|
|
|
|
Event::Configured(config) => {
|
|
|
|
debug!("Acquired IP configuration:");
|
|
|
|
|
|
|
|
debug!(" IP address: {}", config.address);
|
|
|
|
set_ipv4_addr(&mut self.iface, config.address);
|
|
|
|
|
2021-04-12 19:00:23 +00:00
|
|
|
#[cfg(feature = "medium-ethernet")]
|
2021-04-07 17:06:45 +00:00
|
|
|
if medium == Medium::Ethernet {
|
|
|
|
if let Some(gateway) = config.gateway {
|
|
|
|
debug!(" Default gateway: {}", gateway);
|
|
|
|
self.iface
|
|
|
|
.routes_mut()
|
|
|
|
.add_default_ipv4_route(gateway)
|
|
|
|
.unwrap();
|
|
|
|
} else {
|
|
|
|
debug!(" Default gateway: None");
|
|
|
|
self.iface.routes_mut().remove_default_ipv4_route();
|
2021-02-03 04:09:37 +00:00
|
|
|
}
|
2021-04-07 17:06:45 +00:00
|
|
|
}
|
|
|
|
for (i, s) in config.dns_servers.iter().enumerate() {
|
|
|
|
debug!(" DNS server {}: {}", i, s);
|
|
|
|
}
|
2021-04-13 15:14:23 +00:00
|
|
|
|
2022-05-02 14:15:05 +00:00
|
|
|
self.config = Some(config)
|
2021-04-07 17:06:45 +00:00
|
|
|
}
|
|
|
|
Event::Deconfigured => {
|
|
|
|
debug!("Lost IP configuration");
|
|
|
|
set_ipv4_addr(&mut self.iface, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0));
|
2021-04-12 19:00:23 +00:00
|
|
|
#[cfg(feature = "medium-ethernet")]
|
2021-04-07 17:06:45 +00:00
|
|
|
if medium == Medium::Ethernet {
|
|
|
|
self.iface.routes_mut().remove_default_ipv4_route();
|
|
|
|
}
|
2022-05-02 14:15:05 +00:00
|
|
|
self.config = None
|
2021-02-03 04:09:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn poll(&mut self, cx: &mut Context<'_>) {
|
|
|
|
self.iface.device_mut().device.register_waker(cx.waker());
|
|
|
|
self.waker.register(cx.waker());
|
|
|
|
|
|
|
|
let timestamp = instant_to_smoltcp(Instant::now());
|
2021-11-26 03:12:14 +00:00
|
|
|
if self.iface.poll(timestamp).is_err() {
|
2021-02-03 04:09:37 +00:00
|
|
|
// If poll() returns error, it may not be done yet, so poll again later.
|
|
|
|
cx.waker().wake_by_ref();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update link up
|
|
|
|
let old_link_up = self.link_up;
|
|
|
|
self.link_up = self.iface.device_mut().device.link_state() == LinkState::Up;
|
|
|
|
|
|
|
|
// Print when changed
|
|
|
|
if old_link_up != self.link_up {
|
2021-10-17 22:55:43 +00:00
|
|
|
info!("link_up = {:?}", self.link_up);
|
2021-02-03 04:09:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if old_link_up || self.link_up {
|
|
|
|
self.poll_configurator(timestamp)
|
|
|
|
}
|
|
|
|
|
2021-11-26 03:12:14 +00:00
|
|
|
if let Some(poll_at) = self.iface.poll_at(timestamp) {
|
2021-02-03 04:09:37 +00:00
|
|
|
let t = Timer::at(instant_from_smoltcp(poll_at));
|
|
|
|
pin_mut!(t);
|
|
|
|
if t.poll(cx).is_ready() {
|
|
|
|
cx.waker().wake_by_ref();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-07 17:06:45 +00:00
|
|
|
fn set_ipv4_addr(iface: &mut Interface, cidr: Ipv4Cidr) {
|
|
|
|
iface.update_ip_addrs(|addrs| {
|
|
|
|
let dest = addrs.iter_mut().next().unwrap();
|
|
|
|
*dest = IpCidr::Ipv4(cidr);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-02-03 04:09:37 +00:00
|
|
|
/// Initialize embassy_net.
|
|
|
|
/// This function must be called from thread mode.
|
2021-06-20 19:46:26 +00:00
|
|
|
pub fn init<const ADDR: usize, const SOCK: usize, const NEIGH: usize>(
|
|
|
|
device: &'static mut dyn Device,
|
|
|
|
configurator: &'static mut dyn Configurator,
|
|
|
|
resources: &'static mut StackResources<ADDR, SOCK, NEIGH>,
|
|
|
|
) {
|
2021-08-20 13:06:06 +00:00
|
|
|
#[cfg(feature = "medium-ethernet")]
|
2021-02-03 04:09:37 +00:00
|
|
|
let medium = device.capabilities().medium;
|
2021-04-12 19:00:23 +00:00
|
|
|
|
|
|
|
#[cfg(feature = "medium-ethernet")]
|
2021-02-24 21:31:07 +00:00
|
|
|
let ethernet_addr = if medium == Medium::Ethernet {
|
|
|
|
device.ethernet_address()
|
|
|
|
} else {
|
|
|
|
[0, 0, 0, 0, 0, 0]
|
|
|
|
};
|
2021-02-03 04:09:37 +00:00
|
|
|
|
2021-11-26 03:12:14 +00:00
|
|
|
let mut b = InterfaceBuilder::new(DeviceAdapter::new(device), &mut resources.sockets[..]);
|
2021-06-20 19:46:26 +00:00
|
|
|
b = b.ip_addrs(&mut resources.addresses[..]);
|
2021-02-03 04:09:37 +00:00
|
|
|
|
2021-04-12 19:00:23 +00:00
|
|
|
#[cfg(feature = "medium-ethernet")]
|
2021-02-03 04:09:37 +00:00
|
|
|
if medium == Medium::Ethernet {
|
2021-11-26 03:12:14 +00:00
|
|
|
b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(ethernet_addr)));
|
2021-06-20 19:46:26 +00:00
|
|
|
b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..]));
|
|
|
|
b = b.routes(Routes::new(&mut resources.routes[..]));
|
2021-02-03 04:09:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let iface = b.finalize();
|
|
|
|
|
|
|
|
let local_port = loop {
|
|
|
|
let mut res = [0u8; 2];
|
2021-04-12 13:35:54 +00:00
|
|
|
rand(&mut res);
|
2021-02-03 04:09:37 +00:00
|
|
|
let port = u16::from_le_bytes(res);
|
2021-10-17 22:55:43 +00:00
|
|
|
if (LOCAL_PORT_MIN..=LOCAL_PORT_MAX).contains(&port) {
|
2021-02-03 04:09:37 +00:00
|
|
|
break port;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let stack = Stack {
|
|
|
|
iface,
|
|
|
|
link_up: false,
|
2022-05-02 14:15:05 +00:00
|
|
|
config: None,
|
2021-02-03 04:09:37 +00:00
|
|
|
configurator,
|
|
|
|
next_local_port: local_port,
|
|
|
|
waker: WakerRegistration::new(),
|
|
|
|
};
|
|
|
|
|
|
|
|
*STACK.borrow().borrow_mut() = Some(stack);
|
|
|
|
}
|
|
|
|
|
2022-05-02 14:15:05 +00:00
|
|
|
pub fn ethernet_address() -> [u8; 6] {
|
|
|
|
STACK
|
|
|
|
.borrow()
|
|
|
|
.borrow()
|
|
|
|
.as_ref()
|
|
|
|
.unwrap()
|
|
|
|
.iface
|
|
|
|
.device()
|
|
|
|
.device
|
|
|
|
.ethernet_address()
|
|
|
|
}
|
|
|
|
|
2021-02-03 04:09:37 +00:00
|
|
|
pub fn is_init() -> bool {
|
|
|
|
STACK.borrow().borrow().is_some()
|
|
|
|
}
|
|
|
|
|
2021-04-13 15:14:23 +00:00
|
|
|
pub fn is_link_up() -> bool {
|
|
|
|
STACK.borrow().borrow().as_ref().unwrap().link_up
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_config_up() -> bool {
|
2022-05-02 14:15:05 +00:00
|
|
|
STACK.borrow().borrow().as_ref().unwrap().config.is_some()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn config() -> Option<Config> {
|
|
|
|
STACK.borrow().borrow().as_ref().unwrap().config.clone()
|
2021-04-13 15:14:23 +00:00
|
|
|
}
|
|
|
|
|
2022-04-12 18:14:12 +00:00
|
|
|
pub async fn run() -> ! {
|
2021-02-03 04:09:37 +00:00
|
|
|
futures::future::poll_fn(|cx| {
|
|
|
|
Stack::with(|stack| stack.poll(cx));
|
|
|
|
Poll::<()>::Pending
|
|
|
|
})
|
2022-04-12 18:14:12 +00:00
|
|
|
.await;
|
|
|
|
unreachable!()
|
2021-02-03 04:09:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn instant_to_smoltcp(instant: Instant) -> SmolInstant {
|
|
|
|
SmolInstant::from_millis(instant.as_millis() as i64)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn instant_from_smoltcp(instant: SmolInstant) -> Instant {
|
|
|
|
Instant::from_millis(instant.total_millis() as u64)
|
|
|
|
}
|
2021-04-12 13:35:54 +00:00
|
|
|
|
|
|
|
extern "Rust" {
|
|
|
|
fn _embassy_rand(buf: &mut [u8]);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn rand(buf: &mut [u8]) {
|
|
|
|
unsafe { _embassy_rand(buf) }
|
|
|
|
}
|