781:  embassy-net v2 r=Dirbaio a=Dirbaio

- No more `dyn`
- It's no longer a global singleton, you can create muliple net stacks at once.
  - You can't tear them down though, the Device it still has to be `'static` due to restrictions with smoltcp's "fake GAT" in the Device trait. :(
- Removed `_embassy_rand` hack, random seed is passed on creation.



785: stm32: g0: add PLL clock source r=Dirbaio a=willglynn

STM32G0 SYSCLK can be sourced from PLLRCLK. Given that the HSI runs at 16 MHz and the HSE range is 4-48 MHz, the PLL is the only way to reach 64 MHz. This commit adds `ClockSrc::PLL`.

The PLL sources from either HSI16 or HSE, divides it by `m`, and locks its VCO to multiple `n`. It then divides the VCO by `r`, `p`, and `q` to produce up to three associated clock signals:

  * PLLRCLK is one of the inputs on the SYSCLK mux. This is the main reason the user will configure the PLL, so `r` is mandatory and the output is enabled unconditionally.
  * PLLPCLK is available as a clock source for the ADC and I2S peripherals, so `p` is optional and the output is conditional.
  * PLLQCLK exists only on STM32G0B0xx, and exists only to feed the MCO and MCO2 peripherals, so `q` is optional and the output is conditional.

When the user specifies `ClockSrc::PLL(PllConfig)`, `rcc::init()` calls `PllConfig::init()` which initializes the PLL per [RM0454]. It disables the PLL, waits for it to stop, enables the source oscillator, configures the PLL, waits for it to lock, and then enables the appropriate outputs. `rcc::init()` then switches the clock source to PLLRCLK.

`rcc::init()` is now also resonsible for calculating and setting flash wait states. SYSCLCK < 24 MHz is fine in the reset state, but 24-48 MHz requires waiting 1 cycle and 48-64 MHz requires waiting 2 cycles. (This was likely a blocker for anyone using HSE >= 24 MHz, with or without the PLL.) Flash accesses are now automatically slowed down as needed before changing the clock source, and sped up as permitted after changing the clock source. The number of flash wait states also determines if flash prefetching will be profitable, so that is now handled automatically too.

[RM0454]: https://www.st.com/resource/en/reference_manual/rm0454-stm32g0x0-advanced-armbased-32bit-mcus-stmicroelectronics.pdf

Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
Co-authored-by: Will Glynn <will@willglynn.com>
This commit is contained in:
bors[bot] 2022-05-31 00:25:21 +00:00 committed by GitHub
commit 70e4418df9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 941 additions and 740 deletions

View File

@ -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",

View File

@ -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,
})
}
}
}
}

View File

@ -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;
}

View File

@ -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())
}
}
}

View File

@ -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>,

View File

@ -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>;

View File

@ -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,65 +118,209 @@ 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 {
SmolInstant::from_millis(instant.as_millis() as i64)
@ -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) }
}

View File

@ -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()
}
}

View File

@ -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));

View File

@ -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));

View File

@ -1,5 +1,6 @@
use crate::pac::rcc::vals::{Hpre, Hsidiv, Ppre, Sw};
use crate::pac::{PWR, RCC};
use crate::pac::flash::vals::Latency;
use crate::pac::rcc::vals::{self, Hpre, Hsidiv, Ppre, Sw};
use crate::pac::{FLASH, PWR, RCC};
use crate::rcc::{set_freqs, Clocks};
use crate::time::Hertz;
use crate::time::U32Ext;
@ -15,6 +16,7 @@ pub const LSI_FREQ: u32 = 32_000;
pub enum ClockSrc {
HSE(Hertz),
HSI16(HSI16Prescaler),
PLL(PllConfig),
LSI,
}
@ -45,6 +47,132 @@ impl Into<Hsidiv> for HSI16Prescaler {
}
}
/// The PLL configuration.
///
/// * `VCOCLK = source / m * n`
/// * `PLLRCLK = VCOCLK / r`
/// * `PLLQCLK = VCOCLK / q`
/// * `PLLPCLK = VCOCLK / p`
#[derive(Clone, Copy)]
pub struct PllConfig {
/// The source from which the PLL receives a clock signal
pub source: PllSrc,
/// The initial divisor of that clock signal
pub m: Pllm,
/// The PLL VCO multiplier, which must be in the range `8..=86`.
pub n: u8,
/// The final divisor for `PLLRCLK` output which drives the system clock
pub r: Pllr,
/// The divisor for the `PLLQCLK` output, if desired
pub q: Option<Pllr>,
/// The divisor for the `PLLPCLK` output, if desired
pub p: Option<Pllr>,
}
impl Default for PllConfig {
#[inline]
fn default() -> PllConfig {
// HSI16 / 1 * 8 / 2 = 64 MHz
PllConfig {
source: PllSrc::HSI16,
m: Pllm::Div1,
n: 8,
r: Pllr::Div2,
q: None,
p: None,
}
}
}
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum PllSrc {
HSI16,
HSE(Hertz),
}
#[derive(Clone, Copy)]
pub enum Pllm {
Div1,
Div2,
Div3,
Div4,
Div5,
Div6,
Div7,
Div8,
}
impl From<Pllm> for u8 {
fn from(v: Pllm) -> Self {
match v {
Pllm::Div1 => 0b000,
Pllm::Div2 => 0b001,
Pllm::Div3 => 0b010,
Pllm::Div4 => 0b011,
Pllm::Div5 => 0b100,
Pllm::Div6 => 0b101,
Pllm::Div7 => 0b110,
Pllm::Div8 => 0b111,
}
}
}
impl From<Pllm> for u32 {
fn from(v: Pllm) -> Self {
match v {
Pllm::Div1 => 1,
Pllm::Div2 => 2,
Pllm::Div3 => 3,
Pllm::Div4 => 4,
Pllm::Div5 => 5,
Pllm::Div6 => 6,
Pllm::Div7 => 7,
Pllm::Div8 => 8,
}
}
}
#[derive(Clone, Copy)]
pub enum Pllr {
Div2,
Div3,
Div4,
Div5,
Div6,
Div7,
Div8,
}
impl From<Pllr> for u8 {
fn from(v: Pllr) -> Self {
match v {
Pllr::Div2 => 0b000,
Pllr::Div3 => 0b001,
Pllr::Div4 => 0b010,
Pllr::Div5 => 0b011,
Pllr::Div6 => 0b101,
Pllr::Div7 => 0b110,
Pllr::Div8 => 0b111,
}
}
}
impl From<Pllr> for u32 {
fn from(v: Pllr) -> Self {
match v {
Pllr::Div2 => 2,
Pllr::Div3 => 3,
Pllr::Div4 => 4,
Pllr::Div5 => 5,
Pllr::Div6 => 6,
Pllr::Div7 => 7,
Pllr::Div8 => 8,
}
}
}
/// AHB prescaler
#[derive(Clone, Copy, PartialEq)]
pub enum AHBPrescaler {
@ -117,6 +245,95 @@ impl Default for Config {
}
}
impl PllConfig {
pub(crate) unsafe fn init(self) -> u32 {
assert!(self.n >= 8 && self.n <= 86);
let (src, input_freq) = match self.source {
PllSrc::HSI16 => (vals::Pllsrc::HSI16, HSI_FREQ),
PllSrc::HSE(freq) => (vals::Pllsrc::HSE, freq.0),
};
let m_freq = input_freq / u32::from(self.m);
// RM0454 § 5.4.4:
// > Caution: The software must set these bits so that the PLL input frequency after the
// > /M divider is between 2.66 and 16 MHz.
debug_assert!(m_freq >= 2_660_000 && m_freq <= 16_000_000);
let n_freq = m_freq * self.n as u32;
// RM0454 § 5.4.4:
// > Caution: The software must set these bits so that the VCO output frequency is between
// > 64 and 344 MHz.
debug_assert!(n_freq >= 64_000_000 && n_freq <= 344_000_000);
let r_freq = n_freq / u32::from(self.r);
// RM0454 § 5.4.4:
// > Caution: The software must set this bitfield so as not to exceed 64 MHz on this clock.
debug_assert!(r_freq <= 64_000_000);
// RM0454 § 5.2.3:
// > To modify the PLL configuration, proceed as follows:
// > 1. Disable the PLL by setting PLLON to 0 in Clock control register (RCC_CR).
RCC.cr().modify(|w| w.set_pllon(false));
// > 2. Wait until PLLRDY is cleared. The PLL is now fully stopped.
while RCC.cr().read().pllrdy() {}
// > 3. Change the desired parameter.
// Enable whichever clock source we're using, and wait for it to become ready
match self.source {
PllSrc::HSI16 => {
RCC.cr().write(|w| w.set_hsion(true));
while !RCC.cr().read().hsirdy() {}
}
PllSrc::HSE(_) => {
RCC.cr().write(|w| w.set_hseon(true));
while !RCC.cr().read().hserdy() {}
}
}
// Configure PLLSYSCFGR
RCC.pllsyscfgr().modify(|w| {
w.set_pllr(u8::from(self.r));
w.set_pllren(false);
if let Some(q) = self.q {
w.set_pllq(u8::from(q));
}
w.set_pllqen(false);
if let Some(p) = self.p {
w.set_pllp(u8::from(p));
}
w.set_pllpen(false);
w.set_plln(self.n);
w.set_pllm(self.m as u8);
w.set_pllsrc(src)
});
// > 4. Enable the PLL again by setting PLLON to 1.
RCC.cr().modify(|w| w.set_pllon(true));
// Wait for the PLL to become ready
while !RCC.cr().read().pllrdy() {}
// > 5. Enable the desired PLL outputs by configuring PLLPEN, PLLQEN, and PLLREN in PLL
// > configuration register (RCC_PLLCFGR).
RCC.pllsyscfgr().modify(|w| {
// We'll use R for system clock, so enable that unconditionally
w.set_pllren(true);
// We may also use Q or P
w.set_pllqen(self.q.is_some());
w.set_pllpen(self.p.is_some());
});
r_freq
}
}
pub(crate) unsafe fn init(config: Config) {
let (sys_clk, sw) = match config.mux {
ClockSrc::HSI16(div) => {
@ -137,6 +354,10 @@ pub(crate) unsafe fn init(config: Config) {
(freq.0, Sw::HSE)
}
ClockSrc::PLL(pll) => {
let freq = pll.init();
(freq, Sw::PLLRCLK)
}
ClockSrc::LSI => {
// Enable LSI
RCC.csr().write(|w| w.set_lsion(true));
@ -145,12 +366,66 @@ pub(crate) unsafe fn init(config: Config) {
}
};
RCC.cfgr().modify(|w| {
w.set_sw(sw.into());
w.set_hpre(config.ahb_pre.into());
w.set_ppre(config.apb_pre.into());
// Determine the flash latency implied by the target clock speed
// RM0454 § 3.3.4:
let target_flash_latency = if sys_clk <= 24_000_000 {
Latency::WS0
} else if sys_clk <= 48_000_000 {
Latency::WS1
} else {
Latency::WS2
};
// Increase the number of cycles we wait for flash if the new value is higher
// There's no harm in waiting a little too much before the clock change, but we'll
// crash immediately if we don't wait enough after the clock change
let mut set_flash_latency_after = false;
FLASH.acr().modify(|w| {
// Is the current flash latency less than what we need at the new SYSCLK?
if w.latency().0 <= target_flash_latency.0 {
// We must increase the number of wait states now
w.set_latency(target_flash_latency)
} else {
// We may decrease the number of wait states later
set_flash_latency_after = true;
}
// RM0454 § 3.3.5:
// > Prefetch is enabled by setting the PRFTEN bit of the FLASH access control register
// > (FLASH_ACR). This feature is useful if at least one wait state is needed to access the
// > Flash memory.
//
// Enable flash prefetching if we have at least one wait state, and disable it otherwise.
w.set_prften(target_flash_latency.0 > 0);
});
if !set_flash_latency_after {
// Spin until the effective flash latency is compatible with the clock change
while FLASH.acr().read().latency().0 < target_flash_latency.0 {}
}
// Configure SYSCLK source, HCLK divisor, and PCLK divisor all at once
let (sw, hpre, ppre) = (sw.into(), config.ahb_pre.into(), config.apb_pre.into());
RCC.cfgr().modify(|w| {
w.set_sw(sw);
w.set_hpre(hpre);
w.set_ppre(ppre);
});
if set_flash_latency_after {
// We can make the flash require fewer wait states
// Spin until the SYSCLK changes have taken effect
loop {
let cfgr = RCC.cfgr().read();
if cfgr.sw() == sw && cfgr.hpre() == hpre && cfgr.ppre() == ppre {
break;
}
}
// Set the flash latency to require fewer wait states
FLASH.acr().modify(|w| w.set_latency(target_flash_latency));
}
let ahb_div = match config.ahb_pre {
AHBPrescaler::NotDivided => 1,
AHBPrescaler::Div2 => 2,

View File

@ -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)
}

View File

@ -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() {

View File

@ -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

View File

@ -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"]

View File

@ -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 {
@ -62,70 +121,4 @@ 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)));
})
}

View File

@ -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

View File

@ -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 {
@ -62,72 +123,4 @@ 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)));
})
}