diff --git a/embassy-net-driver/README.md b/embassy-net-driver/README.md new file mode 100644 index 00000000..84f25492 --- /dev/null +++ b/embassy-net-driver/README.md @@ -0,0 +1,12 @@ +# embassy-net-driver + + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. diff --git a/embassy-net-driver/src/lib.rs b/embassy-net-driver/src/lib.rs index a39cfecc..4149bf4a 100644 --- a/embassy-net-driver/src/lib.rs +++ b/embassy-net-driver/src/lib.rs @@ -1,20 +1,57 @@ #![no_std] +#![warn(missing_docs)] +#![doc = include_str!("../README.md")] use core::task::Context; +/// Main `embassy-net` driver API. +/// +/// This is essentially an interface for sending and receiving raw network frames. +/// +/// The interface is based on _tokens_, which are types that allow to receive/transmit a +/// single packet. The `receive` and `transmit` functions only construct such tokens, the +/// real sending/receiving operation are performed when the tokens are consumed. pub trait Driver { + /// A token to receive a single network packet. type RxToken<'a>: RxToken where Self: 'a; + + /// A token to transmit a single network packet. type TxToken<'a>: TxToken where Self: 'a; + /// Construct a token pair consisting of one receive token and one transmit token. + /// + /// If there is a packet ready to be received, this function must return `Some`. + /// If there isn't, it must return `None`, and wake `cx.waker()` when a packet is ready. + /// + /// The additional transmit token makes it possible to generate a reply packet based + /// on the contents of the received packet. For example, this makes it possible to + /// handle arbitrarily large ICMP echo ("ping") requests, where the all received bytes + /// need to be sent back, without heap allocation. fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>; + + /// Construct a transmit token. + /// + /// If there is free space in the transmit buffer to transmit a packet, this function must return `Some`. + /// If there isn't, it must return `None`, and wake `cx.waker()` when space becomes available. + /// + /// Note that [`TxToken::consume`] is infallible, so it is not allowed to return a token + /// if there is no free space and fail later. fn transmit(&mut self, cx: &mut Context) -> Option>; + + /// Get the link state. + /// + /// This function must return the current link state of the device, and wake `cx.waker()` when + /// the link state changes. fn link_state(&mut self, cx: &mut Context) -> LinkState; + /// Get a description of device capabilities. fn capabilities(&self) -> Capabilities; + + /// Get the device's Ethernet address. fn ethernet_address(&self) -> [u8; 6]; } @@ -140,10 +177,15 @@ impl Default for Medium { #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub struct ChecksumCapabilities { + /// Checksum behavior for IPv4. pub ipv4: Checksum, + /// Checksum behavior for UDP. pub udp: Checksum, + /// Checksum behavior for TCP. pub tcp: Checksum, + /// Checksum behavior for ICMPv4. pub icmpv4: Checksum, + /// Checksum behavior for ICMPv6. pub icmpv6: Checksum, } @@ -167,9 +209,12 @@ impl Default for Checksum { } } +/// The link state of a network device. #[derive(PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum LinkState { + /// The link is down. Down, + /// The link is up. Up, } diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index fe9514e4..0a47c5d9 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -11,6 +11,9 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net/s features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "igmp"] target = "thumbv7em-none-eabi" +[package.metadata.docs.rs] +features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "igmp"] + [features] default = [] std = [] diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 97320e44..3fd235b2 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -1,4 +1,9 @@ -//! DNS socket with async support. +//! DNS client compatible with the `embedded-nal-async` traits. +//! +//! This exists only for compatibility with crates that use `embedded-nal-async`. +//! Prefer using [`Stack::dns_query`](crate::Stack::dns_query) directly if you're +//! not using `embedded-nal-async`. + use heapless::Vec; pub use smoltcp::socket::dns::{DnsQuery, Socket}; pub(crate) use smoltcp::socket::dns::{GetQueryResultError, StartQueryError}; @@ -34,7 +39,11 @@ impl From for Error { } } -/// Async socket for making DNS queries. +/// DNS client compatible with the `embedded-nal-async` traits. +/// +/// This exists only for compatibility with crates that use `embedded-nal-async`. +/// Prefer using [`Stack::dns_query`](crate::Stack::dns_query) directly if you're +/// not using `embedded-nal-async`. pub struct DnsSocket<'a, D> where D: Driver + 'static, diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index b80784c2..9487c091 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -1,17 +1,18 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))] #![cfg_attr(feature = "nightly", allow(incomplete_features))] +#![warn(missing_docs)] +#![doc = include_str!("../README.md")] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; -pub use embassy_net_driver as driver; - mod device; #[cfg(feature = "dns")] pub mod dns; #[cfg(feature = "tcp")] pub mod tcp; +mod time; #[cfg(feature = "udp")] pub mod udp; @@ -19,33 +20,34 @@ use core::cell::RefCell; use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; +pub use embassy_net_driver as driver; use embassy_net_driver::{Driver, LinkState, Medium}; use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; use futures::pin_mut; use heapless::Vec; +#[cfg(feature = "igmp")] +pub use smoltcp::iface::MulticastError; use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4::{self, RetryConfig}; -#[cfg(feature = "dhcpv4")] -use smoltcp::time::Duration; -// smoltcp reexports -pub use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant}; +#[cfg(feature = "udp")] +pub use smoltcp::wire::IpListenEndpoint; #[cfg(feature = "medium-ethernet")] pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; #[cfg(feature = "proto-ipv6")] pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr}; -#[cfg(feature = "udp")] -pub use smoltcp::{socket::udp::PacketMetadata, wire::IpListenEndpoint}; use crate::device::DriverAdapter; +use crate::time::{instant_from_smoltcp, instant_to_smoltcp}; const LOCAL_PORT_MIN: u16 = 1025; const LOCAL_PORT_MAX: u16 = 65535; #[cfg(feature = "dns")] const MAX_QUERIES: usize = 4; +/// Memory resources needed for a network stack. pub struct StackResources { sockets: [SocketStorage<'static>; SOCK], #[cfg(feature = "dns")] @@ -53,6 +55,7 @@ pub struct StackResources { } impl StackResources { + /// Create a new set of stack resources. pub fn new() -> Self { #[cfg(feature = "dns")] const INIT: Option = None; @@ -64,23 +67,35 @@ impl StackResources { } } +/// Static IP address configuration. #[derive(Debug, Clone, PartialEq, Eq)] pub struct StaticConfig { + /// IP address and subnet mask. pub address: Ipv4Cidr, + /// Default gateway. pub gateway: Option, + /// DNS servers. pub dns_servers: Vec, } +/// DHCP configuration. #[cfg(feature = "dhcpv4")] #[derive(Debug, Clone, PartialEq, Eq)] pub struct DhcpConfig { - pub max_lease_duration: Option, + /// Maximum lease duration. + /// + /// If not set, the lease duration specified by the server will be used. + /// If set, the lease duration will be capped at this value. + pub max_lease_duration: Option, + /// Retry configuration. pub retry_config: RetryConfig, - /// Ignore NAKs. + /// Ignore NAKs from DHCP servers. + /// + /// This is not compliant with the DHCP RFCs, since theoretically we must stop using the assigned IP when receiving a NAK. This can increase reliability on broken networks with buggy routers or rogue DHCP servers, however. pub ignore_naks: bool, - /// Server port config + /// Server port. This is almost always 67. Do not change unless you know what you're doing. pub server_port: u16, - /// Client port config + /// Client port. This is almost always 68. Do not change unless you know what you're doing. pub client_port: u16, } @@ -97,12 +112,18 @@ impl Default for DhcpConfig { } } +/// Network stack configuration. pub enum Config { + /// Use a static IP address configuration. Static(StaticConfig), + /// Use DHCP to obtain an IP address configuration. #[cfg(feature = "dhcpv4")] Dhcp(DhcpConfig), } +/// A network stack. +/// +/// This is the main entry point for the network stack. pub struct Stack { pub(crate) socket: RefCell, inner: RefCell>, @@ -128,6 +149,7 @@ pub(crate) struct SocketStack { } impl Stack { + /// Create a new network stack. pub fn new( mut device: D, config: Config, @@ -205,22 +227,30 @@ impl Stack { f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut()) } + /// Get the MAC address of the network interface. pub fn ethernet_address(&self) -> [u8; 6] { self.with(|_s, i| i.device.ethernet_address()) } + /// Get whether the link is up. pub fn is_link_up(&self) -> bool { self.with(|_s, i| i.link_up) } + /// Get whether the network stack has a valid IP configuration. + /// This is true if the network stack has a static IP configuration or if DHCP has completed pub fn is_config_up(&self) -> bool { self.with(|_s, i| i.config.is_some()) } + /// Get the current IP configuration. pub fn config(&self) -> Option { self.with(|_s, i| i.config.clone()) } + /// Run the network stack. + /// + /// You must call this in a background task, to process network events. pub async fn run(&self) -> ! { poll_fn(|cx| { self.with_mut(|s, i| i.poll(cx, s)); @@ -303,7 +333,8 @@ impl Stack { #[cfg(feature = "igmp")] impl Stack { - pub fn join_multicast_group(&self, addr: T) -> Result + /// Join a multicast group. + pub fn join_multicast_group(&self, addr: T) -> Result where T: Into, { @@ -315,7 +346,8 @@ impl Stack { }) } - pub fn leave_multicast_group(&self, addr: T) -> Result + /// Leave a multicast group. + pub fn leave_multicast_group(&self, addr: T) -> Result where T: Into, { @@ -327,6 +359,7 @@ impl Stack { }) } + /// Get whether the network stack has joined the given multicast group. pub fn has_multicast_group>(&self, addr: T) -> bool { self.socket.borrow().iface.has_multicast_group(addr) } @@ -384,7 +417,7 @@ impl Inner { #[cfg(feature = "dhcpv4")] fn apply_dhcp_config(&self, socket: &mut smoltcp::socket::dhcpv4::Socket, config: DhcpConfig) { socket.set_ignore_naks(config.ignore_naks); - socket.set_max_lease_duration(config.max_lease_duration); + socket.set_max_lease_duration(config.max_lease_duration.map(crate::time::duration_to_smoltcp)); socket.set_ports(config.server_port, config.client_port); socket.set_retry_config(config.retry_config); } @@ -465,11 +498,3 @@ impl Inner { } } } - -fn instant_to_smoltcp(instant: Instant) -> SmolInstant { - SmolInstant::from_millis(instant.as_millis() as i64) -} - -fn instant_from_smoltcp(instant: SmolInstant) -> Instant { - Instant::from_millis(instant.total_millis() as u64) -} diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index c3d8764b..732b6d21 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -1,22 +1,39 @@ +//! TCP sockets. +//! +//! # Listening +//! +//! `embassy-net` does not have a `TcpListener`. Instead, individual `TcpSocket`s can be put into +//! listening mode by calling [`TcpSocket::accept`]. +//! +//! Incoming connections when no socket is listening are rejected. To accept many incoming +//! connections, create many sockets and put them all into listening mode. + use core::cell::RefCell; use core::future::poll_fn; use core::mem; use core::task::Poll; use embassy_net_driver::Driver; +use embassy_time::Duration; use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::tcp; -use smoltcp::time::Duration; +pub use smoltcp::socket::tcp::State; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; +use crate::time::duration_to_smoltcp; use crate::{SocketStack, Stack}; +/// Error returned by TcpSocket read/write functions. #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { + /// The connection was reset. + /// + /// This can happen on receiving a RST packet, or on timeout. ConnectionReset, } +/// Error returned by [`TcpSocket::connect`]. #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ConnectError { @@ -30,6 +47,7 @@ pub enum ConnectError { NoRoute, } +/// Error returned by [`TcpSocket::accept`]. #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum AcceptError { @@ -41,35 +59,50 @@ pub enum AcceptError { ConnectionReset, } +/// A TCP socket. pub struct TcpSocket<'a> { io: TcpIo<'a>, } +/// The reader half of a TCP socket. pub struct TcpReader<'a> { io: TcpIo<'a>, } +/// The writer half of a TCP socket. pub struct TcpWriter<'a> { io: TcpIo<'a>, } impl<'a> TcpReader<'a> { + /// Read data from the socket. + /// + /// Returns how many bytes were read, or an error. If no data is available, it waits + /// until there is at least one byte available. pub async fn read(&mut self, buf: &mut [u8]) -> Result { self.io.read(buf).await } } impl<'a> TcpWriter<'a> { + /// Write data to the socket. + /// + /// Returns how many bytes were written, or an error. If the socket is not ready to + /// accept data, it waits until it is. pub async fn write(&mut self, buf: &[u8]) -> Result { self.io.write(buf).await } + /// Flushes the written data to the socket. + /// + /// This waits until all data has been sent, and ACKed by the remote host. pub async fn flush(&mut self) -> Result<(), Error> { self.io.flush().await } } impl<'a> TcpSocket<'a> { + /// Create a new TCP socket on the given stack, with the given buffers. pub fn new(stack: &'a Stack, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { let s = &mut *stack.socket.borrow_mut(); let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; @@ -87,10 +120,12 @@ impl<'a> TcpSocket<'a> { } } + /// Split the socket into reader and a writer halves. pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) { (TcpReader { io: self.io }, TcpWriter { io: self.io }) } + /// Connect to a remote host. pub async fn connect(&mut self, remote_endpoint: T) -> Result<(), ConnectError> where T: Into, @@ -120,6 +155,9 @@ impl<'a> TcpSocket<'a> { .await } + /// Accept a connection from a remote host. + /// + /// This function puts the socket in listening mode, and waits until a connection is received. pub async fn accept(&mut self, local_endpoint: T) -> Result<(), AcceptError> where T: Into, @@ -142,54 +180,98 @@ impl<'a> TcpSocket<'a> { .await } + /// Read data from the socket. + /// + /// Returns how many bytes were read, or an error. If no data is available, it waits + /// until there is at least one byte available. pub async fn read(&mut self, buf: &mut [u8]) -> Result { self.io.read(buf).await } + /// Write data to the socket. + /// + /// Returns how many bytes were written, or an error. If the socket is not ready to + /// accept data, it waits until it is. pub async fn write(&mut self, buf: &[u8]) -> Result { self.io.write(buf).await } + /// Flushes the written data to the socket. + /// + /// This waits until all data has been sent, and ACKed by the remote host. pub async fn flush(&mut self) -> Result<(), Error> { self.io.flush().await } + /// Set the timeout for the socket. + /// + /// If the timeout is set, the socket will be closed if no data is received for the + /// specified duration. pub fn set_timeout(&mut self, duration: Option) { - self.io.with_mut(|s, _| s.set_timeout(duration)) + self.io + .with_mut(|s, _| s.set_timeout(duration.map(duration_to_smoltcp))) } + /// Set the keep-alive interval for the socket. + /// + /// If the keep-alive interval is set, the socket will send keep-alive packets after + /// the specified duration of inactivity. + /// + /// If not set, the socket will not send keep-alive packets. pub fn set_keep_alive(&mut self, interval: Option) { - self.io.with_mut(|s, _| s.set_keep_alive(interval)) + self.io + .with_mut(|s, _| s.set_keep_alive(interval.map(duration_to_smoltcp))) } + /// Set the hop limit field in the IP header of sent packets. pub fn set_hop_limit(&mut self, hop_limit: Option) { self.io.with_mut(|s, _| s.set_hop_limit(hop_limit)) } + /// Get the local endpoint of the socket. + /// + /// Returns `None` if the socket is not bound (listening) or not connected. pub fn local_endpoint(&self) -> Option { self.io.with(|s, _| s.local_endpoint()) } + /// Get the remote endpoint of the socket. + /// + /// Returns `None` if the socket is not connected. pub fn remote_endpoint(&self) -> Option { self.io.with(|s, _| s.remote_endpoint()) } - pub fn state(&self) -> tcp::State { + /// Get the state of the socket. + pub fn state(&self) -> State { self.io.with(|s, _| s.state()) } + /// Close the write half of the socket. + /// + /// This closes only the write half of the socket. The read half side remains open, the + /// socket can still receive data. + /// + /// Data that has been written to the socket and not yet sent (or not yet ACKed) will still + /// still sent. The last segment of the pending to send data is sent with the FIN flag set. pub fn close(&mut self) { self.io.with_mut(|s, _| s.close()) } + /// Forcibly close the socket. + /// + /// This instantly closes both the read and write halves of the socket. Any pending data + /// that has not been sent will be lost. pub fn abort(&mut self) { self.io.with_mut(|s, _| s.abort()) } + /// Get whether the socket is ready to send data, i.e. whether there is space in the send buffer. pub fn may_send(&self) -> bool { self.io.with(|s, _| s.may_send()) } + /// Get whether the socket is ready to receive data, i.e. whether there is some pending data in the receive buffer. pub fn may_recv(&self) -> bool { self.io.with(|s, _| s.may_recv()) } @@ -341,6 +423,7 @@ mod embedded_io_impls { } } +/// TCP client compatible with `embedded-nal-async` traits. #[cfg(all(feature = "unstable-traits", feature = "nightly"))] pub mod client { use core::cell::UnsafeCell; @@ -352,14 +435,16 @@ pub mod client { use super::*; - /// TCP client capable of creating up to N multiple connections with tx and rx buffers according to TX_SZ and RX_SZ. + /// TCP client connection pool compatible with `embedded-nal-async` traits. + /// + /// The pool is capable of managing up to N concurrent connections with tx and rx buffers according to TX_SZ and RX_SZ. pub struct TcpClient<'d, D: Driver, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> { stack: &'d Stack, state: &'d TcpClientState, } impl<'d, D: Driver, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, D, N, TX_SZ, RX_SZ> { - /// Create a new TcpClient + /// Create a new `TcpClient`. pub fn new(stack: &'d Stack, state: &'d TcpClientState) -> Self { Self { stack, state } } @@ -396,6 +481,7 @@ pub mod client { } } + /// Opened TCP connection in a [`TcpClient`]. pub struct TcpConnection<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> { socket: TcpSocket<'d>, state: &'d TcpClientState, @@ -454,6 +540,7 @@ pub mod client { } impl TcpClientState { + /// Create a new `TcpClientState`. pub const fn new() -> Self { Self { pool: Pool::new() } } diff --git a/embassy-net/src/time.rs b/embassy-net/src/time.rs new file mode 100644 index 00000000..b98d40fd --- /dev/null +++ b/embassy-net/src/time.rs @@ -0,0 +1,20 @@ +#![allow(unused)] + +use embassy_time::{Duration, Instant}; +use smoltcp::time::{Duration as SmolDuration, Instant as SmolInstant}; + +pub(crate) fn instant_to_smoltcp(instant: Instant) -> SmolInstant { + SmolInstant::from_micros(instant.as_micros() as i64) +} + +pub(crate) fn instant_from_smoltcp(instant: SmolInstant) -> Instant { + Instant::from_micros(instant.total_micros() as u64) +} + +pub(crate) fn duration_to_smoltcp(duration: Duration) -> SmolDuration { + SmolDuration::from_micros(duration.as_micros()) +} + +pub(crate) fn duration_from_smoltcp(duration: SmolDuration) -> Duration { + Duration::from_micros(duration.total_micros()) +} diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 476aef12..c9843cfe 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -1,3 +1,5 @@ +//! UDP sockets. + use core::cell::RefCell; use core::future::poll_fn; use core::mem; @@ -5,11 +7,13 @@ use core::task::Poll; use embassy_net_driver::Driver; use smoltcp::iface::{Interface, SocketHandle}; -use smoltcp::socket::udp::{self, PacketMetadata}; +use smoltcp::socket::udp; +pub use smoltcp::socket::udp::PacketMetadata; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; use crate::{SocketStack, Stack}; +/// Error returned by [`UdpSocket::bind`]. #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum BindError { @@ -19,6 +23,7 @@ pub enum BindError { NoRoute, } +/// Error returned by [`UdpSocket::recv_from`] and [`UdpSocket::send_to`]. #[derive(PartialEq, Eq, Clone, Copy, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { @@ -26,12 +31,14 @@ pub enum Error { NoRoute, } +/// An UDP socket. pub struct UdpSocket<'a> { stack: &'a RefCell, handle: SocketHandle, } impl<'a> UdpSocket<'a> { + /// Create a new UDP socket using the provided stack and buffers. pub fn new( stack: &'a Stack, rx_meta: &'a mut [PacketMetadata], @@ -56,6 +63,7 @@ impl<'a> UdpSocket<'a> { } } + /// Bind the socket to a local endpoint. pub fn bind(&mut self, endpoint: T) -> Result<(), BindError> where T: Into, @@ -88,6 +96,11 @@ impl<'a> UdpSocket<'a> { res } + /// Receive a datagram. + /// + /// This method will wait until a datagram is received. + /// + /// Returns the number of bytes received and the remote endpoint. pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> { poll_fn(move |cx| { self.with_mut(|s, _| match s.recv_slice(buf) { @@ -102,6 +115,7 @@ impl<'a> UdpSocket<'a> { .await } + /// Send a datagram to the specified remote endpoint. pub async fn send_to(&self, buf: &[u8], remote_endpoint: T) -> Result<(), Error> where T: Into, @@ -121,22 +135,28 @@ impl<'a> UdpSocket<'a> { .await } + /// Returns the local endpoint of the socket. pub fn endpoint(&self) -> IpListenEndpoint { self.with(|s, _| s.endpoint()) } + /// Returns whether the socket is open. + pub fn is_open(&self) -> bool { self.with(|s, _| s.is_open()) } + /// Close the socket. pub fn close(&mut self) { self.with_mut(|s, _| s.close()) } + /// Returns whether the socket is ready to send data, i.e. it has enough buffer space to hold a packet. pub fn may_send(&self) -> bool { self.with(|s, _| s.can_send()) } + /// Returns whether the socket is ready to receive data, i.e. it has received a packet that's now in the buffer. pub fn may_recv(&self) -> bool { self.with(|s, _| s.can_recv()) } diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index b8a72313..786025c4 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -132,7 +132,7 @@ async fn main(spawner: Spawner) { loop { let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); info!("Listening on TCP:1234..."); if let Err(e) = socket.accept(1234).await { diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 66a6ed4d..431db63e 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -114,7 +114,7 @@ async fn main(spawner: Spawner) { loop { let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); info!("Listening on TCP:1234..."); if let Err(e) = socket.accept(1234).await { diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index e018e18c..d9361625 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -6,6 +6,7 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::tcp::TcpSocket; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_time::Duration; use embedded_io::asynch::Write; use heapless::Vec; use log::*; @@ -75,7 +76,7 @@ async fn main_task(spawner: Spawner) { let mut tx_buffer = [0; 4096]; let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(Duration::from_secs(10))); let remote_endpoint = (Ipv4Address::new(192, 168, 69, 100), 8000); info!("connecting to {:?}...", remote_endpoint); diff --git a/examples/std/src/bin/net_udp.rs b/examples/std/src/bin/net_udp.rs index 328a0536..4df23edf 100644 --- a/examples/std/src/bin/net_udp.rs +++ b/examples/std/src/bin/net_udp.rs @@ -2,8 +2,8 @@ use clap::Parser; use embassy_executor::{Executor, Spawner}; -use embassy_net::udp::UdpSocket; -use embassy_net::{Config, Ipv4Address, Ipv4Cidr, PacketMetadata, Stack, StackResources}; +use embassy_net::udp::{PacketMetadata, UdpSocket}; +use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; use heapless::Vec; use log::*; use rand_core::{OsRng, RngCore}; diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index db9e1839..9131e589 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -126,7 +126,7 @@ async fn main(spawner: Spawner) { loop { let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); info!("Listening on TCP:1234..."); if let Err(e) = socket.accept(1234).await { diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 9febb14e..b947361a 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -91,7 +91,7 @@ async fn main(spawner: Spawner) -> ! { loop { let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); info!("connecting..."); diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 6d650da9..b2e252fc 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -110,7 +110,7 @@ async fn main(spawner: Spawner) -> ! { loop { let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); info!("connecting..."); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 541e4976..61bb7e37 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -92,7 +92,7 @@ async fn main(spawner: Spawner) -> ! { loop { let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); let remote_endpoint = (Ipv4Address::new(10, 42, 0, 1), 8000); info!("connecting..."); diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 98ec0e83..6c5645a4 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -121,7 +121,7 @@ async fn main(spawner: Spawner) { loop { let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + socket.set_timeout(Some(embassy_time::Duration::from_secs(10))); info!("Listening on TCP:1234..."); if let Err(e) = socket.accept(1234).await {