diff --git a/Cargo.toml b/Cargo.toml index c64f4a7..2f267dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,8 @@ rust-version = "1.77" [patch.crates-io] #rs-matter = { git = "https://github.com/project-chip/rs-matter" } -rs-matter = { git = "https://github.com/sysgrok/rs-matter", branch = "next" } +#rs-matter = { git = "https://github.com/sysgrok/rs-matter", branch = "next" } +rs-matter = { git = "https://github.com/sysgrok/rs-matter", branch = "inband-persistence" } #rs-matter = { path = "../rs-matter/rs-matter" } #edge-nal = { git = "https://github.com/sysgrok/edge-net" } #edge-nal-std = { git = "https://github.com/sysgrok/edge-net" } diff --git a/README.md b/README.md index d54b434..3ee4287 100644 --- a/README.md +++ b/README.md @@ -66,24 +66,24 @@ use core::pin::pin; use log::info; -use rs_matter::crypto::{default_crypto, Crypto}; -use rs_matter::dm::clusters::on_off::test::TestOnOffDeviceLogic; -use rs_matter::dm::clusters::on_off::OnOffHooks; -use rs_matter::dm::devices::test::DAC_PRIVKEY; use rs_matter_stack::eth::EthMatterStack; +use rs_matter_stack::matter::crypto::{default_crypto, Crypto}; use rs_matter_stack::matter::dm::clusters::desc; use rs_matter_stack::matter::dm::clusters::desc::ClusterHandler as _; use rs_matter_stack::matter::dm::clusters::on_off; +use rs_matter_stack::matter::dm::clusters::on_off::test::TestOnOffDeviceLogic; +use rs_matter_stack::matter::dm::clusters::on_off::OnOffHooks; +use rs_matter_stack::matter::dm::devices::test::DAC_PRIVKEY; use rs_matter_stack::matter::dm::devices::test::{TEST_DEV_ATT, TEST_DEV_COMM, TEST_DEV_DET}; use rs_matter_stack::matter::dm::devices::DEV_TYPE_ON_OFF_LIGHT; use rs_matter_stack::matter::dm::networks::unix::UnixNetifs; use rs_matter_stack::matter::dm::{Async, Dataver, Endpoint, Node}; use rs_matter_stack::matter::dm::{EmptyHandler, EpClMatcher}; use rs_matter_stack::matter::error::Error; +use rs_matter_stack::matter::persist::DirKvBlobStore; use rs_matter_stack::matter::utils::init::InitMaybeUninit; use rs_matter_stack::matter::{clusters, devices}; use rs_matter_stack::mdns::ZeroconfMdns; -use rs_matter_stack::persist::DirKvBlobStore; use static_cell::StaticCell; @@ -146,10 +146,12 @@ fn main() -> Result<(), Error> { Async(desc::DescHandler::new(Dataver::new_rand(&mut rand)).adapt()), ); - // Create the persister & load any previously saved state - let persist = futures_lite::future::block_on( - stack.create_persist_with_comm_window(&crypto, DirKvBlobStore::new_default()), - )?; + // Create the KV BLOB store and load any previously saved state of `rs-matter` + let mut kv = DirKvBlobStore::new_default(); + futures_lite::future::block_on(stack.startup(&crypto, &mut kv))?; + + // Wrap the KV BLOB store as a shared reference, so that it can be used both by `rs-matter` and the user + let kv = stack.create_shared_kv(kv)?; // Run the Matter stack with our handler // Using `pin!` is completely optional, but reduces the size of the final future @@ -160,12 +162,12 @@ fn main() -> Result<(), Error> { UnixNetifs, // Will use the mDNS implementation based on the `zeroconf` crate ZeroconfMdns, - // Will persist in `/rs-matter` - &persist, // The crypto provider &crypto, // Our `AsyncHandler` + `AsyncMetadata` impl (NODE, handler), + // Will persist in `/rs-matter` + &kv, // No user task future to run (), )); diff --git a/examples/light.rs b/examples/light.rs index a8a0aeb..7ed6a27 100644 --- a/examples/light.rs +++ b/examples/light.rs @@ -12,14 +12,14 @@ use core::pin::pin; use log::info; -use rs_matter::crypto::{default_crypto, Crypto}; -use rs_matter::dm::clusters::on_off::test::TestOnOffDeviceLogic; -use rs_matter::dm::clusters::on_off::OnOffHooks; -use rs_matter::dm::devices::test::DAC_PRIVKEY; use rs_matter_stack::ble::BluerGattPeripheral; +use rs_matter_stack::matter::crypto::{default_crypto, Crypto}; use rs_matter_stack::matter::dm::clusters::desc::{ClusterHandler as _, DescHandler}; use rs_matter_stack::matter::dm::clusters::net_comm::NetworkType; use rs_matter_stack::matter::dm::clusters::on_off; +use rs_matter_stack::matter::dm::clusters::on_off::test::TestOnOffDeviceLogic; +use rs_matter_stack::matter::dm::clusters::on_off::OnOffHooks; +use rs_matter_stack::matter::dm::devices::test::DAC_PRIVKEY; use rs_matter_stack::matter::dm::devices::test::{TEST_DEV_ATT, TEST_DEV_COMM, TEST_DEV_DET}; use rs_matter_stack::matter::dm::devices::DEV_TYPE_ON_OFF_LIGHT; use rs_matter_stack::matter::dm::networks::unix::UnixNetifs; @@ -27,10 +27,10 @@ use rs_matter_stack::matter::dm::networks::wireless::NoopWirelessNetCtl; use rs_matter_stack::matter::dm::{Async, Dataver, Endpoint, Node}; use rs_matter_stack::matter::dm::{EmptyHandler, EpClMatcher}; use rs_matter_stack::matter::error::Error; +use rs_matter_stack::matter::persist::DirKvBlobStore; use rs_matter_stack::matter::utils::init::InitMaybeUninit; use rs_matter_stack::matter::{clusters, devices}; use rs_matter_stack::mdns::ZeroconfMdns; -use rs_matter_stack::persist::DirKvBlobStore; use rs_matter_stack::wireless::PreexistingWireless; use rs_matter_stack::wireless::WifiMatterStack; @@ -96,10 +96,12 @@ fn main() -> Result<(), Error> { Async(DescHandler::new(Dataver::new_rand(&mut rand)).adapt()), ); - // Create the persister & load any previously saved state - let persist = futures_lite::future::block_on( - stack.create_persist_with_comm_window(&crypto, DirKvBlobStore::new_default()), - )?; + // Create the KV BLOB store and load any previously saved state of `rs-matter` + let mut kv = DirKvBlobStore::new_default(); + futures_lite::future::block_on(stack.startup(&crypto, &mut kv))?; + + // Wrap the KV BLOB store as a shared reference, so that it can be used both by `rs-matter` and the user + let kv = stack.create_shared_kv(kv)?; // Run the Matter stack with our handler // Using `pin!` is completely optional, but reduces the size of the final future @@ -116,12 +118,12 @@ fn main() -> Result<(), Error> { // The Bluetooth transport implementation based on the `bluer` crate. BluerGattPeripheral::new(None), ), - // Will persist in `/rs-matter` - &persist, // The crypto provider, used for all the cryptographic operations of the stack &crypto, // Our `AsyncHandler` + `AsyncMetadata` impl (NODE, handler), + // Will persist in `/rs-matter` + &kv, // No user task to run (), )); diff --git a/examples/light_eth.rs b/examples/light_eth.rs index ec1d21f..c682fc2 100644 --- a/examples/light_eth.rs +++ b/examples/light_eth.rs @@ -14,24 +14,24 @@ use core::pin::pin; use log::info; -use rs_matter::crypto::{default_crypto, Crypto}; -use rs_matter::dm::clusters::on_off::test::TestOnOffDeviceLogic; -use rs_matter::dm::clusters::on_off::OnOffHooks; -use rs_matter::dm::devices::test::DAC_PRIVKEY; use rs_matter_stack::eth::EthMatterStack; +use rs_matter_stack::matter::crypto::{default_crypto, Crypto}; use rs_matter_stack::matter::dm::clusters::desc; use rs_matter_stack::matter::dm::clusters::desc::ClusterHandler as _; use rs_matter_stack::matter::dm::clusters::on_off; +use rs_matter_stack::matter::dm::clusters::on_off::test::TestOnOffDeviceLogic; +use rs_matter_stack::matter::dm::clusters::on_off::OnOffHooks; +use rs_matter_stack::matter::dm::devices::test::DAC_PRIVKEY; use rs_matter_stack::matter::dm::devices::test::{TEST_DEV_ATT, TEST_DEV_COMM, TEST_DEV_DET}; use rs_matter_stack::matter::dm::devices::DEV_TYPE_ON_OFF_LIGHT; use rs_matter_stack::matter::dm::networks::unix::UnixNetifs; use rs_matter_stack::matter::dm::{Async, Dataver, Endpoint, Node}; use rs_matter_stack::matter::dm::{EmptyHandler, EpClMatcher}; use rs_matter_stack::matter::error::Error; +use rs_matter_stack::matter::persist::DirKvBlobStore; use rs_matter_stack::matter::utils::init::InitMaybeUninit; use rs_matter_stack::matter::{clusters, devices}; use rs_matter_stack::mdns::ZeroconfMdns; -use rs_matter_stack::persist::DirKvBlobStore; use static_cell::StaticCell; @@ -94,10 +94,12 @@ fn main() -> Result<(), Error> { Async(desc::DescHandler::new(Dataver::new_rand(&mut rand)).adapt()), ); - // Create the persister & load any previously saved state - let persist = futures_lite::future::block_on( - stack.create_persist_with_comm_window(&crypto, DirKvBlobStore::new_default()), - )?; + // Create the KV BLOB store and load any previously saved state of `rs-matter` + let mut kv = DirKvBlobStore::new_default(); + futures_lite::future::block_on(stack.startup(&crypto, &mut kv))?; + + // Wrap the KV BLOB store as a shared reference, so that it can be used both by `rs-matter` and the user + let kv = stack.create_shared_kv(kv)?; // Run the Matter stack with our handler // Using `pin!` is completely optional, but reduces the size of the final future @@ -108,12 +110,12 @@ fn main() -> Result<(), Error> { UnixNetifs, // Will use the mDNS implementation based on the `zeroconf` crate ZeroconfMdns, - // Will persist in `/rs-matter` - &persist, // The crypto provider &crypto, // Our `AsyncHandler` + `AsyncMetadata` impl (NODE, handler), + // Will persist in `/rs-matter` + &kv, // No user task future to run (), )); diff --git a/src/eth.rs b/src/eth.rs index 7c91333..cea1b96 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -1,6 +1,7 @@ +use core::borrow::BorrowMut; use core::future::Future; -use embassy_futures::select::{select, select4}; +use embassy_futures::select::select4; use rs_matter::crypto::{Crypto, RngCore}; use rs_matter::dm::clusters::gen_comm::CommPolicy; @@ -11,6 +12,7 @@ use rs_matter::dm::networks::NetChangeNotif; use rs_matter::dm::{AsyncHandler, AsyncMetadata, Endpoint}; use rs_matter::error::Error; use rs_matter::pairing::DiscoveryCapabilities; +use rs_matter::persist::{KvBlobStore, KvBlobStoreAccess}; use rs_matter::transport::network::NoNetwork; use rs_matter::utils::init::{init, Init}; use rs_matter::utils::select::Coalesce; @@ -18,9 +20,8 @@ use rs_matter::utils::select::Coalesce; use crate::mdns::Mdns; use crate::nal::NetStack; use crate::network::{Embedding, Network}; -use crate::persist::{KvBlobStore, MatterPersist}; use crate::private::Sealed; -use crate::{pin_alloc, MatterStack, UserTask}; +use crate::{pin_alloc, DummyNotify, MatterStack, UserTask}; /// An implementation of the `Network` trait for Ethernet. /// @@ -44,11 +45,6 @@ where { const INIT: Self = Self { embedding: E::INIT }; - type PersistContext<'a> - = () - where - E: 'a; - type Embedding<'a> = E where @@ -64,8 +60,6 @@ where DiscoveryCapabilities::IP } - fn persist_context(&self) -> Self::PersistContext<'_> {} - fn embedding(&self) -> &Self::Embedding<'_> { &self.embedding } @@ -74,9 +68,6 @@ where // A type alias for a Matter stack running over Ethernet. pub type EthMatterStack<'a, const B: usize, E = ()> = MatterStack<'a, B, Eth>; -/// A type alias for the Matter Persister created by calling `EthMatterStack::create_persist`. -pub type EthMatterPersist<'a, S> = MatterPersist<'a, S, ()>; - /// A trait representing a task that needs access to the operational Ethernet interface /// (Network stack and Netif) to perform its work. pub trait EthernetTask { @@ -185,10 +176,44 @@ where ) } - /// Reset the Matter instance to the factory defaults putting it into a - /// Commissionable mode. - pub fn reset(&self) -> Result<(), Error> { - // TODO: Reset fabrics and ACLs + /// Reset the Matter instance to the factory defaults by removing all fabrics and basic info settings + pub async fn reset(&mut self, kv: S) -> Result<(), Error> + where + S: KvBlobStore, + { + let mut buf = unwrap!(self.store_buf.try_get()); + let buf = buf.borrow_mut(); + + self.matter.reset_persist(kv, buf).await + } + + /// Load the persisted state from the provided `KvBlobStore` implementation. + pub async fn load(&mut self, kv: S) -> Result<(), Error> + where + S: KvBlobStore, + { + let mut buf = unwrap!(self.store_buf.try_get()); + let buf = buf.borrow_mut(); + + self.matter.load_persist(kv, buf).await + } + + /// Run the startup sequence of the stack, which includes loading the persisted state + /// and opening the basic communication window if the device is not commissioned yet. + pub async fn startup(&mut self, crypto: C, kv: S) -> Result<(), Error> + where + C: Crypto, + S: KvBlobStore, + { + self.load(kv).await?; + + if !self.is_commissioned() { + info!("Device is not commissioned yet, opening commissioning window..."); + + self.open_basic_comm_window(crypto, &DummyNotify)?; + } else { + info!("Device is already commissioned"); + } Ok(()) } @@ -199,35 +224,35 @@ where /// - `net_stack` - a user-provided network stack implementation /// - `netif` - a user-provided `Netif` implementation for the Ethernet network /// - `mdns` - a user-provided mDNS implementation - /// - `persist` - an `EthMatterPersist` implementation instantiated on the stack with `create_persist` /// - `crypto` - a user-provided crypto implementation /// - `handler` - a user-provided DM handler implementation + /// - `kv` - a user-provided `KvBlobStoreAccess` implementation for loading the persisted state of the stack /// - `user` - a user-provided future that will be polled only when the netif interface is up #[allow(clippy::too_many_arguments)] - pub fn run_preex<'t, U, N, M, S, C, H, X>( + pub fn run_preex<'t, U, N, M, C, H, S, X>( &'t self, net_stack: U, netif: N, mdns: M, - persist: &'t EthMatterPersist<'_, S>, crypto: C, handler: H, + kv: S, user: X, ) -> impl Future> + 't where U: NetStack + 't, N: NetifDiag + NetChangeNotif + 't, M: Mdns + 't, - S: KvBlobStore + 't, C: Crypto + 't, H: AsyncHandler + AsyncMetadata + 't, + S: KvBlobStoreAccess + 't, X: UserTask + 't, { self.run( PreexistingEthernet::new(net_stack, netif, mdns), - persist, crypto, handler, + kv, user, ) } @@ -236,23 +261,23 @@ where /// /// # Arguments /// - `ethernet` - a user-provided `Ethernet` implementation - /// - `persist` - an `EthMatterPersist` implementation instantiated on the stack with `create_persist` /// - `crypto` - a user-provided crypto implementation /// - `handler` - a user-provided DM handler implementation + /// - `kv` - a user-provided `KvBlobStoreAccess` implementation for loading the persisted state of the stack /// - `user` - a user-provided future that will be polled only when the netif interface is up pub async fn run( &self, mut ethernet: N, - persist: &EthMatterPersist<'_, S>, crypto: C, handler: H, + kv: S, user: X, ) -> Result<(), Error> where N: Ethernet, - S: KvBlobStore, C: Crypto, H: AsyncHandler + AsyncMetadata, + S: KvBlobStoreAccess, X: UserTask, { let _lock = self.run_lock.lock().await; @@ -267,26 +292,27 @@ where self.matter().reset_transport()?; - let mut net_task = pin_alloc!( + let net_task = pin_alloc!( self.bump, - self.run_ethernet(&mut ethernet, crypto, handler, user) + self.run_ethernet(&mut ethernet, crypto, handler, kv, user) ); - let mut persist_task = pin_alloc!(self.bump, self.run_psm(persist)); - select(&mut net_task, &mut persist_task).coalesce().await + net_task.await } - fn run_ethernet<'t, N, C, H, X>( + fn run_ethernet<'t, N, C, H, S, X>( &'t self, ethernet: &'t mut N, crypto: C, handler: H, + kv: S, user: X, ) -> impl Future> + 't where N: Ethernet + 't, C: Crypto + 't, H: AsyncHandler + AsyncMetadata + 't, + S: KvBlobStoreAccess + 't, X: UserTask + 't, { Ethernet::run( @@ -295,35 +321,39 @@ where stack: self, crypto, handler, + kv, user_task: user, }, ) } } -struct MatterStackEthernetTask<'a, const B: usize, E, C, H, X> +struct MatterStackEthernetTask<'a, const B: usize, E, C, H, S, X> where E: Embedding, C: Crypto, H: AsyncMetadata + AsyncHandler, + S: KvBlobStoreAccess, X: UserTask, { stack: &'a MatterStack<'a, B, Eth>, crypto: C, handler: H, + kv: S, user_task: X, } -impl EthernetTask for MatterStackEthernetTask<'_, B, E, C, H, X> +impl EthernetTask for MatterStackEthernetTask<'_, B, E, C, H, S, X> where E: Embedding, C: Crypto, H: AsyncMetadata + AsyncHandler, + S: KvBlobStoreAccess, X: UserTask, { - async fn run(&mut self, net_stack: S, netif: I, mut mdns: M) -> Result<(), Error> + async fn run(&mut self, net_stack: N, netif: I, mut mdns: M) -> Result<(), Error> where - S: NetStack, + N: NetStack, I: NetifDiag + NetChangeNotif, M: Mdns, { @@ -332,7 +362,9 @@ where let handler = self.stack .root_handler(&(), &true, &netif, self.crypto.weak_rand()?, &self.handler); - let dm = self.stack.dm(&self.crypto, (&self.handler, handler)); + let dm = self + .stack + .dm(&self.crypto, (&self.handler, handler), &self.kv); let mut net_task = pin_alloc!( self.stack.bump, diff --git a/src/lib.rs b/src/lib.rs index ffb5153..c9d34e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,8 +21,6 @@ use edge_nal::{UdpBind, UdpSplit}; use embassy_futures::select::{select, select3, select_slice}; use embassy_time::Duration; -use persist::{KvBlobBuffer, KvBlobStore, MatterPersist, NetworkPersist}; - use rs_matter::crypto::Crypto; use rs_matter::dm::clusters::basic_info::BasicInfoConfig; use rs_matter::dm::clusters::dev_att::DeviceAttestation; @@ -36,6 +34,7 @@ use rs_matter::dm::{ }; use rs_matter::error::{Error, ErrorCode}; use rs_matter::pairing::qr::QrTextType; +use rs_matter::persist::{KvBlobStore, KvBlobStoreAccess}; use rs_matter::respond::{DefaultResponder, ExchangeHandler, Responder}; use rs_matter::sc::pase::MAX_COMM_WINDOW_TIMEOUT_SECS; use rs_matter::transport::network::{ @@ -52,7 +51,7 @@ use crate::bump::Bump; use crate::mdns::Mdns; use crate::nal::NetStack; use crate::network::Network; -use crate::persist::SharedKvBlobStore; +use crate::persist::{MatterKvBlobStoreBuf, MatterKvBlobStoreBufInstance, MatterSharedKvBlobStore}; #[cfg(feature = "std")] #[allow(unused_imports)] @@ -224,13 +223,14 @@ cfg_if! { const MAX_BUSY_RESPONDERS: usize = 2; -pub(crate) type MatterStackDataModel<'a, C, H> = DataModel< +pub(crate) type MatterStackDataModel<'a, C, H, S> = DataModel< 'a, MAX_SUBSCRIPTIONS, EVENTS_RINGBUF_SIZE, C, PooledBuffers, H, + S, >; /// The `MatterStack` struct is the main entry point for the Matter stack. @@ -252,7 +252,7 @@ where feature = "events-ringbuf-size-2048" ))] events: Events, - store_buf: PooledBuffers<1, KvBlobBuffer>, + store_buf: MatterKvBlobStoreBuf, bump: Bump, run_lock: IfMutex<()>, #[allow(unused)] @@ -303,7 +303,7 @@ where feature = "events-ringbuf-size-2048" ))] events: Events::new(epoch), - store_buf: PooledBuffers::new(0), + store_buf: MatterKvBlobStoreBuf::new(), bump: Bump::new(), run_lock: IfMutex::new(()), network: N::INIT, @@ -353,7 +353,7 @@ where buffers <- PooledBuffers::init(0), subscriptions <- Subscriptions::init(), events <- Events::init(epoch), - store_buf <- PooledBuffers::init(0), + store_buf <- MatterKvBlobStoreBuf::init(), bump <- Bump::init(), run_lock <- IfMutex::init(()), network <- N::init(), @@ -378,7 +378,7 @@ where ), buffers <- PooledBuffers::init(0), subscriptions <- Subscriptions::init(), - store_buf <- PooledBuffers::init(0), + store_buf <- MatterKvBlobStoreBuf::init(), bump <- Bump::init(), run_lock <- IfMutex::init(()), network <- N::init(), @@ -395,96 +395,36 @@ where self.matter.replace_dev_att(dev_att); } - /// Create a new `MatterPersist` instance for the Matter stack. - /// - /// # Arguments - /// - `store` - a reference to a `KvBlobStore` instance - pub fn create_persist<'t, S>(&'t self, store: S) -> MatterPersist<'t, S, N::PersistContext<'t>> - where - S: KvBlobStore + 't, - { - MatterPersist::new( - SharedKvBlobStore::new(store, &self.store_buf), - self.matter(), - self.network().persist_context(), - ) - } - - /// An "all in one" persistence initializer method. - /// - /// # Arguments - /// - `crypto` - a user-provided crypto implementation - /// - `store` - a reference to a `KvBlobStore` instance - /// - /// This method does the following: - /// - Create the Matter Persister for that stack; - /// - Load the stack from the just-created persister; - /// - Check if the state of the device designates a not-yet-commissioned device and if so: - /// - Open the commissioning window for 15 minutes; - /// - Print the device pairing code and QR text to the console - /// - Print the device QR code to the console - /// - /// This method is useful primarily for development/demo purposes. In production scenarios, - /// it is likely that the user would require more fine-graned control over when to open - /// the commissioning window, with what timeout, whether to print the QR code, etc. - pub async fn create_persist_with_comm_window<'t, C, S>( - &'t self, - crypto: C, - store: S, - ) -> Result>, Error> - where - C: Crypto, - S: KvBlobStore + 't, - { - // The data model is not created yet, so we don't have to notify anything - struct DummyNotify; - - impl DynBase for DummyNotify {} - - impl ChangeNotify for DummyNotify { - fn notify(&self, _endpoint_id: EndptId, _cluster_id: ClusterId, _attr_id: AttrId) {} - } - - let persist = self.create_persist(store); - - persist.load().await?; - - if !self.matter().is_commissioned() { - info!("Device is not commissioned yet, opening commissioning window..."); - - self.matter().open_basic_comm_window( - MAX_COMM_WINDOW_TIMEOUT_SECS, - crypto, - &DummyNotify, - )?; - self.matter() - .print_standard_qr_text(self.network.discovery_capabilities())?; - self.matter().print_standard_qr_code( - QrTextType::Unicode, - self.network.discovery_capabilities(), - )?; - } else { - info!("Device is already commissioned"); - } - - Ok(persist) - } - /// Get a reference to the `Matter` instance. pub const fn matter(&self) -> &Matter<'a> { &self.matter } - pub const fn store_buf(&self) -> &PooledBuffers<1, KvBlobBuffer> { - &self.store_buf - } - /// Get a reference to the `Network` instance. /// Useful when the user instantiates `MatterStack` with a custom network type. pub const fn network(&self) -> &N { &self.network } + /// Create a new `MatterSharedKvBlobStore` instance, which is used to read and write blobs from the storage. + /// + /// The user needs to provide a `KvBlobStore` implementation, which is used to actually read and write the blobs from the storage. + pub fn create_shared_kv(&self, kv: S) -> Result, Error> + where + S: KvBlobStore, + { + Ok(MatterSharedKvBlobStore::new(kv, self.kv_store_buf()?)) + } + + /// Get the buffer for the KV blob store, which is used to read and write blobs from the storage. + /// + /// Return an error if the buffer is currently locked by another operation, which means that the caller should wait and try again later. + pub fn kv_store_buf(&self) -> Result, Error> { + self.store_buf + .try_get() + .ok_or(ErrorCode::InvalidState.into()) + } + /// Notifies the Matter instance that there is a change in the state /// of one of the clusters' attributes. /// @@ -545,8 +485,31 @@ where // } /// Return information whether the Matter instance is already commissioned. - pub async fn is_commissioned(&self) -> Result { - Ok(self.matter().is_commissioned()) + pub fn is_commissioned(&self) -> bool { + self.matter().is_commissioned() + } + + /// Open the basic communication window, which allows commissioning tools to discover and commission the device. + /// + /// # Arguments + /// - `crypto` - a user-provided crypto implementation, necessary for the secure sessions establishment that happens in the basic communication window + /// - `notify` - a user-provided `ChangeNotify`; typically, `Data Model::change_notify`; used to notify the Matter instance about changes in the state of the clusters' attributes, so that it can notify commissioning tools about them + pub fn open_basic_comm_window( + &self, + crypto: C, + notify: &dyn ChangeNotify, + ) -> Result<(), Error> + where + C: Crypto, + { + self.matter() + .open_basic_comm_window(MAX_COMM_WINDOW_TIMEOUT_SECS, crypto, notify)?; + + self.matter() + .print_standard_qr_text(self.network.discovery_capabilities())?; + + self.matter() + .print_standard_qr_code(QrTextType::Unicode, self.network.discovery_capabilities()) } /// This method is a specialization of `run_transport_net` over the UDP transport (both IPv4 and IPv6). @@ -777,10 +740,11 @@ where } #[inline(always)] - fn dm(&self, crypto: C, handler: H) -> MatterStackDataModel<'_, C, H> + fn dm(&self, crypto: C, handler: H, kv: S) -> MatterStackDataModel<'_, C, H, S> where C: Crypto, H: AsyncHandler + AsyncMetadata, + S: KvBlobStoreAccess, { #[cfg(any( feature = "events-ringbuf-size-64", @@ -797,6 +761,7 @@ where &self.subscriptions, Some(&self.events), handler, + kv, ); #[cfg(not(any( @@ -814,15 +779,17 @@ where &self.subscriptions, None, handler, + kv, ); dm } - async fn run_dm(&self, dm: &MatterStackDataModel<'_, C, H>) -> Result<(), Error> + async fn run_dm(&self, dm: &MatterStackDataModel<'_, C, H, S>) -> Result<(), Error> where C: Crypto, H: AsyncHandler + AsyncMetadata, + S: KvBlobStoreAccess, { // TODO // Reset the Matter transport buffers and all sessions first @@ -834,10 +801,14 @@ where select(&mut responder, &mut dm_job).coalesce().await } - async fn run_dm_with_bump(&self, dm: &MatterStackDataModel<'_, C, H>) -> Result<(), Error> + async fn run_dm_with_bump( + &self, + dm: &MatterStackDataModel<'_, C, H, S>, + ) -> Result<(), Error> where C: Crypto, H: AsyncHandler + AsyncMetadata, + S: KvBlobStoreAccess, { // TODO // Reset the Matter transport buffers and all sessions first @@ -849,21 +820,14 @@ where select(&mut responder, &mut dm_job).coalesce().await } - fn run_psm<'t, S, C>( - &'t self, - persist: &'t MatterPersist<'_, S, C>, - ) -> impl Future> + 't - where - S: KvBlobStore, - C: NetworkPersist, - { - persist.run() - } - - async fn run_responder(&self, dm: &MatterStackDataModel<'_, C, H>) -> Result<(), Error> + async fn run_responder( + &self, + dm: &MatterStackDataModel<'_, C, H, S>, + ) -> Result<(), Error> where C: Crypto, H: AsyncHandler + AsyncMetadata, + S: KvBlobStoreAccess, { let responder = DefaultResponder::new(dm); @@ -874,13 +838,14 @@ where Ok(()) } - async fn run_responder_with_bump( + async fn run_responder_with_bump( &self, - dm: &MatterStackDataModel<'_, C, H>, + dm: &MatterStackDataModel<'_, C, H, S>, ) -> Result<(), Error> where C: Crypto, H: AsyncHandler + AsyncMetadata, + S: KvBlobStoreAccess, { let responder = DefaultResponder::new(dm); @@ -980,3 +945,12 @@ impl UserTask for () { core::future::pending::>() } } + +// The data model is not created yet, so we don't have to notify anything +pub(crate) struct DummyNotify; + +impl DynBase for DummyNotify {} + +impl ChangeNotify for DummyNotify { + fn notify(&self, _endpoint_id: EndptId, _cluster_id: ClusterId, _attr_id: AttrId) {} +} diff --git a/src/mdns.rs b/src/mdns.rs index 90730c9..ecbeeef 100644 --- a/src/mdns.rs +++ b/src/mdns.rs @@ -207,17 +207,13 @@ impl<'a> AvahiMdns<'a> { Self { connection } } - pub async fn run( + pub async fn run( &mut self, matter: &Matter<'_>, - crypto: C, notify: &dyn ChangeNotify, - ) -> Result<(), Error> - where - C: Crypto, - { + ) -> Result<(), Error> { rs_matter::transport::network::mdns::avahi::AvahiMdnsResponder::new(matter) - .run(self.connection, crypto, notify) + .run(self.connection, |e, c, a| notify.notify(e, c, a)) .await } } @@ -227,7 +223,7 @@ impl Mdns for AvahiMdns<'_> { fn run( &mut self, matter: &Matter<'_>, - crypto: C, + _crypto: C, notify: &dyn ChangeNotify, _udp: U, _mac: &[u8], @@ -239,7 +235,7 @@ impl Mdns for AvahiMdns<'_> { C: Crypto, U: UdpBind, { - Self::run(self, matter, crypto, notify) + Self::run(self, matter, notify) } } @@ -256,17 +252,13 @@ impl<'a> ResolveMdns<'a> { Self { connection } } - pub async fn run( + pub async fn run( &mut self, matter: &Matter<'_>, - crypto: C, notify: &dyn ChangeNotify, - ) -> Result<(), Error> - where - C: Crypto, - { + ) -> Result<(), Error> { rs_matter::transport::network::mdns::resolve::ResolveMdnsResponder::new(matter) - .run(self.connection, crypto, notify) + .run(self.connection, |e, c, a| notify.notify(e, c, a)) .await } } @@ -276,7 +268,7 @@ impl Mdns for ResolveMdns<'_> { fn run( &mut self, matter: &Matter<'_>, - crypto: C, + _crypto: C, notify: &dyn ChangeNotify, _udp: U, _mac: &[u8], @@ -288,7 +280,7 @@ impl Mdns for ResolveMdns<'_> { C: Crypto, U: UdpBind, { - Self::run(self, matter, crypto, notify) + Self::run(self, matter, notify) } } @@ -298,14 +290,13 @@ pub struct ZeroconfMdns; #[cfg(feature = "zeroconf")] impl ZeroconfMdns { - pub async fn run( + pub async fn run( &mut self, matter: &Matter<'_>, - crypto: C, notify: &dyn ChangeNotify, ) -> Result<(), Error> { rs_matter::transport::network::mdns::zeroconf::ZeroconfMdnsResponder::new(matter) - .run(crypto, notify) + .run(|e, c, a| notify.notify(e, c, a)) .await } } @@ -315,7 +306,7 @@ impl Mdns for ZeroconfMdns { fn run( &mut self, matter: &Matter<'_>, - crypto: C, + _crypto: C, notify: &dyn ChangeNotify, _udp: U, _mac: &[u8], @@ -327,7 +318,7 @@ impl Mdns for ZeroconfMdns { C: Crypto, U: UdpBind, { - Self::run(self, matter, crypto, notify) + Self::run(self, matter, notify) } } @@ -337,17 +328,13 @@ pub struct AstroMdns; #[cfg(feature = "astro-dnssd")] impl AstroMdns { - pub async fn run( + pub async fn run( &mut self, matter: &Matter<'_>, - crypto: C, notify: &dyn ChangeNotify, - ) -> Result<(), Error> - where - C: Crypto, - { + ) -> Result<(), Error> { rs_matter::transport::network::mdns::astro::AstroMdnsResponder::new(matter) - .run(crypto, notify) + .run(|e, c, a| notify.notify(e, c, a)) .await } } @@ -357,7 +344,7 @@ impl Mdns for AstroMdns { async fn run( &mut self, matter: &Matter<'_>, - crypto: C, + _crypto: C, notify: &dyn ChangeNotify, _udp: U, _mac: &[u8], @@ -369,7 +356,7 @@ impl Mdns for AstroMdns { C: Crypto, U: UdpBind, { - Self::run(self, matter, crypto, notify).await + Self::run(self, matter, notify).await } } diff --git a/src/network.rs b/src/network.rs index d45e70f..c1a9fe1 100644 --- a/src/network.rs +++ b/src/network.rs @@ -1,7 +1,6 @@ use rs_matter::pairing::DiscoveryCapabilities; use rs_matter::utils::init::{init_from_closure, Init}; -use crate::persist::NetworkPersist; use crate::private::Sealed; /// User data that can be embedded in the stack network @@ -26,11 +25,6 @@ impl Embedding for () { pub trait Network: Sealed { const INIT: Self; - /// The network peristence context to be used by the `Persist` trait. - type PersistContext<'a>: NetworkPersist - where - Self: 'a; - /// Optional additional state embedded in the network state type Embedding<'a>: Embedding where @@ -42,9 +36,6 @@ pub trait Network: Sealed { /// Return the discovery capabilities of this network when commissioning the device. fn discovery_capabilities(&self) -> DiscoveryCapabilities; - /// Return the persistence context for this network. - fn persist_context(&self) -> Self::PersistContext<'_>; - /// Return a reference to the embedded user data. fn embedding(&self) -> &Self::Embedding<'_>; } diff --git a/src/persist.rs b/src/persist.rs index 318ed8e..5962b14 100644 --- a/src/persist.rs +++ b/src/persist.rs @@ -1,546 +1,72 @@ -use core::fmt::Display; +use core::borrow::{Borrow, BorrowMut}; use cfg_if::cfg_if; -use embassy_futures::select::select; - -use rs_matter::dm::networks::wireless::{WirelessNetwork, WirelessNetworks}; -use rs_matter::error::Error; -use rs_matter::utils::storage::pooled::{BufferAccess, PooledBuffer, PooledBuffers}; +use rs_matter::persist::SharedKvBlobStore; +use rs_matter::utils::init::{init, Init}; +use rs_matter::utils::storage::Vec; use rs_matter::utils::sync::{IfMutex, IfMutexGuard}; -use rs_matter::Matter; - -use crate::private::Sealed; - -#[cfg(feature = "std")] -pub use file::DirKvBlobStore; - -/// A persister for Matter that relies on a BLOB key-value storage -/// represented by the `KvBlobStore`/`SharedKvBlobStore` traits. -pub struct MatterPersist<'a, S, C> { - store: SharedKvBlobStore<'a, S>, - matter: &'a Matter<'a>, - networks: C, -} - -impl<'a, S, C> MatterPersist<'a, S, C> -where - S: KvBlobStore, - C: NetworkPersist, -{ - /// Create a new `MatterPersist` instance. - pub fn new(store: SharedKvBlobStore<'a, S>, matter: &'a Matter<'a>, networks: C) -> Self { - Self { - store, - matter, - networks, - } - } - - /// Get the underlying `SharedKvBlobStore` instance. - /// - /// This can be used to access vendor-specific keys. - pub fn store(&self) -> &SharedKvBlobStore<'a, S> { - &self.store - } - - /// Reset the persist instance, removing all stored data from the non-volatile storage - /// as well as removing all ACLs, fabrics and wireless networks from the Matter stack. - pub async fn reset(&self) -> Result<(), Error> { - let (mut kv, mut buf) = self.store.get().await; - - kv.remove(MatterStackKey::Fabrics as _, &mut buf).await?; - kv.remove(MatterStackKey::BasicInfo as _, &mut buf).await?; - kv.remove(MatterStackKey::Networks as _, &mut buf).await?; - - self.matter.reset_persist(false); - self.networks.reset(false); - - self.matter.reset_transport() - } - - /// Load the Matter stack from the non-volatile storage. - pub async fn load(&self) -> Result<(), Error> { - let (mut kv, mut buf) = self.store.get().await; - - kv.load(MatterStackKey::Fabrics as _, &mut buf, |data| { - if let Some(data) = data { - self.matter.load_fabrics(data)?; - } - - Ok(()) - }) - .await?; - - kv.load(MatterStackKey::BasicInfo as _, &mut buf, |data| { - if let Some(data) = data { - self.matter.load_basic_info(data)?; - } - - Ok(()) - }) - .await?; - - kv.load(MatterStackKey::Networks as _, &mut buf, |data| { - if let Some(data) = data { - self.networks.load(data)?; - } - - Ok(()) - }) - .await?; - - Ok(()) - } - - /// Return `true` if the Matter stack has changed since the last call to `store`. - pub fn changed(&self) -> bool { - self.matter.fabrics_changed() || self.networks.changed() - } - - /// Store the Matter stack to the non-volatile storage, if it has changed. - /// - /// Return `true` if the Matter stack was changed and therefore has been stored. - pub async fn save(&self) -> Result { - if self.changed() { - let (mut kv, mut buf) = self.store.get().await; - - if self.matter.fabrics_changed() { - kv.store(MatterStackKey::Fabrics as _, &mut buf, |buf| { - self.matter.store_fabrics(buf) - }) - .await?; - } - - if self.matter.basic_info_changed() { - kv.store(MatterStackKey::BasicInfo as _, &mut buf, |buf| { - self.matter.store_basic_info(buf) - }) - .await?; - } - - if self.networks.changed() { - kv.store(MatterStackKey::Networks as _, &mut buf, |buf| { - self.networks.store(buf) - }) - .await?; - } - - Ok(true) - } else { - Ok(false) - } - } - - /// Run the persist instance, listening for changes in the Matter stack's state. - pub async fn run(&self) -> Result<(), Error> { - loop { - let wait_fabrics = self.matter.wait_persist(); - let wait_networks = self.networks.wait_state_changed(); - - select(wait_fabrics, wait_networks).await; - - self.save().await?; - } - } -} - -/// A perist API that needs to be implemented by the network impl which is used in the Matter stack. -/// -/// The trait is sealed and has only two implementations: -/// - `()` - which is used with the `Eth` network -/// - `&NetworkContext` - which is used with the `WirelessBle` network. -pub trait NetworkPersist: Sealed { - /// Reset all networks, removing all stored data from the memory - fn reset(&self, flag_changed: bool); - - /// Load the networks from the provided data BLOB - fn load(&self, data: &[u8]) -> Result<(), Error>; - - /// Save the networks as BLOB using the provided data buffer - /// - /// Return the length of the data written in the provided buffer. - fn store(&self, buf: &mut [u8]) -> Result; - - /// Check if the networks have changed. - /// - /// This method should return `Ok(true)` if the networks have changed since the last call to `changed`. - fn changed(&self) -> bool; - - /// Wait until the networks have changed. - /// - /// This method might return even if the networks have not changed, - /// so the ultimate litmus test whether the networks did indeed change is trying to call - /// `store` and then inspecting if it returned something. - async fn wait_state_changed(&self); -} - -impl Sealed for &WirelessNetworks {} - -impl NetworkPersist for &WirelessNetworks -where - T: WirelessNetwork, -{ - fn reset(&self, flag_changed: bool) { - WirelessNetworks::reset(self, flag_changed); - } - - fn load(&self, data: &[u8]) -> Result<(), Error> { - WirelessNetworks::load(self, data) - } - - fn store(&self, buf: &mut [u8]) -> Result { - WirelessNetworks::store(self, buf) - } - - fn changed(&self) -> bool { - WirelessNetworks::changed(self) - } - - async fn wait_state_changed(&self) { - WirelessNetworks::wait_persist(self).await - } -} - -/// A no-op implementation of the `NetworksPersist` trait. -/// -/// Used when the Matter stack is configured for Ethernet, as in that case -/// there is no network state that needs to be saved -impl NetworkPersist for () { - fn reset(&self, _flag_changed: bool) {} - - fn load(&self, _data: &[u8]) -> Result<(), Error> { - Ok(()) - } - - fn store(&self, _buf: &mut [u8]) -> Result { - Ok(0) - } - - fn changed(&self) -> bool { - false - } - - async fn wait_state_changed(&self) { - core::future::pending().await - } -} - -/// The first key available for the vendor-specific data. -pub const VENDOR_KEYS_START: u16 = 0x1000; - -/// The keys currently used by the `rs-matter-stack`. -/// -/// All keys with values up to 0xfff are reserved for `rs-matter-stack` use. -/// Keys >= 0x1000 are available for downstream crates. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -#[repr(u16)] -pub enum MatterStackKey { - Fabrics = 0, - BasicInfo = 1, - Networks = 2, -} - -impl TryFrom for MatterStackKey { - type Error = (); - - fn try_from(value: u16) -> Result { - match value { - 0 => Ok(MatterStackKey::Fabrics), - 1 => Ok(MatterStackKey::BasicInfo), - 2 => Ok(MatterStackKey::Networks), - _ => Err(()), - } - } -} - -impl Display for MatterStackKey { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let s = match self { - MatterStackKey::Fabrics => "fabrics", - MatterStackKey::BasicInfo => "basic-info", - MatterStackKey::Networks => "networks", - }; - - write!(f, "{}", s) - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for MatterStackKey { - fn format(&self, f: defmt::Formatter<'_>) { - let s = match self { - MatterStackKey::Fabrics => "fabrics", - MatterStackKey::BasicInfo => "basic-info", - MatterStackKey::Networks => "networks", - }; - - defmt::write!(f, "{}", s) - } -} - -/// A trait representing a key-value BLOB storage. -pub trait KvBlobStore { - /// Load a BLOB with the specified key from the storage. - /// - /// # Arguments - /// - `key` - the key of the BLOB - /// - `buf` - a buffer that the `KvBlobStore` implementation might use for its own purposes - /// - `cb` - a callback that will be called with the loaded data is available - /// or with `None` if the BLOB does not exist. - async fn load(&mut self, key: u16, buf: &mut [u8], cb: F) -> Result<(), Error> - where - F: FnOnce(Option<&[u8]>) -> Result<(), Error>; - - /// Store a BLOB with the specified key in the storage. - /// - /// # Arguments - /// - `key` - the key of the BLOB - /// - `buf` - a buffer that the `KvBlobStore` implementation might use for its own purposes - /// - `cb` - a callback that will be called with a buffer that the implementation - /// should fill with the data to be stored. - async fn store(&mut self, key: u16, buf: &mut [u8], cb: F) -> Result<(), Error> - where - F: FnOnce(&mut [u8]) -> Result; - - /// Remove a BLOB with the specified key from the storage. - /// - /// # Arguments - /// - `key` - the key of the BLOB - /// - `buf` - a buffer that the `KvBlobStore` implementation might use for its own purposes - async fn remove(&mut self, key: u16, buf: &mut [u8]) -> Result<(), Error>; -} -impl KvBlobStore for &mut T -where - T: KvBlobStore, -{ - async fn load(&mut self, key: u16, buf: &mut [u8], cb: F) -> Result<(), Error> - where - F: FnOnce(Option<&[u8]>) -> Result<(), Error>, - { - T::load(self, key, buf, cb).await - } - - async fn store(&mut self, key: u16, buf: &mut [u8], cb: F) -> Result<(), Error> - where - F: FnOnce(&mut [u8]) -> Result, - { - T::store(self, key, buf, cb).await - } - - async fn remove(&mut self, key: u16, buf: &mut [u8]) -> Result<(), Error> { - T::remove(self, key, buf).await +cfg_if! { + if #[cfg(feature = "kv-blob-store-65536")] { + const KV_BLOB_BUF_SIZE: usize = 65536; + } else if #[cfg(feature = "kv-blob-store-32768")] { + const KV_BLOB_BUF_SIZE: usize = 32768; + } else if #[cfg(feature = "kv-blob-store-16384")] { + const KV_BLOB_BUF_SIZE: usize = 16384; + } else if #[cfg(feature = "kv-blob-store-8192")] { + const KV_BLOB_BUF_SIZE: usize = 8192; + } else if #[cfg(feature = "kv-blob-store-4096")] { + const KV_BLOB_BUF_SIZE: usize = 4096; + } else if #[cfg(feature = "kv-blob-store-2048")] { + const KV_BLOB_BUF_SIZE: usize = 2048; + } else if #[cfg(feature = "kv-blob-store-1024")] { + const KV_BLOB_BUF_SIZE: usize = 1024; + } else { + pub const KV_BLOB_BUF_SIZE: usize = 4096; } } -/// A noop implementation of the `KvBlobStore` trait. -pub struct DummyKvBlobStore; - -impl KvBlobStore for DummyKvBlobStore { - async fn load(&mut self, _key: u16, _buf: &mut [u8], _cb: F) -> Result<(), Error> - where - F: FnOnce(Option<&[u8]>) -> Result<(), Error>, - { - Ok(()) - } - - async fn store(&mut self, _key: u16, buf: &mut [u8], cb: F) -> Result<(), Error> - where - F: FnOnce(&mut [u8]) -> Result, - { - // Need to call the callback even if we are not storing for real, so as - // the callback can mark its internal state as "stored". - cb(buf)?; +/// A type alias for the `SharedKvBlobStore` specialization used in `MatterStack`. +pub type MatterSharedKvBlobStore<'a, S> = SharedKvBlobStore>; - Ok(()) - } - - async fn remove(&mut self, _key: u16, _buf: &mut [u8]) -> Result<(), Error> { - Ok(()) - } +/// A buffer for the KV blob store, which is used to read and write blobs from the storage. +pub(crate) struct MatterKvBlobStoreBuf { + inner: IfMutex>, } -/// A shared wrapper around a `KvBlobStore` instance. -pub struct SharedKvBlobStore<'a, S> { - store: IfMutex, - buf: &'a PooledBuffers<1, KvBlobBuffer>, -} - -impl<'a, S> SharedKvBlobStore<'a, S> -where - S: KvBlobStore, -{ - /// Create a new `SharedKvBlobStore` instance. - /// - /// # Arguments - /// - `store` - the wrapped `KvBlobStore` instance - /// - `buf` - the wrapped buffer - pub const fn new(store: S, buf: &'a PooledBuffers<1, KvBlobBuffer>) -> Self { +impl MatterKvBlobStoreBuf { + pub(crate) const fn new() -> Self { Self { - store: IfMutex::new(store), - buf, + inner: IfMutex::new(Vec::new()), } } - /// Get the wrapped `KvBlobStore` instance and the wrapped buffer. - /// - /// If necessary, awaits the buffer to be available. - pub async fn get(&self) -> (IfMutexGuard<'_, S>, PooledBuffer<'_, 1, KvBlobBuffer>) { - let store = self.store.lock().await; - let mut buf = unwrap!(self.buf.get().await); + pub(crate) fn init() -> impl Init { + init!(Self { + inner <- IfMutex::init(Vec::init()), + }) + } + pub(crate) fn try_get(&self) -> Option> { + let mut buf = self.inner.try_lock().ok()?; unwrap!(buf.resize_default(KV_BLOB_BUF_SIZE)); - (store, buf) + Some(MatterKvBlobStoreBufInstance(buf)) } } -#[cfg(feature = "std")] -mod file { - use std::io::{Read, Write}; - - use rs_matter::error::Error; - - use super::KvBlobStore; - - extern crate std; - - /// An implementation of the `KvBlobStore` trait that stores the BLOBs in a directory. - /// - /// The BLOBs are stored in files named after the keys in the specified directory. - #[derive(Debug, Clone)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub struct DirKvBlobStore( - #[cfg_attr(feature = "defmt", defmt(Debug2Format))] std::path::PathBuf, - ); - - impl DirKvBlobStore { - /// Create a new `DirKvStore` instance, which will persist - /// its settings in `/rs-matter`. - pub fn new_default() -> Self { - Self(std::env::temp_dir().join("rs-matter")) - } - - /// Create a new `DirKvStore` instance. - pub const fn new(path: std::path::PathBuf) -> Self { - Self(path) - } - - /// Load a BLOB with the specified key from the directory. - pub fn load<'a>(&self, key: u16, buf: &'a mut [u8]) -> Result, Error> { - let path = self.key_path(key); - - match std::fs::File::open(path) { - Ok(mut file) => { - let mut offset = 0; - - loop { - if offset == buf.len() { - Err(rs_matter::error::ErrorCode::NoSpace)?; - } - - let len = file.read(&mut buf[offset..])?; - - if len == 0 { - break; - } - - offset += len; - } - - let data = &buf[..offset]; +/// An instance of the KV blob store buffer, which is used to read and write blobs from the storage. +pub struct MatterKvBlobStoreBufInstance<'a>(IfMutexGuard<'a, Vec>); - debug!("Key {}: loaded {}B ({:?})", key, data.len(), data); - - Ok(Some(data)) - } - Err(_) => Ok(None), - } - } - - /// Store a BLOB with the specified key in the directory. - pub fn store(&self, key: u16, data: &[u8]) -> Result<(), Error> { - let path = self.key_path(key); - - std::fs::create_dir_all(unwrap!(path.parent()))?; - - let mut file = std::fs::File::create(path)?; - - file.write_all(data)?; - - debug!("Key {}: stored {}B ({:?})", key, data.len(), data); - - Ok(()) - } - - /// Remove a BLOB with the specified key from the directory. - /// If the BLOB does not exist, this method does nothing. - pub fn remove(&self, key: u16) -> Result<(), Error> { - let path = self.key_path(key); - - if std::fs::remove_file(path).is_ok() { - debug!("Key {}: removed", key); - } - - Ok(()) - } - - fn key_path(&self, key: u16) -> std::path::PathBuf { - self.0.join(format!("k_{key:04x}")) - } - } - - impl Default for DirKvBlobStore { - fn default() -> Self { - Self::new_default() - } - } - - impl KvBlobStore for DirKvBlobStore { - async fn load(&mut self, key: u16, buf: &mut [u8], cb: F) -> Result<(), Error> - where - F: FnOnce(Option<&[u8]>) -> Result<(), Error>, - { - let data = DirKvBlobStore::load(self, key, buf)?; - - cb(data) - } - - async fn store(&mut self, key: u16, buf: &mut [u8], cb: F) -> Result<(), Error> - where - F: FnOnce(&mut [u8]) -> Result, - { - let data_len = cb(buf)?; - - DirKvBlobStore::store(self, key, &buf[..data_len]) - } - - async fn remove(&mut self, key: u16, _buf: &mut [u8]) -> Result<(), Error> { - DirKvBlobStore::remove(self, key) - } +impl Borrow<[u8]> for MatterKvBlobStoreBufInstance<'_> { + fn borrow(&self) -> &[u8] { + self.0.as_slice() } } -cfg_if! { - if #[cfg(feature = "kv-blob-store-65536")] { - const KV_BLOB_BUF_SIZE: usize = 65536; - } else if #[cfg(feature = "kv-blob-store-32768")] { - const KV_BLOB_BUF_SIZE: usize = 32768; - } else if #[cfg(feature = "kv-blob-store-16384")] { - const KV_BLOB_BUF_SIZE: usize = 16384; - } else if #[cfg(feature = "kv-blob-store-8192")] { - const KV_BLOB_BUF_SIZE: usize = 8192; - } else if #[cfg(feature = "kv-blob-store-4096")] { - const KV_BLOB_BUF_SIZE: usize = 4096; - } else if #[cfg(feature = "kv-blob-store-2048")] { - const KV_BLOB_BUF_SIZE: usize = 2048; - } else if #[cfg(feature = "kv-blob-store-1024")] { - const KV_BLOB_BUF_SIZE: usize = 1024; - } else { - const KV_BLOB_BUF_SIZE: usize = 4096; +impl BorrowMut<[u8]> for MatterKvBlobStoreBufInstance<'_> { + fn borrow_mut(&mut self) -> &mut [u8] { + self.0.as_mut_slice() } } - -/// A buffer for the `KvBlobStore` trait. -pub type KvBlobBuffer = heapless::Vec; diff --git a/src/wireless.rs b/src/wireless.rs index 5feaf09..d02f51d 100644 --- a/src/wireless.rs +++ b/src/wireless.rs @@ -1,10 +1,11 @@ +use core::borrow::BorrowMut; use core::pin::pin; use embassy_futures::select::{select, select3}; use rs_matter::crypto::Crypto; use rs_matter::dm::clusters::gen_diag::NetifDiag; -use rs_matter::dm::clusters::net_comm::NetCtl; +use rs_matter::dm::clusters::net_comm::{NetCtl, SharedNetworks}; use rs_matter::dm::clusters::wifi_diag::WirelessDiag; use rs_matter::dm::networks::wireless::{ NetCtlState, WirelessMgr, WirelessNetwork, WirelessNetworks, MAX_CREDS_SIZE, @@ -13,6 +14,7 @@ use rs_matter::dm::networks::NetChangeNotif; use rs_matter::dm::ChangeNotify; use rs_matter::error::Error; use rs_matter::pairing::DiscoveryCapabilities; +use rs_matter::persist::KvBlobStore; use rs_matter::transport::network::btp::{AdvData, Btp}; use rs_matter::transport::network::NoNetwork; use rs_matter::utils::cell::RefCell; @@ -24,9 +26,8 @@ use crate::ble::GattPeripheral; use crate::mdns::Mdns; use crate::nal::NetStack; use crate::network::{Embedding, Network}; -use crate::persist::MatterPersist; use crate::private::Sealed; -use crate::{pin_alloc, MatterStack}; +use crate::{pin_alloc, DummyNotify, MatterStack}; pub use gatt::*; pub use thread::*; @@ -36,16 +37,11 @@ mod gatt; mod thread; mod wifi; -const MAX_WIRELESS_NETWORKS: usize = 2; +pub const MAX_WIRELESS_NETWORKS: usize = 2; /// A type alias for a Matter stack running over either Wifi or Thread (and BLE, during commissioning). pub type WirelessMatterStack<'a, const B: usize, T, E = ()> = MatterStack<'a, B, WirelessBle>; -/// A type alias for the Matter Persister created by calling `WirelessMatterStack::create_persist`. -pub type WirelessMatterPersist<'a, S, T> = MatterPersist<'a, S, WirelessPersistContext<'a, T>>; - -type WirelessPersistContext<'a, T> = &'a WirelessNetworks; - /// An implementation of the `Network` trait for a Matter stack running over /// BLE during commissioning, and then over either WiFi or Thread when operating. /// @@ -63,7 +59,7 @@ where T: WirelessNetwork, { btp: Btp, - networks: WirelessNetworks, + networks: SharedNetworks>, net_state: blocking::Mutex>, creds_buf: IfMutex<[u8; MAX_CREDS_SIZE]>, embedding: E, @@ -78,7 +74,7 @@ where pub const fn new() -> Self { Self { btp: Btp::new(), - networks: WirelessNetworks::new(), + networks: SharedNetworks::new(WirelessNetworks::new()), net_state: NetCtlState::new_with_mutex(), creds_buf: IfMutex::new([0; MAX_CREDS_SIZE]), embedding: E::INIT, @@ -89,7 +85,7 @@ where pub fn init() -> impl Init { init!(Self { btp <- Btp::init(), - networks <- WirelessNetworks::init(), + networks <- SharedNetworks::init(WirelessNetworks::init()), net_state <- NetCtlState::init_with_mutex(), creds_buf <- IfMutex::init(zeroed()), embedding <- E::init(), @@ -97,7 +93,7 @@ where } /// Return a reference to the networks storage. - pub fn networks(&self) -> &WirelessNetworks { + pub fn networks(&self) -> &SharedNetworks> { &self.networks } } @@ -126,11 +122,6 @@ where { const INIT: Self = Self::new(); - type PersistContext<'a> - = &'a WirelessNetworks - where - Self: 'a; - type Embedding<'a> = E where @@ -144,10 +135,6 @@ where DiscoveryCapabilities::BLE } - fn persist_context(&self) -> Self::PersistContext<'_> { - &self.networks - } - fn embedding(&self) -> &Self::Embedding<'_> { &self.embedding } @@ -158,6 +145,62 @@ where T: WirelessNetwork, E: Embedding, { + /// Reset the Matter instance to the factory defaults by removing all fabrics and basic info settings + pub async fn reset(&mut self, mut kv: S) -> Result<(), Error> + where + S: KvBlobStore, + { + let mut buf = unwrap!(self.store_buf.try_get()); + let buf = buf.borrow_mut(); + + self.matter.reset_persist(&mut kv, buf).await?; + + self.network + .networks + .get_mut() + .get_mut() + .reset_persist(kv, buf) + .await + } + + /// Load the persisted state from the provided `KvBlobStore` implementation. + pub async fn load(&mut self, mut kv: S) -> Result<(), Error> + where + S: KvBlobStore, + { + let mut buf = unwrap!(self.store_buf.try_get()); + let buf = buf.borrow_mut(); + + self.matter.load_persist(&mut kv, buf).await?; + + self.network + .networks + .get_mut() + .get_mut() + .load_persist(kv, buf) + .await + } + + /// Run the startup sequence of the stack, which includes loading the persisted state + /// and opening the basic communication window if the device is not commissioned yet. + pub async fn startup(&mut self, crypto: C, kv: S) -> Result<(), Error> + where + C: Crypto, + S: KvBlobStore, + { + self.load(kv).await?; + + if !self.is_commissioned() { + info!("Device is not commissioned yet, opening commissioning window..."); + + self.open_basic_comm_window(crypto, &DummyNotify)?; + } else { + info!("Device is already commissioned"); + } + + Ok(()) + } + #[allow(clippy::too_many_arguments)] async fn run_net_coex( &self, @@ -307,7 +350,7 @@ impl PreexistingWireless { } } -pub(crate) struct MatterStackWirelessTask<'a, const B: usize, T, E, C, H, U> +pub(crate) struct MatterStackWirelessTask<'a, const B: usize, T, E, C, H, S, U> where T: WirelessNetwork, E: Embedding, @@ -315,5 +358,6 @@ where stack: &'a MatterStack<'a, B, WirelessBle>, crypto: C, handler: H, + kv: S, user_task: U, } diff --git a/src/wireless/thread.rs b/src/wireless/thread.rs index e87535f..d465f66 100644 --- a/src/wireless/thread.rs +++ b/src/wireless/thread.rs @@ -6,35 +6,32 @@ use embassy_futures::select::{select, select3, select4}; use rs_matter::crypto::{Crypto, RngCore}; use rs_matter::dm::clusters::gen_comm::CommPolicy; use rs_matter::dm::clusters::gen_diag::GenDiag; -use rs_matter::dm::clusters::net_comm::{NetCtl, NetCtlStatus, NetworkType}; +use rs_matter::dm::clusters::net_comm::{NetCtl, NetCtlStatus, NetworkType, SharedNetworks}; use rs_matter::dm::clusters::thread_diag::ThreadDiag; use rs_matter::dm::endpoints::{self, with_sys, with_thread, SysHandler, ThreadHandler}; use rs_matter::dm::networks::wireless::{ - self, NetCtlWithStatusImpl, NoopWirelessNetCtl, WirelessMgr, + self, NetCtlWithStatusImpl, NoopWirelessNetCtl, ThreadNetworks, WirelessMgr, }; use rs_matter::dm::networks::NetChangeNotif; use rs_matter::dm::{clusters::gen_diag::NetifDiag, AsyncHandler}; use rs_matter::dm::{AsyncMetadata, Endpoint}; use rs_matter::error::Error; +use rs_matter::persist::KvBlobStoreAccess; use rs_matter::transport::network::NoNetwork; use rs_matter::utils::select::Coalesce; use crate::mdns::Mdns; use crate::nal::NetStack; use crate::network::Embedding; -use crate::persist::KvBlobStore; -use crate::wireless::{GattPeripheral, GattTask, MatterStackWirelessTask, WirelessMatterPersist}; +use crate::wireless::{GattPeripheral, GattTask, MatterStackWirelessTask}; use crate::{pin_alloc, UserTask}; -use super::{Gatt, PreexistingWireless, WirelessMatterStack}; +use super::{Gatt, PreexistingWireless, WirelessMatterStack, MAX_WIRELESS_NETWORKS}; /// A type alias for a Matter stack running over Thread (and BLE, during commissioning). pub type ThreadMatterStack<'a, const B: usize, E = ()> = WirelessMatterStack<'a, B, wireless::Thread, E>; -/// A type alias for the Matter Persister created by calling `ThreadMatterStack::create_persist`. -pub type ThreadMatterPersist<'a, S> = WirelessMatterPersist<'a, S, wireless::Thread>; - impl WirelessMatterStack<'_, B, wireless::Thread, E> where E: Embedding, @@ -47,21 +44,21 @@ where /// - `controller` - a user-provided `Controller` implementation /// - `mdns` - a user-provided `Mdns` implementation /// - `gatt` - a user-provided `GattPeripheral` implementation - /// - `persist` - a `ThreadMatterPersist` implementation instantiated on the stack with `create_persist` /// - `crypto` - a user-provided `Crypto` implementation /// - `handler` - a user-provided DM handler implementation + /// - `kv` - a user-provided `KvBlobStoreAccess` implementation /// - `user` - a user-provided future that will be polled only when the netif interface is up #[allow(clippy::too_many_arguments)] - pub fn run_preex<'t, U, N, Q, D, G, S, C, H, X>( + pub fn run_preex<'t, U, N, Q, D, G, C, H, S, X>( &'t self, net_stack: U, netif: N, net_ctl: Q, mdns: D, gatt: G, - persist: &'t ThreadMatterPersist<'_, S>, crypto: C, handler: H, + kv: S, user: X, ) -> impl Future> + 't where @@ -70,16 +67,16 @@ where Q: NetCtl + ThreadDiag + NetChangeNotif + 't, D: Mdns + 't, G: GattPeripheral + 't, - S: KvBlobStore + 't, C: Crypto + 't, H: AsyncHandler + AsyncMetadata + 't, + S: KvBlobStoreAccess + 't, X: UserTask + 't, { self.run_coex( PreexistingWireless::new(net_stack, netif, net_ctl, mdns, gatt), - persist, crypto, handler, + kv, user, ) } @@ -88,23 +85,23 @@ where /// /// # Arguments /// - `thread` - a user-provided `ThreadCoex` implementation - /// - `persist` - a `ThreadMatterPersist` implementation instantiated on the stack with `create_persist` /// - `crypto` - a user-provided `Crypto` implementation /// - `handler` - a user-provided DM handler implementation + /// - `kv` - a user-provided `KvBlobStoreAccess` implementation /// - `user` - a user-provided future that will be polled only when the netif interface is up - pub async fn run_coex( + pub async fn run_coex( &self, mut thread: W, - persist: &ThreadMatterPersist<'_, S>, crypto: C, handler: H, + kv: S, user: U, ) -> Result<(), Error> where W: ThreadCoex, - S: KvBlobStore, C: Crypto, H: AsyncHandler + AsyncMetadata, + S: KvBlobStoreAccess, U: UserTask, { let _lock = self.run_lock.lock().await; @@ -119,34 +116,33 @@ where self.matter().reset_transport()?; - let mut net_task = pin_alloc!( + let net_task = pin_alloc!( self.bump, - self.run_thread_coex(&mut thread, crypto, handler, user) + self.run_thread_coex(&mut thread, crypto, handler, kv, user) ); - let mut persist_task = pin_alloc!(self.bump, self.run_psm(persist)); - select(&mut net_task, &mut persist_task).coalesce().await + net_task.await } /// Run the Matter stack for a wireless network where the BLE and the Thread stacks cannot co-exist. /// /// # Arguments /// - `thread` - a user-provided `Thread` + `Gatt` implementation - /// - `persist` - a `ThreadMatterPersist` implementation instantiated on the stack with `create_persist` /// - `crypto` - a user-provided `Crypto` implementation /// - `handler` - a user-provided DM handler implementation + /// - `kv` - a user-provided `KvBlobStoreAccess` implementation /// - `user` - a user-provided future that will be polled only when the netif interface is up - pub async fn run( + pub async fn run( &self, thread: W, - persist: &ThreadMatterPersist<'_, S>, crypto: C, handler: H, + kv: S, user: U, ) -> Result<(), Error> where W: Thread + Gatt, - S: KvBlobStore, + S: KvBlobStoreAccess, C: Crypto, H: AsyncHandler + AsyncMetadata, U: UserTask, @@ -163,48 +159,55 @@ where self.matter().reset_transport()?; - let mut net_task = pin_alloc!(self.bump, self.run_thread(thread, crypto, handler, user)); - let mut persist_task = pin_alloc!(self.bump, self.run_psm(persist)); + let net_task = pin_alloc!( + self.bump, + self.run_thread(thread, crypto, handler, kv, user) + ); - select(&mut net_task, &mut persist_task).coalesce().await + net_task.await } - fn run_thread_coex<'t, W, C, H, U>( + fn run_thread_coex<'t, W, C, H, S, U>( &'t self, thread: &'t mut W, crypto: C, handler: H, + kv: S, user: U, ) -> impl Future> + 't where W: ThreadCoex + 't, C: Crypto + 't, H: AsyncHandler + AsyncMetadata + 't, + S: KvBlobStoreAccess + 't, U: UserTask + 't, { thread.run(MatterStackWirelessTask { stack: self, crypto, handler, + kv, user_task: user, }) } - async fn run_thread( + async fn run_thread( &self, mut thread: W, crypto: C, handler: H, + kv: S, mut user: U, ) -> Result<(), Error> where W: Thread + Gatt, C: Crypto, H: AsyncHandler + AsyncMetadata, + S: KvBlobStoreAccess, U: UserTask, { loop { - let commissioned = self.is_commissioned().await?; + let commissioned = self.is_commissioned(); if !commissioned { Gatt::run( @@ -213,6 +216,7 @@ where stack: self, crypto: &crypto, handler: &handler, + kv: &kv, user_task: &mut user, }, ) @@ -227,10 +231,9 @@ where let root_handler = self.root_handler(&(), &(), &net_ctl, &false, crypto.weak_rand()?, &handler); - let dm = self.dm(&crypto, (&handler, root_handler)); + let dm = self.dm(&crypto, (&handler, root_handler), &kv); - self.matter() - .close_comm_window(&crypto, dm.change_notify())?; + self.matter().close_comm_window(dm.change_notify())?; } Thread::run( @@ -239,6 +242,7 @@ where stack: self, crypto: &crypto, handler: &handler, + kv: &kv, user_task: &mut user, }, ) @@ -262,15 +266,20 @@ where comm_policy: &'a dyn CommPolicy, rand: impl RngCore + Copy, handler: H, - ) -> ThreadHandler<'a, &'a N, SysHandler<'a, H>> + ) -> ThreadHandler< + 'a, + &'a SharedNetworks>, + &'a N, + SysHandler<'a, H>, + > where N: NetCtl + NetCtlStatus + ThreadDiag, { with_thread( gen_diag, netif_diag, - net_ctl, &self.network.networks, + net_ctl, rand, with_sys(comm_policy, rand, handler), ) @@ -446,12 +455,13 @@ where } } -impl<'a, const B: usize, E, C, H, X> GattTask - for MatterStackWirelessTask<'a, B, wireless::Thread, E, C, H, X> +impl<'a, const B: usize, E, C, H, S, X> GattTask + for MatterStackWirelessTask<'a, B, wireless::Thread, E, C, H, S, X> where E: Embedding, C: Crypto, H: AsyncMetadata + AsyncHandler, + S: KvBlobStoreAccess, { async fn run

(&mut self, peripheral: P) -> Result<(), Error> where @@ -470,7 +480,9 @@ where self.crypto.weak_rand()?, &self.handler, ); - let dm = self.stack.dm(&self.crypto, (&self.handler, handler)); + let dm = self + .stack + .dm(&self.crypto, (&self.handler, handler), &self.kv); let mut btp_task = pin!(self.stack.run_btp(&self.crypto, peripheral)); @@ -480,23 +492,24 @@ where } } -impl<'a, const B: usize, E, C, H, X> ThreadTask - for MatterStackWirelessTask<'a, B, wireless::Thread, E, C, H, X> +impl<'a, const B: usize, E, C, H, S, X> ThreadTask + for MatterStackWirelessTask<'a, B, wireless::Thread, E, C, H, S, X> where E: Embedding, C: Crypto, H: AsyncMetadata + AsyncHandler, + S: KvBlobStoreAccess, X: UserTask, { - async fn run( + async fn run( &mut self, - net_stack: S, + net_stack: T, netif: N, net_ctl: Q, mut mdns: D, ) -> Result<(), Error> where - S: NetStack, + T: NetStack, N: NetifDiag + NetChangeNotif, Q: NetCtl + ThreadDiag + NetChangeNotif, D: Mdns, @@ -517,7 +530,9 @@ where self.crypto.weak_rand()?, &self.handler, ); - let dm = self.stack.dm(&self.crypto, (&self.handler, handler)); + let dm = self + .stack + .dm(&self.crypto, (&self.handler, handler), &self.kv); let stack = &mut self.stack; @@ -554,24 +569,25 @@ where } } -impl<'a, const B: usize, E, C, H, X> ThreadCoexTask - for MatterStackWirelessTask<'a, B, wireless::Thread, E, C, H, X> +impl<'a, const B: usize, E, C, H, S, X> ThreadCoexTask + for MatterStackWirelessTask<'a, B, wireless::Thread, E, C, H, S, X> where E: Embedding, C: Crypto, H: AsyncMetadata + AsyncHandler, + S: KvBlobStoreAccess, X: UserTask, { - async fn run( + async fn run( &mut self, - net_stack: S, + net_stack: T, netif: N, net_ctl: Q, mut mdns: D, mut gatt: G, ) -> Result<(), Error> where - S: NetStack, + T: NetStack, N: NetifDiag + NetChangeNotif, Q: NetCtl + ThreadDiag + NetChangeNotif, D: Mdns, @@ -589,7 +605,9 @@ where self.crypto.weak_rand()?, &self.handler, ); - let dm = self.stack.dm(&self.crypto, (&self.handler, handler)); + let dm = self + .stack + .dm(&self.crypto, (&self.handler, handler), &self.kv); let stack = &mut self.stack; let bump = &stack.bump; diff --git a/src/wireless/wifi.rs b/src/wireless/wifi.rs index 55b6bdf..b4b331a 100644 --- a/src/wireless/wifi.rs +++ b/src/wireless/wifi.rs @@ -6,24 +6,24 @@ use embassy_futures::select::{select, select3, select4}; use rs_matter::crypto::{Crypto, RngCore}; use rs_matter::dm::clusters::gen_comm::CommPolicy; use rs_matter::dm::clusters::gen_diag::GenDiag; -use rs_matter::dm::clusters::net_comm::{NetCtl, NetCtlStatus, NetworkType}; +use rs_matter::dm::clusters::net_comm::{NetCtl, NetCtlStatus, NetworkType, SharedNetworks}; use rs_matter::dm::clusters::wifi_diag::WifiDiag; use rs_matter::dm::endpoints::{self, with_sys, with_wifi, SysHandler, WifiHandler}; use rs_matter::dm::networks::wireless::{ - self, NetCtlWithStatusImpl, NoopWirelessNetCtl, WirelessMgr, + self, NetCtlWithStatusImpl, NoopWirelessNetCtl, WifiNetworks, WirelessMgr, }; use rs_matter::dm::networks::NetChangeNotif; use rs_matter::dm::{clusters::gen_diag::NetifDiag, AsyncHandler}; use rs_matter::dm::{AsyncMetadata, Endpoint}; use rs_matter::error::Error; +use rs_matter::persist::KvBlobStoreAccess; use rs_matter::transport::network::NoNetwork; use rs_matter::utils::select::Coalesce; use crate::mdns::Mdns; use crate::nal::NetStack; use crate::network::Embedding; -use crate::persist::KvBlobStore; -use crate::wireless::{GattPeripheral, MatterStackWirelessTask, WirelessMatterPersist}; +use crate::wireless::{GattPeripheral, MatterStackWirelessTask, MAX_WIRELESS_NETWORKS}; use crate::{pin_alloc, UserTask}; use super::{Gatt, GattTask, PreexistingWireless, WirelessMatterStack}; @@ -32,9 +32,6 @@ use super::{Gatt, GattTask, PreexistingWireless, WirelessMatterStack}; pub type WifiMatterStack<'a, const B: usize, E = ()> = WirelessMatterStack<'a, B, wireless::Wifi, E>; -/// A type alias for the Matter Persister created by calling `WifiMatterStack::create_persist`. -pub type WifiMatterPersist<'a, S> = WirelessMatterPersist<'a, S, wireless::Wifi>; - impl WirelessMatterStack<'_, B, wireless::Wifi, E> where E: Embedding, @@ -47,21 +44,21 @@ where /// - `controller` - a user-provided `Controller` implementation /// - `mdns` - a user-provided `Mdns` implementation /// - `gatt` - a user-provided `GattPeripheral` implementation - /// - `persist` - a `WifiMatterPersist` implementation instantiated on the stack with `create_persist` /// - `crypto` - a user-provided `Crypto` implementation /// - `handler` - a user-provided DM handler implementation + /// - `kv` - a user-provided `KvBlobStoreAccess` implementation /// - `user` - a user-provided future that will be polled only when the netif interface is up #[allow(clippy::too_many_arguments)] - pub async fn run_preex<'t, U, N, Q, D, G, S, C, H, X>( + pub async fn run_preex<'t, U, N, Q, D, G, C, H, S, X>( &'t self, net_stack: U, netif: N, net_ctl: Q, mdns: D, gatt: G, - persist: &'t WifiMatterPersist<'_, S>, crypto: C, handler: H, + kv: S, user: X, ) -> impl Future> + 't where @@ -70,16 +67,16 @@ where Q: NetCtl + WifiDiag + NetChangeNotif + 't, D: Mdns + 't, G: GattPeripheral + 't, - S: KvBlobStore + 't, C: Crypto + 't, H: AsyncHandler + AsyncMetadata + 't, + S: KvBlobStoreAccess + 't, X: UserTask + 't, { self.run_coex( PreexistingWireless::new(net_stack, netif, net_ctl, mdns, gatt), - persist, crypto, handler, + kv, user, ) } @@ -88,23 +85,23 @@ where /// /// # Arguments /// - `wifi` - a user-provided `WifiCoex` implementation - /// - `persist` - a `WifiMatterPersist` implementation instantiated on the stack with `create_persist` /// - `crypto` - a user-provided `Crypto` implementation /// - `handler` - a user-provided DM handler implementation + /// - `kv` - a user-provided `KvBlobStoreAccess` implementation /// - `user` - a user-provided future that will be polled only when the netif interface is up - pub async fn run_coex( + pub async fn run_coex( &self, mut wifi: W, - persist: &WifiMatterPersist<'_, S>, crypto: C, handler: H, + kv: S, user: U, ) -> Result<(), Error> where W: WifiCoex, - S: KvBlobStore, C: Crypto, H: AsyncHandler + AsyncMetadata, + S: KvBlobStoreAccess, U: UserTask, { let _lock = self.run_lock.lock().await; @@ -119,36 +116,35 @@ where self.matter().reset_transport()?; - let mut net_task = pin_alloc!( + let net_task = pin_alloc!( self.bump, - self.run_wifi_coex(&mut wifi, crypto, handler, user) + self.run_wifi_coex(&mut wifi, crypto, handler, kv, user) ); - let mut persist_task = pin_alloc!(self.bump, self.run_psm(persist)); - select(&mut net_task, &mut persist_task).coalesce().await + net_task.await } /// Run the Matter stack for a wireless network where the BLE and the Wifi stacks cannot co-exist. /// /// # Arguments /// - `wifi` - a user-provided `Wifi` + `Gatt` implementation - /// - `persist` - a `WifiMatterPersist` implementation instantiated on the stack with `create_persist` /// - `crypto` - a user-provided `Crypto` implementation /// - `handler` - a user-provided DM handler implementation + /// - `kv` - a user-provided `KvBlobStoreAccess` implementation /// - `user` - a user-provided future that will be polled only when the netif interface is up - pub async fn run( + pub async fn run( &self, wifi: W, - persist: &WifiMatterPersist<'_, S>, crypto: C, handler: H, + kv: S, user: U, ) -> Result<(), Error> where W: Wifi + Gatt, - S: KvBlobStore, C: Crypto, H: AsyncHandler + AsyncMetadata, + S: KvBlobStoreAccess, U: UserTask, { let _lock = self.run_lock.lock().await; @@ -163,48 +159,52 @@ where self.matter().reset_transport()?; - let mut net_task = pin_alloc!(self.bump, self.run_wifi(wifi, crypto, handler, user)); - let mut persist_task = pin_alloc!(self.bump, self.run_psm(persist)); + let net_task = pin_alloc!(self.bump, self.run_wifi(wifi, crypto, handler, kv, user)); - select(&mut net_task, &mut persist_task).coalesce().await + net_task.await } - fn run_wifi_coex<'t, W, C, H, U>( + fn run_wifi_coex<'t, W, C, H, S, U>( &'t self, wifi: &'t mut W, crypto: C, handler: H, + kv: S, user: U, ) -> impl Future> + 't where W: WifiCoex + 't, C: Crypto + 't, H: AsyncHandler + AsyncMetadata + 't, + S: KvBlobStoreAccess + 't, U: UserTask + 't, { wifi.run(MatterStackWirelessTask { stack: self, crypto, handler, + kv, user_task: user, }) } - async fn run_wifi( + async fn run_wifi( &self, mut wifi: W, crypto: C, handler: H, + kv: S, mut user: U, ) -> Result<(), Error> where W: Wifi + Gatt, C: Crypto, H: AsyncHandler + AsyncMetadata, + S: KvBlobStoreAccess, U: UserTask, { loop { - let commissioned = self.is_commissioned().await?; + let commissioned = self.is_commissioned(); if !commissioned { Gatt::run( @@ -213,6 +213,7 @@ where stack: self, crypto: &crypto, handler: &handler, + kv: &kv, user_task: &mut user, }, ) @@ -227,10 +228,9 @@ where let root_handler = self.root_handler(&(), &(), &net_ctl, &false, crypto.weak_rand()?, &handler); - let dm = self.dm(&crypto, (&handler, root_handler)); + let dm = self.dm(&crypto, (&handler, root_handler), &kv); - self.matter() - .close_comm_window(&crypto, dm.change_notify())?; + self.matter().close_comm_window(dm.change_notify())?; } Wifi::run( @@ -239,6 +239,7 @@ where stack: self, crypto: &crypto, handler: &handler, + kv: &kv, user_task: &mut user, }, ) @@ -262,15 +263,20 @@ where comm_policy: &'a dyn CommPolicy, rand: impl RngCore + Copy, handler: H, - ) -> WifiHandler<'a, &'a C, SysHandler<'a, H>> + ) -> WifiHandler< + 'a, + &'a SharedNetworks>, + &'a C, + SysHandler<'a, H>, + > where C: NetCtl + NetCtlStatus + WifiDiag, { with_wifi( gen_diag, netif_diag, - net_ctl, &self.network.networks, + net_ctl, rand, with_sys(comm_policy, rand, handler), ) @@ -446,12 +452,13 @@ where } } -impl<'a, const B: usize, E, C, H, U> GattTask - for MatterStackWirelessTask<'a, B, wireless::Wifi, E, C, H, U> +impl<'a, const B: usize, E, C, H, S, U> GattTask + for MatterStackWirelessTask<'a, B, wireless::Wifi, E, C, H, S, U> where E: Embedding, C: Crypto, H: AsyncMetadata + AsyncHandler, + S: KvBlobStoreAccess, { async fn run

(&mut self, peripheral: P) -> Result<(), Error> where @@ -470,7 +477,9 @@ where self.crypto.weak_rand()?, &self.handler, ); - let dm = self.stack.dm(&self.crypto, (&self.handler, handler)); + let dm = self + .stack + .dm(&self.crypto, (&self.handler, handler), &self.kv); let mut btp_task = pin!(self.stack.run_btp(&self.crypto, peripheral)); @@ -480,23 +489,24 @@ where } } -impl<'a, const B: usize, E, C, H, X> WifiTask - for MatterStackWirelessTask<'a, B, wireless::Wifi, E, C, H, X> +impl<'a, const B: usize, E, C, H, S, X> WifiTask + for MatterStackWirelessTask<'a, B, wireless::Wifi, E, C, H, S, X> where E: Embedding, C: Crypto, H: AsyncMetadata + AsyncHandler, + S: KvBlobStoreAccess, X: UserTask, { - async fn run( + async fn run( &mut self, - net_stack: S, + net_stack: T, netif: N, net_ctl: Q, mut mdns: D, ) -> Result<(), Error> where - S: NetStack, + T: NetStack, N: NetifDiag + NetChangeNotif, Q: NetCtl + WifiDiag + NetChangeNotif, D: Mdns, @@ -517,7 +527,9 @@ where self.crypto.weak_rand()?, &self.handler, ); - let dm = self.stack.dm(&self.crypto, (&self.handler, handler)); + let dm = self + .stack + .dm(&self.crypto, (&self.handler, handler), &self.kv); let stack = &mut self.stack; @@ -554,24 +566,25 @@ where } } -impl<'a, const B: usize, E, C, H, X> WifiCoexTask - for MatterStackWirelessTask<'a, B, wireless::Wifi, E, C, H, X> +impl<'a, const B: usize, E, C, H, S, X> WifiCoexTask + for MatterStackWirelessTask<'a, B, wireless::Wifi, E, C, H, S, X> where E: Embedding, C: Crypto, H: AsyncMetadata + AsyncHandler, + S: KvBlobStoreAccess, X: UserTask, { - async fn run( + async fn run( &mut self, - net_stack: S, + net_stack: T, netif: N, net_ctl: Q, mut mdns: D, mut gatt: G, ) -> Result<(), Error> where - S: NetStack, + T: NetStack, N: NetifDiag + NetChangeNotif, Q: NetCtl + WifiDiag + NetChangeNotif, D: Mdns, @@ -589,7 +602,9 @@ where self.crypto.weak_rand()?, &self.handler, ); - let dm = self.stack.dm(&self.crypto, (&self.handler, handler)); + let dm = self + .stack + .dm(&self.crypto, (&self.handler, handler), &self.kv); let stack = &mut self.stack; let bump = &stack.bump;