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:
commit
70e4418df9
18 changed files with 941 additions and 740 deletions
|
@ -20,6 +20,7 @@ std = []
|
||||||
defmt = ["dep:defmt", "smoltcp/defmt"]
|
defmt = ["dep:defmt", "smoltcp/defmt"]
|
||||||
|
|
||||||
tcp = ["smoltcp/socket-tcp"]
|
tcp = ["smoltcp/socket-tcp"]
|
||||||
|
dns = ["smoltcp/socket-dns"]
|
||||||
dhcpv4 = ["medium-ethernet", "smoltcp/socket-dhcpv4"]
|
dhcpv4 = ["medium-ethernet", "smoltcp/socket-dhcpv4"]
|
||||||
medium-ethernet = ["smoltcp/medium-ethernet"]
|
medium-ethernet = ["smoltcp/medium-ethernet"]
|
||||||
medium-ip = ["smoltcp/medium-ip"]
|
medium-ip = ["smoltcp/medium-ip"]
|
||||||
|
@ -49,6 +50,8 @@ atomic-pool = "0.2.1"
|
||||||
|
|
||||||
[dependencies.smoltcp]
|
[dependencies.smoltcp]
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
|
git = "https://github.com/smoltcp-rs/smoltcp"
|
||||||
|
rev = "ed0cf16750a42f30e31fcaf5347915592924b1e3"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [
|
features = [
|
||||||
"proto-ipv4",
|
"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,
|
Up,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 'static required due to the "fake GAT" in smoltcp::phy::Device.
|
||||||
|
// https://github.com/smoltcp-rs/smoltcp/pull/572
|
||||||
pub trait Device {
|
pub trait Device {
|
||||||
fn is_transmit_ready(&mut self) -> bool;
|
fn is_transmit_ready(&mut self) -> bool;
|
||||||
fn transmit(&mut self, pkt: PacketBuf);
|
fn transmit(&mut self, pkt: PacketBuf);
|
||||||
fn receive(&mut self) -> Option<PacketBuf>;
|
fn receive(&mut self) -> Option<PacketBuf>;
|
||||||
|
|
||||||
fn register_waker(&mut self, waker: &Waker);
|
fn register_waker(&mut self, waker: &Waker);
|
||||||
fn capabilities(&mut self) -> DeviceCapabilities;
|
fn capabilities(&self) -> DeviceCapabilities;
|
||||||
fn link_state(&mut self) -> LinkState;
|
fn link_state(&mut self) -> LinkState;
|
||||||
fn ethernet_address(&self) -> [u8; 6];
|
fn ethernet_address(&self) -> [u8; 6];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DeviceAdapter {
|
impl<T: ?Sized + Device> Device for &'static mut T {
|
||||||
pub device: &'static mut dyn Device,
|
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,
|
caps: DeviceCapabilities,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceAdapter {
|
impl<D: Device> DeviceAdapter<D> {
|
||||||
pub(crate) fn new(device: &'static mut dyn Device) -> Self {
|
pub(crate) fn new(device: D) -> Self {
|
||||||
Self {
|
Self {
|
||||||
caps: device.capabilities(),
|
caps: device.capabilities(),
|
||||||
device,
|
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 RxToken = RxToken;
|
||||||
type TxToken = TxToken<'a>;
|
type TxToken = TxToken<'a, D>;
|
||||||
|
|
||||||
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
|
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
|
||||||
let tx_pkt = PacketBox::new(Packet::new())?;
|
let tx_pkt = PacketBox::new(Packet::new())?;
|
||||||
let rx_pkt = self.device.receive()?;
|
let rx_pkt = self.device.receive()?;
|
||||||
let rx_token = RxToken { pkt: rx_pkt };
|
let rx_token = RxToken { pkt: rx_pkt };
|
||||||
let tx_token = TxToken {
|
let tx_token = TxToken {
|
||||||
device: self.device,
|
device: &mut self.device,
|
||||||
pkt: tx_pkt,
|
pkt: tx_pkt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -61,7 +87,7 @@ impl<'a> SmolDevice<'a> for DeviceAdapter {
|
||||||
|
|
||||||
let tx_pkt = PacketBox::new(Packet::new())?;
|
let tx_pkt = PacketBox::new(Packet::new())?;
|
||||||
Some(TxToken {
|
Some(TxToken {
|
||||||
device: self.device,
|
device: &mut self.device,
|
||||||
pkt: tx_pkt,
|
pkt: tx_pkt,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -85,12 +111,12 @@ impl smoltcp::phy::RxToken for RxToken {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TxToken<'a> {
|
pub struct TxToken<'a, D: Device> {
|
||||||
device: &'a mut dyn Device,
|
device: &'a mut D,
|
||||||
pkt: PacketBox,
|
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>
|
fn consume<R, F>(self, _timestamp: SmolInstant, len: usize, f: F) -> smoltcp::Result<R>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
|
F: FnOnce(&mut [u8]) -> smoltcp::Result<R>,
|
||||||
|
|
|
@ -5,20 +5,13 @@
|
||||||
// This mod MUST go first, so that the others see its macros.
|
// This mod MUST go first, so that the others see its macros.
|
||||||
pub(crate) mod fmt;
|
pub(crate) mod fmt;
|
||||||
|
|
||||||
mod config;
|
|
||||||
mod device;
|
mod device;
|
||||||
mod packet_pool;
|
mod packet_pool;
|
||||||
mod stack;
|
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 device::{Device, LinkState};
|
||||||
pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf, MTU};
|
pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf, MTU};
|
||||||
pub use stack::{
|
pub use stack::{Config, ConfigStrategy, Stack, StackResources};
|
||||||
config, ethernet_address, init, is_config_up, is_init, is_link_up, run, StackResources,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "tcp")]
|
#[cfg(feature = "tcp")]
|
||||||
pub mod tcp;
|
pub mod tcp;
|
||||||
|
@ -30,4 +23,3 @@ pub use smoltcp::time::Instant as SmolInstant;
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(feature = "medium-ethernet")]
|
||||||
pub use smoltcp::wire::{EthernetAddress, HardwareAddress};
|
pub use smoltcp::wire::{EthernetAddress, HardwareAddress};
|
||||||
pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr};
|
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::future::Future;
|
||||||
use core::task::Context;
|
use core::task::Context;
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
use embassy::blocking_mutex::ThreadModeMutex;
|
|
||||||
use embassy::time::{Instant, Timer};
|
use embassy::time::{Instant, Timer};
|
||||||
use embassy::waitqueue::WakerRegistration;
|
use embassy::waitqueue::WakerRegistration;
|
||||||
|
use futures::future::poll_fn;
|
||||||
use futures::pin_mut;
|
use futures::pin_mut;
|
||||||
use smoltcp::iface::InterfaceBuilder;
|
use heapless::Vec;
|
||||||
use smoltcp::iface::SocketStorage;
|
#[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::time::Instant as SmolInstant;
|
||||||
use smoltcp::wire::{IpCidr, Ipv4Address, Ipv4Cidr};
|
use smoltcp::wire::{IpCidr, Ipv4Address, Ipv4Cidr};
|
||||||
|
|
||||||
|
@ -18,10 +23,7 @@ use smoltcp::phy::{Device as _, Medium};
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(feature = "medium-ethernet")]
|
||||||
use smoltcp::wire::{EthernetAddress, HardwareAddress, IpAddress};
|
use smoltcp::wire::{EthernetAddress, HardwareAddress, IpAddress};
|
||||||
|
|
||||||
use crate::config::Configurator;
|
|
||||||
use crate::config::Event;
|
|
||||||
use crate::device::{Device, DeviceAdapter, LinkState};
|
use crate::device::{Device, DeviceAdapter, LinkState};
|
||||||
use crate::{Config, Interface};
|
|
||||||
|
|
||||||
const LOCAL_PORT_MIN: u16 = 1025;
|
const LOCAL_PORT_MIN: u16 = 1025;
|
||||||
const LOCAL_PORT_MAX: u16 = 65535;
|
const LOCAL_PORT_MAX: u16 = 65535;
|
||||||
|
@ -51,24 +53,144 @@ 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(crate) struct Stack {
|
pub address: Ipv4Cidr,
|
||||||
pub iface: Interface,
|
pub gateway: Option<Ipv4Address>,
|
||||||
link_up: bool,
|
pub dns_servers: Vec<Ipv4Address, 3>,
|
||||||
config: Option<Config>,
|
|
||||||
next_local_port: u16,
|
|
||||||
configurator: &'static mut dyn Configurator,
|
|
||||||
waker: WakerRegistration,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stack {
|
pub enum ConfigStrategy {
|
||||||
pub(crate) fn with<R>(f: impl FnOnce(&mut Stack) -> R) -> R {
|
Static(Config),
|
||||||
let mut stack = STACK.borrow().borrow_mut();
|
#[cfg(feature = "dhcpv4")]
|
||||||
let stack = stack.as_mut().unwrap();
|
Dhcp,
|
||||||
f(stack)
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<D: Device> Send for Stack<D> {}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
#[cfg(feature = "medium-ethernet")]
|
||||||
|
let ethernet_addr = if medium == Medium::Ethernet {
|
||||||
|
device.ethernet_address()
|
||||||
|
} else {
|
||||||
|
[0, 0, 0, 0, 0, 0]
|
||||||
|
};
|
||||||
|
|
||||||
|
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 {
|
||||||
|
b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(ethernet_addr)));
|
||||||
|
b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..]));
|
||||||
|
b = b.routes(Routes::new(&mut resources.routes[..]));
|
||||||
|
}
|
||||||
|
|
||||||
|
let iface = b.finalize(&mut device);
|
||||||
|
|
||||||
|
let sockets = SocketSet::new(&mut resources.sockets[..]);
|
||||||
|
|
||||||
|
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,
|
||||||
|
#[cfg(feature = "dhcpv4")]
|
||||||
|
dhcp_socket: None,
|
||||||
|
};
|
||||||
|
let mut socket = SocketStack {
|
||||||
|
sockets,
|
||||||
|
iface,
|
||||||
|
waker: WakerRegistration::new(),
|
||||||
|
next_local_port,
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
socket: UnsafeCell::new(socket),
|
||||||
|
inner: UnsafeCell::new(inner),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 ethernet_address(&self) -> [u8; 6] {
|
||||||
|
unsafe { self.with(|_s, i| i.device.device.ethernet_address()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_link_up(&self) -> bool {
|
||||||
|
unsafe { self.with(|_s, i| i.link_up) }
|
||||||
|
}
|
||||||
|
|
||||||
|
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)]
|
#[allow(clippy::absurd_extreme_comparisons)]
|
||||||
pub fn get_local_port(&mut self) -> u16 {
|
pub fn get_local_port(&mut self) -> u16 {
|
||||||
let res = self.next_local_port;
|
let res = self.next_local_port;
|
||||||
|
@ -79,60 +201,68 @@ impl Stack {
|
||||||
};
|
};
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn wake(&mut self) {
|
impl<D: Device + 'static> Inner<D> {
|
||||||
self.waker.wake()
|
fn apply_config(&mut self, s: &mut SocketStack, config: Config) {
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_configurator(&mut self, timestamp: SmolInstant) {
|
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(feature = "medium-ethernet")]
|
||||||
let medium = self.iface.device().capabilities().medium;
|
let medium = self.device.capabilities().medium;
|
||||||
|
|
||||||
match self.configurator.poll(&mut self.iface, timestamp) {
|
debug!("Acquired IP configuration:");
|
||||||
Event::NoChange => {}
|
|
||||||
Event::Configured(config) => {
|
|
||||||
debug!("Acquired IP configuration:");
|
|
||||||
|
|
||||||
debug!(" IP address: {}", config.address);
|
debug!(" IP address: {}", config.address);
|
||||||
set_ipv4_addr(&mut self.iface, config.address);
|
self.set_ipv4_addr(s, config.address);
|
||||||
|
|
||||||
#[cfg(feature = "medium-ethernet")]
|
#[cfg(feature = "medium-ethernet")]
|
||||||
if medium == Medium::Ethernet {
|
if medium == Medium::Ethernet {
|
||||||
if let Some(gateway) = config.gateway {
|
if let Some(gateway) = config.gateway {
|
||||||
debug!(" Default gateway: {}", gateway);
|
debug!(" Default gateway: {}", gateway);
|
||||||
self.iface
|
s.iface
|
||||||
.routes_mut()
|
.routes_mut()
|
||||||
.add_default_ipv4_route(gateway)
|
.add_default_ipv4_route(gateway)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
} else {
|
} else {
|
||||||
debug!(" Default gateway: None");
|
debug!(" Default gateway: None");
|
||||||
self.iface.routes_mut().remove_default_ipv4_route();
|
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)
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (i, s) in config.dns_servers.iter().enumerate() {
|
||||||
|
debug!(" DNS server {}: {}", i, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.config = Some(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll(&mut self, cx: &mut Context<'_>) {
|
#[allow(unused)] // used only with dhcp
|
||||||
self.iface.device_mut().device.register_waker(cx.waker());
|
fn unapply_config(&mut self, s: &mut SocketStack) {
|
||||||
self.waker.register(cx.waker());
|
#[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());
|
let timestamp = instant_to_smoltcp(Instant::now());
|
||||||
if self.iface.poll(timestamp).is_err() {
|
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.
|
// If poll() returns error, it may not be done yet, so poll again later.
|
||||||
cx.waker().wake_by_ref();
|
cx.waker().wake_by_ref();
|
||||||
return;
|
return;
|
||||||
|
@ -140,18 +270,49 @@ impl Stack {
|
||||||
|
|
||||||
// Update link up
|
// Update link up
|
||||||
let old_link_up = self.link_up;
|
let old_link_up = self.link_up;
|
||||||
self.link_up = self.iface.device_mut().device.link_state() == LinkState::Up;
|
self.link_up = self.device.device.link_state() == LinkState::Up;
|
||||||
|
|
||||||
// Print when changed
|
// Print when changed
|
||||||
if old_link_up != self.link_up {
|
if old_link_up != self.link_up {
|
||||||
info!("link_up = {:?}", self.link_up);
|
info!("link_up = {:?}", self.link_up);
|
||||||
}
|
}
|
||||||
|
|
||||||
if old_link_up || self.link_up {
|
#[cfg(feature = "dhcpv4")]
|
||||||
self.poll_configurator(timestamp)
|
if let Some(dhcp_handle) = self.dhcp_socket {
|
||||||
}
|
let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle);
|
||||||
|
|
||||||
if let Some(poll_at) = self.iface.poll_at(timestamp) {
|
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));
|
let t = Timer::at(instant_from_smoltcp(poll_at));
|
||||||
pin_mut!(t);
|
pin_mut!(t);
|
||||||
if t.poll(cx).is_ready() {
|
if t.poll(cx).is_ready() {
|
||||||
|
@ -161,100 +322,6 @@ impl Stack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
|
||||||
resources: &'static mut StackResources<ADDR, SOCK, NEIGH>,
|
|
||||||
) {
|
|
||||||
#[cfg(feature = "medium-ethernet")]
|
|
||||||
let medium = device.capabilities().medium;
|
|
||||||
|
|
||||||
#[cfg(feature = "medium-ethernet")]
|
|
||||||
let ethernet_addr = if medium == Medium::Ethernet {
|
|
||||||
device.ethernet_address()
|
|
||||||
} else {
|
|
||||||
[0, 0, 0, 0, 0, 0]
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut b = InterfaceBuilder::new(DeviceAdapter::new(device), &mut resources.sockets[..]);
|
|
||||||
b = b.ip_addrs(&mut resources.addresses[..]);
|
|
||||||
|
|
||||||
#[cfg(feature = "medium-ethernet")]
|
|
||||||
if medium == Medium::Ethernet {
|
|
||||||
b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(ethernet_addr)));
|
|
||||||
b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..]));
|
|
||||||
b = b.routes(Routes::new(&mut resources.routes[..]));
|
|
||||||
}
|
|
||||||
|
|
||||||
let iface = b.finalize();
|
|
||||||
|
|
||||||
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 stack = Stack {
|
|
||||||
iface,
|
|
||||||
link_up: false,
|
|
||||||
config: None,
|
|
||||||
configurator,
|
|
||||||
next_local_port: local_port,
|
|
||||||
waker: WakerRegistration::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
*STACK.borrow().borrow_mut() = Some(stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ethernet_address() -> [u8; 6] {
|
|
||||||
STACK
|
|
||||||
.borrow()
|
|
||||||
.borrow()
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.iface
|
|
||||||
.device()
|
|
||||||
.device
|
|
||||||
.ethernet_address()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_init() -> bool {
|
|
||||||
STACK.borrow().borrow().is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_link_up() -> bool {
|
|
||||||
STACK.borrow().borrow().as_ref().unwrap().link_up
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_config_up() -> bool {
|
|
||||||
STACK.borrow().borrow().as_ref().unwrap().config.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn config() -> Option<Config> {
|
|
||||||
STACK.borrow().borrow().as_ref().unwrap().config.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run() -> ! {
|
|
||||||
futures::future::poll_fn(|cx| {
|
|
||||||
Stack::with(|stack| stack.poll(cx));
|
|
||||||
Poll::<()>::Pending
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn instant_to_smoltcp(instant: Instant) -> SmolInstant {
|
fn instant_to_smoltcp(instant: Instant) -> SmolInstant {
|
||||||
SmolInstant::from_millis(instant.as_millis() as i64)
|
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 {
|
fn instant_from_smoltcp(instant: SmolInstant) -> Instant {
|
||||||
Instant::from_millis(instant.total_millis() as u64)
|
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::future::Future;
|
||||||
use core::marker::PhantomData;
|
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
use futures::future::poll_fn;
|
use futures::future::poll_fn;
|
||||||
use smoltcp::iface::{Context as SmolContext, SocketHandle};
|
use smoltcp::iface::{Interface, SocketHandle};
|
||||||
use smoltcp::socket::TcpSocket as SyncTcpSocket;
|
use smoltcp::socket::tcp;
|
||||||
use smoltcp::socket::{TcpSocketBuffer, TcpState};
|
|
||||||
use smoltcp::time::Duration;
|
use smoltcp::time::Duration;
|
||||||
use smoltcp::wire::IpEndpoint;
|
use smoltcp::wire::IpEndpoint;
|
||||||
|
use smoltcp::wire::IpListenEndpoint;
|
||||||
|
|
||||||
|
use crate::stack::SocketStack;
|
||||||
|
use crate::Device;
|
||||||
|
|
||||||
use super::stack::Stack;
|
use super::stack::Stack;
|
||||||
|
|
||||||
|
@ -42,78 +45,68 @@ pub enum AcceptError {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TcpSocket<'a> {
|
pub struct TcpSocket<'a> {
|
||||||
handle: SocketHandle,
|
io: TcpIo<'a>,
|
||||||
ghost: PhantomData<&'a mut [u8]>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Unpin for TcpSocket<'a> {}
|
|
||||||
|
|
||||||
pub struct TcpReader<'a> {
|
pub struct TcpReader<'a> {
|
||||||
handle: SocketHandle,
|
io: TcpIo<'a>,
|
||||||
ghost: PhantomData<&'a mut [u8]>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Unpin for TcpReader<'a> {}
|
|
||||||
|
|
||||||
pub struct TcpWriter<'a> {
|
pub struct TcpWriter<'a> {
|
||||||
handle: SocketHandle,
|
io: TcpIo<'a>,
|
||||||
ghost: PhantomData<&'a mut [u8]>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Unpin for TcpWriter<'a> {}
|
|
||||||
|
|
||||||
impl<'a> TcpSocket<'a> {
|
impl<'a> TcpSocket<'a> {
|
||||||
pub fn new(rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self {
|
pub fn new<D: Device>(
|
||||||
let handle = Stack::with(|stack| {
|
stack: &'a Stack<D>,
|
||||||
let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) };
|
rx_buffer: &'a mut [u8],
|
||||||
let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) };
|
tx_buffer: &'a mut [u8],
|
||||||
stack.iface.add_socket(SyncTcpSocket::new(
|
) -> Self {
|
||||||
TcpSocketBuffer::new(rx_buffer),
|
// safety: not accessed reentrantly.
|
||||||
TcpSocketBuffer::new(tx_buffer),
|
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) };
|
||||||
|
let handle = s.sockets.add(tcp::Socket::new(
|
||||||
|
tcp::SocketBuffer::new(rx_buffer),
|
||||||
|
tcp::SocketBuffer::new(tx_buffer),
|
||||||
|
));
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
handle,
|
io: TcpIo {
|
||||||
ghost: PhantomData,
|
stack: &stack.socket,
|
||||||
|
handle,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) {
|
pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) {
|
||||||
(
|
(TcpReader { io: self.io }, TcpWriter { io: self.io })
|
||||||
TcpReader {
|
|
||||||
handle: self.handle,
|
|
||||||
ghost: PhantomData,
|
|
||||||
},
|
|
||||||
TcpWriter {
|
|
||||||
handle: self.handle,
|
|
||||||
ghost: PhantomData,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn connect<T>(&mut self, remote_endpoint: T) -> Result<(), ConnectError>
|
pub async fn connect<T>(&mut self, remote_endpoint: T) -> Result<(), ConnectError>
|
||||||
where
|
where
|
||||||
T: Into<IpEndpoint>,
|
T: Into<IpEndpoint>,
|
||||||
{
|
{
|
||||||
let local_port = Stack::with(|stack| stack.get_local_port());
|
// safety: not accessed reentrantly.
|
||||||
match with_socket(self.handle, |s, cx| {
|
let local_port = unsafe { &mut *self.io.stack.get() }.get_local_port();
|
||||||
s.connect(cx, remote_endpoint, local_port)
|
|
||||||
}) {
|
// safety: not accessed reentrantly.
|
||||||
|
match unsafe {
|
||||||
|
self.io
|
||||||
|
.with_mut(|s, i| s.connect(i, remote_endpoint, local_port))
|
||||||
|
} {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(smoltcp::Error::Illegal) => return Err(ConnectError::InvalidState),
|
Err(tcp::ConnectError::InvalidState) => return Err(ConnectError::InvalidState),
|
||||||
Err(smoltcp::Error::Unaddressable) => return Err(ConnectError::NoRoute),
|
Err(tcp::ConnectError::Unaddressable) => return Err(ConnectError::NoRoute),
|
||||||
// smoltcp returns no errors other than the above.
|
|
||||||
Err(_) => unreachable!(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
futures::future::poll_fn(|cx| {
|
futures::future::poll_fn(|cx| unsafe {
|
||||||
with_socket(self.handle, |s, _| match s.state() {
|
self.io.with_mut(|s, _| match s.state() {
|
||||||
TcpState::Closed | TcpState::TimeWait => {
|
tcp::State::Closed | tcp::State::TimeWait => {
|
||||||
Poll::Ready(Err(ConnectError::ConnectionReset))
|
Poll::Ready(Err(ConnectError::ConnectionReset))
|
||||||
}
|
}
|
||||||
TcpState::Listen => unreachable!(),
|
tcp::State::Listen => unreachable!(),
|
||||||
TcpState::SynSent | TcpState::SynReceived => {
|
tcp::State::SynSent | tcp::State::SynReceived => {
|
||||||
s.register_send_waker(cx.waker());
|
s.register_send_waker(cx.waker());
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
}
|
}
|
||||||
|
@ -125,19 +118,18 @@ impl<'a> TcpSocket<'a> {
|
||||||
|
|
||||||
pub async fn accept<T>(&mut self, local_endpoint: T) -> Result<(), AcceptError>
|
pub async fn accept<T>(&mut self, local_endpoint: T) -> Result<(), AcceptError>
|
||||||
where
|
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(()) => {}
|
Ok(()) => {}
|
||||||
Err(smoltcp::Error::Illegal) => return Err(AcceptError::InvalidState),
|
Err(tcp::ListenError::InvalidState) => return Err(AcceptError::InvalidState),
|
||||||
Err(smoltcp::Error::Unaddressable) => return Err(AcceptError::InvalidPort),
|
Err(tcp::ListenError::Unaddressable) => return Err(AcceptError::InvalidPort),
|
||||||
// smoltcp returns no errors other than the above.
|
|
||||||
Err(_) => unreachable!(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
futures::future::poll_fn(|cx| {
|
futures::future::poll_fn(|cx| unsafe {
|
||||||
with_socket(self.handle, |s, _| match s.state() {
|
self.io.with_mut(|s, _| match s.state() {
|
||||||
TcpState::Listen | TcpState::SynSent | TcpState::SynReceived => {
|
tcp::State::Listen | tcp::State::SynSent | tcp::State::SynReceived => {
|
||||||
s.register_send_waker(cx.waker());
|
s.register_send_waker(cx.waker());
|
||||||
Poll::Pending
|
Poll::Pending
|
||||||
}
|
}
|
||||||
|
@ -148,65 +140,122 @@ impl<'a> TcpSocket<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_timeout(&mut self, duration: Option<Duration>) {
|
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>) {
|
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>) {
|
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 {
|
pub fn local_endpoint(&self) -> Option<IpEndpoint> {
|
||||||
with_socket(self.handle, |s, _| s.local_endpoint())
|
unsafe { self.io.with(|s, _| s.local_endpoint()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remote_endpoint(&self) -> IpEndpoint {
|
pub fn remote_endpoint(&self) -> Option<IpEndpoint> {
|
||||||
with_socket(self.handle, |s, _| s.remote_endpoint())
|
unsafe { self.io.with(|s, _| s.remote_endpoint()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn state(&self) -> TcpState {
|
pub fn state(&self) -> tcp::State {
|
||||||
with_socket(self.handle, |s, _| s.state())
|
unsafe { self.io.with(|s, _| s.state()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(&mut self) {
|
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) {
|
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 {
|
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 {
|
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> {
|
impl<'a> Drop for TcpSocket<'a> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
Stack::with(|stack| {
|
// safety: not accessed reentrantly.
|
||||||
stack.iface.remove_socket(self.handle);
|
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;
|
Self: 'a;
|
||||||
|
|
||||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||||
poll_fn(move |cx| {
|
self.io.read(buf)
|
||||||
// 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!(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,21 +285,7 @@ impl<'d> embedded_io::asynch::Write for TcpSocket<'d> {
|
||||||
Self: 'a;
|
Self: 'a;
|
||||||
|
|
||||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||||
poll_fn(move |cx| {
|
self.io.write(buf)
|
||||||
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!(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
|
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
|
||||||
|
@ -276,9 +293,7 @@ impl<'d> embedded_io::asynch::Write for TcpSocket<'d> {
|
||||||
Self: 'a;
|
Self: 'a;
|
||||||
|
|
||||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||||
poll_fn(move |_| {
|
self.io.flush()
|
||||||
Poll::Ready(Ok(())) // TODO: Is there a better implementation for this?
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,25 +307,7 @@ impl<'d> embedded_io::asynch::Read for TcpReader<'d> {
|
||||||
Self: 'a;
|
Self: 'a;
|
||||||
|
|
||||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> {
|
||||||
poll_fn(move |cx| {
|
self.io.read(buf)
|
||||||
// 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!(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,21 +321,7 @@ impl<'d> embedded_io::asynch::Write for TcpWriter<'d> {
|
||||||
Self: 'a;
|
Self: 'a;
|
||||||
|
|
||||||
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> {
|
||||||
poll_fn(move |cx| {
|
self.io.write(buf)
|
||||||
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!(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
|
type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>>
|
||||||
|
@ -346,8 +329,6 @@ impl<'d> embedded_io::asynch::Write for TcpWriter<'d> {
|
||||||
Self: 'a;
|
Self: 'a;
|
||||||
|
|
||||||
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> {
|
||||||
poll_fn(move |_| {
|
self.io.flush()
|
||||||
Poll::Ready(Ok(())) // TODO: Is there a better implementation for this?
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -319,7 +319,7 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Device
|
||||||
WAKER.register(waker);
|
WAKER.register(waker);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn capabilities(&mut self) -> DeviceCapabilities {
|
fn capabilities(&self) -> DeviceCapabilities {
|
||||||
let mut caps = DeviceCapabilities::default();
|
let mut caps = DeviceCapabilities::default();
|
||||||
caps.max_transmission_unit = MTU;
|
caps.max_transmission_unit = MTU;
|
||||||
caps.max_burst_size = Some(TX.min(RX));
|
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);
|
WAKER.register(waker);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn capabilities(&mut self) -> DeviceCapabilities {
|
fn capabilities(&self) -> DeviceCapabilities {
|
||||||
let mut caps = DeviceCapabilities::default();
|
let mut caps = DeviceCapabilities::default();
|
||||||
caps.max_transmission_unit = MTU;
|
caps.max_transmission_unit = MTU;
|
||||||
caps.max_burst_size = Some(TX.min(RX));
|
caps.max_burst_size = Some(TX.min(RX));
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::pac::rcc::vals::{Hpre, Hsidiv, Ppre, Sw};
|
use crate::pac::flash::vals::Latency;
|
||||||
use crate::pac::{PWR, RCC};
|
use crate::pac::rcc::vals::{self, Hpre, Hsidiv, Ppre, Sw};
|
||||||
|
use crate::pac::{FLASH, PWR, RCC};
|
||||||
use crate::rcc::{set_freqs, Clocks};
|
use crate::rcc::{set_freqs, Clocks};
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use crate::time::U32Ext;
|
use crate::time::U32Ext;
|
||||||
|
@ -15,6 +16,7 @@ pub const LSI_FREQ: u32 = 32_000;
|
||||||
pub enum ClockSrc {
|
pub enum ClockSrc {
|
||||||
HSE(Hertz),
|
HSE(Hertz),
|
||||||
HSI16(HSI16Prescaler),
|
HSI16(HSI16Prescaler),
|
||||||
|
PLL(PllConfig),
|
||||||
LSI,
|
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
|
/// AHB prescaler
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub enum AHBPrescaler {
|
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) {
|
pub(crate) unsafe fn init(config: Config) {
|
||||||
let (sys_clk, sw) = match config.mux {
|
let (sys_clk, sw) = match config.mux {
|
||||||
ClockSrc::HSI16(div) => {
|
ClockSrc::HSI16(div) => {
|
||||||
|
@ -137,6 +354,10 @@ pub(crate) unsafe fn init(config: Config) {
|
||||||
|
|
||||||
(freq.0, Sw::HSE)
|
(freq.0, Sw::HSE)
|
||||||
}
|
}
|
||||||
|
ClockSrc::PLL(pll) => {
|
||||||
|
let freq = pll.init();
|
||||||
|
(freq, Sw::PLLRCLK)
|
||||||
|
}
|
||||||
ClockSrc::LSI => {
|
ClockSrc::LSI => {
|
||||||
// Enable LSI
|
// Enable LSI
|
||||||
RCC.csr().write(|w| w.set_lsion(true));
|
RCC.csr().write(|w| w.set_lsion(true));
|
||||||
|
@ -145,12 +366,66 @@ pub(crate) unsafe fn init(config: Config) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
RCC.cfgr().modify(|w| {
|
// Determine the flash latency implied by the target clock speed
|
||||||
w.set_sw(sw.into());
|
// RM0454 § 3.3.4:
|
||||||
w.set_hpre(config.ahb_pre.into());
|
let target_flash_latency = if sys_clk <= 24_000_000 {
|
||||||
w.set_ppre(config.apb_pre.into());
|
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 {
|
let ahb_div = match config.ahb_pre {
|
||||||
AHBPrescaler::NotDivided => 1,
|
AHBPrescaler::NotDivided => 1,
|
||||||
AHBPrescaler::Div2 => 2,
|
AHBPrescaler::Div2 => 2,
|
||||||
|
|
|
@ -12,8 +12,9 @@ use embassy::channel::Channel;
|
||||||
use embassy::executor::Spawner;
|
use embassy::executor::Spawner;
|
||||||
use embassy::util::Forever;
|
use embassy::util::Forever;
|
||||||
use embassy_net::tcp::TcpSocket;
|
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::pac;
|
||||||
|
use embassy_nrf::rng::Rng;
|
||||||
use embassy_nrf::usb::Driver;
|
use embassy_nrf::usb::Driver;
|
||||||
use embassy_nrf::Peripherals;
|
use embassy_nrf::Peripherals;
|
||||||
use embassy_nrf::{interrupt, peripherals};
|
use embassy_nrf::{interrupt, peripherals};
|
||||||
|
@ -27,6 +28,14 @@ use panic_probe as _;
|
||||||
|
|
||||||
type MyDriver = Driver<'static, peripherals::USBD>;
|
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]
|
#[embassy::task]
|
||||||
async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! {
|
async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! {
|
||||||
device.run().await
|
device.run().await
|
||||||
|
@ -72,8 +81,8 @@ async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy::task]
|
#[embassy::task]
|
||||||
async fn net_task() -> ! {
|
async fn net_task(stack: &'static Stack<Device>) -> ! {
|
||||||
embassy_net::run().await
|
stack.run().await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy::main]
|
#[embassy::main]
|
||||||
|
@ -114,8 +123,7 @@ async fn main(spawner: Spawner, p: Peripherals) {
|
||||||
control_buf: [u8; 128],
|
control_buf: [u8; 128],
|
||||||
serial_state: State<'static>,
|
serial_state: State<'static>,
|
||||||
}
|
}
|
||||||
static RESOURCES: Forever<Resources> = Forever::new();
|
let res: &mut Resources = forever!(Resources {
|
||||||
let res = RESOURCES.put(Resources {
|
|
||||||
device_descriptor: [0; 256],
|
device_descriptor: [0; 256],
|
||||||
config_descriptor: [0; 256],
|
config_descriptor: [0; 256],
|
||||||
bos_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_rx_task(rx)));
|
||||||
unwrap!(spawner.spawn(usb_ncm_tx_task(tx)));
|
unwrap!(spawner.spawn(usb_ncm_tx_task(tx)));
|
||||||
|
|
||||||
// Init embassy-net
|
let config = embassy_net::ConfigStrategy::Dhcp;
|
||||||
struct NetResources {
|
//let config = embassy_net::ConfigStrategy::Static(embassy_net::Config {
|
||||||
resources: embassy_net::StackResources<1, 2, 8>,
|
// address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
|
||||||
configurator: embassy_net::DhcpConfigurator,
|
// dns_servers: Vec::new(),
|
||||||
//configurator: StaticConfigurator,
|
// gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
|
||||||
device: Device,
|
//});
|
||||||
}
|
|
||||||
static NET_RESOURCES: Forever<NetResources> = Forever::new();
|
// Generate random seed
|
||||||
let res = NET_RESOURCES.put(NetResources {
|
let mut rng = Rng::new(p.RNG, interrupt::take!(RNG));
|
||||||
resources: embassy_net::StackResources::new(),
|
let mut seed = [0; 8];
|
||||||
configurator: embassy_net::DhcpConfigurator::new(),
|
rng.blocking_fill_bytes(&mut seed);
|
||||||
//configurator: embassy_net::StaticConfigurator::new(embassy_net::Config {
|
let seed = u64::from_le_bytes(seed);
|
||||||
// address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 1), 24),
|
|
||||||
// dns_servers: Default::default(),
|
// Init network stack
|
||||||
// gateway: None,
|
let device = Device {
|
||||||
//}),
|
mac_addr: our_mac_addr,
|
||||||
device: Device {
|
};
|
||||||
mac_addr: our_mac_addr,
|
let stack = &*forever!(Stack::new(
|
||||||
},
|
device,
|
||||||
});
|
config,
|
||||||
embassy_net::init(&mut res.device, &mut res.configurator, &mut res.resources);
|
forever!(StackResources::<1, 2, 8>::new()),
|
||||||
unwrap!(spawner.spawn(net_task()));
|
seed
|
||||||
|
));
|
||||||
|
|
||||||
|
unwrap!(spawner.spawn(net_task(stack)));
|
||||||
|
|
||||||
// And now we can use it!
|
// And now we can use it!
|
||||||
|
|
||||||
|
@ -188,7 +199,7 @@ async fn main(spawner: Spawner, p: Peripherals) {
|
||||||
let mut buf = [0; 4096];
|
let mut buf = [0; 4096];
|
||||||
|
|
||||||
loop {
|
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)));
|
socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
|
||||||
|
|
||||||
info!("Listening on TCP:1234...");
|
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();
|
let mut caps = embassy_net::DeviceCapabilities::default();
|
||||||
caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header
|
caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header
|
||||||
caps.medium = embassy_net::Medium::Ethernet;
|
caps.medium = embassy_net::Medium::Ethernet;
|
||||||
|
@ -271,9 +282,3 @@ impl embassy_net::Device for Device {
|
||||||
self.mac_addr
|
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::executor::{Executor, Spawner};
|
||||||
use embassy::util::Forever;
|
use embassy::util::Forever;
|
||||||
use embassy_net::tcp::TcpSocket;
|
use embassy_net::tcp::TcpSocket;
|
||||||
use embassy_net::{
|
use embassy_net::{ConfigStrategy, Ipv4Address, Ipv4Cidr, Stack, StackResources};
|
||||||
Config, Configurator, DhcpConfigurator, Ipv4Address, Ipv4Cidr, StackResources,
|
|
||||||
StaticConfigurator,
|
|
||||||
};
|
|
||||||
use embedded_io::asynch::Write;
|
use embedded_io::asynch::Write;
|
||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
use log::*;
|
use log::*;
|
||||||
|
use rand_core::{OsRng, RngCore};
|
||||||
|
|
||||||
#[path = "../tuntap.rs"]
|
#[path = "../tuntap.rs"]
|
||||||
mod tuntap;
|
mod tuntap;
|
||||||
|
|
||||||
use crate::tuntap::TunTapDevice;
|
use crate::tuntap::TunTapDevice;
|
||||||
|
|
||||||
static DEVICE: Forever<TunTapDevice> = Forever::new();
|
macro_rules! forever {
|
||||||
static CONFIG_STATIC: Forever<StaticConfigurator> = Forever::new();
|
($val:expr) => {{
|
||||||
static CONFIG_DYNAMIC: Forever<DhcpConfigurator> = Forever::new();
|
type T = impl Sized;
|
||||||
static NET_RESOURCES: Forever<StackResources<1, 2, 8>> = Forever::new();
|
static FOREVER: Forever<T> = Forever::new();
|
||||||
|
FOREVER.put_with(move || $val)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[clap(version = "1.0")]
|
#[clap(version = "1.0")]
|
||||||
|
@ -34,8 +35,8 @@ struct Opts {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy::task]
|
#[embassy::task]
|
||||||
async fn net_task() {
|
async fn net_task(stack: &'static Stack<TunTapDevice>) -> ! {
|
||||||
embassy_net::run().await
|
stack.run().await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy::task]
|
#[embassy::task]
|
||||||
|
@ -46,28 +47,36 @@ async fn main_task(spawner: Spawner) {
|
||||||
let device = TunTapDevice::new(&opts.tap).unwrap();
|
let device = TunTapDevice::new(&opts.tap).unwrap();
|
||||||
|
|
||||||
// Choose between dhcp or static ip
|
// Choose between dhcp or static ip
|
||||||
let config: &'static mut dyn Configurator = if opts.static_ip {
|
let config = if opts.static_ip {
|
||||||
CONFIG_STATIC.put(StaticConfigurator::new(Config {
|
ConfigStrategy::Static(embassy_net::Config {
|
||||||
address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24),
|
address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24),
|
||||||
dns_servers: Vec::new(),
|
dns_servers: Vec::new(),
|
||||||
gateway: Some(Ipv4Address::new(192, 168, 69, 1)),
|
gateway: Some(Ipv4Address::new(192, 168, 69, 1)),
|
||||||
}))
|
})
|
||||||
} else {
|
} 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
|
// 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
|
// Launch network task
|
||||||
spawner.spawn(net_task()).unwrap();
|
spawner.spawn(net_task(stack)).unwrap();
|
||||||
|
|
||||||
// Then we can use it!
|
// Then we can use it!
|
||||||
let mut rx_buffer = [0; 4096];
|
let mut rx_buffer = [0; 4096];
|
||||||
let mut tx_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)));
|
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();
|
static EXECUTOR: Forever<Executor> = Forever::new();
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -209,7 +209,7 @@ impl Device for TunTapDevice {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn capabilities(&mut self) -> DeviceCapabilities {
|
fn capabilities(&self) -> DeviceCapabilities {
|
||||||
let mut caps = DeviceCapabilities::default();
|
let mut caps = DeviceCapabilities::default();
|
||||||
caps.max_transmission_unit = self.device.get_ref().mtu;
|
caps.max_transmission_unit = self.device.get_ref().mtu;
|
||||||
caps
|
caps
|
||||||
|
|
|
@ -8,7 +8,7 @@ resolver = "2"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
|
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-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"] }
|
embedded-io = { version = "0.3.0", features = ["async"] }
|
||||||
|
|
||||||
defmt = "0.3"
|
defmt = "0.3"
|
||||||
|
@ -24,8 +24,3 @@ nb = "1.0.0"
|
||||||
rand_core = "0.6.3"
|
rand_core = "0.6.3"
|
||||||
critical-section = "0.2.3"
|
critical-section = "0.2.3"
|
||||||
embedded-storage = "0.3.0"
|
embedded-storage = "0.3.0"
|
||||||
|
|
||||||
[dependencies.smoltcp]
|
|
||||||
version = "0.8.0"
|
|
||||||
default-features = false
|
|
||||||
features = ["defmt"]
|
|
||||||
|
|
|
@ -2,130 +2,123 @@
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy::executor::{Executor, Spawner};
|
use embassy::executor::Spawner;
|
||||||
use embassy::time::{Duration, Timer};
|
use embassy::time::{Duration, Timer};
|
||||||
use embassy::util::Forever;
|
use embassy::util::Forever;
|
||||||
use embassy_net::tcp::TcpSocket;
|
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::generic_smi::GenericSMI;
|
||||||
use embassy_stm32::eth::{Ethernet, State};
|
use embassy_stm32::eth::{Ethernet, State};
|
||||||
use embassy_stm32::interrupt;
|
|
||||||
use embassy_stm32::peripherals::ETH;
|
use embassy_stm32::peripherals::ETH;
|
||||||
use embassy_stm32::peripherals::RNG;
|
|
||||||
use embassy_stm32::rng::Rng;
|
use embassy_stm32::rng::Rng;
|
||||||
use embassy_stm32::time::U32Ext;
|
use embassy_stm32::time::U32Ext;
|
||||||
use embassy_stm32::Config;
|
use embassy_stm32::Config;
|
||||||
|
use embassy_stm32::{interrupt, Peripherals};
|
||||||
use embedded_io::asynch::Write;
|
use embedded_io::asynch::Write;
|
||||||
use heapless::Vec;
|
|
||||||
|
|
||||||
use defmt_rtt as _; // global logger
|
use defmt_rtt as _; // global logger
|
||||||
use panic_probe as _;
|
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]
|
#[embassy::task]
|
||||||
async fn main_task(
|
async fn net_task(stack: &'static Stack<Device>) -> ! {
|
||||||
device: &'static mut Ethernet<'static, ETH, GenericSMI, 4, 4>,
|
stack.run().await
|
||||||
config: &'static mut StaticConfigurator,
|
|
||||||
spawner: Spawner,
|
|
||||||
) {
|
|
||||||
let net_resources = NET_RESOURCES.put(StackResources::new());
|
|
||||||
|
|
||||||
// Init network stack
|
|
||||||
embassy_net::init(device, config, net_resources);
|
|
||||||
|
|
||||||
// Launch network task
|
|
||||||
unwrap!(spawner.spawn(net_task()));
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
|
|
||||||
|
|
||||||
let remote_endpoint = (Ipv4Address::new(192, 168, 0, 10), 8000);
|
|
||||||
let r = socket.connect(remote_endpoint).await;
|
|
||||||
if let Err(e) = r {
|
|
||||||
info!("connect error: {:?}", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
info!("connected!");
|
|
||||||
loop {
|
|
||||||
let r = socket.write_all(b"Hello\n").await;
|
|
||||||
if let Err(e) = r {
|
|
||||||
info!("write error: {:?}", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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 {
|
fn config() -> Config {
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.rcc.sys_ck = Some(200.mhz().into());
|
config.rcc.sys_ck = Some(200.mhz().into());
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
#[entry]
|
#[embassy::main(config = "config()")]
|
||||||
fn main() -> ! {
|
async fn main(spawner: Spawner, p: Peripherals) -> ! {
|
||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
|
||||||
info!("Setup RCC...");
|
// Generate random seed.
|
||||||
|
let mut rng = Rng::new(p.RNG);
|
||||||
let p = embassy_stm32::init(config());
|
let mut seed = [0; 8];
|
||||||
|
rng.fill_bytes(&mut seed);
|
||||||
let rng = Rng::new(p.RNG);
|
let seed = u64::from_le_bytes(seed);
|
||||||
unsafe {
|
|
||||||
RNG_INST.replace(rng);
|
|
||||||
}
|
|
||||||
|
|
||||||
let eth_int = interrupt::take!(ETH);
|
let eth_int = interrupt::take!(ETH);
|
||||||
let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
|
let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
|
||||||
let state = STATE.put(State::new());
|
|
||||||
|
|
||||||
let eth = unsafe {
|
let device = unsafe {
|
||||||
ETH.put(Ethernet::new(
|
Ethernet::new(
|
||||||
state, p.ETH, eth_int, p.PA1, p.PA2, p.PC1, p.PA7, p.PC4, p.PC5, p.PG13, p.PB13,
|
forever!(State::new()),
|
||||||
p.PG11, GenericSMI, mac_addr, 0,
|
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 {
|
let config = embassy_net::ConfigStrategy::Dhcp;
|
||||||
address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 0, 61), 24),
|
//let config = embassy_net::ConfigStrategy::Static(embassy_net::Config {
|
||||||
dns_servers: Vec::new(),
|
// address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
|
||||||
gateway: Some(Ipv4Address::new(192, 168, 0, 1)),
|
// dns_servers: Vec::new(),
|
||||||
});
|
// gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
|
||||||
|
//});
|
||||||
|
|
||||||
let config = CONFIG.put(config);
|
// Init network stack
|
||||||
|
let stack = &*forever!(Stack::new(
|
||||||
|
device,
|
||||||
|
config,
|
||||||
|
forever!(StackResources::<1, 2, 8>::new()),
|
||||||
|
seed
|
||||||
|
));
|
||||||
|
|
||||||
let executor = EXECUTOR.put(Executor::new());
|
// Launch network task
|
||||||
|
unwrap!(spawner.spawn(net_task(&stack)));
|
||||||
|
|
||||||
executor.run(move |spawner| {
|
info!("Network task initialized");
|
||||||
unwrap!(spawner.spawn(main_task(eth, config, spawner)));
|
|
||||||
})
|
// Then we can use it!
|
||||||
|
let mut rx_buffer = [0; 1024];
|
||||||
|
let mut tx_buffer = [0; 1024];
|
||||||
|
|
||||||
|
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(10, 42, 0, 1), 8000);
|
||||||
|
info!("connecting...");
|
||||||
|
let r = socket.connect(remote_endpoint).await;
|
||||||
|
if let Err(e) = r {
|
||||||
|
info!("connect error: {:?}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
info!("connected!");
|
||||||
|
loop {
|
||||||
|
let r = socket.write_all(b"Hello\n").await;
|
||||||
|
if let Err(e) = r {
|
||||||
|
info!("write error: {:?}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Timer::after(Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ resolver = "2"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] }
|
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-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"] }
|
embedded-io = { version = "0.3.0", features = ["async"] }
|
||||||
|
|
||||||
defmt = "0.3"
|
defmt = "0.3"
|
||||||
|
@ -28,11 +28,6 @@ micromath = "2.0.0"
|
||||||
stm32-fmc = "0.2.4"
|
stm32-fmc = "0.2.4"
|
||||||
embedded-storage = "0.3.0"
|
embedded-storage = "0.3.0"
|
||||||
|
|
||||||
[dependencies.smoltcp]
|
|
||||||
version = "0.8.0"
|
|
||||||
default-features = false
|
|
||||||
features = ["defmt"]
|
|
||||||
|
|
||||||
# cargo build/run
|
# cargo build/run
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
|
@ -2,90 +2,40 @@
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
use defmt_rtt as _; // global logger
|
|
||||||
use panic_probe as _;
|
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
use embassy::executor::{Executor, Spawner};
|
use embassy::executor::Spawner;
|
||||||
use embassy::time::{Duration, Timer};
|
use embassy::time::{Duration, Timer};
|
||||||
use embassy::util::Forever;
|
use embassy::util::Forever;
|
||||||
use embassy_net::tcp::TcpSocket;
|
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::generic_smi::GenericSMI;
|
||||||
use embassy_stm32::eth::{Ethernet, State};
|
use embassy_stm32::eth::{Ethernet, State};
|
||||||
use embassy_stm32::interrupt;
|
|
||||||
use embassy_stm32::peripherals::ETH;
|
use embassy_stm32::peripherals::ETH;
|
||||||
use embassy_stm32::peripherals::RNG;
|
|
||||||
use embassy_stm32::rng::Rng;
|
use embassy_stm32::rng::Rng;
|
||||||
use embassy_stm32::time::U32Ext;
|
use embassy_stm32::time::U32Ext;
|
||||||
use embassy_stm32::Config;
|
use embassy_stm32::Config;
|
||||||
|
use embassy_stm32::{interrupt, Peripherals};
|
||||||
use embedded_io::asynch::Write;
|
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]
|
#[embassy::task]
|
||||||
async fn main_task(
|
async fn net_task(stack: &'static Stack<Device>) -> ! {
|
||||||
device: &'static mut Ethernet<'static, ETH, GenericSMI, 4, 4>,
|
stack.run().await
|
||||||
config: &'static mut StaticConfigurator,
|
|
||||||
spawner: Spawner,
|
|
||||||
) {
|
|
||||||
let net_resources = NET_RESOURCES.put(StackResources::new());
|
|
||||||
|
|
||||||
// Init network stack
|
|
||||||
embassy_net::init(device, config, net_resources);
|
|
||||||
|
|
||||||
// Launch network task
|
|
||||||
unwrap!(spawner.spawn(net_task()));
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
|
|
||||||
|
|
||||||
let remote_endpoint = (Ipv4Address::new(192, 168, 0, 10), 8000);
|
|
||||||
let r = socket.connect(remote_endpoint).await;
|
|
||||||
if let Err(e) = r {
|
|
||||||
info!("connect error: {:?}", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
info!("connected!");
|
|
||||||
loop {
|
|
||||||
let r = socket.write_all(b"Hello\n").await;
|
|
||||||
if let Err(e) = r {
|
|
||||||
info!("write error: {:?}", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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 {
|
pub fn config() -> Config {
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.rcc.sys_ck = Some(400.mhz().into());
|
config.rcc.sys_ck = Some(400.mhz().into());
|
||||||
|
@ -94,40 +44,83 @@ pub fn config() -> Config {
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
#[entry]
|
#[embassy::main(config = "config()")]
|
||||||
fn main() -> ! {
|
async fn main(spawner: Spawner, p: Peripherals) -> ! {
|
||||||
info!("Hello World!");
|
info!("Hello World!");
|
||||||
|
|
||||||
info!("Setup RCC...");
|
// Generate random seed.
|
||||||
|
let mut rng = Rng::new(p.RNG);
|
||||||
let p = embassy_stm32::init(config());
|
let mut seed = [0; 8];
|
||||||
|
rng.fill_bytes(&mut seed);
|
||||||
let rng = Rng::new(p.RNG);
|
let seed = u64::from_le_bytes(seed);
|
||||||
unsafe {
|
|
||||||
RNG_INST.replace(rng);
|
|
||||||
}
|
|
||||||
|
|
||||||
let eth_int = interrupt::take!(ETH);
|
let eth_int = interrupt::take!(ETH);
|
||||||
let mac_addr = [0x10; 6];
|
let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
|
||||||
let state = STATE.put(State::new());
|
|
||||||
let eth = unsafe {
|
let device = unsafe {
|
||||||
ETH.put(Ethernet::new(
|
Ethernet::new(
|
||||||
state, p.ETH, eth_int, p.PA1, p.PA2, p.PC1, p.PA7, p.PC4, p.PC5, p.PG13, p.PB13,
|
forever!(State::new()),
|
||||||
p.PG11, GenericSMI, mac_addr, 0,
|
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 {
|
let config = embassy_net::ConfigStrategy::Dhcp;
|
||||||
address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 0, 61), 24),
|
//let config = embassy_net::ConfigStrategy::Static(embassy_net::Config {
|
||||||
dns_servers: Vec::new(),
|
// address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24),
|
||||||
gateway: Some(Ipv4Address::new(192, 168, 0, 1)),
|
// dns_servers: Vec::new(),
|
||||||
});
|
// gateway: Some(Ipv4Address::new(10, 42, 0, 1)),
|
||||||
|
//});
|
||||||
|
|
||||||
let config = CONFIG.put(config);
|
// Init network stack
|
||||||
|
let stack = &*forever!(Stack::new(
|
||||||
|
device,
|
||||||
|
config,
|
||||||
|
forever!(StackResources::<1, 2, 8>::new()),
|
||||||
|
seed
|
||||||
|
));
|
||||||
|
|
||||||
let executor = EXECUTOR.put(Executor::new());
|
// Launch network task
|
||||||
|
unwrap!(spawner.spawn(net_task(&stack)));
|
||||||
|
|
||||||
executor.run(move |spawner| {
|
info!("Network task initialized");
|
||||||
unwrap!(spawner.spawn(main_task(eth, config, spawner)));
|
|
||||||
})
|
// Then we can use it!
|
||||||
|
let mut rx_buffer = [0; 1024];
|
||||||
|
let mut tx_buffer = [0; 1024];
|
||||||
|
|
||||||
|
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(10, 42, 0, 1), 8000);
|
||||||
|
info!("connecting...");
|
||||||
|
let r = socket.connect(remote_endpoint).await;
|
||||||
|
if let Err(e) = r {
|
||||||
|
info!("connect error: {:?}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
info!("connected!");
|
||||||
|
loop {
|
||||||
|
let r = socket.write_all(b"Hello\n").await;
|
||||||
|
if let Err(e) = r {
|
||||||
|
info!("write error: {:?}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Timer::after(Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue