embedded_nal_tcpextensions/
lib.rs

1#![no_std]
2//! Extensions to [embedded_nal]'s TCP traits to make more precise use of TCP buffers.
3//!
4//! This is not intended to be a long-term extension of embedded-nal,
5//! but more an experimenation playground for [#59]
6//! where users and implementations can play (ideally behind unstable feature gates);
7//! once this is mature it is hoped that it'll be taken up into embedded-nal.
8//!
9//! # Maintenance status
10//!
11//! This crate has not been updated since embedded-hal 0.6 (as of late 2024, that trait crate is at
12//! 0.9), and is not expected to be updated any further. The experiment has shown that while it may
13//! be possible to implement this on embedded platforms, there can be significant pushback from TCP
14//! stack maintainers to expose this capability (see [#16850]), as it can mess with expectations
15//! around when to ACK and what the usable vs. communicated window size is.
16//!
17//! There may still be some way to implement this optimization, but for the time being, this is not
18//! being followed any further.
19//!
20//! [#59]: https://github.com/rust-embedded-community/embedded-nal/issues/59
21//! [#16850]: https://github.com/RIOT-OS/RIOT/pull/16850
22
23use embedded_nal::nb;
24
25mod wrappers;
26pub use wrappers::*;
27
28/// A specialization of [embedded_nal::TcpClientStack] that allows nonblocking until a given number of bytes can
29/// be read, and writing in an all-or-nothing way.
30///
31/// Stacks that can not provide this can be wrapped in [`BufferedStack::<_, 1152>::new()`] to gain
32/// this capability at the cost of per-socket allocated buffers.
33///
34/// It is an early form of what should resolve
35/// <https://github.com/rust-embedded-community/embedded-nal/issues/59>.
36pub trait TcpExactStack: embedded_nal::TcpClientStack {
37    /// Capacity of the connection's TCP buffer.
38    ///
39    /// Calls to [`receive_exact()`](TcpExactStack::receive_exact) can ask up to this many
40    /// bytes to be returned.
41    ///
42    /// Checking this (possibly in static assertions) is important because applications that'd wait
43    /// for larger chunks of data than the TCP socket can buffer will deadlock -- the application
44    /// waits for the server to send more, but the stack tells the server to stop sending more
45    /// until the application processes the data.
46    ///
47    /// (This is a per-stack-type property. Stacks where this is configurable per socket need to
48    /// report the smallest value, which is guaranteed to be usable with [`receive_exact()`](TcpExactStack::receive_exact) on every
49    /// instance. It is exposed this way to allow users to have suitable const-sized allocations
50    /// for the sockets they are built for.)
51    const RECVBUFLEN: usize;
52
53    /// Receive data from the stream.
54    ///
55    /// Returns `Ok(())` if the whole buffer could be filled, or an error. If a packet has not been
56    /// received when called, or not enough data is present, then [`nb::Error::WouldBlock`] should
57    /// be returned.
58    ///
59    /// # Panics
60    ///
61    /// This may panic when buffer is larger than RECVBUFLEN.
62    fn receive_exact(
63        &mut self,
64        socket: &mut Self::TcpSocket,
65        buffer: &mut [u8],
66    ) -> nb::Result<(), Self::Error>;
67
68    /// Capacity of the connection's TCP send buffer.
69    ///
70    /// Calls to [`send_All()`](TcpExactStack::send_all) can send up to this many bytes.
71    const SENDBUFLEN: usize;
72
73    /// Send all this data to the stream.
74    ///
75    /// Returns `Ok(())` if the whole buffer could be submitted, or an error. If the data can not
76    /// fit into the send buffer when called (especially, but not only, if the send buffer is
77    /// full), then [`nb::Error::WouldBlock`'] is returned.
78    ///
79    /// # Panics
80    ///
81    /// When the passed slice is larger than the advertised SENDBUFLEN, this may panic -- for the
82    /// worse alternative is to return WouldBlock indefinitely, as the message can't be put in the
83    /// send buffer no matter how empty it becomes.
84    fn send_all(
85        &mut self,
86        socket: &mut Self::TcpSocket,
87        buffer: &[u8],
88    ) -> Result<(), embedded_nal::nb::Error<Self::Error>>;
89}