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::cell::Cell;
13use core::fmt::Debug;
14use core::future::Future;
15use core::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6};
16use core::pin::pin;
17
18use cfg_if::cfg_if;
19
20use edge_nal::{UdpBind, UdpSplitMulticast};
21
22use embassy_futures::select::{select, select_slice};
23use embassy_time::Duration;
24
25use rs_matter::crypto::Crypto;
26use rs_matter::dm::clusters::basic_info::BasicInfoConfig;
27use rs_matter::dm::clusters::dev_att::DeviceAttestation;
28use rs_matter::dm::clusters::gen_diag::NetifDiag;
29use rs_matter::dm::clusters::net_comm::{NetCtl, NetCtlStatus, 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::blocking::Mutex;
46use rs_matter::utils::sync::{DynBase, IfMutex};
47use rs_matter::{BasicCommData, Matter, MATTER_PORT};
48
49use crate::bump::Bump;
50use crate::mdns::Mdns;
51use crate::nal::NetStack;
52use crate::network::Network;
53
54#[cfg(feature = "std")]
55#[allow(unused_imports)]
56#[macro_use]
57extern crate std;
58
59#[allow(unused_imports)]
60#[macro_use]
61extern crate alloc;
62
63pub(crate) mod fmt;
65
66pub mod ble;
67pub mod bump;
68pub mod eth;
69pub mod matter;
70pub mod mdns;
71pub mod nal;
72pub mod network;
73pub mod rand;
74pub mod udp;
75pub mod utils;
76pub mod wireless;
77
78mod private {
79 pub trait Sealed {}
81
82 impl Sealed for () {}
83}
84
85cfg_if! {
86 if #[cfg(feature = "max-subscriptions-32")] {
87 const MAX_SUBSCRIPTIONS: usize = 32;
89 } else if #[cfg(feature = "max-subscriptions-16")] {
90 const MAX_SUBSCRIPTIONS: usize = 16;
92 } else if #[cfg(feature = "max-subscriptions-8")] {
93 const MAX_SUBSCRIPTIONS: usize = 8;
95 } else if #[cfg(feature = "max-subscriptions-7")] {
96 const MAX_SUBSCRIPTIONS: usize = 7;
98 } else if #[cfg(feature = "max-subscriptions-6")] {
99 const MAX_SUBSCRIPTIONS: usize = 6;
101 } else if #[cfg(feature = "max-subscriptions-5")] {
102 const MAX_SUBSCRIPTIONS: usize = 5;
104 } else if #[cfg(feature = "max-subscriptions-4")] {
105 const MAX_SUBSCRIPTIONS: usize = 4;
107 } else if #[cfg(feature = "max-subscriptions-3")] {
108 const MAX_SUBSCRIPTIONS: usize = 3;
110 } else if #[cfg(feature = "max-subscriptions-2")] {
111 const MAX_SUBSCRIPTIONS: usize = 2;
113 } else if #[cfg(feature = "max-subscriptions-1")] {
114 const MAX_SUBSCRIPTIONS: usize = 1;
116 } else {
117 const MAX_SUBSCRIPTIONS: usize = 3;
119 }
120}
121
122cfg_if! {
123 if #[cfg(feature = "events-ringbuf-size-2048")] {
124 const EVENTS_RINGBUF_SIZE: usize = 2048;
126 } else if #[cfg(feature = "events-ringbuf-size-1024")] {
127 const EVENTS_RINGBUF_SIZE: usize = 1024;
129 } else if #[cfg(feature = "events-ringbuf-size-512")] {
130 const EVENTS_RINGBUF_SIZE: usize = 512;
132 } else if #[cfg(feature = "events-ringbuf-size-256")] {
133 const EVENTS_RINGBUF_SIZE: usize = 256;
135 } else if #[cfg(feature = "events-ringbuf-size-128")] {
136 const EVENTS_RINGBUF_SIZE: usize = 128;
138 } else if #[cfg(feature = "events-ringbuf-size-64")] {
139 const EVENTS_RINGBUF_SIZE: usize = 64;
141 } else if #[cfg(feature = "events-ringbuf-size-0")] {
142 const EVENTS_RINGBUF_SIZE: usize = 0;
144 } else {
145 const EVENTS_RINGBUF_SIZE: usize = 256;
147 }
148}
149
150cfg_if! {
151 if #[cfg(feature = "max-im-buffers-64")] {
152 const MAX_IM_BUFFERS: usize = 64;
154 } else if #[cfg(feature = "max-im-buffers-32")] {
155 const MAX_IM_BUFFERS: usize = 32;
157 } else if #[cfg(feature = "max-im-buffers-16")] {
158 const MAX_IM_BUFFERS: usize = 16;
160 } else if #[cfg(feature = "max-im-buffers-10")] {
161 const MAX_IM_BUFFERS: usize = 10;
163 } else if #[cfg(feature = "max-im-buffers-9")] {
164 const MAX_IM_BUFFERS: usize = 9;
166 } else if #[cfg(feature = "max-im-buffers-8")] {
167 const MAX_IM_BUFFERS: usize = 8;
169 } else if #[cfg(feature = "max-im-buffers-7")] {
170 const MAX_IM_BUFFERS: usize = 7;
172 } else if #[cfg(feature = "max-im-buffers-6")] {
173 const MAX_IM_BUFFERS: usize = 6;
175 } else if #[cfg(feature = "max-im-buffers-5")] {
176 const MAX_IM_BUFFERS: usize = 5;
178 } else if #[cfg(feature = "max-im-buffers-4")] {
179 const MAX_IM_BUFFERS: usize = 4;
181 } else {
182 const MAX_IM_BUFFERS: usize = 10;
184 }
185}
186
187cfg_if! {
188 if #[cfg(feature = "max-responders-32")] {
189 const MAX_RESPONDERS: usize = 32;
191 } else if #[cfg(feature = "max-responders-16")] {
192 const MAX_RESPONDERS: usize = 16;
194 } else if #[cfg(feature = "max-responders-8")] {
195 const MAX_RESPONDERS: usize = 8;
197 } else if #[cfg(feature = "max-responders-7")] {
198 const MAX_RESPONDERS: usize = 7;
200 } else if #[cfg(feature = "max-responders-6")] {
201 const MAX_RESPONDERS: usize = 6;
203 } else if #[cfg(feature = "max-responders-5")] {
204 const MAX_RESPONDERS: usize = 5;
206 } else if #[cfg(feature = "max-responders-4")] {
207 const MAX_RESPONDERS: usize = 4;
209 } else if #[cfg(feature = "max-responders-3")] {
210 const MAX_RESPONDERS: usize = 3;
212 } else if #[cfg(feature = "max-responders-2")] {
213 const MAX_RESPONDERS: usize = 2;
215 } else if #[cfg(feature = "max-responders-1")] {
216 const MAX_RESPONDERS: usize = 1;
218 } else {
219 const MAX_RESPONDERS: usize = 4;
221 }
222}
223
224const MAX_BUSY_RESPONDERS: usize = 2;
225
226pub type MatterStackInteractionModel<'a, C, H, K, RN, NC> = InteractionModel<
227 'a,
228 C,
229 MatterBuffers<MAX_IM_BUFFERS>,
230 H,
231 K,
232 RN,
233 NC,
234 (),
236 MAX_SUBSCRIPTIONS,
237 EVENTS_RINGBUF_SIZE,
238>;
239
240pub type MatterStackInteractionModelState<RN> =
245 InteractionModelState<RN, MAX_SUBSCRIPTIONS, EVENTS_RINGBUF_SIZE>;
246
247pub struct MatterStack<'a, const B: usize, N>
251where
252 N: Network,
253{
254 matter: Matter<'a>,
255 buffers: MatterBuffers<MAX_IM_BUFFERS>,
256 state: MatterStackInteractionModelState<N::Networks>,
259 bump: Bump<B>,
260 run_lock: IfMutex<()>,
261 im_hydrated: Mutex<Cell<bool>>,
276 #[allow(unused)]
277 network: N,
278 }
280
281impl<'a, const B: usize, N> MatterStack<'a, B, N>
282where
283 N: Network,
284{
285 #[allow(clippy::large_stack_frames)]
287 #[inline(always)]
288 pub const fn new(
289 dev_det: &'a BasicInfoConfig,
290 dev_comm: BasicCommData,
291 dev_att: &'a dyn DeviceAttestation,
292 ) -> Self {
293 Self {
294 matter: Matter::new(dev_det, dev_comm, dev_att, MATTER_PORT),
295 buffers: MatterBuffers::new(),
296 state: MatterStackInteractionModelState::new(N::NETWORKS),
297 bump: Bump::new(),
298 run_lock: IfMutex::new(()),
299 im_hydrated: Mutex::new(Cell::new(false)),
300 network: N::INIT,
301 }
303 }
304
305 #[allow(clippy::large_stack_frames)]
306 pub fn init(
307 dev_det: &'a BasicInfoConfig,
308 dev_comm: BasicCommData,
309 dev_att: &'a dyn DeviceAttestation,
310 ) -> impl Init<Self> {
311 init!(Self {
312 matter <- Matter::init(
313 dev_det,
314 dev_comm,
315 dev_att,
316 MATTER_PORT,
317 ),
318 buffers <- MatterBuffers::init(),
319 state <- MatterStackInteractionModelState::init(N::init_networks()),
320 bump <- Bump::init(),
321 run_lock <- IfMutex::init(()),
322 im_hydrated: Mutex::new(Cell::new(false)),
323 network <- N::init(),
324 })
326 }
327
328 pub fn replace_dev_att(&mut self, dev_att: &'a dyn DeviceAttestation) {
332 self.matter.replace_dev_att(dev_att);
333 }
334
335 pub const fn matter(&self) -> &Matter<'a> {
337 &self.matter
338 }
339
340 pub const fn network(&self) -> &N {
343 &self.network
344 }
345
346 pub fn kv<'s, S: KvBlobStore + 's>(&'s self, store: S) -> impl KvBlobStoreAccess + 's {
353 self.matter().kv(store)
354 }
355
356 pub fn open_basic_comm_window<C>(
398 &self,
399 crypto: C,
400 notify: &dyn AttrChangeNotifier,
401 ) -> Result<(), Error>
402 where
403 C: Crypto,
404 {
405 self.matter()
406 .open_basic_comm_window(MAX_COMM_WINDOW_TIMEOUT_SECS, crypto, notify)?;
407
408 self.matter()
409 .print_standard_qr_text(self.network.discovery_capabilities())?;
410
411 self.matter()
412 .print_standard_qr_code(QrTextType::Unicode, self.network.discovery_capabilities())
413 }
414
415 async fn run_oper_net<C, U, X, R, S>(
427 &self,
428 crypto: C,
429 net_stack: U,
430 net_interface: u32,
431 until: X,
432 mut comm: Option<(R, S)>,
433 ) -> Result<(), Error>
434 where
435 C: Crypto,
436 U: NetStack,
437 X: Future<Output = Result<(), Error>>,
438 R: NetworkReceive,
439 S: NetworkSend,
440 {
441 fn map_err<E: Debug>(e: E) -> Error {
442 warn!("Matter UDP network error: {:?}", debug2format!(e));
443 ErrorCode::StdIoError.into() }
445
446 let udp_bind = unwrap!(net_stack.udp_bind());
447
448 let mut socket = udp_bind
449 .bind(SocketAddr::V6(SocketAddrV6::new(
450 Ipv6Addr::UNSPECIFIED,
451 MATTER_PORT,
452 0,
453 net_interface,
454 )))
455 .await
456 .map_err(map_err)?;
457
458 let (recv, send, m4, m6) = socket.split_multicast();
459
460 let multicast = udp::Udp(udp::Multicast::new(
461 m4,
462 Ipv4Addr::UNSPECIFIED,
464 m6,
465 net_interface,
466 ));
467
468 let mut until_task = pin!(until);
469
470 if let Some((comm_recv, comm_send)) = comm.as_mut() {
471 info!("Running operational and commissioning networks");
472
473 let mut netw_task = pin!(self.run_transport_net(
474 &crypto,
475 ChainedNetwork::new(Address::is_udp, udp::Udp(send), comm_send),
476 ChainedNetwork::new(Address::is_udp, udp::Udp(recv), comm_recv),
477 ChainedNetwork::new(Address::is_udp, multicast, NoNetwork),
478 ));
479
480 select(&mut netw_task, &mut until_task).coalesce().await
481 } else {
482 info!("Running operational network");
483
484 let mut netw_task =
485 pin!(self.run_transport_net(&crypto, udp::Udp(send), udp::Udp(recv), multicast,));
486
487 select(&mut netw_task, &mut until_task).coalesce().await
488 }
489 }
490
491 async fn run_oper_netif_mdns<C, U, I, M>(
505 &self,
506 crypto: C,
507 net_stack: U,
508 netif: I,
509 mut mdns: M,
510 ) -> Result<(), Error>
511 where
512 C: Crypto,
513 U: NetStack,
514 I: NetifDiag + NetChangeNotif,
515 M: Mdns,
516 {
517 #[derive(Clone, Debug, Eq, PartialEq, Hash)]
518 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
519 struct NetifState {
520 ipv6: Ipv6Addr,
521 ipv4: Ipv4Addr,
522 mac: [u8; 8],
523 operational: bool,
524 netif_index: u32,
525 }
526
527 impl NetifState {
528 pub const fn new() -> Self {
529 Self {
530 ipv6: Ipv6Addr::UNSPECIFIED,
531 ipv4: Ipv4Addr::UNSPECIFIED,
532 mac: [0; 8],
533 operational: false,
534 netif_index: 0,
535 }
536 }
537 }
538
539 fn load_netif_state<I>(net_diag: I, state: &mut NetifState) -> Result<(), Error>
540 where
541 I: NetifDiag,
542 {
543 state.operational = false;
544 state.ipv6 = Ipv6Addr::UNSPECIFIED;
545 state.ipv4 = Ipv4Addr::UNSPECIFIED;
546 state.mac = [0; 8];
547
548 net_diag.netifs(&mut |ni| {
549 if ni.operational && !ni.ipv6_addrs.is_empty() {
550 state.operational = true;
551 state.ipv6 = ni.ipv6_addrs[0];
552 state.ipv4 = ni
553 .ipv4_addrs
554 .first()
555 .copied()
556 .unwrap_or(Ipv4Addr::UNSPECIFIED);
557 state.mac = *ni.hw_addr;
558 state.netif_index = ni.netif_index;
559 }
560
561 Ok(())
562 })
563 }
564
565 async fn wait_changed<I>(
566 net_diag: I,
567 cur_state: &NetifState,
568 new_state: &mut NetifState,
569 ) -> Result<(), Error>
570 where
571 I: NetifDiag + NetChangeNotif,
572 {
573 loop {
574 load_netif_state(&net_diag, new_state)?;
575
576 if &*new_state != cur_state {
577 info!(
578 "Netif change detected.\n Old: {:?}\n New: {:?}",
579 cur_state, new_state
580 );
581 break Ok(());
582 }
583
584 trace!("No change");
585 net_diag.wait_changed().await;
586 }
587 }
588
589 let mut new_state = NetifState::new();
594 load_netif_state(&netif, &mut new_state)?;
595
596 loop {
597 let cur_state = new_state.clone();
598
599 let mut netif_changed_task = pin!(wait_changed(&netif, &cur_state, &mut new_state));
600
601 let mut mdns_task = pin!(async {
602 if cur_state.operational {
603 info!("Netif up: {:?}", cur_state);
604
605 let udp_bind = unwrap!(net_stack.udp_bind());
606
607 info!("Running mDNS");
608
609 loop {
610 let _result = mdns
611 .run(
612 self.matter(),
613 &crypto,
614 &udp_bind,
615 &cur_state.mac,
616 cur_state.ipv4,
617 cur_state.ipv6,
618 cur_state.netif_index,
619 )
620 .await;
621
622 warn!("mDNS failed with {:?}, retrying in 5s...", _result);
623 embassy_time::Timer::after(Duration::from_secs(5)).await;
624 }
625 } else {
626 info!("Netif down");
627 core::future::pending::<()>().await;
628 }
629
630 Ok(())
631 });
632
633 select(&mut netif_changed_task, &mut mdns_task)
634 .coalesce()
635 .await?;
636 }
637 }
638
639 #[inline(always)]
640 fn im<C, H, K, NC>(
641 &self,
642 crypto: C,
643 handler: H,
644 kv: K,
645 net_ctl: NC,
646 ) -> MatterStackInteractionModel<'_, C, H, K, N::Networks, NC>
647 where
648 C: Crypto,
649 H: DataModel,
650 K: KvBlobStoreAccess,
651 NC: NetCtl + NetCtlStatus + WirelessDiag + NetChangeNotif,
652 {
653 MatterStackInteractionModel::new_with_net_ctl(
654 self.matter(),
655 crypto,
656 &self.buffers,
657 handler,
658 kv,
659 net_ctl,
660 &self.state,
661 )
662 }
663
664 async fn run_im<C, H, K, RN, NC>(
665 &self,
666 im: &MatterStackInteractionModel<'_, C, H, K, RN, NC>,
667 ) -> Result<(), Error>
668 where
669 C: Crypto,
670 H: DataModel,
671 K: KvBlobStoreAccess,
672 RN: Networks,
673 NC: NetCtl + NetCtlStatus + WirelessDiag + NetChangeNotif,
674 {
675 self.startup_im(im).await?;
680
681 let mut responder = pin!(self.run_responder(im));
682 let mut im_job = pin!(im.run());
683
684 select(&mut responder, &mut im_job).coalesce().await
685 }
686
687 async fn run_im_with_bump<C, H, K, RN, NC>(
688 &self,
689 im: &MatterStackInteractionModel<'_, C, H, K, RN, NC>,
690 ) -> Result<(), Error>
691 where
692 C: Crypto,
693 H: DataModel,
694 K: KvBlobStoreAccess,
695 RN: Networks,
696 NC: NetCtl + NetCtlStatus + WirelessDiag + NetChangeNotif,
697 {
698 self.startup_im(im).await?;
703
704 let mut responder = pin_alloc!(self.bump, self.run_responder_with_bump(im));
705 let mut im_job = pin!(im.run());
706
707 select(&mut responder, &mut im_job).coalesce().await
708 }
709
710 async fn startup_im<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 + NetCtlStatus + WirelessDiag + NetChangeNotif,
727 {
728 if self.im_hydrated.lock(|hydrated| hydrated.replace(true)) {
729 return Ok(());
730 }
731
732 im.startup().await
733 }
734
735 async fn run_responder<C, H, K, RN, NC>(
736 &self,
737 im: &MatterStackInteractionModel<'_, C, H, K, RN, NC>,
738 ) -> Result<(), Error>
739 where
740 C: Crypto,
741 H: DataModel,
742 K: KvBlobStoreAccess,
743 RN: Networks,
744 NC: NetCtl + NetCtlStatus + WirelessDiag + NetChangeNotif,
745 {
746 let responder = DefaultResponder::new(im);
747
748 pin!(responder.run::<MAX_RESPONDERS, MAX_BUSY_RESPONDERS>()).await?;
751
752 Ok(())
753 }
754
755 async fn run_responder_with_bump<C, H, K, RN, NC>(
756 &self,
757 im: &MatterStackInteractionModel<'_, C, H, K, RN, NC>,
758 ) -> Result<(), Error>
759 where
760 C: Crypto,
761 H: DataModel,
762 K: KvBlobStoreAccess,
763 RN: Networks,
764 NC: NetCtl + NetCtlStatus + WirelessDiag + NetChangeNotif,
765 {
766 let responder = DefaultResponder::new(im);
767
768 let mut actual = pin_alloc!(
769 self.bump,
770 self.run_one_responder_with_bump::<MAX_RESPONDERS, _>(responder.responder())
771 );
772 let mut busy = pin_alloc!(
773 self.bump,
774 self.run_one_responder_with_bump::<MAX_BUSY_RESPONDERS, _>(responder.busy_responder())
775 );
776
777 select(&mut actual, &mut busy).coalesce().await
778 }
779
780 async fn run_one_responder_with_bump<const Q: usize, T>(
782 &self,
783 responder: &Responder<'_, T>,
784 ) -> Result<(), Error>
785 where
786 T: ExchangeHandler,
787 {
788 info!("{}: Creating {} handlers", responder.name(), Q);
789
790 let mut handlers = heapless::Vec::<_, Q>::new();
791 debug!(
792 "{}: Handlers size: {}B",
793 responder.name(),
794 core::mem::size_of_val(&handlers)
795 );
796
797 for handler_id in 0..Q {
798 unwrap!(handlers
799 .push(pin_alloc!(self.bump, responder.handle(handler_id)))
800 .map_err(|_| ())); }
802
803 let handlers = pin!(handlers);
804 let handlers = unsafe { handlers.map_unchecked_mut(|handlers| handlers.as_mut_slice()) };
805
806 select_slice(handlers).await.0
807 }
808
809 fn run_transport_net<'t, C, S, R, M>(
810 &'t self,
811 crypto: C,
812 send: S,
813 recv: R,
814 multicast: M,
815 ) -> impl Future<Output = Result<(), Error>> + 't
816 where
817 C: Crypto + 't,
818 S: NetworkSend + 't,
819 R: NetworkReceive + 't,
820 M: NetworkMulticast + 't,
821 {
822 self.matter().run(crypto, send, recv, multicast)
823 }
824}
825
826pub trait UserTask {
834 async fn run<S, N>(&mut self, net_stack: S, netif: N) -> Result<(), Error>
836 where
837 S: NetStack,
838 N: NetifDiag + NetChangeNotif;
839}
840
841impl<T> UserTask for &mut T
842where
843 T: UserTask,
844{
845 fn run<S, N>(&mut self, net_stack: S, netif: N) -> impl Future<Output = Result<(), Error>>
846 where
847 S: NetStack,
848 N: NetifDiag + NetChangeNotif,
849 {
850 (*self).run(net_stack, netif)
851 }
852}
853
854impl UserTask for () {
855 fn run<S, N>(&mut self, _net_stack: S, _netif: N) -> impl Future<Output = Result<(), Error>>
856 where
857 S: NetStack,
858 N: NetifDiag + NetChangeNotif,
859 {
860 core::future::pending::<Result<(), Error>>()
861 }
862}
863
864pub(crate) struct DummyAttrNotifier;
866
867impl DynBase for DummyAttrNotifier {}
868
869impl AttrChangeNotifier for DummyAttrNotifier {
870 fn notify_attr_changed(&self, _endpoint_id: EndptId, _cluster_id: ClusterId, _attr_id: AttrId) {
871 }
872
873 fn notify_cluster_changed(&self, _endpoint_id: EndptId, _cluster_id: ClusterId) {}
874
875 fn notify_endpoint_changed(&self, _endpoint_id: EndptId) {}
876
877 fn notify_all_changed(&self) {}
878}