Skip to main content

rs_matter/dm/
endpoints.rs

1/*
2 *
3 *    Copyright (c) 2023-2026 Project CHIP Authors
4 *
5 *    Licensed under the Apache License, Version 2.0 (the "License");
6 *    you may not use this file except in compliance with the License.
7 *    You may obtain a copy of the License at
8 *
9 *        http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *    Unless required by applicable law or agreed to in writing, software
12 *    distributed under the License is distributed on an "AS IS" BASIS,
13 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *    See the License for the specific language governing permissions and
15 *    limitations under the License.
16 */
17
18use rand_core::RngCore;
19
20use crate::dm::{ClusterId, EmptyHandler};
21use crate::handler_chain_type;
22
23use super::clusters::acl::{self, AclHandler, ClusterHandler as _};
24use super::clusters::adm_comm::{self, AdminCommHandler, ClusterHandler as _};
25use super::clusters::basic_info::{self, BasicInfoHandler, ClusterHandler as _};
26use super::clusters::desc::{self, ClusterHandler as _, DescHandler};
27use super::clusters::eth_diag::{self, ClusterHandler as _, EthDiagHandler};
28use super::clusters::gen_comm::{self, ClusterHandler as _, CommPolicy, GenCommHandler};
29use super::clusters::gen_diag::{self, ClusterHandler as _, GenDiag, GenDiagHandler, NetifDiag};
30use super::clusters::grp_key_mgmt::{self, ClusterHandler as _, GrpKeyMgmtHandler};
31use super::clusters::net_comm::{
32    self, ClusterAsyncHandler as _, NetCommHandler, NetCtl, NetCtlStatus,
33};
34use super::clusters::noc::{self, ClusterHandler as _, NocHandler};
35use super::clusters::sw_diag::{self, ClusterHandler as _, SwDiag, SwDiagHandler};
36use super::clusters::thread_diag::{self, ClusterHandler as _, ThreadDiag, ThreadDiagHandler};
37use super::clusters::time_sync::{self, ClusterHandler as _, TimeSyncHandler};
38use super::clusters::wifi_diag::{
39    self, AlwaysConnected, ClusterHandler as _, WifiDiag, WifiDiagHandler, WirelessDiag,
40};
41use super::networks::eth::EthNetCtl;
42use super::types::{Async, ChainedHandler, Dataver, EndptId, EpClMatcher};
43
44/// A macro to generate the meta-data for the root endpoint (Endpoint 0).
45///
46/// Net-type token (pick one): `sys`, `eth`, `wifi`, `thread` — same meaning
47/// as the corresponding tokens on the [`crate::clusters!`] macro.
48///
49/// Optional cluster-shape modifiers (in order):
50/// - `acl(aux)` — makes the Access Control cluster advertise the provisional
51///   `AUXILIARY` feature and `AuxiliaryACL` attribute (e.g. for nodes hosting
52///   the Groupcast cluster).
53/// - `sw_diag(heap | watermarks | thread, …)` — shapes the Software
54///   Diagnostics cluster.
55/// - `time_sync(time_zone | ntp_client | ntp_server | time_sync_client, …)` —
56///   shapes the Time Synchronization cluster.
57///
58/// See the [`crate::clusters!`] docs for the token semantics.
59///
60/// The Groups cluster is intentionally not part of any of these presets — it
61/// is not a Root Node device-type cluster and has no defined behavior on the
62/// root endpoint. Add `GroupsHandler::CLUSTER` to the application endpoint(s)
63/// where group-addressed traffic is actually meaningful.
64#[allow(unused_macros)]
65#[macro_export]
66macro_rules! root_endpoint {
67    ($t:ident
68        $(, acl($($acl_opt:ident),* $(,)?))?
69        $(, sw_diag($($sw_opt:ident),* $(,)?))?
70        $(, time_sync($($ts_opt:ident),* $(,)?))?
71    ) => {
72        $crate::dm::Endpoint {
73            id: $crate::dm::endpoints::ROOT_ENDPOINT_ID,
74            device_types: $crate::devices!($crate::dm::devices::DEV_TYPE_ROOT_NODE),
75            clusters: $crate::clusters!(
76                $t
77                $(, acl($($acl_opt),*))?
78                $(, sw_diag($($sw_opt),*))?
79                $(, time_sync($($ts_opt),*))?
80                ;
81            ),
82            client_clusters: &[],
83            unique_id: None,
84            semantic_tags: &[],
85        }
86    }
87}
88
89/// A type alias for the handler chain returned by `eth_sys_handler()`.
90pub type EthSysHandler<'a> =
91    SysHandler<'a, EthNetCtl<'a>, eth_diag::HandlerAdaptor<EthDiagHandler>>;
92
93/// A type alias for the handler chain returned by `wifi_sys_handler()`.
94pub type WifiSysHandler<'a, T> = SysHandler<'a, T, wifi_diag::HandlerAdaptor<WifiDiagHandler<'a>>>;
95
96/// A type alias for the handler chain returned by `thread_sys_handler()`.
97pub type ThreadSysHandler<'a, T> =
98    SysHandler<'a, T, thread_diag::HandlerAdaptor<ThreadDiagHandler<'a>>>;
99
100/// A type alias for the handler chain returned by `sys_handler()`.
101pub type SysHandler<'a, T, N> = handler_chain_type!(
102    EpClMatcher => net_comm::HandlerAsyncAdaptor<NetCommHandler<'a, T>>
103    | Async<handler_chain_type!(
104        EpClMatcher => desc::HandlerAdaptor<DescHandler<'a>>,
105        EpClMatcher => basic_info::HandlerAdaptor<BasicInfoHandler>,
106        EpClMatcher => gen_comm::HandlerAdaptor<GenCommHandler<'a>>,
107        EpClMatcher => adm_comm::HandlerAdaptor<AdminCommHandler>,
108        EpClMatcher => noc::HandlerAdaptor<NocHandler>,
109        EpClMatcher => acl::HandlerAdaptor<acl::AclHandler>,
110        EpClMatcher => grp_key_mgmt::HandlerAdaptor<GrpKeyMgmtHandler>,
111        EpClMatcher => sw_diag::HandlerAdaptor<SwDiagHandler<'a>>,
112        EpClMatcher => time_sync::HandlerAdaptor<TimeSyncHandler<'a>>,
113        EpClMatcher => gen_diag::HandlerAdaptor<GenDiagHandler<'a>>,
114        EpClMatcher => N
115    )>
116);
117
118/// The ID of the root endpoint (Endpoint 0)
119pub const ROOT_ENDPOINT_ID: EndptId = 0;
120
121/// Return a system handler for the root endpoint (Endpoint 0).
122/// Use this handler for devices that use Ethernet as the Matter Operational Network.
123///
124/// # Arguments:
125/// - `comm_policy`: The `CommPolicy` implementation.
126/// - `gen_diag`: The `GenDiag` implementation.
127/// - `netif_diag`: The `NetifDiag` implementation.
128/// - `sw_diag`: The `SwDiag` implementation (pass `&()` for the
129///   no-op default: heap counters report `0`).
130/// - `rand`: A random number generator.
131#[allow(clippy::too_many_arguments)]
132pub fn eth_sys_handler<'a, R: RngCore>(
133    comm_policy: &'a dyn CommPolicy,
134    gen_diag: &'a dyn GenDiag,
135    netif_diag: &'a dyn NetifDiag,
136    sw_diag: &'a dyn SwDiag,
137    mut rand: R,
138) -> EthSysHandler<'a> {
139    sys_handler(
140        comm_policy,
141        gen_diag,
142        netif_diag,
143        sw_diag,
144        EthNetCtl::new_default(),
145        &AlwaysConnected,
146        EthDiagHandler::CLUSTER.id,
147        EthDiagHandler::new(Dataver::new_rand(&mut rand)).adapt(),
148        rand,
149    )
150}
151
152/// Return a system handler for the root endpoint (Endpoint 0).
153/// Use this handler for devices that use Wifi as the Matter Operational Network.
154///
155/// # Arguments:
156/// - `comm_policy`: The `CommPolicy` implementation.
157/// - `gen_diag`: The `GenDiag` implementation.
158/// - `netif_diag`: The `NetifDiag` implementation.
159/// - `wifi_diag`: The `WifiDiag` implementation.
160/// - `sw_diag`: The `SwDiag` implementation (pass `&()` for the no-op default).
161/// - `net_ctl`: The `NetCtl` implementation.
162/// - `rand`: A random number generator.
163#[allow(clippy::too_many_arguments)]
164pub fn wifi_sys_handler<'a, R: RngCore, T>(
165    comm_policy: &'a dyn CommPolicy,
166    gen_diag: &'a dyn GenDiag,
167    netif_diag: &'a dyn NetifDiag,
168    wifi_diag: &'a dyn WifiDiag,
169    sw_diag: &'a dyn SwDiag,
170    net_ctl: T,
171    mut rand: R,
172) -> WifiSysHandler<'a, T>
173where
174    T: NetCtl + NetCtlStatus,
175{
176    sys_handler(
177        comm_policy,
178        gen_diag,
179        netif_diag,
180        sw_diag,
181        net_ctl,
182        wifi_diag,
183        WifiDiagHandler::CLUSTER.id,
184        WifiDiagHandler::new(Dataver::new_rand(&mut rand), wifi_diag).adapt(),
185        rand,
186    )
187}
188
189/// Return a system handler for the root endpoint (Endpoint 0).
190/// Use this handler for devices that use Thread as the Matter Operational Network.
191///
192/// # Arguments:
193/// - `comm_policy`: The `CommPolicy` implementation.
194/// - `gen_diag`: The `GenDiag` implementation.
195/// - `netif_diag`: The `NetifDiag` implementation.
196/// - `thread_diag`: The `ThreadDiag` implementation.
197/// - `sw_diag`: The `SwDiag` implementation (pass `&()` for the no-op default).
198/// - `net_ctl`: The `NetCtl` implementation.
199/// - `rand`: A random number generator.
200#[allow(clippy::too_many_arguments)]
201pub fn thread_sys_handler<'a, R: RngCore, T>(
202    comm_policy: &'a dyn CommPolicy,
203    gen_diag: &'a dyn GenDiag,
204    netif_diag: &'a dyn NetifDiag,
205    thread_diag: &'a dyn ThreadDiag,
206    sw_diag: &'a dyn SwDiag,
207    net_ctl: T,
208    mut rand: R,
209) -> ThreadSysHandler<'a, T>
210where
211    T: NetCtl + NetCtlStatus,
212{
213    sys_handler(
214        comm_policy,
215        gen_diag,
216        netif_diag,
217        sw_diag,
218        net_ctl,
219        thread_diag,
220        ThreadDiagHandler::CLUSTER.id,
221        ThreadDiagHandler::new(Dataver::new_rand(&mut rand), thread_diag).adapt(),
222        rand,
223    )
224}
225
226/// Return a system handler for the root endpoint (Endpoint 0).
227/// Note that this handler does not include the Network Diagnostic handler, which is dependent on
228/// the network type and thus is not included in this function.
229///
230/// Use `eth_sys_handler()`, `wifi_sys_handler()` or `thread_sys_handler()` instead to get the appropriate
231/// Network Diagnostic handler included in the handler.
232///
233/// # Arguments:
234/// - `comm_policy`: The `CommPolicy` implementation.
235/// - `gen_diag`: The `GenDiag` implementation.
236/// - `netif_diag`: The `NetifDiag` implementation.
237/// - `networks`: The `Networks` implementation.
238/// - `net_ctl`: The `NetCtl` implementation.
239/// - `rand`: A random number generator.
240#[allow(clippy::too_many_arguments)]
241fn sys_handler<'a, R: RngCore, T, N>(
242    comm_policy: &'a dyn CommPolicy,
243    gen_diag: &'a dyn GenDiag,
244    netif_diag: &'a dyn NetifDiag,
245    sw_diag: &'a dyn SwDiag,
246    net_ctl: T,
247    wireless_diag: &'a dyn WirelessDiag,
248    netw_diag_cluster_id: ClusterId,
249    netw_diag: N,
250    mut rand: R,
251) -> SysHandler<'a, T, N>
252where
253    T: NetCtl + NetCtlStatus,
254{
255    ChainedHandler::new(
256        EpClMatcher::new(
257            Some(ROOT_ENDPOINT_ID),
258            Some(NetCommHandler::<T>::CLUSTER.id),
259        ),
260        NetCommHandler::new(Dataver::new_rand(&mut rand), net_ctl, wireless_diag).adapt(),
261        Async(
262            ChainedHandler::new(
263                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(netw_diag_cluster_id)),
264                netw_diag,
265                EmptyHandler,
266            )
267            .chain(
268                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(GenDiagHandler::CLUSTER.id)),
269                GenDiagHandler::new(Dataver::new_rand(&mut rand), gen_diag, netif_diag).adapt(),
270            )
271            .chain(
272                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(TimeSyncHandler::CLUSTER.id)),
273                TimeSyncHandler::new(Dataver::new_rand(&mut rand)).adapt(),
274            )
275            .chain(
276                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(SwDiagHandler::CLUSTER.id)),
277                SwDiagHandler::new(Dataver::new_rand(&mut rand), sw_diag).adapt(),
278            )
279            .chain(
280                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(GrpKeyMgmtHandler::CLUSTER.id)),
281                GrpKeyMgmtHandler::new(Dataver::new_rand(&mut rand)).adapt(),
282            )
283            .chain(
284                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(AclHandler::CLUSTER.id)),
285                AclHandler::new(Dataver::new_rand(&mut rand)).adapt(),
286            )
287            .chain(
288                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(NocHandler::CLUSTER.id)),
289                NocHandler::new(Dataver::new_rand(&mut rand)).adapt(),
290            )
291            .chain(
292                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(AdminCommHandler::CLUSTER.id)),
293                AdminCommHandler::new(Dataver::new_rand(&mut rand)).adapt(),
294            )
295            .chain(
296                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(GenCommHandler::CLUSTER.id)),
297                GenCommHandler::new(Dataver::new_rand(&mut rand), comm_policy).adapt(),
298            )
299            .chain(
300                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(BasicInfoHandler::CLUSTER.id)),
301                BasicInfoHandler::new(Dataver::new_rand(&mut rand)).adapt(),
302            )
303            .chain(
304                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(DescHandler::CLUSTER.id)),
305                DescHandler::new(Dataver::new_rand(&mut rand)).adapt(),
306            ),
307        ),
308    )
309}
310
311// ---- Sys-handler builders ----------------------------------------------------
312//
313// Thin builders over the `eth_sys_handler` / `wifi_sys_handler` /
314// `thread_sys_handler` free fns: each cluster-data hook is a setter, unset
315// ones fall back to the canonical no-op default (`&true` for `CommPolicy`,
316// `&()` for every other trait — `bool: CommPolicy` and `(): GenDiag` /
317// `NetifDiag` / `TimeSync` / `SwDiag` are already impls in the crate). New
318// hooks can be added later by extending one struct + adding a setter, with
319// no churn on existing call sites.
320
321/// Builder for an Ethernet root-endpoint system handler.
322///
323/// Unset hooks fall back to no-op defaults: `&true` for `CommPolicy`
324/// (commissioning open / allowed) and `&()` for every other trait
325/// (reports nothing / no-op).
326///
327/// ```ignore
328/// let h = EthSysHandlerBuilder::new()
329///     .gen_diag(&my_gen_diag)
330///     .netif_diag(&SysNetifs)
331///     .build(rand);
332/// ```
333pub struct EthSysHandlerBuilder<'a> {
334    comm_policy: &'a dyn CommPolicy,
335    gen_diag: &'a dyn GenDiag,
336    netif_diag: &'a dyn NetifDiag,
337    sw_diag: &'a dyn SwDiag,
338}
339
340impl Default for EthSysHandlerBuilder<'_> {
341    fn default() -> Self {
342        Self::new()
343    }
344}
345
346impl<'a> EthSysHandlerBuilder<'a> {
347    /// Create a builder. Every hook defaults to a no-op provider.
348    pub const fn new() -> Self {
349        Self {
350            comm_policy: &true,
351            gen_diag: &(),
352            netif_diag: &(),
353            sw_diag: &(),
354        }
355    }
356
357    /// Set the `CommPolicy` hook (commissioning window policy).
358    pub const fn comm_policy(mut self, comm_policy: &'a dyn CommPolicy) -> Self {
359        self.comm_policy = comm_policy;
360        self
361    }
362
363    /// Set the `GenDiag` hook (General Diagnostics data provider).
364    pub const fn gen_diag(mut self, gen_diag: &'a dyn GenDiag) -> Self {
365        self.gen_diag = gen_diag;
366        self
367    }
368
369    /// Set the `NetifDiag` hook (network-interface enumeration).
370    pub const fn netif_diag(mut self, netif_diag: &'a dyn NetifDiag) -> Self {
371        self.netif_diag = netif_diag;
372        self
373    }
374
375    /// Set the `SwDiag` hook (Software Diagnostics data provider).
376    pub const fn sw_diag(mut self, sw_diag: &'a dyn SwDiag) -> Self {
377        self.sw_diag = sw_diag;
378        self
379    }
380
381    /// Build the Ethernet system handler.
382    pub fn build<R: RngCore>(self, rand: R) -> EthSysHandler<'a> {
383        eth_sys_handler(
384            self.comm_policy,
385            self.gen_diag,
386            self.netif_diag,
387            self.sw_diag,
388            rand,
389        )
390    }
391}
392
393/// Builder for a Wi-Fi root-endpoint system handler.
394///
395/// `net_ctl` and `wifi_diag` are required (no sensible default) and supplied
396/// to [`Self::new`]; everything else falls back to no-op defaults.
397pub struct WifiSysHandlerBuilder<'a, T> {
398    comm_policy: &'a dyn CommPolicy,
399    gen_diag: &'a dyn GenDiag,
400    netif_diag: &'a dyn NetifDiag,
401    wifi_diag: &'a dyn WifiDiag,
402    sw_diag: &'a dyn SwDiag,
403    net_ctl: T,
404}
405
406impl<'a, T> WifiSysHandlerBuilder<'a, T>
407where
408    T: NetCtl + NetCtlStatus,
409{
410    /// Create a builder. `net_ctl` and `wifi_diag` are required;
411    /// every other hook defaults to a no-op provider.
412    pub const fn new(net_ctl: T, wifi_diag: &'a dyn WifiDiag) -> Self {
413        Self {
414            comm_policy: &true,
415            gen_diag: &(),
416            netif_diag: &(),
417            wifi_diag,
418            sw_diag: &(),
419            net_ctl,
420        }
421    }
422
423    /// Set the `CommPolicy` hook.
424    pub const fn comm_policy(mut self, comm_policy: &'a dyn CommPolicy) -> Self {
425        self.comm_policy = comm_policy;
426        self
427    }
428
429    /// Set the `GenDiag` hook.
430    pub const fn gen_diag(mut self, gen_diag: &'a dyn GenDiag) -> Self {
431        self.gen_diag = gen_diag;
432        self
433    }
434
435    /// Set the `NetifDiag` hook.
436    pub const fn netif_diag(mut self, netif_diag: &'a dyn NetifDiag) -> Self {
437        self.netif_diag = netif_diag;
438        self
439    }
440
441    /// Set the `SwDiag` hook.
442    pub const fn sw_diag(mut self, sw_diag: &'a dyn SwDiag) -> Self {
443        self.sw_diag = sw_diag;
444        self
445    }
446
447    /// Build the Wi-Fi system handler.
448    pub fn build<R: RngCore>(self, rand: R) -> WifiSysHandler<'a, T> {
449        wifi_sys_handler(
450            self.comm_policy,
451            self.gen_diag,
452            self.netif_diag,
453            self.wifi_diag,
454            self.sw_diag,
455            self.net_ctl,
456            rand,
457        )
458    }
459}
460
461/// Builder for a Thread root-endpoint system handler.
462///
463/// `net_ctl` and `thread_diag` are required (no sensible default) and supplied
464/// to [`Self::new`]; everything else falls back to no-op defaults.
465pub struct ThreadSysHandlerBuilder<'a, T> {
466    comm_policy: &'a dyn CommPolicy,
467    gen_diag: &'a dyn GenDiag,
468    netif_diag: &'a dyn NetifDiag,
469    thread_diag: &'a dyn ThreadDiag,
470    sw_diag: &'a dyn SwDiag,
471    net_ctl: T,
472}
473
474impl<'a, T> ThreadSysHandlerBuilder<'a, T>
475where
476    T: NetCtl + NetCtlStatus,
477{
478    /// Create a builder. `net_ctl` and `thread_diag` are required;
479    /// every other hook defaults to a no-op provider.
480    pub const fn new(net_ctl: T, thread_diag: &'a dyn ThreadDiag) -> Self {
481        Self {
482            comm_policy: &true,
483            gen_diag: &(),
484            netif_diag: &(),
485            thread_diag,
486            sw_diag: &(),
487            net_ctl,
488        }
489    }
490
491    /// Set the `CommPolicy` hook.
492    pub const fn comm_policy(mut self, comm_policy: &'a dyn CommPolicy) -> Self {
493        self.comm_policy = comm_policy;
494        self
495    }
496
497    /// Set the `GenDiag` hook.
498    pub const fn gen_diag(mut self, gen_diag: &'a dyn GenDiag) -> Self {
499        self.gen_diag = gen_diag;
500        self
501    }
502
503    /// Set the `NetifDiag` hook.
504    pub const fn netif_diag(mut self, netif_diag: &'a dyn NetifDiag) -> Self {
505        self.netif_diag = netif_diag;
506        self
507    }
508
509    /// Set the `SwDiag` hook.
510    pub const fn sw_diag(mut self, sw_diag: &'a dyn SwDiag) -> Self {
511        self.sw_diag = sw_diag;
512        self
513    }
514
515    /// Build the Thread system handler.
516    pub fn build<R: RngCore>(self, rand: R) -> ThreadSysHandler<'a, T> {
517        thread_sys_handler(
518            self.comm_policy,
519            self.gen_diag,
520            self.netif_diag,
521            self.thread_diag,
522            self.sw_diag,
523            self.net_ctl,
524            rand,
525        )
526    }
527}