Skip to content

Commit

Permalink
Make TcpContext memory usage configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
hulthe committed Feb 14, 2024
1 parent 68c2c36 commit f2a1142
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 48 deletions.
2 changes: 1 addition & 1 deletion samples/nrf52840/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion samples/nrf52840/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ async fn main(spawner: Spawner) {
tx_buffer: [0; 64],
rx_buffer: [0; 64],
},
power_pins
power_pins,
tcp_slots: 3,
);

defmt::info!("Initializing modem");
Expand Down
4 changes: 2 additions & 2 deletions src/drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ use crate::at_command::{CloseConnection, SetGnssPower};
use crate::gnss::GNSS_SLOTS;
use crate::log;
use crate::modem::{CommandRunnerGuard, ModemContext};
use crate::tcp::CONNECTION_SLOTS;
use crate::tcp::MAX_TCP_SLOTS;
use crate::Error;

/// The capacity of the drop channel.
/// Nust be at least the number of unique objects that can be dropped.
const DROP_CAPACITY: usize = GNSS_SLOTS + CONNECTION_SLOTS;
const DROP_CAPACITY: usize = GNSS_SLOTS + MAX_TCP_SLOTS;
pub type DropChannel = Channel<CriticalSectionRawMutex, DropMessage, DROP_CAPACITY>;

/// Type for facilitating asynchronous dropping. See module-level docs for details.
Expand Down
1 change: 1 addition & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::at_command::{httptofs::StatusCode, SimError};

#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Error {
InvalidUtf8,
BufferOverflow,
Expand Down
70 changes: 59 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub mod gnss;
pub mod modem;
pub mod pump;
pub mod read;
mod slot;
pub mod slot;
pub mod tcp;
mod util;
pub mod voltage;
Expand Down Expand Up @@ -93,20 +93,63 @@ pub trait ModemPower {
/// This macro sets up a modem for use, statically allocating pump tasks and channels.
///
/// You can call `Modem::new` directly if you want more control over initialization.
///
/// Here's an abridged example, see `samples` for a more complete example:
///
/// ```ignore
/// use embassy_executor::Spawner;
/// use sim7000_async::{BuildIo, ModemPower};
///
/// let spawner: Spawner;
///
/// struct MyUart { /* --snip-- */}
/// impl BuildIo for MyUart { /* --snip-- */ }
/// let uart: MyUart;
///
/// struct MyPowerPins { /* --snip-- */ }
/// impl ModemPower for MyPowerPins { /* --snip-- */ }
/// let power_pins: MyPowerPins;
///
/// spawn_modem! {
/// &spawner,
/// MyUart as uart,
/// power_pins,
/// tcp_slots: 5, // optional argument. may not exceed max.
/// };
/// ```
///
/// Note that `tcp_slots` is an optional argument that sets how many TCP sockets may be open
/// concurrently. Default is [MAX_TCP_SLOTS](tcp::MAX_TCP_SLOTS), and the value may not exceed this.
/// Each slot consumes a approx [TCP_RX_BUF_LEN](tcp::TCP_RX_BUF_LEN) bytes of RAM.
#[macro_export]
macro_rules! spawn_modem {
// TODO: the "as" keyword hack is a bit weird.
($spawner:expr, $io_ty:ty as $io:expr, $power_pins:expr $(,)?) => {{
static CONTEXT: ::sim7000_async::modem::ModemContext =
::sim7000_async::modem::ModemContext::new();
(
$spawner:expr,
$io_ty:ty as $io:expr,
$power_pins:expr,
tcp_slots: $tcp_slots:expr $(,)?
) => {{
const __TCP_SLOT_COUNT: usize = $tcp_slots;

const ASSERT_TCP_SLOTS_WITHIN_LIMIT: usize = ::sim7000_async::tcp::MAX_TCP_SLOTS - __TCP_SLOT_COUNT;

static SIM7000_TCP_SLOTS: [::sim7000_async::slot::Slot<::sim7000_async::modem::TcpSlot>; __TCP_SLOT_COUNT] = {
use ::sim7000_async::{slot::Slot, modem::TcpSlot};
#[allow(clippy::declare_interior_mutable_const)]
const NEW_SLOT: Slot<TcpSlot> = Slot::new(TcpSlot::new());
[NEW_SLOT; __TCP_SLOT_COUNT]
};

static SIM7000_CONTEXT: ::sim7000_async::modem::ModemContext =
::sim7000_async::modem::ModemContext::new(::sim7000_async::modem::TcpContext::new(&SIM7000_TCP_SLOTS));

let spawner: &Spawner = $spawner;
let (modem, io_pump, tx_pump, rx_pump, drop_pump) =
::sim7000_async::modem::Modem::new($io, $power_pins, &CONTEXT)
::sim7000_async::modem::Modem::new($io, $power_pins, &SIM7000_CONTEXT)
.await
.expect("Failed to create Modem");

mod __tasks {
mod __sim7000_tasks {
use super::*;
use ::sim7000_async::pump_task;
pump_task!(tx_pump, ::sim7000_async::pump::TxPump<'static>);
Expand All @@ -115,11 +158,16 @@ macro_rules! spawn_modem {
pump_task!(io_pump, ::sim7000_async::pump::RawIoPump<'static, $io_ty>);
}

spawner.must_spawn(__tasks::tx_pump(tx_pump));
spawner.must_spawn(__tasks::rx_pump(rx_pump));
spawner.must_spawn(__tasks::drop_pump(drop_pump));
spawner.must_spawn(__tasks::io_pump(io_pump));
spawner.must_spawn(__sim7000_tasks::tx_pump(tx_pump));
spawner.must_spawn(__sim7000_tasks::rx_pump(rx_pump));
spawner.must_spawn(__sim7000_tasks::drop_pump(drop_pump));
spawner.must_spawn(__sim7000_tasks::io_pump(io_pump));

modem
}};
(
$spawner:expr,
$io_ty:ty as $io:expr,
$power_pins:expr $(,)?
) => {spawn_modem!($spawner, $io_ty as $io, $power_pins, tcp_slots: 8)};
}
38 changes: 13 additions & 25 deletions src/modem/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@ use embassy_sync::{
};

use super::{power::PowerSignal, CommandRunner, RawAtCommand};
use crate::at_command::unsolicited::RegistrationStatus;
use crate::slot::Slot;
use crate::tcp::CONNECTION_SLOTS;
use crate::StateSignal;
use crate::{
at_command::unsolicited::RegistrationStatus,
at_command::{
unsolicited::{ConnectionMessage, GnssReport, NetworkRegistration, VoltageWarning},
ResponseCode,
},
drop::DropChannel,
slot::Slot,
tcp::TCP_RX_BUF_LEN,
util::Lagged,
util::RingChannel,
StateSignal,
};
use crate::{drop::DropChannel, util::Lagged};

pub type TcpRxPipe = Pipe<CriticalSectionRawMutex, 3072>;
pub type TcpRxPipe = Pipe<CriticalSectionRawMutex, TCP_RX_BUF_LEN>;
pub type TcpEventChannel = RingChannel<CriticalSectionRawMutex, ConnectionMessage, 8>;

pub struct ModemContext {
Expand All @@ -35,14 +36,14 @@ pub struct ModemContext {
}

impl ModemContext {
pub const fn new() -> Self {
pub const fn new(tcp: TcpContext) -> Self {
ModemContext {
power_signal: PowerSignal::new(),
command_lock: Mutex::new(()),
commands: Channel::new(),
generic_response: Channel::new(),
drop_channel: DropChannel::new(),
tcp: TcpContext::new(),
tcp,
registration_events: StateSignal::new(NetworkRegistration {
status: RegistrationStatus::Unknown,
lac: None,
Expand All @@ -66,7 +67,7 @@ pub struct TcpSlot {
}

pub struct TcpContext {
pub(crate) slots: [Slot<TcpSlot>; CONNECTION_SLOTS],
pub(crate) slots: &'static [Slot<TcpSlot>],
}

impl TcpSlot {
Expand All @@ -79,23 +80,10 @@ impl TcpSlot {
}

impl TcpContext {
pub const fn new() -> Self {
TcpContext {
slots: [
Slot::new(TcpSlot::new()),
Slot::new(TcpSlot::new()),
Slot::new(TcpSlot::new()),
Slot::new(TcpSlot::new()),
Slot::new(TcpSlot::new()),
Slot::new(TcpSlot::new()),
Slot::new(TcpSlot::new()),
Slot::new(TcpSlot::new()),
],
}
pub const fn new(slots: &'static [Slot<TcpSlot>]) -> Self {
TcpContext { slots }
}
}

impl TcpContext {
pub fn claim(&self) -> Option<TcpToken> {
self.slots.iter().enumerate().find_map(|(i, slot)| {
let TcpSlot { rx, events } = slot.claim()?; // find an unclaimed slot
Expand All @@ -108,7 +96,7 @@ impl TcpContext {
}

pub async fn disconnect_all(&self) {
for slot in &self.slots {
for slot in self.slots {
if slot.is_claimed() {
slot.peek().events.send(ConnectionMessage::Closed);
}
Expand Down
3 changes: 2 additions & 1 deletion src/modem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,8 @@ impl<'c, P: ModemPower> Modem<'c, P> {
host: &str,
port: u16,
) -> Result<TcpStream<'c>, ConnectError> {
let tcp_context = self.context.tcp.claim().unwrap();
let tcp_context = self.context.tcp.claim().ok_or(ConnectError::NoFreeSlots)?;

TcpStream::connect(
tcp_context,
host,
Expand Down
10 changes: 5 additions & 5 deletions src/slot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use core::sync::atomic::{AtomicBool, Ordering};

use crate::log;

pub(crate) struct Slot<T: 'static> {
pub struct Slot<T: 'static> {
is_claimed: AtomicBool,
inner: T,
}
Expand All @@ -18,26 +18,26 @@ impl<T: 'static> Slot<T> {
}

/// Try to claim the slot, returns None if the slot has already been claimed
pub fn claim(&self) -> Option<&T> {
pub(crate) fn claim(&self) -> Option<&T> {
self.is_claimed
.fetch_or(true, Ordering::Relaxed)
.not()
.then(|| &self.inner)
}

/// Look in the slot without claiming it
pub fn peek(&self) -> &T {
pub(crate) fn peek(&self) -> &T {
&self.inner
}

/// Release the claim on the slot
pub fn release(&self) {
pub(crate) fn release(&self) {
if !self.is_claimed.fetch_and(false, Ordering::Relaxed) {
log::error!("Tried to release unclaimed Slot<{:?}>", type_name::<T>());
}
}

pub fn is_claimed(&self) -> bool {
pub(crate) fn is_claimed(&self) -> bool {
self.is_claimed.load(Ordering::Relaxed)
}
}
Expand Down
13 changes: 11 additions & 2 deletions src/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ use crate::{
Error,
};

/// The maximum number of parallel connections supported by the modem
pub const CONNECTION_SLOTS: usize = 8;
/// The maximum number of concurrent TCP connections supported by the modem.
pub const MAX_TCP_SLOTS: usize = 8;

/// The number of bytes allocated for each TCP slot receive buffer.
pub const TCP_RX_BUF_LEN: usize = 3072;

#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
Expand All @@ -39,8 +42,14 @@ impl embedded_io_async::Error for TcpError {

#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum ConnectError {
ConnectFailed,

/// No connection slots available, the max number of open connections has been reached.
/// For TCP, this number is [MAX_TCP_SLOTS], and is a hard limit set by the modem.
NoFreeSlots,

Other(crate::Error),

/// The modem gave an unexpected response
Expand Down

0 comments on commit f2a1142

Please sign in to comment.