diff --git a/examples/platform/silabs/MatterConfig.cpp b/examples/platform/silabs/MatterConfig.cpp index e882310c2c..686d65661a 100644 --- a/examples/platform/silabs/MatterConfig.cpp +++ b/examples/platform/silabs/MatterConfig.cpp @@ -433,7 +433,7 @@ CHIP_ERROR SilabsMatterConfig::InitMatter(const char * appName) #ifdef ENABLE_CHIP_SHELL chip::startShellTask(); -#endif +#endif // ENABLE_CHIP_SHELL return CHIP_NO_ERROR; } diff --git a/examples/platform/silabs/MatterShell.cpp b/examples/platform/silabs/MatterShell.cpp index 25b79accef..639e629d6b 100644 --- a/examples/platform/silabs/MatterShell.cpp +++ b/examples/platform/silabs/MatterShell.cpp @@ -28,7 +28,12 @@ #include "sl_cli_config.h" #include "sli_cli_io.h" #include -#endif +#endif // SL_CATALOG_CLI_PRESENT +#ifdef SL_WIFI +#include +#include +#include +#endif // SL_WIFI using namespace ::chip; using Shell::Engine; @@ -194,6 +199,10 @@ void startShellTask() cmd_otcli_init(); #endif +#ifdef SL_WIFI + Shell::SetWiFiDriver(DeviceLayer::NetworkCommissioning::SlWiFiDriver::GetInstance()); +#endif // SL_WIFI + #ifdef SL_CATALOG_CLI_PRESENT cmdSilabsInit(); #endif diff --git a/src/platform/silabs/CHIPDevicePlatformEvent.h b/src/platform/silabs/CHIPDevicePlatformEvent.h index bf2af0228e..339d24fac7 100644 --- a/src/platform/silabs/CHIPDevicePlatformEvent.h +++ b/src/platform/silabs/CHIPDevicePlatformEvent.h @@ -40,7 +40,7 @@ namespace DeviceEventType { */ enum PublicPlatformSpecificEventTypes { - /* None currently defined */ + kSLSystemEvent = kRange_PublicPlatformSpecific, }; /** @@ -48,7 +48,7 @@ enum PublicPlatformSpecificEventTypes */ enum InternalPlatformSpecificEventTypes { - kWFXSystemEvent = kRange_InternalPlatformSpecific, + kPlatformSLEvent = kRange_InternalPlatformSpecific, }; } // namespace DeviceEventType diff --git a/src/platform/silabs/ConnectivityManagerImpl.h b/src/platform/silabs/ConnectivityManagerImpl.h index c2890b936a..1fe249fae4 100644 --- a/src/platform/silabs/ConnectivityManagerImpl.h +++ b/src/platform/silabs/ConnectivityManagerImpl.h @@ -104,6 +104,7 @@ class ConnectivityManagerImpl final : public ConnectivityManager, CHIP_ERROR _GetAndLogWifiStatsCounters(void); void _OnWiFiScanDone(); void _OnWiFiStationProvisionChange(); + CHIP_ERROR _DisconnectNetwork(void); #if CHIP_CONFIG_ENABLE_ICD_SERVER CHIP_ERROR _SetPollingInterval(System::Clock::Milliseconds32 pollingInterval); #endif /* CHIP_CONFIG_ENABLE_ICD_SERVER */ @@ -117,17 +118,25 @@ class ConnectivityManagerImpl final : public ConnectivityManager, // ===== Private members reserved for use by this class only. #if CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION - System::Clock::Timestamp mLastStationConnectFailTime; + // variables for tracking the station mode and state WiFiStationMode mWiFiStationMode; WiFiStationState mWiFiStationState; + bool mWiFiStationAutoConnect; + // TODO: Remove this once the LIT disconnect is managed by the ConnectivityManager + bool mWiFiStationDisconnectPending; + // variables for tracking the last connection failure time and reconnect interval + System::Clock::Timestamp mLastStationConnectFailTime; System::Clock::Timeout mWiFiStationReconnectInterval; + uint8_t mWiFiStationReconnectCount; + // flags for tracking the internet connectivity state BitFlags mFlags; void DriveStationState(void); void OnStationConnected(void); void OnStationDisconnected(void); - void ChangeWiFiStationState(WiFiStationState newState); + void ChangeWiFiStationState(WiFiStationState newState, bool driveStationState = true); static void DriveStationState(::chip::System::Layer * aLayer, void * aAppState); + void ResetReconnectionWiFiStationState(void); void UpdateInternetConnectivityState(void); #endif diff --git a/src/platform/silabs/ConnectivityManagerImpl_WIFI.cpp b/src/platform/silabs/ConnectivityManagerImpl_WIFI.cpp index 4b356357ee..3c768e12c9 100644 --- a/src/platform/silabs/ConnectivityManagerImpl_WIFI.cpp +++ b/src/platform/silabs/ConnectivityManagerImpl_WIFI.cpp @@ -55,68 +55,80 @@ ConnectivityManagerImpl ConnectivityManagerImpl::sInstance; CHIP_ERROR ConnectivityManagerImpl::_Init() { - CHIP_ERROR err; - // Queue work items to bootstrap the AP and station state machines once the Chip event loop is running. mWiFiStationMode = kWiFiStationMode_Disabled; mWiFiStationState = kWiFiStationState_NotConnected; + mWiFiStationAutoConnect = true; + mWiFiStationDisconnectPending = false; mLastStationConnectFailTime = System::Clock::kZero; mWiFiStationReconnectInterval = System::Clock::Milliseconds32(CHIP_DEVICE_CONFIG_WIFI_STATION_RECONNECT_INTERVAL); + mWiFiStationReconnectCount = 1; mFlags.ClearAll(); // TODO Initialize the Chip Addressing and Routing Module. // Ensure that station mode is enabled. - WifiInterface::GetInstance().ConfigureStationMode(); - - err = DeviceLayer::SystemLayer().ScheduleWork(DriveStationState, NULL); + ReturnErrorOnFailure(SetWiFiStationMode(kWiFiStationMode_Enabled)); - SuccessOrExit(err); - -exit: - return err; + // Queue work items to bootstrap the AP and station state machines once the Chip event loop is running. + ReturnErrorOnFailure(DeviceLayer::SystemLayer().ScheduleWork(DriveStationState, NULL)); + return CHIP_NO_ERROR; } void ConnectivityManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event) { // Forward the event to the generic base classes as needed. // Handle Wfx wifi events... - if (event->Type == DeviceEventType::kWFXSystemEvent) + if (event->Type == DeviceEventType::kPlatformSLEvent) { switch (event->Platform.event.WFXSystemEvent.data.genericMsgEvent.header.id) { case to_underlying(WifiInterface::WifiEvent::kStartUp): - ChipLogProgress(DeviceLayer, "WIFI_EVENT_STA_START"); + ChipLogProgress(DeviceLayer, "STARTUP EVENT"); DriveStationState(); break; + case to_underlying(WifiInterface::WifiEvent::kConnect): - ChipLogProgress(DeviceLayer, "WIFI_EVENT_STA_CONNECTED"); - if (mWiFiStationState == kWiFiStationState_Connecting) + ChipLogProgress(DeviceLayer, "CONNECT EVENT"); + ChangeWiFiStationState(kWiFiStationState_Connected); + break; + + case to_underlying(WifiInterface::WifiEvent::kDisconnect): + ChipLogProgress(DeviceLayer, "DISCONNECT EVENT"); + switch (WifiInterface::GetInstance().GetLastDisconnectionReason()) { - if (event->Platform.event.WFXSystemEvent.data.connectEvent.body.status == 0) + // User initiated disconnection outside of the ConnectivityManager + case NetworkCommissioning::Status::kSuccess: + if (mWiFiStationDisconnectPending) { - ChangeWiFiStationState(kWiFiStationState_Connecting_Succeeded); + mWiFiStationDisconnectPending = false; + ChangeWiFiStationState(kWiFiStationState_NotConnected); } else { - ChangeWiFiStationState(kWiFiStationState_Connecting_Failed); + // out of bound disconnect event, since WifiInterface::TriggerDisconnection() is not called + // from the ConnectivityManager, so we need to set the auto connect flag to false + // and drive the station state to not connected + mWiFiStationAutoConnect = false; + DriveStationState(); } - } - DriveStationState(); - break; - case to_underlying(WifiInterface::WifiEvent::kDisconnect): - ChipLogProgress(DeviceLayer, "WIFI_EVENT_STA_DISCONNECTED"); - if (mWiFiStationState == kWiFiStationState_Connecting) - { + break; + // Disconnection due to WiFi connectivity error + default: + VerifyOrReturn( + mWiFiStationState == kWiFiStationState_Connecting || + mWiFiStationState == kWiFiStationState_Connecting_Succeeded || + mWiFiStationState == kWiFiStationState_Connected, + ChipLogDetail(DeviceLayer, "Discard disconnect event as WiFi station was not connecting or connected")); ChangeWiFiStationState(kWiFiStationState_Connecting_Failed); + break; } - DriveStationState(); break; case to_underlying(WifiInterface::WifiEvent::kGotIPv4): - case to_underlying(WifiInterface::WifiEvent::kLostIP): case to_underlying(WifiInterface::WifiEvent::kGotIPv6): - ChipLogProgress(DeviceLayer, "IP Change Event"); + case to_underlying(WifiInterface::WifiEvent::kLostIP): + ChipLogProgress(DeviceLayer, "IP CHANGE EVENT"); UpdateInternetConnectivityState(); break; default: @@ -127,19 +139,8 @@ void ConnectivityManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event) ConnectivityManager::WiFiStationMode ConnectivityManagerImpl::_GetWiFiStationMode(void) { - if (mWiFiStationMode != kWiFiStationMode_ApplicationControlled) - { - if (WifiInterface::GetInstance().IsStationModeEnabled()) - { - mWiFiStationMode = kWiFiStationMode_Enabled; - } - else - { - mWiFiStationMode = kWiFiStationMode_Disabled; - } - } - - return mWiFiStationMode; + VerifyOrReturnValue(mWiFiStationMode != kWiFiStationMode_ApplicationControlled, mWiFiStationMode); + return WifiInterface::GetInstance().IsStationModeEnabled() ? kWiFiStationMode_Enabled : kWiFiStationMode_Disabled; } bool ConnectivityManagerImpl::_IsWiFiStationProvisioned(void) @@ -154,33 +155,58 @@ bool ConnectivityManagerImpl::_IsWiFiStationEnabled(void) CHIP_ERROR ConnectivityManagerImpl::_SetWiFiStationMode(ConnectivityManager::WiFiStationMode val) { - TEMPORARY_RETURN_IGNORED DeviceLayer::SystemLayer().ScheduleWork(DriveStationState, NULL); + // If the new WiFi station mode is the same as the current WiFi station mode, return success. + VerifyOrReturnError(val != mWiFiStationMode, CHIP_NO_ERROR, + ChipLogDetail(DeviceLayer, "SetWiFiStationMode ignored: %s", WiFiStationModeToStr(val))); - if (mWiFiStationMode != val) - { - ChipLogProgress(DeviceLayer, "WiFi station mode change: %s -> %s", WiFiStationModeToStr(mWiFiStationMode), - WiFiStationModeToStr(val)); - } + ChipLogProgress(DeviceLayer, "WiFiStationMode: %s -> %s", WiFiStationModeToStr(mWiFiStationMode), WiFiStationModeToStr(val)); mWiFiStationMode = val; + CHIP_ERROR err = CHIP_NO_ERROR; + switch (mWiFiStationMode) + { + case kWiFiStationMode_Disabled: { + // disconnect if wifi is connected + if (IsWiFiStationConnected()) + { + ReturnErrorOnFailure(DisconnectNetwork()); + } + err = WifiInterface::GetInstance().DisableStationMode(); + VerifyOrReturnError(err == CHIP_NO_ERROR || err == CHIP_ERROR_NOT_IMPLEMENTED, err); + } + break; + case kWiFiStationMode_Enabled: { + ReturnErrorOnFailure(WifiInterface::GetInstance().EnableStationMode()); + } + break; + default: + // kWiFiStationMode_Application + break; + } + ChangeWiFiStationState(kWiFiStationState_NotConnected, false); + mWiFiStationAutoConnect = true; + // do not schedule the DriveStationState if the station is not ready to be driven once the START UP EVENT is received + VerifyOrReturnError(WifiInterface::GetInstance().IsStationReady(), CHIP_NO_ERROR, + ChipLogDetail(DeviceLayer, "WiFi station is not ready")); + ReturnErrorOnFailure(DeviceLayer::SystemLayer().ScheduleWork(DriveStationState, NULL)); return CHIP_NO_ERROR; } -CHIP_ERROR ConnectivityManagerImpl::_SetWiFiStationReconnectInterval(System::Clock::Timeout val) +CHIP_ERROR ConnectivityManagerImpl::_SetWiFiStationReconnectInterval(System::Clock::Timeout timeoutMs) { - mWiFiStationReconnectInterval = val; + mWiFiStationReconnectInterval = timeoutMs; return CHIP_NO_ERROR; } void ConnectivityManagerImpl::_ClearWiFiStationProvision(void) { - if (mWiFiStationMode != kWiFiStationMode_ApplicationControlled) - { - WifiInterface::GetInstance().ClearWifiCredentials(); + // If the WiFi station mode is application controlled, do not clear the WiFi credentials. + VerifyOrReturn(mWiFiStationMode != kWiFiStationMode_ApplicationControlled, + ChipLogProgress(DeviceLayer, "WiFi station is application controlled")); - TEMPORARY_RETURN_IGNORED DeviceLayer::SystemLayer().ScheduleWork(DriveStationState, NULL); - } + WifiInterface::GetInstance().ClearWifiCredentials(); + LogErrorOnFailure(DeviceLayer::SystemLayer().ScheduleWork(DriveStationState, NULL)); } CHIP_ERROR ConnectivityManagerImpl::_GetAndLogWifiStatsCounters(void) @@ -197,7 +223,37 @@ void ConnectivityManagerImpl::_OnWiFiStationProvisionChange() { // Schedule a call to the DriveStationState method to adjust the station state as needed. ChipLogProgress(DeviceLayer, "_ON WIFI PROVISION CHANGE"); - TEMPORARY_RETURN_IGNORED DeviceLayer::SystemLayer().ScheduleWork(DriveStationState, NULL); + LogErrorOnFailure(DeviceLayer::SystemLayer().ScheduleWork(DriveStationState, NULL)); +} + +CHIP_ERROR ConnectivityManagerImpl::_DisconnectNetwork(void) +{ + // if the station is not connected, return success + VerifyOrReturnError(mWiFiStationState != kWiFiStationState_NotConnected, CHIP_NO_ERROR); + + mWiFiStationAutoConnect = false; + + if (mWiFiStationState == kWiFiStationState_Connecting) + { + // user requested to disconnect the network, while the device in connecting state + // reset the reconnection state to avoid the device to reconnect to the network + // if the on going connection attempt failed, the station will be in the NotConnected state. + // if the on going connection attempt succeeded, the station will be in the Connected state, + // driven by the OnPlatformEvent() callback + ChangeWiFiStationState(kWiFiStationState_NotConnected, false); + } + else if (mWiFiStationState == kWiFiStationState_Connected || mWiFiStationState == kWiFiStationState_Connecting_Succeeded) + { + VerifyOrReturnError(!mWiFiStationDisconnectPending, CHIP_ERROR_IN_PROGRESS); + + mWiFiStationDisconnectPending = true; + ChangeWiFiStationState(kWiFiStationState_Disconnecting, false); + // ChangeWiFiStationState() is called in the OnPlatformEvent() callback as per result of TriggerDisconnection() + // next time DriveStationState() will be called, the station will be in the NotConnected state + WifiInterface::GetInstance().TriggerDisconnection(); + } + LogErrorOnFailure(DeviceLayer::SystemLayer().ScheduleWork(DriveStationState, NULL)); + return CHIP_NO_ERROR; } #if CHIP_CONFIG_ENABLE_ICD_SERVER @@ -214,114 +270,122 @@ CHIP_ERROR ConnectivityManagerImpl::_SetPollingInterval(System::Clock::Milliseco void ConnectivityManagerImpl::DriveStationState() { - bool stationConnected = false; - - // Refresh the current station mode. - GetWiFiStationMode(); - + // Refresh the current state and store it in `mWiFiStationMode` + WiFiStationMode stationMode = GetWiFiStationMode(); + CHIP_ERROR err = CHIP_NO_ERROR; + bool isStationConnected = WifiInterface::GetInstance().IsStationConnected(); + bool isStationProvisioned = WifiInterface::GetInstance().IsWifiProvisioned(); + + // if the station mode is application controlled or disabled, return + VerifyOrReturn(stationMode != kWiFiStationMode_ApplicationControlled, + ChipLogProgress(DeviceLayer, "WiFi station is application controlled")); // If the station interface is NOT under application control... - if (mWiFiStationMode != kWiFiStationMode_ApplicationControlled) + // Ensure that the WiFi task is started. + // returns CHIP_NO_ERROR if the task is started successfully, also when the task is already started + err = WifiInterface::GetInstance().StartWifiTask(); + VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(DeviceLayer, "StartWifiTask failed: %" CHIP_ERROR_FORMAT, err.Format())); + // if the station mode is disabled, return + VerifyOrReturn(stationMode != kWiFiStationMode_Disabled, ChipLogProgress(DeviceLayer, "WiFi station is disabled")); + ChipLogDetail(DeviceLayer, "DriveStationState: %s", WiFiStationStateToStr(mWiFiStationState)); + ChipLogDetail(DeviceLayer, "isStationConnected: %d, isStationProvisioned: %d, mWiFiStationAutoConnect: %d", isStationConnected, + isStationProvisioned, mWiFiStationAutoConnect); + if (isStationConnected) { - // Ensure that the Wifi task is started. - CHIP_ERROR error = WifiInterface::GetInstance().StartWifiTask(); - VerifyOrReturn(error == CHIP_NO_ERROR, - ChipLogError(DeviceLayer, "StartWifiTask() failed: %" CHIP_ERROR_FORMAT, error.Format())); + // sync the station state with the actual state + switch (mWiFiStationState) + { + case kWiFiStationState_NotConnected: + case kWiFiStationState_Connecting_Succeeded: + ChangeWiFiStationState(kWiFiStationState_Connected, false); + break; + default: + break; + } - // Ensure that station mode is enabled in the WiFi layer. - WifiInterface::GetInstance().ConfigureStationMode(); + // if the station is not provisioned, disconnect it + if (!isStationProvisioned) + { + ChipLogDetail(DeviceLayer, "WiFi station is not provisioned and is connected, disconnecting"); + ReturnOnFailure(DisconnectNetwork()); + } } - stationConnected = WifiInterface::GetInstance().IsStationConnected(); + // if the station is not connected + VerifyOrReturn(!isStationConnected); - // If the station interface is currently connected ... - if (stationConnected) + // if the station is provisioned and auto connect is enabled and the station is not connected, + // connect it to the access point using the credentials from the staging network + if (mWiFiStationAutoConnect && isStationProvisioned) { - // Advance the station state to Connected if it was previously NotConnected or - // a previously initiated connect attempt succeeded. - if (mWiFiStationState == kWiFiStationState_NotConnected || mWiFiStationState == kWiFiStationState_Connecting_Succeeded) + // if the station is not connected, set the state to connecting + if (mWiFiStationState == kWiFiStationState_NotConnected) { - ChangeWiFiStationState(kWiFiStationState_Connected); - ChipLogProgress(DeviceLayer, "WiFi station interface connected"); - mLastStationConnectFailTime = System::Clock::kZero; - OnStationConnected(); + ChangeWiFiStationState(kWiFiStationState_Connecting, false); } + } - // If the WiFi station interface is no longer enabled, or no longer provisioned, - // disconnect the station from the AP, unless the WiFi station mode is currently - // under application control. -#ifndef SL_ONNETWORK_PAIRING - // Incase of station interface disabled & provisioned, wifi_station should not be disconnected. - // Device will try to reconnect. - if (mWiFiStationMode != kWiFiStationMode_ApplicationControlled && - (mWiFiStationMode != kWiFiStationMode_Enabled && !IsWiFiStationProvisioned())) - { - ChipLogProgress(DeviceLayer, "Disconnecting WiFi station interface"); + // TODO: Verify why `now` is always the same value + System::Clock::Timestamp now = System::SystemClock().GetMonotonicTimestamp(); + System::Clock::Timestamp timeToNextConnect = System::Clock::kZero; - WifiInterface::GetInstance().TriggerDisconnection(); - ChangeWiFiStationState(kWiFiStationState_Disconnecting); - } -#endif - } - // Otherwise the station interface is NOT connected to an AP, so... - else + switch (mWiFiStationState) { - System::Clock::Timestamp now = System::SystemClock().GetMonotonicTimestamp(); - - // Advance the station state to NotConnected if it was previously Connected or Disconnecting, - // or if a previous initiated connect attempt failed. - if (mWiFiStationState == kWiFiStationState_Connected || mWiFiStationState == kWiFiStationState_Disconnecting || - mWiFiStationState == kWiFiStationState_Connecting_Failed) + case kWiFiStationState_Connecting: { + // connect the station to the access point using the credentials from the staging network + err = WifiInterface::GetInstance().ConnectToAccessPoint(); // using the credentials from the staging network + VerifyOrReturn(err == CHIP_NO_ERROR || err == CHIP_ERROR_IN_PROGRESS, + ChipLogError(DeviceLayer, "ConnectToAccessPoint failed: %" CHIP_ERROR_FORMAT, err.Format())); + // ChangeWiFiStationState() is called in the OnPlatformEvent() callback as per result of ConnectWiFiNetwork() + return; + } + break; + case kWiFiStationState_Connecting_Failed: { + // if the station is connecting failed, + // arrange another connection attempt at a suitable point in the future + // TODO: Verify why `mLastStationConnectFailTime` is not assigned the value of `now` + mLastStationConnectFailTime = now; + // Reset the station state to kWiFiStationState_Connecting to start a new connection attempt + timeToNextConnect = (mWiFiStationReconnectInterval * mWiFiStationReconnectCount); + // DriveStationState() will be called again to start a new connection attempt + ChipLogProgress(DeviceLayer, "Next WiFi station reconnect in %" PRIu32 " ms", + System::Clock::Milliseconds32(timeToNextConnect).count()); + + // drive the station state to connecting + ChangeWiFiStationState(kWiFiStationState_Connecting, false); + LogErrorOnFailure(DeviceLayer::SystemLayer().StartTimer(timeToNextConnect, DriveStationState, NULL)); + + // TODO: Revisit this logic + // increase the reconnect interval by the previous interval, for telescoping effect and reduce the frequency of + // reconnect attempts thus saving power + // TODO: Guard this with commissioning mode flag + // mWiFiStationReconnectCount++; + } + break; + case kWiFiStationState_Connected: + case kWiFiStationState_Connecting_Succeeded: { + // Unexpected radio-down while still Connected/Connecting_Succeeded. + // Prefer Connecting_Failed so reconnect is scheduled (same as error + // kDisconnect). Going straight to NotConnected resets the retry timer + // and skips a new join; a later kDisconnect is then discarded. + if (mWiFiStationAutoConnect && isStationProvisioned) { - WiFiStationState prevState = mWiFiStationState; - ChangeWiFiStationState(kWiFiStationState_NotConnected); - if (prevState != kWiFiStationState_Connecting_Failed) - { - ChipLogProgress(DeviceLayer, "WiFi station interface disconnected"); - mLastStationConnectFailTime = System::Clock::kZero; - OnStationDisconnected(); - } - else - { - mLastStationConnectFailTime = now; - } + ChangeWiFiStationState(kWiFiStationState_Connecting_Failed); } - - // If the WiFi station interface is now enabled and provisioned (and by implication, - // not presently under application control), AND the system is not in the process of - // scanning, then... - if (mWiFiStationMode == kWiFiStationMode_Enabled && IsWiFiStationProvisioned()) + else { - // Initiate a connection to the AP if we haven't done so before, or if enough - // time has passed since the last attempt. - if (mLastStationConnectFailTime == System::Clock::kZero || - now >= mLastStationConnectFailTime + mWiFiStationReconnectInterval) - { - if (mWiFiStationState != kWiFiStationState_Connecting) - { - ChipLogProgress(DeviceLayer, "Attempting to connect WiFi"); - SuccessOrExitAction(WifiInterface::GetInstance().ConnectToAccessPoint(), - ChipLogError(DeviceLayer, "ConnectToAccessPoint() failed")); - - ChangeWiFiStationState(kWiFiStationState_Connecting); - } - } - - // Otherwise arrange another connection attempt at a suitable point in the future. - else - { - System::Clock::Timestamp timeToNextConnect = (mLastStationConnectFailTime + mWiFiStationReconnectInterval) - now; - - ChipLogProgress(DeviceLayer, "Next WiFi station reconnect in %" PRIu32 " ms", - System::Clock::Milliseconds32(timeToNextConnect).count()); - - ReturnOnFailure(DeviceLayer::SystemLayer().StartTimer(timeToNextConnect, DriveStationState, NULL)); - } + ChangeWiFiStationState(kWiFiStationState_NotConnected, false); } } - -exit: - - ChipLogProgress(DeviceLayer, "Done driving station state, nothing else to do..."); + break; + case kWiFiStationState_Disconnecting: { + ChangeWiFiStationState(kWiFiStationState_NotConnected, false); + } + break; + default: { + // do nothing + } + break; + } // Kick-off any pending network scan that might have been deferred due to the activity // of the WiFi station. } @@ -331,6 +395,7 @@ void ConnectivityManagerImpl::OnStationConnected() NetworkCommissioning::SlWiFiDriver * nwDriver = NetworkCommissioning::SlWiFiDriver::GetInstance(); // Cannot use the driver if the instance is not initialized. VerifyOrDie(nwDriver != nullptr); // should never be null + nwDriver->UpdateNetworkingStatus(); nwDriver->OnConnectWiFiNetwork(); UpdateInternetConnectivityState(); @@ -343,6 +408,11 @@ void ConnectivityManagerImpl::OnStationConnected() void ConnectivityManagerImpl::OnStationDisconnected() { + NetworkCommissioning::SlWiFiDriver * nwDriver = NetworkCommissioning::SlWiFiDriver::GetInstance(); + // Cannot use the driver if the instance is not initialized. + VerifyOrDie(nwDriver != nullptr); // should never be null + nwDriver->UpdateNetworkingStatus(); + // TODO: Invoke WARM to perform actions that occur when the WiFi station interface goes down. UpdateInternetConnectivityState(); // Alert other components of the new state. @@ -352,23 +422,74 @@ void ConnectivityManagerImpl::OnStationDisconnected() (void) PlatformMgr().PostEvent(&event); } +void ConnectivityManagerImpl::ResetReconnectionWiFiStationState() +{ + mLastStationConnectFailTime = System::Clock::kZero; + mWiFiStationReconnectCount = 1; + DeviceLayer::SystemLayer().CancelTimer(DriveStationState, NULL); +} + void ConnectivityManagerImpl::DriveStationState(::chip::System::Layer * aLayer, void * aAppState) { sInstance.DriveStationState(); } -void ConnectivityManagerImpl::ChangeWiFiStationState(WiFiStationState newState) +void ConnectivityManagerImpl::ChangeWiFiStationState(WiFiStationState newState, bool driveStationState) { - if (mWiFiStationState != newState) + VerifyOrReturn(mWiFiStationState != newState, + ChipLogDetail(DeviceLayer, "ChangeWiFiStationState ignored: %s", WiFiStationStateToStr(newState))); + ChipLogProgress(DeviceLayer, "WiFiStationState: %s -> %s", WiFiStationStateToStr(mWiFiStationState), + WiFiStationStateToStr(newState)); + // Commit the state before notifying. OnStationConnected() calls + // UpdateInternetConnectivityState(), which only reports IPv6 when already Connected. + WiFiStationState prevState = mWiFiStationState; + mWiFiStationState = newState; + switch (newState) + { + case kWiFiStationState_NotConnected: + ResetReconnectionWiFiStationState(); + OnStationDisconnected(); + break; + + case kWiFiStationState_Connecting_Failed: + // TODO: Currently, `ConfigureLITConnect` is not managed by the ConnectivityManager. + // Once it is managed by the ConnectivityManager, this check can be removed. + if (prevState == kWiFiStationState_NotConnected) + { + mWiFiStationAutoConnect = true; + } + // Only notify link loss when an established (or succeeded) link dropped. + // Connecting -> Connecting_Failed is a failed join/retry, not a drop. + if (prevState == kWiFiStationState_Connected || prevState == kWiFiStationState_Connecting_Succeeded) + { + OnStationDisconnected(); + } + break; + + case kWiFiStationState_Connecting_Succeeded: + case kWiFiStationState_Connected: + // TODO: Currently, `ConfigureLITConnect` is not managed by the ConnectivityManager. + // Once it is managed by the ConnectivityManager, this check can be removed. + if (prevState == kWiFiStationState_NotConnected) + { + mWiFiStationAutoConnect = true; + } + ResetReconnectionWiFiStationState(); + OnStationConnected(); + break; + + case kWiFiStationState_Disconnecting: + ResetReconnectionWiFiStationState(); + break; + + default: + // do nothing + break; + } + + if (driveStationState) { - ChipLogProgress(DeviceLayer, "WiFi station state change: %s -> %s", WiFiStationStateToStr(mWiFiStationState), - WiFiStationStateToStr(newState)); - mWiFiStationState = newState; - - NetworkCommissioning::SlWiFiDriver * nwDriver = NetworkCommissioning::SlWiFiDriver::GetInstance(); - // Cannot use the driver if the instance is not initialized. - VerifyOrDie(nwDriver != nullptr); // should never be null - nwDriver->UpdateNetworkingStatus(); + DriveStationState(); } } @@ -381,7 +502,7 @@ void ConnectivityManagerImpl::UpdateInternetConnectivityState(void) IPAddress addr; // If the WiFi station is currently in the connected state... - if (mWiFiStationState == kWiFiStationState_Connected) + if (IsWiFiStationConnected()) { #if CHIP_DEVICE_CONFIG_ENABLE_IPV4 haveIPv4Conn = WifiInterface::GetInstance().HasAnIPv4Address(); diff --git a/src/platform/silabs/NetworkCommissioningWiFiDriver.cpp b/src/platform/silabs/NetworkCommissioningWiFiDriver.cpp index d9b9ef1b5b..cddeb3e7c1 100644 --- a/src/platform/silabs/NetworkCommissioningWiFiDriver.cpp +++ b/src/platform/silabs/NetworkCommissioningWiFiDriver.cpp @@ -45,27 +45,12 @@ CHIP_ERROR SlWiFiDriver::Init(NetworkStatusChangeCallback * networkStatusChangeC mpConnectCallback = nullptr; mpStatusChangeCallback = networkStatusChangeCallback; mDriver = this; - // TODO: default to SL_WIFI_SSID and SL_WIFI_PSK if not defined -#ifdef SL_ONNETWORK_PAIRING - memcpy(&mSavedNetwork.ssid[0], SL_WIFI_SSID, sizeof(SL_WIFI_SSID)); - memcpy(&mSavedNetwork.key[0], SL_WIFI_PSK, sizeof(SL_WIFI_PSK)); - mSavedNetwork.keyLen = sizeof(SL_WIFI_PSK); - mSavedNetwork.ssidLen = sizeof(SL_WIFI_SSID); - err = CHIP_NO_ERROR; -#else - // If reading fails, wifi is not provisioned, no need to go further. - err = SilabsConfig::ReadConfigValueBin(SilabsConfig::kConfigKey_WiFiSSID, mSavedNetwork.ssid, sizeof(mSavedNetwork.ssid), - mSavedNetwork.ssidLen); - VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_NO_ERROR); - - err = SilabsConfig::ReadConfigValueBin(SilabsConfig::kConfigKey_WiFiPSK, mSavedNetwork.key, sizeof(mSavedNetwork.key), - mSavedNetwork.keyLen); - VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_NO_ERROR); -#endif // SL_ONNETWORK_PAIRING - mStagingNetwork = mSavedNetwork; - err = ConnectWiFiNetwork(reinterpret_cast(mSavedNetwork.ssid), mSavedNetwork.ssidLen, - reinterpret_cast(mSavedNetwork.key), mSavedNetwork.keyLen); + err = RevertConfiguration(); + // If the configuration is not found, it means the wifi is not provisioned, no need to go further. + VerifyOrReturnError(err != CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND, CHIP_NO_ERROR); + err = ConnectWiFiNetwork(reinterpret_cast(mStagingNetwork.ssid), mStagingNetwork.ssidLen, + reinterpret_cast(mStagingNetwork.key), mStagingNetwork.keyLen); return err; } @@ -78,6 +63,7 @@ CHIP_ERROR SlWiFiDriver::CommitConfiguration() SilabsConfig::kConfigKey_WiFiSSID, reinterpret_cast(mStagingNetwork.ssid), mStagingNetwork.ssidLen)); ReturnErrorOnFailure(SilabsConfig::WriteConfigValueBin( SilabsConfig::kConfigKey_WiFiPSK, reinterpret_cast(mStagingNetwork.key), mStagingNetwork.keyLen)); + // TODO: Remove commit of security bitmap, as security is automatically determined by the SSID and PSK during connection. ReturnErrorOnFailure(SilabsConfig::WriteConfigValueBin(SilabsConfig::kConfigKey_WiFiSEC, &kDefaultSecurityBitmap, sizeof(kDefaultSecurityBitmap))); @@ -87,8 +73,35 @@ CHIP_ERROR SlWiFiDriver::CommitConfiguration() CHIP_ERROR SlWiFiDriver::RevertConfiguration() { + CHIP_ERROR err = CHIP_NO_ERROR; + // If reading fails, wifi is not provisioned, no need to go further. + err = SilabsConfig::ReadConfigValueBin(SilabsConfig::kConfigKey_WiFiSSID, mSavedNetwork.ssid, sizeof(mSavedNetwork.ssid), + mSavedNetwork.ssidLen); +#if defined(SL_ONNETWORK_PAIRING) && SL_ONNETWORK_PAIRING + if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) + { + memcpy(&mSavedNetwork.ssid, SL_WIFI_SSID, sizeof(SL_WIFI_SSID)); + mSavedNetwork.ssidLen = sizeof(SL_WIFI_SSID); + + err = CHIP_NO_ERROR; + } +#endif // SL_ONNETWORK_PAIRING + VerifyOrReturnError(err == CHIP_NO_ERROR, err); + + err = SilabsConfig::ReadConfigValueBin(SilabsConfig::kConfigKey_WiFiPSK, mSavedNetwork.key, sizeof(mSavedNetwork.key), + mSavedNetwork.keyLen); +#if defined(SL_ONNETWORK_PAIRING) && SL_ONNETWORK_PAIRING + if (err == CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND) + { + memcpy(&mSavedNetwork.key, SL_WIFI_PSK, sizeof(SL_WIFI_PSK)); + mSavedNetwork.keyLen = sizeof(SL_WIFI_PSK); + + err = CHIP_NO_ERROR; + } +#endif // SL_ONNETWORK_PAIRING + VerifyOrReturnError(err == CHIP_NO_ERROR, err); mStagingNetwork = mSavedNetwork; - return CHIP_NO_ERROR; + return err; } bool SlWiFiDriver::NetworkMatch(const Silabs::WifiInterface::WiFiCredentials & network, ByteSpan networkId) @@ -100,17 +113,19 @@ Status SlWiFiDriver::AddOrUpdateNetwork(ByteSpan ssid, ByteSpan credentials, Mut uint8_t & outNetworkIndex) { outDebugText.reduce_size(0); + // since we only support one network, we always return 0 outNetworkIndex = 0; - VerifyOrReturnError(mStagingNetwork.ssidLen == 0 || NetworkMatch(mStagingNetwork, ssid), Status::kBoundsExceeded); - VerifyOrReturnError(credentials.size() <= sizeof(mStagingNetwork.key), Status::kOutOfRange); + + // using ssid as the networkId VerifyOrReturnError(ssid.size() <= sizeof(mStagingNetwork.ssid), Status::kOutOfRange); + VerifyOrReturnError(credentials.size() <= sizeof(mStagingNetwork.key), Status::kOutOfRange); - VerifyOrReturnError(ssid.data() != nullptr, Status::kNetworkNotFound); + VerifyOrReturnError(ssid.data() != nullptr, Status::kNetworkIDNotFound); MutableByteSpan ssidSpan(mStagingNetwork.ssid, sizeof(mStagingNetwork.ssid)); VerifyOrReturnError(CopySpanToMutableSpan(ssid, ssidSpan) == CHIP_NO_ERROR, Status::kBoundsExceeded); mStagingNetwork.ssidLen = ssid.size(); - VerifyOrReturnError(credentials.data() != nullptr, Status::kNetworkNotFound); + VerifyOrReturnError(credentials.data() != nullptr, Status::kNetworkIDNotFound); MutableByteSpan keySpan(mStagingNetwork.key, sizeof(mStagingNetwork.key)); VerifyOrReturnError(CopySpanToMutableSpan(credentials, keySpan) == CHIP_NO_ERROR, Status::kBoundsExceeded); mStagingNetwork.keyLen = credentials.size(); @@ -140,10 +155,11 @@ Status SlWiFiDriver::ReorderNetwork(ByteSpan networkId, uint8_t index, MutableCh CHIP_ERROR SlWiFiDriver::ConnectWiFiNetwork(const char * ssid, uint8_t ssidLen, const char * key, uint8_t keyLen) { + // if the WiFi station is provisioned, disconnect it if (ConnectivityMgr().IsWiFiStationProvisioned()) { ChipLogProgress(DeviceLayer, "Disconnecting for current wifi"); - WifiInterface::GetInstance().TriggerDisconnection(); + ReturnErrorOnFailure(ConnectivityMgr().DisconnectNetwork()); } ReturnErrorOnFailure(ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_Disabled)); @@ -158,76 +174,70 @@ CHIP_ERROR SlWiFiDriver::ConnectWiFiNetwork(const char * ssid, uint8_t ssidLen, memcpy(wifiConfig.key, key, keyLen); wifiConfig.keyLen = keyLen; - wifiConfig.security.Set(chip::app::Clusters::NetworkCommissioning::WiFiSecurityBitmap::kWpa2Personal); - ChipLogProgress(NetworkProvisioning, "Setting up connection for WiFi SSID: %s", NullTerminated(ssid, ssidLen).c_str()); - // Resetting the retry connection state machine for a new access point connection - WifiInterface::GetInstance().ResetConnectionRetryInterval(); + ReturnErrorOnFailure(WifiInterface::GetInstance().SetWifiCredentials(wifiConfig)); ReturnErrorOnFailure(ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_Disabled)); ReturnErrorOnFailure(ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_Enabled)); return CHIP_NO_ERROR; } -// TODO: Re-write implementation with proper driver based callback void SlWiFiDriver::UpdateNetworkingStatus() { - if (mpStatusChangeCallback == nullptr) - { - ChipLogError(NetworkProvisioning, "networkStatusChangeCallback is nil"); - return; - } + VerifyOrReturn(mpStatusChangeCallback != nullptr); + VerifyOrReturn(mStagingNetwork.ssidLen != 0); - if (mStagingNetwork.ssidLen == 0) - { - ChipLogError(NetworkProvisioning, "ssidLen is 0"); - return; - } + bool isStationConnected = WifiInterface::GetInstance().IsStationConnected(); - ByteSpan networkId = ByteSpan((const unsigned char *) mStagingNetwork.ssid, mStagingNetwork.ssidLen); - if (!WifiInterface::GetInstance().IsStationConnected()) - { - // TODO: https://github.com/project-chip/connectedhomeip/issues/26861 - mpStatusChangeCallback->OnNetworkingStatusChange(Status::kUnknownError, MakeOptional(networkId), - MakeOptional(static_cast(SL_STATUS_FAIL))); - return; - } - mpStatusChangeCallback->OnNetworkingStatusChange(Status::kSuccess, MakeOptional(networkId), NullOptional); + Status commissioningError = isStationConnected ? Status::kSuccess : WifiInterface::GetInstance().GetLastDisconnectionReason(); + ByteSpan networkId = ByteSpan((const unsigned char *) mStagingNetwork.ssid, mStagingNetwork.ssidLen); + int32_t connectStatus = (isStationConnected ? SL_STATUS_OK : SL_STATUS_FAIL); + + ChipLogDetail(NetworkProvisioning, "UpdateNetworkingStatus: commissioningError: 0x%02X", + static_cast(commissioningError)); + + mpStatusChangeCallback->OnNetworkingStatusChange(commissioningError, MakeOptional(networkId), MakeOptional(connectStatus)); } void SlWiFiDriver::OnConnectWiFiNetwork() { - if (mpConnectCallback) - { - TEMPORARY_RETURN_IGNORED CommitConfiguration(); - mpConnectCallback->OnResult(Status::kSuccess, CharSpan(), 0); - mpConnectCallback = nullptr; - } + VerifyOrReturn(mpConnectCallback != nullptr); + + bool isStationConnected = WifiInterface::GetInstance().IsStationConnected(); + + Status commissioningError = isStationConnected ? Status::kSuccess : WifiInterface::GetInstance().GetLastDisconnectionReason(); + int32_t connectStatus = (isStationConnected ? SL_STATUS_OK : SL_STATUS_FAIL); + + ChipLogDetail(NetworkProvisioning, "OnConnectWiFiNetwork: commissioningError: 0x%02X", + static_cast(commissioningError)); + + mpConnectCallback->OnResult(commissioningError, // commissioning error + CharSpan(), // debug text + connectStatus // connect status + ); + mpConnectCallback = nullptr; } void SlWiFiDriver::ConnectNetwork(ByteSpan networkId, ConnectCallback * callback) { - CHIP_ERROR err = CHIP_NO_ERROR; - Status networkingStatus = Status::kUnknownError; + CHIP_ERROR err = CHIP_NO_ERROR; + Status commissioningError = Status::kSuccess; - VerifyOrExit(NetworkMatch(mStagingNetwork, networkId), networkingStatus = Status::kNetworkIDNotFound); - VerifyOrExit(mpConnectCallback == nullptr, networkingStatus = Status::kUnknownError); + VerifyOrExit(NetworkMatch(mStagingNetwork, networkId), commissioningError = Status::kNetworkIDNotFound); + VerifyOrExit(mpConnectCallback == nullptr, commissioningError = Status::kUnknownError); err = ConnectWiFiNetwork(reinterpret_cast(mStagingNetwork.ssid), mStagingNetwork.ssidLen, reinterpret_cast(mStagingNetwork.key), mStagingNetwork.keyLen); - if (err == CHIP_NO_ERROR) - { - mpConnectCallback = callback; - networkingStatus = Status::kSuccess; - } + VerifyOrExit(err == CHIP_NO_ERROR, commissioningError = Status::kUnknownError); + mpConnectCallback = callback; exit: - if (networkingStatus != Status::kSuccess) - { - ChipLogError(NetworkProvisioning, "Failed to connect to WiFi network: %" CHIP_ERROR_FORMAT, err.Format()); - mpConnectCallback = nullptr; - callback->OnResult(networkingStatus, CharSpan(), 0); - } + VerifyOrReturn(commissioningError != Status::kSuccess); + ChipLogError(NetworkProvisioning, "Failed to connect to WiFi network: %" CHIP_ERROR_FORMAT, err.Format()); + callback->OnResult(commissioningError, // commissioning error + CharSpan(), // debug text + SL_STATUS_FAIL // connect status + ); } uint32_t SlWiFiDriver::GetSupportedWiFiBandsMask() const @@ -240,12 +250,8 @@ bool SlWiFiDriver::StartScanWiFiNetworks(ByteSpan ssid) ChipLogDetail(DeviceLayer, "Start Scan WiFi Networks"); CHIP_ERROR err = WifiInterface::GetInstance().StartNetworkScan(ssid, OnScanWiFiNetworkDone); - if (err != CHIP_NO_ERROR) - { - ChipLogError(DeviceLayer, "StartNetworkScan failed: %" CHIP_ERROR_FORMAT, err.Format()); - return false; - } - + VerifyOrReturnValue(err == CHIP_NO_ERROR, false, + ChipLogError(DeviceLayer, "StartNetworkScan failed: %" CHIP_ERROR_FORMAT, err.Format())); return true; } @@ -255,26 +261,26 @@ void SlWiFiDriver::OnScanWiFiNetworkDone(NetworkCommissioning::WiFiScanResponse // Cannot use the driver if the instance is not initialized. VerifyOrDie(nwDriver != nullptr); // should never be null - if (!aScanResult) + // If the scan result is null, it means the scan completed. + if (aScanResult == nullptr) { ChipLogProgress(DeviceLayer, "OnScanWiFiNetworkDone: Receive all scanned networks information."); + VerifyOrReturn(nwDriver->mpScanCallback != nullptr); - if (nwDriver->mpScanCallback != nullptr) + if (mScanResponseIter.Count() == 0) { - if (mScanResponseIter.Count() == 0) - { - // if there is no network found, return kNetworkNotFound - TEMPORARY_RETURN_IGNORED DeviceLayer::SystemLayer().ScheduleLambda([nwDriver]() { - nwDriver->mpScanCallback->OnFinished(NetworkCommissioning::Status::kNetworkNotFound, CharSpan(), nullptr); - nwDriver->mpScanCallback = nullptr; - }); - return; - } - TEMPORARY_RETURN_IGNORED DeviceLayer::SystemLayer().ScheduleLambda([nwDriver]() { - nwDriver->mpScanCallback->OnFinished(NetworkCommissioning::Status::kSuccess, CharSpan(), &mScanResponseIter); + // if there is no network found, return kNetworkNotFound + LogErrorOnFailure(DeviceLayer::SystemLayer().ScheduleLambda([nwDriver]() { + nwDriver->mpScanCallback->OnFinished(NetworkCommissioning::Status::kNetworkNotFound, CharSpan(), nullptr); nwDriver->mpScanCallback = nullptr; - }); + })); + return; } + + LogErrorOnFailure(DeviceLayer::SystemLayer().ScheduleLambda([nwDriver]() { + nwDriver->mpScanCallback->OnFinished(NetworkCommissioning::Status::kSuccess, CharSpan(), &mScanResponseIter); + nwDriver->mpScanCallback = nullptr; + })); } else { @@ -284,16 +290,14 @@ void SlWiFiDriver::OnScanWiFiNetworkDone(NetworkCommissioning::WiFiScanResponse void SlWiFiDriver::ScanNetworks(ByteSpan ssid, WiFiDriver::ScanCallback * callback) { - if (callback != nullptr) - { - mpScanCallback = callback; - if (!StartScanWiFiNetworks(ssid)) - { - ChipLogError(DeviceLayer, "ScanWiFiNetworks failed to start"); - mpScanCallback = nullptr; - callback->OnFinished(Status::kUnknownError, CharSpan(), nullptr); - } - } + VerifyOrReturn(callback != nullptr); + mpScanCallback = callback; + + // If the scan fails, return an error to the callback. + VerifyOrReturn(!StartScanWiFiNetworks(ssid)); + ChipLogError(DeviceLayer, "ScanWiFiNetworks failed to start"); + mpScanCallback = nullptr; + callback->OnFinished(Status::kUnknownError, CharSpan(), nullptr); } CHIP_ERROR GetConnectedNetwork(Network & network) diff --git a/src/platform/silabs/PlatformManagerImpl.cpp b/src/platform/silabs/PlatformManagerImpl.cpp index 0e87c67efa..33efb1093f 100644 --- a/src/platform/silabs/PlatformManagerImpl.cpp +++ b/src/platform/silabs/PlatformManagerImpl.cpp @@ -195,7 +195,7 @@ void HandleWFXSystemEvent(sl_wfx_generic_message_t * eventData) ChipDeviceEvent event; memset(&event, 0, sizeof(event)); - event.Type = DeviceEventType::kWFXSystemEvent; + event.Type = DeviceEventType::kPlatformSLEvent; switch (eventData->header.id) { diff --git a/src/platform/silabs/wifi/SiWx/WifiInterfaceImpl.cpp b/src/platform/silabs/wifi/SiWx/WifiInterfaceImpl.cpp index 3409eb89b9..5052eaf310 100644 --- a/src/platform/silabs/wifi/SiWx/WifiInterfaceImpl.cpp +++ b/src/platform/silabs/wifi/SiWx/WifiInterfaceImpl.cpp @@ -597,6 +597,7 @@ void WifiInterfaceImpl::ProcessEvent(WifiPlatformEvent event) wfx_rsi.dev_state.Set(WifiInterface::WifiState::kStationConnected); wfx_rsi.dev_state.Clear(WifiInterface::WifiState::kStationConnecting); ResetConnectivityNotificationFlags(); + NotifySuccessfulConnection(); break; case WifiPlatformEvent::kStationDisconnect: { @@ -659,14 +660,9 @@ void WifiInterfaceImpl::NotifySuccessfulConnection(void) sl_status_t WifiInterfaceImpl::JoinWifiNetwork(void) { - VerifyOrReturnError( - !wfx_rsi.dev_state.HasAny(WifiInterface::WifiState::kStationConnecting, WifiInterface::WifiState::kStationConnected), - SL_STATUS_IN_PROGRESS); sl_status_t status = SL_STATUS_OK; // Start Join Network - wfx_rsi.dev_state.Set(WifiInterface::WifiState::kStationConnecting); - status = SetWifiConfigurations(); VerifyOrReturnError(status == SL_STATUS_OK, status, ChipLogError(DeviceLayer, "Failure to set the Wifi Configurations!")); @@ -709,7 +705,8 @@ sl_status_t WifiInterfaceImpl::JoinWifiNetwork(void) #endif // SL_MATTER_NEUTRAL_LESS_SWITCH_WIFI wfx_rsi.dev_state.Clear(WifiInterface::WifiState::kStationConnecting).Clear(WifiInterface::WifiState::kStationConnected); - ScheduleConnectionAttempt(); + mLastDisconnectionReason = status; + WifiInterface::NotifyDisconnection(status); return status; } @@ -742,7 +739,9 @@ sl_status_t WifiInterfaceImpl::JoinCallback(sl_wifi_event_t event, char * result ChipLogError(DeviceLayer, "JoinCallback: failed: 0x%lx", status); wfx_rsi.dev_state.Clear(WifiInterface::WifiState::kStationConnected); - mInstance.ScheduleConnectionAttempt(); + WifiInterfaceImpl & self = WifiInterfaceImpl::GetInstance(); + self.mLastDisconnectionReason = status; + self.NotifyDisconnection(status); } return status; @@ -862,6 +861,8 @@ void WifiInterfaceImpl::ClearWifiDisconnectedState() NotifyIPv4Change(false); #endif /* CHIP_DEVICE_CONFIG_ENABLE_IPV4 */ NotifyIPv6Change(false); + mLastDisconnectionReason = SL_STATUS_OK; + WifiInterface::NotifyDisconnection(mLastDisconnectionReason); } #if CHIP_CONFIG_ENABLE_ICD_SERVER @@ -893,25 +894,19 @@ CHIP_ERROR WifiInterfaceImpl::InitLitPrecheckInReconnectTimer() CHIP_ERROR WifiInterfaceImpl::ConfigureLITConnect() { - ResetConnectionRetryInterval(); - VerifyOrReturnError(IsWifiProvisioned(), CHIP_NO_ERROR); VerifyOrReturnError(!IsStationConnected(), CHIP_NO_ERROR); - if (!wfx_rsi.dev_state.Has(WifiInterface::WifiState::kStationConnecting)) - { - return ConnectToAccessPoint(); - } + CHIP_ERROR err = ConnectToAccessPoint(); + VerifyOrReturnError(err == CHIP_NO_ERROR || err == CHIP_ERROR_IN_PROGRESS, err); return CHIP_NO_ERROR; } CHIP_ERROR WifiInterfaceImpl::ConfigureLITDisconnect() { - CancelConnectionAttempt(); - wfx_rsi.dev_state.Clear(WifiInterface::WifiState::kStationConnected); - TriggerPlatformWifiDisconnection(); + TriggerDisconnection(); return CHIP_NO_ERROR; } @@ -1129,9 +1124,10 @@ CHIP_ERROR WifiInterfaceImpl::StartWifiTask() return CHIP_NO_ERROR; } -void WifiInterfaceImpl::ConfigureStationMode() +CHIP_ERROR WifiInterfaceImpl::EnableStationMode() { wfx_rsi.dev_state.Set(WifiState::kStationMode); + return CHIP_NO_ERROR; } bool WifiInterfaceImpl::IsStationModeEnabled() @@ -1166,9 +1162,6 @@ void WifiInterfaceImpl::ResetConnectivityNotificationFlags(void) { ResetIPNotificationStates(); mHasNotifiedWifiConnectivity = false; - - WifiPlatformEvent event = WifiPlatformEvent::kConnectionComplete; - PostWifiPlatformEvent(event); } #if CHIP_DEVICE_CONFIG_ENABLE_IPV4 @@ -1222,6 +1215,10 @@ CHIP_ERROR WifiInterfaceImpl::SetWifiCredentials(const WiFiCredentials & credent CHIP_ERROR WifiInterfaceImpl::ConnectToAccessPoint() { VerifyOrReturnError(IsWifiProvisioned(), CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError( + !wfx_rsi.dev_state.HasAny(WifiInterface::WifiState::kStationConnecting, WifiInterface::WifiState::kStationConnected), + CHIP_ERROR_IN_PROGRESS); + wfx_rsi.dev_state.Set(WifiInterface::WifiState::kStationConnecting); ChipLogProgress(DeviceLayer, "connect to access point: %s", wfx_rsi.credentials.ssid); @@ -1229,6 +1226,38 @@ CHIP_ERROR WifiInterfaceImpl::ConnectToAccessPoint() return CHIP_NO_ERROR; } +NetworkCommissioningStatusEnum WifiInterfaceImpl::MapToNetworkCommissioningStatusEnum(uint32_t reason) +{ + + switch (reason) + { + case SL_STATUS_OK: + return NetworkCommissioningStatusEnum::kSuccess; + case SL_STATUS_SI91X_NO_AP_FOUND: + case SL_STATUS_SI91X_BEACON_MISSED_FROM_AP_DURING_JOIN: + case SL_STATUS_SI91X_REJOIN_FAILURE: + return NetworkCommissioningStatusEnum::kNetworkNotFound; + case SL_STATUS_SI91X_INVALID_CHANNEL: + return NetworkCommissioningStatusEnum::kRegulatoryError; + case SL_STATUS_SI91X_INVALID_PSK_IN_WEP_SECURITY: + case SL_STATUS_SI91X_DEAUTHENTICATION_RECEIVED_FROM_AP: + case SL_STATUS_SI91X_ASSOCIATION_FAILED: + case SL_STATUS_SI91X_JOIN_AUTHENTICATION_FAILED: + case SL_STATUS_SI91X_MAX_BEACON_MISCOUNT: + case SL_STATUS_SI91X_DEAUTH_REQUEST_FROM_SUPPLICANT: + case SL_STATUS_SI91X_DEAUTH_REQUEST_FROM_FROM_AP: + case SL_STATUS_SI91X_AUTHENTICATION_TIMEOUT: + return NetworkCommissioningStatusEnum::kAuthFailure; + case SL_STATUS_SI91X_INVALID_SECURITY_MODE_IN_JOIN_COMMAND: + return NetworkCommissioningStatusEnum::kUnsupportedSecurity; + case SL_STATUS_SI91X_ASSOCIATION_TIMEOUT: + case SL_STATUS_SI91X_FOUR_WAY_HANDSHAKE_FAILED: + return NetworkCommissioningStatusEnum::kOtherConnectionFailure; + default: + return NetworkCommissioningStatusEnum::kUnknownError; + } +} + bool WifiInterfaceImpl::HasAnIPv4Address() { bool hasIPv4 = false; diff --git a/src/platform/silabs/wifi/SiWx/WifiInterfaceImpl.h b/src/platform/silabs/wifi/SiWx/WifiInterfaceImpl.h index 04dc669dc4..de82fb1075 100644 --- a/src/platform/silabs/wifi/SiWx/WifiInterfaceImpl.h +++ b/src/platform/silabs/wifi/SiWx/WifiInterfaceImpl.h @@ -53,7 +53,7 @@ class WifiInterfaceImpl final : public WifiInterface CHIP_ERROR GetMacAddress(sl_wfx_interface_t interface, chip::MutableByteSpan & addr) override; CHIP_ERROR StartNetworkScan(chip::ByteSpan ssid, ScanCallback callback) override; CHIP_ERROR StartWifiTask() override; - void ConfigureStationMode() override; + CHIP_ERROR EnableStationMode() override; bool IsStationConnected() override; bool IsStationModeEnabled() override; bool IsStationReady() override; @@ -70,7 +70,7 @@ class WifiInterfaceImpl final : public WifiInterface CHIP_ERROR GetAccessPointInfo(chip::DeviceLayer::NetworkCommissioning::WiFiScanResponse & info) override; CHIP_ERROR GetAccessPointExtendedInfo(wfx_wifi_scan_ext_t & info) override; CHIP_ERROR ResetCounters() override; - + chip::app::Clusters::NetworkCommissioning::NetworkCommissioningStatusEnum MapToNetworkCommissioningStatusEnum(uint32_t reason) override; #if CHIP_CONFIG_ENABLE_ICD_SERVER CHIP_ERROR ConfigureBroadcastFilter(bool enableBroadcastFilter) override; CHIP_ERROR ConfigurePowerSave(PowerSaveInterface::PowerSaveConfiguration configuration, uint32_t listenInterval) override; diff --git a/src/platform/silabs/wifi/WifiInterface.cpp b/src/platform/silabs/wifi/WifiInterface.cpp index 74567ed626..dd9847b38b 100644 --- a/src/platform/silabs/wifi/WifiInterface.cpp +++ b/src/platform/silabs/wifi/WifiInterface.cpp @@ -36,29 +36,6 @@ extern WfxRsi_t wfx_rsi; // As such we can't depend on the platform here as well extern void HandleWFXSystemEvent(sl_wfx_generic_message_t * eventData); -namespace { - -constexpr uint8_t kWlanMinRetryIntervalsInSec = 1; -constexpr uint8_t kWlanMaxRetryIntervalsInSec = 60; -uint8_t retryInterval = kWlanMinRetryIntervalsInSec; - -/** - * @brief Retry timer callback that triggers a reconnection attempt - * - * TODO: The structure of the retry needs to be redone - * - * @param arg - */ -void RetryConnectionTimerHandler(void * arg) -{ - if (chip::DeviceLayer::Silabs::WifiInterface::GetInstance().ConnectToAccessPoint() != CHIP_NO_ERROR) - { - ChipLogError(DeviceLayer, "ConnectToAccessPoint() failed."); - } -} - -} // namespace - namespace chip { namespace DeviceLayer { namespace Silabs { @@ -88,12 +65,12 @@ void WifiInterface::NotifyIPv4Change(bool gotIPv4Addr) } #endif // CHIP_DEVICE_CONFIG_ENABLE_IPV4 -void WifiInterface::NotifyDisconnection(WifiDisconnectionReasons reason) +void WifiInterface::NotifyDisconnection(uint32_t reason) { sl_wfx_disconnect_ind_t evt = {}; evt.header.id = to_underlying(WifiEvent::kDisconnect); evt.header.length = sizeof evt; - evt.body.reason = to_underlying(reason); + evt.body.reason = static_cast(reason); HandleWFXSystemEvent((sl_wfx_generic_message_t *) &evt); } @@ -107,9 +84,6 @@ void WifiInterface::NotifyConnection(const MacAddress & ap) evt.body.channel = wfx_rsi.ap_chan; #endif std::copy(ap.begin(), ap.end(), evt.body.mac); - // Resetting the retry connection state machine for a successful connection - // NOTE: This is required in case an access point gets disconnected after a successful connection. - ResetConnectionRetryInterval(); HandleWFXSystemEvent((sl_wfx_generic_message_t *) &evt); } @@ -126,11 +100,6 @@ void WifiInterface::NotifyWifiTaskInitialized(void) { sl_wfx_startup_ind_t evt = { 0 }; - // TODO: We should move this to the init function and not the notification function - // Creating a timer which will be used to retry connection with AP - mRetryTimer = osTimerNew(RetryConnectionTimerHandler, osTimerOnce, NULL, NULL); - VerifyOrReturn(mRetryTimer != nullptr); - evt.header.id = to_underlying(WifiEvent::kStartUp); evt.header.length = sizeof evt; evt.body.status = 0; @@ -142,44 +111,10 @@ void WifiInterface::NotifyWifiTaskInitialized(void) HandleWFXSystemEvent((sl_wfx_generic_message_t *) &evt); } -// TODO: The retry stategy needs to be re-worked -void WifiInterface::ScheduleConnectionAttempt() -{ - if (retryInterval > kWlanMaxRetryIntervalsInSec) - { - retryInterval = kWlanMaxRetryIntervalsInSec; - } - - if (osTimerStart(mRetryTimer, pdMS_TO_TICKS(retryInterval * 1000)) != osOK) - { - ChipLogProgress(DeviceLayer, "Failed to start retry timer"); - // Sending the join command if retry timer failed to start - if (ConnectToAccessPoint() != CHIP_NO_ERROR) - { - ChipLogError(DeviceLayer, "ConnectToAccessPoint() failed."); - } - - return; - } - - ChipLogProgress(DeviceLayer, "ScheduleConnectionAttempt : Next attempt after %d Seconds", retryInterval); - retryInterval += retryInterval; -} - -void WifiInterface::CancelConnectionAttempt() +chip::app::Clusters::NetworkCommissioning::NetworkCommissioningStatusEnum WifiInterface::GetLastDisconnectionReason() { - if (osTimerIsRunning(mRetryTimer)) - { - osTimerStop(mRetryTimer); - } + return MapToNetworkCommissioningStatusEnum(mLastDisconnectionReason); } - -void WifiInterface::ResetConnectionRetryInterval() -{ - ChipLogDetail(DeviceLayer, "ResetConnectionRetryInterval: Resetting state to default"); - retryInterval = kWlanMinRetryIntervalsInSec; -} - } // namespace Silabs } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/silabs/wifi/WifiInterface.h b/src/platform/silabs/wifi/WifiInterface.h index a3ddd6d74b..c6614ed8dc 100644 --- a/src/platform/silabs/wifi/WifiInterface.h +++ b/src/platform/silabs/wifi/WifiInterface.h @@ -220,9 +220,20 @@ class WifiInterface : public WifiStateProvider, public PowerSaveInterface virtual CHIP_ERROR StartWifiTask() = 0; /** - * @brief Configures the Wi-Fi devices as a Wi-Fi station + * @brief Enables the Wi-Fi station mode + * + * @return CHIP_ERROR CHIP_NO_ERROR if the station mode was successfully enabled + * CHIP_ERROR_INTERNAL if the station mode could not be enabled + */ + virtual CHIP_ERROR EnableStationMode() = 0; + + /** + * @brief Disables the Wi-Fi station mode + * + * @return CHIP_ERROR CHIP_NO_ERROR if the station mode was successfully disabled + * CHIP_ERROR_INTERNAL if the station mode could not be disabled */ - virtual void ConfigureStationMode() = 0; + virtual CHIP_ERROR DisableStationMode() { return CHIP_ERROR_NOT_IMPLEMENTED; } /** * @brief Triggers the device to disconnect from the connected Wi-Fi network @@ -325,9 +336,12 @@ class WifiInterface : public WifiStateProvider, public PowerSaveInterface } /** - * @brief Function resets reconnection attempt interval back to the minimum value + * @brief Function returns the last disconnection reason by mapping platform error codes + * to the NetworkCommissioningStatusEnum value + * @return NetworkCommissioningStatusEnum value for the last disconnection reason, + * kSuccess if the disconnection was user initiated otherwise the reason for the disconnection */ - void ResetConnectionRetryInterval(); + chip::app::Clusters::NetworkCommissioning::NetworkCommissioningStatusEnum GetLastDisconnectionReason(); protected: /** @@ -356,10 +370,12 @@ class WifiInterface : public WifiStateProvider, public PowerSaveInterface /** * @brief Function notifies the PlatformManager that a disconnection event occurred + * The function will use the `GetLastDisconnectionReason()` function to determine + * the reason for the disconnection and notify the PlatformManager. * - * @param reason reason for the disconnection + * @return void */ - void NotifyDisconnection(WifiDisconnectionReasons reason); + void NotifyDisconnection(uint32_t reason); /** * @brief Function notifies the PlatformManager that a connection event occurred @@ -380,26 +396,22 @@ class WifiInterface : public WifiStateProvider, public PowerSaveInterface void NotifyWifiTaskInitialized(void); /** - * @brief Function schedules a reconnection attempt with the Access Point + * @brief Function maps the disconnection reason to the NetworkCommissioningStatusEnum value * - * @note The retry interval increases exponentially with each attempt, starting from a minimum value and doubling each time, - * up to a maximum value. For example, if the initial retry interval is 1 second, the subsequent intervals will be 2 - * seconds, 4 seconds, 8 seconds, and so on, until the maximum retry interval is reached. + * @param reason disconnection reason + * @return NetworkCommissioningStatusEnum value for the disconnection reason */ - void ScheduleConnectionAttempt(); - - /** - * @brief Function cancels the on-going reconnection attempts - */ - void CancelConnectionAttempt(); + virtual chip::app::Clusters::NetworkCommissioning::NetworkCommissioningStatusEnum + MapToNetworkCommissioningStatusEnum(uint32_t reason) + { + return chip::app::Clusters::NetworkCommissioning::NetworkCommissioningStatusEnum::kUnknownError; + } bool mHasNotifiedIPv6 = false; #if CHIP_DEVICE_CONFIG_ENABLE_IPV4 bool mHasNotifiedIPv4 = false; #endif // CHIP_DEVICE_CONFIG_ENABLE_IPV4 - -private: - osTimerId_t mRetryTimer; + uint32_t mLastDisconnectionReason = 0; }; } // namespace Silabs