diff --git a/.gitignore b/.gitignore index cf6578b..041fab0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.vscode /target /Cargo.lock +*.rlib diff --git a/examples/light_eth.rs b/examples/light_eth.rs index b56ec13..c833bcf 100644 --- a/examples/light_eth.rs +++ b/examples/light_eth.rs @@ -9,7 +9,9 @@ //! //! The example implements a fictitious Light device (an On-Off Matter cluster). #![recursion_limit = "256"] +#![feature(maybe_uninit_uninit_array_transpose)] +use core::mem::MaybeUninit; use core::pin::pin; use embassy_futures::select::select; @@ -18,6 +20,7 @@ use embassy_time::{Duration, Timer}; use env_logger::Target; use log::info; +use rs_matter_stack::bump_alloc::BumpAlloc; use rs_matter_stack::eth::EthMatterStack; use rs_matter_stack::matter::dm::clusters::desc; use rs_matter_stack::matter::dm::clusters::desc::ClusterHandler as _; @@ -77,11 +80,17 @@ fn main() -> Result<(), Error> { Async(desc::DescHandler::new(Dataver::new_rand(stack.matter().rand())).adapt()), ); + static ALLOC_MEM: StaticCell<[u8; 90000]> = StaticCell::new(); + let alloc_mem = ALLOC_MEM.uninit(); + let ptr = alloc_mem.as_mut_ptr() as *mut MaybeUninit; + let p: &'static mut [MaybeUninit] = unsafe { core::slice::from_raw_parts_mut(ptr as _, 90000) }; + let bump_alloc = BumpAlloc::new(p); + // Run the Matter stack with our handler // Using `pin!` is completely optional, but saves some memory due to `rustc` // not being very intelligent w.r.t. stack usage in async functions let store = stack.create_shared_store(DirKvBlobStore::new_default()); - let mut matter = pin!(stack.run_preex( + let mut matter = pin!(stack.run_preex2( // The Matter stack needs UDP sockets to communicate with other Matter devices edge_nal_std::Stack::new(), // Will try to find a default network interface @@ -94,6 +103,7 @@ fn main() -> Result<(), Error> { (NODE, handler), // No user task future to run (), + &bump_alloc, )); // Just for demoing purposes: diff --git a/src/bump_alloc.rs b/src/bump_alloc.rs new file mode 100644 index 0000000..72339f2 --- /dev/null +++ b/src/bump_alloc.rs @@ -0,0 +1,133 @@ +//! A simple bump allocator for fixed-size memory chunks +//! +//! This allocator is designed for allocating objects that have a known maximum lifetime. +//! All allocations are freed when the allocator is dropped. + +use core::alloc::Layout; +use core::cell::RefCell; +use core::mem::MaybeUninit; +use core::pin::Pin; +use core::ptr::NonNull; + +struct Inner<'a> { + memory: &'a mut [MaybeUninit], + offset: usize, +} + +/// A bump allocator that uses a provided memory chunk +pub struct BumpAlloc<'a>(RefCell>); + +impl<'a> BumpAlloc<'a> { + /// Create a new bump allocator with the provided memory chunk + pub const fn new(memory: &'a mut [MaybeUninit]) -> Self { + Self(RefCell::new(Inner { memory, offset: 0 })) + } + + /// Allocate memory for an object and pin it + pub fn box_pin(&self, object: T) -> Result>, AllocError> + where + T: Sized, + { + let layout = Layout::new::(); + let ptr = self.alloc_raw(layout)?; + + // Safety: We just allocated the memory and it's properly aligned + unsafe { + let ptr = ptr.as_ptr() as *mut T; + ptr.write(object); + Ok(Pin::new_unchecked(BumpBox { + ptr: NonNull::new_unchecked(ptr), + _allocator: core::marker::PhantomData, + })) + } + } + + fn alloc_raw(&self, layout: Layout) -> Result, AllocError> { + let size = layout.size(); + let align = layout.align(); + + let mut inner = self.0.borrow_mut(); + + let current_offset = inner.offset; + + // Align the offset + let aligned_offset = (current_offset + align - 1) & !(align - 1); + let new_offset = aligned_offset + size; + + if new_offset > inner.memory.len() { + return Err(AllocError); + } + + inner.offset = new_offset; + + // Safety: We checked bounds and alignment + let ptr = unsafe { inner.memory.as_mut_ptr().add(aligned_offset) as *mut u8 }; + + Ok(unsafe { NonNull::new_unchecked(ptr) }) + } + + /// Get the current memory usage + pub fn used(&self) -> usize { + self.0.borrow().offset + } + + /// Get the total capacity + pub fn capacity(&self) -> usize { + self.0.borrow().memory.len() + } +} + +/// A box-like container that uses bump allocation +pub struct BumpBox<'a, T> { + ptr: NonNull, + _allocator: core::marker::PhantomData<&'a ()>, +} + +impl core::ops::Deref for BumpBox<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { self.ptr.as_ref() } + } +} + +impl core::ops::DerefMut for BumpBox<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { self.ptr.as_mut() } + } +} + +impl core::future::Future for BumpBox<'_, T> { + type Output = T::Output; + + fn poll( + self: Pin<&mut Self>, + cx: &mut core::task::Context<'_>, + ) -> core::task::Poll { + // Safety: We maintain the pin invariant + let future = unsafe { self.map_unchecked_mut(|s| &mut **s) }; + future.poll(cx) + } +} + +impl Drop for BumpBox<'_, T> { + fn drop(&mut self) { + // Safety: The pointer is valid and we own the data + unsafe { + self.ptr.as_ptr().drop_in_place(); + } + } +} + +/// Error type for allocation failures +#[derive(Debug)] +pub struct AllocError; + +impl core::fmt::Display for AllocError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Bump allocator out of memory") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for AllocError {} diff --git a/src/eth.rs b/src/eth.rs index 5322e1e..0537920 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -14,6 +14,7 @@ use rs_matter::transport::network::NoNetwork; use rs_matter::utils::init::{init, Init}; use rs_matter::utils::select::Coalesce; +use crate::bump_alloc::BumpAlloc; use crate::mdns::Mdns; use crate::nal::NetStack; use crate::network::{Embedding, Network}; @@ -207,6 +208,34 @@ where .await } + pub async fn run_preex2( + &self, + net_stack: U, + netif: N, + mdns: M, + store: &SharedKvBlobStore<'_, S>, + handler: H, + user: X, + alloc: &BumpAlloc<'_>, + ) -> Result<(), Error> + where + U: NetStack, + N: NetifDiag + NetChangeNotif, + M: Mdns, + S: KvBlobStore, + H: AsyncHandler + AsyncMetadata, + X: UserTask, + { + self.run_with_memory( + PreexistingEthernet::new(net_stack, netif, mdns), + store, + handler, + user, + alloc, + ) + .await + } + /// Run the Matter stack for an Ethernet network. /// /// Parameters: @@ -247,6 +276,50 @@ where select(&mut net_task, &mut persist_task).coalesce().await } + /// Run the Matter stack for an Ethernet network with bump allocation. + /// Uses the provided memory buffer for allocations instead of heap. + /// + /// Parameters: + /// - `ethernet` - a user-provided `Ethernet` implementation + /// - `persist` - a user-provided `Persist` implementation + /// - `handler` - a user-provided DM handler implementation + /// - `user` - a user-provided future that will be polled only when the netif interface is up + /// - `memory` - a memory buffer for internal allocations (recommended size: 16KB+) + pub async fn run_with_memory( + &self, + ethernet: N, + store: &SharedKvBlobStore<'_, S>, + handler: H, + user: X, + alloc: &BumpAlloc<'_>, + ) -> Result<(), Error> + where + N: Ethernet, + S: KvBlobStore, + H: AsyncHandler + AsyncMetadata, + X: UserTask, + { + info!("Matter Stack memory: {}B", core::mem::size_of_val(self)); + + let persist = self.create_persist(store); + + persist.load().await?; + + self.matter().reset_transport()?; + + if !self.is_commissioned().await? { + self.matter() + .enable_basic_commissioning(DiscoveryCapabilities::IP, 0) + .await?; // TODO + } + + let mut net_task = + unwrap!(alloc.box_pin(self.run_ethernet_with_memory(ethernet, handler, user, alloc))); + let mut persist_task = unwrap!(alloc.box_pin(self.run_psm(&persist))); + + select(&mut net_task, &mut persist_task).coalesce().await + } + async fn run_ethernet(&self, mut ethernet: N, handler: H, user: X) -> Result<(), Error> where N: Ethernet, @@ -255,6 +328,25 @@ where { Ethernet::run(&mut ethernet, MatterStackEthernetTask(self, handler, user)).await } + + async fn run_ethernet_with_memory( + &self, + mut ethernet: N, + handler: H, + user: X, + alloc: &BumpAlloc<'_>, + ) -> Result<(), Error> + where + N: Ethernet, + H: AsyncHandler + AsyncMetadata, + X: UserTask, + { + Ethernet::run( + &mut ethernet, + MatterStackEthernetTaskWithMemory(self, handler, user, alloc), + ) + .await + } } struct MatterStackEthernetTask<'a, E, H, X>(&'a MatterStack<'a, Eth>, H, X) @@ -263,6 +355,17 @@ where H: AsyncMetadata + AsyncHandler, X: UserTask; +struct MatterStackEthernetTaskWithMemory<'a, 'b, E, H, X>( + &'a MatterStack<'a, Eth>, + H, + X, + &'a BumpAlloc<'b>, +) +where + E: Embedding + 'static, + H: AsyncMetadata + AsyncHandler, + X: UserTask; + impl EthernetTask for MatterStackEthernetTask<'_, E, H, X> where E: Embedding + 'static, @@ -295,3 +398,49 @@ where .await } } + +impl EthernetTask for MatterStackEthernetTaskWithMemory<'_, '_, E, H, X> +where + E: Embedding + 'static, + H: AsyncMetadata + AsyncHandler, + X: UserTask, +{ + async fn run(&mut self, net_stack: S, netif: C, mut mdns: M) -> Result<(), Error> + where + S: NetStack, + C: NetifDiag + NetChangeNotif, + M: Mdns, + { + info!("Ethernet driver started with bump allocator"); + + // Use bump allocator instead of stack allocation for largest futures + let net_task = self + .3 + .box_pin(self.0.run_oper_net( + &net_stack, + &netif, + &mut mdns, + core::future::pending(), + Option::<(NoNetwork, NoNetwork)>::None, + )) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; + + let handler = self.0.root_handler(&(), &true, &netif, &self.1); + let handler_task = self + .3 + .box_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; + + let mut user_task = pin!(self.2.run(&net_stack, &netif)); + + info!( + "Bump allocator usage: {}/{} bytes", + self.3.used(), + self.3.capacity() + ); + + select3(net_task, handler_task, &mut user_task) + .coalesce() + .await + } +} diff --git a/src/lib.rs b/src/lib.rs index 2be3a17..a5d69ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,8 @@ #![warn(clippy::large_stack_frames)] #![warn(clippy::large_types_passed_by_value)] +pub mod bump_alloc; + use core::future::Future; use core::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6}; use core::pin::pin; diff --git a/src/wireless.rs b/src/wireless.rs index 901e8f0..457bccb 100644 --- a/src/wireless.rs +++ b/src/wireless.rs @@ -19,6 +19,7 @@ use rs_matter::utils::init::{init, zeroed, Init}; use rs_matter::utils::select::Coalesce; use rs_matter::utils::sync::{blocking, IfMutex}; +use crate::bump_alloc::BumpAlloc; use crate::mdns::Mdns; use crate::nal::NetStack; use crate::network::{Embedding, Network}; @@ -336,3 +337,14 @@ where M: RawMutex + Send + Sync + 'static, T: WirelessNetwork, E: Embedding + 'static; + +pub(crate) struct MatterStackWirelessTaskWithMemory<'a, 'b, M, T, E, H, U>( + &'a MatterStack<'a, WirelessBle>, + H, + U, + &'a BumpAlloc<'b>, +) +where + M: RawMutex + Send + Sync + 'static, + T: WirelessNetwork, + E: Embedding + 'static; diff --git a/src/wireless/thread.rs b/src/wireless/thread.rs index e54c4e7..94672bc 100644 --- a/src/wireless/thread.rs +++ b/src/wireless/thread.rs @@ -20,11 +20,12 @@ use rs_matter::transport::network::btp::GattPeripheral; use rs_matter::transport::network::NoNetwork; use rs_matter::utils::select::Coalesce; +use crate::bump_alloc::BumpAlloc; use crate::mdns::Mdns; use crate::nal::NetStack; use crate::network::Embedding; use crate::persist::{KvBlobStore, SharedKvBlobStore}; -use crate::wireless::{GattTask, MatterStackWirelessTask}; +use crate::wireless::{GattTask, MatterStackWirelessTask, MatterStackWirelessTaskWithMemory}; use crate::UserTask; use super::{Gatt, PreexistingWireless, WirelessMatterStack}; @@ -147,6 +148,43 @@ where select(&mut net_task, &mut persist_task).coalesce().await } + /// Run the Matter stack for a Thread network with bump allocation. + /// Uses the provided memory buffer for allocations instead of heap. + /// + /// Parameters: + /// - `thread` - a user-provided Thread implementation + /// - `store` - a user-provided persistent storage implementation + /// - `handler` - a user-provided DM handler implementation + /// - `user` - a user-provided future that will be polled only when the netif interface is up + /// - `memory` - a memory buffer for internal allocations (recommended size: 16KB+) + pub async fn run_with_memory( + &'static self, + thread: W, + store: &SharedKvBlobStore<'_, S>, + handler: H, + user: U, + alloc: &'static BumpAlloc<'_>, + ) -> Result<(), Error> + where + W: Thread + Gatt, + S: KvBlobStore, + H: AsyncHandler + AsyncMetadata, + U: UserTask, + { + info!("Matter Stack memory: {}B", core::mem::size_of_val(self)); + + let persist = self.create_persist(store); + + persist.load().await?; + + self.matter().reset_transport()?; + + let mut net_task = pin!(self.run_thread_with_memory(thread, handler, user, alloc)); + let mut persist_task = pin!(self.run_psm(&persist)); + + select(&mut net_task, &mut persist_task).coalesce().await + } + async fn run_thread_coex( &'static self, mut thread: W, @@ -201,6 +239,45 @@ where } } + async fn run_thread_with_memory( + &'static self, + mut thread: W, + handler: H, + mut user: U, + alloc: &'static BumpAlloc<'_>, + ) -> Result<(), Error> + where + W: Thread + Gatt, + H: AsyncHandler + AsyncMetadata, + U: UserTask, + { + loop { + let commissioned = self.is_commissioned().await?; + + if !commissioned { + self.matter() + .enable_basic_commissioning(DiscoveryCapabilities::BLE, 0) + .await?; // TODO + + Gatt::run( + &mut thread, + MatterStackWirelessTaskWithMemory(self, &handler, &mut user, alloc), + ) + .await?; + } + + if commissioned { + self.matter().disable_commissioning()?; + } + + Thread::run( + &mut thread, + MatterStackWirelessTaskWithMemory(self, &handler, &mut user, alloc), + ) + .await?; + } + } + /// Return a metadata for the root (Endpoint 0) of the Matter Node /// configured for BLE+Thread network. pub const fn root_endpoint() -> Endpoint<'static> { @@ -522,3 +599,163 @@ where .await } } + +impl GattTask + for MatterStackWirelessTaskWithMemory<'static, '_, M, wireless::Thread, E, H, X> +where + M: RawMutex + Send + Sync + 'static, + E: Embedding + 'static, + H: AsyncMetadata + AsyncHandler, +{ + async fn run

(&mut self, peripheral: P) -> Result<(), Error> + where + P: GattPeripheral, + { + let net_ctl = NetCtlWithStatusImpl::new( + &self.0.network.net_state, + NoopWirelessNetCtl::new(NetworkType::Thread), + ); + + // Use bump allocator instead of stack allocation for largest futures + let btp_task = self + .3 + .box_pin(self.0.run_btp(peripheral)) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; + + let handler = self.0.root_handler(&(), &(), &net_ctl, &false, &self.1); + let handler_task = self + .3 + .box_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; + + info!( + "Bump allocator usage: {}/{} bytes", + self.3.used(), + self.3.capacity() + ); + + select(btp_task, handler_task).coalesce().await + } +} + +impl ThreadTask + for MatterStackWirelessTaskWithMemory<'static, '_, M, wireless::Thread, E, H, X> +where + M: RawMutex + Send + Sync + 'static, + E: Embedding + 'static, + H: AsyncMetadata + AsyncHandler, + X: UserTask, +{ + async fn run( + &mut self, + net_stack: S, + netif: N, + net_ctl: C, + mut mdns: D, + ) -> Result<(), Error> + where + S: NetStack, + N: NetifDiag + NetChangeNotif, + C: NetCtl + ThreadDiag + NetChangeNotif, + D: Mdns, + { + info!("Thread driver started with bump allocator"); + + let mut buf = self.0.network.creds_buf.lock().await; + + let mut mgr = WirelessMgr::new(&self.0.network.networks, &net_ctl, &mut buf); + + let stack = &mut self.0; + + // Use bump allocator instead of stack allocation for largest futures + let net_task = self + .3 + .box_pin(stack.run_oper_net( + &net_stack, + &netif, + &mut mdns, + core::future::pending(), + Option::<(NoNetwork, NoNetwork)>::None, + )) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; + + let mut mgr_task = pin!(mgr.run()); + + let net_ctl_s = NetCtlWithStatusImpl::new(&self.0.network.net_state, &net_ctl); + + let handler = self + .0 + .root_handler(&(), &netif, &net_ctl_s, &false, &self.1); + let handler_task = self + .3 + .box_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; + + let mut user_task = pin!(self.2.run(&net_stack, &netif)); + + info!( + "Bump allocator usage: {}/{} bytes", + self.3.used(), + self.3.capacity() + ); + + select4(net_task, &mut mgr_task, handler_task, &mut user_task) + .coalesce() + .await + } +} + +impl ThreadCoexTask + for MatterStackWirelessTaskWithMemory<'static, '_, M, wireless::Thread, E, H, X> +where + M: RawMutex + Send + Sync + 'static, + E: Embedding + 'static, + H: AsyncMetadata + AsyncHandler, + X: UserTask, +{ + async fn run( + &mut self, + net_stack: S, + netif: N, + net_ctl: C, + mut mdns: D, + mut gatt: G, + ) -> Result<(), Error> + where + S: NetStack, + N: NetifDiag + NetChangeNotif, + C: NetCtl + ThreadDiag + NetChangeNotif, + D: Mdns, + G: GattPeripheral, + { + info!("Thread and BLE drivers started with bump allocator"); + + let stack = &mut self.0; + + // Use bump allocator instead of stack allocation for largest futures + let net_task = self + .3 + .box_pin(stack.run_net_coex(&net_stack, &netif, &net_ctl, &mut mdns, &mut gatt)) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; + + let net_ctl_s = NetCtlWithStatusImpl::new(&self.0.network.net_state, &net_ctl); + + let handler = self.0.root_handler(&(), &netif, &net_ctl_s, &true, &self.1); + let handler_task = self + .3 + .box_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; + + let mut user_task = pin!(self.2.run(&net_stack, &netif)); + + info!( + "Bump allocator usage: {}/{} bytes", + self.3.used(), + self.3.capacity() + ); + + select3(net_task, handler_task, &mut user_task) + .coalesce() + .await + } +} diff --git a/src/wireless/wifi.rs b/src/wireless/wifi.rs index 206239c..5676cd9 100644 --- a/src/wireless/wifi.rs +++ b/src/wireless/wifi.rs @@ -20,11 +20,12 @@ use rs_matter::transport::network::btp::GattPeripheral; use rs_matter::transport::network::NoNetwork; use rs_matter::utils::select::Coalesce; +use crate::bump_alloc::BumpAlloc; use crate::mdns::Mdns; use crate::nal::NetStack; use crate::network::Embedding; use crate::persist::{KvBlobStore, SharedKvBlobStore}; -use crate::wireless::MatterStackWirelessTask; +use crate::wireless::{MatterStackWirelessTask, MatterStackWirelessTaskWithMemory}; use crate::UserTask; use super::{Gatt, GattTask, PreexistingWireless, WirelessMatterStack}; @@ -147,6 +148,44 @@ where select(&mut net_task, &mut persist_task).coalesce().await } + /// Run the Matter stack for a WiFi network with bump allocation. + /// Uses the provided memory buffer for allocations instead of heap. + /// + /// Parameters: + /// - `wifi` - a user-provided WiFi implementation + /// - `store` - a user-provided persistent storage implementation + /// - `handler` - a user-provided DM handler implementation + /// - `user` - a user-provided future that will be polled only when the netif interface is up + /// - `memory` - a memory buffer for internal allocations (recommended size: 16KB+) + pub async fn run_with_memory( + &'static self, + wifi: W, + store: &SharedKvBlobStore<'_, S>, + handler: H, + user: U, + alloc: &'static BumpAlloc<'_>, + ) -> Result<(), Error> + where + W: Wifi + Gatt, + S: KvBlobStore, + H: AsyncHandler + AsyncMetadata, + U: UserTask, + { + info!("Matter Stack memory: {}B", core::mem::size_of_val(self)); + + let persist = self.create_persist(store); + + persist.load().await?; + + self.matter().reset_transport()?; + + let mut net_task = + unwrap!(alloc.box_pin(self.run_wifi_with_memory(wifi, handler, user, alloc))); + let mut persist_task = unwrap!(alloc.box_pin(self.run_psm(&persist))); + + select(&mut net_task, &mut persist_task).coalesce().await + } + async fn run_wifi_coex( &'static self, mut wifi: W, @@ -199,6 +238,45 @@ where } } + async fn run_wifi_with_memory( + &'static self, + mut wifi: W, + handler: H, + mut user: U, + alloc: &'static BumpAlloc<'_>, + ) -> Result<(), Error> + where + W: Wifi + Gatt, + H: AsyncHandler + AsyncMetadata, + U: UserTask, + { + loop { + let commissioned = self.is_commissioned().await?; + + if !commissioned { + self.matter() + .enable_basic_commissioning(DiscoveryCapabilities::BLE, 0) + .await?; // TODO + + Gatt::run( + &mut wifi, + MatterStackWirelessTaskWithMemory(self, &handler, &mut user, alloc), + ) + .await?; + } + + if commissioned { + self.matter().disable_commissioning()?; + } + + Wifi::run( + &mut wifi, + MatterStackWirelessTaskWithMemory(self, &handler, &mut user, alloc), + ) + .await?; + } + } + /// Return a metadata for the root (Endpoint 0) of the Matter Node /// configured for BLE+Wifi network. pub const fn root_endpoint() -> Endpoint<'static> { @@ -520,3 +598,162 @@ where .await } } + +impl GattTask + for MatterStackWirelessTaskWithMemory<'static, '_, M, wireless::Wifi, E, H, U> +where + M: RawMutex + Send + Sync + 'static, + E: Embedding + 'static, + H: AsyncMetadata + AsyncHandler, +{ + async fn run

(&mut self, peripheral: P) -> Result<(), Error> + where + P: GattPeripheral, + { + let net_ctl = NetCtlWithStatusImpl::new( + &self.0.network.net_state, + NoopWirelessNetCtl::new(NetworkType::Wifi), + ); + + // Use bump allocator instead of stack allocation for largest futures + let btp_task = self + .3 + .box_pin(self.0.run_btp(peripheral)) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; + + let handler = self.0.root_handler(&(), &(), &net_ctl, &false, &self.1); + let handler_task = self + .3 + .box_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; + + info!( + "Bump allocator usage: {}/{} bytes", + self.3.used(), + self.3.capacity() + ); + + select(btp_task, handler_task).coalesce().await + } +} + +impl WifiTask for MatterStackWirelessTaskWithMemory<'_, '_, M, wireless::Wifi, E, H, X> +where + M: RawMutex + Send + Sync + 'static, + E: Embedding + 'static, + H: AsyncMetadata + AsyncHandler, + X: UserTask, +{ + async fn run( + &mut self, + net_stack: S, + netif: N, + net_ctl: C, + mut mdns: D, + ) -> Result<(), Error> + where + S: NetStack, + N: NetifDiag + NetChangeNotif, + C: NetCtl + WifiDiag + NetChangeNotif, + D: Mdns, + { + info!("WiFi driver started with bump allocator"); + + let mut buf = self.0.network.creds_buf.lock().await; + + let mut mgr = WirelessMgr::new(&self.0.network.networks, &net_ctl, &mut buf); + + let stack = &mut self.0; + + // Use bump allocator instead of stack allocation for largest futures + let net_task = self + .3 + .box_pin(stack.run_oper_net( + &net_stack, + &netif, + &mut mdns, + core::future::pending(), + Option::<(NoNetwork, NoNetwork)>::None, + )) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; + + let mut mgr_task = pin!(mgr.run()); + + let net_ctl_s = NetCtlWithStatusImpl::new(&self.0.network.net_state, &net_ctl); + + let handler = self + .0 + .root_handler(&(), &netif, &net_ctl_s, &false, &self.1); + let handler_task = self + .3 + .box_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; + + let mut user_task = pin!(self.2.run(&net_stack, &netif)); + + info!( + "Bump allocator usage: {}/{} bytes", + self.3.used(), + self.3.capacity() + ); + + select4(net_task, &mut mgr_task, handler_task, &mut user_task) + .coalesce() + .await + } +} + +impl WifiCoexTask + for MatterStackWirelessTaskWithMemory<'static, '_, M, wireless::Wifi, E, H, X> +where + M: RawMutex + Send + Sync + 'static, + E: Embedding + 'static, + H: AsyncMetadata + AsyncHandler, + X: UserTask, +{ + async fn run( + &mut self, + net_stack: S, + netif: N, + net_ctl: C, + mut mdns: D, + mut gatt: G, + ) -> Result<(), Error> + where + S: NetStack, + N: NetifDiag + NetChangeNotif, + C: NetCtl + WifiDiag + NetChangeNotif, + D: Mdns, + G: GattPeripheral, + { + info!("WiFi and BLE drivers started with bump allocator"); + + let stack = &mut self.0; + + // Use bump allocator instead of stack allocation for largest futures + let net_task = self + .3 + .box_pin(stack.run_net_coex(&net_stack, &netif, &net_ctl, &mut mdns, &mut gatt)) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; + + let net_ctl_s = NetCtlWithStatusImpl::new(&self.0.network.net_state, &net_ctl); + + let handler = self.0.root_handler(&(), &netif, &net_ctl_s, &true, &self.1); + let handler_task = self + .3 + .box_pin(self.0.run_handler((&self.1, handler))) + .map_err(|_| rs_matter::error::Error::new(rs_matter::error::ErrorCode::NoMemory))?; + + let mut user_task = pin!(self.2.run(&net_stack, &netif)); + + info!( + "Bump allocator usage: {}/{} bytes", + self.3.used(), + self.3.capacity() + ); + + select3(net_task, handler_task, &mut user_task) + .coalesce() + .await + } +}