diff options
| author | Dario Nieuwenhuis <[email protected]> | 2022-05-23 03:50:43 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2022-05-25 19:56:22 +0200 |
| commit | a5aea995a802fea8fc1b3e4b5fe47bd6d1fca2a4 (patch) | |
| tree | 0fcb4c01914347eff5b3be44b284aa9432e28678 | |
| parent | 36a1f203648dcb402727ea3eb5d30cf1f6993795 (diff) | |
WIP embassy-net v2
| -rw-r--r-- | embassy-net/Cargo.toml | 3 | ||||
| -rw-r--r-- | embassy-net/src/config/dhcp.rs | 55 | ||||
| -rw-r--r-- | embassy-net/src/config/mod.rs | 35 | ||||
| -rw-r--r-- | embassy-net/src/config/statik.rs | 29 | ||||
| -rw-r--r-- | embassy-net/src/device.rs | 50 | ||||
| -rw-r--r-- | embassy-net/src/lib.rs | 10 | ||||
| -rw-r--r-- | embassy-net/src/stack.rs | 393 | ||||
| -rw-r--r-- | embassy-net/src/tcp.rs | 299 | ||||
| -rw-r--r-- | embassy-stm32/src/eth/v1/mod.rs | 2 | ||||
| -rw-r--r-- | embassy-stm32/src/eth/v2/mod.rs | 2 | ||||
| -rw-r--r-- | examples/nrf/src/bin/usb_ethernet.rs | 75 | ||||
| -rw-r--r-- | examples/std/src/bin/net.rs | 51 | ||||
| -rw-r--r-- | examples/std/src/tuntap.rs | 2 | ||||
| -rw-r--r-- | examples/stm32f7/Cargo.toml | 7 | ||||
| -rw-r--r-- | examples/stm32f7/src/bin/eth.rs | 173 | ||||
| -rw-r--r-- | examples/stm32h7/Cargo.toml | 7 | ||||
| -rw-r--r-- | examples/stm32h7/src/bin/eth.rs | 181 |
17 files changed, 650 insertions, 724 deletions
diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index b58b52f18..4692c01cb 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml | |||
| @@ -20,6 +20,7 @@ std = [] | |||
| 20 | defmt = ["dep:defmt", "smoltcp/defmt"] | 20 | defmt = ["dep:defmt", "smoltcp/defmt"] |
| 21 | 21 | ||
| 22 | tcp = ["smoltcp/socket-tcp"] | 22 | tcp = ["smoltcp/socket-tcp"] |
| 23 | dns = ["smoltcp/socket-dns"] | ||
| 23 | dhcpv4 = ["medium-ethernet", "smoltcp/socket-dhcpv4"] | 24 | dhcpv4 = ["medium-ethernet", "smoltcp/socket-dhcpv4"] |
| 24 | medium-ethernet = ["smoltcp/medium-ethernet"] | 25 | medium-ethernet = ["smoltcp/medium-ethernet"] |
| 25 | medium-ip = ["smoltcp/medium-ip"] | 26 | medium-ip = ["smoltcp/medium-ip"] |
| @@ -49,6 +50,8 @@ atomic-pool = "0.2.1" | |||
| 49 | 50 | ||
| 50 | [dependencies.smoltcp] | 51 | [dependencies.smoltcp] |
| 51 | version = "0.8.0" | 52 | version = "0.8.0" |
| 53 | git = "https://github.com/smoltcp-rs/smoltcp" | ||
| 54 | rev = "ed0cf16750a42f30e31fcaf5347915592924b1e3" | ||
| 52 | default-features = false | 55 | default-features = false |
| 53 | features = [ | 56 | features = [ |
| 54 | "proto-ipv4", | 57 | "proto-ipv4", |
diff --git a/embassy-net/src/config/dhcp.rs b/embassy-net/src/config/dhcp.rs deleted file mode 100644 index 298657ed6..000000000 --- a/embassy-net/src/config/dhcp.rs +++ /dev/null | |||
| @@ -1,55 +0,0 @@ | |||
| 1 | use heapless::Vec; | ||
| 2 | use smoltcp::iface::SocketHandle; | ||
| 3 | use smoltcp::socket::{Dhcpv4Event, Dhcpv4Socket}; | ||
| 4 | use smoltcp::time::Instant; | ||
| 5 | |||
| 6 | use super::*; | ||
| 7 | use crate::device::LinkState; | ||
| 8 | use crate::Interface; | ||
| 9 | |||
| 10 | pub struct DhcpConfigurator { | ||
| 11 | handle: Option<SocketHandle>, | ||
| 12 | } | ||
| 13 | |||
| 14 | impl DhcpConfigurator { | ||
| 15 | pub fn new() -> Self { | ||
| 16 | Self { handle: None } | ||
| 17 | } | ||
| 18 | } | ||
| 19 | |||
| 20 | impl Configurator for DhcpConfigurator { | ||
| 21 | fn poll(&mut self, iface: &mut Interface, _timestamp: Instant) -> Event { | ||
| 22 | if self.handle.is_none() { | ||
| 23 | let handle = iface.add_socket(Dhcpv4Socket::new()); | ||
| 24 | self.handle = Some(handle) | ||
| 25 | } | ||
| 26 | |||
| 27 | let link_up = iface.device_mut().device.link_state() == LinkState::Up; | ||
| 28 | |||
| 29 | let socket = iface.get_socket::<Dhcpv4Socket>(self.handle.unwrap()); | ||
| 30 | |||
| 31 | if !link_up { | ||
| 32 | socket.reset(); | ||
| 33 | return Event::Deconfigured; | ||
| 34 | } | ||
| 35 | |||
| 36 | match socket.poll() { | ||
| 37 | None => Event::NoChange, | ||
| 38 | Some(Dhcpv4Event::Deconfigured) => Event::Deconfigured, | ||
| 39 | Some(Dhcpv4Event::Configured(config)) => { | ||
| 40 | let mut dns_servers = Vec::new(); | ||
| 41 | for s in &config.dns_servers { | ||
| 42 | if let Some(addr) = s { | ||
| 43 | dns_servers.push(addr.clone()).unwrap(); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | Event::Configured(Config { | ||
| 48 | address: config.address, | ||
| 49 | gateway: config.router, | ||
| 50 | dns_servers, | ||
| 51 | }) | ||
| 52 | } | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
diff --git a/embassy-net/src/config/mod.rs b/embassy-net/src/config/mod.rs deleted file mode 100644 index eb1b6636a..000000000 --- a/embassy-net/src/config/mod.rs +++ /dev/null | |||
| @@ -1,35 +0,0 @@ | |||
| 1 | use heapless::Vec; | ||
| 2 | use smoltcp::time::Instant; | ||
| 3 | use smoltcp::wire::{Ipv4Address, Ipv4Cidr}; | ||
| 4 | |||
| 5 | use crate::Interface; | ||
| 6 | |||
| 7 | mod statik; | ||
| 8 | pub use statik::StaticConfigurator; | ||
| 9 | |||
| 10 | #[cfg(feature = "dhcpv4")] | ||
| 11 | mod dhcp; | ||
| 12 | #[cfg(feature = "dhcpv4")] | ||
| 13 | pub use dhcp::DhcpConfigurator; | ||
| 14 | |||
| 15 | /// Return value for the `Configurator::poll` function | ||
| 16 | #[derive(Debug, Clone)] | ||
| 17 | pub enum Event { | ||
| 18 | /// No change has occured to the configuration. | ||
| 19 | NoChange, | ||
| 20 | /// Configuration has been lost (for example, DHCP lease has expired) | ||
| 21 | Deconfigured, | ||
| 22 | /// Configuration has been newly acquired, or modified. | ||
| 23 | Configured(Config), | ||
| 24 | } | ||
| 25 | |||
| 26 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
| 27 | pub struct Config { | ||
| 28 | pub address: Ipv4Cidr, | ||
| 29 | pub gateway: Option<Ipv4Address>, | ||
| 30 | pub dns_servers: Vec<Ipv4Address, 3>, | ||
| 31 | } | ||
| 32 | |||
| 33 | pub trait Configurator { | ||
| 34 | fn poll(&mut self, iface: &mut Interface, timestamp: Instant) -> Event; | ||
| 35 | } | ||
diff --git a/embassy-net/src/config/statik.rs b/embassy-net/src/config/statik.rs deleted file mode 100644 index e614db73b..000000000 --- a/embassy-net/src/config/statik.rs +++ /dev/null | |||
| @@ -1,29 +0,0 @@ | |||
| 1 | use smoltcp::time::Instant; | ||
| 2 | |||
| 3 | use super::*; | ||
| 4 | use crate::Interface; | ||
| 5 | |||
| 6 | pub struct StaticConfigurator { | ||
| 7 | config: Config, | ||
| 8 | returned: bool, | ||
| 9 | } | ||
| 10 | |||
| 11 | impl StaticConfigurator { | ||
| 12 | pub fn new(config: Config) -> Self { | ||
| 13 | Self { | ||
| 14 | config, | ||
| 15 | returned: false, | ||
| 16 | } | ||
| 17 | } | ||
| 18 | } | ||
| 19 | |||
| 20 | impl Configurator for StaticConfigurator { | ||
| 21 | fn poll(&mut self, _iface: &mut Interface, _timestamp: Instant) -> Event { | ||
| 22 | if self.returned { | ||
| 23 | Event::NoChange | ||
| 24 | } else { | ||
| 25 | self.returned = true; | ||
| 26 | Event::Configured(self.config.clone()) | ||
| 27 | } | ||
| 28 | } | ||
| 29 | } | ||
diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 1f4fa5208..99c6a2212 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs | |||
| @@ -12,24 +12,50 @@ pub enum LinkState { | |||
| 12 | Up, | 12 | Up, |
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | // 'static required due to the "fake GAT" in smoltcp::phy::Device. | ||
| 16 | // https://github.com/smoltcp-rs/smoltcp/pull/572 | ||
| 15 | pub trait Device { | 17 | pub trait Device { |
| 16 | fn is_transmit_ready(&mut self) -> bool; | 18 | fn is_transmit_ready(&mut self) -> bool; |
| 17 | fn transmit(&mut self, pkt: PacketBuf); | 19 | fn transmit(&mut self, pkt: PacketBuf); |
| 18 | fn receive(&mut self) -> Option<PacketBuf>; | 20 | fn receive(&mut self) -> Option<PacketBuf>; |
| 19 | 21 | ||
| 20 | fn register_waker(&mut self, waker: &Waker); | 22 | fn register_waker(&mut self, waker: &Waker); |
| 21 | fn capabilities(&mut self) -> DeviceCapabilities; | 23 | fn capabilities(&self) -> DeviceCapabilities; |
| 22 | fn link_state(&mut self) -> LinkState; | 24 | fn link_state(&mut self) -> LinkState; |
| 23 | fn ethernet_address(&self) -> [u8; 6]; | 25 | fn ethernet_address(&self) -> [u8; 6]; |
| 24 | } | 26 | } |
| 25 | 27 | ||
| 26 | pub struct DeviceAdapter { | 28 | impl<T: ?Sized + Device> Device for &'static mut T { |
| 27 | pub device: &'static mut dyn Device, | 29 | fn is_transmit_ready(&mut self) -> bool { |
| 30 | T::is_transmit_ready(self) | ||
| 31 | } | ||
| 32 | fn transmit(&mut self, pkt: PacketBuf) { | ||
| 33 | T::transmit(self, pkt) | ||
| 34 | } | ||
| 35 | fn receive(&mut self) -> Option<PacketBuf> { | ||
| 36 | T::receive(self) | ||
| 37 | } | ||
| 38 | fn register_waker(&mut self, waker: &Waker) { | ||
| 39 | T::register_waker(self, waker) | ||
| 40 | } | ||
| 41 | fn capabilities(&self) -> DeviceCapabilities { | ||
| 42 | T::capabilities(self) | ||
| 43 | } | ||
| 44 | fn link_state(&mut self) -> LinkState { | ||
| 45 | T::link_state(self) | ||
| 46 | } | ||
| 47 | fn ethernet_address(&self) -> [u8; 6] { | ||
| 48 | T::ethernet_address(self) | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | pub struct DeviceAdapter<D: Device> { | ||
| 53 | pub device: D, | ||
| 28 | caps: DeviceCapabilities, | 54 | caps: DeviceCapabilities, |
| 29 | } | 55 | } |
| 30 | 56 | ||
| 31 | impl DeviceAdapter { | 57 | impl<D: Device> DeviceAdapter<D> { |
| 32 | pub(crate) fn new(device: &'static mut dyn Device) -> Self { | 58 | pub(crate) fn new(device: D) -> Self { |
| 33 | Self { | 59 | Self { |
| 34 | caps: device.capabilities(), | 60 | caps: device.capabilities(), |
| 35 | device, | 61 | device, |
| @@ -37,16 +63,16 @@ impl DeviceAdapter { | |||
| 37 | } | 63 | } |
| 38 | } | 64 | } |
| 39 | 65 | ||
| 40 | impl<'a> SmolDevice<'a> for DeviceAdapter { | 66 | impl<'a, D: Device + 'static> SmolDevice<'a> for DeviceAdapter<D> { |
| 41 | type RxToken = RxToken; | 67 | type RxToken = RxToken; |
| 42 | type TxToken = TxToken<'a>; | 68 | type TxToken = TxToken<'a, D>; |
| 43 | 69 | ||
| 44 | fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { | 70 | fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { |
| 45 | let tx_pkt = PacketBox::new(Packet::new())?; | 71 | let tx_pkt = PacketBox::new(Packet::new())?; |
| 46 | let rx_pkt = self.device.receive()?; | 72 | let rx_pkt = self.device.receive()?; |
| 47 | let rx_token = RxToken { pkt: rx_pkt }; | 73 | let rx_token = RxToken { pkt: rx_pkt }; |
| 48 | let tx_token = TxToken { | 74 | let tx_token = TxToken { |
| 49 | device: self.device, | 75 | device: &mut self.device, |
| 50 | pkt: tx_pkt, | 76 | pkt: tx_pkt, |
| 51 | }; | 77 | }; |
| 52 | 78 | ||
| @@ -61,7 +87,7 @@ impl<'a> SmolDevice<'a> for DeviceAdapter { | |||
| 61 | 87 | ||
| 62 | let tx_pkt = PacketBox::new(Packet::new())?; | 88 | let tx_pkt = PacketBox::new(Packet::new())?; |
| 63 | Some(TxToken { | 89 | Some(TxToken { |
| 64 | device: self.device, | 90 | device: &mut self.device, |
| 65 | pkt: tx_pkt, | 91 | pkt: tx_pkt, |
| 66 | }) | 92 | }) |
| 67 | } | 93 | } |
| @@ -85,12 +111,12 @@ impl smoltcp::phy::RxToken for RxToken { | |||
| 85 | } | 111 | } |
| 86 | } | 112 | } |
| 87 | 113 | ||
| 88 | pub struct TxToken<'a> { | 114 | pub struct TxToken<'a, D: Device> { |
| 89 | device: &'a mut dyn Device, | 115 | device: &'a mut D, |
| 90 | pkt: PacketBox, | 116 | pkt: PacketBox, |
| 91 | } | 117 | } |
| 92 | 118 | ||
| 93 | impl<'a> smoltcp::phy::TxToken for TxToken<'a> { | 119 | impl<'a, D: Device> smoltcp::phy::TxToken for TxToken<'a, D> { |
| 94 | fn consume<R, F>(self, _timestamp: SmolInstant, len: usize, f: F) -> smoltcp::Result<R> | 120 | fn consume<R, F>(self, _timestamp: SmolInstant, len: usize, f: F) -> smoltcp::Result<R> |
| 95 | where | 121 | where |
| 96 | F: FnOnce(&mut [u8]) -> smoltcp::Result<R>, | 122 | F: FnOnce(&mut [u8]) -> smoltcp::Result<R>, |
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 18dc1ef61..7b5f29f16 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs | |||
| @@ -5,20 +5,13 @@ | |||
| 5 | // This mod MUST go first, so that the others see its macros. | 5 | // This mod MUST go first, so that the others see its macros. |
| 6 | pub(crate) mod fmt; | 6 | pub(crate) mod fmt; |
| 7 | 7 | ||
| 8 | mod config; | ||
| 9 | mod device; | 8 | mod device; |
| 10 | mod packet_pool; | 9 | mod packet_pool; |
| 11 | mod stack; | 10 | mod stack; |
| 12 | 11 | ||
| 13 | #[cfg(feature = "dhcpv4")] | ||
| 14 | pub use config::DhcpConfigurator; | ||
| 15 | pub use config::{Config, Configurator, Event as ConfigEvent, StaticConfigurator}; | ||
| 16 | |||
| 17 | pub use device::{Device, LinkState}; | 12 | pub use device::{Device, LinkState}; |
| 18 | pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf, MTU}; | 13 | pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf, MTU}; |
| 19 | pub use stack::{ | 14 | pub use stack::{Config, ConfigStrategy, Stack, StackResources}; |
| 20 | config, ethernet_address, init, is_config_up, is_init, is_link_up, run, StackResources, | ||
| 21 | }; | ||
| 22 | 15 | ||
| 23 | #[cfg(feature = "tcp")] | 16 | #[cfg(feature = "tcp")] |
| 24 | pub mod tcp; | 17 | pub mod tcp; |
| @@ -30,4 +23,3 @@ pub use smoltcp::time::Instant as SmolInstant; | |||
| 30 | #[cfg(feature = "medium-ethernet")] | 23 | #[cfg(feature = "medium-ethernet")] |
| 31 | pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; | 24 | pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; |
| 32 | pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; | 25 | pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; |
| 33 | pub type Interface = smoltcp::iface::Interface<'static, device::DeviceAdapter>; | ||
diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index 9461f832f..e28370df8 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs | |||
| @@ -1,13 +1,18 @@ | |||
| 1 | use core::cell::RefCell; | 1 | use core::cell::UnsafeCell; |
| 2 | use core::future::Future; | 2 | use core::future::Future; |
| 3 | use core::task::Context; | 3 | use core::task::Context; |
| 4 | use core::task::Poll; | 4 | use core::task::Poll; |
| 5 | use embassy::blocking_mutex::ThreadModeMutex; | ||
| 6 | use embassy::time::{Instant, Timer}; | 5 | use embassy::time::{Instant, Timer}; |
| 7 | use embassy::waitqueue::WakerRegistration; | 6 | use embassy::waitqueue::WakerRegistration; |
| 7 | use futures::future::poll_fn; | ||
| 8 | use futures::pin_mut; | 8 | use futures::pin_mut; |
| 9 | use smoltcp::iface::InterfaceBuilder; | 9 | use heapless::Vec; |
| 10 | use smoltcp::iface::SocketStorage; | 10 | #[cfg(feature = "dhcpv4")] |
| 11 | use smoltcp::iface::SocketHandle; | ||
| 12 | use smoltcp::iface::{Interface, InterfaceBuilder}; | ||
| 13 | use smoltcp::iface::{SocketSet, SocketStorage}; | ||
| 14 | #[cfg(feature = "dhcpv4")] | ||
| 15 | use smoltcp::socket::dhcpv4; | ||
| 11 | use smoltcp::time::Instant as SmolInstant; | 16 | use smoltcp::time::Instant as SmolInstant; |
| 12 | use smoltcp::wire::{IpCidr, Ipv4Address, Ipv4Cidr}; | 17 | use smoltcp::wire::{IpCidr, Ipv4Address, Ipv4Cidr}; |
| 13 | 18 | ||
| @@ -18,10 +23,7 @@ use smoltcp::phy::{Device as _, Medium}; | |||
| 18 | #[cfg(feature = "medium-ethernet")] | 23 | #[cfg(feature = "medium-ethernet")] |
| 19 | use smoltcp::wire::{EthernetAddress, HardwareAddress, IpAddress}; | 24 | use smoltcp::wire::{EthernetAddress, HardwareAddress, IpAddress}; |
| 20 | 25 | ||
| 21 | use crate::config::Configurator; | ||
| 22 | use crate::config::Event; | ||
| 23 | use crate::device::{Device, DeviceAdapter, LinkState}; | 26 | use crate::device::{Device, DeviceAdapter, LinkState}; |
| 24 | use crate::{Config, Interface}; | ||
| 25 | 27 | ||
| 26 | const LOCAL_PORT_MIN: u16 = 1025; | 28 | const LOCAL_PORT_MIN: u16 = 1025; |
| 27 | const LOCAL_PORT_MAX: u16 = 65535; | 29 | const LOCAL_PORT_MAX: u16 = 65535; |
| @@ -51,24 +53,144 @@ impl<const ADDR: usize, const SOCK: usize, const NEIGHBOR: usize> | |||
| 51 | } | 53 | } |
| 52 | } | 54 | } |
| 53 | 55 | ||
| 54 | static STACK: ThreadModeMutex<RefCell<Option<Stack>>> = ThreadModeMutex::new(RefCell::new(None)); | 56 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 57 | pub struct Config { | ||
| 58 | pub address: Ipv4Cidr, | ||
| 59 | pub gateway: Option<Ipv4Address>, | ||
| 60 | pub dns_servers: Vec<Ipv4Address, 3>, | ||
| 61 | } | ||
| 62 | |||
| 63 | pub enum ConfigStrategy { | ||
| 64 | Static(Config), | ||
| 65 | #[cfg(feature = "dhcpv4")] | ||
| 66 | Dhcp, | ||
| 67 | } | ||
| 55 | 68 | ||
| 56 | pub(crate) struct Stack { | 69 | pub struct Stack<D: Device> { |
| 57 | pub iface: Interface, | 70 | pub(crate) socket: UnsafeCell<SocketStack>, |
| 71 | inner: UnsafeCell<Inner<D>>, | ||
| 72 | } | ||
| 73 | |||
| 74 | struct Inner<D: Device> { | ||
| 75 | device: DeviceAdapter<D>, | ||
| 58 | link_up: bool, | 76 | link_up: bool, |
| 59 | config: Option<Config>, | 77 | config: Option<Config>, |
| 78 | #[cfg(feature = "dhcpv4")] | ||
| 79 | dhcp_socket: Option<SocketHandle>, | ||
| 80 | } | ||
| 81 | |||
| 82 | pub(crate) struct SocketStack { | ||
| 83 | pub(crate) sockets: SocketSet<'static>, | ||
| 84 | pub(crate) iface: Interface<'static>, | ||
| 85 | pub(crate) waker: WakerRegistration, | ||
| 60 | next_local_port: u16, | 86 | next_local_port: u16, |
| 61 | configurator: &'static mut dyn Configurator, | ||
| 62 | waker: WakerRegistration, | ||
| 63 | } | 87 | } |
| 64 | 88 | ||
| 65 | impl Stack { | 89 | unsafe impl<D: Device> Send for Stack<D> {} |
| 66 | pub(crate) fn with<R>(f: impl FnOnce(&mut Stack) -> R) -> R { | 90 | |
| 67 | let mut stack = STACK.borrow().borrow_mut(); | 91 | impl<D: Device + 'static> Stack<D> { |
| 68 | let stack = stack.as_mut().unwrap(); | 92 | pub fn new<const ADDR: usize, const SOCK: usize, const NEIGH: usize>( |
| 69 | f(stack) | 93 | device: D, |
| 94 | config: ConfigStrategy, | ||
| 95 | resources: &'static mut StackResources<ADDR, SOCK, NEIGH>, | ||
| 96 | random_seed: u64, | ||
| 97 | ) -> Self { | ||
| 98 | #[cfg(feature = "medium-ethernet")] | ||
| 99 | let medium = device.capabilities().medium; | ||
| 100 | |||
| 101 | #[cfg(feature = "medium-ethernet")] | ||
| 102 | let ethernet_addr = if medium == Medium::Ethernet { | ||
| 103 | device.ethernet_address() | ||
| 104 | } else { | ||
| 105 | [0, 0, 0, 0, 0, 0] | ||
| 106 | }; | ||
| 107 | |||
| 108 | let mut device = DeviceAdapter::new(device); | ||
| 109 | |||
| 110 | let mut b = InterfaceBuilder::new(); | ||
| 111 | b = b.ip_addrs(&mut resources.addresses[..]); | ||
| 112 | b = b.random_seed(random_seed); | ||
| 113 | |||
| 114 | #[cfg(feature = "medium-ethernet")] | ||
| 115 | if medium == Medium::Ethernet { | ||
| 116 | b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(ethernet_addr))); | ||
| 117 | b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..])); | ||
| 118 | b = b.routes(Routes::new(&mut resources.routes[..])); | ||
| 119 | } | ||
| 120 | |||
| 121 | let iface = b.finalize(&mut device); | ||
| 122 | |||
| 123 | let sockets = SocketSet::new(&mut resources.sockets[..]); | ||
| 124 | |||
| 125 | let next_local_port = | ||
| 126 | (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; | ||
| 127 | |||
| 128 | let mut inner = Inner { | ||
| 129 | device, | ||
| 130 | link_up: false, | ||
| 131 | config: None, | ||
| 132 | #[cfg(feature = "dhcpv4")] | ||
| 133 | dhcp_socket: None, | ||
| 134 | }; | ||
| 135 | let mut socket = SocketStack { | ||
| 136 | sockets, | ||
| 137 | iface, | ||
| 138 | waker: WakerRegistration::new(), | ||
| 139 | next_local_port, | ||
| 140 | }; | ||
| 141 | |||
| 142 | match config { | ||
| 143 | ConfigStrategy::Static(config) => inner.apply_config(&mut socket, config), | ||
| 144 | #[cfg(feature = "dhcpv4")] | ||
| 145 | ConfigStrategy::Dhcp => { | ||
| 146 | let handle = socket.sockets.add(smoltcp::socket::dhcpv4::Socket::new()); | ||
| 147 | inner.dhcp_socket = Some(handle); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | Self { | ||
| 152 | socket: UnsafeCell::new(socket), | ||
| 153 | inner: UnsafeCell::new(inner), | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | /// SAFETY: must not call reentrantly. | ||
| 158 | unsafe fn with<R>(&self, f: impl FnOnce(&SocketStack, &Inner<D>) -> R) -> R { | ||
| 159 | f(&*self.socket.get(), &*self.inner.get()) | ||
| 160 | } | ||
| 161 | |||
| 162 | /// SAFETY: must not call reentrantly. | ||
| 163 | unsafe fn with_mut<R>(&self, f: impl FnOnce(&mut SocketStack, &mut Inner<D>) -> R) -> R { | ||
| 164 | f(&mut *self.socket.get(), &mut *self.inner.get()) | ||
| 165 | } | ||
| 166 | |||
| 167 | pub fn ethernet_address(&self) -> [u8; 6] { | ||
| 168 | unsafe { self.with(|_s, i| i.device.device.ethernet_address()) } | ||
| 169 | } | ||
| 170 | |||
| 171 | pub fn is_link_up(&self) -> bool { | ||
| 172 | unsafe { self.with(|_s, i| i.link_up) } | ||
| 173 | } | ||
| 174 | |||
| 175 | pub fn is_config_up(&self) -> bool { | ||
| 176 | unsafe { self.with(|_s, i| i.config.is_some()) } | ||
| 177 | } | ||
| 178 | |||
| 179 | pub fn config(&self) -> Option<Config> { | ||
| 180 | unsafe { self.with(|_s, i| i.config.clone()) } | ||
| 181 | } | ||
| 182 | |||
| 183 | pub async fn run(&self) -> ! { | ||
| 184 | poll_fn(|cx| { | ||
| 185 | unsafe { self.with_mut(|s, i| i.poll(cx, s)) } | ||
| 186 | Poll::<()>::Pending | ||
| 187 | }) | ||
| 188 | .await; | ||
| 189 | unreachable!() | ||
| 70 | } | 190 | } |
| 191 | } | ||
| 71 | 192 | ||
| 193 | impl SocketStack { | ||
| 72 | #[allow(clippy::absurd_extreme_comparisons)] | 194 | #[allow(clippy::absurd_extreme_comparisons)] |
| 73 | pub fn get_local_port(&mut self) -> u16 { | 195 | pub fn get_local_port(&mut self) -> u16 { |
| 74 | let res = self.next_local_port; | 196 | let res = self.next_local_port; |
| @@ -79,60 +201,68 @@ impl Stack { | |||
| 79 | }; | 201 | }; |
| 80 | res | 202 | res |
| 81 | } | 203 | } |
| 204 | } | ||
| 205 | |||
| 206 | impl<D: Device + 'static> Inner<D> { | ||
| 207 | fn apply_config(&mut self, s: &mut SocketStack, config: Config) { | ||
| 208 | #[cfg(feature = "medium-ethernet")] | ||
| 209 | let medium = self.device.capabilities().medium; | ||
| 210 | |||
| 211 | debug!("Acquired IP configuration:"); | ||
| 82 | 212 | ||
| 83 | pub(crate) fn wake(&mut self) { | 213 | debug!(" IP address: {}", config.address); |
| 84 | self.waker.wake() | 214 | self.set_ipv4_addr(s, config.address); |
| 215 | |||
| 216 | #[cfg(feature = "medium-ethernet")] | ||
| 217 | if medium == Medium::Ethernet { | ||
| 218 | if let Some(gateway) = config.gateway { | ||
| 219 | debug!(" Default gateway: {}", gateway); | ||
| 220 | s.iface | ||
| 221 | .routes_mut() | ||
| 222 | .add_default_ipv4_route(gateway) | ||
| 223 | .unwrap(); | ||
| 224 | } else { | ||
| 225 | debug!(" Default gateway: None"); | ||
| 226 | s.iface.routes_mut().remove_default_ipv4_route(); | ||
| 227 | } | ||
| 228 | } | ||
| 229 | for (i, s) in config.dns_servers.iter().enumerate() { | ||
| 230 | debug!(" DNS server {}: {}", i, s); | ||
| 231 | } | ||
| 232 | |||
| 233 | self.config = Some(config) | ||
| 85 | } | 234 | } |
| 86 | 235 | ||
| 87 | fn poll_configurator(&mut self, timestamp: SmolInstant) { | 236 | #[allow(unused)] // used only with dhcp |
| 237 | fn unapply_config(&mut self, s: &mut SocketStack) { | ||
| 88 | #[cfg(feature = "medium-ethernet")] | 238 | #[cfg(feature = "medium-ethernet")] |
| 89 | let medium = self.iface.device().capabilities().medium; | 239 | let medium = self.device.capabilities().medium; |
| 90 | |||
| 91 | match self.configurator.poll(&mut self.iface, timestamp) { | ||
| 92 | Event::NoChange => {} | ||
| 93 | Event::Configured(config) => { | ||
| 94 | debug!("Acquired IP configuration:"); | ||
| 95 | |||
| 96 | debug!(" IP address: {}", config.address); | ||
| 97 | set_ipv4_addr(&mut self.iface, config.address); | ||
| 98 | |||
| 99 | #[cfg(feature = "medium-ethernet")] | ||
| 100 | if medium == Medium::Ethernet { | ||
| 101 | if let Some(gateway) = config.gateway { | ||
| 102 | debug!(" Default gateway: {}", gateway); | ||
| 103 | self.iface | ||
| 104 | .routes_mut() | ||
| 105 | .add_default_ipv4_route(gateway) | ||
| 106 | .unwrap(); | ||
| 107 | } else { | ||
| 108 | debug!(" Default gateway: None"); | ||
| 109 | self.iface.routes_mut().remove_default_ipv4_route(); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | for (i, s) in config.dns_servers.iter().enumerate() { | ||
| 113 | debug!(" DNS server {}: {}", i, s); | ||
| 114 | } | ||
| 115 | 240 | ||
| 116 | self.config = Some(config) | 241 | debug!("Lost IP configuration"); |
| 117 | } | 242 | self.set_ipv4_addr(s, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)); |
| 118 | Event::Deconfigured => { | 243 | #[cfg(feature = "medium-ethernet")] |
| 119 | debug!("Lost IP configuration"); | 244 | if medium == Medium::Ethernet { |
| 120 | set_ipv4_addr(&mut self.iface, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)); | 245 | s.iface.routes_mut().remove_default_ipv4_route(); |
| 121 | #[cfg(feature = "medium-ethernet")] | ||
| 122 | if medium == Medium::Ethernet { | ||
| 123 | self.iface.routes_mut().remove_default_ipv4_route(); | ||
| 124 | } | ||
| 125 | self.config = None | ||
| 126 | } | ||
| 127 | } | 246 | } |
| 247 | self.config = None | ||
| 248 | } | ||
| 249 | |||
| 250 | fn set_ipv4_addr(&mut self, s: &mut SocketStack, cidr: Ipv4Cidr) { | ||
| 251 | s.iface.update_ip_addrs(|addrs| { | ||
| 252 | let dest = addrs.iter_mut().next().unwrap(); | ||
| 253 | *dest = IpCidr::Ipv4(cidr); | ||
| 254 | }); | ||
| 128 | } | 255 | } |
| 129 | 256 | ||
| 130 | fn poll(&mut self, cx: &mut Context<'_>) { | 257 | fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { |
| 131 | self.iface.device_mut().device.register_waker(cx.waker()); | 258 | self.device.device.register_waker(cx.waker()); |
| 132 | self.waker.register(cx.waker()); | 259 | s.waker.register(cx.waker()); |
| 133 | 260 | ||
| 134 | let timestamp = instant_to_smoltcp(Instant::now()); | 261 | let timestamp = instant_to_smoltcp(Instant::now()); |
| 135 | if self.iface.poll(timestamp).is_err() { | 262 | if s.iface |
| 263 | .poll(timestamp, &mut self.device, &mut s.sockets) | ||
| 264 | .is_err() | ||
| 265 | { | ||
| 136 | // If poll() returns error, it may not be done yet, so poll again later. | 266 | // If poll() returns error, it may not be done yet, so poll again later. |
| 137 | cx.waker().wake_by_ref(); | 267 | cx.waker().wake_by_ref(); |
| 138 | return; | 268 | return; |
| @@ -140,18 +270,49 @@ impl Stack { | |||
| 140 | 270 | ||
| 141 | // Update link up | 271 | // Update link up |
| 142 | let old_link_up = self.link_up; | 272 | let old_link_up = self.link_up; |
| 143 | self.link_up = self.iface.device_mut().device.link_state() == LinkState::Up; | 273 | self.link_up = self.device.device.link_state() == LinkState::Up; |
| 144 | 274 | ||
| 145 | // Print when changed | 275 | // Print when changed |
| 146 | if old_link_up != self.link_up { | 276 | if old_link_up != self.link_up { |
| 147 | info!("link_up = {:?}", self.link_up); | 277 | info!("link_up = {:?}", self.link_up); |
| 148 | } | 278 | } |
| 149 | 279 | ||
| 150 | if old_link_up || self.link_up { | 280 | #[cfg(feature = "dhcpv4")] |
| 151 | self.poll_configurator(timestamp) | 281 | if let Some(dhcp_handle) = self.dhcp_socket { |
| 282 | let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle); | ||
| 283 | |||
| 284 | if self.link_up { | ||
| 285 | match socket.poll() { | ||
| 286 | None => {} | ||
| 287 | Some(dhcpv4::Event::Deconfigured) => self.unapply_config(s), | ||
| 288 | Some(dhcpv4::Event::Configured(config)) => { | ||
| 289 | let mut dns_servers = Vec::new(); | ||
| 290 | for s in &config.dns_servers { | ||
| 291 | if let Some(addr) = s { | ||
| 292 | dns_servers.push(addr.clone()).unwrap(); | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | self.apply_config( | ||
| 297 | s, | ||
| 298 | Config { | ||
| 299 | address: config.address, | ||
| 300 | gateway: config.router, | ||
| 301 | dns_servers, | ||
| 302 | }, | ||
| 303 | ) | ||
| 304 | } | ||
| 305 | } | ||
| 306 | } else if old_link_up { | ||
| 307 | socket.reset(); | ||
| 308 | self.unapply_config(s); | ||
| 309 | } | ||
| 152 | } | 310 | } |
| 311 | //if old_link_up || self.link_up { | ||
| 312 | // self.poll_configurator(timestamp) | ||
| 313 | //} | ||
| 153 | 314 | ||
| 154 | if let Some(poll_at) = self.iface.poll_at(timestamp) { | 315 | if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) { |
| 155 | let t = Timer::at(instant_from_smoltcp(poll_at)); | 316 | let t = Timer::at(instant_from_smoltcp(poll_at)); |
| 156 | pin_mut!(t); | 317 | pin_mut!(t); |
| 157 | if t.poll(cx).is_ready() { | 318 | if t.poll(cx).is_ready() { |
| @@ -161,100 +322,6 @@ impl Stack { | |||
| 161 | } | 322 | } |
| 162 | } | 323 | } |
| 163 | 324 | ||
| 164 | fn set_ipv4_addr(iface: &mut Interface, cidr: Ipv4Cidr) { | ||
| 165 | iface.update_ip_addrs(|addrs| { | ||
| 166 | let dest = addrs.iter_mut().next().unwrap(); | ||
| 167 | *dest = IpCidr::Ipv4(cidr); | ||
| 168 | }); | ||
| 169 | } | ||
| 170 | |||
| 171 | /// Initialize embassy_net. | ||
| 172 | /// This function must be called from thread mode. | ||
| 173 | pub fn init<const ADDR: usize, const SOCK: usize, const NEIGH: usize>( | ||
| 174 | device: &'static mut dyn Device, | ||
| 175 | configurator: &'static mut dyn Configurator, | ||
| 176 | resources: &'static mut StackResources<ADDR, SOCK, NEIGH>, | ||
| 177 | ) { | ||
| 178 | #[cfg(feature = "medium-ethernet")] | ||
| 179 | let medium = device.capabilities().medium; | ||
| 180 | |||
| 181 | #[cfg(feature = "medium-ethernet")] | ||
| 182 | let ethernet_addr = if medium == Medium::Ethernet { | ||
| 183 | device.ethernet_address() | ||
| 184 | } else { | ||
| 185 | [0, 0, 0, 0, 0, 0] | ||
| 186 | }; | ||
| 187 | |||
| 188 | let mut b = InterfaceBuilder::new(DeviceAdapter::new(device), &mut resources.sockets[..]); | ||
| 189 | b = b.ip_addrs(&mut resources.addresses[..]); | ||
| 190 | |||
| 191 | #[cfg(feature = "medium-ethernet")] | ||
| 192 | if medium == Medium::Ethernet { | ||
| 193 | b = b.hardware_addr(HardwareAddress::Ethernet(EthernetAddress(ethernet_addr))); | ||
| 194 | b = b.neighbor_cache(NeighborCache::new(&mut resources.neighbor_cache[..])); | ||
| 195 | b = b.routes(Routes::new(&mut resources.routes[..])); | ||
| 196 | } | ||
| 197 | |||
| 198 | let iface = b.finalize(); | ||
| 199 | |||
| 200 | let local_port = loop { | ||
| 201 | let mut res = [0u8; 2]; | ||
| 202 | rand(&mut res); | ||
| 203 | let port = u16::from_le_bytes(res); | ||
| 204 | if (LOCAL_PORT_MIN..=LOCAL_PORT_MAX).contains(&port) { | ||
| 205 | break port; | ||
| 206 | } | ||
| 207 | }; | ||
| 208 | |||
| 209 | let stack = Stack { | ||
| 210 | iface, | ||
| 211 | link_up: false, | ||
| 212 | config: None, | ||
| 213 | configurator, | ||
| 214 | next_local_port: local_port, | ||
| 215 | waker: WakerRegistration::new(), | ||
| 216 | }; | ||
| 217 | |||
| 218 | *STACK.borrow().borrow_mut() = Some(stack); | ||
| 219 | } | ||
| 220 | |||
| 221 | pub fn ethernet_address() -> [u8; 6] { | ||
| 222 | STACK | ||
| 223 | .borrow() | ||
| 224 | .borrow() | ||
| 225 | .as_ref() | ||
| 226 | .unwrap() | ||
| 227 | .iface | ||
| 228 | .device() | ||
| 229 | .device | ||
| 230 | .ethernet_address() | ||
| 231 | } | ||
| 232 | |||
| 233 | pub fn is_init() -> bool { | ||
| 234 | STACK.borrow().borrow().is_some() | ||
| 235 | } | ||
| 236 | |||
| 237 | pub fn is_link_up() -> bool { | ||
| 238 | STACK.borrow().borrow().as_ref().unwrap().link_up | ||
| 239 | } | ||
| 240 | |||
| 241 | pub fn is_config_up() -> bool { | ||
| 242 | STACK.borrow().borrow().as_ref().unwrap().config.is_some() | ||
| 243 | } | ||
| 244 | |||
| 245 | pub fn config() -> Option<Config> { | ||
| 246 | STACK.borrow().borrow().as_ref().unwrap().config.clone() | ||
| 247 | } | ||
| 248 | |||
| 249 | pub async fn run() -> ! { | ||
| 250 | futures::future::poll_fn(|cx| { | ||
| 251 | Stack::with(|stack| stack.poll(cx)); | ||
| 252 | Poll::<()>::Pending | ||
| 253 | }) | ||
| 254 | .await; | ||
| 255 | unreachable!() | ||
| 256 | } | ||
| 257 | |||
| 258 | fn instant_to_smoltcp(instant: Instant) -> SmolInstant { | 325 | fn instant_to_smoltcp(instant: Instant) -> SmolInstant { |
| 259 | SmolInstant::from_millis(instant.as_millis() as i64) | 326 | SmolInstant::from_millis(instant.as_millis() as i64) |
| 260 | } | 327 | } |
| @@ -262,11 +329,3 @@ fn instant_to_smoltcp(instant: Instant) -> SmolInstant { | |||
| 262 | fn instant_from_smoltcp(instant: SmolInstant) -> Instant { | 329 | fn instant_from_smoltcp(instant: SmolInstant) -> Instant { |
| 263 | Instant::from_millis(instant.total_millis() as u64) | 330 | Instant::from_millis(instant.total_millis() as u64) |
| 264 | } | 331 | } |
| 265 | |||
| 266 | extern "Rust" { | ||
| 267 | fn _embassy_rand(buf: &mut [u8]); | ||
| 268 | } | ||
| 269 | |||
| 270 | fn rand(buf: &mut [u8]) { | ||
| 271 | unsafe { _embassy_rand(buf) } | ||
| 272 | } | ||
diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index c18651b93..2d81e66bd 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs | |||
| @@ -1,13 +1,16 @@ | |||
| 1 | use core::cell::UnsafeCell; | ||
| 1 | use core::future::Future; | 2 | use core::future::Future; |
| 2 | use core::marker::PhantomData; | ||
| 3 | use core::mem; | 3 | use core::mem; |
| 4 | use core::task::Poll; | 4 | use core::task::Poll; |
| 5 | use futures::future::poll_fn; | 5 | use futures::future::poll_fn; |
| 6 | use smoltcp::iface::{Context as SmolContext, SocketHandle}; | 6 | use smoltcp::iface::{Interface, SocketHandle}; |
| 7 | use smoltcp::socket::TcpSocket as SyncTcpSocket; | 7 | use smoltcp::socket::tcp; |
| 8 | use smoltcp::socket::{TcpSocketBuffer, TcpState}; | ||
| 9 | use smoltcp::time::Duration; | 8 | use smoltcp::time::Duration; |
| 10 | use smoltcp::wire::IpEndpoint; | 9 | use smoltcp::wire::IpEndpoint; |
| 10 | use smoltcp::wire::IpListenEndpoint; | ||
| 11 | |||
| 12 | use crate::stack::SocketStack; | ||
| 13 | use crate::Device; | ||
| 11 | 14 | ||
| 12 | use super::stack::Stack; | 15 | use super::stack::Stack; |
| 13 | 16 | ||
| @@ -42,78 +45,68 @@ pub enum AcceptError { | |||
| 42 | } | 45 | } |
| 43 | 46 | ||
| 44 | pub struct TcpSocket<'a> { | 47 | pub struct TcpSocket<'a> { |
| 45 | handle: SocketHandle, | 48 | io: TcpIo<'a>, |
| 46 | ghost: PhantomData<&'a mut [u8]>, | ||
| 47 | } | 49 | } |
| 48 | 50 | ||
| 49 | impl<'a> Unpin for TcpSocket<'a> {} | ||
| 50 | |||
| 51 | pub struct TcpReader<'a> { | 51 | pub struct TcpReader<'a> { |
| 52 | handle: SocketHandle, | 52 | io: TcpIo<'a>, |
| 53 | ghost: PhantomData<&'a mut [u8]>, | ||
| 54 | } | 53 | } |
| 55 | 54 | ||
| 56 | impl<'a> Unpin for TcpReader<'a> {} | ||
| 57 | |||
| 58 | pub struct TcpWriter<'a> { | 55 | pub struct TcpWriter<'a> { |
| 59 | handle: SocketHandle, | 56 | io: TcpIo<'a>, |
| 60 | ghost: PhantomData<&'a mut [u8]>, | ||
| 61 | } | 57 | } |
| 62 | 58 | ||
| 63 | impl<'a> Unpin for TcpWriter<'a> {} | ||
| 64 | |||
| 65 | impl<'a> TcpSocket<'a> { | 59 | impl<'a> TcpSocket<'a> { |
| 66 | pub fn new(rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { | 60 | pub fn new<D: Device>( |
| 67 | let handle = Stack::with(|stack| { | 61 | stack: &'a Stack<D>, |
| 68 | let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; | 62 | rx_buffer: &'a mut [u8], |
| 69 | let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; | 63 | tx_buffer: &'a mut [u8], |
| 70 | stack.iface.add_socket(SyncTcpSocket::new( | 64 | ) -> Self { |
| 71 | TcpSocketBuffer::new(rx_buffer), | 65 | // safety: not accessed reentrantly. |
| 72 | TcpSocketBuffer::new(tx_buffer), | 66 | let s = unsafe { &mut *stack.socket.get() }; |
| 73 | )) | 67 | let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; |
| 74 | }); | 68 | let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; |
| 69 | let handle = s.sockets.add(tcp::Socket::new( | ||
| 70 | tcp::SocketBuffer::new(rx_buffer), | ||
| 71 | tcp::SocketBuffer::new(tx_buffer), | ||
| 72 | )); | ||
| 75 | 73 | ||
| 76 | Self { | 74 | Self { |
| 77 | handle, | 75 | io: TcpIo { |
| 78 | ghost: PhantomData, | 76 | stack: &stack.socket, |
| 77 | handle, | ||
| 78 | }, | ||
| 79 | } | 79 | } |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) { | 82 | pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) { |
| 83 | ( | 83 | (TcpReader { io: self.io }, TcpWriter { io: self.io }) |
| 84 | TcpReader { | ||
| 85 | handle: self.handle, | ||
| 86 | ghost: PhantomData, | ||
| 87 | }, | ||
| 88 | TcpWriter { | ||
| 89 | handle: self.handle, | ||
| 90 | ghost: PhantomData, | ||
| 91 | }, | ||
| 92 | ) | ||
| 93 | } | 84 | } |
| 94 | 85 | ||
| 95 | pub async fn connect<T>(&mut self, remote_endpoint: T) -> Result<(), ConnectError> | 86 | pub async fn connect<T>(&mut self, remote_endpoint: T) -> Result<(), ConnectError> |
| 96 | where | 87 | where |
| 97 | T: Into<IpEndpoint>, | 88 | T: Into<IpEndpoint>, |
| 98 | { | 89 | { |
| 99 | let local_port = Stack::with(|stack| stack.get_local_port()); | 90 | // safety: not accessed reentrantly. |
| 100 | match with_socket(self.handle, |s, cx| { | 91 | let local_port = unsafe { &mut *self.io.stack.get() }.get_local_port(); |
| 101 | s.connect(cx, remote_endpoint, local_port) | 92 | |
| 102 | }) { | 93 | // safety: not accessed reentrantly. |
| 94 | match unsafe { | ||
| 95 | self.io | ||
| 96 | .with_mut(|s, i| s.connect(i, remote_endpoint, local_port)) | ||
| 97 | } { | ||
| 103 | Ok(()) => {} | 98 | Ok(()) => {} |
| 104 | Err(smoltcp::Error::Illegal) => return Err(ConnectError::InvalidState), | 99 | Err(tcp::ConnectError::InvalidState) => return Err(ConnectError::InvalidState), |
| 105 | Err(smoltcp::Error::Unaddressable) => return Err(ConnectError::NoRoute), | 100 | Err(tcp::ConnectError::Unaddressable) => return Err(ConnectError::NoRoute), |
| 106 | // smoltcp returns no errors other than the above. | ||
| 107 | Err(_) => unreachable!(), | ||
| 108 | } | 101 | } |
| 109 | 102 | ||
| 110 | futures::future::poll_fn(|cx| { | 103 | futures::future::poll_fn(|cx| unsafe { |
| 111 | with_socket(self.handle, |s, _| match s.state() { | 104 | self.io.with_mut(|s, _| match s.state() { |
| 112 | TcpState::Closed | TcpState::TimeWait => { | 105 | tcp::State::Closed | tcp::State::TimeWait => { |
| 113 | Poll::Ready(Err(ConnectError::ConnectionReset)) | 106 | Poll::Ready(Err(ConnectError::ConnectionReset)) |
| 114 | } | 107 | } |
| 115 | TcpState::Listen => unreachable!(), | 108 | tcp::State::Listen => unreachable!(), |
| 116 | TcpState::SynSent | TcpState::SynReceived => { | 109 | tcp::State::SynSent | tcp::State::SynReceived => { |
| 117 | s.register_send_waker(cx.waker()); | 110 | s.register_send_waker(cx.waker()); |
| 118 | Poll::Pending | 111 | Poll::Pending |
| 119 | } | 112 | } |
| @@ -125,19 +118,18 @@ impl<'a> TcpSocket<'a> { | |||
| 125 | 118 | ||
| 126 | pub async fn accept<T>(&mut self, local_endpoint: T) -> Result<(), AcceptError> | 119 | pub async fn accept<T>(&mut self, local_endpoint: T) -> Result<(), AcceptError> |
| 127 | where | 120 | where |
| 128 | T: Into<IpEndpoint>, | 121 | T: Into<IpListenEndpoint>, |
| 129 | { | 122 | { |
| 130 | match with_socket(self.handle, |s, _| s.listen(local_endpoint)) { | 123 | // safety: not accessed reentrantly. |
| 124 | match unsafe { self.io.with_mut(|s, _| s.listen(local_endpoint)) } { | ||
| 131 | Ok(()) => {} | 125 | Ok(()) => {} |
| 132 | Err(smoltcp::Error::Illegal) => return Err(AcceptError::InvalidState), | 126 | Err(tcp::ListenError::InvalidState) => return Err(AcceptError::InvalidState), |
| 133 | Err(smoltcp::Error::Unaddressable) => return Err(AcceptError::InvalidPort), | 127 | Err(tcp::ListenError::Unaddressable) => return Err(AcceptError::InvalidPort), |
| 134 | // smoltcp returns no errors other than the above. | ||
| 135 | Err(_) => unreachable!(), | ||
| 136 | } | 128 | } |
| 137 | 129 | ||
| 138 | futures::future::poll_fn(|cx| { | 130 | futures::future::poll_fn(|cx| unsafe { |
| 139 | with_socket(self.handle, |s, _| match s.state() { | 131 | self.io.with_mut(|s, _| match s.state() { |
| 140 | TcpState::Listen | TcpState::SynSent | TcpState::SynReceived => { | 132 | tcp::State::Listen | tcp::State::SynSent | tcp::State::SynReceived => { |
| 141 | s.register_send_waker(cx.waker()); | 133 | s.register_send_waker(cx.waker()); |
| 142 | Poll::Pending | 134 | Poll::Pending |
| 143 | } | 135 | } |
| @@ -148,88 +140,84 @@ impl<'a> TcpSocket<'a> { | |||
| 148 | } | 140 | } |
| 149 | 141 | ||
| 150 | pub fn set_timeout(&mut self, duration: Option<Duration>) { | 142 | pub fn set_timeout(&mut self, duration: Option<Duration>) { |
| 151 | with_socket(self.handle, |s, _| s.set_timeout(duration)) | 143 | unsafe { self.io.with_mut(|s, _| s.set_timeout(duration)) } |
| 152 | } | 144 | } |
| 153 | 145 | ||
| 154 | pub fn set_keep_alive(&mut self, interval: Option<Duration>) { | 146 | pub fn set_keep_alive(&mut self, interval: Option<Duration>) { |
| 155 | with_socket(self.handle, |s, _| s.set_keep_alive(interval)) | 147 | unsafe { self.io.with_mut(|s, _| s.set_keep_alive(interval)) } |
| 156 | } | 148 | } |
| 157 | 149 | ||
| 158 | pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) { | 150 | pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) { |
| 159 | with_socket(self.handle, |s, _| s.set_hop_limit(hop_limit)) | 151 | unsafe { self.io.with_mut(|s, _| s.set_hop_limit(hop_limit)) } |
| 160 | } | 152 | } |
| 161 | 153 | ||
| 162 | pub fn local_endpoint(&self) -> IpEndpoint { | 154 | pub fn local_endpoint(&self) -> Option<IpEndpoint> { |
| 163 | with_socket(self.handle, |s, _| s.local_endpoint()) | 155 | unsafe { self.io.with(|s, _| s.local_endpoint()) } |
| 164 | } | 156 | } |
| 165 | 157 | ||
| 166 | pub fn remote_endpoint(&self) -> IpEndpoint { | 158 | pub fn remote_endpoint(&self) -> Option<IpEndpoint> { |
| 167 | with_socket(self.handle, |s, _| s.remote_endpoint()) | 159 | unsafe { self.io.with(|s, _| s.remote_endpoint()) } |
| 168 | } | 160 | } |
| 169 | 161 | ||
| 170 | pub fn state(&self) -> TcpState { | 162 | pub fn state(&self) -> tcp::State { |
| 171 | with_socket(self.handle, |s, _| s.state()) | 163 | unsafe { self.io.with(|s, _| s.state()) } |
| 172 | } | 164 | } |
| 173 | 165 | ||
| 174 | pub fn close(&mut self) { | 166 | pub fn close(&mut self) { |
| 175 | with_socket(self.handle, |s, _| s.close()) | 167 | unsafe { self.io.with_mut(|s, _| s.close()) } |
| 176 | } | 168 | } |
| 177 | 169 | ||
| 178 | pub fn abort(&mut self) { | 170 | pub fn abort(&mut self) { |
| 179 | with_socket(self.handle, |s, _| s.abort()) | 171 | unsafe { self.io.with_mut(|s, _| s.abort()) } |
| 180 | } | 172 | } |
| 181 | 173 | ||
| 182 | pub fn may_send(&self) -> bool { | 174 | pub fn may_send(&self) -> bool { |
| 183 | with_socket(self.handle, |s, _| s.may_send()) | 175 | unsafe { self.io.with(|s, _| s.may_send()) } |
| 184 | } | 176 | } |
| 185 | 177 | ||
| 186 | pub fn may_recv(&self) -> bool { | 178 | pub fn may_recv(&self) -> bool { |
| 187 | with_socket(self.handle, |s, _| s.may_recv()) | 179 | unsafe { self.io.with(|s, _| s.may_recv()) } |
| 188 | } | 180 | } |
| 189 | } | 181 | } |
| 190 | 182 | ||
| 191 | fn with_socket<R>( | ||
| 192 | handle: SocketHandle, | ||
| 193 | f: impl FnOnce(&mut SyncTcpSocket, &mut SmolContext) -> R, | ||
| 194 | ) -> R { | ||
| 195 | Stack::with(|stack| { | ||
| 196 | let res = { | ||
| 197 | let (s, cx) = stack.iface.get_socket_and_context::<SyncTcpSocket>(handle); | ||
| 198 | f(s, cx) | ||
| 199 | }; | ||
| 200 | stack.wake(); | ||
| 201 | res | ||
| 202 | }) | ||
| 203 | } | ||
| 204 | |||
| 205 | impl<'a> Drop for TcpSocket<'a> { | 183 | impl<'a> Drop for TcpSocket<'a> { |
| 206 | fn drop(&mut self) { | 184 | fn drop(&mut self) { |
| 207 | Stack::with(|stack| { | 185 | // safety: not accessed reentrantly. |
| 208 | stack.iface.remove_socket(self.handle); | 186 | let s = unsafe { &mut *self.io.stack.get() }; |
| 209 | }) | 187 | s.sockets.remove(self.io.handle); |
| 210 | } | 188 | } |
| 211 | } | 189 | } |
| 212 | 190 | ||
| 213 | impl embedded_io::Error for Error { | 191 | // ======================= |
| 214 | fn kind(&self) -> embedded_io::ErrorKind { | ||
| 215 | embedded_io::ErrorKind::Other | ||
| 216 | } | ||
| 217 | } | ||
| 218 | 192 | ||
| 219 | impl<'d> embedded_io::Io for TcpSocket<'d> { | 193 | #[derive(Copy, Clone)] |
| 220 | type Error = Error; | 194 | pub struct TcpIo<'a> { |
| 195 | stack: &'a UnsafeCell<SocketStack>, | ||
| 196 | handle: SocketHandle, | ||
| 221 | } | 197 | } |
| 222 | 198 | ||
| 223 | impl<'d> embedded_io::asynch::Read for TcpSocket<'d> { | 199 | impl<'d> TcpIo<'d> { |
| 224 | type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> | 200 | /// SAFETY: must not call reentrantly. |
| 225 | where | 201 | unsafe fn with<R>(&self, f: impl FnOnce(&tcp::Socket, &Interface) -> R) -> R { |
| 226 | Self: 'a; | 202 | let s = &*self.stack.get(); |
| 203 | let socket = s.sockets.get::<tcp::Socket>(self.handle); | ||
| 204 | f(socket, &s.iface) | ||
| 205 | } | ||
| 227 | 206 | ||
| 228 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { | 207 | /// SAFETY: must not call reentrantly. |
| 229 | poll_fn(move |cx| { | 208 | unsafe fn with_mut<R>(&mut self, f: impl FnOnce(&mut tcp::Socket, &mut Interface) -> R) -> R { |
| 209 | let s = &mut *self.stack.get(); | ||
| 210 | let socket = s.sockets.get_mut::<tcp::Socket>(self.handle); | ||
| 211 | let res = f(socket, &mut s.iface); | ||
| 212 | s.waker.wake(); | ||
| 213 | res | ||
| 214 | } | ||
| 215 | |||
| 216 | async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { | ||
| 217 | poll_fn(move |cx| unsafe { | ||
| 230 | // CAUTION: smoltcp semantics around EOF are different to what you'd expect | 218 | // CAUTION: smoltcp semantics around EOF are different to what you'd expect |
| 231 | // from posix-like IO, so we have to tweak things here. | 219 | // from posix-like IO, so we have to tweak things here. |
| 232 | with_socket(self.handle, |s, _| match s.recv_slice(buf) { | 220 | self.with_mut(|s, _| match s.recv_slice(buf) { |
| 233 | // No data ready | 221 | // No data ready |
| 234 | Ok(0) => { | 222 | Ok(0) => { |
| 235 | s.register_recv_waker(cx.waker()); | 223 | s.register_recv_waker(cx.waker()); |
| @@ -238,24 +226,17 @@ impl<'d> embedded_io::asynch::Read for TcpSocket<'d> { | |||
| 238 | // Data ready! | 226 | // Data ready! |
| 239 | Ok(n) => Poll::Ready(Ok(n)), | 227 | Ok(n) => Poll::Ready(Ok(n)), |
| 240 | // EOF | 228 | // EOF |
| 241 | Err(smoltcp::Error::Finished) => Poll::Ready(Ok(0)), | 229 | Err(tcp::RecvError::Finished) => Poll::Ready(Ok(0)), |
| 242 | // Connection reset. TODO: this can also be timeouts etc, investigate. | 230 | // Connection reset. TODO: this can also be timeouts etc, investigate. |
| 243 | Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)), | 231 | Err(tcp::RecvError::InvalidState) => Poll::Ready(Err(Error::ConnectionReset)), |
| 244 | // smoltcp returns no errors other than the above. | ||
| 245 | Err(_) => unreachable!(), | ||
| 246 | }) | 232 | }) |
| 247 | }) | 233 | }) |
| 234 | .await | ||
| 248 | } | 235 | } |
| 249 | } | ||
| 250 | |||
| 251 | impl<'d> embedded_io::asynch::Write for TcpSocket<'d> { | ||
| 252 | type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> | ||
| 253 | where | ||
| 254 | Self: 'a; | ||
| 255 | 236 | ||
| 256 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { | 237 | async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> { |
| 257 | poll_fn(move |cx| { | 238 | poll_fn(move |cx| unsafe { |
| 258 | with_socket(self.handle, |s, _| match s.send_slice(buf) { | 239 | self.with_mut(|s, _| match s.send_slice(buf) { |
| 259 | // Not ready to send (no space in the tx buffer) | 240 | // Not ready to send (no space in the tx buffer) |
| 260 | Ok(0) => { | 241 | Ok(0) => { |
| 261 | s.register_send_waker(cx.waker()); | 242 | s.register_send_waker(cx.waker()); |
| @@ -264,11 +245,47 @@ impl<'d> embedded_io::asynch::Write for TcpSocket<'d> { | |||
| 264 | // Some data sent | 245 | // Some data sent |
| 265 | Ok(n) => Poll::Ready(Ok(n)), | 246 | Ok(n) => Poll::Ready(Ok(n)), |
| 266 | // Connection reset. TODO: this can also be timeouts etc, investigate. | 247 | // Connection reset. TODO: this can also be timeouts etc, investigate. |
| 267 | Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)), | 248 | Err(tcp::SendError::InvalidState) => Poll::Ready(Err(Error::ConnectionReset)), |
| 268 | // smoltcp returns no errors other than the above. | ||
| 269 | Err(_) => unreachable!(), | ||
| 270 | }) | 249 | }) |
| 271 | }) | 250 | }) |
| 251 | .await | ||
| 252 | } | ||
| 253 | |||
| 254 | async fn flush(&mut self) -> Result<(), Error> { | ||
| 255 | poll_fn(move |_| { | ||
| 256 | Poll::Ready(Ok(())) // TODO: Is there a better implementation for this? | ||
| 257 | }) | ||
| 258 | .await | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | impl embedded_io::Error for Error { | ||
| 263 | fn kind(&self) -> embedded_io::ErrorKind { | ||
| 264 | embedded_io::ErrorKind::Other | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | impl<'d> embedded_io::Io for TcpSocket<'d> { | ||
| 269 | type Error = Error; | ||
| 270 | } | ||
| 271 | |||
| 272 | impl<'d> embedded_io::asynch::Read for TcpSocket<'d> { | ||
| 273 | type ReadFuture<'a> = impl Future<Output = Result<usize, Self::Error>> | ||
| 274 | where | ||
| 275 | Self: 'a; | ||
| 276 | |||
| 277 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { | ||
| 278 | self.io.read(buf) | ||
| 279 | } | ||
| 280 | } | ||
| 281 | |||
| 282 | impl<'d> embedded_io::asynch::Write for TcpSocket<'d> { | ||
| 283 | type WriteFuture<'a> = impl Future<Output = Result<usize, Self::Error>> | ||
| 284 | where | ||
| 285 | Self: 'a; | ||
| 286 | |||
| 287 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { | ||
| 288 | self.io.write(buf) | ||
| 272 | } | 289 | } |
| 273 | 290 | ||
| 274 | type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> | 291 | type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> |
| @@ -276,9 +293,7 @@ impl<'d> embedded_io::asynch::Write for TcpSocket<'d> { | |||
| 276 | Self: 'a; | 293 | Self: 'a; |
| 277 | 294 | ||
| 278 | fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { | 295 | fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { |
| 279 | poll_fn(move |_| { | 296 | self.io.flush() |
| 280 | Poll::Ready(Ok(())) // TODO: Is there a better implementation for this? | ||
| 281 | }) | ||
| 282 | } | 297 | } |
| 283 | } | 298 | } |
| 284 | 299 | ||
| @@ -292,25 +307,7 @@ impl<'d> embedded_io::asynch::Read for TcpReader<'d> { | |||
| 292 | Self: 'a; | 307 | Self: 'a; |
| 293 | 308 | ||
| 294 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { | 309 | fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a> { |
| 295 | poll_fn(move |cx| { | 310 | self.io.read(buf) |
| 296 | // CAUTION: smoltcp semantics around EOF are different to what you'd expect | ||
| 297 | // from posix-like IO, so we have to tweak things here. | ||
| 298 | with_socket(self.handle, |s, _| match s.recv_slice(buf) { | ||
| 299 | // No data ready | ||
| 300 | Ok(0) => { | ||
| 301 | s.register_recv_waker(cx.waker()); | ||
| 302 | Poll::Pending | ||
| 303 | } | ||
| 304 | // Data ready! | ||
| 305 | Ok(n) => Poll::Ready(Ok(n)), | ||
| 306 | // EOF | ||
| 307 | Err(smoltcp::Error::Finished) => Poll::Ready(Ok(0)), | ||
| 308 | // Connection reset. TODO: this can also be timeouts etc, investigate. | ||
| 309 | Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)), | ||
| 310 | // smoltcp returns no errors other than the above. | ||
| 311 | Err(_) => unreachable!(), | ||
| 312 | }) | ||
| 313 | }) | ||
| 314 | } | 311 | } |
| 315 | } | 312 | } |
| 316 | 313 | ||
| @@ -324,21 +321,7 @@ impl<'d> embedded_io::asynch::Write for TcpWriter<'d> { | |||
| 324 | Self: 'a; | 321 | Self: 'a; |
| 325 | 322 | ||
| 326 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { | 323 | fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteFuture<'a> { |
| 327 | poll_fn(move |cx| { | 324 | self.io.write(buf) |
| 328 | with_socket(self.handle, |s, _| match s.send_slice(buf) { | ||
| 329 | // Not ready to send (no space in the tx buffer) | ||
| 330 | Ok(0) => { | ||
| 331 | s.register_send_waker(cx.waker()); | ||
| 332 | Poll::Pending | ||
| 333 | } | ||
| 334 | // Some data sent | ||
| 335 | Ok(n) => Poll::Ready(Ok(n)), | ||
| 336 | // Connection reset. TODO: this can also be timeouts etc, investigate. | ||
| 337 | Err(smoltcp::Error::Illegal) => Poll::Ready(Err(Error::ConnectionReset)), | ||
| 338 | // smoltcp returns no errors other than the above. | ||
| 339 | Err(_) => unreachable!(), | ||
| 340 | }) | ||
| 341 | }) | ||
| 342 | } | 325 | } |
| 343 | 326 | ||
| 344 | type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> | 327 | type FlushFuture<'a> = impl Future<Output = Result<(), Self::Error>> |
| @@ -346,8 +329,6 @@ impl<'d> embedded_io::asynch::Write for TcpWriter<'d> { | |||
| 346 | Self: 'a; | 329 | Self: 'a; |
| 347 | 330 | ||
| 348 | fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { | 331 | fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { |
| 349 | poll_fn(move |_| { | 332 | self.io.flush() |
| 350 | Poll::Ready(Ok(())) // TODO: Is there a better implementation for this? | ||
| 351 | }) | ||
| 352 | } | 333 | } |
| 353 | } | 334 | } |
diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index e6ffd047d..465436d06 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs | |||
| @@ -319,7 +319,7 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Device | |||
| 319 | WAKER.register(waker); | 319 | WAKER.register(waker); |
| 320 | } | 320 | } |
| 321 | 321 | ||
| 322 | fn capabilities(&mut self) -> DeviceCapabilities { | 322 | fn capabilities(&self) -> DeviceCapabilities { |
| 323 | let mut caps = DeviceCapabilities::default(); | 323 | let mut caps = DeviceCapabilities::default(); |
| 324 | caps.max_transmission_unit = MTU; | 324 | caps.max_transmission_unit = MTU; |
| 325 | caps.max_burst_size = Some(TX.min(RX)); | 325 | caps.max_burst_size = Some(TX.min(RX)); |
diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index d43d4f1a8..afde8ea9e 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs | |||
| @@ -253,7 +253,7 @@ impl<'d, T: Instance, P: PHY, const TX: usize, const RX: usize> Device | |||
| 253 | WAKER.register(waker); | 253 | WAKER.register(waker); |
| 254 | } | 254 | } |
| 255 | 255 | ||
| 256 | fn capabilities(&mut self) -> DeviceCapabilities { | 256 | fn capabilities(&self) -> DeviceCapabilities { |
| 257 | let mut caps = DeviceCapabilities::default(); | 257 | let mut caps = DeviceCapabilities::default(); |
| 258 | caps.max_transmission_unit = MTU; | 258 | caps.max_transmission_unit = MTU; |
| 259 | caps.max_burst_size = Some(TX.min(RX)); | 259 | caps.max_burst_size = Some(TX.min(RX)); |
diff --git a/examples/nrf/src/bin/usb_ethernet.rs b/examples/nrf/src/bin/usb_ethernet.rs index 843487c03..49f2fb89a 100644 --- a/examples/nrf/src/bin/usb_ethernet.rs +++ b/examples/nrf/src/bin/usb_ethernet.rs | |||
| @@ -12,8 +12,9 @@ use embassy::channel::Channel; | |||
| 12 | use embassy::executor::Spawner; | 12 | use embassy::executor::Spawner; |
| 13 | use embassy::util::Forever; | 13 | use embassy::util::Forever; |
| 14 | use embassy_net::tcp::TcpSocket; | 14 | use embassy_net::tcp::TcpSocket; |
| 15 | use embassy_net::{PacketBox, PacketBoxExt, PacketBuf}; | 15 | use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; |
| 16 | use embassy_nrf::pac; | 16 | use embassy_nrf::pac; |
| 17 | use embassy_nrf::rng::Rng; | ||
| 17 | use embassy_nrf::usb::Driver; | 18 | use embassy_nrf::usb::Driver; |
| 18 | use embassy_nrf::Peripherals; | 19 | use embassy_nrf::Peripherals; |
| 19 | use embassy_nrf::{interrupt, peripherals}; | 20 | use embassy_nrf::{interrupt, peripherals}; |
| @@ -27,6 +28,14 @@ use panic_probe as _; | |||
| 27 | 28 | ||
| 28 | type MyDriver = Driver<'static, peripherals::USBD>; | 29 | type MyDriver = Driver<'static, peripherals::USBD>; |
| 29 | 30 | ||
| 31 | macro_rules! forever { | ||
| 32 | ($val:expr) => {{ | ||
| 33 | type T = impl Sized; | ||
| 34 | static FOREVER: Forever<T> = Forever::new(); | ||
| 35 | FOREVER.put_with(move || $val) | ||
| 36 | }}; | ||
| 37 | } | ||
| 38 | |||
| 30 | #[embassy::task] | 39 | #[embassy::task] |
| 31 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { | 40 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { |
| 32 | device.run().await | 41 | device.run().await |
| @@ -72,8 +81,8 @@ async fn usb_ncm_tx_task(mut class: Sender<'static, MyDriver>) { | |||
| 72 | } | 81 | } |
| 73 | 82 | ||
| 74 | #[embassy::task] | 83 | #[embassy::task] |
| 75 | async fn net_task() -> ! { | 84 | async fn net_task(stack: &'static Stack<Device>) -> ! { |
| 76 | embassy_net::run().await | 85 | stack.run().await |
| 77 | } | 86 | } |
| 78 | 87 | ||
| 79 | #[embassy::main] | 88 | #[embassy::main] |
| @@ -114,8 +123,7 @@ async fn main(spawner: Spawner, p: Peripherals) { | |||
| 114 | control_buf: [u8; 128], | 123 | control_buf: [u8; 128], |
| 115 | serial_state: State<'static>, | 124 | serial_state: State<'static>, |
| 116 | } | 125 | } |
| 117 | static RESOURCES: Forever<Resources> = Forever::new(); | 126 | let res: &mut Resources = forever!(Resources { |
| 118 | let res = RESOURCES.put(Resources { | ||
| 119 | device_descriptor: [0; 256], | 127 | device_descriptor: [0; 256], |
| 120 | config_descriptor: [0; 256], | 128 | config_descriptor: [0; 256], |
| 121 | bos_descriptor: [0; 256], | 129 | bos_descriptor: [0; 256], |
| @@ -158,28 +166,31 @@ async fn main(spawner: Spawner, p: Peripherals) { | |||
| 158 | unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); | 166 | unwrap!(spawner.spawn(usb_ncm_rx_task(rx))); |
| 159 | unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); | 167 | unwrap!(spawner.spawn(usb_ncm_tx_task(tx))); |
| 160 | 168 | ||
| 161 | // Init embassy-net | 169 | let config = embassy_net::ConfigStrategy::Dhcp; |
| 162 | struct NetResources { | 170 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { |
| 163 | resources: embassy_net::StackResources<1, 2, 8>, | 171 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), |
| 164 | configurator: embassy_net::DhcpConfigurator, | 172 | // dns_servers: Vec::new(), |
| 165 | //configurator: StaticConfigurator, | 173 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), |
| 166 | device: Device, | 174 | //}); |
| 167 | } | 175 | |
| 168 | static NET_RESOURCES: Forever<NetResources> = Forever::new(); | 176 | // Generate random seed |
| 169 | let res = NET_RESOURCES.put(NetResources { | 177 | let mut rng = Rng::new(p.RNG, interrupt::take!(RNG)); |
| 170 | resources: embassy_net::StackResources::new(), | 178 | let mut seed = [0; 8]; |
| 171 | configurator: embassy_net::DhcpConfigurator::new(), | 179 | rng.blocking_fill_bytes(&mut seed); |
| 172 | //configurator: embassy_net::StaticConfigurator::new(embassy_net::Config { | 180 | let seed = u64::from_le_bytes(seed); |
| 173 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 1), 24), | 181 | |
| 174 | // dns_servers: Default::default(), | 182 | // Init network stack |
| 175 | // gateway: None, | 183 | let device = Device { |
| 176 | //}), | 184 | mac_addr: our_mac_addr, |
| 177 | device: Device { | 185 | }; |
| 178 | mac_addr: our_mac_addr, | 186 | let stack = &*forever!(Stack::new( |
| 179 | }, | 187 | device, |
| 180 | }); | 188 | config, |
| 181 | embassy_net::init(&mut res.device, &mut res.configurator, &mut res.resources); | 189 | forever!(StackResources::<1, 2, 8>::new()), |
| 182 | unwrap!(spawner.spawn(net_task())); | 190 | seed |
| 191 | )); | ||
| 192 | |||
| 193 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 183 | 194 | ||
| 184 | // And now we can use it! | 195 | // And now we can use it! |
| 185 | 196 | ||
| @@ -188,7 +199,7 @@ async fn main(spawner: Spawner, p: Peripherals) { | |||
| 188 | let mut buf = [0; 4096]; | 199 | let mut buf = [0; 4096]; |
| 189 | 200 | ||
| 190 | loop { | 201 | loop { |
| 191 | let mut socket = TcpSocket::new(&mut rx_buffer, &mut tx_buffer); | 202 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); |
| 192 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); | 203 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); |
| 193 | 204 | ||
| 194 | info!("Listening on TCP:1234..."); | 205 | info!("Listening on TCP:1234..."); |
| @@ -246,7 +257,7 @@ impl embassy_net::Device for Device { | |||
| 246 | } | 257 | } |
| 247 | } | 258 | } |
| 248 | 259 | ||
| 249 | fn capabilities(&mut self) -> embassy_net::DeviceCapabilities { | 260 | fn capabilities(&self) -> embassy_net::DeviceCapabilities { |
| 250 | let mut caps = embassy_net::DeviceCapabilities::default(); | 261 | let mut caps = embassy_net::DeviceCapabilities::default(); |
| 251 | caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header | 262 | caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header |
| 252 | caps.medium = embassy_net::Medium::Ethernet; | 263 | caps.medium = embassy_net::Medium::Ethernet; |
| @@ -271,9 +282,3 @@ impl embassy_net::Device for Device { | |||
| 271 | self.mac_addr | 282 | self.mac_addr |
| 272 | } | 283 | } |
| 273 | } | 284 | } |
| 274 | |||
| 275 | #[no_mangle] | ||
| 276 | fn _embassy_rand(buf: &mut [u8]) { | ||
| 277 | // TODO | ||
| 278 | buf.fill(0x42) | ||
| 279 | } | ||
diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index daedffb0f..74073ee81 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs | |||
| @@ -4,23 +4,24 @@ use clap::Parser; | |||
| 4 | use embassy::executor::{Executor, Spawner}; | 4 | use embassy::executor::{Executor, Spawner}; |
| 5 | use embassy::util::Forever; | 5 | use embassy::util::Forever; |
| 6 | use embassy_net::tcp::TcpSocket; | 6 | use embassy_net::tcp::TcpSocket; |
| 7 | use embassy_net::{ | 7 | use embassy_net::{ConfigStrategy, Ipv4Address, Ipv4Cidr, Stack, StackResources}; |
| 8 | Config, Configurator, DhcpConfigurator, Ipv4Address, Ipv4Cidr, StackResources, | ||
| 9 | StaticConfigurator, | ||
| 10 | }; | ||
| 11 | use embedded_io::asynch::Write; | 8 | use embedded_io::asynch::Write; |
| 12 | use heapless::Vec; | 9 | use heapless::Vec; |
| 13 | use log::*; | 10 | use log::*; |
| 11 | use rand_core::{OsRng, RngCore}; | ||
| 14 | 12 | ||
| 15 | #[path = "../tuntap.rs"] | 13 | #[path = "../tuntap.rs"] |
| 16 | mod tuntap; | 14 | mod tuntap; |
| 17 | 15 | ||
| 18 | use crate::tuntap::TunTapDevice; | 16 | use crate::tuntap::TunTapDevice; |
| 19 | 17 | ||
| 20 | static DEVICE: Forever<TunTapDevice> = Forever::new(); | 18 | macro_rules! forever { |
| 21 | static CONFIG_STATIC: Forever<StaticConfigurator> = Forever::new(); | 19 | ($val:expr) => {{ |
| 22 | static CONFIG_DYNAMIC: Forever<DhcpConfigurator> = Forever::new(); | 20 | type T = impl Sized; |
| 23 | static NET_RESOURCES: Forever<StackResources<1, 2, 8>> = Forever::new(); | 21 | static FOREVER: Forever<T> = Forever::new(); |
| 22 | FOREVER.put_with(move || $val) | ||
| 23 | }}; | ||
| 24 | } | ||
| 24 | 25 | ||
| 25 | #[derive(Parser)] | 26 | #[derive(Parser)] |
| 26 | #[clap(version = "1.0")] | 27 | #[clap(version = "1.0")] |
| @@ -34,8 +35,8 @@ struct Opts { | |||
| 34 | } | 35 | } |
| 35 | 36 | ||
| 36 | #[embassy::task] | 37 | #[embassy::task] |
| 37 | async fn net_task() { | 38 | async fn net_task(stack: &'static Stack<TunTapDevice>) -> ! { |
| 38 | embassy_net::run().await | 39 | stack.run().await |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 41 | #[embassy::task] | 42 | #[embassy::task] |
| @@ -46,28 +47,36 @@ async fn main_task(spawner: Spawner) { | |||
| 46 | let device = TunTapDevice::new(&opts.tap).unwrap(); | 47 | let device = TunTapDevice::new(&opts.tap).unwrap(); |
| 47 | 48 | ||
| 48 | // Choose between dhcp or static ip | 49 | // Choose between dhcp or static ip |
| 49 | let config: &'static mut dyn Configurator = if opts.static_ip { | 50 | let config = if opts.static_ip { |
| 50 | CONFIG_STATIC.put(StaticConfigurator::new(Config { | 51 | ConfigStrategy::Static(embassy_net::Config { |
| 51 | address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), | 52 | address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), |
| 52 | dns_servers: Vec::new(), | 53 | dns_servers: Vec::new(), |
| 53 | gateway: Some(Ipv4Address::new(192, 168, 69, 1)), | 54 | gateway: Some(Ipv4Address::new(192, 168, 69, 1)), |
| 54 | })) | 55 | }) |
| 55 | } else { | 56 | } else { |
| 56 | CONFIG_DYNAMIC.put(DhcpConfigurator::new()) | 57 | ConfigStrategy::Dhcp |
| 57 | }; | 58 | }; |
| 58 | 59 | ||
| 59 | let net_resources = StackResources::new(); | 60 | // Generate random seed |
| 61 | let mut seed = [0; 8]; | ||
| 62 | OsRng.fill_bytes(&mut seed); | ||
| 63 | let seed = u64::from_le_bytes(seed); | ||
| 60 | 64 | ||
| 61 | // Init network stack | 65 | // Init network stack |
| 62 | embassy_net::init(DEVICE.put(device), config, NET_RESOURCES.put(net_resources)); | 66 | let stack = &*forever!(Stack::new( |
| 67 | device, | ||
| 68 | config, | ||
| 69 | forever!(StackResources::<1, 2, 8>::new()), | ||
| 70 | seed | ||
| 71 | )); | ||
| 63 | 72 | ||
| 64 | // Launch network task | 73 | // Launch network task |
| 65 | spawner.spawn(net_task()).unwrap(); | 74 | spawner.spawn(net_task(stack)).unwrap(); |
| 66 | 75 | ||
| 67 | // Then we can use it! | 76 | // Then we can use it! |
| 68 | let mut rx_buffer = [0; 4096]; | 77 | let mut rx_buffer = [0; 4096]; |
| 69 | let mut tx_buffer = [0; 4096]; | 78 | let mut tx_buffer = [0; 4096]; |
| 70 | let mut socket = TcpSocket::new(&mut rx_buffer, &mut tx_buffer); | 79 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); |
| 71 | 80 | ||
| 72 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); | 81 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); |
| 73 | 82 | ||
| @@ -88,12 +97,6 @@ async fn main_task(spawner: Spawner) { | |||
| 88 | } | 97 | } |
| 89 | } | 98 | } |
| 90 | 99 | ||
| 91 | #[no_mangle] | ||
| 92 | fn _embassy_rand(buf: &mut [u8]) { | ||
| 93 | use rand_core::{OsRng, RngCore}; | ||
| 94 | OsRng.fill_bytes(buf); | ||
| 95 | } | ||
| 96 | |||
| 97 | static EXECUTOR: Forever<Executor> = Forever::new(); | 100 | static EXECUTOR: Forever<Executor> = Forever::new(); |
| 98 | 101 | ||
| 99 | fn main() { | 102 | fn main() { |
diff --git a/examples/std/src/tuntap.rs b/examples/std/src/tuntap.rs index 09a4be070..b70767a3a 100644 --- a/examples/std/src/tuntap.rs +++ b/examples/std/src/tuntap.rs | |||
| @@ -209,7 +209,7 @@ impl Device for TunTapDevice { | |||
| 209 | } | 209 | } |
| 210 | } | 210 | } |
| 211 | 211 | ||
| 212 | fn capabilities(&mut self) -> DeviceCapabilities { | 212 | fn capabilities(&self) -> DeviceCapabilities { |
| 213 | let mut caps = DeviceCapabilities::default(); | 213 | let mut caps = DeviceCapabilities::default(); |
| 214 | caps.max_transmission_unit = self.device.get_ref().mtu; | 214 | caps.max_transmission_unit = self.device.get_ref().mtu; |
| 215 | caps | 215 | caps |
diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index e68d1d58c..cd7394a5c 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml | |||
| @@ -8,7 +8,7 @@ resolver = "2" | |||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } | 9 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } | 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "net", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } |
| 11 | embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "medium-ethernet", "pool-16"] } | 11 | embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } |
| 12 | embedded-io = { version = "0.3.0", features = ["async"] } | 12 | embedded-io = { version = "0.3.0", features = ["async"] } |
| 13 | 13 | ||
| 14 | defmt = "0.3" | 14 | defmt = "0.3" |
| @@ -24,8 +24,3 @@ nb = "1.0.0" | |||
| 24 | rand_core = "0.6.3" | 24 | rand_core = "0.6.3" |
| 25 | critical-section = "0.2.3" | 25 | critical-section = "0.2.3" |
| 26 | embedded-storage = "0.3.0" | 26 | embedded-storage = "0.3.0" |
| 27 | |||
| 28 | [dependencies.smoltcp] | ||
| 29 | version = "0.8.0" | ||
| 30 | default-features = false | ||
| 31 | features = ["defmt"] | ||
diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index dca9338b2..af012f826 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs | |||
| @@ -2,130 +2,123 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use cortex_m_rt::entry; | ||
| 6 | use defmt::*; | 5 | use defmt::*; |
| 7 | use embassy::executor::{Executor, Spawner}; | 6 | use embassy::executor::Spawner; |
| 8 | use embassy::time::{Duration, Timer}; | 7 | use embassy::time::{Duration, Timer}; |
| 9 | use embassy::util::Forever; | 8 | use embassy::util::Forever; |
| 10 | use embassy_net::tcp::TcpSocket; | 9 | use embassy_net::tcp::TcpSocket; |
| 11 | use embassy_net::{Config as NetConfig, Ipv4Address, Ipv4Cidr, StackResources, StaticConfigurator}; | 10 | use embassy_net::{Ipv4Address, Stack, StackResources}; |
| 12 | use embassy_stm32::eth::generic_smi::GenericSMI; | 11 | use embassy_stm32::eth::generic_smi::GenericSMI; |
| 13 | use embassy_stm32::eth::{Ethernet, State}; | 12 | use embassy_stm32::eth::{Ethernet, State}; |
| 14 | use embassy_stm32::interrupt; | ||
| 15 | use embassy_stm32::peripherals::ETH; | 13 | use embassy_stm32::peripherals::ETH; |
| 16 | use embassy_stm32::peripherals::RNG; | ||
| 17 | use embassy_stm32::rng::Rng; | 14 | use embassy_stm32::rng::Rng; |
| 18 | use embassy_stm32::time::U32Ext; | 15 | use embassy_stm32::time::U32Ext; |
| 19 | use embassy_stm32::Config; | 16 | use embassy_stm32::Config; |
| 17 | use embassy_stm32::{interrupt, Peripherals}; | ||
| 20 | use embedded_io::asynch::Write; | 18 | use embedded_io::asynch::Write; |
| 21 | use heapless::Vec; | ||
| 22 | 19 | ||
| 23 | use defmt_rtt as _; // global logger | 20 | use defmt_rtt as _; // global logger |
| 24 | use panic_probe as _; | 21 | use panic_probe as _; |
| 25 | 22 | use rand_core::RngCore; | |
| 26 | #[embassy::task] | 23 | |
| 27 | async fn main_task( | 24 | macro_rules! forever { |
| 28 | device: &'static mut Ethernet<'static, ETH, GenericSMI, 4, 4>, | 25 | ($val:expr) => {{ |
| 29 | config: &'static mut StaticConfigurator, | 26 | type T = impl Sized; |
| 30 | spawner: Spawner, | 27 | static FOREVER: Forever<T> = Forever::new(); |
| 31 | ) { | 28 | FOREVER.put_with(move || $val) |
| 32 | let net_resources = NET_RESOURCES.put(StackResources::new()); | 29 | }}; |
| 33 | |||
| 34 | // Init network stack | ||
| 35 | embassy_net::init(device, config, net_resources); | ||
| 36 | |||
| 37 | // Launch network task | ||
| 38 | unwrap!(spawner.spawn(net_task())); | ||
| 39 | |||
| 40 | info!("Network task initialized"); | ||
| 41 | |||
| 42 | // Then we can use it! | ||
| 43 | let mut rx_buffer = [0; 1024]; | ||
| 44 | let mut tx_buffer = [0; 1024]; | ||
| 45 | let mut socket = TcpSocket::new(&mut rx_buffer, &mut tx_buffer); | ||
| 46 | |||
| 47 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); | ||
| 48 | |||
| 49 | let remote_endpoint = (Ipv4Address::new(192, 168, 0, 10), 8000); | ||
| 50 | let r = socket.connect(remote_endpoint).await; | ||
| 51 | if let Err(e) = r { | ||
| 52 | info!("connect error: {:?}", e); | ||
| 53 | return; | ||
| 54 | } | ||
| 55 | info!("connected!"); | ||
| 56 | loop { | ||
| 57 | let r = socket.write_all(b"Hello\n").await; | ||
| 58 | if let Err(e) = r { | ||
| 59 | info!("write error: {:?}", e); | ||
| 60 | return; | ||
| 61 | } | ||
| 62 | Timer::after(Duration::from_secs(1)).await; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | #[embassy::task] | ||
| 67 | async fn net_task() { | ||
| 68 | embassy_net::run().await | ||
| 69 | } | 30 | } |
| 70 | 31 | ||
| 71 | #[no_mangle] | 32 | type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; |
| 72 | fn _embassy_rand(buf: &mut [u8]) { | ||
| 73 | use rand_core::RngCore; | ||
| 74 | 33 | ||
| 75 | critical_section::with(|_| unsafe { | 34 | #[embassy::task] |
| 76 | unwrap!(RNG_INST.as_mut()).fill_bytes(buf); | 35 | async fn net_task(stack: &'static Stack<Device>) -> ! { |
| 77 | }); | 36 | stack.run().await |
| 78 | } | 37 | } |
| 79 | 38 | ||
| 80 | static mut RNG_INST: Option<Rng<RNG>> = None; | ||
| 81 | |||
| 82 | static EXECUTOR: Forever<Executor> = Forever::new(); | ||
| 83 | static STATE: Forever<State<'static, ETH, 4, 4>> = Forever::new(); | ||
| 84 | static ETH: Forever<Ethernet<'static, ETH, GenericSMI, 4, 4>> = Forever::new(); | ||
| 85 | static CONFIG: Forever<StaticConfigurator> = Forever::new(); | ||
| 86 | static NET_RESOURCES: Forever<StackResources<1, 2, 8>> = Forever::new(); | ||
| 87 | |||
| 88 | fn config() -> Config { | 39 | fn config() -> Config { |
| 89 | let mut config = Config::default(); | 40 | let mut config = Config::default(); |
| 90 | config.rcc.sys_ck = Some(200.mhz().into()); | 41 | config.rcc.sys_ck = Some(200.mhz().into()); |
| 91 | config | 42 | config |
| 92 | } | 43 | } |
| 93 | 44 | ||
| 94 | #[entry] | 45 | #[embassy::main(config = "config()")] |
| 95 | fn main() -> ! { | 46 | async fn main(spawner: Spawner, p: Peripherals) -> ! { |
| 96 | info!("Hello World!"); | 47 | info!("Hello World!"); |
| 97 | 48 | ||
| 98 | info!("Setup RCC..."); | 49 | // Generate random seed. |
| 99 | 50 | let mut rng = Rng::new(p.RNG); | |
| 100 | let p = embassy_stm32::init(config()); | 51 | let mut seed = [0; 8]; |
| 101 | 52 | rng.fill_bytes(&mut seed); | |
| 102 | let rng = Rng::new(p.RNG); | 53 | let seed = u64::from_le_bytes(seed); |
| 103 | unsafe { | ||
| 104 | RNG_INST.replace(rng); | ||
| 105 | } | ||
| 106 | 54 | ||
| 107 | let eth_int = interrupt::take!(ETH); | 55 | let eth_int = interrupt::take!(ETH); |
| 108 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; | 56 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; |
| 109 | let state = STATE.put(State::new()); | ||
| 110 | 57 | ||
| 111 | let eth = unsafe { | 58 | let device = unsafe { |
| 112 | ETH.put(Ethernet::new( | 59 | Ethernet::new( |
| 113 | state, p.ETH, eth_int, p.PA1, p.PA2, p.PC1, p.PA7, p.PC4, p.PC5, p.PG13, p.PB13, | 60 | forever!(State::new()), |
| 114 | p.PG11, GenericSMI, mac_addr, 0, | 61 | p.ETH, |
| 115 | )) | 62 | eth_int, |
| 63 | p.PA1, | ||
| 64 | p.PA2, | ||
| 65 | p.PC1, | ||
| 66 | p.PA7, | ||
| 67 | p.PC4, | ||
| 68 | p.PC5, | ||
| 69 | p.PG13, | ||
| 70 | p.PB13, | ||
| 71 | p.PG11, | ||
| 72 | GenericSMI, | ||
| 73 | mac_addr, | ||
| 74 | 0, | ||
| 75 | ) | ||
| 116 | }; | 76 | }; |
| 117 | 77 | ||
| 118 | let config = StaticConfigurator::new(NetConfig { | 78 | let config = embassy_net::ConfigStrategy::Dhcp; |
| 119 | address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 0, 61), 24), | 79 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { |
| 120 | dns_servers: Vec::new(), | 80 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), |
| 121 | gateway: Some(Ipv4Address::new(192, 168, 0, 1)), | 81 | // dns_servers: Vec::new(), |
| 122 | }); | 82 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), |
| 83 | //}); | ||
| 123 | 84 | ||
| 124 | let config = CONFIG.put(config); | 85 | // Init network stack |
| 86 | let stack = &*forever!(Stack::new( | ||
| 87 | device, | ||
| 88 | config, | ||
| 89 | forever!(StackResources::<1, 2, 8>::new()), | ||
| 90 | seed | ||
| 91 | )); | ||
| 125 | 92 | ||
| 126 | let executor = EXECUTOR.put(Executor::new()); | 93 | // Launch network task |
| 94 | unwrap!(spawner.spawn(net_task(&stack))); | ||
| 127 | 95 | ||
| 128 | executor.run(move |spawner| { | 96 | info!("Network task initialized"); |
| 129 | unwrap!(spawner.spawn(main_task(eth, config, spawner))); | 97 | |
| 130 | }) | 98 | // Then we can use it! |
| 99 | let mut rx_buffer = [0; 1024]; | ||
| 100 | let mut tx_buffer = [0; 1024]; | ||
| 101 | |||
| 102 | loop { | ||
| 103 | let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); | ||
| 104 | |||
| 105 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); | ||
| 106 | |||
| 107 | let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); | ||
| 108 | info!("connecting..."); | ||
| 109 | let r = socket.connect(remote_endpoint).await; | ||
| 110 | if let Err(e) = r { | ||
| 111 | info!("connect error: {:?}", e); | ||
| 112 | continue; | ||
| 113 | } | ||
| 114 | info!("connected!"); | ||
| 115 | loop { | ||
| 116 | let r = socket.write_all(b"Hello\n").await; | ||
| 117 | if let Err(e) = r { | ||
| 118 | info!("write error: {:?}", e); | ||
| 119 | return; | ||
| 120 | } | ||
| 121 | Timer::after(Duration::from_secs(1)).await; | ||
| 122 | } | ||
| 123 | } | ||
| 131 | } | 124 | } |
diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 8aa3b31e0..0f480f7a8 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml | |||
| @@ -8,7 +8,7 @@ resolver = "2" | |||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] } | 9 | embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] } |
| 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } | 10 | embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "net", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } |
| 11 | embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "medium-ethernet", "pool-16"] } | 11 | embassy-net = { path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } |
| 12 | embedded-io = { version = "0.3.0", features = ["async"] } | 12 | embedded-io = { version = "0.3.0", features = ["async"] } |
| 13 | 13 | ||
| 14 | defmt = "0.3" | 14 | defmt = "0.3" |
| @@ -28,11 +28,6 @@ micromath = "2.0.0" | |||
| 28 | stm32-fmc = "0.2.4" | 28 | stm32-fmc = "0.2.4" |
| 29 | embedded-storage = "0.3.0" | 29 | embedded-storage = "0.3.0" |
| 30 | 30 | ||
| 31 | [dependencies.smoltcp] | ||
| 32 | version = "0.8.0" | ||
| 33 | default-features = false | ||
| 34 | features = ["defmt"] | ||
| 35 | |||
| 36 | # cargo build/run | 31 | # cargo build/run |
| 37 | [profile.dev] | 32 | [profile.dev] |
| 38 | codegen-units = 1 | 33 | codegen-units = 1 |
diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 8ece29403..649ff2605 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs | |||
| @@ -2,90 +2,40 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use defmt_rtt as _; // global logger | ||
| 6 | use panic_probe as _; | ||
| 7 | |||
| 8 | use cortex_m_rt::entry; | ||
| 9 | use defmt::*; | 5 | use defmt::*; |
| 10 | use embassy::executor::{Executor, Spawner}; | 6 | use embassy::executor::Spawner; |
| 11 | use embassy::time::{Duration, Timer}; | 7 | use embassy::time::{Duration, Timer}; |
| 12 | use embassy::util::Forever; | 8 | use embassy::util::Forever; |
| 13 | use embassy_net::tcp::TcpSocket; | 9 | use embassy_net::tcp::TcpSocket; |
| 14 | use embassy_net::{Config as NetConfig, Ipv4Address, Ipv4Cidr, StackResources, StaticConfigurator}; | 10 | use embassy_net::{Ipv4Address, Stack, StackResources}; |
| 15 | use embassy_stm32::eth::generic_smi::GenericSMI; | 11 | use embassy_stm32::eth::generic_smi::GenericSMI; |
| 16 | use embassy_stm32::eth::{Ethernet, State}; | 12 | use embassy_stm32::eth::{Ethernet, State}; |
| 17 | use embassy_stm32::interrupt; | ||
| 18 | use embassy_stm32::peripherals::ETH; | 13 | use embassy_stm32::peripherals::ETH; |
| 19 | use embassy_stm32::peripherals::RNG; | ||
| 20 | use embassy_stm32::rng::Rng; | 14 | use embassy_stm32::rng::Rng; |
| 21 | use embassy_stm32::time::U32Ext; | 15 | use embassy_stm32::time::U32Ext; |
| 22 | use embassy_stm32::Config; | 16 | use embassy_stm32::Config; |
| 17 | use embassy_stm32::{interrupt, Peripherals}; | ||
| 23 | use embedded_io::asynch::Write; | 18 | use embedded_io::asynch::Write; |
| 24 | use heapless::Vec; | ||
| 25 | |||
| 26 | #[embassy::task] | ||
| 27 | async fn main_task( | ||
| 28 | device: &'static mut Ethernet<'static, ETH, GenericSMI, 4, 4>, | ||
| 29 | config: &'static mut StaticConfigurator, | ||
| 30 | spawner: Spawner, | ||
| 31 | ) { | ||
| 32 | let net_resources = NET_RESOURCES.put(StackResources::new()); | ||
| 33 | |||
| 34 | // Init network stack | ||
| 35 | embassy_net::init(device, config, net_resources); | ||
| 36 | |||
| 37 | // Launch network task | ||
| 38 | unwrap!(spawner.spawn(net_task())); | ||
| 39 | |||
| 40 | info!("Network task initialized"); | ||
| 41 | |||
| 42 | // Then we can use it! | ||
| 43 | let mut rx_buffer = [0; 1024]; | ||
| 44 | let mut tx_buffer = [0; 1024]; | ||
| 45 | let mut socket = TcpSocket::new(&mut rx_buffer, &mut tx_buffer); | ||
| 46 | |||
| 47 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); | ||
| 48 | |||
| 49 | let remote_endpoint = (Ipv4Address::new(192, 168, 0, 10), 8000); | ||
| 50 | let r = socket.connect(remote_endpoint).await; | ||
| 51 | if let Err(e) = r { | ||
| 52 | info!("connect error: {:?}", e); | ||
| 53 | return; | ||
| 54 | } | ||
| 55 | info!("connected!"); | ||
| 56 | loop { | ||
| 57 | let r = socket.write_all(b"Hello\n").await; | ||
| 58 | if let Err(e) = r { | ||
| 59 | info!("write error: {:?}", e); | ||
| 60 | return; | ||
| 61 | } | ||
| 62 | Timer::after(Duration::from_secs(1)).await; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | 19 | ||
| 66 | #[embassy::task] | 20 | use defmt_rtt as _; // global logger |
| 67 | async fn net_task() { | 21 | use panic_probe as _; |
| 68 | embassy_net::run().await | 22 | use rand_core::RngCore; |
| 23 | |||
| 24 | macro_rules! forever { | ||
| 25 | ($val:expr) => {{ | ||
| 26 | type T = impl Sized; | ||
| 27 | static FOREVER: Forever<T> = Forever::new(); | ||
| 28 | FOREVER.put_with(move || $val) | ||
| 29 | }}; | ||
| 69 | } | 30 | } |
| 70 | 31 | ||
| 71 | #[no_mangle] | 32 | type Device = Ethernet<'static, ETH, GenericSMI, 4, 4>; |
| 72 | fn _embassy_rand(buf: &mut [u8]) { | ||
| 73 | use rand_core::RngCore; | ||
| 74 | 33 | ||
| 75 | critical_section::with(|_| unsafe { | 34 | #[embassy::task] |
| 76 | unwrap!(RNG_INST.as_mut()).fill_bytes(buf); | 35 | async fn net_task(stack: &'static Stack<Device>) -> ! { |
| 77 | }); | 36 | stack.run().await |
| 78 | } | 37 | } |
| 79 | 38 | ||
| 80 | static mut RNG_INST: Option<Rng<RNG>> = None; | ||
| 81 | |||
| 82 | static EXECUTOR: Forever<Executor> = Forever::new(); | ||
| 83 | static STATE: Forever<State<'static, ETH, 4, 4>> = Forever::new(); | ||
| 84 | static ETH: Forever<Ethernet<'static, ETH, GenericSMI, 4, 4>> = Forever::new(); | ||
| 85 | static CONFIG: Forever<StaticConfigurator> = Forever::new(); | ||
| 86 | static NET_RESOURCES: Forever<StackResources<1, 2, 8>> = Forever::new(); | ||
| 87 | |||
| 88 | #[allow(unused)] | ||
| 89 | pub fn config() -> Config { | 39 | pub fn config() -> Config { |
| 90 | let mut config = Config::default(); | 40 | let mut config = Config::default(); |
| 91 | config.rcc.sys_ck = Some(400.mhz().into()); | 41 | config.rcc.sys_ck = Some(400.mhz().into()); |
| @@ -94,40 +44,83 @@ pub fn config() -> Config { | |||
| 94 | config | 44 | config |
| 95 | } | 45 | } |
| 96 | 46 | ||
| 97 | #[entry] | 47 | #[embassy::main(config = "config()")] |
| 98 | fn main() -> ! { | 48 | async fn main(spawner: Spawner, p: Peripherals) -> ! { |
| 99 | info!("Hello World!"); | 49 | info!("Hello World!"); |
| 100 | 50 | ||
| 101 | info!("Setup RCC..."); | 51 | // Generate random seed. |
| 102 | 52 | let mut rng = Rng::new(p.RNG); | |
| 103 | let p = embassy_stm32::init(config()); | 53 | let mut seed = [0; 8]; |
| 104 | 54 | rng.fill_bytes(&mut seed); | |
| 105 | let rng = Rng::new(p.RNG); | 55 | let seed = u64::from_le_bytes(seed); |
| 106 | unsafe { | ||
| 107 | RNG_INST.replace(rng); | ||
| 108 | } | ||
| 109 | 56 | ||
| 110 | let eth_int = interrupt::take!(ETH); | 57 | let eth_int = interrupt::take!(ETH); |
| 111 | let mac_addr = [0x10; 6]; | 58 | let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; |
| 112 | let state = STATE.put(State::new()); | 59 | |
| 113 | let eth = unsafe { | 60 | let device = unsafe { |
| 114 | ETH.put(Ethernet::new( | 61 | Ethernet::new( |
| 115 | state, p.ETH, eth_int, p.PA1, p.PA2, p.PC1, p.PA7, p.PC4, p.PC5, p.PG13, p.PB13, | 62 | forever!(State::new()), |
| 116 | p.PG11, GenericSMI, mac_addr, 0, | 63 | p.ETH, |
| 117 | )) | 64 | eth_int, |
| 65 | p.PA1, | ||
| 66 | p.PA2, | ||
| 67 | p.PC1, | ||
| 68 | p.PA7, | ||
| 69 | p.PC4, | ||
| 70 | p.PC5, | ||
| 71 | p.PG13, | ||
| 72 | p.PB13, | ||
| 73 | p.PG11, | ||
| 74 | GenericSMI, | ||
| 75 | mac_addr, | ||
| 76 | 0, | ||
| 77 | ) | ||
| 118 | }; | 78 | }; |
| 119 | 79 | ||
| 120 | let config = StaticConfigurator::new(NetConfig { | 80 | let config = embassy_net::ConfigStrategy::Dhcp; |
| 121 | address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 0, 61), 24), | 81 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { |
| 122 | dns_servers: Vec::new(), | 82 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), |
| 123 | gateway: Some(Ipv4Address::new(192, 168, 0, 1)), | 83 | // dns_servers: Vec::new(), |
| 124 | }); | 84 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), |
| 85 | //}); | ||
| 86 | |||
| 87 | // Init network stack | ||
| 88 | let stack = &*forever!(Stack::new( | ||
| 89 | device, | ||
| 90 | config, | ||
| 91 | forever!(StackResources::<1, 2, 8>::new()), | ||
| 92 | seed | ||
| 93 | )); | ||
| 125 | 94 | ||
| 126 | let config = CONFIG.put(config); | 95 | // Launch network task |
| 96 | unwrap!(spawner.spawn(net_task(&stack))); | ||
| 127 | 97 | ||
| 128 | let executor = EXECUTOR.put(Executor::new()); | 98 | info!("Network task initialized"); |
| 129 | 99 | ||
| 130 | executor.run(move |spawner| { | 100 | // Then we can use it! |
| 131 | unwrap!(spawner.spawn(main_task(eth, config, spawner))); | 101 | let mut rx_buffer = [0; 1024]; |
| 132 | }) | 102 | let mut tx_buffer = [0; 1024]; |
| 103 | |||
| 104 | loop { | ||
| 105 | let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); | ||
| 106 | |||
| 107 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); | ||
| 108 | |||
| 109 | let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); | ||
| 110 | info!("connecting..."); | ||
| 111 | let r = socket.connect(remote_endpoint).await; | ||
| 112 | if let Err(e) = r { | ||
| 113 | info!("connect error: {:?}", e); | ||
| 114 | continue; | ||
| 115 | } | ||
| 116 | info!("connected!"); | ||
| 117 | loop { | ||
| 118 | let r = socket.write_all(b"Hello\n").await; | ||
| 119 | if let Err(e) = r { | ||
| 120 | info!("write error: {:?}", e); | ||
| 121 | return; | ||
| 122 | } | ||
| 123 | Timer::after(Duration::from_secs(1)).await; | ||
| 124 | } | ||
| 125 | } | ||
| 133 | } | 126 | } |
