Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
* The networks store of the wireless stacks is a type parameter, `S`, after `E` - `WifiMatterStack<'a, B, E, S>`, `ThreadMatterStack<'a, B, E, S>` - by default a `WirelessNetworks` with `MAX_WIRELESS_NETWORKS` networks; it lives in `WirelessBle`, lent to the Network Commissioning handler by `run`
* (Breaking) The `Network` trait loses `Networks`, `NETWORKS` and `init_networks`; `MatterStackInteractionModel` and `MatterStackInteractionModelState` lose their networks and network controller parameters
* (Breaking) Ethernet diagnostics: `run_preex`, `PreexistingEthernet::new` and `EthernetTask::run` take an `EthDiag` (`&()` if there is nothing to report)
* Thread and Wi-Fi diagnostics: forward the MAC/MLE counters and the other new optional attributes of the operational network controller
* `InterfaceEnabled`: the operational network controller decides whether the interface can be disabled (`NetCtl::set_enabled`); it can't during BLE commissioning

## [0.3.1] - 2026-09-20
* Raise MSRV to 1.89
Expand Down
43 changes: 7 additions & 36 deletions src/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@ use embassy_futures::select::select4;
use rs_matter::crypto::Crypto;
use rs_matter::dm::clusters::eth_diag::EthDiag;
use rs_matter::dm::clusters::gen_diag::NetifDiag;
use rs_matter::dm::clusters::net_comm::{DummyNetworks, NetworkType};
use rs_matter::dm::endpoints::eth_net_handler;
use rs_matter::dm::networks::wireless::NoopWirelessNetCtl;
use rs_matter::dm::networks::NetChangeNotif;
use rs_matter::dm::{DataModel, Endpoint};
use rs_matter::error::Error;
use rs_matter::pairing::DiscoveryCapabilities;
use rs_matter::persist::{KvBlobStore, KvBlobStoreAccess};
use rs_matter::root_endpoint;
use rs_matter::transport::network::NoNetwork;
use rs_matter::utils::init::{init, init_from_closure, Init};
use rs_matter::utils::init::{init, Init};
use rs_matter::utils::select::Coalesce;

use crate::mdns::Mdns;
Expand Down Expand Up @@ -51,26 +49,12 @@ where
where
E: 'a;

// Ethernet does not manage network credentials, so use the no-op networks store.
type Networks = DummyNetworks;

const NETWORKS: Self::Networks = DummyNetworks;

fn init() -> impl Init<Self> {
init!(Self {
embedding <- E::init(),
})
}

fn init_networks() -> impl Init<Self::Networks> {
unsafe {
init_from_closure(|slot: *mut DummyNetworks| {
slot.write(DummyNetworks);
Ok(())
})
}
}

fn discovery_capabilities(&self) -> DiscoveryCapabilities {
DiscoveryCapabilities::IP
}
Expand Down Expand Up @@ -208,17 +192,9 @@ where

self.matter.factory_reset(&kv)?;

// Reset the events counter (and the - no-op for Ethernet - networks store)
// so we don't carry a stale watermark across a factory reset
// (Matter Core spec R1.5.1, §7.14.1.1).
self.im(
crypto,
handler,
&kv,
NoopWirelessNetCtl::new(NetworkType::Ethernet),
)
.factory_reset()
.await
// Reset the events counter so we don't carry a stale watermark across a
// factory reset (Matter Core spec R1.5.1, §7.14.1.1).
self.im(crypto, handler, &kv).factory_reset().await
}

/// Run the startup sequence of the stack: re-hydrate the persisted state and
Expand Down Expand Up @@ -414,14 +390,9 @@ where
&self.handler,
self.crypto.weak_rand()?,
);
// Ethernet does not manage networks, so use the inert wireless net-ctl;
// the engine's connection-manager branch then stays dormant.
let im = self.stack.im(
&self.crypto,
(&self.handler, combined),
&self.kv,
NoopWirelessNetCtl::new(NetworkType::Ethernet),
);
let im = self
.stack
.im(&self.crypto, (&self.handler, combined), &self.kv);

let mut net_task = pin_alloc!(
self.stack.bump,
Expand Down
80 changes: 28 additions & 52 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ use rs_matter::crypto::{Crypto, Rng};
use rs_matter::dm::clusters::basic_info::BasicInfoConfig;
use rs_matter::dm::clusters::dev_att::DeviceAttestation;
use rs_matter::dm::clusters::gen_diag::NetifDiag;
use rs_matter::dm::clusters::net_comm::{NetCtl, NetCtlStatus, Networks};
use rs_matter::dm::clusters::sw_diag::SwDiag;
use rs_matter::dm::clusters::wifi_diag::WirelessDiag;
use rs_matter::dm::endpoints::RootHandler;
use rs_matter::dm::networks::NetChangeNotif;
use rs_matter::dm::{AttrChangeNotifier, AttrId, ClusterId, DataModel, EndptId};
Expand Down Expand Up @@ -226,26 +224,22 @@ cfg_if! {

const MAX_BUSY_RESPONDERS: usize = 2;

pub type MatterStackInteractionModel<'a, C, H, K, RN, NC> = InteractionModel<
pub type MatterStackInteractionModel<'a, C, H, K> = InteractionModel<
'a,
C,
MatterBuffers<MAX_IM_BUFFERS>,
H,
K,
RN,
NC,
// The accessory role: inbound `ReportData` is disowned.
(),
MAX_SUBSCRIPTIONS,
EVENTS_RINGBUF_SIZE,
>;

/// The `InteractionModelState` specialization owned by `MatterStack`.
///
/// It owns the subscriptions table, the events queue and the `rs-matter`
/// networks store as a single unit. The KV scratch buffer now lives in `Matter`.
pub type MatterStackInteractionModelState<RN> =
InteractionModelState<RN, MAX_SUBSCRIPTIONS, EVENTS_RINGBUF_SIZE>;
/// The `InteractionModelState` specialization owned by `MatterStack`: the
/// subscriptions table and the events queue.
pub type MatterStackInteractionModelState =
InteractionModelState<MAX_SUBSCRIPTIONS, EVENTS_RINGBUF_SIZE>;

/// The `MatterStack` struct is the main entry point for the Matter stack.
///
Expand All @@ -256,13 +250,13 @@ where
{
matter: Matter<'a>,
buffers: MatterBuffers<MAX_IM_BUFFERS>,
/// The interaction-model state: subscriptions table, events queue, the
/// `rs-matter` networks store, and the KV scratch buffer, owned as one unit.
state: MatterStackInteractionModelState<N::Networks>,
/// The interaction-model state: subscriptions table and events queue.
state: MatterStackInteractionModelState,
bump: Bump<B>,
run_lock: IfMutex<()>,
/// Whether the Interaction Model state (events watermark, networks store,
/// persisted subscriptions) has already been re-hydrated from the KV store.
/// Whether the Interaction Model state (events watermark, persisted
/// subscriptions) and the cluster handlers (the networks store among them)
/// have already been re-hydrated from the KV store.
///
/// `InteractionModel::startup` cannot be driven from `MatterStack::startup`,
/// because it has to run on the very Interaction Model instance that is then
Expand Down Expand Up @@ -315,7 +309,7 @@ where
Self {
matter: Matter::new(dev_det, dev_comm, dev_att, MATTER_PORT),
buffers: MatterBuffers::new(),
state: MatterStackInteractionModelState::new(N::NETWORKS),
state: MatterStackInteractionModelState::new(),
bump: Bump::new(),
run_lock: IfMutex::new(()),
im_hydrated: Mutex::new(Cell::new(false)),
Expand All @@ -338,7 +332,7 @@ where
MATTER_PORT,
),
buffers <- MatterBuffers::init(),
state <- MatterStackInteractionModelState::init(N::init_networks()),
state <- MatterStackInteractionModelState::init(),
bump <- Bump::init(),
run_lock <- IfMutex::init(()),
im_hydrated: Mutex::new(Cell::new(false)),
Expand Down Expand Up @@ -672,40 +666,30 @@ where
}

#[inline(always)]
fn im<C, H, K, NC>(
&self,
crypto: C,
handler: H,
kv: K,
net_ctl: NC,
) -> MatterStackInteractionModel<'_, C, H, K, N::Networks, NC>
fn im<C, H, K>(&self, crypto: C, handler: H, kv: K) -> MatterStackInteractionModel<'_, C, H, K>
where
C: Crypto,
H: DataModel,
K: KvBlobStoreAccess,
NC: NetCtl + NetCtlStatus + WirelessDiag + NetChangeNotif,
{
MatterStackInteractionModel::new_with_net_ctl(
MatterStackInteractionModel::new(
self.matter(),
crypto,
&self.buffers,
handler,
kv,
net_ctl,
&self.state,
)
}

async fn run_im<C, H, K, RN, NC>(
async fn run_im<C, H, K>(
&self,
im: &MatterStackInteractionModel<'_, C, H, K, RN, NC>,
im: &MatterStackInteractionModel<'_, C, H, K>,
) -> Result<(), Error>
where
C: Crypto,
H: DataModel,
K: KvBlobStoreAccess,
RN: Networks,
NC: NetCtl + NetCtlStatus + WirelessDiag + NetChangeNotif,
{
// TODO
// Reset the Matter transport buffers and all sessions first
Expand All @@ -719,16 +703,14 @@ where
select(&mut responder, &mut im_job).coalesce().await
}

async fn run_im_with_bump<C, H, K, RN, NC>(
async fn run_im_with_bump<C, H, K>(
&self,
im: &MatterStackInteractionModel<'_, C, H, K, RN, NC>,
im: &MatterStackInteractionModel<'_, C, H, K>,
) -> Result<(), Error>
where
C: Crypto,
H: DataModel,
K: KvBlobStoreAccess,
RN: Networks,
NC: NetCtl + NetCtlStatus + WirelessDiag + NetChangeNotif,
{
// TODO
// Reset the Matter transport buffers and all sessions first
Expand All @@ -742,23 +724,21 @@ where
select(&mut responder, &mut im_job).coalesce().await
}

/// Re-hydrate the Interaction Model state (events watermark, networks store,
/// persisted subscriptions) and deliver the `Startup` lifecycle op to the
/// cluster handlers - but only for the first Interaction Model instance that
/// this stack runs (see `im_hydrated`).
/// Re-hydrate the Interaction Model state (events watermark, persisted
/// subscriptions) and deliver the `Startup` lifecycle op to the cluster
/// handlers, which load the networks store among others - but only for the
/// first Interaction Model instance that this stack runs (see `im_hydrated`).
///
/// `BasicInformation::StartUp` is emitted by `InteractionModel::run` itself,
/// once per process lifetime.
async fn startup_im<C, H, K, RN, NC>(
async fn startup_im<C, H, K>(
&self,
im: &MatterStackInteractionModel<'_, C, H, K, RN, NC>,
im: &MatterStackInteractionModel<'_, C, H, K>,
) -> Result<(), Error>
where
C: Crypto,
H: DataModel,
K: KvBlobStoreAccess,
RN: Networks,
NC: NetCtl + NetCtlStatus + WirelessDiag + NetChangeNotif,
{
if self.im_hydrated.lock(|hydrated| hydrated.replace(true)) {
return Ok(());
Expand All @@ -767,16 +747,14 @@ where
im.startup().await
}

async fn run_responder<C, H, K, RN, NC>(
async fn run_responder<C, H, K>(
&self,
im: &MatterStackInteractionModel<'_, C, H, K, RN, NC>,
im: &MatterStackInteractionModel<'_, C, H, K>,
) -> Result<(), Error>
where
C: Crypto,
H: DataModel,
K: KvBlobStoreAccess,
RN: Networks,
NC: NetCtl + NetCtlStatus + WirelessDiag + NetChangeNotif,
{
let responder = DefaultResponder::new(im);

Expand All @@ -787,16 +765,14 @@ where
Ok(())
}

async fn run_responder_with_bump<C, H, K, RN, NC>(
async fn run_responder_with_bump<C, H, K>(
&self,
im: &MatterStackInteractionModel<'_, C, H, K, RN, NC>,
im: &MatterStackInteractionModel<'_, C, H, K>,
) -> Result<(), Error>
where
C: Crypto,
H: DataModel,
K: KvBlobStoreAccess,
RN: Networks,
NC: NetCtl + NetCtlStatus + WirelessDiag + NetChangeNotif,
{
let responder = DefaultResponder::new(im);

Expand Down
16 changes: 0 additions & 16 deletions src/network.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use rs_matter::dm::clusters::net_comm::Networks as RsNetworks;
use rs_matter::pairing::DiscoveryCapabilities;
use rs_matter::utils::init::{init_from_closure, Init};

Expand Down Expand Up @@ -31,24 +30,9 @@ pub trait Network: Sealed {
where
Self: 'a;

/// The `rs-matter` networks store type owned by the stack's
/// `InteractionModelState` for this network kind.
///
/// For Ethernet (and other transports where the Matter stack does not manage
/// network credentials) this is a no-op store; for the wireless (BLE+Wifi/Thread)
/// network it is a `WirelessNetworks` store.
type Networks: RsNetworks;

/// A const initializer for the `rs-matter` networks store (used by the
/// `const` `MatterStack::new` constructor).
const NETWORKS: Self::Networks;

/// Return an in-place initializer for the network type.
fn init() -> impl Init<Self>;

/// Return an in-place initializer for the `rs-matter` networks store.
fn init_networks() -> impl Init<Self::Networks>;

/// Return the discovery capabilities of this network when commissioning the device.
fn discovery_capabilities(&self) -> DiscoveryCapabilities;

Expand Down
Loading
Loading