diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-05-15 00:39:57 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2023-05-15 00:56:09 +0200 |
| commit | d07821d8516f39568066c6a0ccf0953f3fad5318 (patch) | |
| tree | 6087afa764ef77620fe10abfe39e28b4e2b56424 | |
| parent | 62857bdb2d0c34aa2ee9e82454ee0182139bab2c (diff) | |
net: document crate.
| -rw-r--r-- | embassy-net/Cargo.toml | 3 | ||||
| -rw-r--r-- | embassy-net/src/dns.rs | 13 | ||||
| -rw-r--r-- | embassy-net/src/lib.rs | 49 | ||||
| -rw-r--r-- | embassy-net/src/tcp.rs | 87 | ||||
| -rw-r--r-- | embassy-net/src/udp.rs | 19 |
5 files changed, 160 insertions, 11 deletions
diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index fe9514e46..0a47c5d94 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 | |||
| 11 | features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "igmp"] | 11 | features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "igmp"] |
| 12 | target = "thumbv7em-none-eabi" | 12 | target = "thumbv7em-none-eabi" |
| 13 | 13 | ||
| 14 | [package.metadata.docs.rs] | ||
| 15 | features = ["nightly", "unstable-traits", "defmt", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6", "medium-ethernet", "medium-ip", "igmp"] | ||
| 16 | |||
| 14 | [features] | 17 | [features] |
| 15 | default = [] | 18 | default = [] |
| 16 | std = [] | 19 | std = [] |
diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 97320e44b..3fd235b2c 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs | |||
| @@ -1,4 +1,9 @@ | |||
| 1 | //! DNS socket with async support. | 1 | //! DNS client compatible with the `embedded-nal-async` traits. |
| 2 | //! | ||
| 3 | //! This exists only for compatibility with crates that use `embedded-nal-async`. | ||
| 4 | //! Prefer using [`Stack::dns_query`](crate::Stack::dns_query) directly if you're | ||
| 5 | //! not using `embedded-nal-async`. | ||
| 6 | |||
| 2 | use heapless::Vec; | 7 | use heapless::Vec; |
| 3 | pub use smoltcp::socket::dns::{DnsQuery, Socket}; | 8 | pub use smoltcp::socket::dns::{DnsQuery, Socket}; |
| 4 | pub(crate) use smoltcp::socket::dns::{GetQueryResultError, StartQueryError}; | 9 | pub(crate) use smoltcp::socket::dns::{GetQueryResultError, StartQueryError}; |
| @@ -34,7 +39,11 @@ impl From<StartQueryError> for Error { | |||
| 34 | } | 39 | } |
| 35 | } | 40 | } |
| 36 | 41 | ||
| 37 | /// Async socket for making DNS queries. | 42 | /// DNS client compatible with the `embedded-nal-async` traits. |
| 43 | /// | ||
| 44 | /// This exists only for compatibility with crates that use `embedded-nal-async`. | ||
| 45 | /// Prefer using [`Stack::dns_query`](crate::Stack::dns_query) directly if you're | ||
| 46 | /// not using `embedded-nal-async`. | ||
| 38 | pub struct DnsSocket<'a, D> | 47 | pub struct DnsSocket<'a, D> |
| 39 | where | 48 | where |
| 40 | D: Driver + 'static, | 49 | D: Driver + 'static, |
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 572eda3ba..9487c0913 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs | |||
| @@ -1,12 +1,12 @@ | |||
| 1 | #![cfg_attr(not(feature = "std"), no_std)] | 1 | #![cfg_attr(not(feature = "std"), no_std)] |
| 2 | #![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))] | 2 | #![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))] |
| 3 | #![cfg_attr(feature = "nightly", allow(incomplete_features))] | 3 | #![cfg_attr(feature = "nightly", allow(incomplete_features))] |
| 4 | #![warn(missing_docs)] | ||
| 5 | #![doc = include_str!("../README.md")] | ||
| 4 | 6 | ||
| 5 | // This mod MUST go first, so that the others see its macros. | 7 | // This mod MUST go first, so that the others see its macros. |
| 6 | pub(crate) mod fmt; | 8 | pub(crate) mod fmt; |
| 7 | 9 | ||
| 8 | pub use embassy_net_driver as driver; | ||
| 9 | |||
| 10 | mod device; | 10 | mod device; |
| 11 | #[cfg(feature = "dns")] | 11 | #[cfg(feature = "dns")] |
| 12 | pub mod dns; | 12 | pub mod dns; |
| @@ -20,11 +20,14 @@ use core::cell::RefCell; | |||
| 20 | use core::future::{poll_fn, Future}; | 20 | use core::future::{poll_fn, Future}; |
| 21 | use core::task::{Context, Poll}; | 21 | use core::task::{Context, Poll}; |
| 22 | 22 | ||
| 23 | pub use embassy_net_driver as driver; | ||
| 23 | use embassy_net_driver::{Driver, LinkState, Medium}; | 24 | use embassy_net_driver::{Driver, LinkState, Medium}; |
| 24 | use embassy_sync::waitqueue::WakerRegistration; | 25 | use embassy_sync::waitqueue::WakerRegistration; |
| 25 | use embassy_time::{Instant, Timer}; | 26 | use embassy_time::{Instant, Timer}; |
| 26 | use futures::pin_mut; | 27 | use futures::pin_mut; |
| 27 | use heapless::Vec; | 28 | use heapless::Vec; |
| 29 | #[cfg(feature = "igmp")] | ||
| 30 | pub use smoltcp::iface::MulticastError; | ||
| 28 | use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; | 31 | use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; |
| 29 | #[cfg(feature = "dhcpv4")] | 32 | #[cfg(feature = "dhcpv4")] |
| 30 | use smoltcp::socket::dhcpv4::{self, RetryConfig}; | 33 | use smoltcp::socket::dhcpv4::{self, RetryConfig}; |
| @@ -44,6 +47,7 @@ const LOCAL_PORT_MAX: u16 = 65535; | |||
| 44 | #[cfg(feature = "dns")] | 47 | #[cfg(feature = "dns")] |
| 45 | const MAX_QUERIES: usize = 4; | 48 | const MAX_QUERIES: usize = 4; |
| 46 | 49 | ||
| 50 | /// Memory resources needed for a network stack. | ||
| 47 | pub struct StackResources<const SOCK: usize> { | 51 | pub struct StackResources<const SOCK: usize> { |
| 48 | sockets: [SocketStorage<'static>; SOCK], | 52 | sockets: [SocketStorage<'static>; SOCK], |
| 49 | #[cfg(feature = "dns")] | 53 | #[cfg(feature = "dns")] |
| @@ -51,6 +55,7 @@ pub struct StackResources<const SOCK: usize> { | |||
| 51 | } | 55 | } |
| 52 | 56 | ||
| 53 | impl<const SOCK: usize> StackResources<SOCK> { | 57 | impl<const SOCK: usize> StackResources<SOCK> { |
| 58 | /// Create a new set of stack resources. | ||
| 54 | pub fn new() -> Self { | 59 | pub fn new() -> Self { |
| 55 | #[cfg(feature = "dns")] | 60 | #[cfg(feature = "dns")] |
| 56 | const INIT: Option<dns::DnsQuery> = None; | 61 | const INIT: Option<dns::DnsQuery> = None; |
| @@ -62,23 +67,35 @@ impl<const SOCK: usize> StackResources<SOCK> { | |||
| 62 | } | 67 | } |
| 63 | } | 68 | } |
| 64 | 69 | ||
| 70 | /// Static IP address configuration. | ||
| 65 | #[derive(Debug, Clone, PartialEq, Eq)] | 71 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 66 | pub struct StaticConfig { | 72 | pub struct StaticConfig { |
| 73 | /// IP address and subnet mask. | ||
| 67 | pub address: Ipv4Cidr, | 74 | pub address: Ipv4Cidr, |
| 75 | /// Default gateway. | ||
| 68 | pub gateway: Option<Ipv4Address>, | 76 | pub gateway: Option<Ipv4Address>, |
| 77 | /// DNS servers. | ||
| 69 | pub dns_servers: Vec<Ipv4Address, 3>, | 78 | pub dns_servers: Vec<Ipv4Address, 3>, |
| 70 | } | 79 | } |
| 71 | 80 | ||
| 81 | /// DHCP configuration. | ||
| 72 | #[cfg(feature = "dhcpv4")] | 82 | #[cfg(feature = "dhcpv4")] |
| 73 | #[derive(Debug, Clone, PartialEq, Eq)] | 83 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 74 | pub struct DhcpConfig { | 84 | pub struct DhcpConfig { |
| 85 | /// Maximum lease duration. | ||
| 86 | /// | ||
| 87 | /// If not set, the lease duration specified by the server will be used. | ||
| 88 | /// If set, the lease duration will be capped at this value. | ||
| 75 | pub max_lease_duration: Option<embassy_time::Duration>, | 89 | pub max_lease_duration: Option<embassy_time::Duration>, |
| 90 | /// Retry configuration. | ||
| 76 | pub retry_config: RetryConfig, | 91 | pub retry_config: RetryConfig, |
| 77 | /// Ignore NAKs. | 92 | /// Ignore NAKs from DHCP servers. |
| 93 | /// | ||
| 94 | /// 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. | ||
| 78 | pub ignore_naks: bool, | 95 | pub ignore_naks: bool, |
| 79 | /// Server port config | 96 | /// Server port. This is almost always 67. Do not change unless you know what you're doing. |
| 80 | pub server_port: u16, | 97 | pub server_port: u16, |
| 81 | /// Client port config | 98 | /// Client port. This is almost always 68. Do not change unless you know what you're doing. |
| 82 | pub client_port: u16, | 99 | pub client_port: u16, |
| 83 | } | 100 | } |
| 84 | 101 | ||
| @@ -95,12 +112,18 @@ impl Default for DhcpConfig { | |||
| 95 | } | 112 | } |
| 96 | } | 113 | } |
| 97 | 114 | ||
| 115 | /// Network stack configuration. | ||
| 98 | pub enum Config { | 116 | pub enum Config { |
| 117 | /// Use a static IP address configuration. | ||
| 99 | Static(StaticConfig), | 118 | Static(StaticConfig), |
| 119 | /// Use DHCP to obtain an IP address configuration. | ||
| 100 | #[cfg(feature = "dhcpv4")] | 120 | #[cfg(feature = "dhcpv4")] |
| 101 | Dhcp(DhcpConfig), | 121 | Dhcp(DhcpConfig), |
| 102 | } | 122 | } |
| 103 | 123 | ||
| 124 | /// A network stack. | ||
| 125 | /// | ||
| 126 | /// This is the main entry point for the network stack. | ||
| 104 | pub struct Stack<D: Driver> { | 127 | pub struct Stack<D: Driver> { |
| 105 | pub(crate) socket: RefCell<SocketStack>, | 128 | pub(crate) socket: RefCell<SocketStack>, |
| 106 | inner: RefCell<Inner<D>>, | 129 | inner: RefCell<Inner<D>>, |
| @@ -126,6 +149,7 @@ pub(crate) struct SocketStack { | |||
| 126 | } | 149 | } |
| 127 | 150 | ||
| 128 | impl<D: Driver + 'static> Stack<D> { | 151 | impl<D: Driver + 'static> Stack<D> { |
| 152 | /// Create a new network stack. | ||
| 129 | pub fn new<const SOCK: usize>( | 153 | pub fn new<const SOCK: usize>( |
| 130 | mut device: D, | 154 | mut device: D, |
| 131 | config: Config, | 155 | config: Config, |
| @@ -203,22 +227,30 @@ impl<D: Driver + 'static> Stack<D> { | |||
| 203 | f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut()) | 227 | f(&mut *self.socket.borrow_mut(), &mut *self.inner.borrow_mut()) |
| 204 | } | 228 | } |
| 205 | 229 | ||
| 230 | /// Get the MAC address of the network interface. | ||
| 206 | pub fn ethernet_address(&self) -> [u8; 6] { | 231 | pub fn ethernet_address(&self) -> [u8; 6] { |
| 207 | self.with(|_s, i| i.device.ethernet_address()) | 232 | self.with(|_s, i| i.device.ethernet_address()) |
| 208 | } | 233 | } |
| 209 | 234 | ||
| 235 | /// Get whether the link is up. | ||
| 210 | pub fn is_link_up(&self) -> bool { | 236 | pub fn is_link_up(&self) -> bool { |
| 211 | self.with(|_s, i| i.link_up) | 237 | self.with(|_s, i| i.link_up) |
| 212 | } | 238 | } |
| 213 | 239 | ||
| 240 | /// Get whether the network stack has a valid IP configuration. | ||
| 241 | /// This is true if the network stack has a static IP configuration or if DHCP has completed | ||
| 214 | pub fn is_config_up(&self) -> bool { | 242 | pub fn is_config_up(&self) -> bool { |
| 215 | self.with(|_s, i| i.config.is_some()) | 243 | self.with(|_s, i| i.config.is_some()) |
| 216 | } | 244 | } |
| 217 | 245 | ||
| 246 | /// Get the current IP configuration. | ||
| 218 | pub fn config(&self) -> Option<StaticConfig> { | 247 | pub fn config(&self) -> Option<StaticConfig> { |
| 219 | self.with(|_s, i| i.config.clone()) | 248 | self.with(|_s, i| i.config.clone()) |
| 220 | } | 249 | } |
| 221 | 250 | ||
| 251 | /// Run the network stack. | ||
| 252 | /// | ||
| 253 | /// You must call this in a background task, to process network events. | ||
| 222 | pub async fn run(&self) -> ! { | 254 | pub async fn run(&self) -> ! { |
| 223 | poll_fn(|cx| { | 255 | poll_fn(|cx| { |
| 224 | self.with_mut(|s, i| i.poll(cx, s)); | 256 | self.with_mut(|s, i| i.poll(cx, s)); |
| @@ -301,7 +333,8 @@ impl<D: Driver + 'static> Stack<D> { | |||
| 301 | 333 | ||
| 302 | #[cfg(feature = "igmp")] | 334 | #[cfg(feature = "igmp")] |
| 303 | impl<D: Driver + smoltcp::phy::Device + 'static> Stack<D> { | 335 | impl<D: Driver + smoltcp::phy::Device + 'static> Stack<D> { |
| 304 | pub fn join_multicast_group<T>(&self, addr: T) -> Result<bool, smoltcp::iface::MulticastError> | 336 | /// Join a multicast group. |
| 337 | pub fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError> | ||
| 305 | where | 338 | where |
| 306 | T: Into<IpAddress>, | 339 | T: Into<IpAddress>, |
| 307 | { | 340 | { |
| @@ -313,7 +346,8 @@ impl<D: Driver + smoltcp::phy::Device + 'static> Stack<D> { | |||
| 313 | }) | 346 | }) |
| 314 | } | 347 | } |
| 315 | 348 | ||
| 316 | pub fn leave_multicast_group<T>(&self, addr: T) -> Result<bool, smoltcp::iface::MulticastError> | 349 | /// Leave a multicast group. |
| 350 | pub fn leave_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError> | ||
| 317 | where | 351 | where |
| 318 | T: Into<IpAddress>, | 352 | T: Into<IpAddress>, |
| 319 | { | 353 | { |
| @@ -325,6 +359,7 @@ impl<D: Driver + smoltcp::phy::Device + 'static> Stack<D> { | |||
| 325 | }) | 359 | }) |
| 326 | } | 360 | } |
| 327 | 361 | ||
| 362 | /// Get whether the network stack has joined the given multicast group. | ||
| 328 | pub fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool { | 363 | pub fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool { |
| 329 | self.socket.borrow().iface.has_multicast_group(addr) | 364 | self.socket.borrow().iface.has_multicast_group(addr) |
| 330 | } | 365 | } |
diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 05b8bb54e..732b6d217 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs | |||
| @@ -1,3 +1,13 @@ | |||
| 1 | //! TCP sockets. | ||
| 2 | //! | ||
| 3 | //! # Listening | ||
| 4 | //! | ||
| 5 | //! `embassy-net` does not have a `TcpListener`. Instead, individual `TcpSocket`s can be put into | ||
| 6 | //! listening mode by calling [`TcpSocket::accept`]. | ||
| 7 | //! | ||
| 8 | //! Incoming connections when no socket is listening are rejected. To accept many incoming | ||
| 9 | //! connections, create many sockets and put them all into listening mode. | ||
| 10 | |||
| 1 | use core::cell::RefCell; | 11 | use core::cell::RefCell; |
| 2 | use core::future::poll_fn; | 12 | use core::future::poll_fn; |
| 3 | use core::mem; | 13 | use core::mem; |
| @@ -13,12 +23,17 @@ use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; | |||
| 13 | use crate::time::duration_to_smoltcp; | 23 | use crate::time::duration_to_smoltcp; |
| 14 | use crate::{SocketStack, Stack}; | 24 | use crate::{SocketStack, Stack}; |
| 15 | 25 | ||
| 26 | /// Error returned by TcpSocket read/write functions. | ||
| 16 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | 27 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] |
| 17 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 28 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 18 | pub enum Error { | 29 | pub enum Error { |
| 30 | /// The connection was reset. | ||
| 31 | /// | ||
| 32 | /// This can happen on receiving a RST packet, or on timeout. | ||
| 19 | ConnectionReset, | 33 | ConnectionReset, |
| 20 | } | 34 | } |
| 21 | 35 | ||
| 36 | /// Error returned by [`TcpSocket::connect`]. | ||
| 22 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | 37 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] |
| 23 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 38 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 24 | pub enum ConnectError { | 39 | pub enum ConnectError { |
| @@ -32,6 +47,7 @@ pub enum ConnectError { | |||
| 32 | NoRoute, | 47 | NoRoute, |
| 33 | } | 48 | } |
| 34 | 49 | ||
| 50 | /// Error returned by [`TcpSocket::accept`]. | ||
| 35 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | 51 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] |
| 36 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 52 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 37 | pub enum AcceptError { | 53 | pub enum AcceptError { |
| @@ -43,35 +59,50 @@ pub enum AcceptError { | |||
| 43 | ConnectionReset, | 59 | ConnectionReset, |
| 44 | } | 60 | } |
| 45 | 61 | ||
| 62 | /// A TCP socket. | ||
| 46 | pub struct TcpSocket<'a> { | 63 | pub struct TcpSocket<'a> { |
| 47 | io: TcpIo<'a>, | 64 | io: TcpIo<'a>, |
| 48 | } | 65 | } |
| 49 | 66 | ||
| 67 | /// The reader half of a TCP socket. | ||
| 50 | pub struct TcpReader<'a> { | 68 | pub struct TcpReader<'a> { |
| 51 | io: TcpIo<'a>, | 69 | io: TcpIo<'a>, |
| 52 | } | 70 | } |
| 53 | 71 | ||
| 72 | /// The writer half of a TCP socket. | ||
| 54 | pub struct TcpWriter<'a> { | 73 | pub struct TcpWriter<'a> { |
| 55 | io: TcpIo<'a>, | 74 | io: TcpIo<'a>, |
| 56 | } | 75 | } |
| 57 | 76 | ||
| 58 | impl<'a> TcpReader<'a> { | 77 | impl<'a> TcpReader<'a> { |
| 78 | /// Read data from the socket. | ||
| 79 | /// | ||
| 80 | /// Returns how many bytes were read, or an error. If no data is available, it waits | ||
| 81 | /// until there is at least one byte available. | ||
| 59 | pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { | 82 | pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { |
| 60 | self.io.read(buf).await | 83 | self.io.read(buf).await |
| 61 | } | 84 | } |
| 62 | } | 85 | } |
| 63 | 86 | ||
| 64 | impl<'a> TcpWriter<'a> { | 87 | impl<'a> TcpWriter<'a> { |
| 88 | /// Write data to the socket. | ||
| 89 | /// | ||
| 90 | /// Returns how many bytes were written, or an error. If the socket is not ready to | ||
| 91 | /// accept data, it waits until it is. | ||
| 65 | pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> { | 92 | pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> { |
| 66 | self.io.write(buf).await | 93 | self.io.write(buf).await |
| 67 | } | 94 | } |
| 68 | 95 | ||
| 96 | /// Flushes the written data to the socket. | ||
| 97 | /// | ||
| 98 | /// This waits until all data has been sent, and ACKed by the remote host. | ||
| 69 | pub async fn flush(&mut self) -> Result<(), Error> { | 99 | pub async fn flush(&mut self) -> Result<(), Error> { |
| 70 | self.io.flush().await | 100 | self.io.flush().await |
| 71 | } | 101 | } |
| 72 | } | 102 | } |
| 73 | 103 | ||
| 74 | impl<'a> TcpSocket<'a> { | 104 | impl<'a> TcpSocket<'a> { |
| 105 | /// Create a new TCP socket on the given stack, with the given buffers. | ||
| 75 | pub fn new<D: Driver>(stack: &'a Stack<D>, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { | 106 | pub fn new<D: Driver>(stack: &'a Stack<D>, rx_buffer: &'a mut [u8], tx_buffer: &'a mut [u8]) -> Self { |
| 76 | let s = &mut *stack.socket.borrow_mut(); | 107 | let s = &mut *stack.socket.borrow_mut(); |
| 77 | let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; | 108 | let rx_buffer: &'static mut [u8] = unsafe { mem::transmute(rx_buffer) }; |
| @@ -89,10 +120,12 @@ impl<'a> TcpSocket<'a> { | |||
| 89 | } | 120 | } |
| 90 | } | 121 | } |
| 91 | 122 | ||
| 123 | /// Split the socket into reader and a writer halves. | ||
| 92 | pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) { | 124 | pub fn split(&mut self) -> (TcpReader<'_>, TcpWriter<'_>) { |
| 93 | (TcpReader { io: self.io }, TcpWriter { io: self.io }) | 125 | (TcpReader { io: self.io }, TcpWriter { io: self.io }) |
| 94 | } | 126 | } |
| 95 | 127 | ||
| 128 | /// Connect to a remote host. | ||
| 96 | pub async fn connect<T>(&mut self, remote_endpoint: T) -> Result<(), ConnectError> | 129 | pub async fn connect<T>(&mut self, remote_endpoint: T) -> Result<(), ConnectError> |
| 97 | where | 130 | where |
| 98 | T: Into<IpEndpoint>, | 131 | T: Into<IpEndpoint>, |
| @@ -122,6 +155,9 @@ impl<'a> TcpSocket<'a> { | |||
| 122 | .await | 155 | .await |
| 123 | } | 156 | } |
| 124 | 157 | ||
| 158 | /// Accept a connection from a remote host. | ||
| 159 | /// | ||
| 160 | /// This function puts the socket in listening mode, and waits until a connection is received. | ||
| 125 | pub async fn accept<T>(&mut self, local_endpoint: T) -> Result<(), AcceptError> | 161 | pub async fn accept<T>(&mut self, local_endpoint: T) -> Result<(), AcceptError> |
| 126 | where | 162 | where |
| 127 | T: Into<IpListenEndpoint>, | 163 | T: Into<IpListenEndpoint>, |
| @@ -144,56 +180,98 @@ impl<'a> TcpSocket<'a> { | |||
| 144 | .await | 180 | .await |
| 145 | } | 181 | } |
| 146 | 182 | ||
| 183 | /// Read data from the socket. | ||
| 184 | /// | ||
| 185 | /// Returns how many bytes were read, or an error. If no data is available, it waits | ||
| 186 | /// until there is at least one byte available. | ||
| 147 | pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { | 187 | pub async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> { |
| 148 | self.io.read(buf).await | 188 | self.io.read(buf).await |
| 149 | } | 189 | } |
| 150 | 190 | ||
| 191 | /// Write data to the socket. | ||
| 192 | /// | ||
| 193 | /// Returns how many bytes were written, or an error. If the socket is not ready to | ||
| 194 | /// accept data, it waits until it is. | ||
| 151 | pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> { | 195 | pub async fn write(&mut self, buf: &[u8]) -> Result<usize, Error> { |
| 152 | self.io.write(buf).await | 196 | self.io.write(buf).await |
| 153 | } | 197 | } |
| 154 | 198 | ||
| 199 | /// Flushes the written data to the socket. | ||
| 200 | /// | ||
| 201 | /// This waits until all data has been sent, and ACKed by the remote host. | ||
| 155 | pub async fn flush(&mut self) -> Result<(), Error> { | 202 | pub async fn flush(&mut self) -> Result<(), Error> { |
| 156 | self.io.flush().await | 203 | self.io.flush().await |
| 157 | } | 204 | } |
| 158 | 205 | ||
| 206 | /// Set the timeout for the socket. | ||
| 207 | /// | ||
| 208 | /// If the timeout is set, the socket will be closed if no data is received for the | ||
| 209 | /// specified duration. | ||
| 159 | pub fn set_timeout(&mut self, duration: Option<Duration>) { | 210 | pub fn set_timeout(&mut self, duration: Option<Duration>) { |
| 160 | self.io | 211 | self.io |
| 161 | .with_mut(|s, _| s.set_timeout(duration.map(duration_to_smoltcp))) | 212 | .with_mut(|s, _| s.set_timeout(duration.map(duration_to_smoltcp))) |
| 162 | } | 213 | } |
| 163 | 214 | ||
| 215 | /// Set the keep-alive interval for the socket. | ||
| 216 | /// | ||
| 217 | /// If the keep-alive interval is set, the socket will send keep-alive packets after | ||
| 218 | /// the specified duration of inactivity. | ||
| 219 | /// | ||
| 220 | /// If not set, the socket will not send keep-alive packets. | ||
| 164 | pub fn set_keep_alive(&mut self, interval: Option<Duration>) { | 221 | pub fn set_keep_alive(&mut self, interval: Option<Duration>) { |
| 165 | self.io | 222 | self.io |
| 166 | .with_mut(|s, _| s.set_keep_alive(interval.map(duration_to_smoltcp))) | 223 | .with_mut(|s, _| s.set_keep_alive(interval.map(duration_to_smoltcp))) |
| 167 | } | 224 | } |
| 168 | 225 | ||
| 226 | /// Set the hop limit field in the IP header of sent packets. | ||
| 169 | pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) { | 227 | pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) { |
| 170 | self.io.with_mut(|s, _| s.set_hop_limit(hop_limit)) | 228 | self.io.with_mut(|s, _| s.set_hop_limit(hop_limit)) |
| 171 | } | 229 | } |
| 172 | 230 | ||
| 231 | /// Get the local endpoint of the socket. | ||
| 232 | /// | ||
| 233 | /// Returns `None` if the socket is not bound (listening) or not connected. | ||
| 173 | pub fn local_endpoint(&self) -> Option<IpEndpoint> { | 234 | pub fn local_endpoint(&self) -> Option<IpEndpoint> { |
| 174 | self.io.with(|s, _| s.local_endpoint()) | 235 | self.io.with(|s, _| s.local_endpoint()) |
| 175 | } | 236 | } |
| 176 | 237 | ||
| 238 | /// Get the remote endpoint of the socket. | ||
| 239 | /// | ||
| 240 | /// Returns `None` if the socket is not connected. | ||
| 177 | pub fn remote_endpoint(&self) -> Option<IpEndpoint> { | 241 | pub fn remote_endpoint(&self) -> Option<IpEndpoint> { |
| 178 | self.io.with(|s, _| s.remote_endpoint()) | 242 | self.io.with(|s, _| s.remote_endpoint()) |
| 179 | } | 243 | } |
| 180 | 244 | ||
| 245 | /// Get the state of the socket. | ||
| 181 | pub fn state(&self) -> State { | 246 | pub fn state(&self) -> State { |
| 182 | self.io.with(|s, _| s.state()) | 247 | self.io.with(|s, _| s.state()) |
| 183 | } | 248 | } |
| 184 | 249 | ||
| 250 | /// Close the write half of the socket. | ||
| 251 | /// | ||
| 252 | /// This closes only the write half of the socket. The read half side remains open, the | ||
| 253 | /// socket can still receive data. | ||
| 254 | /// | ||
| 255 | /// Data that has been written to the socket and not yet sent (or not yet ACKed) will still | ||
| 256 | /// still sent. The last segment of the pending to send data is sent with the FIN flag set. | ||
| 185 | pub fn close(&mut self) { | 257 | pub fn close(&mut self) { |
| 186 | self.io.with_mut(|s, _| s.close()) | 258 | self.io.with_mut(|s, _| s.close()) |
| 187 | } | 259 | } |
| 188 | 260 | ||
| 261 | /// Forcibly close the socket. | ||
| 262 | /// | ||
| 263 | /// This instantly closes both the read and write halves of the socket. Any pending data | ||
| 264 | /// that has not been sent will be lost. | ||
| 189 | pub fn abort(&mut self) { | 265 | pub fn abort(&mut self) { |
| 190 | self.io.with_mut(|s, _| s.abort()) | 266 | self.io.with_mut(|s, _| s.abort()) |
| 191 | } | 267 | } |
| 192 | 268 | ||
| 269 | /// Get whether the socket is ready to send data, i.e. whether there is space in the send buffer. | ||
| 193 | pub fn may_send(&self) -> bool { | 270 | pub fn may_send(&self) -> bool { |
| 194 | self.io.with(|s, _| s.may_send()) | 271 | self.io.with(|s, _| s.may_send()) |
| 195 | } | 272 | } |
| 196 | 273 | ||
| 274 | /// Get whether the socket is ready to receive data, i.e. whether there is some pending data in the receive buffer. | ||
| 197 | pub fn may_recv(&self) -> bool { | 275 | pub fn may_recv(&self) -> bool { |
| 198 | self.io.with(|s, _| s.may_recv()) | 276 | self.io.with(|s, _| s.may_recv()) |
| 199 | } | 277 | } |
| @@ -345,6 +423,7 @@ mod embedded_io_impls { | |||
| 345 | } | 423 | } |
| 346 | } | 424 | } |
| 347 | 425 | ||
| 426 | /// TCP client compatible with `embedded-nal-async` traits. | ||
| 348 | #[cfg(all(feature = "unstable-traits", feature = "nightly"))] | 427 | #[cfg(all(feature = "unstable-traits", feature = "nightly"))] |
| 349 | pub mod client { | 428 | pub mod client { |
| 350 | use core::cell::UnsafeCell; | 429 | use core::cell::UnsafeCell; |
| @@ -356,14 +435,16 @@ pub mod client { | |||
| 356 | 435 | ||
| 357 | use super::*; | 436 | use super::*; |
| 358 | 437 | ||
| 359 | /// TCP client capable of creating up to N multiple connections with tx and rx buffers according to TX_SZ and RX_SZ. | 438 | /// TCP client connection pool compatible with `embedded-nal-async` traits. |
| 439 | /// | ||
| 440 | /// The pool is capable of managing up to N concurrent connections with tx and rx buffers according to TX_SZ and RX_SZ. | ||
| 360 | pub struct TcpClient<'d, D: Driver, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> { | 441 | pub struct TcpClient<'d, D: Driver, const N: usize, const TX_SZ: usize = 1024, const RX_SZ: usize = 1024> { |
| 361 | stack: &'d Stack<D>, | 442 | stack: &'d Stack<D>, |
| 362 | state: &'d TcpClientState<N, TX_SZ, RX_SZ>, | 443 | state: &'d TcpClientState<N, TX_SZ, RX_SZ>, |
| 363 | } | 444 | } |
| 364 | 445 | ||
| 365 | impl<'d, D: Driver, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, D, N, TX_SZ, RX_SZ> { | 446 | impl<'d, D: Driver, const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClient<'d, D, N, TX_SZ, RX_SZ> { |
| 366 | /// Create a new TcpClient | 447 | /// Create a new `TcpClient`. |
| 367 | pub fn new(stack: &'d Stack<D>, state: &'d TcpClientState<N, TX_SZ, RX_SZ>) -> Self { | 448 | pub fn new(stack: &'d Stack<D>, state: &'d TcpClientState<N, TX_SZ, RX_SZ>) -> Self { |
| 368 | Self { stack, state } | 449 | Self { stack, state } |
| 369 | } | 450 | } |
| @@ -400,6 +481,7 @@ pub mod client { | |||
| 400 | } | 481 | } |
| 401 | } | 482 | } |
| 402 | 483 | ||
| 484 | /// Opened TCP connection in a [`TcpClient`]. | ||
| 403 | pub struct TcpConnection<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> { | 485 | pub struct TcpConnection<'d, const N: usize, const TX_SZ: usize, const RX_SZ: usize> { |
| 404 | socket: TcpSocket<'d>, | 486 | socket: TcpSocket<'d>, |
| 405 | state: &'d TcpClientState<N, TX_SZ, RX_SZ>, | 487 | state: &'d TcpClientState<N, TX_SZ, RX_SZ>, |
| @@ -458,6 +540,7 @@ pub mod client { | |||
| 458 | } | 540 | } |
| 459 | 541 | ||
| 460 | impl<const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClientState<N, TX_SZ, RX_SZ> { | 542 | impl<const N: usize, const TX_SZ: usize, const RX_SZ: usize> TcpClientState<N, TX_SZ, RX_SZ> { |
| 543 | /// Create a new `TcpClientState`. | ||
| 461 | pub const fn new() -> Self { | 544 | pub const fn new() -> Self { |
| 462 | Self { pool: Pool::new() } | 545 | Self { pool: Pool::new() } |
| 463 | } | 546 | } |
diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index fe425914b..c9843cfe8 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | //! UDP sockets. | ||
| 2 | |||
| 1 | use core::cell::RefCell; | 3 | use core::cell::RefCell; |
| 2 | use core::future::poll_fn; | 4 | use core::future::poll_fn; |
| 3 | use core::mem; | 5 | use core::mem; |
| @@ -11,6 +13,7 @@ use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; | |||
| 11 | 13 | ||
| 12 | use crate::{SocketStack, Stack}; | 14 | use crate::{SocketStack, Stack}; |
| 13 | 15 | ||
| 16 | /// Error returned by [`UdpSocket::bind`]. | ||
| 14 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | 17 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] |
| 15 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 18 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 16 | pub enum BindError { | 19 | pub enum BindError { |
| @@ -20,6 +23,7 @@ pub enum BindError { | |||
| 20 | NoRoute, | 23 | NoRoute, |
| 21 | } | 24 | } |
| 22 | 25 | ||
| 26 | /// Error returned by [`UdpSocket::recv_from`] and [`UdpSocket::send_to`]. | ||
| 23 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | 27 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] |
| 24 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 28 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 25 | pub enum Error { | 29 | pub enum Error { |
| @@ -27,12 +31,14 @@ pub enum Error { | |||
| 27 | NoRoute, | 31 | NoRoute, |
| 28 | } | 32 | } |
| 29 | 33 | ||
| 34 | /// An UDP socket. | ||
| 30 | pub struct UdpSocket<'a> { | 35 | pub struct UdpSocket<'a> { |
| 31 | stack: &'a RefCell<SocketStack>, | 36 | stack: &'a RefCell<SocketStack>, |
| 32 | handle: SocketHandle, | 37 | handle: SocketHandle, |
| 33 | } | 38 | } |
| 34 | 39 | ||
| 35 | impl<'a> UdpSocket<'a> { | 40 | impl<'a> UdpSocket<'a> { |
| 41 | /// Create a new UDP socket using the provided stack and buffers. | ||
| 36 | pub fn new<D: Driver>( | 42 | pub fn new<D: Driver>( |
| 37 | stack: &'a Stack<D>, | 43 | stack: &'a Stack<D>, |
| 38 | rx_meta: &'a mut [PacketMetadata], | 44 | rx_meta: &'a mut [PacketMetadata], |
| @@ -57,6 +63,7 @@ impl<'a> UdpSocket<'a> { | |||
| 57 | } | 63 | } |
| 58 | } | 64 | } |
| 59 | 65 | ||
| 66 | /// Bind the socket to a local endpoint. | ||
| 60 | pub fn bind<T>(&mut self, endpoint: T) -> Result<(), BindError> | 67 | pub fn bind<T>(&mut self, endpoint: T) -> Result<(), BindError> |
| 61 | where | 68 | where |
| 62 | T: Into<IpListenEndpoint>, | 69 | T: Into<IpListenEndpoint>, |
| @@ -89,6 +96,11 @@ impl<'a> UdpSocket<'a> { | |||
| 89 | res | 96 | res |
| 90 | } | 97 | } |
| 91 | 98 | ||
| 99 | /// Receive a datagram. | ||
| 100 | /// | ||
| 101 | /// This method will wait until a datagram is received. | ||
| 102 | /// | ||
| 103 | /// Returns the number of bytes received and the remote endpoint. | ||
| 92 | pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> { | 104 | pub async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), Error> { |
| 93 | poll_fn(move |cx| { | 105 | poll_fn(move |cx| { |
| 94 | self.with_mut(|s, _| match s.recv_slice(buf) { | 106 | self.with_mut(|s, _| match s.recv_slice(buf) { |
| @@ -103,6 +115,7 @@ impl<'a> UdpSocket<'a> { | |||
| 103 | .await | 115 | .await |
| 104 | } | 116 | } |
| 105 | 117 | ||
| 118 | /// Send a datagram to the specified remote endpoint. | ||
| 106 | pub async fn send_to<T>(&self, buf: &[u8], remote_endpoint: T) -> Result<(), Error> | 119 | pub async fn send_to<T>(&self, buf: &[u8], remote_endpoint: T) -> Result<(), Error> |
| 107 | where | 120 | where |
| 108 | T: Into<IpEndpoint>, | 121 | T: Into<IpEndpoint>, |
| @@ -122,22 +135,28 @@ impl<'a> UdpSocket<'a> { | |||
| 122 | .await | 135 | .await |
| 123 | } | 136 | } |
| 124 | 137 | ||
| 138 | /// Returns the local endpoint of the socket. | ||
| 125 | pub fn endpoint(&self) -> IpListenEndpoint { | 139 | pub fn endpoint(&self) -> IpListenEndpoint { |
| 126 | self.with(|s, _| s.endpoint()) | 140 | self.with(|s, _| s.endpoint()) |
| 127 | } | 141 | } |
| 128 | 142 | ||
| 143 | /// Returns whether the socket is open. | ||
| 144 | |||
| 129 | pub fn is_open(&self) -> bool { | 145 | pub fn is_open(&self) -> bool { |
| 130 | self.with(|s, _| s.is_open()) | 146 | self.with(|s, _| s.is_open()) |
| 131 | } | 147 | } |
| 132 | 148 | ||
| 149 | /// Close the socket. | ||
| 133 | pub fn close(&mut self) { | 150 | pub fn close(&mut self) { |
| 134 | self.with_mut(|s, _| s.close()) | 151 | self.with_mut(|s, _| s.close()) |
| 135 | } | 152 | } |
| 136 | 153 | ||
| 154 | /// Returns whether the socket is ready to send data, i.e. it has enough buffer space to hold a packet. | ||
| 137 | pub fn may_send(&self) -> bool { | 155 | pub fn may_send(&self) -> bool { |
| 138 | self.with(|s, _| s.can_send()) | 156 | self.with(|s, _| s.can_send()) |
| 139 | } | 157 | } |
| 140 | 158 | ||
| 159 | /// Returns whether the socket is ready to receive data, i.e. it has received a packet that's now in the buffer. | ||
| 141 | pub fn may_recv(&self) -> bool { | 160 | pub fn may_recv(&self) -> bool { |
| 142 | self.with(|s, _| s.can_recv()) | 161 | self.with(|s, _| s.can_recv()) |
| 143 | } | 162 | } |
