/* * * 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 #include #include extern uint32_t apNum; using namespace ::chip; namespace chip { namespace DeviceLayer { namespace NetworkCommissioning { CHIP_ERROR AmebaWiFiDriver::Init(NetworkStatusChangeCallback * networkStatusChangeCallback) { CHIP_ERROR err; size_t ssidLen = 0; size_t credentialsLen = 0; mpScanCallback = nullptr; mpConnectCallback = nullptr; mpStatusChangeCallback = networkStatusChangeCallback; rtw_wifi_config_t config = { 0 }; err = chip::DeviceLayer::Internal::AmebaUtils::GetWiFiConfig(&config); if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) { return CHIP_NO_ERROR; } memcpy(mSavedNetwork.ssid, config.ssid, sizeof(config.ssid)); memcpy(mSavedNetwork.credentials, config.password, sizeof(config.password)); mSavedNetwork.ssidLen = config.ssid_len; mSavedNetwork.credentialsLen = config.password_len; mStagingNetwork = mSavedNetwork; return err; } void AmebaWiFiDriver::Shutdown() { mpStatusChangeCallback = nullptr; } CHIP_ERROR AmebaWiFiDriver::CommitConfiguration() { rtw_wifi_config_t config = { 0 }; memcpy(config.ssid, mStagingNetwork.ssid, mStagingNetwork.ssidLen); memcpy(config.password, mStagingNetwork.credentials, mStagingNetwork.credentialsLen); ReturnErrorOnFailure(chip::DeviceLayer::Internal::AmebaUtils::SetWiFiConfig(&config)); mSavedNetwork = mStagingNetwork; return CHIP_NO_ERROR; } CHIP_ERROR AmebaWiFiDriver::RevertConfiguration() { mStagingNetwork = mSavedNetwork; return CHIP_NO_ERROR; } bool AmebaWiFiDriver::NetworkMatch(const WiFiNetwork & network, ByteSpan networkId) { return networkId.size() == network.ssidLen && memcmp(networkId.data(), network.ssid, network.ssidLen) == 0; } Status AmebaWiFiDriver::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); mStagingNetwork = {}; memcpy(mStagingNetwork.credentials, credentials.data(), credentials.size()); mStagingNetwork.credentialsLen = static_cast(credentials.size()); memcpy(mStagingNetwork.ssid, ssid.data(), ssid.size()); mStagingNetwork.ssidLen = static_cast(ssid.size()); return Status::kSuccess; } Status AmebaWiFiDriver::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 = {}; mStagingNetwork.ssidLen = 0; return Status::kSuccess; } Status AmebaWiFiDriver::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 AmebaWiFiDriver::ConnectWiFiNetwork(const char * ssid, uint8_t ssidLen, const char * key, uint8_t keyLen) { CHIP_ERROR err = CHIP_NO_ERROR; bool connected; #if CHIP_DEVICE_CONFIG_ENABLE_WIFI // If device is already connected to WiFi, then disconnect the WiFi, chip::DeviceLayer::Internal::AmebaUtils::IsStationConnected(connected); if (connected) { ConnectivityMgrImpl().ChangeWiFiStationState(ConnectivityManager::kWiFiStationState_Disconnecting); ChipLogProgress(DeviceLayer, "Disconnecting WiFi station interface"); err = chip::DeviceLayer::Internal::AmebaUtils::WiFiDisconnect(); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "WiFiDisconnect() failed"); return err; } } // clear the WiFi configurations and add the newly provided WiFi configurations. if (chip::DeviceLayer::Internal::AmebaUtils::IsStationProvisioned()) { err = chip::DeviceLayer::Internal::AmebaUtils::ClearWiFiConfig(); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "ClearWiFiStationProvision failed"); return err; } } DeviceLayer::ConnectivityManager::WiFiStationState state = DeviceLayer::ConnectivityManager::kWiFiStationState_Connecting; DeviceLayer::SystemLayer().ScheduleLambda([state, ssid, key]() { ConnectivityMgrImpl().ChangeWiFiStationState(state); chip::DeviceLayer::Internal::AmebaUtils::WiFiConnect(ssid, key); }); #endif return err; } void AmebaWiFiDriver::OnConnectWiFiNetwork() { if (mpConnectCallback) { mpConnectCallback->OnResult(Status::kSuccess, CharSpan(), 0); mpConnectCallback = nullptr; } } void AmebaWiFiDriver::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, "Ameba NetworkCommissioningDelegate: SSID: %s", 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); } } CHIP_ERROR AmebaWiFiDriver::StartScanWiFiNetworks(ByteSpan ssid) { if (!ssid.empty()) // ssid is given, only scan this network { matter_scan_networks_with_ssid(ssid.data(), ssid.size()); } else // scan all networks { matter_scan_networks(); } return CHIP_NO_ERROR; } void AmebaWiFiDriver::OnScanWiFiNetworkDone() { uint16_t NumAP = apNum; apNum = 0; if (!NumAP) { ChipLogProgress(DeviceLayer, "No AP found"); if (mpScanCallback != nullptr) { mpScanCallback->OnFinished(Status::kSuccess, CharSpan(), nullptr); mpScanCallback = nullptr; } return; } rtw_scan_result_t * ScanResult = matter_get_scan_results(); if (ScanResult) { if (CHIP_NO_ERROR == DeviceLayer::SystemLayer().ScheduleLambda([NumAP, ScanResult]() { AmebaScanResponseIterator iter(NumAP, 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; } } } CHIP_ERROR GetConfiguredNetwork(Network & network) { rtw_wifi_setting_t wifi_setting; matter_wifi_get_setting(WLAN0_IDX, &wifi_setting); uint8_t length = strnlen(reinterpret_cast(wifi_setting.ssid), DeviceLayer::Internal::kMaxWiFiSSIDLength); if (length > sizeof(network.networkID)) { ChipLogError(DeviceLayer, "SSID too long"); return CHIP_ERROR_INTERNAL; } memcpy(network.networkID, wifi_setting.ssid, length); network.networkIDLen = length; return CHIP_NO_ERROR; } void AmebaWiFiDriver::OnNetworkStatusChange() { Network configuredNetwork; bool staEnabled = false, staConnected = false; VerifyOrReturn(chip::DeviceLayer::Internal::AmebaUtils::IsStationEnabled(staEnabled) == CHIP_NO_ERROR); VerifyOrReturn(staEnabled && mpStatusChangeCallback != nullptr); CHIP_ERROR err = GetConfiguredNetwork(configuredNetwork); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "Failed to get configured network when updating network status: %s", err.AsString()); return; } VerifyOrReturn(chip::DeviceLayer::Internal::AmebaUtils::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())); } void AmebaWiFiDriver::ScanNetworks(ByteSpan ssid, WiFiDriver::ScanCallback * callback) { if (callback != nullptr) { mpScanCallback = callback; if (StartScanWiFiNetworks(ssid) != CHIP_NO_ERROR) { mpScanCallback = nullptr; callback->OnFinished(Status::kUnknownError, CharSpan(), nullptr); } } } CHIP_ERROR AmebaWiFiDriver::SetLastDisconnectReason(const ChipDeviceEvent * event) { VerifyOrReturnError(event->Type == DeviceEventType::kRtkWiFiStationDisconnectedEvent, CHIP_ERROR_INVALID_ARGUMENT); mLastDisconnectedReason = matter_wifi_get_last_error(); return CHIP_NO_ERROR; } int32_t AmebaWiFiDriver::GetLastDisconnectReason() { return mLastDisconnectedReason; } size_t AmebaWiFiDriver::WiFiNetworkIterator::Count() { return mDriver->mStagingNetwork.ssidLen == 0 ? 0 : 1; } bool AmebaWiFiDriver::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 configuredNetwork; CHIP_ERROR err = GetConfiguredNetwork(configuredNetwork); if (err == CHIP_NO_ERROR) { bool isConnected = false; err = chip::DeviceLayer::Internal::AmebaUtils::IsStationConnected(isConnected); if (isConnected && configuredNetwork.networkIDLen == item.networkIDLen && memcmp(configuredNetwork.networkID, item.networkID, item.networkIDLen) == 0) { item.connected = true; } } return true; } } // namespace NetworkCommissioning } // namespace DeviceLayer } // namespace chip