aboutsummaryrefslogtreecommitdiff
path: root/embassy-net/src
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-net/src')
-rw-r--r--embassy-net/src/config/dhcp.rs80
-rw-r--r--embassy-net/src/config/mod.rs34
-rw-r--r--embassy-net/src/config/statik.rs26
-rw-r--r--embassy-net/src/device.rs103
-rw-r--r--embassy-net/src/fmt.rs118
-rw-r--r--embassy-net/src/lib.rs31
-rw-r--r--embassy-net/src/packet_pool.rs88
-rw-r--r--embassy-net/src/pool.rs245
-rw-r--r--embassy-net/src/stack.rs212
-rw-r--r--embassy-net/src/tcp_socket.rs178
10 files changed, 1115 insertions, 0 deletions
diff --git a/embassy-net/src/config/dhcp.rs b/embassy-net/src/config/dhcp.rs
new file mode 100644
index 000000000..f5d598bdf
--- /dev/null
+++ b/embassy-net/src/config/dhcp.rs
@@ -0,0 +1,80 @@
1use embassy::util::Forever;
2use heapless::consts::*;
3use heapless::Vec;
4use smoltcp::dhcp::Dhcpv4Client;
5use smoltcp::socket::{RawPacketMetadata, RawSocketBuffer};
6use smoltcp::time::Instant;
7use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
8
9use super::*;
10use crate::{device::LinkState, fmt::*};
11use crate::{Interface, SocketSet};
12
13pub struct DhcpResources {
14 rx_buffer: [u8; 900],
15 tx_buffer: [u8; 600],
16 rx_meta: [RawPacketMetadata; 1],
17 tx_meta: [RawPacketMetadata; 1],
18}
19
20pub struct DhcpConfigurator {
21 client: Option<Dhcpv4Client>,
22}
23
24impl DhcpConfigurator {
25 pub fn new() -> Self {
26 Self { client: None }
27 }
28}
29
30static DHCP_RESOURCES: Forever<DhcpResources> = Forever::new();
31
32impl Configurator for DhcpConfigurator {
33 fn poll(
34 &mut self,
35 iface: &mut Interface,
36 sockets: &mut SocketSet,
37 timestamp: Instant,
38 ) -> Option<Config> {
39 if self.client.is_none() {
40 let res = DHCP_RESOURCES.put(DhcpResources {
41 rx_buffer: [0; 900],
42 tx_buffer: [0; 600],
43 rx_meta: [RawPacketMetadata::EMPTY; 1],
44 tx_meta: [RawPacketMetadata::EMPTY; 1],
45 });
46 let rx_buffer = RawSocketBuffer::new(&mut res.rx_meta[..], &mut res.rx_buffer[..]);
47 let tx_buffer = RawSocketBuffer::new(&mut res.tx_meta[..], &mut res.tx_buffer[..]);
48 let dhcp = Dhcpv4Client::new(sockets, rx_buffer, tx_buffer, timestamp);
49 info!("created dhcp");
50 self.client = Some(dhcp)
51 }
52
53 let client = self.client.as_mut().unwrap();
54
55 let link_up = iface.device_mut().device.link_state() == LinkState::Up;
56 if !link_up {
57 client.reset(timestamp);
58 return Some(Config::Down);
59 }
60
61 let config = client.poll(iface, sockets, timestamp).unwrap_or(None)?;
62
63 if config.address.is_none() {
64 return Some(Config::Down);
65 }
66
67 let mut dns_servers = Vec::new();
68 for s in &config.dns_servers {
69 if let Some(addr) = s {
70 dns_servers.push(addr.clone()).unwrap();
71 }
72 }
73
74 return Some(Config::Up(UpConfig {
75 address: config.address.unwrap(),
76 gateway: config.router.unwrap_or(Ipv4Address::UNSPECIFIED),
77 dns_servers,
78 }));
79 }
80}
diff --git a/embassy-net/src/config/mod.rs b/embassy-net/src/config/mod.rs
new file mode 100644
index 000000000..596374f9e
--- /dev/null
+++ b/embassy-net/src/config/mod.rs
@@ -0,0 +1,34 @@
1use heapless::consts::*;
2use heapless::Vec;
3use smoltcp::time::Instant;
4use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
5
6use crate::fmt::*;
7use crate::{Interface, SocketSet};
8
9mod dhcp;
10mod statik;
11pub use dhcp::DhcpConfigurator;
12pub use statik::StaticConfigurator;
13
14#[derive(Debug, Clone)]
15pub enum Config {
16 Down,
17 Up(UpConfig),
18}
19
20#[derive(Debug, Clone)]
21pub struct UpConfig {
22 pub address: Ipv4Cidr,
23 pub gateway: Ipv4Address,
24 pub dns_servers: Vec<Ipv4Address, U3>,
25}
26
27pub trait Configurator {
28 fn poll(
29 &mut self,
30 iface: &mut Interface,
31 sockets: &mut SocketSet,
32 timestamp: Instant,
33 ) -> Option<Config>;
34}
diff --git a/embassy-net/src/config/statik.rs b/embassy-net/src/config/statik.rs
new file mode 100644
index 000000000..52196f48a
--- /dev/null
+++ b/embassy-net/src/config/statik.rs
@@ -0,0 +1,26 @@
1use smoltcp::time::Instant;
2
3use super::*;
4use crate::fmt::*;
5use crate::{Interface, SocketSet};
6
7pub struct StaticConfigurator {
8 config: UpConfig,
9}
10
11impl StaticConfigurator {
12 pub fn new(config: UpConfig) -> Self {
13 Self { config }
14 }
15}
16
17impl Configurator for StaticConfigurator {
18 fn poll(
19 &mut self,
20 _iface: &mut Interface,
21 _sockets: &mut SocketSet,
22 _timestamp: Instant,
23 ) -> Option<Config> {
24 Some(Config::Up(self.config.clone()))
25 }
26}
diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs
new file mode 100644
index 000000000..95a62e792
--- /dev/null
+++ b/embassy-net/src/device.rs
@@ -0,0 +1,103 @@
1use core::task::{Poll, Waker};
2use smoltcp::phy::Device as SmolDevice;
3use smoltcp::phy::DeviceCapabilities;
4use smoltcp::time::Instant as SmolInstant;
5use smoltcp::Result;
6
7use crate::fmt::*;
8use crate::{Packet, PacketBox, PacketBuf};
9
10#[derive(PartialEq, Eq, Clone, Copy)]
11pub enum LinkState {
12 Down,
13 Up,
14}
15
16pub trait Device {
17 fn is_transmit_ready(&mut self) -> bool;
18 fn transmit(&mut self, pkt: PacketBuf);
19 fn receive(&mut self) -> Option<PacketBuf>;
20
21 fn register_waker(&mut self, waker: &Waker);
22 fn capabilities(&mut self) -> DeviceCapabilities;
23 fn link_state(&mut self) -> LinkState;
24}
25
26pub struct DeviceAdapter {
27 pub device: &'static mut dyn Device,
28 caps: DeviceCapabilities,
29}
30
31impl DeviceAdapter {
32 pub(crate) fn new(device: &'static mut dyn Device) -> Self {
33 Self {
34 caps: device.capabilities(),
35 device,
36 }
37 }
38}
39
40impl<'a> SmolDevice<'a> for DeviceAdapter {
41 type RxToken = RxToken;
42 type TxToken = TxToken<'a>;
43
44 fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
45 let rx_pkt = self.device.receive()?;
46 let tx_pkt = PacketBox::new(Packet::new()).unwrap(); // TODO: not sure about unwrap
47 let rx_token = RxToken { pkt: rx_pkt };
48 let tx_token = TxToken {
49 device: self.device,
50 pkt: tx_pkt,
51 };
52
53 Some((rx_token, tx_token))
54 }
55
56 /// Construct a transmit token.
57 fn transmit(&'a mut self) -> Option<Self::TxToken> {
58 if !self.device.is_transmit_ready() {
59 return None;
60 }
61
62 let tx_pkt = PacketBox::new(Packet::new())?;
63 Some(TxToken {
64 device: self.device,
65 pkt: tx_pkt,
66 })
67 }
68
69 /// Get a description of device capabilities.
70 fn capabilities(&self) -> DeviceCapabilities {
71 self.caps.clone()
72 }
73}
74
75pub struct RxToken {
76 pkt: PacketBuf,
77}
78
79impl smoltcp::phy::RxToken for RxToken {
80 fn consume<R, F>(mut self, _timestamp: SmolInstant, f: F) -> Result<R>
81 where
82 F: FnOnce(&mut [u8]) -> Result<R>,
83 {
84 f(&mut self.pkt)
85 }
86}
87
88pub struct TxToken<'a> {
89 device: &'a mut dyn Device,
90 pkt: PacketBox,
91}
92
93impl<'a> smoltcp::phy::TxToken for TxToken<'a> {
94 fn consume<R, F>(mut self, _timestamp: SmolInstant, len: usize, f: F) -> Result<R>
95 where
96 F: FnOnce(&mut [u8]) -> Result<R>,
97 {
98 let mut buf = self.pkt.slice(0..len);
99 let r = f(&mut buf)?;
100 self.device.transmit(buf);
101 Ok(r)
102 }
103}
diff --git a/embassy-net/src/fmt.rs b/embassy-net/src/fmt.rs
new file mode 100644
index 000000000..4da69766c
--- /dev/null
+++ b/embassy-net/src/fmt.rs
@@ -0,0 +1,118 @@
1#![macro_use]
2
3#[cfg(all(feature = "defmt", feature = "log"))]
4compile_error!("You may not enable both `defmt` and `log` features.");
5
6pub use fmt::*;
7
8#[cfg(feature = "defmt")]
9mod fmt {
10 pub use defmt::{
11 assert, assert_eq, assert_ne, debug, debug_assert, debug_assert_eq, debug_assert_ne, error,
12 info, panic, todo, trace, unreachable, unwrap, warn,
13 };
14}
15
16#[cfg(feature = "log")]
17mod fmt {
18 pub use core::{
19 assert, assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, panic, todo,
20 unreachable,
21 };
22 pub use log::{debug, error, info, trace, warn};
23}
24
25#[cfg(not(any(feature = "defmt", feature = "log")))]
26mod fmt {
27 #![macro_use]
28
29 pub use core::{
30 assert, assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, panic, todo,
31 unreachable,
32 };
33
34 #[macro_export]
35 macro_rules! trace {
36 ($($msg:expr),+ $(,)?) => {
37 ()
38 };
39 }
40
41 #[macro_export]
42 macro_rules! debug {
43 ($($msg:expr),+ $(,)?) => {
44 ()
45 };
46 }
47
48 #[macro_export]
49 macro_rules! info {
50 ($($msg:expr),+ $(,)?) => {
51 ()
52 };
53 }
54
55 #[macro_export]
56 macro_rules! warn {
57 ($($msg:expr),+ $(,)?) => {
58 ()
59 };
60 }
61
62 #[macro_export]
63 macro_rules! error {
64 ($($msg:expr),+ $(,)?) => {
65 ()
66 };
67 }
68}
69
70#[cfg(not(feature = "defmt"))]
71#[macro_export]
72macro_rules! unwrap {
73 ($arg:expr) => {
74 match $crate::fmt::Try::into_result($arg) {
75 ::core::result::Result::Ok(t) => t,
76 ::core::result::Result::Err(e) => {
77 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
78 }
79 }
80 };
81 ($arg:expr, $($msg:expr),+ $(,)? ) => {
82 match $crate::fmt::Try::into_result($arg) {
83 ::core::result::Result::Ok(t) => t,
84 ::core::result::Result::Err(e) => {
85 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
86 }
87 }
88 }
89}
90
91#[derive(Debug, Copy, Clone, Eq, PartialEq)]
92pub struct NoneError;
93
94pub trait Try {
95 type Ok;
96 type Error;
97 fn into_result(self) -> Result<Self::Ok, Self::Error>;
98}
99
100impl<T> Try for Option<T> {
101 type Ok = T;
102 type Error = NoneError;
103
104 #[inline]
105 fn into_result(self) -> Result<T, NoneError> {
106 self.ok_or(NoneError)
107 }
108}
109
110impl<T, E> Try for Result<T, E> {
111 type Ok = T;
112 type Error = E;
113
114 #[inline]
115 fn into_result(self) -> Self {
116 self
117 }
118}
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs
new file mode 100644
index 000000000..a2a320adf
--- /dev/null
+++ b/embassy-net/src/lib.rs
@@ -0,0 +1,31 @@
1#![cfg_attr(not(feature = "std"), no_std)]
2#![feature(const_fn)]
3#![feature(const_in_array_repeat_expressions)]
4#![feature(const_generics)]
5#![feature(const_evaluatable_checked)]
6#![allow(incomplete_features)]
7
8// This mod MUST go first, so that the others see its macros.
9pub(crate) mod fmt;
10
11mod pool; // TODO extract to embassy, or to own crate
12
13mod config;
14mod device;
15mod packet_pool;
16mod stack;
17mod tcp_socket;
18
19pub use config::{Config, Configurator, DhcpConfigurator, StaticConfigurator, UpConfig};
20pub use device::{Device, LinkState};
21pub use packet_pool::{Packet, PacketBox, PacketBuf};
22pub use stack::{init, is_init, run};
23pub use tcp_socket::TcpSocket;
24
25// smoltcp reexports
26pub use smoltcp::phy::{DeviceCapabilities, Medium};
27pub use smoltcp::time::Duration as SmolDuration;
28pub use smoltcp::time::Instant as SmolInstant;
29pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr};
30pub type Interface = smoltcp::iface::Interface<'static, device::DeviceAdapter>;
31pub type SocketSet = smoltcp::socket::SocketSet<'static>;
diff --git a/embassy-net/src/packet_pool.rs b/embassy-net/src/packet_pool.rs
new file mode 100644
index 000000000..246356431
--- /dev/null
+++ b/embassy-net/src/packet_pool.rs
@@ -0,0 +1,88 @@
1use as_slice::{AsMutSlice, AsSlice};
2use core::ops::{Deref, DerefMut, Range};
3
4use super::pool::{BitPool, Box, StaticPool};
5
6pub const MTU: usize = 1514;
7pub const PACKET_POOL_SIZE: usize = 4;
8
9pool!(pub PacketPool: [Packet; PACKET_POOL_SIZE]);
10pub type PacketBox = Box<PacketPool>;
11
12pub struct Packet(pub [u8; MTU]);
13
14impl Packet {
15 pub const fn new() -> Self {
16 Self([0; MTU])
17 }
18}
19
20impl Box<PacketPool> {
21 pub fn slice(self, range: Range<usize>) -> PacketBuf {
22 PacketBuf {
23 packet: self,
24 range,
25 }
26 }
27}
28
29impl AsSlice for Packet {
30 type Element = u8;
31
32 fn as_slice(&self) -> &[Self::Element] {
33 &self.deref()[..]
34 }
35}
36
37impl AsMutSlice for Packet {
38 fn as_mut_slice(&mut self) -> &mut [Self::Element] {
39 &mut self.deref_mut()[..]
40 }
41}
42
43impl Deref for Packet {
44 type Target = [u8; MTU];
45
46 fn deref(&self) -> &[u8; MTU] {
47 &self.0
48 }
49}
50
51impl DerefMut for Packet {
52 fn deref_mut(&mut self) -> &mut [u8; MTU] {
53 &mut self.0
54 }
55}
56
57pub struct PacketBuf {
58 packet: PacketBox,
59 range: Range<usize>,
60}
61
62impl AsSlice for PacketBuf {
63 type Element = u8;
64
65 fn as_slice(&self) -> &[Self::Element] {
66 &self.packet[self.range.clone()]
67 }
68}
69
70impl AsMutSlice for PacketBuf {
71 fn as_mut_slice(&mut self) -> &mut [Self::Element] {
72 &mut self.packet[self.range.clone()]
73 }
74}
75
76impl Deref for PacketBuf {
77 type Target = [u8];
78
79 fn deref(&self) -> &[u8] {
80 &self.packet[self.range.clone()]
81 }
82}
83
84impl DerefMut for PacketBuf {
85 fn deref_mut(&mut self) -> &mut [u8] {
86 &mut self.packet[self.range.clone()]
87 }
88}
diff --git a/embassy-net/src/pool.rs b/embassy-net/src/pool.rs
new file mode 100644
index 000000000..3ab36e4cc
--- /dev/null
+++ b/embassy-net/src/pool.rs
@@ -0,0 +1,245 @@
1#![macro_use]
2
3use as_slice::{AsMutSlice, AsSlice};
4use core::cmp;
5use core::fmt;
6use core::hash::{Hash, Hasher};
7use core::mem::MaybeUninit;
8use core::ops::{Deref, DerefMut};
9use core::sync::atomic::{AtomicU32, Ordering};
10
11use crate::fmt::{assert, *};
12
13struct AtomicBitset<const N: usize>
14where
15 [AtomicU32; (N + 31) / 32]: Sized,
16{
17 used: [AtomicU32; (N + 31) / 32],
18}
19
20impl<const N: usize> AtomicBitset<N>
21where
22 [AtomicU32; (N + 31) / 32]: Sized,
23{
24 const fn new() -> Self {
25 const Z: AtomicU32 = AtomicU32::new(0);
26 Self {
27 used: [Z; (N + 31) / 32],
28 }
29 }
30
31 fn alloc(&self) -> Option<usize> {
32 for (i, val) in self.used.iter().enumerate() {
33 let res = val.fetch_update(Ordering::AcqRel, Ordering::Acquire, |val| {
34 let n = val.trailing_ones() as usize + i * 32;
35 if n >= N {
36 None
37 } else {
38 Some(val | (1 << n))
39 }
40 });
41 if let Ok(val) = res {
42 let n = val.trailing_ones() as usize + i * 32;
43 return Some(n);
44 }
45 }
46 None
47 }
48 fn free(&self, i: usize) {
49 assert!(i < N);
50 self.used[i / 32].fetch_and(!(1 << ((i % 32) as u32)), Ordering::AcqRel);
51 }
52}
53
54pub trait Pool<T> {
55 fn alloc(&self) -> Option<*mut T>;
56 unsafe fn free(&self, p: *mut T);
57}
58
59pub struct BitPool<T, const N: usize>
60where
61 [AtomicU32; (N + 31) / 32]: Sized,
62{
63 used: AtomicBitset<N>,
64 data: MaybeUninit<[T; N]>,
65}
66
67impl<T, const N: usize> BitPool<T, N>
68where
69 [AtomicU32; (N + 31) / 32]: Sized,
70{
71 pub const fn new() -> Self {
72 Self {
73 used: AtomicBitset::new(),
74 data: MaybeUninit::uninit(),
75 }
76 }
77}
78
79impl<T, const N: usize> Pool<T> for BitPool<T, N>
80where
81 [AtomicU32; (N + 31) / 32]: Sized,
82{
83 fn alloc(&self) -> Option<*mut T> {
84 let n = self.used.alloc()?;
85 let origin = self.data.as_ptr() as *mut T;
86 Some(unsafe { origin.add(n) })
87 }
88
89 /// safety: p must be a pointer obtained from self.alloc that hasn't been freed yet.
90 unsafe fn free(&self, p: *mut T) {
91 let origin = self.data.as_ptr() as *mut T;
92 let n = p.offset_from(origin);
93 assert!(n >= 0);
94 assert!((n as usize) < N);
95 self.used.free(n as usize);
96 }
97}
98
99pub trait StaticPool: 'static {
100 type Item: 'static;
101 type Pool: Pool<Self::Item>;
102 fn get() -> &'static Self::Pool;
103}
104
105pub struct Box<P: StaticPool> {
106 ptr: *mut P::Item,
107}
108
109impl<P: StaticPool> Box<P> {
110 pub fn new(item: P::Item) -> Option<Self> {
111 let p = match P::get().alloc() {
112 Some(p) => p,
113 None => {
114 warn!("alloc failed!");
115 return None;
116 }
117 };
118 //trace!("allocated {:u32}", p as u32);
119 unsafe { p.write(item) };
120 Some(Self { ptr: p })
121 }
122}
123
124impl<P: StaticPool> Drop for Box<P> {
125 fn drop(&mut self) {
126 unsafe {
127 //trace!("dropping {:u32}", self.ptr as u32);
128 self.ptr.drop_in_place();
129 P::get().free(self.ptr);
130 };
131 }
132}
133
134unsafe impl<P: StaticPool> Send for Box<P> where P::Item: Send {}
135
136unsafe impl<P: StaticPool> Sync for Box<P> where P::Item: Sync {}
137
138unsafe impl<P: StaticPool> stable_deref_trait::StableDeref for Box<P> {}
139
140impl<P: StaticPool> AsSlice for Box<P>
141where
142 P::Item: AsSlice,
143{
144 type Element = <P::Item as AsSlice>::Element;
145
146 fn as_slice(&self) -> &[Self::Element] {
147 self.deref().as_slice()
148 }
149}
150
151impl<P: StaticPool> AsMutSlice for Box<P>
152where
153 P::Item: AsMutSlice,
154{
155 fn as_mut_slice(&mut self) -> &mut [Self::Element] {
156 self.deref_mut().as_mut_slice()
157 }
158}
159
160impl<P: StaticPool> Deref for Box<P> {
161 type Target = P::Item;
162
163 fn deref(&self) -> &P::Item {
164 unsafe { &*self.ptr }
165 }
166}
167
168impl<P: StaticPool> DerefMut for Box<P> {
169 fn deref_mut(&mut self) -> &mut P::Item {
170 unsafe { &mut *self.ptr }
171 }
172}
173
174impl<P: StaticPool> fmt::Debug for Box<P>
175where
176 P::Item: fmt::Debug,
177{
178 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179 <P::Item as fmt::Debug>::fmt(self, f)
180 }
181}
182
183impl<P: StaticPool> fmt::Display for Box<P>
184where
185 P::Item: fmt::Display,
186{
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 <P::Item as fmt::Display>::fmt(self, f)
189 }
190}
191
192impl<P: StaticPool> PartialEq for Box<P>
193where
194 P::Item: PartialEq,
195{
196 fn eq(&self, rhs: &Box<P>) -> bool {
197 <P::Item as PartialEq>::eq(self, rhs)
198 }
199}
200
201impl<P: StaticPool> Eq for Box<P> where P::Item: Eq {}
202
203impl<P: StaticPool> PartialOrd for Box<P>
204where
205 P::Item: PartialOrd,
206{
207 fn partial_cmp(&self, rhs: &Box<P>) -> Option<cmp::Ordering> {
208 <P::Item as PartialOrd>::partial_cmp(self, rhs)
209 }
210}
211
212impl<P: StaticPool> Ord for Box<P>
213where
214 P::Item: Ord,
215{
216 fn cmp(&self, rhs: &Box<P>) -> cmp::Ordering {
217 <P::Item as Ord>::cmp(self, rhs)
218 }
219}
220
221impl<P: StaticPool> Hash for Box<P>
222where
223 P::Item: Hash,
224{
225 fn hash<H>(&self, state: &mut H)
226 where
227 H: Hasher,
228 {
229 <P::Item as Hash>::hash(self, state)
230 }
231}
232
233macro_rules! pool {
234 ($vis:vis $name:ident: [$ty:ty; $size:expr]) => {
235 $vis struct $name;
236 impl StaticPool for $name {
237 type Item = $ty;
238 type Pool = BitPool<$ty, $size>;
239 fn get() -> &'static Self::Pool {
240 static POOL: BitPool<$ty, $size> = BitPool::new();
241 &POOL
242 }
243 }
244 };
245}
diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs
new file mode 100644
index 000000000..c353f1bb1
--- /dev/null
+++ b/embassy-net/src/stack.rs
@@ -0,0 +1,212 @@
1use core::future::Future;
2use core::task::Context;
3use core::task::Poll;
4use core::{cell::RefCell, future};
5use embassy::time::{Instant, Timer};
6use embassy::util::ThreadModeMutex;
7use embassy::util::{Forever, WakerRegistration};
8use futures::pin_mut;
9use smoltcp::iface::{InterfaceBuilder, Neighbor, NeighborCache, Route, Routes};
10use smoltcp::phy::Device as _;
11use smoltcp::phy::Medium;
12use smoltcp::socket::SocketSetItem;
13use smoltcp::time::Instant as SmolInstant;
14use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address};
15
16use crate::device::{Device, DeviceAdapter};
17use crate::fmt::*;
18use crate::{
19 config::{Config, Configurator},
20 device::LinkState,
21};
22use crate::{Interface, SocketSet};
23
24const ADDRESSES_LEN: usize = 1;
25const NEIGHBOR_CACHE_LEN: usize = 8;
26const SOCKETS_LEN: usize = 2;
27const LOCAL_PORT_MIN: u16 = 1025;
28const LOCAL_PORT_MAX: u16 = 65535;
29
30struct StackResources {
31 addresses: [IpCidr; ADDRESSES_LEN],
32 neighbor_cache: [Option<(IpAddress, Neighbor)>; NEIGHBOR_CACHE_LEN],
33 sockets: [Option<SocketSetItem<'static>>; SOCKETS_LEN],
34 routes: [Option<(IpCidr, Route)>; 1],
35}
36
37static STACK_RESOURCES: Forever<StackResources> = Forever::new();
38static STACK: ThreadModeMutex<RefCell<Option<Stack>>> = ThreadModeMutex::new(RefCell::new(None));
39
40pub(crate) struct Stack {
41 iface: Interface,
42 pub sockets: SocketSet,
43 link_up: bool,
44 next_local_port: u16,
45 configurator: &'static mut dyn Configurator,
46 waker: WakerRegistration,
47}
48
49impl Stack {
50 pub(crate) fn with<R>(f: impl FnOnce(&mut Stack) -> R) -> R {
51 let mut stack = STACK.borrow().borrow_mut();
52 let stack = stack.as_mut().unwrap();
53 f(stack)
54 }
55
56 pub fn get_local_port(&mut self) -> u16 {
57 let res = self.next_local_port;
58 self.next_local_port = if res >= LOCAL_PORT_MAX {
59 LOCAL_PORT_MIN
60 } else {
61 res + 1
62 };
63 res
64 }
65
66 pub(crate) fn wake(&mut self) {
67 self.waker.wake()
68 }
69
70 fn poll_configurator(&mut self, timestamp: SmolInstant) {
71 if let Some(config) = self
72 .configurator
73 .poll(&mut self.iface, &mut self.sockets, timestamp)
74 {
75 let medium = self.iface.device().capabilities().medium;
76
77 let (addr, gateway) = match config {
78 Config::Up(config) => (config.address.into(), Some(config.gateway)),
79 Config::Down => (IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32), None),
80 };
81
82 self.iface.update_ip_addrs(|addrs| {
83 let curr_addr = &mut addrs[0];
84 if *curr_addr != addr {
85 info!("IPv4 address: {:?} -> {:?}", *curr_addr, addr);
86 *curr_addr = addr;
87 }
88 });
89
90 if medium == Medium::Ethernet {
91 self.iface.routes_mut().update(|r| {
92 let cidr = IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0);
93 let curr_gateway = r.get(&cidr).map(|r| r.via_router);
94
95 if curr_gateway != gateway.map(|a| a.into()) {
96 info!("IPv4 gateway: {:?} -> {:?}", curr_gateway, gateway);
97 if let Some(gateway) = gateway {
98 r.insert(cidr, Route::new_ipv4_gateway(gateway)).unwrap();
99 } else {
100 r.remove(&cidr);
101 }
102 }
103 });
104 }
105 }
106 }
107
108 fn poll(&mut self, cx: &mut Context<'_>) {
109 self.iface.device_mut().device.register_waker(cx.waker());
110 self.waker.register(cx.waker());
111
112 let timestamp = instant_to_smoltcp(Instant::now());
113 if let Err(e) = self.iface.poll(&mut self.sockets, timestamp) {
114 // If poll() returns error, it may not be done yet, so poll again later.
115 cx.waker().wake_by_ref();
116 return;
117 }
118
119 // Update link up
120 let old_link_up = self.link_up;
121 self.link_up = self.iface.device_mut().device.link_state() == LinkState::Up;
122
123 // Print when changed
124 if old_link_up != self.link_up {
125 if self.link_up {
126 info!("Link up!");
127 } else {
128 info!("Link down!");
129 }
130 }
131
132 if old_link_up || self.link_up {
133 self.poll_configurator(timestamp)
134 }
135
136 if let Some(poll_at) = self.iface.poll_at(&mut self.sockets, timestamp) {
137 let t = Timer::at(instant_from_smoltcp(poll_at));
138 pin_mut!(t);
139 if t.poll(cx).is_ready() {
140 cx.waker().wake_by_ref();
141 }
142 }
143 }
144}
145
146/// Initialize embassy_net.
147/// This function must be called from thread mode.
148pub fn init(device: &'static mut dyn Device, configurator: &'static mut dyn Configurator) {
149 let res = STACK_RESOURCES.put(StackResources {
150 addresses: [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32)],
151 neighbor_cache: [None; NEIGHBOR_CACHE_LEN],
152 sockets: [None; SOCKETS_LEN],
153 routes: [None; 1],
154 });
155
156 let ethernet_addr = EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]);
157
158 let medium = device.capabilities().medium;
159
160 let mut b = InterfaceBuilder::new(DeviceAdapter::new(device));
161 b = b.ip_addrs(&mut res.addresses[..]);
162
163 if medium == Medium::Ethernet {
164 b = b.ethernet_addr(ethernet_addr);
165 b = b.neighbor_cache(NeighborCache::new(&mut res.neighbor_cache[..]));
166 b = b.routes(Routes::new(&mut res.routes[..]));
167 }
168
169 let iface = b.finalize();
170
171 let sockets = SocketSet::new(&mut res.sockets[..]);
172
173 let local_port = loop {
174 let mut res = [0u8; 2];
175 embassy::rand::rand(&mut res);
176 let port = u16::from_le_bytes(res);
177 if port >= LOCAL_PORT_MIN && port <= LOCAL_PORT_MAX {
178 break port;
179 }
180 };
181
182 let stack = Stack {
183 iface,
184 sockets,
185 link_up: false,
186 configurator,
187 next_local_port: local_port,
188 waker: WakerRegistration::new(),
189 };
190
191 *STACK.borrow().borrow_mut() = Some(stack);
192}
193
194pub fn is_init() -> bool {
195 STACK.borrow().borrow().is_some()
196}
197
198pub async fn run() {
199 futures::future::poll_fn(|cx| {
200 Stack::with(|stack| stack.poll(cx));
201 Poll::<()>::Pending
202 })
203 .await
204}
205
206fn instant_to_smoltcp(instant: Instant) -> SmolInstant {
207 SmolInstant::from_millis(instant.as_millis() as i64)
208}
209
210fn instant_from_smoltcp(instant: SmolInstant) -> Instant {
211 Instant::from_millis(instant.total_millis() as u64)
212}
diff --git a/embassy-net/src/tcp_socket.rs b/embassy-net/src/tcp_socket.rs
new file mode 100644
index 000000000..7f4eb014c
--- /dev/null
+++ b/embassy-net/src/tcp_socket.rs
@@ -0,0 +1,178 @@
1use core::marker::PhantomData;
2use core::mem;
3use core::pin::Pin;
4use core::task::{Context, Poll};
5use embassy::io;
6use embassy::io::{AsyncBufRead, AsyncWrite};
7use smoltcp::socket::SocketHandle;
8use smoltcp::socket::TcpSocket as SyncTcpSocket;
9use smoltcp::socket::{TcpSocketBuffer, TcpState};
10use smoltcp::time::Duration;
11use smoltcp::wire::IpEndpoint;
12use smoltcp::{Error, Result};
13
14use super::stack::Stack;
15use crate::fmt::*;
16
17pub struct TcpSocket<'a> {
18 handle: SocketHandle,
19 ghost: PhantomData<&'a mut [u8]>,
20}
21
22impl<'a> Unpin for TcpSocket<'a> {}
23
24impl<'a> TcpSocket<'a> {
25 pub fn new(rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self {
26 let handle = Stack::with(|stack| {
27 let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) };
28 let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) };
29 stack.sockets.add(SyncTcpSocket::new(
30 TcpSocketBuffer::new(rx_buffer),
31 TcpSocketBuffer::new(tx_buffer),
32 ))
33 });
34
35 Self {
36 handle,
37 ghost: PhantomData,
38 }
39 }
40
41 pub async fn connect<T>(&mut self, remote_endpoint: T) -> Result<()>
42 where
43 T: Into<IpEndpoint>,
44 {
45 let local_port = Stack::with(|stack| stack.get_local_port());
46 self.with(|s| s.connect(remote_endpoint, local_port))?;
47
48 futures::future::poll_fn(|cx| {
49 self.with(|s| match s.state() {
50 TcpState::Closed | TcpState::TimeWait => Poll::Ready(Err(Error::Unaddressable)),
51 TcpState::Listen => Poll::Ready(Err(Error::Illegal)),
52 TcpState::SynSent | TcpState::SynReceived => {
53 s.register_send_waker(cx.waker());
54 Poll::Pending
55 }
56 _ => Poll::Ready(Ok(())),
57 })
58 })
59 .await
60 }
61
62 pub fn set_timeout(&mut self, duration: Option<Duration>) {
63 self.with(|s| s.set_timeout(duration))
64 }
65
66 pub fn set_keep_alive(&mut self, interval: Option<Duration>) {
67 self.with(|s| s.set_keep_alive(interval))
68 }
69
70 pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) {
71 self.with(|s| s.set_hop_limit(hop_limit))
72 }
73
74 pub fn local_endpoint(&self) -> IpEndpoint {
75 self.with(|s| s.local_endpoint())
76 }
77
78 pub fn remote_endpoint(&self) -> IpEndpoint {
79 self.with(|s| s.remote_endpoint())
80 }
81
82 pub fn state(&self) -> TcpState {
83 self.with(|s| s.state())
84 }
85
86 pub fn close(&mut self) {
87 self.with(|s| s.close())
88 }
89
90 pub fn abort(&mut self) {
91 self.with(|s| s.abort())
92 }
93
94 pub fn may_send(&self) -> bool {
95 self.with(|s| s.may_send())
96 }
97
98 pub fn may_recv(&self) -> bool {
99 self.with(|s| s.may_recv())
100 }
101
102 fn with<R>(&self, f: impl FnOnce(&mut SyncTcpSocket) -> R) -> R {
103 Stack::with(|stack| {
104 let res = {
105 let mut s = stack.sockets.get::<SyncTcpSocket>(self.handle);
106 f(&mut *s)
107 };
108 stack.wake();
109 res
110 })
111 }
112}
113
114fn to_ioerr(e: Error) -> io::Error {
115 warn!("smoltcp err: {:?}", e);
116 // todo
117 io::Error::Other
118}
119
120impl<'a> Drop for TcpSocket<'a> {
121 fn drop(&mut self) {
122 Stack::with(|stack| {
123 stack.sockets.remove(self.handle);
124 })
125 }
126}
127
128impl<'a> AsyncBufRead for TcpSocket<'a> {
129 fn poll_fill_buf<'z>(
130 self: Pin<&'z mut Self>,
131 cx: &mut Context<'_>,
132 ) -> Poll<io::Result<&'z [u8]>> {
133 self.with(|socket| match socket.peek(1 << 30) {
134 // No data ready
135 Ok(buf) if buf.len() == 0 => {
136 socket.register_recv_waker(cx.waker());
137 Poll::Pending
138 }
139 // Data ready!
140 Ok(buf) => {
141 // Safety:
142 // - User can't touch the inner TcpSocket directly at all.
143 // - The socket itself won't touch these bytes until consume() is called, which
144 // requires the user to release this borrow.
145 let buf: &'z [u8] = unsafe { core::mem::transmute(&*buf) };
146 Poll::Ready(Ok(buf))
147 }
148 // EOF
149 Err(Error::Finished) => Poll::Ready(Ok(&[][..])),
150 // Error
151 Err(e) => Poll::Ready(Err(to_ioerr(e))),
152 })
153 }
154
155 fn consume(self: Pin<&mut Self>, amt: usize) {
156 self.with(|s| s.recv(|_| (amt, ()))).unwrap()
157 }
158}
159
160impl<'a> AsyncWrite for TcpSocket<'a> {
161 fn poll_write(
162 self: Pin<&mut Self>,
163 cx: &mut Context<'_>,
164 buf: &[u8],
165 ) -> Poll<io::Result<usize>> {
166 self.with(|s| match s.send_slice(buf) {
167 // Not ready to send (no space in the tx buffer)
168 Ok(0) => {
169 s.register_send_waker(cx.waker());
170 Poll::Pending
171 }
172 // Some data sent
173 Ok(n) => Poll::Ready(Ok(n)),
174 // Error
175 Err(e) => Poll::Ready(Err(to_ioerr(e))),
176 })
177 }
178}