/* * * Copyright (c) 2022 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @file * Provides the wrapper for nRF WiFi API */ #include "WiFiManager.h" #include #include #include #include #include #include #include #include #include extern "C" { #include #include #include #include // extern function to obtain bssid from status buffer // It is defined in zephyr/subsys/net/ip/utils.c extern char * net_sprint_ll_addr_buf(const uint8_t * ll, uint8_t ll_len, char * buf, int buflen); } namespace chip { namespace DeviceLayer { namespace { app::Clusters::NetworkCommissioning::WiFiBandEnum ConvertBandEnum(uint8_t band) { switch (band) { case WIFI_FREQ_BAND_2_4_GHZ: return app::Clusters::NetworkCommissioning::WiFiBandEnum::k2g4; case WIFI_FREQ_BAND_5_GHZ: return app::Clusters::NetworkCommissioning::WiFiBandEnum::k5g; case WIFI_FREQ_BAND_6_GHZ: return app::Clusters::NetworkCommissioning::WiFiBandEnum::k6g; default: return app::Clusters::NetworkCommissioning::WiFiBandEnum::kUnknownEnumValue; } } NetworkCommissioning::WiFiScanResponse ToScanResponse(const wifi_scan_result * result) { NetworkCommissioning::WiFiScanResponse response = {}; if (result != nullptr) { static_assert(sizeof(response.ssid) == sizeof(result->ssid), "SSID length mismatch"); static_assert(sizeof(response.bssid) == sizeof(result->mac), "BSSID length mismatch"); // TODO: Distinguish WPA versions response.security.Set(result->security == WIFI_SECURITY_TYPE_PSK ? NetworkCommissioning::WiFiSecurity::kWpaPersonal : NetworkCommissioning::WiFiSecurity::kUnencrypted); response.channel = result->channel; response.rssi = result->rssi; response.ssidLen = result->ssid_length; response.wiFiBand = ConvertBandEnum(result->band); memcpy(response.ssid, result->ssid, result->ssid_length); // TODO: MAC/BSSID is not filled by the Wi-Fi driver memcpy(response.bssid, result->mac, result->mac_length); } return response; } // Matter expectations towards Wi-Fi version codes are unaligned with // what wpa_supplicant provides. This function maps supplicant codes // to the ones defined in the Matter spec (11.14.5.2. WiFiVersionEnum) app::Clusters::WiFiNetworkDiagnostics::WiFiVersionEnum MapToMatterWiFiVersionCode(wifi_link_mode wifiVersion) { using app::Clusters::WiFiNetworkDiagnostics::WiFiVersionEnum; if (wifiVersion < WIFI_1 || wifiVersion > WIFI_6E) { ChipLogError(DeviceLayer, "Unsupported Wi-Fi version detected"); return WiFiVersionEnum::kA; // let's return 'a' by default } switch (wifiVersion) { case WIFI_1: return WiFiVersionEnum::kB; case WIFI_2: return WiFiVersionEnum::kA; case WIFI_6E: return WiFiVersionEnum::kAx; // treat as 802.11ax default: break; } return static_cast(wifiVersion - 1); } // Matter expectations towards Wi-Fi security type codes are unaligned with // what wpa_supplicant provides. This function maps supplicant codes // to the ones defined in the Matter spec (11.14.3.1. SecurityType enum) app::Clusters::WiFiNetworkDiagnostics::SecurityTypeEnum MapToMatterSecurityType(wifi_security_type securityType) { using app::Clusters::WiFiNetworkDiagnostics::SecurityTypeEnum; switch (securityType) { case WIFI_SECURITY_TYPE_NONE: return SecurityTypeEnum::kNone; case WIFI_SECURITY_TYPE_PSK: case WIFI_SECURITY_TYPE_PSK_SHA256: return SecurityTypeEnum::kWpa2; case WIFI_SECURITY_TYPE_SAE: return SecurityTypeEnum::kWpa3; default: break; } return SecurityTypeEnum::kUnspecified; } } // namespace const Map WiFiManager::sStatusMap({ { WIFI_STATE_DISCONNECTED, WiFiManager::StationStatus::DISCONNECTED }, { WIFI_STATE_INTERFACE_DISABLED, WiFiManager::StationStatus::DISABLED }, { WIFI_STATE_INACTIVE, WiFiManager::StationStatus::DISABLED }, { WIFI_STATE_SCANNING, WiFiManager::StationStatus::SCANNING }, { WIFI_STATE_AUTHENTICATING, WiFiManager::StationStatus::CONNECTING }, { WIFI_STATE_ASSOCIATING, WiFiManager::StationStatus::CONNECTING }, { WIFI_STATE_ASSOCIATED, WiFiManager::StationStatus::CONNECTED }, { WIFI_STATE_4WAY_HANDSHAKE, WiFiManager::StationStatus::PROVISIONING }, { WIFI_STATE_GROUP_HANDSHAKE, WiFiManager::StationStatus::PROVISIONING }, { WIFI_STATE_COMPLETED, WiFiManager::StationStatus::FULLY_PROVISIONED } }); const Map WiFiManager::sEventHandlerMap({ { NET_EVENT_WIFI_SCAN_RESULT, WiFiManager::ScanResultHandler }, { NET_EVENT_WIFI_SCAN_DONE, WiFiManager::ScanDoneHandler }, { NET_EVENT_WIFI_CONNECT_RESULT, WiFiManager::ConnectHandler }, { NET_EVENT_WIFI_DISCONNECT_RESULT, WiFiManager::DisconnectHandler }, { NET_EVENT_WIFI_DISCONNECT_COMPLETE, WiFiManager::DisconnectHandler }, }); void WiFiManager::WifiMgmtEventHandler(net_mgmt_event_callback * cb, uint32_t mgmtEvent, net_if * iface) { if (iface == Instance().mNetIf) { Platform::UniquePtr eventData(new uint8_t[cb->info_length]); VerifyOrReturn(eventData); memcpy(eventData.get(), cb->info, cb->info_length); sEventHandlerMap[mgmtEvent](std::move(eventData), cb->info_length); } } void WiFiManager::IPv6MgmtEventHandler(net_mgmt_event_callback * cb, uint32_t mgmtEvent, net_if * iface) { if (((mgmtEvent == NET_EVENT_IPV6_ADDR_ADD) || (mgmtEvent == NET_EVENT_IPV6_ADDR_DEL)) && cb->info) { IPv6AddressChangeHandler(cb->info); } } CHIP_ERROR WiFiManager::Init() { mNetIf = InetUtils::GetWiFiInterface(); VerifyOrReturnError(mNetIf != nullptr, INET_ERROR_UNKNOWN_INTERFACE); net_mgmt_init_event_callback(&mWiFiMgmtClbk, WifiMgmtEventHandler, kWifiManagementEvents); net_mgmt_init_event_callback(&mIPv6MgmtClbk, IPv6MgmtEventHandler, kIPv6ManagementEvents); net_mgmt_add_event_callback(&mWiFiMgmtClbk); net_mgmt_add_event_callback(&mIPv6MgmtClbk); ChipLogDetail(DeviceLayer, "WiFiManager has been initialized"); return CHIP_NO_ERROR; } CHIP_ERROR WiFiManager::Scan(const ByteSpan & ssid, ScanResultCallback resultCallback, ScanDoneCallback doneCallback, bool internalScan) { mInternalScan = internalScan; mScanResultCallback = resultCallback; mScanDoneCallback = doneCallback; mCachedWiFiState = mWiFiState; mWiFiState = WIFI_STATE_SCANNING; mSsidFound = false; wifi_scan_params * scanParams{ nullptr }; size_t scanParamsSize{ 0 }; if (!ssid.empty()) { /* We must assume that the ssid is handled as a NULL-terminated string. Note that the mScanSsidBuffer is initialized with zeros. */ VerifyOrReturnError(ssid.size() < sizeof(mScanSsidBuffer), CHIP_ERROR_INVALID_ARGUMENT); memcpy(mScanSsidBuffer, ssid.data(), ssid.size()); mScanSsidBuffer[ssid.size()] = 0; // indicate the end of ssid string mScanParams.ssids[0] = mScanSsidBuffer; mScanParams.ssids[1] = nullptr; // indicate the end of ssids list scanParams = &mScanParams; scanParamsSize = sizeof(*scanParams); } if (0 != net_mgmt(NET_REQUEST_WIFI_SCAN, mNetIf, scanParams, scanParamsSize)) { ChipLogError(DeviceLayer, "Scan request failed"); return CHIP_ERROR_INTERNAL; } ChipLogDetail(DeviceLayer, "WiFi scanning started..."); return CHIP_NO_ERROR; } CHIP_ERROR WiFiManager::ClearStationProvisioningData() { mWiFiParams.mRssi = std::numeric_limits::min(); memset(&mWiFiParams.mParams, 0, sizeof(mWiFiParams.mParams)); return CHIP_NO_ERROR; } CHIP_ERROR WiFiManager::Connect(const ByteSpan & ssid, const ByteSpan & credentials, const ConnectionHandling & handling) { ChipLogDetail(DeviceLayer, "Connecting to WiFi network: %*s", ssid.size(), ssid.data()); mHandling = handling; mWiFiState = WIFI_STATE_ASSOCIATING; // Store SSID and credentials and perform the scan to detect the security mode supported by the AP. // Zephyr WiFi connect request will be issued in the callback when we have the SSID match. mWantedNetwork.Erase(); memcpy(mWantedNetwork.ssid, ssid.data(), ssid.size()); memcpy(mWantedNetwork.pass, credentials.data(), credentials.size()); mWantedNetwork.ssidLen = ssid.size(); mWantedNetwork.passLen = credentials.size(); return Scan(ssid, nullptr, nullptr, true /* internal scan */); } CHIP_ERROR WiFiManager::Disconnect() { mApplicationDisconnectRequested = true; int status = net_mgmt(NET_REQUEST_WIFI_DISCONNECT, mNetIf, NULL, 0); if (status) { mApplicationDisconnectRequested = false; if (status == -EALREADY) { ChipLogDetail(DeviceLayer, "Already disconnected"); } else { ChipLogDetail(DeviceLayer, "Disconnect request failed"); return CHIP_ERROR_INTERNAL; } } else { ChipLogDetail(DeviceLayer, "Disconnect requested"); } return CHIP_NO_ERROR; } CHIP_ERROR WiFiManager::GetWiFiInfo(WiFiInfo & info) const { wifi_iface_status status = { 0 }; if (net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, mNetIf, &status, sizeof(wifi_iface_status))) { ChipLogError(DeviceLayer, "Status request failed"); return CHIP_ERROR_INTERNAL; } if (status.state >= WIFI_STATE_ASSOCIATED) { info.mSecurityType = MapToMatterSecurityType(status.security); info.mWiFiVersion = MapToMatterWiFiVersionCode(status.link_mode); info.mRssi = static_cast(status.rssi); info.mChannel = static_cast(status.channel); info.mSsidLen = status.ssid_len; memcpy(info.mSsid, status.ssid, status.ssid_len); memcpy(info.mBssId, status.bssid, sizeof(status.bssid)); return CHIP_NO_ERROR; } return CHIP_ERROR_INTERNAL; } CHIP_ERROR WiFiManager::GetNetworkStatistics(NetworkStatistics & stats) const { net_stats_wifi data{}; net_mgmt(NET_REQUEST_STATS_GET_WIFI, mNetIf, &data, sizeof(data)); stats.mPacketMulticastRxCount = data.multicast.rx; stats.mPacketMulticastTxCount = data.multicast.tx; stats.mPacketUnicastRxCount = data.unicast.rx; stats.mPacketUnicastTxCount = data.unicast.tx; stats.mBeaconsSuccessCount = data.sta_mgmt.beacons_rx; stats.mBeaconsLostCount = data.sta_mgmt.beacons_miss; return CHIP_NO_ERROR; } void WiFiManager::ScanResultHandler(Platform::UniquePtr data, size_t length) { // Validate that input data size matches the expected one. VerifyOrReturn(length == sizeof(wifi_scan_result)); // Contrary to other handlers, offload accumulating of the scan results from the CHIP thread to the caller's thread const wifi_scan_result * scanResult = reinterpret_cast(data.get()); if (Instance().mInternalScan && Instance().mWantedNetwork.GetSsidSpan().data_equal(ByteSpan(scanResult->ssid, scanResult->ssid_length))) { // Prepare the connection parameters // In case there are many networks with the same SSID choose the one with the best RSSI if (scanResult->rssi > Instance().mWiFiParams.mRssi) { Instance().ClearStationProvisioningData(); Instance().mWiFiParams.mParams.ssid_length = static_cast(Instance().mWantedNetwork.ssidLen); Instance().mWiFiParams.mParams.ssid = Instance().mWantedNetwork.ssid; // Fallback to the WIFI_SECURITY_TYPE_PSK if the security is unknown Instance().mWiFiParams.mParams.security = scanResult->security <= WIFI_SECURITY_TYPE_MAX ? scanResult->security : WIFI_SECURITY_TYPE_PSK; Instance().mWiFiParams.mParams.psk_length = static_cast(Instance().mWantedNetwork.passLen); Instance().mWiFiParams.mParams.mfp = (scanResult->mfp == WIFI_MFP_REQUIRED) ? WIFI_MFP_REQUIRED : WIFI_MFP_OPTIONAL; // If the security is none, WiFi driver expects the psk to be nullptr if (Instance().mWiFiParams.mParams.security == WIFI_SECURITY_TYPE_NONE) { Instance().mWiFiParams.mParams.psk = nullptr; } else { Instance().mWiFiParams.mParams.psk = Instance().mWantedNetwork.pass; } Instance().mWiFiParams.mParams.timeout = Instance().mHandling.mConnectionTimeout.count(); Instance().mWiFiParams.mParams.channel = WIFI_CHANNEL_ANY; Instance().mWiFiParams.mRssi = scanResult->rssi; Instance().mWiFiParams.mParams.band = WIFI_FREQ_BAND_UNKNOWN; Instance().mSsidFound = true; } } if (Instance().mScanResultCallback && !Instance().mInternalScan) { Instance().mScanResultCallback(ToScanResponse(scanResult)); } } void WiFiManager::ScanDoneHandler(Platform::UniquePtr data, size_t length) { // Validate that input data size matches the expected one. VerifyOrReturn(length == sizeof(wifi_status)); CHIP_ERROR err = SystemLayer().ScheduleLambda([capturedData = data.get()] { Platform::UniquePtr safePtr(capturedData); uint8_t * rawData = safePtr.get(); const wifi_status * status = reinterpret_cast(rawData); ScanDoneStatus scanDoneStatus = status->status; if (scanDoneStatus) { ChipLogError(DeviceLayer, "Wi-Fi scan finalization failure (%d)", scanDoneStatus); } else { ChipLogProgress(DeviceLayer, "Wi-Fi scan done"); } if (Instance().mScanDoneCallback && !Instance().mInternalScan) { Instance().mScanDoneCallback(scanDoneStatus); // restore the connection state from before the scan request was issued Instance().mWiFiState = Instance().mCachedWiFiState; return; } // Internal scan is supposed to be followed by a connection request if the SSID has been found if (Instance().mInternalScan) { if (!Instance().mSsidFound) { auto currentTimeout = Instance().CalculateNextRecoveryTime(); ChipLogProgress(DeviceLayer, "Starting connection recover: re-scanning... (next attempt in %d ms)", currentTimeout.count()); DeviceLayer::SystemLayer().StartTimer(currentTimeout, Recover, nullptr); return; } Instance().mWiFiState = WIFI_STATE_ASSOCIATING; if (net_mgmt(NET_REQUEST_WIFI_CONNECT, Instance().mNetIf, &(Instance().mWiFiParams.mParams), sizeof(wifi_connect_req_params))) { ChipLogError(DeviceLayer, "Connection request failed"); if (Instance().mHandling.mOnConnectionDone) { Instance().mHandling.mOnConnectionDone(WIFI_STATUS_CONN_FAIL); } Instance().mWiFiState = WIFI_STATE_DISCONNECTED; return; } ChipLogProgress(DeviceLayer, "Connection to %*s requested [RSSI=%d]", Instance().mWiFiParams.mParams.ssid_length, Instance().mWiFiParams.mParams.ssid, Instance().mWiFiParams.mRssi); Instance().mInternalScan = false; } }); if (CHIP_NO_ERROR == err) { // the ownership has been transferred to the worker thread - release the buffer data.release(); } } void WiFiManager::SendRouterSolicitation(System::Layer * layer, void * param) { net_if_start_rs(Instance().mNetIf); Instance().mRouterSolicitationCounter++; if (Instance().mRouterSolicitationCounter < kRouterSolicitationMaxCount) { DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(kRouterSolicitationIntervalMs), SendRouterSolicitation, nullptr); } else { Instance().mRouterSolicitationCounter = 0; } } void WiFiManager::ConnectHandler(Platform::UniquePtr data, size_t length) { using app::Clusters::WiFiNetworkDiagnostics::AssociationFailureCauseEnum; // Validate that input data size matches the expected one. VerifyOrReturn(length == sizeof(wifi_status)); CHIP_ERROR err = SystemLayer().ScheduleLambda([capturedData = data.get()] { Platform::UniquePtr safePtr(capturedData); uint8_t * rawData = safePtr.get(); const wifi_status * status = reinterpret_cast(rawData); wifi_conn_status connStatus = status->conn_status; if (connStatus) { ChipLogProgress(DeviceLayer, "Connection to WiFi network failed or was terminated by another request"); Instance().mWiFiState = WIFI_STATE_DISCONNECTED; if (Instance().mHandling.mOnConnectionDone) { Instance().mHandling.mOnConnectionDone(connStatus); } WiFiDiagnosticsDelegate * delegate = GetDiagnosticDataProvider().GetWiFiDiagnosticsDelegate(); if (delegate) { uint16_t reason = Instance().GetLastDisconnectReason(); uint8_t associationFailureCause; switch (connStatus) { case WIFI_STATUS_CONN_WRONG_PASSWORD: associationFailureCause = to_underlying(AssociationFailureCauseEnum::kAuthenticationFailed); break; case WIFI_STATUS_CONN_FAIL: case WIFI_STATUS_CONN_TIMEOUT: associationFailureCause = to_underlying(AssociationFailureCauseEnum::kAssociationFailed); break; case WIFI_STATUS_CONN_AP_NOT_FOUND: associationFailureCause = to_underlying(AssociationFailureCauseEnum::kSsidNotFound); break; default: associationFailureCause = to_underlying(AssociationFailureCauseEnum::kUnknown); break; } delegate->OnAssociationFailureDetected(associationFailureCause, reason); } } else // The connection has been established successfully. { // Workaround needed until sending Router Solicitation after connect will be done by the driver. DeviceLayer::SystemLayer().StartTimer( System::Clock::Milliseconds32(chip::Crypto::GetRandU16() % kMaxInitialRouterSolicitationDelayMs), SendRouterSolicitation, nullptr); ChipLogProgress(DeviceLayer, "Connected to WiFi network"); Instance().mWiFiState = WIFI_STATE_COMPLETED; if (Instance().mHandling.mOnConnectionDone) { Instance().mHandling.mOnConnectionDone(connStatus); } Instance().PostConnectivityStatusChange(kConnectivity_Established); // Workaround needed to re-initialize mDNS server after Wi-Fi interface is operative chip::DeviceLayer::ChipDeviceEvent event; event.Type = chip::DeviceLayer::DeviceEventType::kDnssdInitialized; CHIP_ERROR error = chip::DeviceLayer::PlatformMgr().PostEvent(&event); if (error != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "Cannot post event [error: %s]", ErrorStr(error)); } WiFiDiagnosticsDelegate * delegate = GetDiagnosticDataProvider().GetWiFiDiagnosticsDelegate(); if (delegate) { delegate->OnConnectionStatusChanged( to_underlying(app::Clusters::WiFiNetworkDiagnostics::ConnectionStatusEnum::kConnected)); } } // cleanup the provisioning data as it is configured per each connect request Instance().ClearStationProvisioningData(); }); if (CHIP_NO_ERROR == err) { // the ownership has been transferred to the worker thread - release the buffer data.release(); } } void WiFiManager::DisconnectHandler(Platform::UniquePtr data, size_t length) { // Validate that input data size matches the expected one. VerifyOrReturn(length == sizeof(wifi_status)); CHIP_ERROR err = SystemLayer().ScheduleLambda([capturedData = data.get()] { Platform::UniquePtr safePtr(capturedData); uint8_t * rawData = safePtr.get(); const wifi_status * status = reinterpret_cast(rawData); uint16_t reason; switch (status->disconn_reason) { case WIFI_REASON_DISCONN_UNSPECIFIED: reason = WLAN_REASON_UNSPECIFIED; break; case WIFI_REASON_DISCONN_USER_REQUEST: reason = WLAN_REASON_DEAUTH_LEAVING; break; case WIFI_REASON_DISCONN_AP_LEAVING: reason = WLAN_REASON_DEAUTH_LEAVING; break; case WIFI_REASON_DISCONN_INACTIVITY: reason = WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY; break; default: reason = WLAN_REASON_UNSPECIFIED; break; } Instance().SetLastDisconnectReason(reason); ChipLogProgress(DeviceLayer, "WiFi station disconnected"); Instance().mWiFiState = WIFI_STATE_DISCONNECTED; Instance().PostConnectivityStatusChange(kConnectivity_Lost); WiFiDiagnosticsDelegate * delegate = GetDiagnosticDataProvider().GetWiFiDiagnosticsDelegate(); if (delegate) { delegate->OnConnectionStatusChanged( to_underlying(app::Clusters::WiFiNetworkDiagnostics::ConnectionStatusEnum::kNotConnected)); delegate->OnDisconnectionDetected(reason); } }); if (CHIP_NO_ERROR == err) { // the ownership has been transferred to the worker thread - release the buffer data.release(); } } void WiFiManager::IPv6AddressChangeHandler(const void * data) { const in6_addr * addr = reinterpret_cast(data); // Filter out link-local addresses that are not routable outside of a local network. if (!net_ipv6_is_ll_addr(addr)) { // This is needed to send mDNS queries containing updated IPv6 addresses. ChipDeviceEvent event; event.Type = DeviceEventType::kDnssdRestartNeeded; CHIP_ERROR error = PlatformMgr().PostEvent(&event); if (error != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "Cannot post event: %" CHIP_ERROR_FORMAT, error.Format()); } } } WiFiManager::StationStatus WiFiManager::GetStationStatus() const { return WiFiManager::sStatusMap[mWiFiState]; } void WiFiManager::PostConnectivityStatusChange(ConnectivityChange changeType) { ChipDeviceEvent networkEvent{}; networkEvent.Type = DeviceEventType::kWiFiConnectivityChange; networkEvent.WiFiConnectivityChange.Result = changeType; PlatformMgr().PostEventOrDie(&networkEvent); } void WiFiManager::Recover(System::Layer *, void *) { // Prevent scheduling recovery if we are already connected to the network. if (Instance().mWiFiState == WIFI_STATE_COMPLETED) { Instance().AbortConnectionRecovery(); return; } // If kConnectionRecoveryMaxOverallInterval has a non-zero value prevent endless re-scan. if (0 != kConnectionRecoveryMaxRetries && (++Instance().mConnectionRecoveryCounter >= kConnectionRecoveryMaxRetries)) { Instance().AbortConnectionRecovery(); return; } Instance().Scan(Instance().mWantedNetwork.GetSsidSpan(), nullptr, nullptr, true /* internal scan */); } void WiFiManager::ResetRecoveryTime() { mConnectionRecoveryTimeMs = kConnectionRecoveryMinIntervalMs; mConnectionRecoveryCounter = 0; } void WiFiManager::AbortConnectionRecovery() { DeviceLayer::SystemLayer().CancelTimer(Recover, nullptr); Instance().ResetRecoveryTime(); } System::Clock::Milliseconds32 WiFiManager::CalculateNextRecoveryTime() { if (mConnectionRecoveryTimeMs > kConnectionRecoveryMaxIntervalMs) { // Find the new random jitter value in range [-jitter, +jitter]. int32_t jitter = chip::Crypto::GetRandU32() % (2 * jitter + 1) - jitter; mConnectionRecoveryTimeMs = kConnectionRecoveryMaxIntervalMs + jitter; return System::Clock::Milliseconds32(mConnectionRecoveryTimeMs); } else { uint32_t currentRecoveryTimeout = mConnectionRecoveryTimeMs; mConnectionRecoveryTimeMs = mConnectionRecoveryTimeMs * 2; return System::Clock::Milliseconds32(currentRecoveryTimeout); } } CHIP_ERROR WiFiManager::SetLowPowerMode(bool onoff) { VerifyOrReturnError(nullptr != mNetIf, CHIP_ERROR_INTERNAL); wifi_ps_config currentConfig{}; if (net_mgmt(NET_REQUEST_WIFI_PS_CONFIG, mNetIf, ¤tConfig, sizeof(currentConfig))) { ChipLogError(DeviceLayer, "Get current low power mode config request failed"); return CHIP_ERROR_INTERNAL; } if ((currentConfig.ps_params.enabled == WIFI_PS_ENABLED && onoff == false) || (currentConfig.ps_params.enabled == WIFI_PS_DISABLED && onoff == true)) { wifi_ps_params params{ .enabled = onoff ? WIFI_PS_ENABLED : WIFI_PS_DISABLED }; if (net_mgmt(NET_REQUEST_WIFI_PS, mNetIf, ¶ms, sizeof(params))) { ChipLogError(DeviceLayer, "Set low power mode request failed"); return CHIP_ERROR_INTERNAL; } ChipLogProgress(DeviceLayer, "Successfully set low power mode [%d]", onoff); return CHIP_NO_ERROR; } ChipLogDetail(DeviceLayer, "Low power mode is already in requested state [%d]", onoff); return CHIP_NO_ERROR; } void WiFiManager::SetLastDisconnectReason(uint16_t reason) { mLastDisconnectedReason = reason; } uint16_t WiFiManager::GetLastDisconnectReason() { return mLastDisconnectedReason; } } // namespace DeviceLayer } // namespace chip