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 _, TimeSync, TimeSyncHandler};
38use super::clusters::wifi_diag::{self, ClusterHandler as _, WifiDiag, WifiDiagHandler};
39use super::networks::eth::EthNetCtl;
40use super::types::{Async, ChainedHandler, Dataver, EndptId, EpClMatcher};
41
42/// A macro to generate the meta-data for the root endpoint (Endpoint 0).
43///
44/// Net-type token (pick one): `sys`, `eth`, `wifi`, `thread` — same meaning
45/// as the corresponding tokens on the [`crate::clusters!`] macro.
46///
47/// Optional cluster-shape modifiers (in order):
48/// - `sw_diag(heap | watermarks | thread, …)` — shapes the Software
49///   Diagnostics cluster.
50/// - `time_sync(time_zone | ntp_client | ntp_server | time_sync_client, …)` —
51///   shapes the Time Synchronization cluster.
52///
53/// See the [`crate::clusters!`] docs for the token semantics.
54///
55/// The Groups cluster is intentionally not part of any of these presets — it
56/// is not a Root Node device-type cluster and has no defined behavior on the
57/// root endpoint. Add `GroupsHandler::CLUSTER` to the application endpoint(s)
58/// where group-addressed traffic is actually meaningful.
59#[allow(unused_macros)]
60#[macro_export]
61macro_rules! root_endpoint {
62    ($t:ident
63        $(, sw_diag($($sw_opt:ident),* $(,)?))?
64        $(, time_sync($($ts_opt:ident),* $(,)?))?
65    ) => {
66        $crate::dm::Endpoint {
67            id: $crate::dm::endpoints::ROOT_ENDPOINT_ID,
68            device_types: $crate::devices!($crate::dm::devices::DEV_TYPE_ROOT_NODE),
69            clusters: $crate::clusters!(
70                $t
71                $(, sw_diag($($sw_opt),*))?
72                $(, time_sync($($ts_opt),*))?
73                ;
74            ),
75            client_clusters: &[],
76        }
77    }
78}
79
80/// A type alias for the handler chain returned by `eth_sys_handler()`.
81pub type EthSysHandler<'a> = SysHandler<'a, EthNetCtl, eth_diag::HandlerAdaptor<EthDiagHandler>>;
82
83/// A type alias for the handler chain returned by `wifi_sys_handler()`.
84pub type WifiSysHandler<'a, T> = SysHandler<'a, T, wifi_diag::HandlerAdaptor<WifiDiagHandler<'a>>>;
85
86/// A type alias for the handler chain returned by `thread_sys_handler()`.
87pub type ThreadSysHandler<'a, T> =
88    SysHandler<'a, T, thread_diag::HandlerAdaptor<ThreadDiagHandler<'a>>>;
89
90/// A type alias for the handler chain returned by `sys_handler()`.
91pub type SysHandler<'a, T, N> = handler_chain_type!(
92    EpClMatcher => net_comm::HandlerAsyncAdaptor<NetCommHandler<T>>
93    | Async<handler_chain_type!(
94        EpClMatcher => desc::HandlerAdaptor<DescHandler<'a>>,
95        EpClMatcher => basic_info::HandlerAdaptor<BasicInfoHandler>,
96        EpClMatcher => gen_comm::HandlerAdaptor<GenCommHandler<'a>>,
97        EpClMatcher => adm_comm::HandlerAdaptor<AdminCommHandler>,
98        EpClMatcher => noc::HandlerAdaptor<NocHandler>,
99        EpClMatcher => acl::HandlerAdaptor<acl::AclHandler>,
100        EpClMatcher => grp_key_mgmt::HandlerAdaptor<GrpKeyMgmtHandler>,
101        EpClMatcher => sw_diag::HandlerAdaptor<SwDiagHandler<'a>>,
102        EpClMatcher => time_sync::HandlerAdaptor<TimeSyncHandler<'a>>,
103        EpClMatcher => gen_diag::HandlerAdaptor<GenDiagHandler<'a>>,
104        EpClMatcher => N
105    )>
106);
107
108/// The ID of the root endpoint (Endpoint 0)
109pub const ROOT_ENDPOINT_ID: EndptId = 0;
110
111/// Return a system handler for the root endpoint (Endpoint 0).
112/// Use this handler for devices that use Ethernet as the Matter Operational Network.
113///
114/// # Arguments:
115/// - `comm_policy`: The `CommPolicy` implementation.
116/// - `gen_diag`: The `GenDiag` implementation.
117/// - `netif_diag`: The `NetifDiag` implementation.
118/// - `time_sync`: The `TimeSync` implementation (pass `&()` for the
119///   no-op default: `UTCTime = Null`, `Granularity = NoTime`,
120///   `TimeSource = None`).
121/// - `sw_diag`: The `SwDiag` implementation (pass `&()` for the
122///   no-op default: heap counters report `0`).
123/// - `rand`: A random number generator.
124#[allow(clippy::too_many_arguments)]
125pub fn eth_sys_handler<'a, R: RngCore>(
126    comm_policy: &'a dyn CommPolicy,
127    gen_diag: &'a dyn GenDiag,
128    netif_diag: &'a dyn NetifDiag,
129    time_sync: &'a dyn TimeSync,
130    sw_diag: &'a dyn SwDiag,
131    mut rand: R,
132) -> EthSysHandler<'a> {
133    sys_handler(
134        comm_policy,
135        gen_diag,
136        netif_diag,
137        time_sync,
138        sw_diag,
139        EthNetCtl,
140        EthDiagHandler::CLUSTER.id,
141        EthDiagHandler::new(Dataver::new_rand(&mut rand)).adapt(),
142        rand,
143    )
144}
145
146/// Return a system handler for the root endpoint (Endpoint 0).
147/// Use this handler for devices that use Wifi as the Matter Operational Network.
148///
149/// # Arguments:
150/// - `comm_policy`: The `CommPolicy` implementation.
151/// - `gen_diag`: The `GenDiag` implementation.
152/// - `netif_diag`: The `NetifDiag` implementation.
153/// - `wifi_diag`: The `WifiDiag` implementation.
154/// - `time_sync`: The `TimeSync` implementation (pass `&()` for the no-op default).
155/// - `sw_diag`: The `SwDiag` implementation (pass `&()` for the no-op default).
156/// - `net_ctl`: The `NetCtl` implementation.
157/// - `rand`: A random number generator.
158#[allow(clippy::too_many_arguments)]
159pub fn wifi_sys_handler<'a, R: RngCore, T>(
160    comm_policy: &'a dyn CommPolicy,
161    gen_diag: &'a dyn GenDiag,
162    netif_diag: &'a dyn NetifDiag,
163    wifi_diag: &'a dyn WifiDiag,
164    time_sync: &'a dyn TimeSync,
165    sw_diag: &'a dyn SwDiag,
166    net_ctl: T,
167    mut rand: R,
168) -> WifiSysHandler<'a, T>
169where
170    T: NetCtl + NetCtlStatus,
171{
172    sys_handler(
173        comm_policy,
174        gen_diag,
175        netif_diag,
176        time_sync,
177        sw_diag,
178        net_ctl,
179        WifiDiagHandler::CLUSTER.id,
180        WifiDiagHandler::new(Dataver::new_rand(&mut rand), wifi_diag).adapt(),
181        rand,
182    )
183}
184
185/// Return a system handler for the root endpoint (Endpoint 0).
186/// Use this handler for devices that use Thread as the Matter Operational Network.
187///
188/// # Arguments:
189/// - `comm_policy`: The `CommPolicy` implementation.
190/// - `gen_diag`: The `GenDiag` implementation.
191/// - `netif_diag`: The `NetifDiag` implementation.
192/// - `thread_diag`: The `ThreadDiag` implementation.
193/// - `time_sync`: The `TimeSync` implementation (pass `&()` for the no-op default).
194/// - `sw_diag`: The `SwDiag` implementation (pass `&()` for the no-op default).
195/// - `net_ctl`: The `NetCtl` implementation.
196/// - `rand`: A random number generator.
197#[allow(clippy::too_many_arguments)]
198pub fn thread_sys_handler<'a, R: RngCore, T>(
199    comm_policy: &'a dyn CommPolicy,
200    gen_diag: &'a dyn GenDiag,
201    netif_diag: &'a dyn NetifDiag,
202    thread_diag: &'a dyn ThreadDiag,
203    time_sync: &'a dyn TimeSync,
204    sw_diag: &'a dyn SwDiag,
205    net_ctl: T,
206    mut rand: R,
207) -> ThreadSysHandler<'a, T>
208where
209    T: NetCtl + NetCtlStatus,
210{
211    sys_handler(
212        comm_policy,
213        gen_diag,
214        netif_diag,
215        time_sync,
216        sw_diag,
217        net_ctl,
218        ThreadDiagHandler::CLUSTER.id,
219        ThreadDiagHandler::new(Dataver::new_rand(&mut rand), thread_diag).adapt(),
220        rand,
221    )
222}
223
224/// Return a system handler for the root endpoint (Endpoint 0).
225/// Note that this handler does not include the Network Diagnostic handler, which is dependent on
226/// the network type and thus is not included in this function.
227///
228/// Use `eth_sys_handler()`, `wifi_sys_handler()` or `thread_sys_handler()` instead to get the appropriate
229/// Network Diagnostic handler included in the handler.
230///
231/// # Arguments:
232/// - `comm_policy`: The `CommPolicy` implementation.
233/// - `gen_diag`: The `GenDiag` implementation.
234/// - `netif_diag`: The `NetifDiag` implementation.
235/// - `networks`: The `Networks` implementation.
236/// - `net_ctl`: The `NetCtl` implementation.
237/// - `rand`: A random number generator.
238#[allow(clippy::too_many_arguments)]
239fn sys_handler<'a, R: RngCore, T, N>(
240    comm_policy: &'a dyn CommPolicy,
241    gen_diag: &'a dyn GenDiag,
242    netif_diag: &'a dyn NetifDiag,
243    time_sync: &'a dyn TimeSync,
244    sw_diag: &'a dyn SwDiag,
245    net_ctl: T,
246    netw_diag_cluster_id: ClusterId,
247    netw_diag: N,
248    mut rand: R,
249) -> SysHandler<'a, T, N>
250where
251    T: NetCtl + NetCtlStatus,
252{
253    ChainedHandler::new(
254        EpClMatcher::new(
255            Some(ROOT_ENDPOINT_ID),
256            Some(NetCommHandler::<T>::CLUSTER.id),
257        ),
258        NetCommHandler::new(Dataver::new_rand(&mut rand), net_ctl).adapt(),
259        Async(
260            ChainedHandler::new(
261                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(netw_diag_cluster_id)),
262                netw_diag,
263                EmptyHandler,
264            )
265            .chain(
266                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(GenDiagHandler::CLUSTER.id)),
267                GenDiagHandler::new(Dataver::new_rand(&mut rand), gen_diag, netif_diag).adapt(),
268            )
269            .chain(
270                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(TimeSyncHandler::CLUSTER.id)),
271                TimeSyncHandler::new(Dataver::new_rand(&mut rand), time_sync).adapt(),
272            )
273            .chain(
274                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(SwDiagHandler::CLUSTER.id)),
275                SwDiagHandler::new(Dataver::new_rand(&mut rand), sw_diag).adapt(),
276            )
277            .chain(
278                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(GrpKeyMgmtHandler::CLUSTER.id)),
279                GrpKeyMgmtHandler::new(Dataver::new_rand(&mut rand)).adapt(),
280            )
281            .chain(
282                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(AclHandler::CLUSTER.id)),
283                AclHandler::new(Dataver::new_rand(&mut rand)).adapt(),
284            )
285            .chain(
286                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(NocHandler::CLUSTER.id)),
287                NocHandler::new(Dataver::new_rand(&mut rand)).adapt(),
288            )
289            .chain(
290                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(AdminCommHandler::CLUSTER.id)),
291                AdminCommHandler::new(Dataver::new_rand(&mut rand)).adapt(),
292            )
293            .chain(
294                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(GenCommHandler::CLUSTER.id)),
295                GenCommHandler::new(Dataver::new_rand(&mut rand), comm_policy).adapt(),
296            )
297            .chain(
298                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(BasicInfoHandler::CLUSTER.id)),
299                BasicInfoHandler::new(Dataver::new_rand(&mut rand)).adapt(),
300            )
301            .chain(
302                EpClMatcher::new(Some(ROOT_ENDPOINT_ID), Some(DescHandler::CLUSTER.id)),
303                DescHandler::new(Dataver::new_rand(&mut rand)).adapt(),
304            ),
305        ),
306    )
307}
308
309// ---- Sys-handler builders ----------------------------------------------------
310//
311// Thin builders over the `eth_sys_handler` / `wifi_sys_handler` /
312// `thread_sys_handler` free fns: each cluster-data hook is a setter, unset
313// ones fall back to the canonical no-op default (`&true` for `CommPolicy`,
314// `&()` for every other trait — `bool: CommPolicy` and `(): GenDiag` /
315// `NetifDiag` / `TimeSync` / `SwDiag` are already impls in the crate). New
316// hooks can be added later by extending one struct + adding a setter, with
317// no churn on existing call sites.
318
319/// Builder for an Ethernet root-endpoint system handler.
320///
321/// Unset hooks fall back to no-op defaults: `&true` for `CommPolicy`
322/// (commissioning open / allowed) and `&()` for every other trait
323/// (reports nothing / no-op).
324///
325/// ```ignore
326/// let h = EthSysHandlerBuilder::new()
327///     .gen_diag(&my_gen_diag)
328///     .netif_diag(&SysNetifs)
329///     .build(rand);
330/// ```
331pub struct EthSysHandlerBuilder<'a> {
332    comm_policy: &'a dyn CommPolicy,
333    gen_diag: &'a dyn GenDiag,
334    netif_diag: &'a dyn NetifDiag,
335    time_sync: &'a dyn TimeSync,
336    sw_diag: &'a dyn SwDiag,
337}
338
339impl Default for EthSysHandlerBuilder<'_> {
340    fn default() -> Self {
341        Self::new()
342    }
343}
344
345impl<'a> EthSysHandlerBuilder<'a> {
346    /// Create a builder. Every hook defaults to a no-op provider.
347    pub const fn new() -> Self {
348        Self {
349            comm_policy: &true,
350            gen_diag: &(),
351            netif_diag: &(),
352            time_sync: &(),
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 `TimeSync` hook (feature-gated members of the Time
376    /// Synchronization cluster — `TIME_ZONE` / `NTP_CLIENT` /
377    /// `NTP_SERVER` / `TIME_SYNC_CLIENT`). The mandatory members
378    /// (`UTCTime`, `Granularity`, `TimeSource`, `SetUTCTime`) are
379    /// always served from the Matter-wide
380    /// [Last-Known-Good UTC Time](crate::Matter::last_known_utc_time)
381    /// state and don't need a provider.
382    pub const fn time_sync(mut self, time_sync: &'a dyn TimeSync) -> Self {
383        self.time_sync = time_sync;
384        self
385    }
386
387    /// Set the `SwDiag` hook (Software Diagnostics data provider).
388    pub const fn sw_diag(mut self, sw_diag: &'a dyn SwDiag) -> Self {
389        self.sw_diag = sw_diag;
390        self
391    }
392
393    /// Build the Ethernet system handler.
394    pub fn build<R: RngCore>(self, rand: R) -> EthSysHandler<'a> {
395        eth_sys_handler(
396            self.comm_policy,
397            self.gen_diag,
398            self.netif_diag,
399            self.time_sync,
400            self.sw_diag,
401            rand,
402        )
403    }
404}
405
406/// Builder for a Wi-Fi root-endpoint system handler.
407///
408/// `net_ctl` and `wifi_diag` are required (no sensible default) and supplied
409/// to [`Self::new`]; everything else falls back to no-op defaults.
410pub struct WifiSysHandlerBuilder<'a, T> {
411    comm_policy: &'a dyn CommPolicy,
412    gen_diag: &'a dyn GenDiag,
413    netif_diag: &'a dyn NetifDiag,
414    wifi_diag: &'a dyn WifiDiag,
415    time_sync: &'a dyn TimeSync,
416    sw_diag: &'a dyn SwDiag,
417    net_ctl: T,
418}
419
420impl<'a, T> WifiSysHandlerBuilder<'a, T>
421where
422    T: NetCtl + NetCtlStatus,
423{
424    /// Create a builder. `net_ctl` and `wifi_diag` are required;
425    /// every other hook defaults to a no-op provider.
426    pub const fn new(net_ctl: T, wifi_diag: &'a dyn WifiDiag) -> Self {
427        Self {
428            comm_policy: &true,
429            gen_diag: &(),
430            netif_diag: &(),
431            wifi_diag,
432            time_sync: &(),
433            sw_diag: &(),
434            net_ctl,
435        }
436    }
437
438    /// Set the `CommPolicy` hook.
439    pub const fn comm_policy(mut self, comm_policy: &'a dyn CommPolicy) -> Self {
440        self.comm_policy = comm_policy;
441        self
442    }
443
444    /// Set the `GenDiag` hook.
445    pub const fn gen_diag(mut self, gen_diag: &'a dyn GenDiag) -> Self {
446        self.gen_diag = gen_diag;
447        self
448    }
449
450    /// Set the `NetifDiag` hook.
451    pub const fn netif_diag(mut self, netif_diag: &'a dyn NetifDiag) -> Self {
452        self.netif_diag = netif_diag;
453        self
454    }
455
456    /// Set the `TimeSync` hook (feature-gated members only — see
457    /// [`EthSysHandlerBuilder::time_sync`]).
458    pub const fn time_sync(mut self, time_sync: &'a dyn TimeSync) -> Self {
459        self.time_sync = time_sync;
460        self
461    }
462
463    /// Set the `SwDiag` hook.
464    pub const fn sw_diag(mut self, sw_diag: &'a dyn SwDiag) -> Self {
465        self.sw_diag = sw_diag;
466        self
467    }
468
469    /// Build the Wi-Fi system handler.
470    pub fn build<R: RngCore>(self, rand: R) -> WifiSysHandler<'a, T> {
471        wifi_sys_handler(
472            self.comm_policy,
473            self.gen_diag,
474            self.netif_diag,
475            self.wifi_diag,
476            self.time_sync,
477            self.sw_diag,
478            self.net_ctl,
479            rand,
480        )
481    }
482}
483
484/// Builder for a Thread root-endpoint system handler.
485///
486/// `net_ctl` and `thread_diag` are required (no sensible default) and supplied
487/// to [`Self::new`]; everything else falls back to no-op defaults.
488pub struct ThreadSysHandlerBuilder<'a, T> {
489    comm_policy: &'a dyn CommPolicy,
490    gen_diag: &'a dyn GenDiag,
491    netif_diag: &'a dyn NetifDiag,
492    thread_diag: &'a dyn ThreadDiag,
493    time_sync: &'a dyn TimeSync,
494    sw_diag: &'a dyn SwDiag,
495    net_ctl: T,
496}
497
498impl<'a, T> ThreadSysHandlerBuilder<'a, T>
499where
500    T: NetCtl + NetCtlStatus,
501{
502    /// Create a builder. `net_ctl` and `thread_diag` are required;
503    /// every other hook defaults to a no-op provider.
504    pub const fn new(net_ctl: T, thread_diag: &'a dyn ThreadDiag) -> Self {
505        Self {
506            comm_policy: &true,
507            gen_diag: &(),
508            netif_diag: &(),
509            thread_diag,
510            time_sync: &(),
511            sw_diag: &(),
512            net_ctl,
513        }
514    }
515
516    /// Set the `CommPolicy` hook.
517    pub const fn comm_policy(mut self, comm_policy: &'a dyn CommPolicy) -> Self {
518        self.comm_policy = comm_policy;
519        self
520    }
521
522    /// Set the `GenDiag` hook.
523    pub const fn gen_diag(mut self, gen_diag: &'a dyn GenDiag) -> Self {
524        self.gen_diag = gen_diag;
525        self
526    }
527
528    /// Set the `NetifDiag` hook.
529    pub const fn netif_diag(mut self, netif_diag: &'a dyn NetifDiag) -> Self {
530        self.netif_diag = netif_diag;
531        self
532    }
533
534    /// Set the `TimeSync` hook (feature-gated members only — see
535    /// [`EthSysHandlerBuilder::time_sync`]).
536    pub const fn time_sync(mut self, time_sync: &'a dyn TimeSync) -> Self {
537        self.time_sync = time_sync;
538        self
539    }
540
541    /// Set the `SwDiag` hook.
542    pub const fn sw_diag(mut self, sw_diag: &'a dyn SwDiag) -> Self {
543        self.sw_diag = sw_diag;
544        self
545    }
546
547    /// Build the Thread system handler.
548    pub fn build<R: RngCore>(self, rand: R) -> ThreadSysHandler<'a, T> {
549        thread_sys_handler(
550            self.comm_policy,
551            self.gen_diag,
552            self.netif_diag,
553            self.thread_diag,
554            self.time_sync,
555            self.sw_diag,
556            self.net_ctl,
557            rand,
558        )
559    }
560}