WIP embassy-net v2
This commit is contained in:
		@@ -20,6 +20,7 @@ std = []
 | 
			
		||||
defmt = ["dep:defmt", "smoltcp/defmt"]
 | 
			
		||||
 | 
			
		||||
tcp = ["smoltcp/socket-tcp"]
 | 
			
		||||
dns = ["smoltcp/socket-dns"]
 | 
			
		||||
dhcpv4 = ["medium-ethernet", "smoltcp/socket-dhcpv4"]
 | 
			
		||||
medium-ethernet = ["smoltcp/medium-ethernet"]
 | 
			
		||||
medium-ip = ["smoltcp/medium-ip"]
 | 
			
		||||
@@ -49,6 +50,8 @@ atomic-pool = "0.2.1"
 | 
			
		||||
 | 
			
		||||
[dependencies.smoltcp]
 | 
			
		||||
version = "0.8.0"
 | 
			
		||||
git = "https://github.com/smoltcp-rs/smoltcp"
 | 
			
		||||
rev = "ed0cf16750a42f30e31fcaf5347915592924b1e3"
 | 
			
		||||
default-features = false
 | 
			
		||||
features = [
 | 
			
		||||
  "proto-ipv4",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,55 +0,0 @@
 | 
			
		||||
use heapless::Vec;
 | 
			
		||||
use smoltcp::iface::SocketHandle;
 | 
			
		||||
use smoltcp::socket::{Dhcpv4Event, Dhcpv4Socket};
 | 
			
		||||
use smoltcp::time::Instant;
 | 
			
		||||
 | 
			
		||||
use super::*;
 | 
			
		||||
use crate::device::LinkState;
 | 
			
		||||
use crate::Interface;
 | 
			
		||||
 | 
			
		||||
pub struct DhcpConfigurator {
 | 
			
		||||
    handle: Option<SocketHandle>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DhcpConfigurator {
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        Self { handle: None }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Configurator for DhcpConfigurator {
 | 
			
		||||
    fn poll(&mut self, iface: &mut Interface, _timestamp: Instant) -> Event {
 | 
			
		||||
        if self.handle.is_none() {
 | 
			
		||||
            let handle = iface.add_socket(Dhcpv4Socket::new());
 | 
			
		||||
            self.handle = Some(handle)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let link_up = iface.device_mut().device.link_state() == LinkState::Up;
 | 
			
		||||
 | 
			
		||||
        let socket = iface.get_socket::<Dhcpv4Socket>(self.handle.unwrap());
 | 
			
		||||
 | 
			
		||||
        if !link_up {
 | 
			
		||||
            socket.reset();
 | 
			
		||||
            return Event::Deconfigured;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        match socket.poll() {
 | 
			
		||||
            None => Event::NoChange,
 | 
			
		||||
            Some(Dhcpv4Event::Deconfigured) => Event::Deconfigured,
 | 
			
		||||
            Some(Dhcpv4Event::Configured(config)) => {
 | 
			
		||||
                let mut dns_servers = Vec::new();
 | 
			
		||||
                for s in &config.dns_servers {
 | 
			
		||||
                    if let Some(addr) = s {
 | 
			
		||||
                        dns_servers.push(addr.clone()).unwrap();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Event::Configured(Config {
 | 
			
		||||
                    address: config.address,
 | 
			
		||||
                    gateway: config.router,
 | 
			
		||||
                    dns_servers,
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,35 +0,0 @@
 | 
			
		||||
use heapless::Vec;
 | 
			
		||||
use smoltcp::time::Instant;
 | 
			
		||||
use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
 | 
			
		||||
 | 
			
		||||
use crate::Interface;
 | 
			
		||||
 | 
			
		||||
mod statik;
 | 
			
		||||
pub use statik::StaticConfigurator;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "dhcpv4")]
 | 
			
		||||
mod dhcp;
 | 
			
		||||
#[cfg(feature = "dhcpv4")]
 | 
			
		||||
pub use dhcp::DhcpConfigurator;
 | 
			
		||||
 | 
			
		||||
/// Return value for the `Configurator::poll` function
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub enum Event {
 | 
			
		||||
    /// No change has occured to the configuration.
 | 
			
		||||
    NoChange,
 | 
			
		||||
    /// Configuration has been lost (for example, DHCP lease has expired)
 | 
			
		||||
    Deconfigured,
 | 
			
		||||
    /// Configuration has been newly acquired, or modified.
 | 
			
		||||
    Configured(Config),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
			
		||||
pub struct Config {
 | 
			
		||||
    pub address: Ipv4Cidr,
 | 
			
		||||
    pub gateway: Option<Ipv4Address>,
 | 
			
		||||
    pub dns_servers: Vec<Ipv4Address, 3>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait Configurator {
 | 
			
		||||
    fn poll(&mut self, iface: &mut Interface, timestamp: Instant) -> Event;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
use smoltcp::time::Instant;
 | 
			
		||||
 | 
			
		||||
use super::*;
 | 
			
		||||
use crate::Interface;
 | 
			
		||||
 | 
			
		||||
pub struct StaticConfigurator {
 | 
			
		||||
    config: Config,
 | 
			
		||||
    returned: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl StaticConfigurator {
 | 
			
		||||
    pub fn new(config: Config) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            config,
 | 
			
		||||
            returned: false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Configurator for StaticConfigurator {
 | 
			
		||||
    fn poll(&mut self, _iface: &mut Interface, _timestamp: Instant) -> Event {
 | 
			
		||||
        if self.returned {
 | 
			
		||||
            Event::NoChange
 | 
			
		||||
        } else {
 | 
			
		||||
            self.returned = true;
 | 
			
		||||
            Event::Configured(self.config.clone())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -12,24 +12,50 @@ pub enum LinkState {
 | 
			
		||||
    Up,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 'static required due to the "fake GAT" in smoltcp::phy::Device.
 | 
			
		||||
// https://github.com/smoltcp-rs/smoltcp/pull/572
 | 
			
		||||
pub trait Device {
 | 
			
		||||
    fn is_transmit_ready(&mut self) -> bool;
 | 
			
		||||
    fn transmit(&mut self, pkt: PacketBuf);
 | 
			
		||||
    fn receive(&mut self) -> Option<PacketBuf>;
 | 
			
		||||
 | 
			
		||||
    fn register_waker(&mut self, waker: &Waker);
 | 
			
		||||
    fn capabilities(&mut self) -> DeviceCapabilities;
 | 
			
		||||
    fn capabilities(&self) -> DeviceCapabilities;
 | 
			
		||||
    fn link_state(&mut self) -> LinkState;
 | 
			
		||||
    fn ethernet_address(&self) -> [u8; 6];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct DeviceAdapter {
 | 
			
		||||
    pub device: &'static mut dyn Device,
 | 
			
		||||
impl<T: ?Sized + Device> Device for &'static mut T {
 | 
			
		||||
    fn is_transmit_ready(&mut self) -> bool {
 | 
			
		||||
        T::is_transmit_ready(self)
 | 
			
		||||
    }
 | 
			
		||||
    fn transmit(&mut self, pkt: PacketBuf) {
 | 
			
		||||
        T::transmit(self, pkt)
 | 
			
		||||
    }
 | 
			
		||||
    fn receive(&mut self) -> Option<PacketBuf> {
 | 
			
		||||
        T::receive(self)
 | 
			
		||||
    }
 | 
			
		||||
    fn register_waker(&mut self, waker: &Waker) {
 | 
			
		||||
        T::register_waker(self, waker)
 | 
			
		||||
    }
 | 
			
		||||
    fn capabilities(&self) -> DeviceCapabilities {
 | 
			
		||||
        T::capabilities(self)
 | 
			
		||||
    }
 | 
			
		||||
    fn link_state(&mut self) -> LinkState {
 | 
			
		||||
        T::link_state(self)
 | 
			
		||||
    }
 | 
			
		||||
    fn ethernet_address(&self) -> [u8; 6] {
 | 
			
		||||
        T::ethernet_address(self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct DeviceAdapter<D: Device> {
 | 
			
		||||
    pub device: D,
 | 
			
		||||
    caps: DeviceCapabilities,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DeviceAdapter {
 | 
			
		||||
    pub(crate) fn new(device: &'static mut dyn Device) -> Self {
 | 
			
		||||
impl<D: Device> DeviceAdapter<D> {
 | 
			
		||||
    pub(crate) fn new(device: D) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            caps: device.capabilities(),
 | 
			
		||||
            device,
 | 
			
		||||
@@ -37,16 +63,16 @@ impl DeviceAdapter {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> SmolDevice<'a> for DeviceAdapter {
 | 
			
		||||
impl<'a, D: Device + 'static> SmolDevice<'a> for DeviceAdapter<D> {
 | 
			
		||||
    type RxToken = RxToken;
 | 
			
		||||
    type TxToken = TxToken<'a>;
 | 
			
		||||
    type TxToken = TxToken<'a, D>;
 | 
			
		||||
 | 
			
		||||
    fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
 | 
			
		||||
        let tx_pkt = PacketBox::new(Packet::new())?;
 | 
			
		||||
        let rx_pkt = self.device.receive()?;
 | 
			
		||||
        let rx_token = RxToken { pkt: rx_pkt };
 | 
			
		||||
        let tx_token = TxToken {
 | 
			
		||||
            device: self.device,
 | 
			
		||||
            device: &mut self.device,
 | 
			
		||||
            pkt: tx_pkt,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
@@ -61,7 +87,7 @@ impl<'a> SmolDevice<'a> for DeviceAdapter {
 | 
			
		||||
 | 
			
		||||
        let tx_pkt = PacketBox::new(Packet::new())?;
 | 
			
		||||
        Some(TxToken {
 | 
			
		||||
            device: self.device,
 | 
			
		||||
            device: &mut self.device,
 | 
			
		||||
            pkt: tx_pkt,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
@@ -85,12 +111,12 @@ impl smoltcp::phy::RxToken for RxToken {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct TxToken<'a> {
 | 
			
		||||
    device: &'a mut dyn Device,
 | 
			
		||||
pub struct TxToken<'a, D: Device> {
 | 
			
		||||
    device: &'a mut D,
 | 
			
		||||
    pkt: PacketBox,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> smoltcp::phy::TxToken for TxToken<'a> {
 | 
			
		||||
impl<'a, D: Device> smoltcp::phy::TxToken for TxToken<'a, D> {
 | 
			
		||||
    fn consume<R, F>(self, _timestamp: SmolInstant, len: usize, f: F) -> smoltcp::Result<R>
 | 
			
		||||
    where
 | 
			
		||||
        F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
 | 
			
		||||
 
 | 
			
		||||
@@ -5,20 +5,13 @@
 | 
			
		||||
// This mod MUST go first, so that the others see its macros.
 | 
			
		||||
pub(crate) mod fmt;
 | 
			
		||||
 | 
			
		||||
mod config;
 | 
			
		||||
mod device;
 | 
			
		||||
mod packet_pool;
 | 
			
		||||
mod stack;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "dhcpv4")]
 | 
			
		||||
pub use config::DhcpConfigurator;
 | 
			
		||||
pub use config::{Config, Configurator, Event as ConfigEvent, StaticConfigurator};
 | 
			
		||||
 | 
			
		||||
pub use device::{Device, LinkState};
 | 
			
		||||
pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf, MTU};
 | 
			
		||||
pub use stack::{
 | 
			
		||||
    config, ethernet_address, init, is_config_up, is_init, is_link_up, run, StackResources,
 | 
			
		||||
};
 | 
			
		||||
pub use stack::{Config, ConfigStrategy, Stack, StackResources};
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "tcp")]
 | 
			
		||||
pub mod tcp;
 | 
			
		||||
@@ -30,4 +23,3 @@ pub use smoltcp::time::Instant as SmolInstant;
 | 
			
		||||
#[cfg(feature = "medium-ethernet")]
 | 
			
		||||
pub use smoltcp::wire::{EthernetAddress, HardwareAddress};
 | 
			
		||||
pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr};
 | 
			
		||||
pub type Interface = smoltcp::iface::Interface<'static, device::DeviceAdapter>;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,18 @@
 | 
			
		||||
use core::cell::RefCell;
 | 
			
		||||
use core::cell::UnsafeCell;
 | 
			
		||||
use core::future::Future;
 | 
			
		||||
use core::task::Context;
 | 
			
		||||
use core::task::Poll;
 | 
			
		||||
use embassy::blocking_mutex::ThreadModeMutex;
 | 
			
		||||
use embassy::time::{Instant, Timer};
 | 
			
		||||
use embassy::waitqueue::WakerRegistration;
 | 
			
		||||
use futures::future::poll_fn;
 | 
			
		||||
use futures::pin_mut;
 | 
			
		||||
use smoltcp::iface::InterfaceBuilder;
 | 
			
		||||
use smoltcp::iface::SocketStorage;
 | 
			
		||||
use heapless::Vec;
 | 
			
		||||
#[cfg(feature = "dhcpv4")]
 | 
			
		||||
use smoltcp::iface::SocketHandle;
 | 
			
		||||
use smoltcp::iface::{Interface, InterfaceBuilder};
 | 
			
		||||
use smoltcp::iface::{SocketSet, SocketStorage};
 | 
			
		||||
#[cfg(feature = "dhcpv4")]
 | 
			
		||||
use smoltcp::socket::dhcpv4;
 | 
			
		||||
use smoltcp::time::Instant as SmolInstant;
 | 
			
		||||
use smoltcp::wire::{IpCidr, Ipv4Address, Ipv4Cidr};
 | 
			
		||||
 | 
			
		||||
@@ -18,10 +23,7 @@ use smoltcp::phy::{Device as _, Medium};
 | 
			
		||||
#[cfg(feature = "medium-ethernet")]
 | 
			
		||||
use smoltcp::wire::{EthernetAddress, HardwareAddress, IpAddress};
 | 
			
		||||
 | 
			
		||||
use crate::config::Configurator;
 | 
			
		||||
use crate::config::Event;
 | 
			
		||||
use crate::device::{Device, DeviceAdapter, LinkState};
 | 
			
		||||
use crate::{Config, Interface};
 | 
			
		||||
 | 
			
		||||
const LOCAL_PORT_MIN: u16 = 1025;
 | 
			
		||||
const LOCAL_PORT_MAX: u16 = 65535;
 | 
			
		||||
@@ -51,130 +53,48 @@ impl<const ADDR: usize, const SOCK: usize, const NEIGHBOR: usize>
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static STACK: ThreadModeMutex<RefCell<Option<Stack>>> = ThreadModeMutex::new(RefCell::new(None));
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Eq)]
 | 
			
		||||
pub struct Config {
 | 
			
		||||
    pub address: Ipv4Cidr,
 | 
			
		||||
    pub gateway: Option<Ipv4Address>,
 | 
			
		||||
    pub dns_servers: Vec<Ipv4Address, 3>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) struct Stack {
 | 
			
		||||
    pub iface: Interface,
 | 
			
		||||
pub enum ConfigStrategy {
 | 
			
		||||
    Static(Config),
 | 
			
		||||
    #[cfg(feature = "dhcpv4")]
 | 
			
		||||
    Dhcp,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Stack<D: Device> {
 | 
			
		||||
    pub(crate) socket: UnsafeCell<SocketStack>,
 | 
			
		||||
    inner: UnsafeCell<Inner<D>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Inner<D: Device> {
 | 
			
		||||
    device: DeviceAdapter<D>,
 | 
			
		||||
    link_up: bool,
 | 
			
		||||
    config: Option<Config>,
 | 
			
		||||
    #[cfg(feature = "dhcpv4")]
 | 
			
		||||
    dhcp_socket: Option<SocketHandle>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) struct SocketStack {
 | 
			
		||||
    pub(crate) sockets: SocketSet<'static>,
 | 
			
		||||
    pub(crate) iface: Interface<'static>,
 | 
			
		||||
    pub(crate) waker: WakerRegistration,
 | 
			
		||||
    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)
 | 
			
		||||
    }
 | 
			
		||||
unsafe impl<D: Device> Send for Stack<D> {}
 | 
			
		||||
 | 
			
		||||
    #[allow(clippy::absurd_extreme_comparisons)]
 | 
			
		||||
    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) {
 | 
			
		||||
        #[cfg(feature = "medium-ethernet")]
 | 
			
		||||
        let medium = self.iface.device().capabilities().medium;
 | 
			
		||||
 | 
			
		||||
        match self.configurator.poll(&mut self.iface, timestamp) {
 | 
			
		||||
            Event::NoChange => {}
 | 
			
		||||
            Event::Configured(config) => {
 | 
			
		||||
                debug!("Acquired IP configuration:");
 | 
			
		||||
 | 
			
		||||
                debug!("   IP address:      {}", config.address);
 | 
			
		||||
                set_ipv4_addr(&mut self.iface, config.address);
 | 
			
		||||
 | 
			
		||||
                #[cfg(feature = "medium-ethernet")]
 | 
			
		||||
                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();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                for (i, s) in config.dns_servers.iter().enumerate() {
 | 
			
		||||
                    debug!("   DNS server {}:    {}", i, s);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                self.config = Some(config)
 | 
			
		||||
            }
 | 
			
		||||
            Event::Deconfigured => {
 | 
			
		||||
                debug!("Lost IP configuration");
 | 
			
		||||
                set_ipv4_addr(&mut self.iface, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0));
 | 
			
		||||
                #[cfg(feature = "medium-ethernet")]
 | 
			
		||||
                if medium == Medium::Ethernet {
 | 
			
		||||
                    self.iface.routes_mut().remove_default_ipv4_route();
 | 
			
		||||
                }
 | 
			
		||||
                self.config = None
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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());
 | 
			
		||||
        if self.iface.poll(timestamp).is_err() {
 | 
			
		||||
            // 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 {
 | 
			
		||||
            info!("link_up = {:?}", self.link_up);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if old_link_up || self.link_up {
 | 
			
		||||
            self.poll_configurator(timestamp)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(poll_at) = self.iface.poll_at(timestamp) {
 | 
			
		||||
            let t = Timer::at(instant_from_smoltcp(poll_at));
 | 
			
		||||
            pin_mut!(t);
 | 
			
		||||
            if t.poll(cx).is_ready() {
 | 
			
		||||
                cx.waker().wake_by_ref();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Initialize embassy_net.
 | 
			
		||||
/// This function must be called from thread mode.
 | 
			
		||||
pub fn init<const ADDR: usize, const SOCK: usize, const NEIGH: usize>(
 | 
			
		||||
    device: &'static mut dyn Device,
 | 
			
		||||
    configurator: &'static mut dyn Configurator,
 | 
			
		||||
impl<D: Device + 'static> Stack<D> {
 | 
			
		||||
    pub fn new<const ADDR: usize, const SOCK: usize, const NEIGH: usize>(
 | 
			
		||||
        device: D,
 | 
			
		||||
        config: ConfigStrategy,
 | 
			
		||||
        resources: &'static mut StackResources<ADDR, SOCK, NEIGH>,
 | 
			
		||||
) {
 | 
			
		||||
        random_seed: u64,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        #[cfg(feature = "medium-ethernet")]
 | 
			
		||||
        let medium = device.capabilities().medium;
 | 
			
		||||
 | 
			
		||||
@@ -185,8 +105,11 @@ pub fn init<const ADDR: usize, const SOCK: usize, const NEIGH: usize>(
 | 
			
		||||
            [0, 0, 0, 0, 0, 0]
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    let mut b = InterfaceBuilder::new(DeviceAdapter::new(device), &mut resources.sockets[..]);
 | 
			
		||||
        let mut device = DeviceAdapter::new(device);
 | 
			
		||||
 | 
			
		||||
        let mut b = InterfaceBuilder::new();
 | 
			
		||||
        b = b.ip_addrs(&mut resources.addresses[..]);
 | 
			
		||||
        b = b.random_seed(random_seed);
 | 
			
		||||
 | 
			
		||||
        #[cfg(feature = "medium-ethernet")]
 | 
			
		||||
        if medium == Medium::Ethernet {
 | 
			
		||||
@@ -195,64 +118,208 @@ pub fn init<const ADDR: usize, const SOCK: usize, const NEIGH: usize>(
 | 
			
		||||
            b = b.routes(Routes::new(&mut resources.routes[..]));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    let iface = b.finalize();
 | 
			
		||||
        let iface = b.finalize(&mut device);
 | 
			
		||||
 | 
			
		||||
    let local_port = loop {
 | 
			
		||||
        let mut res = [0u8; 2];
 | 
			
		||||
        rand(&mut res);
 | 
			
		||||
        let port = u16::from_le_bytes(res);
 | 
			
		||||
        if (LOCAL_PORT_MIN..=LOCAL_PORT_MAX).contains(&port) {
 | 
			
		||||
            break port;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
        let sockets = SocketSet::new(&mut resources.sockets[..]);
 | 
			
		||||
 | 
			
		||||
    let stack = Stack {
 | 
			
		||||
        iface,
 | 
			
		||||
        let next_local_port =
 | 
			
		||||
            (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN;
 | 
			
		||||
 | 
			
		||||
        let mut inner = Inner {
 | 
			
		||||
            device,
 | 
			
		||||
            link_up: false,
 | 
			
		||||
            config: None,
 | 
			
		||||
        configurator,
 | 
			
		||||
        next_local_port: local_port,
 | 
			
		||||
            #[cfg(feature = "dhcpv4")]
 | 
			
		||||
            dhcp_socket: None,
 | 
			
		||||
        };
 | 
			
		||||
        let mut socket = SocketStack {
 | 
			
		||||
            sockets,
 | 
			
		||||
            iface,
 | 
			
		||||
            waker: WakerRegistration::new(),
 | 
			
		||||
            next_local_port,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
    *STACK.borrow().borrow_mut() = Some(stack);
 | 
			
		||||
}
 | 
			
		||||
        match config {
 | 
			
		||||
            ConfigStrategy::Static(config) => inner.apply_config(&mut socket, config),
 | 
			
		||||
            #[cfg(feature = "dhcpv4")]
 | 
			
		||||
            ConfigStrategy::Dhcp => {
 | 
			
		||||
                let handle = socket.sockets.add(smoltcp::socket::dhcpv4::Socket::new());
 | 
			
		||||
                inner.dhcp_socket = Some(handle);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
pub fn ethernet_address() -> [u8; 6] {
 | 
			
		||||
    STACK
 | 
			
		||||
        .borrow()
 | 
			
		||||
        .borrow()
 | 
			
		||||
        .as_ref()
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .iface
 | 
			
		||||
        .device()
 | 
			
		||||
        .device
 | 
			
		||||
        .ethernet_address()
 | 
			
		||||
}
 | 
			
		||||
        Self {
 | 
			
		||||
            socket: UnsafeCell::new(socket),
 | 
			
		||||
            inner: UnsafeCell::new(inner),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
pub fn is_init() -> bool {
 | 
			
		||||
    STACK.borrow().borrow().is_some()
 | 
			
		||||
}
 | 
			
		||||
    /// SAFETY: must not call reentrantly.
 | 
			
		||||
    unsafe fn with<R>(&self, f: impl FnOnce(&SocketStack, &Inner<D>) -> R) -> R {
 | 
			
		||||
        f(&*self.socket.get(), &*self.inner.get())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
pub fn is_link_up() -> bool {
 | 
			
		||||
    STACK.borrow().borrow().as_ref().unwrap().link_up
 | 
			
		||||
}
 | 
			
		||||
    /// SAFETY: must not call reentrantly.
 | 
			
		||||
    unsafe fn with_mut<R>(&self, f: impl FnOnce(&mut SocketStack, &mut Inner<D>) -> R) -> R {
 | 
			
		||||
        f(&mut *self.socket.get(), &mut *self.inner.get())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
pub fn is_config_up() -> bool {
 | 
			
		||||
    STACK.borrow().borrow().as_ref().unwrap().config.is_some()
 | 
			
		||||
}
 | 
			
		||||
    pub fn ethernet_address(&self) -> [u8; 6] {
 | 
			
		||||
        unsafe { self.with(|_s, i| i.device.device.ethernet_address()) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
pub fn config() -> Option<Config> {
 | 
			
		||||
    STACK.borrow().borrow().as_ref().unwrap().config.clone()
 | 
			
		||||
}
 | 
			
		||||
    pub fn is_link_up(&self) -> bool {
 | 
			
		||||
        unsafe { self.with(|_s, i| i.link_up) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
pub async fn run() -> ! {
 | 
			
		||||
    futures::future::poll_fn(|cx| {
 | 
			
		||||
        Stack::with(|stack| stack.poll(cx));
 | 
			
		||||
    pub fn is_config_up(&self) -> bool {
 | 
			
		||||
        unsafe { self.with(|_s, i| i.config.is_some()) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn config(&self) -> Option<Config> {
 | 
			
		||||
        unsafe { self.with(|_s, i| i.config.clone()) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn run(&self) -> ! {
 | 
			
		||||
        poll_fn(|cx| {
 | 
			
		||||
            unsafe { self.with_mut(|s, i| i.poll(cx, s)) }
 | 
			
		||||
            Poll::<()>::Pending
 | 
			
		||||
        })
 | 
			
		||||
        .await;
 | 
			
		||||
        unreachable!()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SocketStack {
 | 
			
		||||
    #[allow(clippy::absurd_extreme_comparisons)]
 | 
			
		||||
    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
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<D: Device + 'static> Inner<D> {
 | 
			
		||||
    fn apply_config(&mut self, s: &mut SocketStack, config: Config) {
 | 
			
		||||
        #[cfg(feature = "medium-ethernet")]
 | 
			
		||||
        let medium = self.device.capabilities().medium;
 | 
			
		||||
 | 
			
		||||
        debug!("Acquired IP configuration:");
 | 
			
		||||
 | 
			
		||||
        debug!("   IP address:      {}", config.address);
 | 
			
		||||
        self.set_ipv4_addr(s, config.address);
 | 
			
		||||
 | 
			
		||||
        #[cfg(feature = "medium-ethernet")]
 | 
			
		||||
        if medium == Medium::Ethernet {
 | 
			
		||||
            if let Some(gateway) = config.gateway {
 | 
			
		||||
                debug!("   Default gateway: {}", gateway);
 | 
			
		||||
                s.iface
 | 
			
		||||
                    .routes_mut()
 | 
			
		||||
                    .add_default_ipv4_route(gateway)
 | 
			
		||||
                    .unwrap();
 | 
			
		||||
            } else {
 | 
			
		||||
                debug!("   Default gateway: None");
 | 
			
		||||
                s.iface.routes_mut().remove_default_ipv4_route();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        for (i, s) in config.dns_servers.iter().enumerate() {
 | 
			
		||||
            debug!("   DNS server {}:    {}", i, s);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.config = Some(config)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[allow(unused)] // used only with dhcp
 | 
			
		||||
    fn unapply_config(&mut self, s: &mut SocketStack) {
 | 
			
		||||
        #[cfg(feature = "medium-ethernet")]
 | 
			
		||||
        let medium = self.device.capabilities().medium;
 | 
			
		||||
 | 
			
		||||
        debug!("Lost IP configuration");
 | 
			
		||||
        self.set_ipv4_addr(s, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0));
 | 
			
		||||
        #[cfg(feature = "medium-ethernet")]
 | 
			
		||||
        if medium == Medium::Ethernet {
 | 
			
		||||
            s.iface.routes_mut().remove_default_ipv4_route();
 | 
			
		||||
        }
 | 
			
		||||
        self.config = None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn set_ipv4_addr(&mut self, s: &mut SocketStack, cidr: Ipv4Cidr) {
 | 
			
		||||
        s.iface.update_ip_addrs(|addrs| {
 | 
			
		||||
            let dest = addrs.iter_mut().next().unwrap();
 | 
			
		||||
            *dest = IpCidr::Ipv4(cidr);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) {
 | 
			
		||||
        self.device.device.register_waker(cx.waker());
 | 
			
		||||
        s.waker.register(cx.waker());
 | 
			
		||||
 | 
			
		||||
        let timestamp = instant_to_smoltcp(Instant::now());
 | 
			
		||||
        if s.iface
 | 
			
		||||
            .poll(timestamp, &mut self.device, &mut s.sockets)
 | 
			
		||||
            .is_err()
 | 
			
		||||
        {
 | 
			
		||||
            // 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.device.device.link_state() == LinkState::Up;
 | 
			
		||||
 | 
			
		||||
        // Print when changed
 | 
			
		||||
        if old_link_up != self.link_up {
 | 
			
		||||
            info!("link_up = {:?}", self.link_up);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[cfg(feature = "dhcpv4")]
 | 
			
		||||
        if let Some(dhcp_handle) = self.dhcp_socket {
 | 
			
		||||
            let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle);
 | 
			
		||||
 | 
			
		||||
            if self.link_up {
 | 
			
		||||
                match socket.poll() {
 | 
			
		||||
                    None => {}
 | 
			
		||||
                    Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s),
 | 
			
		||||
                    Some(dhcpv4::Event::Configured(config)) => {
 | 
			
		||||
                        let mut dns_servers = Vec::new();
 | 
			
		||||
                        for s in &config.dns_servers {
 | 
			
		||||
                            if let Some(addr) = s {
 | 
			
		||||
                                dns_servers.push(addr.clone()).unwrap();
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        self.apply_config(
 | 
			
		||||
                            s,
 | 
			
		||||
                            Config {
 | 
			
		||||
                                address: config.address,
 | 
			
		||||
                                gateway: config.router,
 | 
			
		||||
                                dns_servers,
 | 
			
		||||
                            },
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else if old_link_up {
 | 
			
		||||
                socket.reset();
 | 
			
		||||
                self.unapply_config(s);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        //if old_link_up || self.link_up {
 | 
			
		||||
        //    self.poll_configurator(timestamp)
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) {
 | 
			
		||||
            let t = Timer::at(instant_from_smoltcp(poll_at));
 | 
			
		||||
            pin_mut!(t);
 | 
			
		||||
            if t.poll(cx).is_ready() {
 | 
			
		||||
                cx.waker().wake_by_ref();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn instant_to_smoltcp(instant: Instant) -> SmolInstant {
 | 
			
		||||
@@ -262,11 +329,3 @@ fn instant_to_smoltcp(instant: Instant) -> SmolInstant {
 | 
			
		||||
fn instant_from_smoltcp(instant: SmolInstant) -> Instant {
 | 
			
		||||
    Instant::from_millis(instant.total_millis() as u64)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern "Rust" {
 | 
			
		||||
    fn _embassy_rand(buf: &mut [u8]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn rand(buf: &mut [u8]) {
 | 
			
		||||
    unsafe { _embassy_rand(buf) }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,16 @@
 | 
			
		||||
use core::cell::UnsafeCell;
 | 
			
		||||
use core::future::Future;
 | 
			
		||||
use core::marker::PhantomData;
 | 
			
		||||
use core::mem;
 | 
			
		||||
use core::task::Poll;
 | 
			
		||||
use futures::future::poll_fn;
 | 
			
		||||
use smoltcp::iface::{Context as SmolContext, SocketHandle};
 | 
			
		||||
use smoltcp::socket::TcpSocket as SyncTcpSocket;
 | 
			
		||||
use smoltcp::socket::{TcpSocketBuffer, TcpState};
 | 
			
		||||
use smoltcp::iface::{Interface, SocketHandle};
 | 
			
		||||
use smoltcp::socket::tcp;
 | 
			
		||||
use smoltcp::time::Duration;
 | 
			
		||||
use smoltcp::wire::IpEndpoint;
 | 
			
		||||
use smoltcp::wire::IpListenEndpoint;
 | 
			
		||||
 | 
			
		||||
use crate::stack::SocketStack;
 | 
			
		||||
use crate::Device;
 | 
			
		||||
 | 
			
		||||
use super::stack::Stack;
 | 
			
		||||
 | 
			
		||||
@@ -42,78 +45,68 @@ pub enum AcceptError {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct TcpSocket<'a> {
 | 
			
		||||
    handle: SocketHandle,
 | 
			
		||||
    ghost: PhantomData<&'a mut [u8]>,
 | 
			
		||||
    io: TcpIo<'a>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Unpin for TcpSocket<'a> {}
 | 
			
		||||
 | 
			
		||||
pub struct TcpReader<'a> {
 | 
			
		||||
    handle: SocketHandle,
 | 
			
		||||
    ghost: PhantomData<&'a mut [u8]>,
 | 
			
		||||
    io: TcpIo<'a>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Unpin for TcpReader<'a> {}
 | 
			
		||||
 | 
			
		||||
pub struct TcpWriter<'a> {
 | 
			
		||||
    handle: SocketHandle,
 | 
			
		||||
    ghost: PhantomData<&'a mut [u8]>,
 | 
			
		||||
    io: TcpIo<'a>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Unpin for TcpWriter<'a> {}
 | 
			
		||||
 | 
			
		||||
impl<'a> TcpSocket<'a> {
 | 
			
		||||
    pub fn new(rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self {
 | 
			
		||||
        let handle = Stack::with(|stack| {
 | 
			
		||||
    pub fn new<D: Device>(
 | 
			
		||||
        stack: &'a Stack<D>,
 | 
			
		||||
        rx_buffer: &'a mut [u8],
 | 
			
		||||
        tx_buffer: &'a mut [u8],
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        // safety: not accessed reentrantly.
 | 
			
		||||
        let s = unsafe { &mut *stack.socket.get() };
 | 
			
		||||
        let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) };
 | 
			
		||||
        let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) };
 | 
			
		||||
            stack.iface.add_socket(SyncTcpSocket::new(
 | 
			
		||||
                TcpSocketBuffer::new(rx_buffer),
 | 
			
		||||
                TcpSocketBuffer::new(tx_buffer),
 | 
			
		||||
            ))
 | 
			
		||||
        });
 | 
			
		||||
        let handle = s.sockets.add(tcp::Socket::new(
 | 
			
		||||
            tcp::SocketBuffer::new(rx_buffer),
 | 
			
		||||
            tcp::SocketBuffer::new(tx_buffer),
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            io: TcpIo {
 | 
			
		||||
                stack: &stack.socket,
 | 
			
		||||
                handle,
 | 
			
		||||
            ghost: PhantomData,
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) {
 | 
			
		||||
        (
 | 
			
		||||
            TcpReader {
 | 
			
		||||
                handle: self.handle,
 | 
			
		||||
                ghost: PhantomData,
 | 
			
		||||
            },
 | 
			
		||||
            TcpWriter {
 | 
			
		||||
                handle: self.handle,
 | 
			
		||||
                ghost: PhantomData,
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        (TcpReader { io: self.io }, TcpWriter { io: self.io })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn connect<T>(&mut self, remote_endpoint: T) -> Result<(), ConnectError>
 | 
			
		||||
    where
 | 
			
		||||
        T: Into<IpEndpoint>,
 | 
			
		||||
    {
 | 
			
		||||
        let local_port = Stack::with(|stack| stack.get_local_port());
 | 
			
		||||
        match with_socket(self.handle, |s, cx| {
 | 
			
		||||
            s.connect(cx, remote_endpoint, local_port)
 | 
			
		||||
        }) {
 | 
			
		||||
        // safety: not accessed reentrantly.
 | 
			
		||||
        let local_port = unsafe { &mut *self.io.stack.get() }.get_local_port();
 | 
			
		||||
 | 
			
		||||
        // safety: not accessed reentrantly.
 | 
			
		||||
        match unsafe {
 | 
			
		||||
            self.io
 | 
			
		||||
                .with_mut(|s, i| s.connect(i, remote_endpoint, local_port))
 | 
			
		||||
        } {
 | 
			
		||||
            Ok(()) => {}
 | 
			
		||||
            Err(smoltcp::Error::Illegal) => return Err(ConnectError::InvalidState),
 | 
			
		||||
            Err(smoltcp::Error::Unaddressable) => return Err(ConnectError::NoRoute),
 | 
			
		||||
            // smoltcp returns no errors other than the above.
 | 
			
		||||
            Err(_) => unreachable!(),
 | 
			
		||||
            Err(tcp::ConnectError::InvalidState) => return Err(ConnectError::InvalidState),
 | 
			
		||||
            Err(tcp::ConnectError::Unaddressable) => return Err(ConnectError::NoRoute),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        futures::future::poll_fn(|cx| {
 | 
			
		||||
            with_socket(self.handle, |s, _| match s.state() {
 | 
			
		||||
                TcpState::Closed | TcpState::TimeWait => {
 | 
			
		||||
        futures::future::poll_fn(|cx| unsafe {
 | 
			
		||||
            self.io.with_mut(|s, _| match s.state() {
 | 
			
		||||
                tcp::State::Closed | tcp::State::TimeWait => {
 | 
			
		||||
                    Poll::Ready(Err(ConnectError::ConnectionReset))
 | 
			
		||||
                }
 | 
			
		||||
                TcpState::Listen => unreachable!(),
 | 
			
		||||
                TcpState::SynSent | TcpState::SynReceived => {
 | 
			
		||||
                tcp::State::Listen => unreachable!(),
 | 
			
		||||
                tcp::State::SynSent | tcp::State::SynReceived => {
 | 
			
		||||
                    s.register_send_waker(cx.waker());
 | 
			
		||||
                    Poll::Pending
 | 
			
		||||
                }
 | 
			
		||||
@@ -125,19 +118,18 @@ impl<'a> TcpSocket<'a> {
 | 
			
		||||
 | 
			
		||||
    pub async fn accept<T>(&mut self, local_endpoint: T) -> Result<(), AcceptError>
 | 
			
		||||
    where
 | 
			
		||||
        T: Into<IpEndpoint>,
 | 
			
		||||
        T: Into<IpListenEndpoint>,
 | 
			
		||||
    {
 | 
			
		||||
        match with_socket(self.handle, |s, _| s.listen(local_endpoint)) {
 | 
			
		||||
        // safety: not accessed reentrantly.
 | 
			
		||||
        match unsafe { self.io.with_mut(|s, _| s.listen(local_endpoint)) } {
 | 
			
		||||
            Ok(()) => {}
 | 
			
		||||
            Err(smoltcp::Error::Illegal) => return Err(AcceptError::InvalidState),
 | 
			
		||||
            Err(smoltcp::Error::Unaddressable) => return Err(AcceptError::InvalidPort),
 | 
			
		||||
            // smoltcp returns no errors other than the above.
 | 
			
		||||
            Err(_) => unreachable!(),
 | 
			
		||||
            Err(tcp::ListenError::InvalidState) => return Err(AcceptError::InvalidState),
 | 
			
		||||
            Err(tcp::ListenError::Unaddressable) => return Err(AcceptError::InvalidPort),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        futures::future::poll_fn(|cx| {
 | 
			
		||||
            with_socket(self.handle, |s, _| match s.state() {
 | 
			
		||||
                TcpState::Listen | TcpState::SynSent | TcpState::SynReceived => {
 | 
			
		||||
        futures::future::poll_fn(|cx| unsafe {
 | 
			
		||||
            self.io.with_mut(|s, _| match s.state() {
 | 
			
		||||
                tcp::State::Listen | tcp::State::SynSent | tcp::State::SynReceived => {
 | 
			
		||||
                    s.register_send_waker(cx.waker());
 | 
			
		||||
                    Poll::Pending
 | 
			
		||||
                }
 | 
			
		||||
@@ -148,65 +140,122 @@ impl<'a> TcpSocket<'a> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_timeout(&mut self, duration: Option<Duration>) {
 | 
			
		||||
        with_socket(self.handle, |s, _| s.set_timeout(duration))
 | 
			
		||||
        unsafe { self.io.with_mut(|s, _| s.set_timeout(duration)) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_keep_alive(&mut self, interval: Option<Duration>) {
 | 
			
		||||
        with_socket(self.handle, |s, _| s.set_keep_alive(interval))
 | 
			
		||||
        unsafe { self.io.with_mut(|s, _| s.set_keep_alive(interval)) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) {
 | 
			
		||||
        with_socket(self.handle, |s, _| s.set_hop_limit(hop_limit))
 | 
			
		||||
        unsafe { self.io.with_mut(|s, _| s.set_hop_limit(hop_limit)) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn local_endpoint(&self) -> IpEndpoint {
 | 
			
		||||
        with_socket(self.handle, |s, _| s.local_endpoint())
 | 
			
		||||
    pub fn local_endpoint(&self) -> Option<IpEndpoint> {
 | 
			
		||||
        unsafe { self.io.with(|s, _| s.local_endpoint()) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn remote_endpoint(&self) -> IpEndpoint {
 | 
			
		||||
        with_socket(self.handle, |s, _| s.remote_endpoint())
 | 
			
		||||
    pub fn remote_endpoint(&self) -> Option<IpEndpoint> {
 | 
			
		||||
        unsafe { self.io.with(|s, _| s.remote_endpoint()) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn state(&self) -> TcpState {
 | 
			
		||||
        with_socket(self.handle, |s, _| s.state())
 | 
			
		||||
    pub fn state(&self) -> tcp::State {
 | 
			
		||||
        unsafe { self.io.with(|s, _| s.state()) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn close(&mut self) {
 | 
			
		||||
        with_socket(self.handle, |s, _| s.close())
 | 
			
		||||
        unsafe { self.io.with_mut(|s, _| s.close()) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn abort(&mut self) {
 | 
			
		||||
        with_socket(self.handle, |s, _| s.abort())
 | 
			
		||||
        unsafe { self.io.with_mut(|s, _| s.abort()) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn may_send(&self) -> bool {
 | 
			
		||||
        with_socket(self.handle, |s, _| s.may_send())
 | 
			
		||||
        unsafe { self.io.with(|s, _| s.may_send()) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn may_recv(&self) -> bool {
 | 
			
		||||
        with_socket(self.handle, |s, _| s.may_recv())
 | 
			
		||||
        unsafe { self.io.with(|s, _| s.may_recv()) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn with_socket<R>(
 | 
			
		||||
    handle: SocketHandle,
 | 
			
		||||
    f: impl FnOnce(&mut SyncTcpSocket, &mut SmolContext) -> R,
 | 
			
		||||
) -> R {
 | 
			
		||||
    Stack::with(|stack| {
 | 
			
		||||
        let res = {
 | 
			
		||||
            let (s, cx) = stack.iface.get_socket_and_context::<SyncTcpSocket>(handle);
 | 
			
		||||
            f(s, cx)
 | 
			
		||||
        };
 | 
			
		||||
        stack.wake();
 | 
			
		||||
        res
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Drop for TcpSocket<'a> {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        Stack::with(|stack| {
 | 
			
		||||
            stack.iface.remove_socket(self.handle);
 | 
			
		||||
        // safety: not accessed reentrantly.
 | 
			
		||||
        let s = unsafe { &mut *self.io.stack.get() };
 | 
			
		||||
        s.sockets.remove(self.io.handle);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// =======================
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone)]
 | 
			
		||||
pub struct TcpIo<'a> {
 | 
			
		||||
    stack: &'a UnsafeCell<SocketStack>,
 | 
			
		||||
    handle: SocketHandle,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'d> TcpIo<'d> {
 | 
			
		||||
    /// SAFETY: must not call reentrantly.
 | 
			
		||||
    unsafe fn with<R>(&self, f: impl FnOnce(&tcp::Socket, &Interface) -> R) -> R {
 | 
			
		||||
        let s = &*self.stack.get();
 | 
			
		||||
        let socket = s.sockets.get::<tcp::Socket>(self.handle);
 | 
			
		||||
        f(socket, &s.iface)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// SAFETY: must not call reentrantly.
 | 
			
		||||
    unsafe fn with_mut<R>(&mut self, f: impl FnOnce(&mut tcp::Socket, &mut Interface) -> R) -> R {
 | 
			
		||||
        let s = &mut *self.stack.get();
 | 
			
		||||
        let socket = s.sockets.get_mut::<tcp::Socket>(self.handle);
 | 
			
		||||
        let res = f(socket, &mut s.iface);
 | 
			
		||||
        s.waker.wake();
 | 
			
		||||
        res
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
 | 
			
		||||
        poll_fn(move |cx| unsafe {
 | 
			
		||||
            // CAUTION: smoltcp semantics around EOF are different to what you'd expect
 | 
			
		||||
            // from posix-like IO, so we have to tweak things here.
 | 
			
		||||
            self.with_mut(|s, _| match s.recv_slice(buf) {
 | 
			
		||||
                // No data ready
 | 
			
		||||
                Ok(0) => {
 | 
			
		||||
                    s.register_recv_waker(cx.waker());
 | 
			
		||||
                    Poll::Pending
 | 
			
		||||
                }
 | 
			
		||||
                // Data ready!
 | 
			
		||||
                Ok(n) => Poll::Ready(Ok(n)),
 | 
			
		||||
                // EOF
 | 
			
		||||
                Err(tcp::RecvError::Finished) => Poll::Ready(Ok(0)),
 | 
			
		||||
                // Connection reset. TODO: this can also be timeouts etc, investigate.
 | 
			
		||||
                Err(tcp::RecvError::InvalidState) => Poll::Ready(Err(Error::ConnectionReset)),
 | 
			
		||||
            })
 | 
			
		||||
        })
 | 
			
		||||
        .await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
 | 
			
		||||
        poll_fn(move |cx| unsafe {
 | 
			
		||||
            self.with_mut(|s, _| match s.send_slice(buf) {
 | 
			
		||||
                // Not ready to send (no space in the tx buffer)
 | 
			
		||||
                Ok(0) => {
 | 
			
		||||
                    s.register_send_waker(cx.waker());
 | 
			
		||||
                    Poll::Pending
 | 
			
		||||
                }
 | 
			
		||||
                // Some data sent
 | 
			
		||||
                Ok(n) => Poll::Ready(Ok(n)),
 | 
			
		||||
                // Connection reset. TODO: this can also be timeouts etc, investigate.
 | 
			
		||||
                Err(tcp::SendError::InvalidState) => Poll::Ready(Err(Error::ConnectionReset)),
 | 
			
		||||
            })
 | 
			
		||||
        })
 | 
			
		||||
        .await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async fn flush(&mut self) -> Result<(), Error> {
 | 
			
		||||
        poll_fn(move |_| {
 | 
			
		||||
            Poll::Ready(Ok(())) // TODO: Is there a better implementation for this?
 | 
			
		||||
        })
 | 
			
		||||
        .await
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -226,25 +275,7 @@ impl<'d> embedded_io::asynch::Read for TcpSocket<'d> {
 | 
			
		||||
        Self: 'a;
 | 
			
		||||
 | 
			
		||||
    fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
 | 
			
		||||
        poll_fn(move |cx| {
 | 
			
		||||
            // CAUTION: smoltcp semantics around EOF are different to what you'd expect
 | 
			
		||||
            // from posix-like IO, so we have to tweak things here.
 | 
			
		||||
            with_socket(self.handle, |s, _| match s.recv_slice(buf) {
 | 
			
		||||
                // No data ready
 | 
			
		||||
                Ok(0) => {
 | 
			
		||||
                    s.register_recv_waker(cx.waker());
 | 
			
		||||
                    Poll::Pending
 | 
			
		||||
                }
 | 
			
		||||
                // Data ready!
 | 
			
		||||
                Ok(n) => Poll::Ready(Ok(n)),
 | 
			
		||||
                // EOF
 | 
			
		||||
                Err(smoltcp::Error::Finished) => Poll::Ready(Ok(0)),
 | 
			
		||||
                // Connection reset. TODO: this can also be timeouts etc, investigate.
 | 
			
		||||
                Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)),
 | 
			
		||||
                // smoltcp returns no errors other than the above.
 | 
			
		||||
                Err(_) => unreachable!(),
 | 
			
		||||
            })
 | 
			
		||||
        })
 | 
			
		||||
        self.io.read(buf)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -254,21 +285,7 @@ impl<'d> embedded_io::asynch::Write for TcpSocket<'d> {
 | 
			
		||||
        Self: 'a;
 | 
			
		||||
 | 
			
		||||
    fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
 | 
			
		||||
        poll_fn(move |cx| {
 | 
			
		||||
            with_socket(self.handle, |s, _| match s.send_slice(buf) {
 | 
			
		||||
                // Not ready to send (no space in the tx buffer)
 | 
			
		||||
                Ok(0) => {
 | 
			
		||||
                    s.register_send_waker(cx.waker());
 | 
			
		||||
                    Poll::Pending
 | 
			
		||||
                }
 | 
			
		||||
                // Some data sent
 | 
			
		||||
                Ok(n) => Poll::Ready(Ok(n)),
 | 
			
		||||
                // Connection reset. TODO: this can also be timeouts etc, investigate.
 | 
			
		||||
                Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)),
 | 
			
		||||
                // smoltcp returns no errors other than the above.
 | 
			
		||||
                Err(_) => unreachable!(),
 | 
			
		||||
            })
 | 
			
		||||
        })
 | 
			
		||||
        self.io.write(buf)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
 | 
			
		||||
@@ -276,9 +293,7 @@ impl<'d> embedded_io::asynch::Write for TcpSocket<'d> {
 | 
			
		||||
        Self: 'a;
 | 
			
		||||
 | 
			
		||||
    fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
 | 
			
		||||
        poll_fn(move |_| {
 | 
			
		||||
            Poll::Ready(Ok(())) // TODO: Is there a better implementation for this?
 | 
			
		||||
        })
 | 
			
		||||
        self.io.flush()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -292,25 +307,7 @@ impl<'d> embedded_io::asynch::Read for TcpReader<'d> {
 | 
			
		||||
        Self: 'a;
 | 
			
		||||
 | 
			
		||||
    fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
 | 
			
		||||
        poll_fn(move |cx| {
 | 
			
		||||
            // CAUTION: smoltcp semantics around EOF are different to what you'd expect
 | 
			
		||||
            // from posix-like IO, so we have to tweak things here.
 | 
			
		||||
            with_socket(self.handle, |s, _| match s.recv_slice(buf) {
 | 
			
		||||
                // No data ready
 | 
			
		||||
                Ok(0) => {
 | 
			
		||||
                    s.register_recv_waker(cx.waker());
 | 
			
		||||
                    Poll::Pending
 | 
			
		||||
                }
 | 
			
		||||
                // Data ready!
 | 
			
		||||
                Ok(n) => Poll::Ready(Ok(n)),
 | 
			
		||||
                // EOF
 | 
			
		||||
                Err(smoltcp::Error::Finished) => Poll::Ready(Ok(0)),
 | 
			
		||||
                // Connection reset. TODO: this can also be timeouts etc, investigate.
 | 
			
		||||
                Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)),
 | 
			
		||||
                // smoltcp returns no errors other than the above.
 | 
			
		||||
                Err(_) => unreachable!(),
 | 
			
		||||
            })
 | 
			
		||||
        })
 | 
			
		||||
        self.io.read(buf)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -324,21 +321,7 @@ impl<'d> embedded_io::asynch::Write for TcpWriter<'d> {
 | 
			
		||||
        Self: 'a;
 | 
			
		||||
 | 
			
		||||
    fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
 | 
			
		||||
        poll_fn(move |cx| {
 | 
			
		||||
            with_socket(self.handle, |s, _| match s.send_slice(buf) {
 | 
			
		||||
                // Not ready to send (no space in the tx buffer)
 | 
			
		||||
                Ok(0) => {
 | 
			
		||||
                    s.register_send_waker(cx.waker());
 | 
			
		||||
                    Poll::Pending
 | 
			
		||||
                }
 | 
			
		||||
                // Some data sent
 | 
			
		||||
                Ok(n) => Poll::Ready(Ok(n)),
 | 
			
		||||
                // Connection reset. TODO: this can also be timeouts etc, investigate.
 | 
			
		||||
                Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)),
 | 
			
		||||
                // smoltcp returns no errors other than the above.
 | 
			
		||||
                Err(_) => unreachable!(),
 | 
			
		||||
            })
 | 
			
		||||
        })
 | 
			
		||||
        self.io.write(buf)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
 | 
			
		||||
@@ -346,8 +329,6 @@ impl<'d> embedded_io::asynch::Write for TcpWriter<'d> {
 | 
			
		||||
        Self: 'a;
 | 
			
		||||
 | 
			
		||||
    fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
 | 
			
		||||
        poll_fn(move |_| {
 | 
			
		||||
            Poll::Ready(Ok(())) // TODO: Is there a better implementation for this?
 | 
			
		||||
        })
 | 
			
		||||
        self.io.flush()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -319,7 +319,7 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Device
 | 
			
		||||
        WAKER.register(waker);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn capabilities(&mut self) -> DeviceCapabilities {
 | 
			
		||||
    fn capabilities(&self) -> DeviceCapabilities {
 | 
			
		||||
        let mut caps = DeviceCapabilities::default();
 | 
			
		||||
        caps.max_transmission_unit = MTU;
 | 
			
		||||
        caps.max_burst_size = Some(TX.min(RX));
 | 
			
		||||
 
 | 
			
		||||
@@ -253,7 +253,7 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Device
 | 
			
		||||
        WAKER.register(waker);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn capabilities(&mut self) -> DeviceCapabilities {
 | 
			
		||||
    fn capabilities(&self) -> DeviceCapabilities {
 | 
			
		||||
        let mut caps = DeviceCapabilities::default();
 | 
			
		||||
        caps.max_transmission_unit = MTU;
 | 
			
		||||
        caps.max_burst_size = Some(TX.min(RX));
 | 
			
		||||
 
 | 
			
		||||
@@ -12,8 +12,9 @@ use embassy::channel::Channel;
 | 
			
		||||
use embassy::executor::Spawner;
 | 
			
		||||
use embassy::util::Forever;
 | 
			
		||||
use embassy_net::tcp::TcpSocket;
 | 
			
		||||
use embassy_net::{PacketBox, PacketBoxExt, PacketBuf};
 | 
			
		||||
use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources};
 | 
			
		||||
use embassy_nrf::pac;
 | 
			
		||||
use embassy_nrf::rng::Rng;
 | 
			
		||||
use embassy_nrf::usb::Driver;
 | 
			
		||||
use embassy_nrf::Peripherals;
 | 
			
		||||
use embassy_nrf::{interrupt, peripherals};
 | 
			
		||||
@@ -27,6 +28,14 @@ use panic_probe as _;
 | 
			
		||||
 | 
			
		||||
type MyDriver = Driver<'static, peripherals::USBD>;
 | 
			
		||||
 | 
			
		||||
macro_rules! forever {
 | 
			
		||||
    ($val:expr) => {{
 | 
			
		||||
        type T = impl Sized;
 | 
			
		||||
        static FOREVER: Forever<T> = Forever::new();
 | 
			
		||||
        FOREVER.put_with(move || $val)
 | 
			
		||||
    }};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[embassy::task]
 | 
			
		||||
async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! {
 | 
			
		||||
    device.run().await
 | 
			
		||||
@@ -72,8 +81,8 @@ async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[embassy::task]
 | 
			
		||||
async fn net_task() -> ! {
 | 
			
		||||
    embassy_net::run().await
 | 
			
		||||
async fn net_task(stack: &'static Stack<Device>) -> ! {
 | 
			
		||||
    stack.run().await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[embassy::main]
 | 
			
		||||
@@ -114,8 +123,7 @@ async fn main(spawner: Spawner, p: Peripherals) {
 | 
			
		||||
        control_buf: [u8; 128],
 | 
			
		||||
        serial_state: State<'static>,
 | 
			
		||||
    }
 | 
			
		||||
    static RESOURCES: Forever<Resources> = Forever::new();
 | 
			
		||||
    let res = RESOURCES.put(Resources {
 | 
			
		||||
    let res: &mut Resources = forever!(Resources {
 | 
			
		||||
        device_descriptor: [0; 256],
 | 
			
		||||
        config_descriptor: [0; 256],
 | 
			
		||||
        bos_descriptor: [0; 256],
 | 
			
		||||
@@ -158,28 +166,31 @@ async fn main(spawner: Spawner, p: Peripherals) {
 | 
			
		||||
    unwrap!(spawner.spawn(usb_ncm_rx_task(rx)));
 | 
			
		||||
    unwrap!(spawner.spawn(usb_ncm_tx_task(tx)));
 | 
			
		||||
 | 
			
		||||
    // Init embassy-net
 | 
			
		||||
    struct NetResources {
 | 
			
		||||
        resources: embassy_net::StackResources<1, 2, 8>,
 | 
			
		||||
        configurator: embassy_net::DhcpConfigurator,
 | 
			
		||||
        //configurator: StaticConfigurator,
 | 
			
		||||
        device: Device,
 | 
			
		||||
    }
 | 
			
		||||
    static NET_RESOURCES: Forever<NetResources> = Forever::new();
 | 
			
		||||
    let res = NET_RESOURCES.put(NetResources {
 | 
			
		||||
        resources: embassy_net::StackResources::new(),
 | 
			
		||||
        configurator: embassy_net::DhcpConfigurator::new(),
 | 
			
		||||
        //configurator: embassy_net::StaticConfigurator::new(embassy_net::Config {
 | 
			
		||||
        //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 1), 24),
 | 
			
		||||
        //    dns_servers: Default::default(),
 | 
			
		||||
        //    gateway: None,
 | 
			
		||||
        //}),
 | 
			
		||||
        device: Device {
 | 
			
		||||
    let config = embassy_net::ConfigStrategy::Dhcp;
 | 
			
		||||
    //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config {
 | 
			
		||||
    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
 | 
			
		||||
    //    dns_servers: Vec::new(),
 | 
			
		||||
    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
 | 
			
		||||
    //});
 | 
			
		||||
 | 
			
		||||
    // Generate random seed
 | 
			
		||||
    let mut rng = Rng::new(p.RNG, interrupt::take!(RNG));
 | 
			
		||||
    let mut seed = [0; 8];
 | 
			
		||||
    rng.blocking_fill_bytes(&mut seed);
 | 
			
		||||
    let seed = u64::from_le_bytes(seed);
 | 
			
		||||
 | 
			
		||||
    // Init network stack
 | 
			
		||||
    let device = Device {
 | 
			
		||||
        mac_addr: our_mac_addr,
 | 
			
		||||
        },
 | 
			
		||||
    });
 | 
			
		||||
    embassy_net::init(&mut res.device, &mut res.configurator, &mut res.resources);
 | 
			
		||||
    unwrap!(spawner.spawn(net_task()));
 | 
			
		||||
    };
 | 
			
		||||
    let stack = &*forever!(Stack::new(
 | 
			
		||||
        device,
 | 
			
		||||
        config,
 | 
			
		||||
        forever!(StackResources::<1, 2, 8>::new()),
 | 
			
		||||
        seed
 | 
			
		||||
    ));
 | 
			
		||||
 | 
			
		||||
    unwrap!(spawner.spawn(net_task(stack)));
 | 
			
		||||
 | 
			
		||||
    // And now we can use it!
 | 
			
		||||
 | 
			
		||||
@@ -188,7 +199,7 @@ async fn main(spawner: Spawner, p: Peripherals) {
 | 
			
		||||
    let mut buf = [0; 4096];
 | 
			
		||||
 | 
			
		||||
    loop {
 | 
			
		||||
        let mut socket = TcpSocket::new(&mut rx_buffer, &mut tx_buffer);
 | 
			
		||||
        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
 | 
			
		||||
        socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
 | 
			
		||||
 | 
			
		||||
        info!("Listening on TCP:1234...");
 | 
			
		||||
@@ -246,7 +257,7 @@ impl embassy_net::Device for Device {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn capabilities(&mut self) -> embassy_net::DeviceCapabilities {
 | 
			
		||||
    fn capabilities(&self) -> embassy_net::DeviceCapabilities {
 | 
			
		||||
        let mut caps = embassy_net::DeviceCapabilities::default();
 | 
			
		||||
        caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header
 | 
			
		||||
        caps.medium = embassy_net::Medium::Ethernet;
 | 
			
		||||
@@ -271,9 +282,3 @@ impl embassy_net::Device for Device {
 | 
			
		||||
        self.mac_addr
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[no_mangle]
 | 
			
		||||
fn _embassy_rand(buf: &mut [u8]) {
 | 
			
		||||
    // TODO
 | 
			
		||||
    buf.fill(0x42)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,23 +4,24 @@ use clap::Parser;
 | 
			
		||||
use embassy::executor::{Executor, Spawner};
 | 
			
		||||
use embassy::util::Forever;
 | 
			
		||||
use embassy_net::tcp::TcpSocket;
 | 
			
		||||
use embassy_net::{
 | 
			
		||||
    Config, Configurator, DhcpConfigurator, Ipv4Address, Ipv4Cidr, StackResources,
 | 
			
		||||
    StaticConfigurator,
 | 
			
		||||
};
 | 
			
		||||
use embassy_net::{ConfigStrategy, Ipv4Address, Ipv4Cidr, Stack, StackResources};
 | 
			
		||||
use embedded_io::asynch::Write;
 | 
			
		||||
use heapless::Vec;
 | 
			
		||||
use log::*;
 | 
			
		||||
use rand_core::{OsRng, RngCore};
 | 
			
		||||
 | 
			
		||||
#[path = "../tuntap.rs"]
 | 
			
		||||
mod tuntap;
 | 
			
		||||
 | 
			
		||||
use crate::tuntap::TunTapDevice;
 | 
			
		||||
 | 
			
		||||
static DEVICE: Forever<TunTapDevice> = Forever::new();
 | 
			
		||||
static CONFIG_STATIC: Forever<StaticConfigurator> = Forever::new();
 | 
			
		||||
static CONFIG_DYNAMIC: Forever<DhcpConfigurator> = Forever::new();
 | 
			
		||||
static NET_RESOURCES: Forever<StackResources<1, 2, 8>> = Forever::new();
 | 
			
		||||
macro_rules! forever {
 | 
			
		||||
    ($val:expr) => {{
 | 
			
		||||
        type T = impl Sized;
 | 
			
		||||
        static FOREVER: Forever<T> = Forever::new();
 | 
			
		||||
        FOREVER.put_with(move || $val)
 | 
			
		||||
    }};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Parser)]
 | 
			
		||||
#[clap(version = "1.0")]
 | 
			
		||||
@@ -34,8 +35,8 @@ struct Opts {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[embassy::task]
 | 
			
		||||
async fn net_task() {
 | 
			
		||||
    embassy_net::run().await
 | 
			
		||||
async fn net_task(stack: &'static Stack<TunTapDevice>) -> ! {
 | 
			
		||||
    stack.run().await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[embassy::task]
 | 
			
		||||
@@ -46,28 +47,36 @@ async fn main_task(spawner: Spawner) {
 | 
			
		||||
    let device = TunTapDevice::new(&opts.tap).unwrap();
 | 
			
		||||
 | 
			
		||||
    // Choose between dhcp or static ip
 | 
			
		||||
    let config: &'static mut dyn Configurator = if opts.static_ip {
 | 
			
		||||
        CONFIG_STATIC.put(StaticConfigurator::new(Config {
 | 
			
		||||
    let config = if opts.static_ip {
 | 
			
		||||
        ConfigStrategy::Static(embassy_net::Config {
 | 
			
		||||
            address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24),
 | 
			
		||||
            dns_servers: Vec::new(),
 | 
			
		||||
            gateway: Some(Ipv4Address::new(192, 168, 69, 1)),
 | 
			
		||||
        }))
 | 
			
		||||
        })
 | 
			
		||||
    } else {
 | 
			
		||||
        CONFIG_DYNAMIC.put(DhcpConfigurator::new())
 | 
			
		||||
        ConfigStrategy::Dhcp
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let net_resources = StackResources::new();
 | 
			
		||||
    // Generate random seed
 | 
			
		||||
    let mut seed = [0; 8];
 | 
			
		||||
    OsRng.fill_bytes(&mut seed);
 | 
			
		||||
    let seed = u64::from_le_bytes(seed);
 | 
			
		||||
 | 
			
		||||
    // Init network stack
 | 
			
		||||
    embassy_net::init(DEVICE.put(device), config, NET_RESOURCES.put(net_resources));
 | 
			
		||||
    let stack = &*forever!(Stack::new(
 | 
			
		||||
        device,
 | 
			
		||||
        config,
 | 
			
		||||
        forever!(StackResources::<1, 2, 8>::new()),
 | 
			
		||||
        seed
 | 
			
		||||
    ));
 | 
			
		||||
 | 
			
		||||
    // Launch network task
 | 
			
		||||
    spawner.spawn(net_task()).unwrap();
 | 
			
		||||
    spawner.spawn(net_task(stack)).unwrap();
 | 
			
		||||
 | 
			
		||||
    // Then we can use it!
 | 
			
		||||
    let mut rx_buffer = [0; 4096];
 | 
			
		||||
    let mut tx_buffer = [0; 4096];
 | 
			
		||||
    let mut socket = TcpSocket::new(&mut rx_buffer, &mut tx_buffer);
 | 
			
		||||
    let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
 | 
			
		||||
 | 
			
		||||
    socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
 | 
			
		||||
 | 
			
		||||
@@ -88,12 +97,6 @@ async fn main_task(spawner: Spawner) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[no_mangle]
 | 
			
		||||
fn _embassy_rand(buf: &mut [u8]) {
 | 
			
		||||
    use rand_core::{OsRng, RngCore};
 | 
			
		||||
    OsRng.fill_bytes(buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static EXECUTOR: Forever<Executor> = Forever::new();
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
 
 | 
			
		||||
@@ -209,7 +209,7 @@ impl Device for TunTapDevice {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn capabilities(&mut self) -> DeviceCapabilities {
 | 
			
		||||
    fn capabilities(&self) -> DeviceCapabilities {
 | 
			
		||||
        let mut caps = DeviceCapabilities::default();
 | 
			
		||||
        caps.max_transmission_unit = self.device.get_ref().mtu;
 | 
			
		||||
        caps
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ resolver = "2"
 | 
			
		||||
[dependencies]
 | 
			
		||||
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
 | 
			
		||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"]  }
 | 
			
		||||
embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "medium-ethernet", "pool-16"] }
 | 
			
		||||
embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] }
 | 
			
		||||
embedded-io = { version = "0.3.0", features = ["async"] }
 | 
			
		||||
 | 
			
		||||
defmt = "0.3"
 | 
			
		||||
@@ -24,8 +24,3 @@ nb = "1.0.0"
 | 
			
		||||
rand_core = "0.6.3"
 | 
			
		||||
critical-section = "0.2.3"
 | 
			
		||||
embedded-storage = "0.3.0"
 | 
			
		||||
 | 
			
		||||
[dependencies.smoltcp]
 | 
			
		||||
version = "0.8.0"
 | 
			
		||||
default-features = false
 | 
			
		||||
features = ["defmt"]
 | 
			
		||||
 
 | 
			
		||||
@@ -2,55 +2,114 @@
 | 
			
		||||
#![no_main]
 | 
			
		||||
#![feature(type_alias_impl_trait)]
 | 
			
		||||
 | 
			
		||||
use cortex_m_rt::entry;
 | 
			
		||||
use defmt::*;
 | 
			
		||||
use embassy::executor::{Executor, Spawner};
 | 
			
		||||
use embassy::executor::Spawner;
 | 
			
		||||
use embassy::time::{Duration, Timer};
 | 
			
		||||
use embassy::util::Forever;
 | 
			
		||||
use embassy_net::tcp::TcpSocket;
 | 
			
		||||
use embassy_net::{Config as NetConfig, Ipv4Address, Ipv4Cidr, StackResources, StaticConfigurator};
 | 
			
		||||
use embassy_net::{Ipv4Address, Stack, StackResources};
 | 
			
		||||
use embassy_stm32::eth::generic_smi::GenericSMI;
 | 
			
		||||
use embassy_stm32::eth::{Ethernet, State};
 | 
			
		||||
use embassy_stm32::interrupt;
 | 
			
		||||
use embassy_stm32::peripherals::ETH;
 | 
			
		||||
use embassy_stm32::peripherals::RNG;
 | 
			
		||||
use embassy_stm32::rng::Rng;
 | 
			
		||||
use embassy_stm32::time::U32Ext;
 | 
			
		||||
use embassy_stm32::Config;
 | 
			
		||||
use embassy_stm32::{interrupt, Peripherals};
 | 
			
		||||
use embedded_io::asynch::Write;
 | 
			
		||||
use heapless::Vec;
 | 
			
		||||
 | 
			
		||||
use defmt_rtt as _; // global logger
 | 
			
		||||
use panic_probe as _;
 | 
			
		||||
use rand_core::RngCore;
 | 
			
		||||
 | 
			
		||||
macro_rules! forever {
 | 
			
		||||
    ($val:expr) => {{
 | 
			
		||||
        type T = impl Sized;
 | 
			
		||||
        static FOREVER: Forever<T> = Forever::new();
 | 
			
		||||
        FOREVER.put_with(move || $val)
 | 
			
		||||
    }};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>;
 | 
			
		||||
 | 
			
		||||
#[embassy::task]
 | 
			
		||||
async fn main_task(
 | 
			
		||||
    device: &'static mut Ethernet<'static, ETH, GenericSMI, 4, 4>,
 | 
			
		||||
    config: &'static mut StaticConfigurator,
 | 
			
		||||
    spawner: Spawner,
 | 
			
		||||
) {
 | 
			
		||||
    let net_resources = NET_RESOURCES.put(StackResources::new());
 | 
			
		||||
async fn net_task(stack: &'static Stack<Device>) -> ! {
 | 
			
		||||
    stack.run().await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn config() -> Config {
 | 
			
		||||
    let mut config = Config::default();
 | 
			
		||||
    config.rcc.sys_ck = Some(200.mhz().into());
 | 
			
		||||
    config
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[embassy::main(config = "config()")]
 | 
			
		||||
async fn main(spawner: Spawner, p: Peripherals) -> ! {
 | 
			
		||||
    info!("Hello World!");
 | 
			
		||||
 | 
			
		||||
    // Generate random seed.
 | 
			
		||||
    let mut rng = Rng::new(p.RNG);
 | 
			
		||||
    let mut seed = [0; 8];
 | 
			
		||||
    rng.fill_bytes(&mut seed);
 | 
			
		||||
    let seed = u64::from_le_bytes(seed);
 | 
			
		||||
 | 
			
		||||
    let eth_int = interrupt::take!(ETH);
 | 
			
		||||
    let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
 | 
			
		||||
 | 
			
		||||
    let device = unsafe {
 | 
			
		||||
        Ethernet::new(
 | 
			
		||||
            forever!(State::new()),
 | 
			
		||||
            p.ETH,
 | 
			
		||||
            eth_int,
 | 
			
		||||
            p.PA1,
 | 
			
		||||
            p.PA2,
 | 
			
		||||
            p.PC1,
 | 
			
		||||
            p.PA7,
 | 
			
		||||
            p.PC4,
 | 
			
		||||
            p.PC5,
 | 
			
		||||
            p.PG13,
 | 
			
		||||
            p.PB13,
 | 
			
		||||
            p.PG11,
 | 
			
		||||
            GenericSMI,
 | 
			
		||||
            mac_addr,
 | 
			
		||||
            0,
 | 
			
		||||
        )
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let config = embassy_net::ConfigStrategy::Dhcp;
 | 
			
		||||
    //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config {
 | 
			
		||||
    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
 | 
			
		||||
    //    dns_servers: Vec::new(),
 | 
			
		||||
    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
 | 
			
		||||
    //});
 | 
			
		||||
 | 
			
		||||
    // Init network stack
 | 
			
		||||
    embassy_net::init(device, config, net_resources);
 | 
			
		||||
    let stack = &*forever!(Stack::new(
 | 
			
		||||
        device,
 | 
			
		||||
        config,
 | 
			
		||||
        forever!(StackResources::<1, 2, 8>::new()),
 | 
			
		||||
        seed
 | 
			
		||||
    ));
 | 
			
		||||
 | 
			
		||||
    // Launch network task
 | 
			
		||||
    unwrap!(spawner.spawn(net_task()));
 | 
			
		||||
    unwrap!(spawner.spawn(net_task(&stack)));
 | 
			
		||||
 | 
			
		||||
    info!("Network task initialized");
 | 
			
		||||
 | 
			
		||||
    // Then we can use it!
 | 
			
		||||
    let mut rx_buffer = [0; 1024];
 | 
			
		||||
    let mut tx_buffer = [0; 1024];
 | 
			
		||||
    let mut socket = TcpSocket::new(&mut rx_buffer, &mut tx_buffer);
 | 
			
		||||
 | 
			
		||||
    loop {
 | 
			
		||||
        let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer);
 | 
			
		||||
 | 
			
		||||
        socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
 | 
			
		||||
 | 
			
		||||
    let remote_endpoint = (Ipv4Address::new(192, 168, 0, 10), 8000);
 | 
			
		||||
        let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000);
 | 
			
		||||
        info!("connecting...");
 | 
			
		||||
        let r = socket.connect(remote_endpoint).await;
 | 
			
		||||
        if let Err(e) = r {
 | 
			
		||||
            info!("connect error: {:?}", e);
 | 
			
		||||
        return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        info!("connected!");
 | 
			
		||||
        loop {
 | 
			
		||||
@@ -61,71 +120,5 @@ async fn main_task(
 | 
			
		||||
            }
 | 
			
		||||
            Timer::after(Duration::from_secs(1)).await;
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[embassy::task]
 | 
			
		||||
async fn net_task() {
 | 
			
		||||
    embassy_net::run().await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[no_mangle]
 | 
			
		||||
fn _embassy_rand(buf: &mut [u8]) {
 | 
			
		||||
    use rand_core::RngCore;
 | 
			
		||||
 | 
			
		||||
    critical_section::with(|_| unsafe {
 | 
			
		||||
        unwrap!(RNG_INST.as_mut()).fill_bytes(buf);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static mut RNG_INST: Option<Rng<RNG>> = None;
 | 
			
		||||
 | 
			
		||||
static EXECUTOR: Forever<Executor> = Forever::new();
 | 
			
		||||
static STATE: Forever<State<'static, ETH, 4, 4>> = Forever::new();
 | 
			
		||||
static ETH: Forever<Ethernet<'static, ETH, GenericSMI, 4, 4>> = Forever::new();
 | 
			
		||||
static CONFIG: Forever<StaticConfigurator> = Forever::new();
 | 
			
		||||
static NET_RESOURCES: Forever<StackResources<1, 2, 8>> = Forever::new();
 | 
			
		||||
 | 
			
		||||
fn config() -> Config {
 | 
			
		||||
    let mut config = Config::default();
 | 
			
		||||
    config.rcc.sys_ck = Some(200.mhz().into());
 | 
			
		||||
    config
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[entry]
 | 
			
		||||
fn main() -> ! {
 | 
			
		||||
    info!("Hello World!");
 | 
			
		||||
 | 
			
		||||
    info!("Setup RCC...");
 | 
			
		||||
 | 
			
		||||
    let p = embassy_stm32::init(config());
 | 
			
		||||
 | 
			
		||||
    let rng = Rng::new(p.RNG);
 | 
			
		||||
    unsafe {
 | 
			
		||||
        RNG_INST.replace(rng);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let eth_int = interrupt::take!(ETH);
 | 
			
		||||
    let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
 | 
			
		||||
    let state = STATE.put(State::new());
 | 
			
		||||
 | 
			
		||||
    let eth = unsafe {
 | 
			
		||||
        ETH.put(Ethernet::new(
 | 
			
		||||
            state, p.ETH, eth_int, p.PA1, p.PA2, p.PC1, p.PA7, p.PC4, p.PC5, p.PG13, p.PB13,
 | 
			
		||||
            p.PG11, GenericSMI, mac_addr, 0,
 | 
			
		||||
        ))
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let config = StaticConfigurator::new(NetConfig {
 | 
			
		||||
        address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 0, 61), 24),
 | 
			
		||||
        dns_servers: Vec::new(),
 | 
			
		||||
        gateway: Some(Ipv4Address::new(192, 168, 0, 1)),
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let config = CONFIG.put(config);
 | 
			
		||||
 | 
			
		||||
    let executor = EXECUTOR.put(Executor::new());
 | 
			
		||||
 | 
			
		||||
    executor.run(move |spawner| {
 | 
			
		||||
        unwrap!(spawner.spawn(main_task(eth, config, spawner)));
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,7 @@ resolver = "2"
 | 
			
		||||
[dependencies]
 | 
			
		||||
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] }
 | 
			
		||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] }
 | 
			
		||||
embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "medium-ethernet", "pool-16"] }
 | 
			
		||||
embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] }
 | 
			
		||||
embedded-io = { version = "0.3.0", features = ["async"] }
 | 
			
		||||
 | 
			
		||||
defmt = "0.3"
 | 
			
		||||
@@ -28,11 +28,6 @@ micromath = "2.0.0"
 | 
			
		||||
stm32-fmc = "0.2.4"
 | 
			
		||||
embedded-storage = "0.3.0"
 | 
			
		||||
 | 
			
		||||
[dependencies.smoltcp]
 | 
			
		||||
version = "0.8.0"
 | 
			
		||||
default-features = false
 | 
			
		||||
features = ["defmt"]
 | 
			
		||||
 | 
			
		||||
# cargo build/run
 | 
			
		||||
[profile.dev]
 | 
			
		||||
codegen-units = 1
 | 
			
		||||
 
 | 
			
		||||
@@ -2,55 +2,116 @@
 | 
			
		||||
#![no_main]
 | 
			
		||||
#![feature(type_alias_impl_trait)]
 | 
			
		||||
 | 
			
		||||
use defmt_rtt as _; // global logger
 | 
			
		||||
use panic_probe as _;
 | 
			
		||||
 | 
			
		||||
use cortex_m_rt::entry;
 | 
			
		||||
use defmt::*;
 | 
			
		||||
use embassy::executor::{Executor, Spawner};
 | 
			
		||||
use embassy::executor::Spawner;
 | 
			
		||||
use embassy::time::{Duration, Timer};
 | 
			
		||||
use embassy::util::Forever;
 | 
			
		||||
use embassy_net::tcp::TcpSocket;
 | 
			
		||||
use embassy_net::{Config as NetConfig, Ipv4Address, Ipv4Cidr, StackResources, StaticConfigurator};
 | 
			
		||||
use embassy_net::{Ipv4Address, Stack, StackResources};
 | 
			
		||||
use embassy_stm32::eth::generic_smi::GenericSMI;
 | 
			
		||||
use embassy_stm32::eth::{Ethernet, State};
 | 
			
		||||
use embassy_stm32::interrupt;
 | 
			
		||||
use embassy_stm32::peripherals::ETH;
 | 
			
		||||
use embassy_stm32::peripherals::RNG;
 | 
			
		||||
use embassy_stm32::rng::Rng;
 | 
			
		||||
use embassy_stm32::time::U32Ext;
 | 
			
		||||
use embassy_stm32::Config;
 | 
			
		||||
use embassy_stm32::{interrupt, Peripherals};
 | 
			
		||||
use embedded_io::asynch::Write;
 | 
			
		||||
use heapless::Vec;
 | 
			
		||||
 | 
			
		||||
use defmt_rtt as _; // global logger
 | 
			
		||||
use panic_probe as _;
 | 
			
		||||
use rand_core::RngCore;
 | 
			
		||||
 | 
			
		||||
macro_rules! forever {
 | 
			
		||||
    ($val:expr) => {{
 | 
			
		||||
        type T = impl Sized;
 | 
			
		||||
        static FOREVER: Forever<T> = Forever::new();
 | 
			
		||||
        FOREVER.put_with(move || $val)
 | 
			
		||||
    }};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>;
 | 
			
		||||
 | 
			
		||||
#[embassy::task]
 | 
			
		||||
async fn main_task(
 | 
			
		||||
    device: &'static mut Ethernet<'static, ETH, GenericSMI, 4, 4>,
 | 
			
		||||
    config: &'static mut StaticConfigurator,
 | 
			
		||||
    spawner: Spawner,
 | 
			
		||||
) {
 | 
			
		||||
    let net_resources = NET_RESOURCES.put(StackResources::new());
 | 
			
		||||
async fn net_task(stack: &'static Stack<Device>) -> ! {
 | 
			
		||||
    stack.run().await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn config() -> Config {
 | 
			
		||||
    let mut config = Config::default();
 | 
			
		||||
    config.rcc.sys_ck = Some(400.mhz().into());
 | 
			
		||||
    config.rcc.hclk = Some(200.mhz().into());
 | 
			
		||||
    config.rcc.pll1.q_ck = Some(100.mhz().into());
 | 
			
		||||
    config
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[embassy::main(config = "config()")]
 | 
			
		||||
async fn main(spawner: Spawner, p: Peripherals) -> ! {
 | 
			
		||||
    info!("Hello World!");
 | 
			
		||||
 | 
			
		||||
    // Generate random seed.
 | 
			
		||||
    let mut rng = Rng::new(p.RNG);
 | 
			
		||||
    let mut seed = [0; 8];
 | 
			
		||||
    rng.fill_bytes(&mut seed);
 | 
			
		||||
    let seed = u64::from_le_bytes(seed);
 | 
			
		||||
 | 
			
		||||
    let eth_int = interrupt::take!(ETH);
 | 
			
		||||
    let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
 | 
			
		||||
 | 
			
		||||
    let device = unsafe {
 | 
			
		||||
        Ethernet::new(
 | 
			
		||||
            forever!(State::new()),
 | 
			
		||||
            p.ETH,
 | 
			
		||||
            eth_int,
 | 
			
		||||
            p.PA1,
 | 
			
		||||
            p.PA2,
 | 
			
		||||
            p.PC1,
 | 
			
		||||
            p.PA7,
 | 
			
		||||
            p.PC4,
 | 
			
		||||
            p.PC5,
 | 
			
		||||
            p.PG13,
 | 
			
		||||
            p.PB13,
 | 
			
		||||
            p.PG11,
 | 
			
		||||
            GenericSMI,
 | 
			
		||||
            mac_addr,
 | 
			
		||||
            0,
 | 
			
		||||
        )
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let config = embassy_net::ConfigStrategy::Dhcp;
 | 
			
		||||
    //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config {
 | 
			
		||||
    //    address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
 | 
			
		||||
    //    dns_servers: Vec::new(),
 | 
			
		||||
    //    gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
 | 
			
		||||
    //});
 | 
			
		||||
 | 
			
		||||
    // Init network stack
 | 
			
		||||
    embassy_net::init(device, config, net_resources);
 | 
			
		||||
    let stack = &*forever!(Stack::new(
 | 
			
		||||
        device,
 | 
			
		||||
        config,
 | 
			
		||||
        forever!(StackResources::<1, 2, 8>::new()),
 | 
			
		||||
        seed
 | 
			
		||||
    ));
 | 
			
		||||
 | 
			
		||||
    // Launch network task
 | 
			
		||||
    unwrap!(spawner.spawn(net_task()));
 | 
			
		||||
    unwrap!(spawner.spawn(net_task(&stack)));
 | 
			
		||||
 | 
			
		||||
    info!("Network task initialized");
 | 
			
		||||
 | 
			
		||||
    // Then we can use it!
 | 
			
		||||
    let mut rx_buffer = [0; 1024];
 | 
			
		||||
    let mut tx_buffer = [0; 1024];
 | 
			
		||||
    let mut socket = TcpSocket::new(&mut rx_buffer, &mut tx_buffer);
 | 
			
		||||
 | 
			
		||||
    loop {
 | 
			
		||||
        let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer);
 | 
			
		||||
 | 
			
		||||
        socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
 | 
			
		||||
 | 
			
		||||
    let remote_endpoint = (Ipv4Address::new(192, 168, 0, 10), 8000);
 | 
			
		||||
        let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000);
 | 
			
		||||
        info!("connecting...");
 | 
			
		||||
        let r = socket.connect(remote_endpoint).await;
 | 
			
		||||
        if let Err(e) = r {
 | 
			
		||||
            info!("connect error: {:?}", e);
 | 
			
		||||
        return;
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
        info!("connected!");
 | 
			
		||||
        loop {
 | 
			
		||||
@@ -61,73 +122,5 @@ async fn main_task(
 | 
			
		||||
            }
 | 
			
		||||
            Timer::after(Duration::from_secs(1)).await;
 | 
			
		||||
        }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[embassy::task]
 | 
			
		||||
async fn net_task() {
 | 
			
		||||
    embassy_net::run().await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[no_mangle]
 | 
			
		||||
fn _embassy_rand(buf: &mut [u8]) {
 | 
			
		||||
    use rand_core::RngCore;
 | 
			
		||||
 | 
			
		||||
    critical_section::with(|_| unsafe {
 | 
			
		||||
        unwrap!(RNG_INST.as_mut()).fill_bytes(buf);
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static mut RNG_INST: Option<Rng<RNG>> = None;
 | 
			
		||||
 | 
			
		||||
static EXECUTOR: Forever<Executor> = Forever::new();
 | 
			
		||||
static STATE: Forever<State<'static, ETH, 4, 4>> = Forever::new();
 | 
			
		||||
static ETH: Forever<Ethernet<'static, ETH, GenericSMI, 4, 4>> = Forever::new();
 | 
			
		||||
static CONFIG: Forever<StaticConfigurator> = Forever::new();
 | 
			
		||||
static NET_RESOURCES: Forever<StackResources<1, 2, 8>> = Forever::new();
 | 
			
		||||
 | 
			
		||||
#[allow(unused)]
 | 
			
		||||
pub fn config() -> Config {
 | 
			
		||||
    let mut config = Config::default();
 | 
			
		||||
    config.rcc.sys_ck = Some(400.mhz().into());
 | 
			
		||||
    config.rcc.hclk = Some(200.mhz().into());
 | 
			
		||||
    config.rcc.pll1.q_ck = Some(100.mhz().into());
 | 
			
		||||
    config
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[entry]
 | 
			
		||||
fn main() -> ! {
 | 
			
		||||
    info!("Hello World!");
 | 
			
		||||
 | 
			
		||||
    info!("Setup RCC...");
 | 
			
		||||
 | 
			
		||||
    let p = embassy_stm32::init(config());
 | 
			
		||||
 | 
			
		||||
    let rng = Rng::new(p.RNG);
 | 
			
		||||
    unsafe {
 | 
			
		||||
        RNG_INST.replace(rng);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let eth_int = interrupt::take!(ETH);
 | 
			
		||||
    let mac_addr = [0x10; 6];
 | 
			
		||||
    let state = STATE.put(State::new());
 | 
			
		||||
    let eth = unsafe {
 | 
			
		||||
        ETH.put(Ethernet::new(
 | 
			
		||||
            state, p.ETH, eth_int, p.PA1, p.PA2, p.PC1, p.PA7, p.PC4, p.PC5, p.PG13, p.PB13,
 | 
			
		||||
            p.PG11, GenericSMI, mac_addr, 0,
 | 
			
		||||
        ))
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let config = StaticConfigurator::new(NetConfig {
 | 
			
		||||
        address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 0, 61), 24),
 | 
			
		||||
        dns_servers: Vec::new(),
 | 
			
		||||
        gateway: Some(Ipv4Address::new(192, 168, 0, 1)),
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let config = CONFIG.put(config);
 | 
			
		||||
 | 
			
		||||
    let executor = EXECUTOR.put(Executor::new());
 | 
			
		||||
 | 
			
		||||
    executor.run(move |spawner| {
 | 
			
		||||
        unwrap!(spawner.spawn(main_task(eth, config, spawner)));
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user