blob: c201dad7a6661c8d8dc0efc24861286f07dcf880 [file] [log] [blame]
/*
*
* Copyright (c) 2021-2022 Project CHIP Authors
* Copyright 2023 NXP
*
* 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 <platform/CHIPDeviceLayer.h>
#include "NetworkCommissioningDriver.h"
#include <lib/support/CodeUtils.h>
#include <lib/support/SafeInt.h>
#include <wlan.h>
using namespace ::chip;
#if CHIP_DEVICE_CONFIG_ENABLE_WPA
namespace chip {
namespace DeviceLayer {
namespace NetworkCommissioning {
namespace {
constexpr char kWiFiSSIDKeyName[] = "wifi-ssid";
constexpr char kWiFiCredentialsKeyName[] = "wifi-pass";
} // namespace
class NXPScanResponseIterator : public Iterator<WiFiScanResponse>
{
public:
NXPScanResponseIterator(const size_t size, WiFiScanResponse * responses) : mSize(size), mResponses(responses) {}
size_t Count() override { return mSize; }
bool Next(WiFiScanResponse & item) override
{
if (mIternum >= mSize)
{
return false;
}
item = mResponses[mIternum];
mIternum++;
return true;
}
void Release() override {}
private:
const size_t mSize;
WiFiScanResponse * mResponses;
size_t mIternum = 0;
};
CHIP_ERROR NXPWiFiDriver::Init(NetworkStatusChangeCallback * networkStatusChangeCallback)
{
CHIP_ERROR err;
size_t ssidLen = 0;
size_t credentialsLen = 0;
err = PersistedStorage::KeyValueStoreMgr().Get(kWiFiSSIDKeyName, mSavedNetwork.ssid, sizeof(mSavedNetwork.ssid), &ssidLen);
if (err != CHIP_NO_ERROR)
{
ChipLogProgress(DeviceLayer, "WiFi network SSID not retrieved from persisted storage: %" CHIP_ERROR_FORMAT, err.Format());
return err;
}
err = PersistedStorage::KeyValueStoreMgr().Get(kWiFiCredentialsKeyName, mSavedNetwork.credentials,
sizeof(mSavedNetwork.credentials), &credentialsLen);
if (err != CHIP_NO_ERROR)
{
ChipLogProgress(DeviceLayer, "WiFi network credentials not retrieved from persisted storage: %" CHIP_ERROR_FORMAT,
err.Format());
return err;
}
mSavedNetwork.credentialsLen = credentialsLen;
mSavedNetwork.ssidLen = ssidLen;
mStagingNetwork = mSavedNetwork;
mpScanCallback = nullptr;
mpConnectCallback = nullptr;
mpStatusChangeCallback = networkStatusChangeCallback;
// Connect to saved network
err = ConnectWiFiNetwork(mSavedNetwork.ssid, ssidLen, mSavedNetwork.credentials, credentialsLen);
return err;
}
void NXPWiFiDriver::Shutdown()
{
mpStatusChangeCallback = nullptr;
}
CHIP_ERROR NXPWiFiDriver::CommitConfiguration()
{
ReturnErrorOnFailure(PersistedStorage::KeyValueStoreMgr().Put(kWiFiSSIDKeyName, mStagingNetwork.ssid, mStagingNetwork.ssidLen));
ReturnErrorOnFailure(PersistedStorage::KeyValueStoreMgr().Put(kWiFiCredentialsKeyName, mStagingNetwork.credentials,
mStagingNetwork.credentialsLen));
mSavedNetwork = mStagingNetwork;
return CHIP_NO_ERROR;
}
CHIP_ERROR NXPWiFiDriver::RevertConfiguration()
{
mStagingNetwork = mSavedNetwork;
return CHIP_NO_ERROR;
}
bool NXPWiFiDriver::NetworkMatch(const WiFiNetwork & network, ByteSpan networkId)
{
return networkId.size() == network.ssidLen && memcmp(networkId.data(), network.ssid, network.ssidLen) == 0;
}
Status NXPWiFiDriver::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);
memcpy(mStagingNetwork.credentials, credentials.data(), credentials.size());
mStagingNetwork.credentialsLen = static_cast<decltype(mStagingNetwork.credentialsLen)>(credentials.size());
memcpy(mStagingNetwork.ssid, ssid.data(), ssid.size());
mStagingNetwork.ssidLen = static_cast<decltype(mStagingNetwork.ssidLen)>(ssid.size());
return Status::kSuccess;
}
Status NXPWiFiDriver::RemoveNetwork(ByteSpan networkId, MutableCharSpan & outDebugText, uint8_t & outNetworkIndex)
{
int err_code = 0;
outDebugText.reduce_size(0);
outNetworkIndex = 0;
VerifyOrReturnError(NetworkMatch(mStagingNetwork, networkId), Status::kNetworkIDNotFound);
err_code = wlan_remove_network((char *) networkId.data());
switch (err_code)
{
case -WM_E_INVAL:
ChipLogError(DeviceLayer, "Error: Network not found");
break;
case WM_SUCCESS:
/* Use empty ssid for representing invalid network */
mStagingNetwork.ssidLen = 0;
memset(mStagingNetwork.ssid, 0, DeviceLayer::Internal::kMaxWiFiSSIDLength);
memset(mStagingNetwork.credentials, 0, DeviceLayer::Internal::kMaxWiFiKeyLength);
/* Save to persistent memory */
CommitConfiguration();
ChipLogProgress(DeviceLayer, "Successfully removed network");
break;
case WLAN_ERROR_STATE:
ChipLogError(DeviceLayer, "Error: Can't remove network in this state");
break;
default:
ChipLogError(DeviceLayer, "Error: Unable to remove network");
break;
}
return Status::kSuccess;
}
/* Returns the network SSID. User needs to allocate a buffer of size >= DeviceLayer::Internal::kMaxWiFiSSIDLength.
* ssid - pointer to the returned SSID
*/
Status NXPWiFiDriver::GetNetworkSSID(char * ssid)
{
VerifyOrReturnError(ssid != NULL, Status::kOutOfRange);
memcpy(ssid, mStagingNetwork.ssid, mStagingNetwork.ssidLen);
return Status::kSuccess;
}
/* Returns the network password. User needs to allocate a buffer of size >= DeviceLayer::Internal::kMaxWiFiKeyLength.
* credentials - pointer to the returned password
*/
Status NXPWiFiDriver::GetNetworkPassword(char * credentials)
{
VerifyOrReturnError(credentials != NULL, Status::kOutOfRange);
memcpy(credentials, mStagingNetwork.credentials, mStagingNetwork.credentialsLen);
return Status::kSuccess;
}
Status NXPWiFiDriver::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 NXPWiFiDriver::ConnectWiFiNetwork(const char * ssid, uint8_t ssidLen, const char * key, uint8_t keyLen)
{
return ConnectivityMgrImpl().ProvisionWiFiNetwork(ssid, ssidLen, key, keyLen);
}
void NXPWiFiDriver::OnConnectWiFiNetwork(Status commissioningError, CharSpan debugText, int32_t connectStatus)
{
CommitConfiguration();
if (mpConnectCallback != nullptr)
{
mpConnectCallback->OnResult(commissioningError, debugText, connectStatus);
mpConnectCallback = nullptr;
}
}
void NXPWiFiDriver::ConnectNetwork(ByteSpan networkId, ConnectCallback * callback)
{
CHIP_ERROR err = CHIP_NO_ERROR;
Status networkingStatus = Status::kSuccess;
ChipLogProgress(NetworkProvisioning, "Connecting to WiFi network: SSID: %.*s", static_cast<int>(networkId.size()),
networkId.data());
VerifyOrExit(NetworkMatch(mStagingNetwork, networkId), networkingStatus = Status::kNetworkIDNotFound);
VerifyOrExit(mpConnectCallback == nullptr, networkingStatus = Status::kUnknownError);
mpConnectCallback = callback;
err = ConnectWiFiNetwork(reinterpret_cast<const char *>(mStagingNetwork.ssid), mStagingNetwork.ssidLen,
reinterpret_cast<const char *>(mStagingNetwork.credentials), mStagingNetwork.credentialsLen);
exit:
if (err != CHIP_NO_ERROR)
{
networkingStatus = Status::kUnknownError;
}
if (networkingStatus != Status::kSuccess)
{
ChipLogError(NetworkProvisioning, "Failed to start connecting to WiFi network: %" CHIP_ERROR_FORMAT, err.Format());
mpConnectCallback = nullptr;
}
/* Always inform the cluster of the network status so that in case of success,
* we have time send the response to the controller before switching to a new network.
*/
if (callback != nullptr)
{
callback->OnResult(networkingStatus, CharSpan(), 0);
}
}
CHIP_ERROR NXPWiFiDriver::StartScanWiFiNetworks(ByteSpan ssid)
{
wlan_scan_params_v2_t wlan_scan_param;
ChipLogProgress(DeviceLayer, "Scan for WiFi network(s) requested");
(void) memset(&wlan_scan_param, 0, sizeof(wlan_scan_params_v2_t));
wlan_scan_param.cb = &NXPWiFiDriver::OnScanWiFiNetworkDone;
if ((ssid.size() > 0) && (ssid.size() < MLAN_MAX_SSID_LENGTH))
{
#ifdef CONFIG_COMBO_SCAN
(void) memcpy(wlan_scan_param.ssid[0], ssid.data(), ssid.size());
#else
(void) memcpy(wlan_scan_param.ssid, ssid.data(), ssid.size());
#endif
}
int status = wlan_scan_with_opt(wlan_scan_param);
if (status != WM_SUCCESS)
{
ChipLogError(DeviceLayer, "Failed to start scan for WiFi network(s): %d", status);
return CHIP_ERROR_INTERNAL;
}
return CHIP_NO_ERROR;
}
// TODO should be modified to do it in the context of the Matter stack
int NXPWiFiDriver::OnScanWiFiNetworkDone(unsigned int count)
{
ChipLogProgress(DeviceLayer, "Scan for WiFi network(s) done, found: %u", count);
if (count == 0)
{
if (GetInstance().mpScanCallback != nullptr)
{
GetInstance().mpScanCallback->OnFinished(Status::kSuccess, CharSpan(), nullptr);
GetInstance().mpScanCallback = nullptr;
}
return WM_SUCCESS;
}
std::unique_ptr<struct WiFiScanResponse[]> response_list_ptr(new struct WiFiScanResponse[count]);
if (response_list_ptr == nullptr)
{
ChipLogError(DeviceLayer, "Can't allocate memory for scan response_list");
if (GetInstance().mpScanCallback)
{
GetInstance().mpScanCallback->OnFinished(Status::kUnknownError, CharSpan(), nullptr);
GetInstance().mpScanCallback = nullptr;
}
return WM_SUCCESS;
}
struct WiFiScanResponse * response_list = response_list_ptr.get();
int valid = 0;
for (int i = 0; i < count; i++)
{
struct wlan_scan_result res;
struct WiFiScanResponse response;
int status = wlan_get_scan_result(i, &res);
if (status != WM_SUCCESS)
{
ChipLogError(DeviceLayer, "Can't get scan result: %d", status);
continue;
}
response.security = WiFiSecurity::kUnencrypted;
if (res.wep != 0U)
{
response.security = WiFiSecurity::kWep;
}
else if (res.wpa != 0U)
{
response.security = WiFiSecurity::kWpaPersonal;
}
else if (res.wpa2 != 0U)
{
response.security = WiFiSecurity::kWpa2Personal;
}
else if (res.wpa3_sae != 0U)
{
response.security = WiFiSecurity::kWpa3Personal;
}
else if (res.wpa2_entp != 0U)
{
// ChipLogProgress(DeviceLayer, "Ignoring scanned network with WPA2-Enterprise security");
// continue;
}
if (res.ssid_len > DeviceLayer::Internal::kMaxWiFiSSIDLength)
{
ChipLogError(DeviceLayer, "Ignoring scanned network with too long SSID: %u", res.ssid_len);
continue;
}
memcpy(response.ssid, res.ssid, res.ssid_len);
response.ssidLen = res.ssid_len;
memcpy(response.bssid, res.bssid, DeviceLayer::Internal::kWiFiBSSIDLength);
response.channel = (uint16_t) res.channel;
response.wiFiBand = chip::DeviceLayer::NetworkCommissioning::WiFiBand::k2g4; // TODO 5 GHz also possible, but results don't
// show this information
response.rssi = -static_cast<int8_t>(res.rssi);
response_list[valid] = response;
valid++;
}
if (CHIP_NO_ERROR == DeviceLayer::SystemLayer().ScheduleLambda([valid, response_list]() {
std::unique_ptr<struct WiFiScanResponse[]> auto_free(response_list);
NXPScanResponseIterator iter(valid, response_list);
if (GetInstance().mpScanCallback)
{
GetInstance().mpScanCallback->OnFinished(Status::kSuccess, CharSpan(), &iter);
GetInstance().mpScanCallback = nullptr;
}
else
{
ChipLogError(DeviceLayer, "Can't find the ScanCallback function");
}
}))
{
response_list_ptr.release();
}
return WM_SUCCESS;
}
void NXPWiFiDriver::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);
}
}
}
uint32_t NXPWiFiDriver::GetSupportedWiFiBandsMask() const
{
uint32_t bands = static_cast<uint32_t>(1UL << chip::to_underlying(WiFiBandEnum::k2g4));
#ifdef CONFIG_5GHz_SUPPORT
bands |= (1UL << chip::to_underlying(WiFiBandEnum::k5g));
#endif
return bands;
}
static CHIP_ERROR GetConnectedNetwork(Network & network)
{
struct wlan_network wlan_network;
int result;
result = wlan_get_current_network(&wlan_network);
if (result != WM_SUCCESS)
{
return CHIP_ERROR_INTERNAL;
}
uint8_t length = strnlen(reinterpret_cast<const char *>(wlan_network.ssid), DeviceLayer::Internal::kMaxWiFiSSIDLength);
if (length > sizeof(network.networkID))
{
return CHIP_ERROR_INTERNAL;
}
memcpy(network.networkID, wlan_network.ssid, length);
network.networkIDLen = length;
return CHIP_NO_ERROR;
}
size_t NXPWiFiDriver::WiFiNetworkIterator::Count()
{
return mDriver->mStagingNetwork.ssidLen == 0 ? 0 : 1;
}
bool NXPWiFiDriver::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
#endif // CHIP_DEVICE_CONFIG_ENABLE_WPA