1use core::fmt::{self, Debug};
21use core::future::{ready, Future};
22
23use crate::dm::clusters::gen_comm::GenCommHandler;
24use crate::dm::networks::wireless::{Thread, ThreadTLV, MAX_WIRELESS_NETWORK_ID_LEN};
25use crate::dm::networks::NetChangeNotif;
26use crate::dm::{ArrayAttributeRead, Cluster, Dataver, InvokeContext, ReadContext, WriteContext};
27use crate::error::{Error, ErrorCode};
28use crate::persist::{Persist, NETWORKS_KEY};
29use crate::tlv::{
30 Nullable, NullableBuilder, Octets, OctetsBuilder, TLVBuilder, TLVBuilderParent, TLVWrite,
31 ToTLVArrayBuilder, ToTLVBuilder,
32};
33use crate::utils::cell::RefCell;
34use crate::utils::init::{init, Init};
35use crate::utils::sync::blocking::Mutex;
36use crate::utils::sync::{DynBase, Notification};
37use crate::with;
38
39pub use crate::dm::clusters::decl::network_commissioning::*;
40pub use crate::dm::clusters::groups;
41
42#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
44#[cfg_attr(feature = "defmt", derive(defmt::Format))]
45pub enum NetworkType {
46 Ethernet,
47 Wifi,
48 Thread,
49}
50
51impl NetworkType {
52 pub const fn cluster(&self) -> Cluster<'static> {
54 match self {
55 Self::Ethernet => FULL_CLUSTER
56 .with_features(Feature::ETHERNET_NETWORK_INTERFACE.bits())
57 .with_attrs(with!(required))
58 .with_cmds(with!()),
59 Self::Wifi => FULL_CLUSTER
60 .with_features(Feature::WI_FI_NETWORK_INTERFACE.bits())
61 .with_attrs(with!(required; AttributeId::ScanMaxTimeSeconds | AttributeId::ConnectMaxTimeSeconds | AttributeId::SupportedWiFiBands))
62 .with_cmds(with!(CommandId::AddOrUpdateWiFiNetwork | CommandId::ScanNetworks | CommandId::RemoveNetwork | CommandId::ConnectNetwork | CommandId::ReorderNetwork)),
63 Self::Thread => FULL_CLUSTER
64 .with_features(Feature::THREAD_NETWORK_INTERFACE.bits())
65 .with_attrs(with!(required; AttributeId::ScanMaxTimeSeconds | AttributeId::ConnectMaxTimeSeconds | AttributeId::ThreadVersion | AttributeId::SupportedThreadFeatures))
66 .with_cmds(with!(CommandId::AddOrUpdateThreadNetwork | CommandId::ScanNetworks | CommandId::RemoveNetwork | CommandId::ConnectNetwork | CommandId::ReorderNetwork)),
67 }
68 }
69}
70
71#[derive(Debug, Clone, Eq, PartialEq, Hash)]
73#[cfg_attr(feature = "defmt", derive(defmt::Format))]
74pub struct NetworkInfo<'a> {
75 pub network_id: &'a [u8],
77 pub connected: bool,
79}
80
81impl NetworkInfo<'_> {
82 fn read_into<P: TLVBuilderParent>(
84 &self,
85 builder: NetworkInfoStructBuilder<P>,
86 ) -> Result<P, Error> {
87 builder
88 .network_id(Octets::new(self.network_id))?
89 .connected(self.connected)?
90 .network_identifier(None)?
91 .client_identifier(None)?
92 .end()
93 }
94}
95
96#[derive(Debug, Clone, Eq, PartialEq, Hash)]
98#[cfg_attr(feature = "defmt", derive(defmt::Format))]
99pub enum NetworkScanInfo<'a> {
100 Wifi {
102 security: WiFiSecurityBitmap,
103 ssid: &'a [u8],
104 bssid: &'a [u8],
105 channel: u16,
106 band: WiFiBandEnum,
107 rssi: i8,
108 },
109 Thread {
111 pan_id: u16,
112 ext_pan_id: u64,
113 network_name: &'a str,
114 channel: u16,
115 version: u8,
116 ext_addr: &'a [u8],
117 rssi: i8,
118 lqi: u8,
119 },
120}
121
122impl NetworkScanInfo<'_> {
123 pub fn wifi_read_into<P: TLVBuilderParent>(
126 &self,
127 builder: WiFiInterfaceScanResultStructBuilder<P>,
128 ) -> Result<P, Error> {
129 let NetworkScanInfo::Wifi {
130 security,
131 ssid,
132 bssid,
133 channel,
134 band,
135 rssi,
136 } = self
137 else {
138 panic!("Wifi scan info expected");
139 };
140
141 builder
142 .security(*security)?
143 .ssid(Octets::new(ssid))?
144 .bssid(Octets::new(bssid))?
145 .channel(*channel)?
146 .wi_fi_band(*band)?
147 .rssi(*rssi)?
148 .end()
149 }
150
151 pub fn thread_read_into<P: TLVBuilderParent>(
154 &self,
155 builder: ThreadInterfaceScanResultStructBuilder<P>,
156 ) -> Result<P, Error> {
157 let NetworkScanInfo::Thread {
158 pan_id,
159 ext_pan_id: extended_pan_id,
160 network_name,
161 channel,
162 version,
163 ext_addr,
164 rssi,
165 lqi,
166 } = self
167 else {
168 panic!("Thread scan info expected");
169 };
170
171 builder
172 .pan_id(*pan_id)?
173 .extended_pan_id(*extended_pan_id)?
174 .network_name(network_name)?
175 .channel(*channel)?
176 .version(*version)?
177 .extended_address(Octets::new(ext_addr))?
178 .rssi(*rssi)?
179 .lqi(*lqi)?
180 .end()
181 }
182}
183
184#[derive(Debug, Clone, Eq, PartialEq, Hash)]
186pub enum WirelessCreds<'a> {
187 Wifi { ssid: &'a [u8], pass: &'a [u8] },
189 Thread { dataset_tlv: &'a [u8] },
191}
192
193impl WirelessCreds<'_> {
194 pub fn id(&self) -> Result<&[u8], Error> {
198 match self {
199 WirelessCreds::Wifi { ssid, .. } => Ok(ssid),
200 WirelessCreds::Thread { dataset_tlv } => Thread::dataset_ext_pan_id(dataset_tlv),
201 }
202 }
203
204 pub fn check_match(&self, net_type: NetworkType) -> Result<(), Error> {
206 match self {
207 WirelessCreds::Wifi { .. } if matches!(net_type, NetworkType::Wifi) => Ok(()),
208 WirelessCreds::Thread { .. } if matches!(net_type, NetworkType::Thread) => Ok(()),
209 _ => Err(ErrorCode::InvalidAction.into()),
210 }
211 }
212}
213
214impl fmt::Display for WirelessCreds<'_> {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 match self {
217 WirelessCreds::Wifi { ssid, .. } => write!(
218 f,
219 "SSID({})",
220 core::str::from_utf8(ssid).ok().unwrap_or("???")
221 ),
222 WirelessCreds::Thread { dataset_tlv } => write!(
223 f,
224 "ExtEpanId({:?})",
225 ThreadTLV::new(dataset_tlv).ext_pan_id().ok().unwrap_or(&[])
226 ),
227 }
228 }
229}
230
231#[cfg(feature = "defmt")]
232impl defmt::Format for WirelessCreds<'_> {
233 fn format(&self, fmt: defmt::Formatter) {
234 match self {
235 WirelessCreds::Wifi { ssid, .. } => defmt::write!(
236 fmt,
237 "SSID({})",
238 core::str::from_utf8(ssid).ok().unwrap_or("???")
239 ),
240 WirelessCreds::Thread { dataset_tlv } => defmt::write!(
241 fmt,
242 "ExtEpanId({:?})",
243 ThreadTLV::new(dataset_tlv).ext_pan_id().ok().unwrap_or(&[])
244 ),
245 }
246 }
247}
248
249#[derive(Debug)]
251#[cfg_attr(feature = "defmt", derive(defmt::Format))]
252pub enum NetworksError {
253 NetworkIdNotFound,
254 DuplicateNetworkId,
255 OutOfRange,
256 BoundsExceeded,
257 Other(Error),
258}
259
260impl From<Error> for NetworksError {
261 fn from(err: Error) -> Self {
262 NetworksError::Other(err)
263 }
264}
265
266#[derive(Debug)]
268#[cfg_attr(feature = "defmt", derive(defmt::Format))]
269pub enum NetCtlError {
270 NetworkNotFound,
271 UnsupportedSecurity,
272 AuthFailure,
273 OtherConnectionFailure,
274 IpBindFailed,
275 IpV6Failed,
276 Other(Error),
277}
278
279impl From<Error> for NetCtlError {
280 fn from(err: Error) -> Self {
281 NetCtlError::Other(err)
282 }
283}
284
285impl NetworkCommissioningStatusEnum {
286 pub fn map<T>(
289 result: Result<T, NetworksError>,
290 ) -> Result<(NetworkCommissioningStatusEnum, Option<i32>, Option<T>), Error> {
291 if let Some((status, err_code)) = NetworkCommissioningStatusEnum::map_status(&result) {
292 Ok((status, err_code, result.ok()))
293 } else {
294 match result {
295 Err(NetworksError::Other(e)) => Err(e),
296 _ => unreachable!(),
297 }
298 }
299 }
300
301 pub fn map_status<T>(
304 result: &Result<T, NetworksError>,
305 ) -> Option<(NetworkCommissioningStatusEnum, Option<i32>)> {
306 match result {
307 Ok(_) => Some((NetworkCommissioningStatusEnum::Success, None)),
308 Err(NetworksError::NetworkIdNotFound) => {
309 Some((NetworkCommissioningStatusEnum::NetworkIDNotFound, None))
310 }
311 Err(NetworksError::DuplicateNetworkId) => {
312 Some((NetworkCommissioningStatusEnum::DuplicateNetworkID, None))
313 }
314 Err(NetworksError::OutOfRange) => {
315 Some((NetworkCommissioningStatusEnum::OutOfRange, None))
316 }
317 Err(NetworksError::BoundsExceeded) => {
318 Some((NetworkCommissioningStatusEnum::BoundsExceeded, None))
319 }
320 Err(NetworksError::Other(_)) => None,
321 }
322 }
323
324 pub fn map_ctl<T>(
327 result: Result<T, NetCtlError>,
328 ) -> Result<(NetworkCommissioningStatusEnum, Option<i32>, Option<T>), Error> {
329 if let Some((status, err_code)) = NetworkCommissioningStatusEnum::map_ctl_status(&result) {
330 Ok((status, err_code, result.ok()))
331 } else {
332 match result {
333 Err(NetCtlError::Other(e)) => Err(e),
334 _ => unreachable!(),
335 }
336 }
337 }
338
339 pub fn map_ctl_status<T>(
342 result: &Result<T, NetCtlError>,
343 ) -> Option<(NetworkCommissioningStatusEnum, Option<i32>)> {
344 match result {
345 Ok(_) => Some((NetworkCommissioningStatusEnum::Success, None)),
346 Err(NetCtlError::UnsupportedSecurity) => {
347 Some((NetworkCommissioningStatusEnum::UnsupportedSecurity, None))
348 }
349 Err(NetCtlError::AuthFailure) => {
350 Some((NetworkCommissioningStatusEnum::AuthFailure, None))
351 }
352 Err(NetCtlError::IpBindFailed) => {
353 Some((NetworkCommissioningStatusEnum::IPBindFailed, None))
354 }
355 Err(NetCtlError::IpV6Failed) => {
356 Some((NetworkCommissioningStatusEnum::IPV6Failed, None))
357 }
358 Err(NetCtlError::OtherConnectionFailure) => {
359 Some((NetworkCommissioningStatusEnum::OtherConnectionFailure, None))
360 }
361 Err(NetCtlError::NetworkNotFound) => {
362 Some((NetworkCommissioningStatusEnum::NetworkNotFound, None))
363 }
364 Err(NetCtlError::Other(_)) => None,
365 }
366 }
367
368 pub fn read_into<P: TLVBuilderParent>(
370 &self,
371 index: Option<u8>,
372 builder: NetworkConfigResponseBuilder<P>,
373 ) -> Result<P, Error> {
374 builder
375 .networking_status(*self)?
376 .debug_text(None)?
377 .network_index(index)?
378 .client_identity(None)?
379 .possession_signature(None)?
380 .end()
381 }
382}
383
384pub trait Networks {
386 fn max_networks(&self) -> Result<u8, Error>;
390
391 fn networks(&self, f: &mut dyn FnMut(&NetworkInfo) -> Result<(), Error>) -> Result<(), Error>;
393
394 fn creds(
402 &self,
403 network_id: &[u8],
404 f: &mut dyn FnMut(&WirelessCreds) -> Result<(), Error>,
405 ) -> Result<u8, NetworksError>;
406
407 fn next_creds(
420 &self,
421 last_network_id: Option<&[u8]>,
422 f: &mut dyn FnMut(&WirelessCreds) -> Result<(), Error>,
423 ) -> Result<bool, Error>;
424
425 fn enabled(&self) -> Result<bool, Error>;
427
428 fn set_enabled(&mut self, enabled: bool) -> Result<(), Error>;
430
431 fn add_or_update(&mut self, creds: &WirelessCreds<'_>) -> Result<u8, NetworksError>;
439
440 fn reorder(&mut self, index: u8, network_id: &[u8]) -> Result<u8, NetworksError>;
448
449 fn remove(&mut self, network_id: &[u8]) -> Result<u8, NetworksError>;
455
456 fn commissioned(&self) -> Result<bool, Error>;
458
459 fn set_commissioned(&mut self, commissioned: bool) -> Result<(), Error>;
461
462 fn reset(&mut self) -> Result<(), Error>;
464
465 fn load(&mut self, data: &[u8]) -> Result<(), Error>;
467
468 fn save(&self, buf: &mut [u8]) -> Result<Option<usize>, Error>;
471}
472
473impl<T> Networks for &mut T
474where
475 T: Networks,
476{
477 fn max_networks(&self) -> Result<u8, Error> {
478 (**self).max_networks()
479 }
480
481 fn networks(&self, f: &mut dyn FnMut(&NetworkInfo) -> Result<(), Error>) -> Result<(), Error> {
482 (**self).networks(f)
483 }
484
485 fn creds(
486 &self,
487 network_id: &[u8],
488 f: &mut dyn FnMut(&WirelessCreds) -> Result<(), Error>,
489 ) -> Result<u8, NetworksError> {
490 (**self).creds(network_id, f)
491 }
492
493 fn next_creds(
494 &self,
495 last_network_id: Option<&[u8]>,
496 f: &mut dyn FnMut(&WirelessCreds) -> Result<(), Error>,
497 ) -> Result<bool, Error> {
498 (**self).next_creds(last_network_id, f)
499 }
500
501 fn enabled(&self) -> Result<bool, Error> {
502 (**self).enabled()
503 }
504
505 fn set_enabled(&mut self, enabled: bool) -> Result<(), Error> {
506 (*self).set_enabled(enabled)
507 }
508
509 fn add_or_update(&mut self, creds: &WirelessCreds<'_>) -> Result<u8, NetworksError> {
510 (*self).add_or_update(creds)
511 }
512
513 fn reorder(&mut self, index: u8, network_id: &[u8]) -> Result<u8, NetworksError> {
514 (*self).reorder(index, network_id)
515 }
516
517 fn remove(&mut self, network_id: &[u8]) -> Result<u8, NetworksError> {
518 (*self).remove(network_id)
519 }
520
521 fn commissioned(&self) -> Result<bool, Error> {
522 (**self).commissioned()
523 }
524
525 fn set_commissioned(&mut self, commissioned: bool) -> Result<(), Error> {
526 (**self).set_commissioned(commissioned)
527 }
528
529 fn reset(&mut self) -> Result<(), Error> {
530 (**self).reset()
531 }
532
533 fn load(&mut self, data: &[u8]) -> Result<(), Error> {
534 (**self).load(data)
535 }
536
537 fn save(&self, buf: &mut [u8]) -> Result<Option<usize>, Error> {
538 (**self).save(buf)
539 }
540}
541
542impl Networks for &mut dyn Networks {
543 fn max_networks(&self) -> Result<u8, Error> {
544 (**self).max_networks()
545 }
546
547 fn networks(&self, f: &mut dyn FnMut(&NetworkInfo) -> Result<(), Error>) -> Result<(), Error> {
548 (**self).networks(f)
549 }
550
551 fn creds(
552 &self,
553 network_id: &[u8],
554 f: &mut dyn FnMut(&WirelessCreds) -> Result<(), Error>,
555 ) -> Result<u8, NetworksError> {
556 (**self).creds(network_id, f)
557 }
558
559 fn next_creds(
560 &self,
561 last_network_id: Option<&[u8]>,
562 f: &mut dyn FnMut(&WirelessCreds) -> Result<(), Error>,
563 ) -> Result<bool, Error> {
564 (**self).next_creds(last_network_id, f)
565 }
566
567 fn enabled(&self) -> Result<bool, Error> {
568 (**self).enabled()
569 }
570
571 fn set_enabled(&mut self, enabled: bool) -> Result<(), Error> {
572 (**self).set_enabled(enabled)
573 }
574
575 fn add_or_update(&mut self, creds: &WirelessCreds<'_>) -> Result<u8, NetworksError> {
576 (**self).add_or_update(creds)
577 }
578
579 fn reorder(&mut self, index: u8, network_id: &[u8]) -> Result<u8, NetworksError> {
580 (**self).reorder(index, network_id)
581 }
582
583 fn remove(&mut self, network_id: &[u8]) -> Result<u8, NetworksError> {
584 (**self).remove(network_id)
585 }
586
587 fn commissioned(&self) -> Result<bool, Error> {
588 (**self).commissioned()
589 }
590
591 fn set_commissioned(&mut self, commissioned: bool) -> Result<(), Error> {
592 (**self).set_commissioned(commissioned)
593 }
594
595 fn reset(&mut self) -> Result<(), Error> {
596 (**self).reset()
597 }
598
599 fn load(&mut self, data: &[u8]) -> Result<(), Error> {
600 (**self).load(data)
601 }
602
603 fn save(&self, buf: &mut [u8]) -> Result<Option<usize>, Error> {
604 (**self).save(buf)
605 }
606}
607
608pub trait NetworksAccess {
609 fn access<F: FnOnce(&mut dyn Networks) -> R, R>(&self, f: F) -> R;
610}
611
612impl<T> NetworksAccess for &T
613where
614 T: NetworksAccess,
615{
616 fn access<F: FnOnce(&mut dyn Networks) -> R, R>(&self, f: F) -> R {
617 (*self).access(f)
618 }
619}
620
621pub struct DummyNetworkAccess;
622
623impl NetworksAccess for DummyNetworkAccess {
624 fn access<F: FnOnce(&mut dyn Networks) -> R, R>(&self, f: F) -> R {
625 f(&mut DummyNetworks)
626 }
627}
628
629pub struct DummyNetworks;
630
631impl Networks for DummyNetworks {
632 fn max_networks(&self) -> Result<u8, Error> {
633 Ok(0)
634 }
635
636 fn networks(&self, _f: &mut dyn FnMut(&NetworkInfo) -> Result<(), Error>) -> Result<(), Error> {
637 Ok(())
638 }
639
640 fn creds(
641 &self,
642 _network_id: &[u8],
643 _f: &mut dyn FnMut(&WirelessCreds) -> Result<(), Error>,
644 ) -> Result<u8, NetworksError> {
645 Err(NetworksError::NetworkIdNotFound)
646 }
647
648 fn next_creds(
649 &self,
650 _last_network_id: Option<&[u8]>,
651 _f: &mut dyn FnMut(&WirelessCreds) -> Result<(), Error>,
652 ) -> Result<bool, Error> {
653 Ok(false)
654 }
655
656 fn enabled(&self) -> Result<bool, Error> {
657 Ok(false)
658 }
659
660 fn set_enabled(&mut self, _enabled: bool) -> Result<(), Error> {
661 Ok(())
662 }
663
664 fn add_or_update(&mut self, _creds: &WirelessCreds<'_>) -> Result<u8, NetworksError> {
665 Err(NetworksError::Other(ErrorCode::InvalidAction.into()))
666 }
667
668 fn reorder(&mut self, _index: u8, _network_id: &[u8]) -> Result<u8, NetworksError> {
669 Err(NetworksError::Other(ErrorCode::InvalidAction.into()))
670 }
671
672 fn remove(&mut self, _network_id: &[u8]) -> Result<u8, NetworksError> {
673 Err(NetworksError::Other(ErrorCode::InvalidAction.into()))
674 }
675
676 fn commissioned(&self) -> Result<bool, Error> {
677 Ok(false)
678 }
679
680 fn set_commissioned(&mut self, _commissioned: bool) -> Result<(), Error> {
681 Ok(())
682 }
683
684 fn reset(&mut self) -> Result<(), Error> {
685 Ok(())
686 }
687
688 fn load(&mut self, _data: &[u8]) -> Result<(), Error> {
689 Ok(())
690 }
691
692 fn save(&self, _buf: &mut [u8]) -> Result<Option<usize>, Error> {
693 Ok(None)
694 }
695}
696
697pub trait NetCtl {
699 fn net_type(&self) -> NetworkType;
701
702 fn connect_max_time_seconds(&self) -> u8 {
706 30
707 }
708
709 fn scan_max_time_seconds(&self) -> u8 {
713 30
714 }
715
716 fn supported_wifi_bands<F>(&self, mut f: F) -> Result<(), Error>
722 where
723 F: FnMut(WiFiBandEnum) -> Result<(), Error>,
724 {
725 f(WiFiBandEnum::V2G4)
726 }
727
728 fn supported_thread_features(&self) -> ThreadCapabilitiesBitmap {
734 ThreadCapabilitiesBitmap::empty()
735 }
736
737 fn thread_version(&self) -> u16 {
743 4
744 }
745
746 async fn scan<F>(&self, network: Option<&[u8]>, f: F) -> Result<(), NetCtlError>
750 where
751 F: FnMut(&NetworkScanInfo) -> Result<(), Error>;
752
753 async fn connect(&self, creds: &WirelessCreds) -> Result<(), NetCtlError>;
757}
758
759impl<T> NetCtl for &T
760where
761 T: NetCtl,
762{
763 fn net_type(&self) -> NetworkType {
764 (*self).net_type()
765 }
766
767 fn connect_max_time_seconds(&self) -> u8 {
768 (*self).connect_max_time_seconds()
769 }
770
771 fn scan_max_time_seconds(&self) -> u8 {
772 (*self).scan_max_time_seconds()
773 }
774
775 fn supported_wifi_bands<F>(&self, f: F) -> Result<(), Error>
776 where
777 F: FnMut(WiFiBandEnum) -> Result<(), Error>,
778 {
779 (*self).supported_wifi_bands(f)
780 }
781
782 fn supported_thread_features(&self) -> ThreadCapabilitiesBitmap {
783 (*self).supported_thread_features()
784 }
785
786 fn thread_version(&self) -> u16 {
787 (*self).thread_version()
788 }
789
790 fn scan<F>(&self, network: Option<&[u8]>, f: F) -> impl Future<Output = Result<(), NetCtlError>>
791 where
792 F: FnMut(&NetworkScanInfo) -> Result<(), Error>,
793 {
794 (*self).scan(network, f)
795 }
796
797 fn connect(&self, creds: &WirelessCreds<'_>) -> impl Future<Output = Result<(), NetCtlError>> {
798 (*self).connect(creds)
799 }
800}
801
802pub trait NetCtlStatus {
804 fn last_networking_status(&self) -> Result<Option<NetworkCommissioningStatusEnum>, Error>;
808
809 fn last_network_id<F, R>(&self, f: F) -> Result<R, Error>
813 where
814 F: FnOnce(Option<&[u8]>) -> Result<R, Error>;
815
816 fn last_connect_error_value(&self) -> Result<Option<i32>, Error>;
820}
821
822impl<T> NetCtlStatus for &T
823where
824 T: NetCtlStatus,
825{
826 fn last_networking_status(&self) -> Result<Option<NetworkCommissioningStatusEnum>, Error> {
827 (*self).last_networking_status()
828 }
829
830 fn last_network_id<F, R>(&self, f: F) -> Result<R, Error>
831 where
832 F: FnOnce(Option<&[u8]>) -> Result<R, Error>,
833 {
834 (*self).last_network_id(f)
835 }
836
837 fn last_connect_error_value(&self) -> Result<Option<i32>, Error> {
838 (*self).last_connect_error_value()
839 }
840}
841
842pub struct SharedNetworks<N> {
844 state: Mutex<RefCell<N>>,
845 state_changed: Notification,
846}
847
848impl<N> SharedNetworks<N> {
849 pub const fn new(networks: N) -> Self {
851 Self {
852 state: Mutex::new(RefCell::new(networks)),
853 state_changed: Notification::new(),
854 }
855 }
856
857 pub fn init(networks: impl Init<N>) -> impl Init<Self> {
859 init!(Self {
860 state <- Mutex::init(RefCell::init(networks)),
861 state_changed: Notification::new(),
862 })
863 }
864
865 pub fn get_mut(&mut self) -> &mut RefCell<N> {
867 self.state.get_mut()
868 }
869
870 pub fn wait_state_changed(&self) -> impl Future<Output = ()> + '_ {
872 self.state_changed.wait()
873 }
874}
875
876impl<N> DynBase for SharedNetworks<N> where N: Send {}
877
878impl<N> NetworksAccess for SharedNetworks<N>
879where
880 N: Networks,
881{
882 fn access<F: FnOnce(&mut dyn Networks) -> R, R>(&self, f: F) -> R {
883 self.state.lock(|state| {
884 let mut networks = state.borrow_mut();
885
886 let mut instance = SharedNetworksInstance {
887 networks: &mut *networks,
888 changed: &self.state_changed,
889 };
890
891 f(&mut instance)
892 })
893 }
894}
895
896impl<N> NetChangeNotif for SharedNetworks<N> {
897 fn wait_changed(&self) -> impl Future<Output = ()> {
898 self.state_changed.wait()
899 }
900}
901
902pub struct SharedNetworksInstance<'a> {
904 networks: &'a mut dyn Networks,
905 changed: &'a Notification,
906}
907
908impl Networks for SharedNetworksInstance<'_> {
909 fn max_networks(&self) -> Result<u8, Error> {
910 self.networks.max_networks()
911 }
912
913 fn networks(&self, f: &mut dyn FnMut(&NetworkInfo) -> Result<(), Error>) -> Result<(), Error> {
914 self.networks.networks(f)
915 }
916
917 fn creds(
918 &self,
919 network_id: &[u8],
920 f: &mut dyn FnMut(&WirelessCreds) -> Result<(), Error>,
921 ) -> Result<u8, NetworksError> {
922 self.networks.creds(network_id, f)
923 }
924
925 fn next_creds(
926 &self,
927 last_network_id: Option<&[u8]>,
928 f: &mut dyn FnMut(&WirelessCreds) -> Result<(), Error>,
929 ) -> Result<bool, Error> {
930 self.networks.next_creds(last_network_id, f)
931 }
932
933 fn enabled(&self) -> Result<bool, Error> {
934 self.networks.enabled()
935 }
936
937 fn set_enabled(&mut self, enabled: bool) -> Result<(), Error> {
938 self.networks.set_enabled(enabled)?;
939
940 self.changed.notify();
941
942 Ok(())
943 }
944
945 fn add_or_update(&mut self, creds: &WirelessCreds<'_>) -> Result<u8, NetworksError> {
946 let index = self.networks.add_or_update(creds)?;
947
948 self.changed.notify();
949
950 Ok(index)
951 }
952
953 fn reorder(&mut self, index: u8, network_id: &[u8]) -> Result<u8, NetworksError> {
954 let index = self.networks.reorder(index, network_id)?;
955
956 self.changed.notify();
957
958 Ok(index)
959 }
960
961 fn remove(&mut self, network_id: &[u8]) -> Result<u8, NetworksError> {
962 let index = self.networks.remove(network_id)?;
963
964 self.changed.notify();
965
966 Ok(index)
967 }
968
969 fn commissioned(&self) -> Result<bool, Error> {
970 self.networks.commissioned()
971 }
972
973 fn set_commissioned(&mut self, commissioned: bool) -> Result<(), Error> {
974 self.networks.set_commissioned(commissioned)?;
975
976 self.changed.notify();
977
978 Ok(())
979 }
980
981 fn reset(&mut self) -> Result<(), Error> {
982 self.networks.reset()?;
983
984 self.changed.notify();
985
986 Ok(())
987 }
988
989 fn load(&mut self, data: &[u8]) -> Result<(), Error> {
990 self.networks.load(data)
991 }
992
993 fn save(&self, buf: &mut [u8]) -> Result<Option<usize>, Error> {
994 let len = self.networks.save(buf)?;
995
996 Ok(len)
997 }
998}
999
1000#[derive(Clone)]
1002pub struct NetCommHandler<T> {
1003 dataver: Dataver,
1004 net_ctl: T,
1005}
1006
1007impl<T> NetCommHandler<T> {
1008 pub const fn new(dataver: Dataver, net_ctl: T) -> Self {
1010 Self { dataver, net_ctl }
1011 }
1012
1013 pub const fn adapt(self) -> HandlerAsyncAdaptor<Self> {
1015 HandlerAsyncAdaptor(self)
1016 }
1017}
1018
1019impl<T> ClusterAsyncHandler for NetCommHandler<T>
1020where
1021 T: NetCtl + NetCtlStatus,
1022{
1023 const CLUSTER: Cluster<'static> = NetworkType::Ethernet.cluster(); fn dataver(&self) -> u32 {
1026 self.dataver.get()
1027 }
1028
1029 fn dataver_changed(&self) {
1030 self.dataver.changed();
1031 }
1032
1033 fn max_networks(&self, ctx: impl ReadContext) -> impl Future<Output = Result<u8, Error>> {
1034 ready(ctx.networks().access(|networks| networks.max_networks()))
1035 }
1036
1037 fn connect_max_time_seconds(
1038 &self,
1039 _ctx: impl ReadContext,
1040 ) -> impl Future<Output = Result<u8, Error>> {
1041 ready(Ok(self.net_ctl.connect_max_time_seconds()))
1042 }
1043
1044 fn scan_max_time_seconds(
1045 &self,
1046 _ctx: impl ReadContext,
1047 ) -> impl Future<Output = Result<u8, Error>> {
1048 ready(Ok(self.net_ctl.scan_max_time_seconds()))
1049 }
1050
1051 fn supported_wi_fi_bands<P: TLVBuilderParent>(
1052 &self,
1053 _ctx: impl ReadContext,
1054 builder: ArrayAttributeRead<
1055 ToTLVArrayBuilder<P, WiFiBandEnum>,
1056 ToTLVBuilder<P, WiFiBandEnum>,
1057 >,
1058 ) -> impl Future<Output = Result<P, Error>> {
1059 ready(match builder {
1060 ArrayAttributeRead::ReadAll(builder) => builder.with(|builder| {
1061 let mut builder = Some(builder);
1062
1063 self.net_ctl.supported_wifi_bands(|band| {
1064 builder = Some(unwrap!(builder.take()).push(&band)?);
1065
1066 Ok(())
1067 })?;
1068
1069 unwrap!(builder.take()).end()
1070 }),
1071 ArrayAttributeRead::ReadOne(index, builder) => {
1072 let mut current = 0;
1073 let mut builder = Some(builder);
1074 let mut parent = None;
1075
1076 match self.net_ctl.supported_wifi_bands(|band| {
1077 if current == index {
1078 parent = Some(unwrap!(builder.take()).set(&band)?);
1079 }
1080
1081 current += 1;
1082
1083 Ok(())
1084 }) {
1085 Err(e) => Err(e),
1086 Ok(()) => {
1087 if let Some(parent) = parent {
1088 Ok(parent)
1089 } else {
1090 Err(ErrorCode::ConstraintError.into())
1091 }
1092 }
1093 }
1094 }
1095 ArrayAttributeRead::ReadNone(builder) => builder.end(),
1096 })
1097 }
1098
1099 fn supported_thread_features(
1100 &self,
1101 _ctx: impl ReadContext,
1102 ) -> impl Future<Output = Result<ThreadCapabilitiesBitmap, Error>> {
1103 ready(Ok(self.net_ctl.supported_thread_features()))
1104 }
1105
1106 fn thread_version(&self, _ctx: impl ReadContext) -> impl Future<Output = Result<u16, Error>> {
1107 ready(Ok(self.net_ctl.thread_version()))
1108 }
1109
1110 fn networks<P: TLVBuilderParent>(
1111 &self,
1112 ctx: impl ReadContext,
1113 builder: ArrayAttributeRead<NetworkInfoStructArrayBuilder<P>, NetworkInfoStructBuilder<P>>,
1114 ) -> impl Future<Output = Result<P, Error>> {
1115 ready(ctx.networks().access(|networks| match builder {
1116 ArrayAttributeRead::ReadAll(builder) => builder.with(|builder| {
1117 let mut builder = Some(builder);
1118
1119 networks.networks(&mut |ni| {
1120 builder = Some(ni.read_into(unwrap!(builder.take()).push()?)?);
1121
1122 Ok(())
1123 })?;
1124
1125 unwrap!(builder.take()).end()
1126 }),
1127 ArrayAttributeRead::ReadOne(index, builder) => {
1128 let mut current = 0;
1129 let mut builder = Some(builder);
1130 let mut parent = None;
1131
1132 networks.networks(&mut |ni| {
1133 if current == index {
1134 parent = Some(ni.read_into(unwrap!(builder.take()))?);
1135 }
1136
1137 current += 1;
1138
1139 Ok(())
1140 })?;
1141
1142 if let Some(parent) = parent {
1143 Ok(parent)
1144 } else {
1145 Err(ErrorCode::ConstraintError.into())
1146 }
1147 }
1148 ArrayAttributeRead::ReadNone(builder) => builder.end(),
1149 }))
1150 }
1151
1152 fn interface_enabled(
1153 &self,
1154 ctx: impl ReadContext,
1155 ) -> impl Future<Output = Result<bool, Error>> {
1156 ready(ctx.networks().access(|networks| networks.enabled()))
1157 }
1158
1159 fn last_networking_status(
1160 &self,
1161 _ctx: impl ReadContext,
1162 ) -> impl Future<Output = Result<Nullable<NetworkCommissioningStatusEnum>, Error>> {
1163 ready(self.net_ctl.last_networking_status().map(Nullable::new))
1164 }
1165
1166 fn last_network_id<P: TLVBuilderParent>(
1167 &self,
1168 _ctx: impl ReadContext,
1169 builder: NullableBuilder<P, OctetsBuilder<P>>,
1170 ) -> impl Future<Output = Result<P, Error>> {
1171 ready(self.net_ctl.last_network_id(|network_id| {
1172 if let Some(network_id) = network_id {
1173 builder.non_null()?.set(Octets::new(network_id))
1174 } else {
1175 builder.null()
1176 }
1177 }))
1178 }
1179
1180 fn last_connect_error_value(
1181 &self,
1182 _ctx: impl ReadContext,
1183 ) -> impl Future<Output = Result<Nullable<i32>, Error>> {
1184 ready(self.net_ctl.last_connect_error_value().map(Nullable::new))
1185 }
1186
1187 async fn set_interface_enabled(
1188 &self,
1189 ctx: impl WriteContext,
1190 value: bool,
1191 ) -> Result<(), Error> {
1192 let mut persist = Persist::new(ctx.kv());
1193
1194 ctx.exchange().with_state(|state| {
1195 ctx.networks().access(|networks| {
1196 networks.set_enabled(value)?;
1197
1198 if !state.failsafe.is_armed() {
1202 persist.store(NETWORKS_KEY, |buf| networks.save(buf))?;
1203 }
1204
1205 Ok(())
1206 })
1207 })?;
1208
1209 persist.run()
1210 }
1211
1212 async fn handle_scan_networks<P: TLVBuilderParent>(
1213 &self,
1214 _ctx: impl InvokeContext,
1215 request: ScanNetworksRequest<'_>,
1216 response: ScanNetworksResponseBuilder<P>,
1217 ) -> Result<P, Error> {
1218 match self.net_ctl.net_type() {
1219 NetworkType::Thread => {
1220 let mut builder = Some(response);
1221 let mut array_builder = None;
1222
1223 let (status, _, _) = NetworkCommissioningStatusEnum::map_ctl(
1224 self.net_ctl
1225 .scan(
1226 request
1227 .ssid()?
1228 .as_ref()
1229 .and_then(|ssid| ssid.as_opt_ref())
1230 .map(|ssid| ssid.0),
1231 |network| {
1232 let abuilder = if let Some(builder) = builder.take() {
1233 builder
1234 .networking_status(NetworkCommissioningStatusEnum::Success)?
1235 .debug_text(None)?
1236 .wi_fi_scan_results()?
1237 .none()
1238 .thread_scan_results()?
1239 .some()?
1240 } else {
1241 unwrap!(array_builder.take())
1242 };
1243
1244 array_builder = Some(network.thread_read_into(abuilder.push()?)?);
1245
1246 Ok(())
1247 },
1248 )
1249 .await
1250 .map(|_| 0),
1251 )?;
1252
1253 if let Some(builder) = builder {
1254 builder
1255 .networking_status(status)?
1256 .debug_text(None)?
1257 .wi_fi_scan_results()?
1258 .none()
1259 .thread_scan_results()?
1260 .none()
1261 .end()
1262 } else {
1263 unwrap!(array_builder.take()).end()?.end()
1264 }
1265 }
1266 NetworkType::Wifi => {
1267 let mut builder = Some(response);
1268 let mut array_builder = None;
1269
1270 let (status, _, _) = NetworkCommissioningStatusEnum::map_ctl(
1271 self.net_ctl
1272 .scan(
1273 request
1274 .ssid()?
1275 .as_ref()
1276 .and_then(|ssid| ssid.as_opt_ref())
1277 .map(|ssid| ssid.0),
1278 |network| {
1279 let abuilder = if let Some(builder) = builder.take() {
1280 builder
1281 .networking_status(NetworkCommissioningStatusEnum::Success)?
1282 .debug_text(None)?
1283 .wi_fi_scan_results()?
1284 .some()?
1285 } else {
1286 unwrap!(array_builder.take())
1287 };
1288
1289 array_builder = Some(network.wifi_read_into(abuilder.push()?)?);
1290
1291 Ok(())
1292 },
1293 )
1294 .await
1295 .map(|_| 0),
1296 )?;
1297
1298 if let Some(builder) = builder {
1299 builder
1300 .networking_status(status)?
1301 .debug_text(None)?
1302 .wi_fi_scan_results()?
1303 .none()
1304 .thread_scan_results()?
1305 .none()
1306 .end()
1307 } else {
1308 unwrap!(array_builder.take())
1309 .end()?
1310 .thread_scan_results()?
1311 .none()
1312 .end()
1313 }
1314 }
1315 NetworkType::Ethernet => Err(ErrorCode::InvalidAction.into()),
1316 }
1317 }
1318
1319 async fn handle_add_or_update_wi_fi_network<P: TLVBuilderParent>(
1320 &self,
1321 ctx: impl InvokeContext,
1322 request: AddOrUpdateWiFiNetworkRequest<'_>,
1323 response: NetworkConfigResponseBuilder<P>,
1324 ) -> Result<P, Error> {
1325 let (status, _, index) = NetworkCommissioningStatusEnum::map(
1326 GenCommHandler::with_armed_failsafe_ex(&ctx, |_, _| {
1327 ctx.networks().access(|networks| {
1328 let index = networks.add_or_update(&WirelessCreds::Wifi {
1329 ssid: request.ssid()?.0,
1330 pass: request.credentials()?.0,
1331 })?;
1332
1333 Ok(index)
1334 })
1335 }),
1336 )?;
1337
1338 ctx.notify_own_cluster_changed();
1340
1341 status.read_into(index, response)
1342 }
1343
1344 async fn handle_add_or_update_thread_network<P: TLVBuilderParent>(
1345 &self,
1346 ctx: impl InvokeContext,
1347 request: AddOrUpdateThreadNetworkRequest<'_>,
1348 response: NetworkConfigResponseBuilder<P>,
1349 ) -> Result<P, Error> {
1350 let (status, _, index) = NetworkCommissioningStatusEnum::map(
1351 GenCommHandler::with_armed_failsafe_ex(&ctx, |_, _| {
1352 ctx.networks().access(|networks| {
1353 let index = networks.add_or_update(&WirelessCreds::Thread {
1354 dataset_tlv: request.operational_dataset()?.0,
1355 })?;
1356
1357 Ok(index)
1358 })
1359 }),
1360 )?;
1361
1362 ctx.notify_own_cluster_changed();
1364
1365 status.read_into(index, response)
1366 }
1367
1368 async fn handle_remove_network<P: TLVBuilderParent>(
1369 &self,
1370 ctx: impl InvokeContext,
1371 request: RemoveNetworkRequest<'_>,
1372 response: NetworkConfigResponseBuilder<P>,
1373 ) -> Result<P, Error> {
1374 let (status, _, index) = NetworkCommissioningStatusEnum::map(
1375 GenCommHandler::with_armed_failsafe_ex(&ctx, |_, _| {
1376 ctx.networks().access(|networks| {
1377 let index = networks.remove(request.network_id()?.0)?;
1378
1379 Ok(index)
1380 })
1381 }),
1382 )?;
1383
1384 ctx.notify_own_cluster_changed();
1386
1387 status.read_into(index, response)
1388 }
1389
1390 async fn handle_connect_network<P: TLVBuilderParent>(
1391 &self,
1392 ctx: impl InvokeContext,
1393 request: ConnectNetworkRequest<'_>,
1394 mut response: ConnectNetworkResponseBuilder<P>,
1395 ) -> Result<P, Error> {
1396 if request.network_id()?.0.len() > MAX_WIRELESS_NETWORK_ID_LEN {
1397 return Err(ErrorCode::ConstraintError.into());
1398 }
1399
1400 let (status, err_code) = match self.net_ctl.net_type() {
1401 NetworkType::Thread => {
1402 let dataset_buf = response.writer().available_space();
1403 let mut dataset_len = 0;
1404
1405 let (mut status, mut err_code, _) = NetworkCommissioningStatusEnum::map(
1406 GenCommHandler::with_armed_failsafe_ex(&ctx, |_, _| {
1407 ctx.networks().access(|networks| {
1408 networks.creds(request.network_id()?.0, &mut |creds| {
1409 let WirelessCreds::Thread { dataset_tlv } = creds else {
1410 error!("Thread creds expected");
1411 return Err(ErrorCode::InvalidAction.into());
1412 };
1413
1414 if dataset_tlv.len() > dataset_buf.len() {
1415 error!("Dataset too large");
1416 return Err(ErrorCode::ConstraintError.into());
1417 }
1418
1419 dataset_buf[..dataset_tlv.len()].copy_from_slice(dataset_tlv);
1420 dataset_len = dataset_tlv.len();
1421
1422 Ok(())
1423 })
1424 })
1425 }),
1426 )?;
1427
1428 if matches!(status, NetworkCommissioningStatusEnum::Success) {
1429 (status, err_code, _) = NetworkCommissioningStatusEnum::map_ctl(
1430 self.net_ctl
1431 .connect(&WirelessCreds::Thread {
1432 dataset_tlv: &dataset_buf[..dataset_len],
1433 })
1434 .await,
1435 )?;
1436 }
1437
1438 (status, err_code)
1439 }
1440 NetworkType::Wifi => {
1441 let buf = response.writer().available_space();
1442 let (ssid_buf, pass_buf) = buf.split_at_mut(buf.len() / 2);
1443 let mut ssid_len = 0;
1444 let mut pass_len = 0;
1445
1446 let (mut status, mut err_code, _) = NetworkCommissioningStatusEnum::map(
1447 GenCommHandler::with_armed_failsafe_ex(&ctx, |_, _| {
1448 ctx.networks().access(|networks| {
1449 networks.creds(request.network_id()?.0, &mut |creds| {
1450 let WirelessCreds::Wifi { ssid, pass } = creds else {
1451 error!("Wifi creds expected");
1452 return Err(ErrorCode::InvalidAction.into());
1453 };
1454
1455 if ssid.len() > ssid_buf.len() {
1456 error!("SSID too large");
1457 return Err(ErrorCode::ConstraintError.into());
1458 }
1459
1460 if pass.len() > pass_buf.len() {
1461 error!("Password too large");
1462 return Err(ErrorCode::ConstraintError.into());
1463 }
1464
1465 ssid_buf[..ssid.len()].copy_from_slice(ssid);
1466 ssid_len = ssid.len();
1467 pass_buf[..pass.len()].copy_from_slice(pass);
1468 pass_len = pass.len();
1469
1470 Ok(())
1471 })
1472 })
1473 }),
1474 )?;
1475
1476 if matches!(status, NetworkCommissioningStatusEnum::Success) {
1477 (status, err_code, _) = NetworkCommissioningStatusEnum::map_ctl(
1478 self.net_ctl
1479 .connect(&WirelessCreds::Wifi {
1480 ssid: &ssid_buf[..ssid_len],
1481 pass: &pass_buf[..pass_len],
1482 })
1483 .await,
1484 )?;
1485 }
1486
1487 (status, err_code)
1488 }
1489 NetworkType::Ethernet => {
1490 return Err(ErrorCode::InvalidAction.into());
1491 }
1492 };
1493
1494 ctx.notify_own_cluster_changed();
1496
1497 response
1498 .networking_status(status)?
1499 .debug_text(None)?
1500 .error_value(Nullable::new(err_code))?
1501 .end()
1502 }
1503
1504 async fn handle_reorder_network<P: TLVBuilderParent>(
1505 &self,
1506 ctx: impl InvokeContext,
1507 request: ReorderNetworkRequest<'_>,
1508 response: NetworkConfigResponseBuilder<P>,
1509 ) -> Result<P, Error> {
1510 let (status, _, index) = NetworkCommissioningStatusEnum::map(
1511 GenCommHandler::with_armed_failsafe_ex(&ctx, |_, _| {
1512 ctx.networks().access(|networks| {
1513 let index =
1514 networks.reorder(request.network_index()? as _, request.network_id()?.0)?;
1515
1516 Ok(index)
1517 })
1518 }),
1519 )?;
1520
1521 ctx.notify_own_cluster_changed();
1523
1524 status.read_into(index, response)
1525 }
1526
1527 fn handle_query_identity<P: TLVBuilderParent>(
1528 &self,
1529 _ctx: impl InvokeContext,
1530 _request: QueryIdentityRequest<'_>,
1531 _response: QueryIdentityResponseBuilder<P>,
1532 ) -> impl Future<Output = Result<P, Error>> {
1533 ready(Err(ErrorCode::CommandNotFound.into()))
1534 }
1535}
1536
1537impl<T> Debug for NetCommHandler<T> {
1538 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1539 f.debug_struct("NetCommHandler")
1540 .field("dataver", &self.dataver.get())
1541 .finish()
1542 }
1543}
1544
1545#[cfg(feature = "defmt")]
1546impl<T> defmt::Format for NetCommHandler<T> {
1547 fn format(&self, f: defmt::Formatter) {
1548 defmt::write!(f, "NetCommHandler {{ dataver: {} }}", self.dataver.get());
1549 }
1550}