diff --git a/embassy-net-wiznet/src/chip/w5500.rs b/embassy-net-wiznet/src/chip/w5500.rs index 198ba32261..93774aef60 100644 --- a/embassy-net-wiznet/src/chip/w5500.rs +++ b/embassy-net-wiznet/src/chip/w5500.rs @@ -35,7 +35,9 @@ impl super::SealedChip for W5500 { const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x02); const SOCKET_INTR_CLR: Self::Address = (RegisterBlock::Socket0, 0x02); - const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 7); + // Disable filter on mac-address + const SOCKET_MODE_VALUE: u8 = (1 << 2); + // const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 7); const BUF_SIZE: u16 = 0x4000; const AUTO_WRAP: bool = true; diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 4c8075c430..a4c5d9bdfb 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -52,7 +52,7 @@ features = ["defmt", "tcp", "udp", "raw", "dns", "icmp", "dhcpv4", "proto-ipv6", [features] ## Enable defmt -defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt", "embassy-time/defmt", "heapless/defmt-03", "defmt?/ip_in_core"] +defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt", "embassy-time/defmt", "heapless/defmt", "defmt?/ip_in_core"] ## Enable log log = ["dep:log"] @@ -69,6 +69,10 @@ icmp = ["smoltcp/socket-icmp"] udp = ["smoltcp/socket-udp"] ## Enable Raw support raw = ["smoltcp/socket-raw"] +## Enable Raw Ethernet socket support +# Currently socket-raw is added for compile error in smoll-tcp +# See https://github.com/smoltcp-rs/smoltcp/pull/1013#issuecomment-3327778090 +eth = ["smoltcp/socket-eth", "smoltcp/socket-raw"] ## Enable TCP support tcp = ["smoltcp/socket-tcp"] ## Enable DNS support @@ -112,6 +116,9 @@ embassy-sync = { version = "0.7.2", path = "../embassy-sync" } embedded-io-async = { version = "0.6.1" } managed = { version = "0.8.0", default-features = false, features = [ "map" ] } -heapless = { version = "0.8", default-features = false } +heapless = { version = "0.9", default-features = false } embedded-nal-async = "0.8.0" document-features = "0.2.7" + +[patch.crates-io] +smoltcp = { git = "https://github.com/ususdei/smoltcp.git", rev = "c189a2e8832dc7951c7455465f61151b95bb1e21" } diff --git a/embassy-net/src/eth.rs b/embassy-net/src/eth.rs new file mode 100644 index 0000000000..7c40674a0b --- /dev/null +++ b/embassy-net/src/eth.rs @@ -0,0 +1,188 @@ +//! Raw Ethernet sockets. + +use core::future::{Future, poll_fn}; +use core::mem; +use core::task::{Context, Poll}; + +use smoltcp::iface::{Interface, SocketHandle}; +use smoltcp::socket::eth; +pub use smoltcp::socket::eth::EthMetadata; +pub use smoltcp::socket::eth::PacketMetadata; +pub use smoltcp::wire::{IpProtocol, IpVersion}; + +use crate::Stack; + +/// Error returned by [`EthSocket::recv`] and [`EthSocket::send`]. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum RecvError { + /// Provided buffer was smaller than the received packet. + Truncated, +} + +/// An Raw Ethernet socket. +pub struct EthSocket<'a> { + stack: Stack<'a>, + handle: SocketHandle, +} + +impl<'a> EthSocket<'a> { + /// Create a new Raw Ethernet socket using the provided stack and buffers. + pub fn new( + stack: Stack<'a>, + eth_type: Option, + rx_meta: &'a mut [PacketMetadata], + rx_buffer: &'a mut [u8], + tx_meta: &'a mut [PacketMetadata], + tx_buffer: &'a mut [u8], + ) -> Self { + let handle = stack.with_mut(|i| { + let rx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(rx_meta) }; + let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; + let tx_meta: &'static mut [PacketMetadata] = unsafe { mem::transmute(tx_meta) }; + let tx_buffer: &'static mut [u8] = unsafe { mem::transmute(tx_buffer) }; + i.sockets.add(eth::Socket::new( + eth_type, + eth::PacketBuffer::new(rx_meta, rx_buffer), + eth::PacketBuffer::new(tx_meta, tx_buffer), + )) + }); + + Self { stack, handle } + } + + fn with_mut(&self, f: impl FnOnce(&mut eth::Socket, &mut Interface) -> R) -> R { + self.stack.with_mut(|i| { + let socket = i.sockets.get_mut::(self.handle); + let res = f(socket, &mut i.iface); + i.waker.wake(); + res + }) + } + + /// Wait until the socket becomes readable. + /// + /// A socket is readable when a packet has been received, or when there are queued packets in + /// the buffer. + pub fn wait_recv_ready(&self) -> impl Future + '_ { + poll_fn(move |cx| self.poll_recv_ready(cx)) + } + + /// Receive a datagram. + /// + /// This method will wait until a datagram is received. + pub async fn recv(&self, buf: &mut [u8]) -> Result<(usize, EthMetadata), RecvError> { + poll_fn(move |cx| self.poll_recv(buf, cx)).await + } + + /// Wait until a datagram can be read. + /// + /// When no datagram is readable, this method will return `Poll::Pending` and + /// register the current task to be notified when a datagram is received. + /// + /// When a datagram is received, this method will return `Poll::Ready`. + pub fn poll_recv_ready(&self, cx: &mut Context<'_>) -> Poll<()> { + self.with_mut(|s, _| { + if s.can_recv() { + Poll::Ready(()) + } else { + // socket buffer is empty wait until at least one byte has arrived + s.register_recv_waker(cx.waker()); + Poll::Pending + } + }) + } + + /// Receive a datagram. + /// + /// When no datagram is available, this method will return `Poll::Pending` and + /// register the current task to be notified when a datagram is received. + pub fn poll_recv(&self, buf: &mut [u8], cx: &mut Context<'_>) -> Poll> { + self.with_mut(|s, _| match s.recv_slice(buf) { + Ok(n) => Poll::Ready(Ok(n)), + // No data ready + Err(eth::RecvError::Truncated) => Poll::Ready(Err(RecvError::Truncated)), + Err(eth::RecvError::Exhausted) => { + s.register_recv_waker(cx.waker()); + Poll::Pending + } + }) + } + + /// Wait until the socket becomes writable. + /// + /// A socket becomes writable when there is space in the buffer, from initial memory or after + /// dispatching datagrams on a full buffer. + pub fn wait_send_ready(&self) -> impl Future + '_ { + poll_fn(move |cx| self.poll_send_ready(cx)) + } + + /// Wait until a datagram can be sent. + /// + /// When no datagram can be sent (i.e. the buffer is full), this method will return + /// `Poll::Pending` and register the current task to be notified when + /// space is freed in the buffer after a datagram has been dispatched. + /// + /// When a datagram can be sent, this method will return `Poll::Ready`. + pub fn poll_send_ready(&self, cx: &mut Context<'_>) -> Poll<()> { + self.with_mut(|s, _| { + if s.can_send() { + Poll::Ready(()) + } else { + // socket buffer is full wait until a datagram has been dispatched + s.register_send_waker(cx.waker()); + Poll::Pending + } + }) + } + + /// Send a datagram. + /// + /// This method will wait until the datagram has been sent.` + pub fn send<'s>(&'s self, buf: &'s [u8], meta: EthMetadata) -> impl Future + 's { + poll_fn(move |cx| self.poll_send(buf, cx, meta)) + } + + /// Send a datagram. + /// + /// When the datagram has been sent, this method will return `Poll::Ready(Ok())`. + /// + /// When the socket's send buffer is full, this method will return `Poll::Pending` + /// and register the current task to be notified when the buffer has space available. + pub fn poll_send(&self, buf: &[u8], cx: &mut Context<'_>, meta: EthMetadata) -> Poll<()> { + self.with_mut(|s, _| match s.send_slice(buf, meta) { + // Entire datagram has been sent + Ok(()) => Poll::Ready(()), + Err(eth::SendError::BufferFull) => { + s.register_send_waker(cx.waker()); + Poll::Pending + } + }) + } + + /// Flush the socket. + /// + /// This method will wait until the socket is flushed. + pub fn flush(&mut self) -> impl Future + '_ { + poll_fn(|cx| { + self.with_mut(|s, _| { + if s.send_queue() == 0 { + Poll::Ready(()) + } else { + s.register_send_waker(cx.waker()); + Poll::Pending + } + }) + }) + } +} + +impl Drop for EthSocket<'_> { + fn drop(&mut self) { + self.stack.with_mut(|i| i.sockets.remove(self.handle)); + } +} + +fn _assert_covariant<'a, 'b: 'a>(x: EthSocket<'b>) -> EthSocket<'a> { + x +} diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index a49955c96a..9be862a161 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -16,6 +16,8 @@ pub(crate) mod fmt; #[cfg(feature = "dns")] pub mod dns; mod driver_util; +#[cfg(feature = "eth")] +pub mod eth; #[cfg(feature = "icmp")] pub mod icmp; #[cfg(feature = "raw")] diff --git a/embassy-net/src/raw.rs b/embassy-net/src/raw.rs index 89d2dd3507..51bad6dd94 100644 --- a/embassy-net/src/raw.rs +++ b/embassy-net/src/raw.rs @@ -30,8 +30,8 @@ impl<'a> RawSocket<'a> { /// Create a new Raw socket using the provided stack and buffers. pub fn new( stack: Stack<'a>, - ip_version: IpVersion, - ip_protocol: IpProtocol, + ip_version: Option, + ip_protocol: Option, rx_meta: &'a mut [PacketMetadata], rx_buffer: &'a mut [u8], tx_meta: &'a mut [PacketMetadata],