/* * * 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 "lega_wlan_api.h" #include #include #include #include #include #include #include #include using namespace ::chip; using namespace ::chip::DeviceLayer::Internal; namespace chip { namespace DeviceLayer { namespace NetworkCommissioning { CHIP_ERROR ASRWiFiDriver::Init(NetworkStatusChangeCallback * networkStatusChangeCallback) { CHIP_ERROR err; mpScanCallback = nullptr; mpConnectCallback = nullptr; mpStatusChangeCallback = networkStatusChangeCallback; lega_wlan_wifi_conf config = { 0 }; err = chip::DeviceLayer::Internal::ASRUtils::asr_wifi_get_config(&config); if (err == CHIP_ERROR_KEY_NOT_FOUND) { return CHIP_NO_ERROR; } memcpy(mSavedNetwork.ssid, config.wifi_ssid, config.ssid_len + 1); memcpy(mSavedNetwork.credentials, config.wifi_key, config.key_len + 1); mSavedNetwork.ssidLen = config.ssid_len; mSavedNetwork.credentialsLen = config.key_len; mStagingNetwork = mSavedNetwork; return err; } void ASRWiFiDriver::Shutdown() { mpStatusChangeCallback = nullptr; } CHIP_ERROR ASRWiFiDriver::CommitConfiguration() { lega_wlan_wifi_conf config = { 0 }; memcpy(config.wifi_ssid, mStagingNetwork.ssid, mStagingNetwork.ssidLen); memcpy(config.wifi_key, mStagingNetwork.credentials, mStagingNetwork.credentialsLen); config.ssid_len = mStagingNetwork.ssidLen; config.key_len = mStagingNetwork.credentialsLen; ReturnErrorOnFailure(chip::DeviceLayer::Internal::ASRUtils::asr_wifi_set_config(&config)); mSavedNetwork = mStagingNetwork; return CHIP_NO_ERROR; } CHIP_ERROR ASRWiFiDriver::RevertConfiguration() { mStagingNetwork = mSavedNetwork; return CHIP_NO_ERROR; } bool ASRWiFiDriver::NetworkMatch(const WiFiNetwork & network, ByteSpan networkId) { return networkId.size() == network.ssidLen && memcmp(networkId.data(), network.ssid, network.ssidLen) == 0; } Status ASRWiFiDriver::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 ASRWiFiDriver::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 ASRWiFiDriver::ReorderNetwork(ByteSpan networkId, uint8_t index, MutableCharSpan & outDebugText) { outDebugText.reduce_size(0); // Only one network is supported now VerifyOrReturnError(index == 0, Status::kOutOfRange); VerifyOrReturnError(NetworkMatch(mStagingNetwork, networkId), Status::kNetworkIDNotFound); return Status::kSuccess; } CHIP_ERROR ASRWiFiDriver::ConnectWiFiNetwork(const char * ssid, uint8_t ssidLen, const char * key, uint8_t keyLen) { CHIP_ERROR err = CHIP_NO_ERROR; ReturnErrorOnFailure(ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_Disabled)); // Set the wifi configuration lega_wlan_wifi_conf wifiConfig; // Set the wifi configuration memset(&wifiConfig, 0, sizeof(wifiConfig)); memcpy(wifiConfig.wifi_ssid, ssid, ssidLen); memcpy(wifiConfig.wifi_key, key, keyLen); wifiConfig.ssid_len = ssidLen; wifiConfig.key_len = keyLen; err = chip::DeviceLayer::Internal::ASRUtils::asr_wifi_set_config(&wifiConfig); SuccessOrExit(err); ReturnErrorOnFailure(ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_Disabled)); err = ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_Enabled); exit: return err; } void ASRWiFiDriver::OnConnectWiFiNetwork() { if (mpConnectCallback) { CommitConfiguration(); mpConnectCallback->OnResult(Status::kSuccess, CharSpan(), 0); mpConnectCallback = nullptr; } } void ASRWiFiDriver::ConnectNetwork(ByteSpan networkId, ConnectCallback * callback) { CHIP_ERROR err = CHIP_NO_ERROR; Status networkingStatus = Status::kSuccess; VerifyOrExit(NetworkMatch(mStagingNetwork, networkId), networkingStatus = Status::kNetworkIDNotFound); VerifyOrExit(mpConnectCallback == nullptr, networkingStatus = Status::kUnknownError); ChipLogProgress(NetworkProvisioning, "ASR NetworkCommissioningDelegate: SSID: %s", StringOrNullMarker(mStagingNetwork.ssid)); err = ConnectWiFiNetwork(reinterpret_cast(mStagingNetwork.ssid), mStagingNetwork.ssidLen, reinterpret_cast(mStagingNetwork.credentials), mStagingNetwork.credentialsLen); mpConnectCallback = callback; exit: if (err != CHIP_NO_ERROR) { networkingStatus = Status::kUnknownError; } if (networkingStatus != Status::kSuccess) { ChipLogError(NetworkProvisioning, "Failed to connect to WiFi network:%s", chip::ErrorStr(err)); mpConnectCallback = nullptr; callback->OnResult(networkingStatus, CharSpan(), 0); } } void ASRWiFiDriver::OnScanWiFiNetworkDone() { uint8_t ap_num = chip::DeviceLayer::Internal::ASRUtils::GetScanApNum(); if (!ap_num) { ChipLogProgress(DeviceLayer, "No AP found"); if (mpScanCallback != nullptr) { mpScanCallback->OnFinished(Status::kSuccess, CharSpan(), nullptr); mpScanCallback = nullptr; } return; } lega_wlan_scan_ap_record_t * ScanResult = chip::DeviceLayer::Internal::ASRUtils::GetScanResults(); if (ScanResult) { if (CHIP_NO_ERROR == DeviceLayer::SystemLayer().ScheduleLambda([ap_num, ScanResult]() { ASRScanResponseIterator iter(ap_num, ScanResult); if (GetInstance().mpScanCallback) { GetInstance().mpScanCallback->OnFinished(Status::kSuccess, CharSpan(), &iter); GetInstance().mpScanCallback = nullptr; } else { ChipLogError(DeviceLayer, "can't find the ScanCallback function"); } })) { ChipLogProgress(DeviceLayer, "ScheduleLambda OK"); } } else { ChipLogError(DeviceLayer, "can't get ap_records "); if (mpScanCallback) { mpScanCallback->OnFinished(Status::kUnknownError, CharSpan(), nullptr); mpScanCallback = nullptr; } } } void ASRWiFiDriver::ScanNetworks(ByteSpan ssid, WiFiDriver::ScanCallback * callback) { if (callback != nullptr) { mpScanCallback = callback; if (chip::DeviceLayer::Internal::ASRUtils::StartScanWiFiNetworks(ssid) != CHIP_NO_ERROR) { mpScanCallback = nullptr; callback->OnFinished(Status::kUnknownError, CharSpan(), nullptr); } } } CHIP_ERROR GetConnectedNetwork(Network & network) { lega_wlan_ap_info_adv_t * apinfo = lega_wlan_get_associated_apinfo(); if ((apinfo == NULL) || (apinfo->ssid[0] == 0)) { return CHIP_ERROR_INTERNAL; } uint8_t length = strnlen(reinterpret_cast(apinfo->ssid), DeviceLayer::Internal::kMaxWiFiSSIDLength); if (length > sizeof(network.networkID)) { ChipLogError(DeviceLayer, "SSID too long"); return CHIP_ERROR_INTERNAL; } memcpy(network.networkID, apinfo->ssid, length); network.networkIDLen = length; return CHIP_NO_ERROR; } void ASRWiFiDriver::OnNetworkStatusChange() { Network configuredNetwork; bool staEnabled = false, staConnected = false; VerifyOrReturn(ASRUtils::IsStationEnabled(staEnabled) == CHIP_NO_ERROR); VerifyOrReturn(staEnabled && mpStatusChangeCallback != nullptr); CHIP_ERROR err = GetConnectedNetwork(configuredNetwork); if (err != CHIP_NO_ERROR) { return; } VerifyOrReturn(ASRUtils::IsStationConnected(staConnected) == CHIP_NO_ERROR); if (staConnected) { mpStatusChangeCallback->OnNetworkingStatusChange( Status::kSuccess, MakeOptional(ByteSpan(configuredNetwork.networkID, configuredNetwork.networkIDLen)), NullOptional); return; } mpStatusChangeCallback->OnNetworkingStatusChange( Status::kUnknownError, MakeOptional(ByteSpan(configuredNetwork.networkID, configuredNetwork.networkIDLen)), MakeOptional(GetLastDisconnectReason())); } CHIP_ERROR ASRWiFiDriver::SetLastDisconnectReason(int32_t reason) { mLastDisconnectedReason = reason; return CHIP_NO_ERROR; } int32_t ASRWiFiDriver::GetLastDisconnectReason() { return mLastDisconnectedReason; } size_t ASRWiFiDriver::WiFiNetworkIterator::Count() { return mDriver->mStagingNetwork.ssidLen == 0 ? 0 : 1; } bool ASRWiFiDriver::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