blob: 7404a0272297be1fb344c32d750af15c4fde8a41 [file] [log] [blame]
/*
*
* Copyright (c) 2024 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 "ZephyrWifiDriver.h"
#include <stdint.h>
#include <platform/KeyValueStoreManager.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/SafeInt.h>
#include <lib/support/TypeTraits.h>
#include <platform/CHIPDeviceLayer.h>
using namespace ::chip;
using namespace ::chip::DeviceLayer::Internal;
using namespace ::chip::DeviceLayer::PersistedStorage;
using namespace ::chip::app::Clusters::NetworkCommissioning;
namespace chip {
namespace DeviceLayer {
namespace NetworkCommissioning {
size_t ZephyrWifiDriver::WiFiNetworkIterator::Count()
{
VerifyOrReturnValue(mDriver != nullptr, 0);
return mDriver->mStagingNetwork.IsConfigured() ? 1 : 0;
}
bool ZephyrWifiDriver::WiFiNetworkIterator::Next(Network & item)
{
// we assume only one network is actually supported
// TODO: verify if this can be extended
if (mExhausted || 0 == Count())
{
return false;
}
memcpy(item.networkID, mDriver->mStagingNetwork.ssid, mDriver->mStagingNetwork.ssidLen);
item.networkIDLen = static_cast<uint8_t>(mDriver->mStagingNetwork.ssidLen);
item.connected = false;
mExhausted = true;
WiFiManager::WiFiInfo wifiInfo;
if (CHIP_NO_ERROR == WiFiManager::Instance().GetWiFiInfo(wifiInfo))
{
if (WiFiManager::StationStatus::CONNECTED <= WiFiManager::Instance().GetStationStatus())
{
if (wifiInfo.mSsidLen == item.networkIDLen && 0 == memcmp(wifiInfo.mSsid, item.networkID, wifiInfo.mSsidLen))
{
item.connected = true;
}
}
}
return true;
}
bool ZephyrWifiScanResponseIterator::Next(WiFiScanResponse & item)
{
if (mResultId < mResultCount)
{
item = mResults[mResultId++];
return true;
}
return false;
}
void ZephyrWifiScanResponseIterator::Release()
{
mResultId = mResultCount = 0;
Platform::MemoryFree(mResults);
mResults = nullptr;
}
void ZephyrWifiScanResponseIterator::Add(const WiFiScanResponse & result)
{
void * newResults = Platform::MemoryRealloc(mResults, (mResultCount + 1) * sizeof(WiFiScanResponse));
if (newResults)
{
mResults = static_cast<WiFiScanResponse *>(newResults);
mResults[mResultCount++] = result;
}
}
CHIP_ERROR ZephyrWifiDriver::Init(NetworkStatusChangeCallback * networkStatusChangeCallback)
{
mpNetworkStatusChangeCallback = networkStatusChangeCallback;
LoadFromStorage();
if (mStagingNetwork.IsConfigured())
{
WiFiManager::ConnectionHandling handling{ [](const wifi_conn_status & connStatus) {
Instance().OnNetworkConnStatusChanged(connStatus);
},
System::Clock::Seconds32{ kWiFiConnectNetworkTimeoutSeconds } };
ReturnErrorOnFailure(
WiFiManager::Instance().Connect(mStagingNetwork.GetSsidSpan(), mStagingNetwork.GetPassSpan(), handling));
}
return CHIP_NO_ERROR;
}
void ZephyrWifiDriver::OnNetworkConnStatusChanged(const wifi_conn_status & connStatus)
{
// TODO: check if we can report more accurate errors
Status status = connStatus ? Status::kUnknownError : Status::kSuccess;
if (status == Status::kSuccess)
{
ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_Enabled);
}
if (mpNetworkStatusChangeCallback)
{
const uint8_t * ssid{};
size_t ssidLen{};
WiFiManager::WiFiInfo wifiInfo;
if (CHIP_NO_ERROR == WiFiManager::Instance().GetWiFiInfo(wifiInfo))
{
ssid = wifiInfo.mSsid;
ssidLen = wifiInfo.mSsidLen;
}
else
{
ssid = WiFiManager::Instance().GetWantedNetwork().ssid;
ssidLen = WiFiManager::Instance().GetWantedNetwork().ssidLen;
}
mpNetworkStatusChangeCallback->OnNetworkingStatusChange(status, MakeOptional(ByteSpan(ssid, ssidLen)),
connStatus ? MakeOptional(static_cast<int32_t>(connStatus))
: NullOptional);
}
if (mpConnectCallback)
{
mpConnectCallback->OnResult(status, CharSpan(), 0);
mpConnectCallback = nullptr;
}
}
void ZephyrWifiDriver::Shutdown()
{
mpNetworkStatusChangeCallback = nullptr;
}
CHIP_ERROR ZephyrWifiDriver::CommitConfiguration()
{
ReturnErrorOnFailure(KeyValueStoreMgr().Put(kPassKey, mStagingNetwork.pass, mStagingNetwork.passLen));
ReturnErrorOnFailure(KeyValueStoreMgr().Put(kSsidKey, mStagingNetwork.ssid, mStagingNetwork.ssidLen));
return CHIP_NO_ERROR;
}
CHIP_ERROR ZephyrWifiDriver::RevertConfiguration()
{
LoadFromStorage();
if (WiFiManager::StationStatus::CONNECTING <= WiFiManager::Instance().GetStationStatus())
{
WiFiManager::WiFiInfo wifiInfo;
ReturnErrorOnFailure(WiFiManager::Instance().GetWiFiInfo(wifiInfo));
if (mStagingNetwork.GetSsidSpan().data_equal(ByteSpan(wifiInfo.mSsid, wifiInfo.mSsidLen)))
{
// we are already connected to this network, so return prematurely
return CHIP_NO_ERROR;
}
WiFiManager::Instance().Disconnect();
}
if (mStagingNetwork.IsConfigured())
{
WiFiManager::ConnectionHandling handling{ [](const wifi_conn_status & connStatus) {
Instance().OnNetworkConnStatusChanged(connStatus);
},
System::Clock::Seconds32{ kWiFiConnectNetworkTimeoutSeconds } };
ReturnErrorOnFailure(
WiFiManager::Instance().Connect(mStagingNetwork.GetSsidSpan(), mStagingNetwork.GetPassSpan(), handling));
}
return CHIP_NO_ERROR;
}
Status ZephyrWifiDriver::AddOrUpdateNetwork(ByteSpan ssid, ByteSpan credentials, MutableCharSpan & outDebugText,
uint8_t & outNetworkIndex)
{
outDebugText = {};
outNetworkIndex = 0;
VerifyOrReturnError(!mStagingNetwork.IsConfigured() || ssid.data_equal(mStagingNetwork.GetSsidSpan()), Status::kBoundsExceeded);
VerifyOrReturnError(ssid.size() <= sizeof(mStagingNetwork.ssid), Status::kOutOfRange);
VerifyOrReturnError(credentials.size() <= sizeof(mStagingNetwork.pass), Status::kOutOfRange);
mStagingNetwork.Erase();
memcpy(mStagingNetwork.ssid, ssid.data(), ssid.size());
memcpy(mStagingNetwork.pass, credentials.data(), credentials.size());
mStagingNetwork.ssidLen = ssid.size();
mStagingNetwork.passLen = credentials.size();
return Status::kSuccess;
}
Status ZephyrWifiDriver::RemoveNetwork(ByteSpan networkId, MutableCharSpan & outDebugText, uint8_t & outNetworkIndex)
{
outDebugText = {};
outNetworkIndex = 0;
VerifyOrReturnError(networkId.data_equal(mStagingNetwork.GetSsidSpan()), Status::kNetworkIDNotFound);
mStagingNetwork.Clear();
return Status::kSuccess;
}
Status ZephyrWifiDriver::ReorderNetwork(ByteSpan networkId, uint8_t index, MutableCharSpan & outDebugText)
{
outDebugText = {};
// Only one network is supported for now
VerifyOrReturnError(index == 0, Status::kOutOfRange);
VerifyOrReturnError(networkId.data_equal(mStagingNetwork.GetSsidSpan()), Status::kNetworkIDNotFound);
return Status::kSuccess;
}
void ZephyrWifiDriver::ConnectNetwork(ByteSpan networkId, ConnectCallback * callback)
{
Status status = Status::kSuccess;
WiFiManager::StationStatus stationStatus;
WiFiManager::ConnectionHandling handling{ [](const wifi_conn_status & connStatus) {
Instance().OnNetworkConnStatusChanged(connStatus);
},
System::Clock::Seconds32{ kWiFiConnectNetworkTimeoutSeconds } };
VerifyOrExit(mpConnectCallback == nullptr, status = Status::kUnknownError);
mpConnectCallback = callback;
stationStatus = WiFiManager::Instance().GetStationStatus();
VerifyOrExit(WiFiManager::StationStatus::CONNECTING != stationStatus, status = Status::kOtherConnectionFailure);
VerifyOrExit(networkId.data_equal(mStagingNetwork.GetSsidSpan()), status = Status::kNetworkIDNotFound);
WiFiManager::Instance().Connect(mStagingNetwork.GetSsidSpan(), mStagingNetwork.GetPassSpan(), handling);
exit:
if (mpConnectCallback != nullptr)
{
if (status == Status::kSuccess)
{
/* If the device is already connected to a network, send the success status right now to the cluster
* to make sure we have time to send command response before switch to another network */
if (stationStatus >= WiFiManager::StationStatus::CONNECTED)
{
mpConnectCallback->OnResult(status, CharSpan(), 0);
mpConnectCallback = nullptr;
}
}
else
{
mpConnectCallback->OnResult(status, CharSpan(), 0);
mpConnectCallback = nullptr;
}
}
}
void ZephyrWifiDriver::LoadFromStorage()
{
WiFiManager::WiFiNetwork network;
mStagingNetwork = {};
ReturnOnFailure(KeyValueStoreMgr().Get(kSsidKey, network.ssid, sizeof(network.ssid), &network.ssidLen));
ReturnOnFailure(KeyValueStoreMgr().Get(kPassKey, network.pass, sizeof(network.pass), &network.passLen));
mStagingNetwork = network;
}
void ZephyrWifiDriver::OnScanWiFiNetworkDone(const WiFiManager::ScanDoneStatus & status)
{
VerifyOrReturn(mScanCallback != nullptr);
mScanCallback->OnFinished(status ? Status::kUnknownError : Status::kSuccess, CharSpan(), &mScanResponseIterator);
mScanCallback = nullptr;
}
void ZephyrWifiDriver::OnScanWiFiNetworkResult(const WiFiScanResponse & response)
{
mScanResponseIterator.Add(response);
}
void ZephyrWifiDriver::ScanNetworks(ByteSpan ssid, WiFiDriver::ScanCallback * callback)
{
mScanCallback = callback;
CHIP_ERROR error = WiFiManager::Instance().Scan(
ssid, [](const WiFiScanResponse & response) { Instance().OnScanWiFiNetworkResult(response); },
[](const WiFiManager::ScanDoneStatus & status) { Instance().OnScanWiFiNetworkDone(status); });
if (error != CHIP_NO_ERROR)
{
mScanCallback = nullptr;
callback->OnFinished(Status::kUnknownError, CharSpan(), nullptr);
}
}
uint32_t ZephyrWifiDriver::GetSupportedWiFiBandsMask() const
{
uint32_t bands = static_cast<uint32_t>(1UL << chip::to_underlying(WiFiBandEnum::k2g4));
bands |= static_cast<uint32_t>(1UL << chip::to_underlying(WiFiBandEnum::k5g));
return bands;
}
} // namespace NetworkCommissioning
} // namespace DeviceLayer
} // namespace chip