1use core::marker::PhantomData;
2use core::pin::pin;
3
4use embassy_futures::select::{select, select3, select4};
5
6use rs_matter::crypto::Crypto;
7use rs_matter::dm::clusters::gen_diag::NetifDiag;
8use rs_matter::dm::clusters::net_comm::{self, NetCtlError, NetworkType, WirelessCreds};
9use rs_matter::dm::clusters::wifi_diag::WirelessDiag;
10use rs_matter::dm::clusters::{thread_diag, wifi_diag};
11use rs_matter::dm::networks::wireless::{
12 NetCtlState, NoopWirelessNetCtl, WirelessNetwork, WirelessNetworks,
13};
14use rs_matter::dm::networks::NetChangeNotif;
15use rs_matter::dm::DataModel;
16use rs_matter::error::Error;
17use rs_matter::pairing::DiscoveryCapabilities;
18use rs_matter::persist::KvBlobStore;
19use rs_matter::sc::pase::CommWindowState;
20use rs_matter::transport::network::btp::{AdvData, Btp};
21use rs_matter::transport::network::NoNetwork;
22use rs_matter::utils::cell::RefCell;
23use rs_matter::utils::init::{init, Init};
24use rs_matter::utils::select::Coalesce;
25use rs_matter::utils::sync::blocking;
26use rs_matter::utils::sync::DynBase;
27
28use crate::ble::GattPeripheral;
29use crate::mdns::Mdns;
30use crate::nal::NetStack;
31use crate::network::{Embedding, Network};
32use crate::private::Sealed;
33use crate::{pin_alloc, DummyAttrNotifier, MatterStack};
34
35pub use gatt::*;
36pub use thread::*;
37pub use wifi::*;
38
39mod gatt;
40mod thread;
41mod wifi;
42
43pub const MAX_WIRELESS_NETWORKS: usize = 2;
44
45pub type WirelessMatterStack<'a, const B: usize, T, E = ()> = MatterStack<'a, B, WirelessBle<T, E>>;
47
48pub struct WirelessBle<T, E = ()>
61where
62 T: WirelessNetwork,
63{
64 btp: Btp,
65 net_state: blocking::Mutex<RefCell<NetCtlState>>,
66 embedding: E,
67 _network: PhantomData<fn() -> T>,
71}
72
73impl<T, E> WirelessBle<T, E>
74where
75 T: WirelessNetwork,
76 E: Embedding,
77{
78 pub const fn new() -> Self {
80 Self {
81 btp: Btp::new(),
82 net_state: NetCtlState::new_with_mutex(),
83 embedding: E::INIT,
84 _network: PhantomData,
85 }
86 }
87
88 pub fn init() -> impl Init<Self> {
90 init!(Self {
91 btp <- Btp::init(),
92 net_state <- NetCtlState::init_with_mutex(),
93 embedding <- E::init(),
94 _network: PhantomData,
95 })
96 }
97}
98
99pub enum WirelessNetCtl<'a, Q> {
121 Commissioning(NetworkType),
123 Operational(&'a Q),
125}
126
127impl<Q> net_comm::NetCtl for WirelessNetCtl<'_, Q>
128where
129 Q: net_comm::NetCtl,
130{
131 fn net_type(&self) -> NetworkType {
132 match self {
133 Self::Commissioning(net_type) => *net_type,
134 Self::Operational(q) => q.net_type(),
135 }
136 }
137
138 fn connect_max_time_seconds(&self) -> u8 {
139 match self {
140 Self::Commissioning(_) => 0,
141 Self::Operational(q) => q.connect_max_time_seconds(),
142 }
143 }
144
145 fn scan_max_time_seconds(&self) -> u8 {
146 match self {
147 Self::Commissioning(_) => 0,
148 Self::Operational(q) => q.scan_max_time_seconds(),
149 }
150 }
151
152 fn supported_wifi_bands<F>(&self, f: F) -> Result<(), Error>
153 where
154 F: FnMut(net_comm::WiFiBandEnum) -> Result<(), Error>,
155 {
156 match self {
157 Self::Commissioning(_) => Ok(()),
158 Self::Operational(q) => q.supported_wifi_bands(f),
159 }
160 }
161
162 fn supported_thread_features(&self) -> net_comm::ThreadCapabilitiesBitmap {
163 match self {
164 Self::Commissioning(_) => net_comm::ThreadCapabilitiesBitmap::empty(),
165 Self::Operational(q) => q.supported_thread_features(),
166 }
167 }
168
169 fn thread_version(&self) -> u16 {
170 match self {
171 Self::Commissioning(_) => 0,
172 Self::Operational(q) => q.thread_version(),
173 }
174 }
175
176 async fn scan<F>(&self, network: Option<&[u8]>, f: F) -> Result<(), NetCtlError>
177 where
178 F: FnMut(&net_comm::NetworkScanInfo) -> Result<(), Error>,
179 {
180 match self {
181 Self::Commissioning(_) => Err(NetCtlError::Other(
183 rs_matter::error::ErrorCode::InvalidAction.into(),
184 )),
185 Self::Operational(q) => q.scan(network, f).await,
186 }
187 }
188
189 async fn connect(&self, creds: &WirelessCreds<'_>) -> Result<(), NetCtlError> {
190 match self {
191 Self::Commissioning(net_type) => Ok(creds.check_match(*net_type)?),
193 Self::Operational(q) => q.connect(creds).await,
194 }
195 }
196}
197
198impl<Q> NetChangeNotif for WirelessNetCtl<'_, Q>
199where
200 Q: NetChangeNotif,
201{
202 async fn wait_changed(&self) {
203 match self {
204 Self::Commissioning(_) => core::future::pending().await,
205 Self::Operational(q) => q.wait_changed().await,
206 }
207 }
208}
209
210#[cfg(feature = "sync-mutex")]
211impl<Q> DynBase for WirelessNetCtl<'_, Q> where Q: Send + Sync {}
212
213#[cfg(not(feature = "sync-mutex"))]
214impl<Q> DynBase for WirelessNetCtl<'_, Q> {}
215
216impl<Q> WirelessDiag for WirelessNetCtl<'_, Q>
217where
218 Q: WirelessDiag,
219{
220 fn connected(&self) -> Result<bool, Error> {
221 match self {
222 Self::Commissioning(_) => Ok(false),
223 Self::Operational(q) => q.connected(),
224 }
225 }
226}
227
228impl<Q> wifi_diag::WifiDiag for WirelessNetCtl<'_, Q>
235where
236 Q: wifi_diag::WifiDiag,
237{
238 fn bssid(&self, f: &mut dyn FnMut(Option<&[u8]>) -> Result<(), Error>) -> Result<(), Error> {
239 match self {
240 Self::Commissioning(_) => f(None),
241 Self::Operational(q) => q.bssid(f),
242 }
243 }
244
245 fn security_type(
246 &self,
247 ) -> Result<rs_matter::tlv::Nullable<wifi_diag::SecurityTypeEnum>, Error> {
248 match self {
249 Self::Commissioning(_) => Ok(rs_matter::tlv::Nullable::none()),
250 Self::Operational(q) => q.security_type(),
251 }
252 }
253
254 fn wi_fi_version(&self) -> Result<rs_matter::tlv::Nullable<wifi_diag::WiFiVersionEnum>, Error> {
255 match self {
256 Self::Commissioning(_) => Ok(rs_matter::tlv::Nullable::none()),
257 Self::Operational(q) => q.wi_fi_version(),
258 }
259 }
260
261 fn channel_number(&self) -> Result<rs_matter::tlv::Nullable<u16>, Error> {
262 match self {
263 Self::Commissioning(_) => Ok(rs_matter::tlv::Nullable::none()),
264 Self::Operational(q) => q.channel_number(),
265 }
266 }
267
268 fn rssi(&self) -> Result<rs_matter::tlv::Nullable<i8>, Error> {
269 match self {
270 Self::Commissioning(_) => Ok(rs_matter::tlv::Nullable::none()),
271 Self::Operational(q) => q.rssi(),
272 }
273 }
274}
275
276impl<Q> thread_diag::ThreadDiag for WirelessNetCtl<'_, Q>
277where
278 Q: thread_diag::ThreadDiag,
279{
280 fn channel(&self) -> Result<Option<u16>, Error> {
281 match self {
282 Self::Commissioning(_) => Ok(None),
283 Self::Operational(q) => q.channel(),
284 }
285 }
286 fn routing_role(&self) -> Result<Option<thread_diag::RoutingRoleEnum>, Error> {
287 match self {
288 Self::Commissioning(_) => Ok(None),
289 Self::Operational(q) => q.routing_role(),
290 }
291 }
292 fn network_name(
293 &self,
294 f: &mut dyn FnMut(Option<&str>) -> Result<(), Error>,
295 ) -> Result<(), Error> {
296 match self {
297 Self::Commissioning(_) => f(None),
298 Self::Operational(q) => q.network_name(f),
299 }
300 }
301 fn pan_id(&self) -> Result<Option<u16>, Error> {
302 match self {
303 Self::Commissioning(_) => Ok(None),
304 Self::Operational(q) => q.pan_id(),
305 }
306 }
307 fn extended_pan_id(&self) -> Result<Option<u64>, Error> {
308 match self {
309 Self::Commissioning(_) => Ok(None),
310 Self::Operational(q) => q.extended_pan_id(),
311 }
312 }
313 fn mesh_local_prefix(
314 &self,
315 f: &mut dyn FnMut(Option<&[u8]>) -> Result<(), Error>,
316 ) -> Result<(), Error> {
317 match self {
318 Self::Commissioning(_) => f(None),
319 Self::Operational(q) => q.mesh_local_prefix(f),
320 }
321 }
322 fn neighbor_table(
323 &self,
324 f: &mut dyn FnMut(&thread_diag::NeighborTable) -> Result<(), Error>,
325 ) -> Result<(), Error> {
326 match self {
327 Self::Commissioning(_) => Ok(()),
328 Self::Operational(q) => q.neighbor_table(f),
329 }
330 }
331 fn route_table(
332 &self,
333 f: &mut dyn FnMut(&thread_diag::RouteTable) -> Result<(), Error>,
334 ) -> Result<(), Error> {
335 match self {
336 Self::Commissioning(_) => Ok(()),
337 Self::Operational(q) => q.route_table(f),
338 }
339 }
340 fn partition_id(&self) -> Result<Option<u32>, Error> {
341 match self {
342 Self::Commissioning(_) => Ok(None),
343 Self::Operational(q) => q.partition_id(),
344 }
345 }
346 fn weighting(&self) -> Result<Option<u16>, Error> {
347 match self {
348 Self::Commissioning(_) => Ok(None),
349 Self::Operational(q) => q.weighting(),
350 }
351 }
352 fn data_version(&self) -> Result<Option<u16>, Error> {
353 match self {
354 Self::Commissioning(_) => Ok(None),
355 Self::Operational(q) => q.data_version(),
356 }
357 }
358 fn stable_data_version(&self) -> Result<Option<u16>, Error> {
359 match self {
360 Self::Commissioning(_) => Ok(None),
361 Self::Operational(q) => q.stable_data_version(),
362 }
363 }
364 fn leader_router_id(&self) -> Result<Option<u8>, Error> {
365 match self {
366 Self::Commissioning(_) => Ok(None),
367 Self::Operational(q) => q.leader_router_id(),
368 }
369 }
370 fn ext_address(&self) -> Result<Option<u64>, Error> {
371 match self {
372 Self::Commissioning(_) => Ok(None),
373 Self::Operational(q) => q.ext_address(),
374 }
375 }
376 fn rloc_16(&self) -> Result<Option<u16>, Error> {
377 match self {
378 Self::Commissioning(_) => Ok(None),
379 Self::Operational(q) => q.rloc_16(),
380 }
381 }
382 fn security_policy(&self) -> Result<Option<thread_diag::SecurityPolicy>, Error> {
383 match self {
384 Self::Commissioning(_) => Ok(None),
385 Self::Operational(q) => q.security_policy(),
386 }
387 }
388 fn channel_page0_mask(
389 &self,
390 f: &mut dyn FnMut(Option<&[u8]>) -> Result<(), Error>,
391 ) -> Result<(), Error> {
392 match self {
393 Self::Commissioning(_) => f(None),
394 Self::Operational(q) => q.channel_page0_mask(f),
395 }
396 }
397 fn operational_dataset_components(
398 &self,
399 f: &mut dyn FnMut(Option<&thread_diag::OperationalDatasetComponents>) -> Result<(), Error>,
400 ) -> Result<(), Error> {
401 match self {
402 Self::Commissioning(_) => f(None),
403 Self::Operational(q) => q.operational_dataset_components(f),
404 }
405 }
406 fn active_network_faults_list(
407 &self,
408 f: &mut dyn FnMut(thread_diag::NetworkFaultEnum) -> Result<(), Error>,
409 ) -> Result<(), Error> {
410 match self {
411 Self::Commissioning(_) => Ok(()),
412 Self::Operational(q) => q.active_network_faults_list(f),
413 }
414 }
415}
416
417impl<T, E> Default for WirelessBle<T, E>
418where
419 T: WirelessNetwork,
420 E: Embedding,
421{
422 fn default() -> Self {
423 Self::new()
424 }
425}
426
427impl<T, E> Sealed for WirelessBle<T, E>
428where
429 T: WirelessNetwork,
430 E: Embedding,
431{
432}
433
434impl<T, E> Network for WirelessBle<T, E>
435where
436 T: WirelessNetwork,
437 E: Embedding,
438{
439 const INIT: Self = Self::new();
440
441 type Embedding<'a>
442 = E
443 where
444 Self: 'a;
445
446 type Networks = WirelessNetworks<MAX_WIRELESS_NETWORKS, T>;
448
449 const NETWORKS: Self::Networks = WirelessNetworks::new();
450
451 fn init() -> impl Init<Self> {
452 WirelessBle::init()
453 }
454
455 fn init_networks() -> impl Init<Self::Networks> {
456 WirelessNetworks::init()
457 }
458
459 fn discovery_capabilities(&self) -> DiscoveryCapabilities {
460 DiscoveryCapabilities::BLE
461 }
462
463 fn embedding(&self) -> &Self::Embedding<'_> {
464 &self.embedding
465 }
466}
467
468impl<const B: usize, T, E> MatterStack<'_, B, WirelessBle<T, E>>
469where
470 T: WirelessNetwork,
471 E: Embedding,
472{
473 pub async fn reset<C, H, S>(&mut self, crypto: C, handler: H, store: S) -> Result<(), Error>
479 where
480 C: Crypto,
481 H: DataModel,
482 S: KvBlobStore,
483 {
484 let kv = self.matter.kv(store);
485
486 self.matter.factory_reset(&kv)?;
487
488 self.im(
495 crypto,
496 handler,
497 &kv,
498 NoopWirelessNetCtl::new(NetworkType::Ethernet),
499 )
500 .factory_reset()
501 .await
502 }
503
504 pub async fn startup<C, S>(&mut self, crypto: C, store: S) -> Result<(), Error>
515 where
516 C: Crypto,
517 S: KvBlobStore,
518 {
519 let kv = self.matter.kv(store);
520
521 self.matter.startup(&kv)?;
522
523 if !self.matter().has_fabrics() {
524 info!("Device is not commissioned yet, opening commissioning window...");
525
526 self.open_basic_comm_window(crypto, &DummyAttrNotifier)?;
527 } else {
528 info!("Device is already commissioned");
529 }
530
531 Ok(())
532 }
533
534 async fn run_net_coex<C, S, N, D, G>(
541 &self,
542 crypto: C,
543 net_stack: S,
544 netif: N,
545 mut mdns: D,
546 mut gatt: G,
547 ) -> Result<(), Error>
548 where
549 C: Crypto,
550 S: NetStack,
551 N: NetifDiag + NetChangeNotif,
552 D: Mdns,
553 G: GattPeripheral,
554 {
555 self.run_btp_coex(&crypto, &net_stack, &netif, &mut mdns, &mut gatt)
556 .await
557 }
558
559 async fn run_btp_coex<C, S, N, D, P>(
560 &self,
561 crypto: C,
562 net_stack: S,
563 netif: N,
564 mut mdns: D,
565 peripheral: P,
566 ) -> Result<(), Error>
567 where
568 C: Crypto,
569 S: NetStack,
570 N: NetifDiag + NetChangeNotif,
571 D: Mdns,
572 P: GattPeripheral,
573 {
574 info!("BLE driver started");
575
576 info!("Running in concurrent commissioning mode (BLE and Wireless)");
577
578 let adv_data = AdvData::new(
579 self.matter().dev_det(),
580 self.matter().dev_comm().discriminator,
581 );
582
583 let mut btp_task = pin_alloc!(
584 self.bump,
585 self.run_gatt_while_ble_commissionable(peripheral, &adv_data)
586 );
587
588 let mut net_task = pin_alloc!(
589 self.bump,
590 self.run_oper_net(
591 &crypto,
592 &net_stack,
593 0, core::future::pending(),
595 Some((&self.network.btp, &self.network.btp))
596 )
597 );
598
599 let mut mdns_task = pin_alloc!(
600 self.bump,
601 self.run_oper_netif_mdns(&crypto, &net_stack, &netif, &mut mdns)
602 );
603
604 select3(&mut btp_task, &mut net_task, &mut mdns_task)
605 .coalesce()
606 .await
607 }
608
609 async fn run_btp<C, P>(&self, crypto: C, mut peripheral: P) -> Result<(), Error>
610 where
611 C: Crypto,
612 P: GattPeripheral,
613 {
614 info!("BLE driver started");
615
616 info!("Running in non-concurrent commissioning mode (BLE only)");
617
618 let adv_data = AdvData::new(
619 self.matter().dev_det(),
620 self.matter().dev_comm().discriminator,
621 );
622
623 let mut btp_task = pin_alloc!(
624 self.bump,
625 peripheral.run(&self.network.btp, "BT", &adv_data)
626 );
627
628 let mut net_task =
629 pin!(self.run_transport_net(&crypto, &self.network.btp, &self.network.btp, NoNetwork));
630
631 let mut comm_window_task = pin!(async {
636 self.wait_comm_window(|state| !state.is_open_on_all_transports())
637 .await;
638
639 Ok(())
640 });
641
642 let mut oper_net_act_task = pin!(async {
643 NetCtlState::wait_prov_ready(&self.network.net_state, &self.network.btp).await;
644
645 embassy_time::Timer::after(embassy_time::Duration::from_secs(2)).await;
650
651 Ok(())
652 });
653
654 select4(
655 &mut btp_task,
656 &mut net_task,
657 &mut oper_net_act_task,
658 &mut comm_window_task,
659 )
660 .coalesce()
661 .await
662 }
663
664 async fn run_gatt_while_ble_commissionable<P>(
668 &self,
669 mut peripheral: P,
670 adv_data: &AdvData,
671 ) -> Result<(), Error>
672 where
673 P: GattPeripheral,
674 {
675 loop {
676 let state = self.wait_comm_window(CommWindowState::is_open).await;
685
686 if !state.is_open_on_all_transports() {
687 info!("Commissioning window opened over CASE; BLE peripheral not started");
688
689 self.wait_comm_window(|state| !state.is_open()).await;
690
691 continue;
692 }
693
694 info!("Commissioning window opened; BLE peripheral started");
695
696 {
697 let mut peripheral_task = pin!(peripheral.run(&self.network.btp, "BT", adv_data));
698 let mut closed_task = pin!(async {
699 self.wait_comm_window(|state| !state.is_open()).await;
700 Ok(())
701 });
702
703 select(&mut peripheral_task, &mut closed_task)
704 .coalesce()
705 .await?;
706 } info!("Commissioning window closed; BLE peripheral stopped");
709 }
710 }
711
712 async fn wait_next_comm_window(&self) {
723 self.wait_comm_window(|state| !state.is_open_on_all_transports())
724 .await;
725 self.wait_comm_window(CommWindowState::is_open_on_all_transports)
726 .await;
727 }
728
729 fn reset_net_ctl_state(&self) {
739 self.network.net_state.lock(|state| {
740 let mut state = state.borrow_mut();
741
742 state.network_id.clear();
743 state.networking_status = None;
744 state.connect_error_value = None;
745 });
746 }
747
748 async fn wait_comm_window<F>(&self, until: F) -> CommWindowState
757 where
758 F: Fn(&CommWindowState) -> bool,
759 {
760 const POLL_INTERVAL_SECS: u64 = 2;
761
762 loop {
763 let state = self.matter().comm_window_state();
764
765 if until(&state) {
766 break state;
767 }
768
769 embassy_time::Timer::after(embassy_time::Duration::from_secs(POLL_INTERVAL_SECS)).await;
770 }
771 }
772}
773
774pub struct PreexistingWireless<S, N, C, M, G> {
780 pub(crate) net_stack: S,
781 pub(crate) netif: N,
782 pub(crate) net_ctl: C,
783 pub(crate) mdns: M,
784 pub(crate) gatt: G,
785}
786
787impl<S, N, C, M, G> PreexistingWireless<S, N, C, M, G> {
788 pub const fn new(net_stack: S, netif: N, net_ctl: C, mdns: M, gatt: G) -> Self {
791 Self {
792 net_stack,
793 netif,
794 net_ctl,
795 mdns,
796 gatt,
797 }
798 }
799}
800
801pub(crate) struct MatterStackWirelessTask<'a, const B: usize, T, E, C, H, K, U, Q>
802where
803 T: WirelessNetwork,
804 E: Embedding,
805{
806 stack: &'a MatterStack<'a, B, WirelessBle<T, E>>,
807 crypto: C,
808 handler: H,
809 kv: K,
810 user_task: U,
811 _net_ctl: PhantomData<fn() -> Q>,
812}