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
62pub(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 pub trait Sealed {}
80
81 impl Sealed for () {}
82}
83
84cfg_if! {
85 if #[cfg(feature = "max-subscriptions-32")] {
86 const MAX_SUBSCRIPTIONS: usize = 32;
88 } else if #[cfg(feature = "max-subscriptions-16")] {
89 const MAX_SUBSCRIPTIONS: usize = 16;
91 } else if #[cfg(feature = "max-subscriptions-8")] {
92 const MAX_SUBSCRIPTIONS: usize = 8;
94 } else if #[cfg(feature = "max-subscriptions-7")] {
95 const MAX_SUBSCRIPTIONS: usize = 7;
97 } else if #[cfg(feature = "max-subscriptions-6")] {
98 const MAX_SUBSCRIPTIONS: usize = 6;
100 } else if #[cfg(feature = "max-subscriptions-5")] {
101 const MAX_SUBSCRIPTIONS: usize = 5;
103 } else if #[cfg(feature = "max-subscriptions-4")] {
104 const MAX_SUBSCRIPTIONS: usize = 4;
106 } else if #[cfg(feature = "max-subscriptions-3")] {
107 const MAX_SUBSCRIPTIONS: usize = 3;
109 } else if #[cfg(feature = "max-subscriptions-2")] {
110 const MAX_SUBSCRIPTIONS: usize = 2;
112 } else if #[cfg(feature = "max-subscriptions-1")] {
113 const MAX_SUBSCRIPTIONS: usize = 1;
115 } else {
116 const MAX_SUBSCRIPTIONS: usize = 3;
118 }
119}
120
121cfg_if! {
122 if #[cfg(feature = "events-ringbuf-size-0")] {
123 const EVENTS_RINGBUF_SIZE: usize = 0;
125 } else if #[cfg(feature = "events-ringbuf-size-64")] {
126 const EVENTS_RINGBUF_SIZE: usize = 64;
128 } else if #[cfg(feature = "events-ringbuf-size-128")] {
129 const EVENTS_RINGBUF_SIZE: usize = 128;
131 } else if #[cfg(feature = "events-ringbuf-size-256")] {
132 const EVENTS_RINGBUF_SIZE: usize = 256;
134 } else if #[cfg(feature = "events-ringbuf-size-512")] {
135 const EVENTS_RINGBUF_SIZE: usize = 512;
137 } else if #[cfg(feature = "events-ringbuf-size-1024")] {
138 const EVENTS_RINGBUF_SIZE: usize = 1024;
140 } else if #[cfg(feature = "events-ringbuf-size-2048")] {
141 const EVENTS_RINGBUF_SIZE: usize = 2048;
143 } else {
144 const EVENTS_RINGBUF_SIZE: usize = 0;
146 }
147}
148
149cfg_if! {
150 if #[cfg(feature = "max-im-buffers-64")] {
151 const MAX_IM_BUFFERS: usize = 64;
153 } else if #[cfg(feature = "max-im-buffers-32")] {
154 const MAX_IM_BUFFERS: usize = 32;
156 } else if #[cfg(feature = "max-im-buffers-16")] {
157 const MAX_IM_BUFFERS: usize = 16;
159 } else if #[cfg(feature = "max-im-buffers-10")] {
160 const MAX_IM_BUFFERS: usize = 10;
162 } else if #[cfg(feature = "max-im-buffers-9")] {
163 const MAX_IM_BUFFERS: usize = 9;
165 } else if #[cfg(feature = "max-im-buffers-8")] {
166 const MAX_IM_BUFFERS: usize = 8;
168 } else if #[cfg(feature = "max-im-buffers-7")] {
169 const MAX_IM_BUFFERS: usize = 7;
171 } else if #[cfg(feature = "max-im-buffers-6")] {
172 const MAX_IM_BUFFERS: usize = 6;
174 } else if #[cfg(feature = "max-im-buffers-5")] {
175 const MAX_IM_BUFFERS: usize = 5;
177 } else if #[cfg(feature = "max-im-buffers-4")] {
178 const MAX_IM_BUFFERS: usize = 4;
180 } else {
181 const MAX_IM_BUFFERS: usize = 10;
183 }
184}
185
186cfg_if! {
187 if #[cfg(feature = "max-responders-32")] {
188 const MAX_RESPONDERS: usize = 32;
190 } else if #[cfg(feature = "max-responders-16")] {
191 const MAX_RESPONDERS: usize = 16;
193 } else if #[cfg(feature = "max-responders-8")] {
194 const MAX_RESPONDERS: usize = 8;
196 } else if #[cfg(feature = "max-responders-7")] {
197 const MAX_RESPONDERS: usize = 7;
199 } else if #[cfg(feature = "max-responders-6")] {
200 const MAX_RESPONDERS: usize = 6;
202 } else if #[cfg(feature = "max-responders-5")] {
203 const MAX_RESPONDERS: usize = 5;
205 } else if #[cfg(feature = "max-responders-4")] {
206 const MAX_RESPONDERS: usize = 4;
208 } else if #[cfg(feature = "max-responders-3")] {
209 const MAX_RESPONDERS: usize = 3;
211 } else if #[cfg(feature = "max-responders-2")] {
212 const MAX_RESPONDERS: usize = 2;
214 } else if #[cfg(feature = "max-responders-1")] {
215 const MAX_RESPONDERS: usize = 1;
217 } else {
218 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
237pub type MatterStackInteractionModelState<RN> =
242 InteractionModelState<RN, MAX_SUBSCRIPTIONS, EVENTS_RINGBUF_SIZE>;
243
244pub struct MatterStack<'a, const B: usize, N>
248where
249 N: Network,
250{
251 matter: Matter<'a>,
252 buffers: MatterBuffers<MAX_IM_BUFFERS>,
253 state: MatterStackInteractionModelState<N::Networks>,
256 bump: Bump<B>,
257 run_lock: IfMutex<()>,
258 #[allow(unused)]
259 network: N,
260 }
262
263impl<'a, const B: usize, N> MatterStack<'a, B, N>
264where
265 N: Network,
266{
267 #[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 }
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 })
306 }
307
308 pub fn replace_dev_att(&mut self, dev_att: &'a dyn DeviceAttestation) {
312 self.matter.replace_dev_att(dev_att);
313 }
314
315 pub const fn matter(&self) -> &Matter<'a> {
317 &self.matter
318 }
319
320 pub const fn network(&self) -> &N {
323 &self.network
324 }
325
326 pub fn kv<'s, S: KvBlobStore + 's>(&'s self, store: S) -> impl KvBlobStoreAccess + 's {
333 self.matter().kv(store)
334 }
335
336 pub fn is_commissioned(&self) -> bool {
374 self.matter().is_commissioned()
375 }
376
377 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 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() }
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 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 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 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 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 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 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 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 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(|_| ())); }
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
808pub trait UserTask {
816 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
846pub(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}