Skip to main content

secure_serial/
resources.rs

1//! Owned queues and buffer pools for one side of a secure-serial link.
2//!
3//! This module is described in detail on [`SecureSerialResources`]; see the crate README for how
4//! tasks and [`crate::SecureSerialSender`] connect to the channels here.
5
6use embassy_sync::{blocking_mutex::raw::RawMutex, channel};
7use embedded_buffer_pool::{BufferGuard, BufferPool, MappedBufferGuard};
8use heapless::Vec;
9
10use crate::protocol::{Ack, CHUNK_LEN_MAX};
11
12/// Queues and pools for [`crate::run_read`], [`crate::run_write`], and [`crate::SecureSerialSender`]
13/// on a single endpoint (one UART side).
14///
15/// This type groups the TX chunk pool, TX/RX [`embassy_sync::channel::Channel`]s, and the RX
16/// reassembly pool so you do not wire six separate static items by hand.
17///
18/// # Sizes
19///
20/// - `N_INFLIGHT`: capacity of the outgoing chunk queue, TX [`BufferPool`], and ACK channels (same as
21///   in-flight chunk/ACK window); must be in 1..=32.
22/// - `N_RX_POOL`: number of parallel RX packet buffers and RX completion queue depth (1..=32).
23/// - `N_BUF`: byte length of each RX reassembly buffer (must fit your largest packet).
24///
25/// [`BufferPool`] and channels require fixed sizes in that range.
26///
27/// # `static` placement
28///
29/// [`embedded_buffer_pool::BufferPool`] only exposes `take` / `try_take` on `&'static` pools.
30/// Put this struct in a `static_cell::StaticCell` (or another `'static` slot) so references
31/// passed to [`crate::SecureSerialSender::new`] and async tasks are valid for the whole program.
32///
33/// # Example (Cortex-M / `no_std`)
34///
35/// Store the bundle once in a `static_cell::StaticCell`, then reborrow it as a shared
36/// `&'static SecureSerialResources<...>` so every task can hold a copy of the pointer (the
37/// mutex-backed channels inside are safe to share). Use [`SecureSerialResourcesDefault`] if the
38/// default sizes (8 TX slots, 4 × 4096-byte RX buffers) fit your link.
39///
40/// ```ignore
41/// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
42/// use embassy_executor::Spawner;
43/// use static_cell::StaticCell;
44/// use secure_serial::{SecureSerialResources, SecureSerialResourcesDefault};
45///
46/// static SERIAL_RES: StaticCell<SecureSerialResourcesDefault<CriticalSectionRawMutex>> =
47///     StaticCell::new();
48///
49/// fn init_serial_tasks(spawner: Spawner) {
50///     let res_mut = SERIAL_RES.init(SecureSerialResourcesDefault::new());
51///     let res: &'static SecureSerialResourcesDefault<CriticalSectionRawMutex> = res_mut;
52///
53///     // `tx_pool()` and channel endpoints are valid for `'static` because `res` is.
54///     let mut tx_rx = res.tx_chunks_receiver();
55///     let mut acks_out_rx = res.acks_to_wire_receiver();
56///     unwrap!(spawner.spawn(run_write_task(&mut tx_rx, &mut acks_out_rx)));
57///     unwrap!(spawner.spawn(run_read_task(res)));
58///     unwrap!(spawner.spawn(app_task(res)));
59/// }
60///
61/// #[embassy_executor::task]
62/// async fn run_write_task(
63///     tx_rx: &mut /* `Receiver` for DATA chunks from `tx_chunks_receiver` */,
64///     acks_out_rx: &mut /* `Receiver` for ACKs from `acks_to_wire_receiver` */,
65/// ) {
66///     let _ = secure_serial::run_write(&mut /* uart */, tx_rx, acks_out_rx, &mut /* crc */).await;
67/// }
68/// ```
69pub struct SecureSerialResources<
70    M: RawMutex + 'static,
71    const N_INFLIGHT: usize,
72    const N_RX_POOL: usize,
73    const N_BUF: usize,
74> {
75    tx_pool: BufferPool<M, Vec<u8, CHUNK_LEN_MAX>, N_INFLIGHT>,
76    tx_chunks: channel::Channel<M, BufferGuard<M, Vec<u8, CHUNK_LEN_MAX>>, N_INFLIGHT>,
77    acks_to_send: channel::Channel<M, Ack, N_INFLIGHT>,
78    acks_received: channel::Channel<M, Ack, N_INFLIGHT>,
79    rx_pool: BufferPool<M, [u8; N_BUF], N_RX_POOL>,
80    rx_complete: channel::Channel<M, MappedBufferGuard<M, [u8]>, N_RX_POOL>,
81}
82
83/// Common defaults: 8 TX slots, 4 RX buffers, 4096 bytes per RX buffer.
84pub type SecureSerialResourcesDefault<M> = SecureSerialResources<M, 8, 4, 4096>;
85
86impl<M: RawMutex + 'static, const N_INFLIGHT: usize, const N_RX_POOL: usize, const N_BUF: usize>
87    SecureSerialResources<M, N_INFLIGHT, N_RX_POOL, N_BUF>
88{
89    /// Builds pools and channels; all buffers start empty and channels empty.
90    ///
91    /// # Panics
92    ///
93    /// If `N_INFLIGHT` or `N_RX_POOL` is not in `1..=32`.
94    pub fn new() -> Self {
95        assert!(N_INFLIGHT > 0 && N_INFLIGHT <= 32);
96        assert!(N_RX_POOL > 0 && N_RX_POOL <= 32);
97
98        let tx_backing = core::array::from_fn(|_| Vec::new());
99        let tx_pool = BufferPool::new(tx_backing);
100        let tx_chunks = channel::Channel::new();
101        let acks_to_send = channel::Channel::new();
102        let acks_received = channel::Channel::new();
103        let rx_backing = core::array::from_fn(|_| [0u8; N_BUF]);
104        let rx_pool = BufferPool::new(rx_backing);
105        let rx_complete = channel::Channel::new();
106        Self {
107            tx_pool,
108            tx_chunks,
109            acks_to_send,
110            acks_received,
111            rx_pool,
112            rx_complete,
113        }
114    }
115
116    /// Pool for outbound DATA chunks; pass to [`crate::SecureSerialSender::new`] when `self` is `'static`.
117    #[inline]
118    pub fn tx_pool(&self) -> &BufferPool<M, Vec<u8, CHUNK_LEN_MAX>, N_INFLIGHT> {
119        &self.tx_pool
120    }
121
122    /// Sender for encoded DATA chunks consumed by [`crate::run_write`].
123    #[inline]
124    pub fn tx_chunks_sender(
125        &self,
126    ) -> channel::Sender<'_, M, BufferGuard<M, Vec<u8, CHUNK_LEN_MAX>>, N_INFLIGHT> {
127        self.tx_chunks.sender()
128    }
129
130    /// Receiver used by [`crate::run_write`] for pending DATA frames.
131    #[inline]
132    pub fn tx_chunks_receiver(
133        &self,
134    ) -> channel::Receiver<'_, M, BufferGuard<M, Vec<u8, CHUNK_LEN_MAX>>, N_INFLIGHT> {
135        self.tx_chunks.receiver()
136    }
137
138    /// ACKs generated locally that must be written to the wire (feed [`crate::run_write`]).
139    #[inline]
140    pub fn acks_to_wire_sender(&self) -> channel::Sender<'_, M, Ack, N_INFLIGHT> {
141        self.acks_to_send.sender()
142    }
143
144    #[inline]
145    pub fn acks_to_wire_receiver(&self) -> channel::Receiver<'_, M, Ack, N_INFLIGHT> {
146        self.acks_to_send.receiver()
147    }
148
149    /// ACKs received from the peer (feed [`crate::SecureSerialSender::new`] as `rx_confirm`).
150    #[inline]
151    pub fn acks_from_peer_sender(&self) -> channel::Sender<'_, M, Ack, N_INFLIGHT> {
152        self.acks_received.sender()
153    }
154
155    #[inline]
156    pub fn acks_from_peer_receiver(&self) -> channel::Receiver<'_, M, Ack, N_INFLIGHT> {
157        self.acks_received.receiver()
158    }
159
160    /// Pool for partially reassembled RX packets; pass to [`crate::run_read`] when `self` is `'static`.
161    #[inline]
162    pub fn rx_pool(&self) -> &BufferPool<M, [u8; N_BUF], N_RX_POOL> {
163        &self.rx_pool
164    }
165
166    /// Completed packets from [`crate::run_read`].
167    #[inline]
168    pub fn rx_complete_sender(
169        &self,
170    ) -> channel::Sender<'_, M, MappedBufferGuard<M, [u8]>, N_RX_POOL> {
171        self.rx_complete.sender()
172    }
173
174    #[inline]
175    pub fn rx_complete_receiver(
176        &self,
177    ) -> channel::Receiver<'_, M, MappedBufferGuard<M, [u8]>, N_RX_POOL> {
178        self.rx_complete.receiver()
179    }
180}