Skip to main content

rs_matter_stack/
lib.rs

1#![no_std]
2#![allow(async_fn_in_trait)]
3#![allow(unknown_lints)]
4#![allow(renamed_and_removed_lints)]
5#![allow(unexpected_cfgs)]
6#![allow(clippy::declare_interior_mutable_const)]
7#![allow(clippy::uninlined_format_args)]
8#![warn(clippy::large_futures)]
9#![warn(clippy::large_stack_frames)]
10#![warn(clippy::large_types_passed_by_value)]
11
12use core::fmt::Debug;
13use core::future::Future;
14use core::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6};
15use core::pin::pin;
16
17use cfg_if::cfg_if;
18
19use edge_nal::{UdpBind, UdpSplitMulticast};
20
21use embassy_futures::select::{select, select_slice};
22use embassy_time::Duration;
23
24use rs_matter::crypto::Crypto;
25use rs_matter::dm::clusters::basic_info::BasicInfoConfig;
26use rs_matter::dm::clusters::decl::basic_information::StartUp;
27use rs_matter::dm::clusters::dev_att::DeviceAttestation;
28use rs_matter::dm::clusters::gen_diag::NetifDiag;
29use rs_matter::dm::clusters::net_comm::{NetCtl, Networks};
30use rs_matter::dm::clusters::wifi_diag::WirelessDiag;
31use rs_matter::dm::networks::NetChangeNotif;
32use rs_matter::dm::{AttrChangeNotifier, AttrId, ClusterId, DataModel, EndptId};
33use rs_matter::error::{Error, ErrorCode};
34use rs_matter::im::{InteractionModel, InteractionModelState};
35use rs_matter::pairing::qr::QrTextType;
36use rs_matter::persist::{KvBlobStore, KvBlobStoreAccess};
37use rs_matter::respond::{DefaultResponder, ExchangeHandler, Responder};
38use rs_matter::sc::pase::MAX_COMM_WINDOW_TIMEOUT_SECS;
39use rs_matter::transport::exchange::MatterBuffers;
40use rs_matter::transport::network::{
41    Address, ChainedNetwork, NetworkMulticast, NetworkReceive, NetworkSend, NoNetwork,
42};
43use rs_matter::utils::init::{init, Init};
44use rs_matter::utils::select::Coalesce;
45use rs_matter::utils::sync::{DynBase, IfMutex};
46use rs_matter::{BasicCommData, Matter, MATTER_PORT};
47
48use crate::bump::Bump;
49use crate::mdns::Mdns;
50use crate::nal::NetStack;
51use crate::network::Network;
52
53#[cfg(feature = "std")]
54#[allow(unused_imports)]
55#[macro_use]
56extern crate std;
57
58#[allow(unused_imports)]
59#[macro_use]
60extern crate alloc;
61
62// This mod MUST go first, so that the others see its macros.
63pub(crate) mod fmt;
64
65pub mod ble;
66pub mod bump;
67pub mod eth;
68pub mod matter;
69pub mod mdns;
70pub mod nal;
71pub mod network;
72pub mod rand;
73pub mod udp;
74pub mod utils;
75pub mod wireless;
76
77mod private {
78    /// A marker super-trait for sealed traits
79    pub trait Sealed {}
80
81    impl Sealed for () {}
82}
83
84cfg_if! {
85    if #[cfg(feature = "max-subscriptions-32")] {
86        /// Max number of subscriptions
87        const MAX_SUBSCRIPTIONS: usize = 32;
88    } else if #[cfg(feature = "max-subscriptions-16")] {
89        /// Max number of subscriptions
90        const MAX_SUBSCRIPTIONS: usize = 16;
91    } else if #[cfg(feature = "max-subscriptions-8")] {
92        /// Max number of subscriptions
93        const MAX_SUBSCRIPTIONS: usize = 8;
94    } else if #[cfg(feature = "max-subscriptions-7")] {
95        /// Max number of subscriptions
96        const MAX_SUBSCRIPTIONS: usize = 7;
97    } else if #[cfg(feature = "max-subscriptions-6")] {
98        /// Max number of subscriptions
99        const MAX_SUBSCRIPTIONS: usize = 6;
100    } else if #[cfg(feature = "max-subscriptions-5")] {
101        /// Max number of subscriptions
102        const MAX_SUBSCRIPTIONS: usize = 5;
103    } else if #[cfg(feature = "max-subscriptions-4")] {
104        /// Max number of subscriptions
105        const MAX_SUBSCRIPTIONS: usize = 4;
106    } else if #[cfg(feature = "max-subscriptions-3")] {
107        /// Max number of subscriptions
108        const MAX_SUBSCRIPTIONS: usize = 3;
109    } else if #[cfg(feature = "max-subscriptions-2")] {
110        /// Max number of subscriptions
111        const MAX_SUBSCRIPTIONS: usize = 2;
112    } else if #[cfg(feature = "max-subscriptions-1")] {
113        /// Max number of subscriptions
114        const MAX_SUBSCRIPTIONS: usize = 1;
115    } else {
116        /// Max number of subscriptions
117        const MAX_SUBSCRIPTIONS: usize = 3;
118    }
119}
120
121cfg_if! {
122    if #[cfg(feature = "events-ringbuf-size-0")] {
123        /// Events ringbuf size
124        const EVENTS_RINGBUF_SIZE: usize = 0;
125    } else if #[cfg(feature = "events-ringbuf-size-64")] {
126        /// Events ringbuf size
127        const EVENTS_RINGBUF_SIZE: usize = 64;
128    } else if #[cfg(feature = "events-ringbuf-size-128")] {
129        /// Events ringbuf size
130        const EVENTS_RINGBUF_SIZE: usize = 128;
131    } else if #[cfg(feature = "events-ringbuf-size-256")] {
132        /// Events ringbuf size
133        const EVENTS_RINGBUF_SIZE: usize = 256;
134    } else if #[cfg(feature = "events-ringbuf-size-512")] {
135        /// Events ringbuf size
136        const EVENTS_RINGBUF_SIZE: usize = 512;
137    } else if #[cfg(feature = "events-ringbuf-size-1024")] {
138        /// Events ringbuf size
139        const EVENTS_RINGBUF_SIZE: usize = 1024;
140    } else if #[cfg(feature = "events-ringbuf-size-2048")] {
141        /// Events ringbuf size
142        const EVENTS_RINGBUF_SIZE: usize = 2048;
143    } else {
144        /// Events ringbuf size
145        const EVENTS_RINGBUF_SIZE: usize = 0;
146    }
147}
148
149cfg_if! {
150    if #[cfg(feature = "max-im-buffers-64")] {
151        /// Max number of IM buffers
152        const MAX_IM_BUFFERS: usize = 64;
153    } else if #[cfg(feature = "max-im-buffers-32")] {
154        /// Max number of IM buffers
155        const MAX_IM_BUFFERS: usize = 32;
156    } else if #[cfg(feature = "max-im-buffers-16")] {
157        /// Max number of IM buffers
158        const MAX_IM_BUFFERS: usize = 16;
159    } else if #[cfg(feature = "max-im-buffers-10")] {
160        /// Max number of IM buffers
161        const MAX_IM_BUFFERS: usize = 10;
162    } else if #[cfg(feature = "max-im-buffers-9")] {
163        /// Max number of IM buffers
164        const MAX_IM_BUFFERS: usize = 9;
165    } else if #[cfg(feature = "max-im-buffers-8")] {
166        /// Max number of IM buffers
167        const MAX_IM_BUFFERS: usize = 8;
168    } else if #[cfg(feature = "max-im-buffers-7")] {
169        /// Max number of IM buffers
170        const MAX_IM_BUFFERS: usize = 7;
171    } else if #[cfg(feature = "max-im-buffers-6")] {
172        /// Max number of IM buffers
173        const MAX_IM_BUFFERS: usize = 6;
174    } else if #[cfg(feature = "max-im-buffers-5")] {
175        /// Max number of IM buffers
176        const MAX_IM_BUFFERS: usize = 5;
177    } else if #[cfg(feature = "max-im-buffers-4")] {
178        /// Max number of IM buffers
179        const MAX_IM_BUFFERS: usize = 4;
180    } else {
181        /// Max number of IM buffers
182        const MAX_IM_BUFFERS: usize = 10;
183    }
184}
185
186cfg_if! {
187    if #[cfg(feature = "max-responders-32")] {
188        /// Max number of concurrent responders
189        const MAX_RESPONDERS: usize = 32;
190    } else if #[cfg(feature = "max-responders-16")] {
191        /// Max number of concurrent responders
192        const MAX_RESPONDERS: usize = 16;
193    } else if #[cfg(feature = "max-responders-8")] {
194        /// Max number of concurrent responders
195        const MAX_RESPONDERS: usize = 8;
196    } else if #[cfg(feature = "max-responders-7")] {
197        /// Max number of concurrent responders
198        const MAX_RESPONDERS: usize = 7;
199    } else if #[cfg(feature = "max-responders-6")] {
200        /// Max number of concurrent responders
201        const MAX_RESPONDERS: usize = 6;
202    } else if #[cfg(feature = "max-responders-5")] {
203        /// Max number of concurrent responders
204        const MAX_RESPONDERS: usize = 5;
205    } else if #[cfg(feature = "max-responders-4")] {
206        /// Max number of concurrent responders
207        const MAX_RESPONDERS: usize = 4;
208    } else if #[cfg(feature = "max-responders-3")] {
209        /// Max number of concurrent responders
210        const MAX_RESPONDERS: usize = 3;
211    } else if #[cfg(feature = "max-responders-2")] {
212        /// Max number of concurrent responders
213        const MAX_RESPONDERS: usize = 2;
214    } else if #[cfg(feature = "max-responders-1")] {
215        /// Max number of concurrent responders
216        const MAX_RESPONDERS: usize = 1;
217    } else {
218        /// Max number of concurrent responders
219        const MAX_RESPONDERS: usize = 4;
220    }
221}
222
223const MAX_BUSY_RESPONDERS: usize = 2;
224
225pub type MatterStackInteractionModel<'a, C, H, K, RN, NC> = InteractionModel<
226    'a,
227    C,
228    MatterBuffers<MAX_IM_BUFFERS>,
229    H,
230    K,
231    RN,
232    NC,
233    MAX_SUBSCRIPTIONS,
234    EVENTS_RINGBUF_SIZE,
235>;
236
237/// The `InteractionModelState` specialization owned by `MatterStack`.
238///
239/// It owns the subscriptions table, the events queue and the `rs-matter`
240/// networks store as a single unit. The KV scratch buffer now lives in `Matter`.
241pub type MatterStackInteractionModelState<RN> =
242    InteractionModelState<RN, MAX_SUBSCRIPTIONS, EVENTS_RINGBUF_SIZE>;
243
244/// The `MatterStack` struct is the main entry point for the Matter stack.
245///
246/// It wraps the actual `rs-matter` Matter instance and provides a simplified API for running the stack.
247pub struct MatterStack<'a, const B: usize, N>
248where
249    N: Network,
250{
251    matter: Matter<'a>,
252    buffers: MatterBuffers<MAX_IM_BUFFERS>,
253    /// The interaction-model state: subscriptions table, events queue, the
254    /// `rs-matter` networks store, and the KV scratch buffer, owned as one unit.
255    state: MatterStackInteractionModelState<N::Networks>,
256    bump: Bump<B>,
257    run_lock: IfMutex<()>,
258    #[allow(unused)]
259    network: N,
260    //netif_conf: Signal<Option<NetifConf>>,
261}
262
263impl<'a, const B: usize, N> MatterStack<'a, B, N>
264where
265    N: Network,
266{
267    /// Create a new `MatterStack` instance.
268    #[allow(clippy::large_stack_frames)]
269    #[inline(always)]
270    pub const fn new(
271        dev_det: &'a BasicInfoConfig,
272        dev_comm: BasicCommData,
273        dev_att: &'a dyn DeviceAttestation,
274    ) -> Self {
275        Self {
276            matter: Matter::new(dev_det, dev_comm, dev_att, MATTER_PORT),
277            buffers: MatterBuffers::new(),
278            state: MatterStackInteractionModelState::new(N::NETWORKS),
279            bump: Bump::new(),
280            run_lock: IfMutex::new(()),
281            network: N::INIT,
282            //netif_conf: Signal::new(None),
283        }
284    }
285
286    #[allow(clippy::large_stack_frames)]
287    pub fn init(
288        dev_det: &'a BasicInfoConfig,
289        dev_comm: BasicCommData,
290        dev_att: &'a dyn DeviceAttestation,
291    ) -> impl Init<Self> {
292        init!(Self {
293            matter <- Matter::init(
294                dev_det,
295                dev_comm,
296                dev_att,
297                MATTER_PORT,
298            ),
299            buffers <- MatterBuffers::init(),
300            state <- MatterStackInteractionModelState::init(N::init_networks()),
301            bump <- Bump::init(),
302            run_lock <- IfMutex::init(()),
303            network <- N::init(),
304            //netif_conf: Signal::new(None),
305        })
306    }
307
308    /// A utility method to replace the initial Device Attestation Data Fetcher with another one.
309    ///
310    /// Reasoning and use-cases explained in the documentation of `replace_mdns`.
311    pub fn replace_dev_att(&mut self, dev_att: &'a dyn DeviceAttestation) {
312        self.matter.replace_dev_att(dev_att);
313    }
314
315    /// Get a reference to the `Matter` instance.
316    pub const fn matter(&self) -> &Matter<'a> {
317        &self.matter
318    }
319
320    /// Get a reference to the `Network` instance.
321    /// Useful when the user instantiates `MatterStack` with a custom network type.
322    pub const fn network(&self) -> &N {
323        &self.network
324    }
325
326    /// Create a new shared `KvBlobStore` instance, which is used to read and write blobs from the storage.
327    ///
328    /// The user needs to provide a `KvBlobStore` implementation, which is used to actually read and write the blobs from the storage.
329    ///
330    /// # Arguments
331    /// - `store` - the raw [`KvBlobStore`] implementation to wrap
332    pub fn kv<'s, S: KvBlobStore + 's>(&'s self, store: S) -> impl KvBlobStoreAccess + 's {
333        self.matter().kv(store)
334    }
335
336    // /// User code hook to get the state of the netif passed to the
337    // /// `run_with_netif` method.
338    // ///
339    // /// Useful when user code needs to bring up/down its own IP services depending on
340    // /// when the netif controlled by Matter goes up, down or changes its IP configuration.
341    // pub async fn get_netif_conf(&self) -> Option<NetifConf> {
342    //     self.netif_conf
343    //         .wait(|netif_conf| Some(netif_conf.clone()))
344    //         .await
345    // }
346
347    // fn update_netif_conf(&self, netif_conf: Option<&NetifConf>) -> bool {
348    //     self.netif_conf.modify(|global_ip_info| {
349    //         if global_ip_info.as_ref() != netif_conf {
350    //             *global_ip_info = netif_conf.cloned();
351    //             (true, true)
352    //         } else {
353    //             (false, false)
354    //         }
355    //     })
356    // }
357
358    // /// User code hook to detect changes to the IP state of the netif passed to the
359    // /// `run_with_netif` method.
360    // ///
361    // /// Useful when user code needs to bring up/down its own IP services depending on
362    // /// when the netif controlled by Matter goes up, down or changes its IP configuration.
363    // pub async fn wait_netif_changed(
364    //     &self,
365    //     prev_netif_info: Option<&NetifConf>,
366    // ) -> Option<NetifConf> {
367    //     self.netif_conf
368    //         .wait(|netif_info| (netif_info.as_ref() != prev_netif_info).then(|| netif_info.clone()))
369    //         .await
370    // }
371
372    /// Return information whether the Matter instance is already commissioned.
373    pub fn is_commissioned(&self) -> bool {
374        self.matter().is_commissioned()
375    }
376
377    /// Open the basic communication window, which allows commissioning tools to discover and commission the device.
378    ///
379    /// # Arguments
380    /// - `crypto` - a user-provided crypto implementation, necessary for the secure sessions establishment that happens in the basic communication window
381    /// - `notify` - a user-provided `AttrChangeNotifier`; 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
382    pub fn open_basic_comm_window<C>(
383        &self,
384        crypto: C,
385        notify: &dyn AttrChangeNotifier,
386    ) -> Result<(), Error>
387    where
388        C: Crypto,
389    {
390        self.matter()
391            .open_basic_comm_window(MAX_COMM_WINDOW_TIMEOUT_SECS, crypto, notify)?;
392
393        self.matter()
394            .print_standard_qr_text(self.network.discovery_capabilities())?;
395
396        self.matter()
397            .print_standard_qr_code(QrTextType::Unicode, self.network.discovery_capabilities())
398    }
399
400    /// This method is a specialization of `run_transport_net` over the UDP transport (both IPv4 and IPv6).
401    /// It calls `run_transport_net`.
402    ///
403    /// #Arguments
404    /// - `crypto` - a user-provided crypto implementation, necessary for the secure sessions establishment that happens in the operational network
405    /// - `net_stack` - a user-provided network stack that implements `UdpBind`, `UdpConnect`, `TcpBind`, `TcpConnect`, and `Dns`
406    /// - `netif` - a user-provided `Netif` implementation
407    /// - `until` - the method will return once this future becomes ready
408    /// - `comm` - a tuple of additional and optional `NetworkReceive` and `NetworkSend` transport implementations
409    ///   (useful when a second transport needs to run in parallel with the operational Matter transport,
410    ///   i.e. when using concurrent commissisoning)
411    async fn run_oper_net<C, U, X, R, S>(
412        &self,
413        crypto: C,
414        net_stack: U,
415        net_interface: u32,
416        until: X,
417        mut comm: Option<(R, S)>,
418    ) -> Result<(), Error>
419    where
420        C: Crypto,
421        U: NetStack,
422        X: Future<Output = Result<(), Error>>,
423        R: NetworkReceive,
424        S: NetworkSend,
425    {
426        fn map_err<E: Debug>(e: E) -> Error {
427            warn!("Matter UDP network error: {:?}", debug2format!(e));
428            ErrorCode::StdIoError.into() // TODO
429        }
430
431        let udp_bind = unwrap!(net_stack.udp_bind());
432
433        let mut socket = udp_bind
434            .bind(SocketAddr::V6(SocketAddrV6::new(
435                Ipv6Addr::UNSPECIFIED,
436                MATTER_PORT,
437                0,
438                net_interface,
439            )))
440            .await
441            .map_err(map_err)?;
442
443        let (recv, send, m4, m6) = socket.split_multicast();
444
445        let multicast = udp::Udp(udp::Multicast::new(
446            m4,
447            // rs-matter does not really use IPv4 multicast for Groups, so we can just use `Ipv4Addr::UNSPECIFIED` here.
448            Ipv4Addr::UNSPECIFIED,
449            m6,
450            net_interface,
451        ));
452
453        let mut until_task = pin!(until);
454
455        if let Some((comm_recv, comm_send)) = comm.as_mut() {
456            info!("Running operational and commissioning networks");
457
458            let mut netw_task = pin!(self.run_transport_net(
459                &crypto,
460                ChainedNetwork::new(Address::is_udp, udp::Udp(send), comm_send),
461                ChainedNetwork::new(Address::is_udp, udp::Udp(recv), comm_recv),
462                ChainedNetwork::new(Address::is_udp, multicast, NoNetwork),
463            ));
464
465            select(&mut netw_task, &mut until_task).coalesce().await
466        } else {
467            info!("Running operational network");
468
469            let mut netw_task =
470                pin!(self.run_transport_net(&crypto, udp::Udp(send), udp::Udp(recv), multicast,));
471
472            select(&mut netw_task, &mut until_task).coalesce().await
473        }
474    }
475
476    /// This method runs the mDNS service.
477    ///
478    /// The netif instance is necessary, so that the loop can monitor the network and bring up/down
479    /// the mDNS service when the netif goes up/down or changes its IP addresses.
480    ///
481    /// This is necessary because mDNS needs to know the current IP addresses and
482    /// also needs to stop when the netif goes down.
483    ///
484    /// # Arguments
485    /// - `crypto` - a user-provided crypto implementation
486    /// - `net_stack` - a user-provided network stack that implements `UdpBind`, `UdpConnect`, `TcpBind`, `TcpConnect`, and `Dns`
487    /// - `netif` - a user-provided `Netif` implementation
488    /// - `mdns` - a user-provided mDNS implementation
489    async fn run_oper_netif_mdns<C, U, I, M>(
490        &self,
491        crypto: C,
492        net_stack: U,
493        netif: I,
494        mut mdns: M,
495    ) -> Result<(), Error>
496    where
497        C: Crypto,
498        U: NetStack,
499        I: NetifDiag + NetChangeNotif,
500        M: Mdns,
501    {
502        #[derive(Clone, Debug, Eq, PartialEq, Hash)]
503        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
504        struct NetifState {
505            ipv6: Ipv6Addr,
506            ipv4: Ipv4Addr,
507            mac: [u8; 8],
508            operational: bool,
509            netif_index: u32,
510        }
511
512        impl NetifState {
513            pub const fn new() -> Self {
514                Self {
515                    ipv6: Ipv6Addr::UNSPECIFIED,
516                    ipv4: Ipv4Addr::UNSPECIFIED,
517                    mac: [0; 8],
518                    operational: false,
519                    netif_index: 0,
520                }
521            }
522        }
523
524        fn load_netif_state<I>(net_diag: I, state: &mut NetifState) -> Result<(), Error>
525        where
526            I: NetifDiag,
527        {
528            state.operational = false;
529            state.ipv6 = Ipv6Addr::UNSPECIFIED;
530            state.ipv4 = Ipv4Addr::UNSPECIFIED;
531            state.mac = [0; 8];
532
533            net_diag.netifs(&mut |ni| {
534                if ni.operational && !ni.ipv6_addrs.is_empty() {
535                    state.operational = true;
536                    state.ipv6 = ni.ipv6_addrs[0];
537                    state.ipv4 = ni
538                        .ipv4_addrs
539                        .first()
540                        .copied()
541                        .unwrap_or(Ipv4Addr::UNSPECIFIED);
542                    state.mac = *ni.hw_addr;
543                    state.netif_index = ni.netif_index;
544                }
545
546                Ok(())
547            })
548        }
549
550        async fn wait_changed<I>(
551            net_diag: I,
552            cur_state: &NetifState,
553            new_state: &mut NetifState,
554        ) -> Result<(), Error>
555        where
556            I: NetifDiag + NetChangeNotif,
557        {
558            loop {
559                load_netif_state(&net_diag, new_state)?;
560
561                if &*new_state != cur_state {
562                    info!(
563                        "Netif change detected.\n    Old: {:?}\n    New: {:?}",
564                        cur_state, new_state
565                    );
566                    break Ok(());
567                }
568
569                trace!("No change");
570                net_diag.wait_changed().await;
571            }
572        }
573
574        // let _guard = scopeguard::guard((), |_| {
575        //     self.update_netif_conf(None);
576        // });
577
578        let mut new_state = NetifState::new();
579        load_netif_state(&netif, &mut new_state)?;
580
581        loop {
582            let cur_state = new_state.clone();
583
584            let mut netif_changed_task = pin!(wait_changed(&netif, &cur_state, &mut new_state));
585
586            let mut mdns_task = pin!(async {
587                if cur_state.operational {
588                    info!("Netif up: {:?}", cur_state);
589
590                    let udp_bind = unwrap!(net_stack.udp_bind());
591
592                    info!("Running mDNS");
593
594                    loop {
595                        let _result = mdns
596                            .run(
597                                self.matter(),
598                                &crypto,
599                                &udp_bind,
600                                &cur_state.mac,
601                                cur_state.ipv4,
602                                cur_state.ipv6,
603                                cur_state.netif_index,
604                            )
605                            .await;
606
607                        warn!("mDNS failed with {:?}, retrying in 5s...", _result);
608                        embassy_time::Timer::after(Duration::from_secs(5)).await;
609                    }
610                } else {
611                    info!("Netif down");
612                    core::future::pending::<()>().await;
613                }
614
615                Ok(())
616            });
617
618            select(&mut netif_changed_task, &mut mdns_task)
619                .coalesce()
620                .await?;
621        }
622    }
623
624    #[inline(always)]
625    fn im<C, H, K, NC>(
626        &self,
627        crypto: C,
628        handler: H,
629        kv: K,
630        net_ctl: NC,
631    ) -> MatterStackInteractionModel<'_, C, H, K, N::Networks, NC>
632    where
633        C: Crypto,
634        H: DataModel,
635        K: KvBlobStoreAccess,
636        NC: NetCtl + WirelessDiag + NetChangeNotif,
637    {
638        MatterStackInteractionModel::new_with_net_ctl(
639            self.matter(),
640            crypto,
641            &self.buffers,
642            handler,
643            kv,
644            net_ctl,
645            &self.state,
646        )
647    }
648
649    async fn run_im<C, H, K, RN, NC>(
650        &self,
651        im: &MatterStackInteractionModel<'_, C, H, K, RN, NC>,
652    ) -> Result<(), Error>
653    where
654        C: Crypto,
655        H: DataModel,
656        K: KvBlobStoreAccess,
657        RN: Networks,
658        NC: NetCtl + WirelessDiag + NetChangeNotif,
659    {
660        // TODO
661        // Reset the Matter transport buffers and all sessions first
662        // self.matter().reset_transport()?;
663
664        self.emit_startup_event(im);
665
666        let mut responder = pin!(self.run_responder(im));
667        let mut im_job = pin!(im.run());
668
669        select(&mut responder, &mut im_job).coalesce().await
670    }
671
672    async fn run_im_with_bump<C, H, K, RN, NC>(
673        &self,
674        im: &MatterStackInteractionModel<'_, C, H, K, RN, NC>,
675    ) -> Result<(), Error>
676    where
677        C: Crypto,
678        H: DataModel,
679        K: KvBlobStoreAccess,
680        RN: Networks,
681        NC: NetCtl + WirelessDiag + NetChangeNotif,
682    {
683        // TODO
684        // Reset the Matter transport buffers and all sessions first
685        // self.matter().reset_transport()?;
686
687        self.emit_startup_event(im);
688
689        let mut responder = pin_alloc!(self.bump, self.run_responder_with_bump(im));
690        let mut im_job = pin!(im.run());
691
692        select(&mut responder, &mut im_job).coalesce().await
693    }
694
695    /// Emit `BasicInformation::StartUp` on the root endpoint, as required
696    /// by Matter 1.5.1 Core ยง11.1.6.1 (SHALL).
697    fn emit_startup_event<C, H, K, RN, NC>(
698        &self,
699        im: &MatterStackInteractionModel<'_, C, H, K, RN, NC>,
700    ) where
701        C: Crypto,
702        H: DataModel,
703        K: KvBlobStoreAccess,
704        RN: Networks,
705        NC: NetCtl + WirelessDiag + NetChangeNotif,
706    {
707        let sw_ver = self.matter().dev_det().sw_ver;
708        match StartUp::emit_for(im, 0, |b| b.software_version(sw_ver)?.end()) {
709            Ok(event_number) => info!(
710                "BasicInformation::StartUp emitted (sw_ver={}, event_number={})",
711                sw_ver, event_number,
712            ),
713            Err(e) => warn!("Failed to emit BasicInformation::StartUp: {:?}", e),
714        }
715    }
716
717    async fn run_responder<C, H, K, RN, NC>(
718        &self,
719        im: &MatterStackInteractionModel<'_, C, H, K, RN, NC>,
720    ) -> Result<(), Error>
721    where
722        C: Crypto,
723        H: DataModel,
724        K: KvBlobStoreAccess,
725        RN: Networks,
726        NC: NetCtl + WirelessDiag + NetChangeNotif,
727    {
728        let responder = DefaultResponder::new(im);
729
730        // Run the responder with up to MAX_RESPONDERS handlers (i.e. MAX_RESPONDERS exchanges can be handled simultenously)
731        // Clients trying to open more exchanges than the ones currently running will get "I'm busy, please try again later"
732        pin!(responder.run::<MAX_RESPONDERS, MAX_BUSY_RESPONDERS>()).await?;
733
734        Ok(())
735    }
736
737    async fn run_responder_with_bump<C, H, K, RN, NC>(
738        &self,
739        im: &MatterStackInteractionModel<'_, C, H, K, RN, NC>,
740    ) -> Result<(), Error>
741    where
742        C: Crypto,
743        H: DataModel,
744        K: KvBlobStoreAccess,
745        RN: Networks,
746        NC: NetCtl + WirelessDiag + NetChangeNotif,
747    {
748        let responder = DefaultResponder::new(im);
749
750        let mut actual = pin_alloc!(
751            self.bump,
752            self.run_one_responder_with_bump::<MAX_RESPONDERS, _>(responder.responder())
753        );
754        let mut busy = pin_alloc!(
755            self.bump,
756            self.run_one_responder_with_bump::<MAX_BUSY_RESPONDERS, _>(responder.busy_responder())
757        );
758
759        select(&mut actual, &mut busy).coalesce().await
760    }
761
762    /// Run a responder with Q handlers using the provided bump allocator.
763    async fn run_one_responder_with_bump<const Q: usize, T>(
764        &self,
765        responder: &Responder<'_, T>,
766    ) -> Result<(), Error>
767    where
768        T: ExchangeHandler,
769    {
770        info!("{}: Creating {} handlers", responder.name(), Q);
771
772        let mut handlers = heapless::Vec::<_, Q>::new();
773        debug!(
774            "{}: Handlers size: {}B",
775            responder.name(),
776            core::mem::size_of_val(&handlers)
777        );
778
779        for handler_id in 0..Q {
780            unwrap!(handlers
781                .push(pin_alloc!(self.bump, responder.handle(handler_id)))
782                .map_err(|_| ())); // Cannot fail because the vector has size N
783        }
784
785        let handlers = pin!(handlers);
786        let handlers = unsafe { handlers.map_unchecked_mut(|handlers| handlers.as_mut_slice()) };
787
788        select_slice(handlers).await.0
789    }
790
791    fn run_transport_net<'t, C, S, R, M>(
792        &'t self,
793        crypto: C,
794        send: S,
795        recv: R,
796        multicast: M,
797    ) -> impl Future<Output = Result<(), Error>> + 't
798    where
799        C: Crypto + 't,
800        S: NetworkSend + 't,
801        R: NetworkReceive + 't,
802        M: NetworkMulticast + 't,
803    {
804        self.matter().run(crypto, send, recv, multicast)
805    }
806}
807
808/// A trait representing a user task that needs access to the operational network interface
809/// (Netif and net stack) to perform its work.
810///
811/// Note that the task would be started only when `rs-matter`
812/// brings up the operational interface (eth, wifi or thread)
813/// and if the interface goes down, the user task would be stopped.
814/// Upon re-connection, the task would be started again.
815pub trait UserTask {
816    /// Run the task with the given network stack and network interface
817    async fn run<S, N>(&mut self, net_stack: S, netif: N) -> Result<(), Error>
818    where
819        S: NetStack,
820        N: NetifDiag + NetChangeNotif;
821}
822
823impl<T> UserTask for &mut T
824where
825    T: UserTask,
826{
827    fn run<S, N>(&mut self, net_stack: S, netif: N) -> impl Future<Output = Result<(), Error>>
828    where
829        S: NetStack,
830        N: NetifDiag + NetChangeNotif,
831    {
832        (*self).run(net_stack, netif)
833    }
834}
835
836impl UserTask for () {
837    fn run<S, N>(&mut self, _net_stack: S, _netif: N) -> impl Future<Output = Result<(), Error>>
838    where
839        S: NetStack,
840        N: NetifDiag + NetChangeNotif,
841    {
842        core::future::pending::<Result<(), Error>>()
843    }
844}
845
846// The data model is not created yet, so we don't have to notify anything
847pub(crate) struct DummyAttrNotifier;
848
849impl DynBase for DummyAttrNotifier {}
850
851impl AttrChangeNotifier for DummyAttrNotifier {
852    fn notify_attr_changed(&self, _endpoint_id: EndptId, _cluster_id: ClusterId, _attr_id: AttrId) {
853    }
854
855    fn notify_cluster_changed(&self, _endpoint_id: EndptId, _cluster_id: ClusterId) {}
856
857    fn notify_endpoint_changed(&self, _endpoint_id: EndptId) {}
858
859    fn notify_all_changed(&self) {}
860}