Skip to main content

rs_matter/dm/clusters/
net_comm.rs

1/*
2 *
3 *    Copyright (c) 2025-2026 Project CHIP Authors
4 *
5 *    Licensed under the Apache License, Version 2.0 (the "License");
6 *    you may not use this file except in compliance with the License.
7 *    You may obtain a copy of the License at
8 *
9 *        http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *    Unless required by applicable law or agreed to in writing, software
12 *    distributed under the License is distributed on an "AS IS" BASIS,
13 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *    See the License for the specific language governing permissions and
15 *    limitations under the License.
16 */
17
18//! This module contains the implementation of the Network Commissioning cluster and its handler.
19
20use 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/// Network type supported by the `NetCtl` implementations
43#[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    /// Return an instance of the Network Commissioning cluster meta-data for the given network type.
53    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/// Network information as returned by the `Networks` trait
72#[derive(Debug, Clone, Eq, PartialEq, Hash)]
73#[cfg_attr(feature = "defmt", derive(defmt::Format))]
74pub struct NetworkInfo<'a> {
75    /// The network ID of the network
76    pub network_id: &'a [u8],
77    /// Whether this network is currently connected
78    pub connected: bool,
79}
80
81impl NetworkInfo<'_> {
82    /// Read the network information into the given `NetworkInfoStructBuilder`.
83    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/// Network scan information as returned by the `NetCtl::scan` method
97#[derive(Debug, Clone, Eq, PartialEq, Hash)]
98#[cfg_attr(feature = "defmt", derive(defmt::Format))]
99pub enum NetworkScanInfo<'a> {
100    /// WiFi network scan information when the network type is `NetworkType::Wifi`
101    Wifi {
102        security: WiFiSecurityBitmap,
103        ssid: &'a [u8],
104        bssid: &'a [u8],
105        channel: u16,
106        band: WiFiBandEnum,
107        rssi: i8,
108    },
109    /// Thread network scan information when the network type is `NetworkType::Thread`
110    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    /// Read the network scan information into the given `NetworkScanInfoStructBuilder`.
124    /// If the network type is not `NetworkType::Wifi`, this method will panic.
125    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    /// Read the network scan information into the given `ThreadInterfaceScanResultStructBuilder`.
152    /// If the network type is not `NetworkType::Thread`, this method will panic.
153    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/// Wireless credentials used for connecting to a network
185#[derive(Debug, Clone, Eq, PartialEq, Hash)]
186pub enum WirelessCreds<'a> {
187    /// WiFi credentials
188    Wifi { ssid: &'a [u8], pass: &'a [u8] },
189    /// Thread credentials
190    Thread { dataset_tlv: &'a [u8] },
191}
192
193impl WirelessCreds<'_> {
194    /// Return the network ID of the credentials
195    /// For Wifi networks, this is the SSID
196    /// For Thread networks, this is the extended PAN ID
197    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    /// Check if the credentials match the given network type
205    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/// Network error type for the `Networks` trait
250#[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/// Network error type for the `NetCtl` trait
267#[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    /// Map the result of a network storage operation to a `NetworkCommissioningStatusEnum` if the operation
287    /// failed, or return the index of the network if it succeeded.
288    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    /// Map the result of a network storage operation to a `NetworkCommissioningStatusEnum` and error code  if the operation
302    /// failed, or return the index of the network if it succeeded.
303    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    /// Map the result of a network IO operation to a `NetworkCommissioningStatusEnum` if the operation
325    /// failed, or return the index of the network if it succeeded.
326    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    /// Map the result of a network IO operation to a `NetworkCommissioningStatusEnum` and error code  if the operation
340    /// failed, or return the index of the network if it succeeded.
341    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    /// Read the networking status and the provided optional index into the given `NetworkConfigResponseBuilder`.
369    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
384/// Trait for managing networks' credentials storage
385pub trait Networks {
386    /// Return the maximum number of networks supported by the implementation
387    ///
388    /// For `NetworkType::Ethernet` this method should always return 1
389    fn max_networks(&self) -> Result<u8, Error>;
390
391    /// Iterate over the networks recorded in the implementation and call the provided function for each network
392    fn networks(&self, f: &mut dyn FnMut(&NetworkInfo) -> Result<(), Error>) -> Result<(), Error>;
393
394    /// Get the credentials for the given network ID by calling the provided function
395    ///
396    /// For `NetworkType::Ethernet` this method should always fail with an error.
397    ///
398    /// The function will be called with the credentials for the network ID, or an error if the network ID is not found.
399    ///
400    /// Return the index of the network ID if found, or an error if not found.
401    fn creds(
402        &self,
403        network_id: &[u8],
404        f: &mut dyn FnMut(&WirelessCreds) -> Result<(), Error>,
405    ) -> Result<u8, NetworksError>;
406
407    /// Return the next credentials after the ones corresponding to the given network ID by calling the provided function
408    ///
409    /// For `NetworkType::Ethernet` this method should always fail with an error.
410    ///
411    /// If the network ID is `None` or credentials with the provided network ID cannot be found,
412    /// the first credentials will be returned.
413    ///
414    /// If the credentials corresponding to the network ID are the last ones recorded in the `Netwrks` trait implementation,
415    /// the method will wrap-over and will return the first credentials or even the same credentials again if there is only one
416    /// recorded network.
417    ///
418    /// Return `true` if the credentials were found, `false` otherwise.
419    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    /// Return whether the network interface is enabled
426    fn enabled(&self) -> Result<bool, Error>;
427
428    /// Set the network interface enabled or disabled
429    fn set_enabled(&mut self, enabled: bool) -> Result<(), Error>;
430
431    /// Add or update the credentials for the given network ID
432    ///
433    /// For `NetworkType::Ethernet` this method should always fail with an error.
434    ///
435    /// The network ID is derived from the credentials.
436    ///
437    /// Return the index of the network ID if it was added or updated, or an error if the operation failed.
438    fn add_or_update(&mut self, creds: &WirelessCreds<'_>) -> Result<u8, NetworksError>;
439
440    /// Reorder the network with the given index
441    ///
442    /// For `NetworkType::Ethernet` this method should always fail with an error.
443    ///
444    /// The index is the new index of the network ID.
445    ///
446    /// Return the index of the network ID if it was reordered, or an error if the operation failed.
447    fn reorder(&mut self, index: u8, network_id: &[u8]) -> Result<u8, NetworksError>;
448
449    /// Remove the network with the given network ID
450    ///
451    /// For `NetworkType::Ethernet` this method should always fail with an error.
452    ///
453    /// Return the index of the network ID if it was removed, or an error if the operation failed.
454    fn remove(&mut self, network_id: &[u8]) -> Result<u8, NetworksError>;
455
456    /// Return whether the network interface is commissioned
457    fn commissioned(&self) -> Result<bool, Error>;
458
459    /// Set the commissioned state of the network interface
460    fn set_commissioned(&mut self, commissioned: bool) -> Result<(), Error>;
461
462    /// Reset the networks to the initial state, removing all recorded network credentials
463    fn reset(&mut self) -> Result<(), Error>;
464
465    /// Load the networks' credentials from the given data
466    fn load(&mut self, data: &[u8]) -> Result<(), Error>;
467
468    /// Save the networks' credentials into the given buffer and return the number of bytes written
469    /// or `None` if the networks do not need persistence.
470    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
697/// Trait for managing network connectivity
698pub trait NetCtl {
699    /// Return the network type of the implementation
700    fn net_type(&self) -> NetworkType;
701
702    /// Return the maximum time in seconds for connecting to a network
703    ///
704    /// Default implementation returns 30 seconds
705    fn connect_max_time_seconds(&self) -> u8 {
706        30
707    }
708
709    /// Return the maximum time in seconds for scanning for networks
710    ///
711    /// Default implementation returns 30 seconds
712    fn scan_max_time_seconds(&self) -> u8 {
713        30
714    }
715
716    /// Return the supported WiFi bands for the implementation in the provided closure
717    ///
718    /// Default implementation returns 2.4GHz band only
719    ///
720    /// NOTE: This method is only relevant when `net_type` is `NetworkType::Wifi`
721    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    /// Return the supported Thread features for the implementation
729    ///
730    /// Default implementation returns an empty bitmap
731    ///
732    /// NOTE: This method is only relevant when `net_type` is `NetworkType::Thread`
733    fn supported_thread_features(&self) -> ThreadCapabilitiesBitmap {
734        ThreadCapabilitiesBitmap::empty()
735    }
736
737    /// Return the Thread version for the implementation
738    ///
739    /// Default implementation returns 4 (Thread 1.3.0)
740    ///
741    /// NOTE: This method is only relevant when `net_type` is `NetworkType::Thread`
742    fn thread_version(&self) -> u16 {
743        4
744    }
745
746    /// Scan for networks and call the provided function for each network found
747    ///
748    /// For `NetworkType::Ethernet` this method should always fail with an error.
749    async fn scan<F>(&self, network: Option<&[u8]>, f: F) -> Result<(), NetCtlError>
750    where
751        F: FnMut(&NetworkScanInfo) -> Result<(), Error>;
752
753    /// Connect to the network with the given credentials
754    ///
755    /// For `NetworkType::Ethernet` this method should always fail with an error.
756    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
802/// Trait for providing the status of the last `scan` / `connect` operation
803pub trait NetCtlStatus {
804    /// Return the networking status of the last scan or connect operation
805    ///
806    /// For `NetworkType::Ethernet` this method should always return `Ok(None)`
807    fn last_networking_status(&self) -> Result<Option<NetworkCommissioningStatusEnum>, Error>;
808
809    /// Return the network ID of the last connect operation
810    ///
811    /// For `NetworkType::Ethernet` this method should always return `Ok(None)`
812    fn last_network_id<F, R>(&self, f: F) -> Result<R, Error>
813    where
814        F: FnOnce(Option<&[u8]>) -> Result<R, Error>;
815
816    /// Return the error value of the last connect operation
817    ///
818    /// For `NetworkType::Ethernet` this method should always return `Ok(None)`
819    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
842/// A type providing shared access to a `Networks` implementation with change notification capabilities.
843pub struct SharedNetworks<N> {
844    state: Mutex<RefCell<N>>,
845    state_changed: Notification,
846}
847
848impl<N> SharedNetworks<N> {
849    /// Create a new instance.
850    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    /// Return an in-place initializer for the struct.
858    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    /// Get a mutable reference to the inner `Networks` implementation.
866    pub fn get_mut(&mut self) -> &mut RefCell<N> {
867        self.state.get_mut()
868    }
869
870    /// Wait for the state to change.
871    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
902/// A wrapper around a `Networks` implementation that notifies on changes to the networks state.
903pub 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/// The system implementation of a handler for the Network Commissioning Matter cluster.
1001#[derive(Clone)]
1002pub struct NetCommHandler<T> {
1003    dataver: Dataver,
1004    net_ctl: T,
1005}
1006
1007impl<T> NetCommHandler<T> {
1008    /// Create a new instance of `NetCommHandler` with the given `Dataver` and `NetCtl`.
1009    pub const fn new(dataver: Dataver, net_ctl: T) -> Self {
1010        Self { dataver, net_ctl }
1011    }
1012
1013    /// Adapt the handler instance to the generic `rs-matter` `AsyncHandler` trait
1014    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(); // TODO
1024
1025    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                // NOTE: Not sure this is a spec-compliant behavor:
1199                // If the failsafe is armed for _any_ fabric, we'll NOT persist the network changes until commissioning is complete.
1200                // And we'll LOSE those changes if the failsafe times out before commissioning completes.
1201                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        // Networks list mutated
1339        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        // Networks list mutated
1363        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        // Networks list mutated
1385        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        // LastNetworkingStatus / LastNetworkID / LastConnectErrorValue mutated
1495        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        // Networks order mutated
1522        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}