/* * * Copyright (c) 2021 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. */ #include #include #include #include #include #include using namespace ::chip; using namespace ::chip::DeviceLayer::Internal; namespace chip { namespace DeviceLayer { namespace NetworkCommissioning { namespace { NetworkCommissioning::WiFiScanResponse * sScanResult; SlScanResponseIterator mScanResponseIter(sScanResult); } // namespace CHIP_ERROR SlWiFiDriver::Init(NetworkStatusChangeCallback * networkStatusChangeCallback) { CHIP_ERROR err; size_t ssidLen = 0; size_t credentialsLen = 0; mpScanCallback = nullptr; mpConnectCallback = nullptr; mpStatusChangeCallback = networkStatusChangeCallback; #ifdef SL_ONNETWORK_PAIRING memcpy(&mSavedNetwork.ssid[0], SL_WIFI_SSID, sizeof(SL_WIFI_SSID)); memcpy(&mSavedNetwork.credentials[0], SL_WIFI_PSK, sizeof(SL_WIFI_PSK)); credentialsLen = sizeof(SL_WIFI_PSK); ssidLen = sizeof(SL_WIFI_SSID); mSavedNetwork.credentialsLen = credentialsLen; mSavedNetwork.ssidLen = ssidLen; mStagingNetwork = mSavedNetwork; err = CHIP_NO_ERROR; #else // If reading fails, wifi is not provisioned, no need to go further. err = SilabsConfig::ReadConfigValueStr(SilabsConfig::kConfigKey_WiFiSSID, mSavedNetwork.ssid, sizeof(mSavedNetwork.ssid), ssidLen); VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_NO_ERROR); err = SilabsConfig::ReadConfigValueStr(SilabsConfig::kConfigKey_WiFiPSK, mSavedNetwork.credentials, sizeof(mSavedNetwork.credentials), credentialsLen); VerifyOrReturnError(err == CHIP_NO_ERROR, CHIP_NO_ERROR); mSavedNetwork.credentialsLen = credentialsLen; mSavedNetwork.ssidLen = ssidLen; mStagingNetwork = mSavedNetwork; #endif ConnectWiFiNetwork(mSavedNetwork.ssid, ssidLen, mSavedNetwork.credentials, credentialsLen); return err; } CHIP_ERROR SlWiFiDriver::CommitConfiguration() { uint8_t securityType = WFX_SEC_WPA2; ReturnErrorOnFailure(SilabsConfig::WriteConfigValueStr(SilabsConfig::kConfigKey_WiFiSSID, mStagingNetwork.ssid)); ReturnErrorOnFailure(SilabsConfig::WriteConfigValueStr(SilabsConfig::kConfigKey_WiFiPSK, mStagingNetwork.credentials)); ReturnErrorOnFailure(SilabsConfig::WriteConfigValueBin(SilabsConfig::kConfigKey_WiFiSEC, &securityType, sizeof(securityType))); mSavedNetwork = mStagingNetwork; return CHIP_NO_ERROR; } CHIP_ERROR SlWiFiDriver::RevertConfiguration() { mStagingNetwork = mSavedNetwork; return CHIP_NO_ERROR; } bool SlWiFiDriver::NetworkMatch(const WiFiNetwork & network, ByteSpan networkId) { return networkId.size() == network.ssidLen && memcmp(networkId.data(), network.ssid, network.ssidLen) == 0; } Status SlWiFiDriver::AddOrUpdateNetwork(ByteSpan ssid, ByteSpan credentials, MutableCharSpan & outDebugText, uint8_t & outNetworkIndex) { outDebugText.reduce_size(0); outNetworkIndex = 0; VerifyOrReturnError(mStagingNetwork.ssidLen == 0 || NetworkMatch(mStagingNetwork, ssid), Status::kBoundsExceeded); VerifyOrReturnError(credentials.size() <= sizeof(mStagingNetwork.credentials), Status::kOutOfRange); VerifyOrReturnError(ssid.size() <= sizeof(mStagingNetwork.ssid), Status::kOutOfRange); memset(mStagingNetwork.credentials, 0, sizeof(mStagingNetwork.credentials)); memcpy(mStagingNetwork.credentials, credentials.data(), credentials.size()); mStagingNetwork.credentialsLen = static_cast(credentials.size()); memset(mStagingNetwork.ssid, 0, sizeof(mStagingNetwork.ssid)); memcpy(mStagingNetwork.ssid, ssid.data(), ssid.size()); mStagingNetwork.ssidLen = static_cast(ssid.size()); return Status::kSuccess; } Status SlWiFiDriver::RemoveNetwork(ByteSpan networkId, MutableCharSpan & outDebugText, uint8_t & outNetworkIndex) { outDebugText.reduce_size(0); outNetworkIndex = 0; VerifyOrReturnError(NetworkMatch(mStagingNetwork, networkId), Status::kNetworkIDNotFound); // Use empty ssid for representing invalid network mStagingNetwork.ssidLen = 0; return Status::kSuccess; } Status SlWiFiDriver::ReorderNetwork(ByteSpan networkId, uint8_t index, MutableCharSpan & outDebugText) { outDebugText.reduce_size(0); // Only one network is supported for now VerifyOrReturnError(index == 0, Status::kOutOfRange); VerifyOrReturnError(NetworkMatch(mStagingNetwork, networkId), Status::kNetworkIDNotFound); return Status::kSuccess; } CHIP_ERROR SlWiFiDriver::ConnectWiFiNetwork(const char * ssid, uint8_t ssidLen, const char * key, uint8_t keyLen) { int32_t status = SL_STATUS_OK; if (ConnectivityMgr().IsWiFiStationProvisioned()) { ChipLogProgress(DeviceLayer, "Disconecting for current wifi"); status = wfx_sta_discon(); if (status != SL_STATUS_OK) { return CHIP_ERROR_INTERNAL; } } ReturnErrorOnFailure(ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_Disabled)); // Set the wifi configuration wfx_wifi_provision_t wifiConfig; memset(&wifiConfig, 0, sizeof(wifiConfig)); VerifyOrReturnError(ssidLen <= WFX_MAX_SSID_LENGTH, CHIP_ERROR_BUFFER_TOO_SMALL); memcpy(wifiConfig.ssid, ssid, ssidLen); wifiConfig.ssid_length = ssidLen; VerifyOrReturnError(keyLen < WFX_MAX_PASSKEY_LENGTH, CHIP_ERROR_BUFFER_TOO_SMALL); memcpy(wifiConfig.passkey, key, keyLen); wifiConfig.passkey_length = keyLen; wifiConfig.security = WFX_SEC_WPA2; ChipLogProgress(NetworkProvisioning, "Setting up connection for WiFi SSID: %.*s", static_cast(ssidLen), ssid); // Configure the WFX WiFi interface. wfx_set_wifi_provision(&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; } if (mStagingNetwork.ssidLen == 0) { ChipLogError(NetworkProvisioning, "ssidLen is 0"); return; } ByteSpan networkId = ByteSpan((const unsigned char *) mStagingNetwork.ssid, mStagingNetwork.ssidLen); if (!wfx_is_sta_connected()) { // 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); } void SlWiFiDriver::OnConnectWiFiNetwork() { if (mpConnectCallback) { CommitConfiguration(); mpConnectCallback->OnResult(Status::kSuccess, CharSpan(), 0); mpConnectCallback = nullptr; } } void SlWiFiDriver::ConnectNetwork(ByteSpan networkId, ConnectCallback * callback) { CHIP_ERROR err = CHIP_NO_ERROR; Status networkingStatus = Status::kUnknownError; VerifyOrExit(NetworkMatch(mStagingNetwork, networkId), networkingStatus = Status::kNetworkIDNotFound); VerifyOrExit(mpConnectCallback == nullptr, networkingStatus = Status::kUnknownError); err = ConnectWiFiNetwork(mStagingNetwork.ssid, mStagingNetwork.ssidLen, mStagingNetwork.credentials, mStagingNetwork.credentialsLen); if (err == CHIP_NO_ERROR) { mpConnectCallback = callback; networkingStatus = Status::kSuccess; } exit: if (networkingStatus != Status::kSuccess) { ChipLogError(NetworkProvisioning, "Failed to connect to WiFi network:%s", chip::ErrorStr(err)); mpConnectCallback = nullptr; callback->OnResult(networkingStatus, CharSpan(), 0); } } chip::BitFlags SlWiFiDriver::ConvertSecuritytype(wfx_sec_t security) { chip::BitFlags securityType; if (security == WFX_SEC_NONE) { securityType = WiFiSecurity::kUnencrypted; } else if (security == WFX_SEC_WEP) { securityType = WiFiSecurity::kWep; } else if (security == WFX_SEC_WPA) { securityType = WiFiSecurity::kWpaPersonal; } else if (security == WFX_SEC_WPA2) { securityType = WiFiSecurity::kWpa2Personal; } else if (security == WFX_SEC_WPA3) { securityType = WiFiSecurity::kWpa3Personal; } else { // wfx_sec_t support more type securityType = WiFiSecurity::kUnencrypted; } return securityType; } bool SlWiFiDriver::StartScanWiFiNetworks(ByteSpan ssid) { bool scanStarted = false; ChipLogProgress(DeviceLayer, "Start Scan WiFi Networks"); if (!ssid.empty()) // ssid is given, only scan this network { char cSsid[DeviceLayer::Internal::kMaxWiFiSSIDLength] = {}; memcpy(cSsid, ssid.data(), ssid.size()); scanStarted = wfx_start_scan(cSsid, OnScanWiFiNetworkDone); } else // scan all networks { scanStarted = wfx_start_scan(nullptr, OnScanWiFiNetworkDone); } return scanStarted; } void SlWiFiDriver::OnScanWiFiNetworkDone(wfx_wifi_scan_result_t * aScanResult) { ChipLogProgress(DeviceLayer, "OnScanWiFiNetworkDone"); if (!aScanResult) { if (GetInstance().mpScanCallback != nullptr) { if (mScanResponseIter.Count() == 0) { // if there is no network found, return kNetworkNotFound DeviceLayer::SystemLayer().ScheduleLambda([]() { GetInstance().mpScanCallback->OnFinished(NetworkCommissioning::Status::kNetworkNotFound, CharSpan(), nullptr); GetInstance().mpScanCallback = nullptr; }); return; } DeviceLayer::SystemLayer().ScheduleLambda([]() { GetInstance().mpScanCallback->OnFinished(NetworkCommissioning::Status::kSuccess, CharSpan(), &mScanResponseIter); GetInstance().mpScanCallback = nullptr; }); } } else { NetworkCommissioning::WiFiScanResponse scanResponse = {}; scanResponse.security.Set(GetInstance().ConvertSecuritytype(aScanResult->security)); scanResponse.channel = aScanResult->chan; scanResponse.rssi = aScanResult->rssi; scanResponse.ssidLen = strnlen(aScanResult->ssid, DeviceLayer::Internal::kMaxWiFiSSIDLength); memcpy(scanResponse.ssid, aScanResult->ssid, scanResponse.ssidLen); memcpy(scanResponse.bssid, aScanResult->bssid, sizeof(scanResponse.bssid)); mScanResponseIter.Add(&scanResponse); } } 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); } } } CHIP_ERROR GetConnectedNetwork(Network & network) { wfx_wifi_provision_t wifiConfig; network.networkIDLen = 0; network.connected = false; // we are able to fetch the wifi provision data and STA should be connected VerifyOrReturnError(wfx_get_wifi_provision(&wifiConfig), CHIP_ERROR_UNINITIALIZED); VerifyOrReturnError(wfx_is_sta_connected(), CHIP_ERROR_NOT_CONNECTED); network.connected = true; uint8_t length = strnlen(wifiConfig.ssid, DeviceLayer::Internal::kMaxWiFiSSIDLength); VerifyOrReturnError(length < sizeof(network.networkID), CHIP_ERROR_BUFFER_TOO_SMALL); memcpy(network.networkID, wifiConfig.ssid, length); network.networkIDLen = length; return CHIP_NO_ERROR; } size_t SlWiFiDriver::WiFiNetworkIterator::Count() { return mDriver->mStagingNetwork.ssidLen == 0 ? 0 : 1; } bool SlWiFiDriver::WiFiNetworkIterator::Next(Network & item) { if (mExhausted || mDriver->mStagingNetwork.ssidLen == 0) { return false; } memcpy(item.networkID, mDriver->mStagingNetwork.ssid, mDriver->mStagingNetwork.ssidLen); item.networkIDLen = mDriver->mStagingNetwork.ssidLen; item.connected = false; mExhausted = true; Network connectedNetwork; CHIP_ERROR err = GetConnectedNetwork(connectedNetwork); if (err == CHIP_NO_ERROR) { if (connectedNetwork.networkIDLen == item.networkIDLen && memcmp(connectedNetwork.networkID, item.networkID, item.networkIDLen) == 0) { item.connected = true; } } return true; } } // namespace NetworkCommissioning } // namespace DeviceLayer } // namespace chip