blob: 36b2ae978d65c84ae0c5e5abbd3c4a1846fd7816 [file] [log] [blame]
/*
*
* Copyright (c) 2020-2022 Project CHIP Authors
* Copyright (c) 2020 Nest Labs, Inc.
* Copyright 2023 NXP
* All rights reserved.
*
* 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.
*/
/* this file behaves like a config.h, comes first */
#include <platform/internal/CHIPDeviceLayerInternal.h>
#include "NetworkCommissioningDriver.h"
#include <lib/support/CodeUtils.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/ConnectivityManager.h>
#include <platform/internal/BLEManager.h>
#include <platform/internal/GenericConnectivityManagerImpl_UDP.ipp>
#if INET_CONFIG_ENABLE_TCP_ENDPOINT
#include <platform/internal/GenericConnectivityManagerImpl_TCP.ipp>
#endif
#include <lwip/dns.h>
#include <lwip/ip_addr.h>
#include <lwip/nd6.h>
#include <lwip/netif.h>
#if CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
#include <platform/internal/GenericConnectivityManagerImpl_BLE.ipp>
#endif
#if CHIP_DEVICE_CONFIG_ENABLE_WPA
extern "C" {
#include "wlan.h"
#include "wm_net.h"
#include <wm_os.h>
}
#include <platform/internal/GenericConnectivityManagerImpl_WiFi.ipp>
#include <app/server/Dnssd.h>
#endif /* CHIP_DEVICE_CONFIG_ENABLE_WPA */
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
#include <platform/internal/GenericConnectivityManagerImpl_Thread.ipp>
#endif
using namespace ::chip;
using namespace ::chip::Inet;
using namespace ::chip::System;
using namespace ::chip::TLV;
using namespace ::chip::DeviceLayer::Internal;
using namespace ::chip::DeviceLayer::DeviceEventType;
namespace chip {
namespace DeviceLayer {
ConnectivityManagerImpl ConnectivityManagerImpl::sInstance;
#if CHIP_DEVICE_CONFIG_ENABLE_WPA
netif_ext_callback_t ConnectivityManagerImpl::sNetifCallback;
#endif /* CHIP_DEVICE_CONFIG_ENABLE_WPA */
CHIP_ERROR ConnectivityManagerImpl::_Init()
{
CHIP_ERROR err = CHIP_NO_ERROR;
mWiFiStationMode = kWiFiStationMode_Disabled;
mWiFiStationState = kWiFiStationState_NotConnected;
mWiFiStationReconnectIntervalMS = CHIP_DEVICE_CONFIG_WIFI_STATION_RECONNECT_INTERVAL;
// Initialize the generic base classes that require it.
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
GenericConnectivityManagerImpl_Thread<ConnectivityManagerImpl>::_Init();
#endif
SuccessOrExit(err);
exit:
return err;
}
void ConnectivityManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event)
{
// Forward the event to the generic base classes as needed.
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
GenericConnectivityManagerImpl_Thread<ConnectivityManagerImpl>::_OnPlatformEvent(event);
#endif
#if CHIP_DEVICE_CONFIG_ENABLE_WPA
if (event->Type == kPlatformNxpWlanEvent)
{
ProcessWlanEvent(event->Platform.WlanEventReason);
}
else if (event->Type == kPlatformNxpIpChangeEvent)
{
UpdateInternetConnectivityState();
}
else if (event->Type == kPlatformNxpStartWlanConnectEvent)
{
if (wlan_add_network(event->Platform.pNetworkDataEvent) == WM_SUCCESS)
{
_SetWiFiStationState(kWiFiStationState_Connecting);
ChipLogProgress(DeviceLayer, "WLAN connecting to network.name = \"%s\"", event->Platform.pNetworkDataEvent->name);
wlan_connect(event->Platform.pNetworkDataEvent->name);
}
if (event->Platform.pNetworkDataEvent != NULL)
{
free(event->Platform.pNetworkDataEvent);
}
}
#endif
}
#if CHIP_DEVICE_CONFIG_ENABLE_WPA
ConnectivityManager::WiFiStationMode ConnectivityManagerImpl::_GetWiFiStationMode()
{
return mWiFiStationMode;
}
CHIP_ERROR ConnectivityManagerImpl::_SetWiFiStationMode(ConnectivityManager::WiFiStationMode val)
{
CHIP_ERROR err = CHIP_NO_ERROR;
VerifyOrExit(val != ConnectivityManager::kWiFiStationMode_NotSupported, err = CHIP_ERROR_INVALID_ARGUMENT);
if (mWiFiStationMode != val)
{
ChipLogProgress(DeviceLayer, "WiFi station mode change: %s -> %s", WiFiStationModeToStr(mWiFiStationMode),
WiFiStationModeToStr(val));
}
mWiFiStationMode = val;
exit:
return err;
}
void ConnectivityManagerImpl::_SetWiFiStationState(ConnectivityManager::WiFiStationState val)
{
if (mWiFiStationState != val)
{
ChipLogProgress(DeviceLayer, "WiFi station state change: %s -> %s", WiFiStationStateToStr(mWiFiStationState),
WiFiStationStateToStr(val));
}
mWiFiStationState = val;
}
CHIP_ERROR ConnectivityManagerImpl::_SetWiFiAPMode(WiFiAPMode val)
{
CHIP_ERROR err = CHIP_NO_ERROR;
VerifyOrExit(val != kWiFiAPMode_NotSupported, err = CHIP_ERROR_INVALID_ARGUMENT);
if (mWiFiAPMode != val)
{
ChipLogProgress(DeviceLayer, "WiFi AP mode change: %s -> %s", WiFiAPModeToStr(mWiFiAPMode), WiFiAPModeToStr(val));
}
mWiFiAPMode = val;
exit:
return err;
}
bool ConnectivityManagerImpl::_IsWiFiStationEnabled()
{
return GetWiFiStationMode() == kWiFiStationMode_Enabled;
}
bool ConnectivityManagerImpl::_IsWiFiStationConnected()
{
return (mWiFiStationState == kWiFiStationState_Connected);
}
bool ConnectivityManagerImpl::_IsWiFiStationApplicationControlled()
{
return mWiFiStationMode == ConnectivityManager::kWiFiStationMode_ApplicationControlled;
}
void ConnectivityManagerImpl::ProcessWlanEvent(enum wlan_event_reason wlanEvent)
{
#if CHIP_DETAIL_LOGGING
enum wlan_connection_state state;
int result;
result = wlan_get_connection_state(&state);
if (result == WM_SUCCESS)
{
ChipLogDetail(DeviceLayer, "WLAN event: %d, WLAN connection state: %d", wlanEvent, state);
}
else
{
ChipLogDetail(DeviceLayer, "WLAN event: %d, WLAN connection state: unknown", wlanEvent);
}
#endif /* CHIP_DETAIL_LOGGING */
switch (wlanEvent)
{
case WLAN_REASON_SUCCESS:
ChipLogProgress(DeviceLayer, "Connected to WLAN network = %d", is_sta_ipv6_connected());
if (sInstance._GetWiFiStationState() == kWiFiStationState_Connecting)
{
sInstance._SetWiFiStationState(kWiFiStationState_Connecting_Succeeded);
sInstance._SetWiFiStationState(kWiFiStationState_Connected);
NetworkCommissioning::NXPWiFiDriver::GetInstance().OnConnectWiFiNetwork(NetworkCommissioning::Status::kSuccess,
CharSpan(), wlanEvent);
sInstance.OnStationConnected();
}
break;
case WLAN_REASON_AUTH_SUCCESS:
ChipLogProgress(DeviceLayer, "Associated to WLAN network");
break;
case WLAN_REASON_CONNECT_FAILED:
ChipLogError(DeviceLayer, "WLAN (re)connect failed");
sInstance._SetWiFiStationState(kWiFiStationState_NotConnected);
UpdateInternetConnectivityState();
break;
case WLAN_REASON_NETWORK_NOT_FOUND:
ChipLogError(DeviceLayer, "WLAN network not found");
NetworkCommissioning::NXPWiFiDriver::GetInstance().OnConnectWiFiNetwork(NetworkCommissioning::Status::kNetworkNotFound,
CharSpan(), wlanEvent);
break;
case WLAN_REASON_NETWORK_AUTH_FAILED:
ChipLogError(DeviceLayer, "Authentication to WLAN network failed");
NetworkCommissioning::NXPWiFiDriver::GetInstance().OnConnectWiFiNetwork(NetworkCommissioning::Status::kAuthFailure,
CharSpan(), wlanEvent);
ChipLogError(DeviceLayer, "Authentication to WLAN network failed end");
break;
case WLAN_REASON_LINK_LOST:
ChipLogError(DeviceLayer, "WLAN link lost");
if (sInstance._GetWiFiStationState() == kWiFiStationState_Connected)
{
sInstance._SetWiFiStationState(kWiFiStationState_NotConnected);
sInstance.OnStationDisconnected();
}
break;
case WLAN_REASON_USER_DISCONNECT:
ChipLogProgress(DeviceLayer, "Disconnected from WLAN network");
sInstance._SetWiFiStationState(kWiFiStationState_NotConnected);
sInstance.OnStationDisconnected();
break;
case WLAN_REASON_INITIALIZED:
sInstance._SetWiFiStationState(kWiFiStationState_NotConnected);
sInstance._SetWiFiStationMode(kWiFiStationMode_Enabled);
break;
default:
break;
}
}
int ConnectivityManagerImpl::_WlanEventCallback(enum wlan_event_reason wlanEvent, void * data)
{
ChipDeviceEvent event;
event.Type = DeviceEventType::kPlatformNxpWlanEvent;
event.Platform.WlanEventReason = wlanEvent;
(void) PlatformMgr().PostEvent(&event);
return 0;
}
void ConnectivityManagerImpl::OnStationConnected()
{
CHIP_ERROR err;
ChipDeviceEvent event;
event.Type = DeviceEventType::kWiFiConnectivityChange;
event.WiFiConnectivityChange.Result = kConnectivity_Established;
(void) PlatformMgr().PostEvent(&event);
/* Update the connectivity state in case the connected event has been received after getting an IP addr */
UpdateInternetConnectivityState();
}
void ConnectivityManagerImpl::OnStationDisconnected()
{
CHIP_ERROR err;
ChipDeviceEvent event;
event.Type = DeviceEventType::kWiFiConnectivityChange;
event.WiFiConnectivityChange.Result = kConnectivity_Lost;
(void) PlatformMgr().PostEvent(&event);
/* Update the connectivity state in case the connected event has been received after getting an IP addr */
UpdateInternetConnectivityState();
}
void ConnectivityManagerImpl::UpdateInternetConnectivityState()
{
bool haveIPv4Conn = false;
bool haveIPv6Conn = false;
const bool hadIPv4Conn = mFlags.Has(ConnectivityFlags::kHaveIPv4InternetConnectivity);
const bool hadIPv6Conn = mFlags.Has(ConnectivityFlags::kHaveIPv6InternetConnectivity);
const ip_addr_t * addr4;
const ip6_addr_t * addr6;
CHIP_ERROR err;
ChipDeviceEvent event;
// If the WiFi station is currently in the connected state...
if (_IsWiFiStationConnected())
{
// Get the LwIP netif for the WiFi station interface.
struct netif * netif = static_cast<struct netif *>(net_get_mlan_handle());
// If the WiFi station interface is up...
if ((netif != nullptr) && netif_is_up(netif) && netif_is_link_up(netif))
{
#if INET_CONFIG_ENABLE_IPV4
// Check if a DNS server is currently configured. If so...
ip_addr_t dnsServerAddr = *dns_getserver(0);
if (!ip_addr_isany_val(dnsServerAddr))
{
// If the station interface has been assigned an IPv4 address, and has
// an IPv4 gateway, then presume that the device has IPv4 Internet
// connectivity.
if (!ip4_addr_isany_val(*netif_ip4_addr(netif)) && !ip4_addr_isany_val(*netif_ip4_gw(netif)))
{
haveIPv4Conn = true;
addr4 = &netif->ip_addr;
}
}
#endif
// Search among the IPv6 addresses assigned to the interface for an
// address that is in the valid state. Search goes backwards because
// the link-local address is in the first slot and we prefer to report
// other than the link-local address value if there are multiple addresses.
for (int i = (LWIP_IPV6_NUM_ADDRESSES - 1); i >= 0; i--)
{
if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)))
{
haveIPv6Conn = true;
addr6 = netif_ip6_addr(netif, i);
break;
}
}
}
}
// Update the current state.
mFlags.Set(ConnectivityFlags::kHaveIPv4InternetConnectivity, haveIPv4Conn)
.Set(ConnectivityFlags::kHaveIPv6InternetConnectivity, haveIPv6Conn);
if (haveIPv4Conn != hadIPv4Conn)
{
/* Check if the */
event.Type = DeviceEventType::kInternetConnectivityChange;
event.InternetConnectivityChange.IPv4 = GetConnectivityChange(hadIPv4Conn, haveIPv4Conn);
event.InternetConnectivityChange.IPv6 = kConnectivity_NoChange;
if (haveIPv4Conn)
{
event.InternetConnectivityChange.ipAddress = IPAddress(*addr4);
/* (Re-)start the DNSSD server */
chip::app::DnssdServer::Instance().StartServer();
}
err = PlatformMgr().PostEvent(&event);
VerifyOrDie(err == CHIP_NO_ERROR);
ChipLogProgress(DeviceLayer, "%s Internet connectivity %s", "IPv4", (haveIPv4Conn) ? "ESTABLISHED" : "LOST");
}
if (haveIPv6Conn != hadIPv6Conn)
{
event.Type = DeviceEventType::kInternetConnectivityChange;
event.InternetConnectivityChange.IPv4 = kConnectivity_NoChange;
event.InternetConnectivityChange.IPv6 = GetConnectivityChange(hadIPv6Conn, haveIPv6Conn);
if (haveIPv6Conn)
{
event.InternetConnectivityChange.ipAddress = IPAddress(*addr6);
/* (Re-)start the DNSSD server */
chip::app::DnssdServer::Instance().StartServer();
}
err = PlatformMgr().PostEvent(&event);
VerifyOrDie(err == CHIP_NO_ERROR);
ChipLogProgress(DeviceLayer, "%s Internet connectivity %s", "IPv6", (haveIPv6Conn) ? "ESTABLISHED" : "LOST");
}
}
void ConnectivityManagerImpl::_NetifExtCallback(struct netif * netif, netif_nsc_reason_t reason,
const netif_ext_callback_args_t * args)
{
struct netif * station_netif;
ChipDeviceEvent event;
ChipLogDetail(DeviceLayer, "_NetifExtCallback: netif=%p, reason=0x%04x", netif, reason);
station_netif = static_cast<struct netif *>(net_get_mlan_handle());
if (netif == station_netif)
{
event.Type = DeviceEventType::kPlatformNxpIpChangeEvent;
(void) PlatformMgr().PostEvent(&event);
}
}
void ConnectivityManagerImpl::StartWiFiManagement()
{
struct netif * netif = nullptr;
EventBits_t bits;
int32_t result;
LOCK_TCPIP_CORE();
netif = static_cast<struct netif *>(net_get_mlan_handle());
if (netif != nullptr)
{
memset(&ConnectivityManagerImpl::sNetifCallback, 0, sizeof(ConnectivityManagerImpl::sNetifCallback));
netif_add_ext_callback(&ConnectivityManagerImpl::sNetifCallback, &_NetifExtCallback);
}
UNLOCK_TCPIP_CORE();
result = wlan_start(_WlanEventCallback);
if (result != WM_SUCCESS)
{
ChipLogError(DeviceLayer, "Failed to start WLAN Connection Manager");
chipDie();
}
}
#endif // CHIP_DEVICE_CONFIG_ENABLE_WPA
CHIP_ERROR ConnectivityManagerImpl::ProvisionWiFiNetwork(const char * ssid, uint8_t ssidLen, const char * key, uint8_t keyLen)
{
#if CHIP_DEVICE_CONFIG_ENABLE_WPA
CHIP_ERROR ret = CHIP_NO_ERROR;
struct wlan_network * pNetworkData = (struct wlan_network *) malloc(sizeof(struct wlan_network));
int result;
VerifyOrExit(pNetworkData != NULL, ret = CHIP_ERROR_NO_MEMORY);
VerifyOrExit(ssidLen <= IEEEtypes_SSID_SIZE, ret = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(mWiFiStationState != kWiFiStationState_Connecting, ret = CHIP_ERROR_BUSY);
memset(pNetworkData, 0, sizeof(struct wlan_network));
if (ssidLen < WLAN_NETWORK_NAME_MAX_LENGTH)
{
memcpy(pNetworkData->name, ssid, ssidLen);
pNetworkData->name[ssidLen] = '\0';
}
else
{
memcpy(pNetworkData->name, ssid, WLAN_NETWORK_NAME_MAX_LENGTH);
pNetworkData->name[WLAN_NETWORK_NAME_MAX_LENGTH] = '\0';
}
memcpy(pNetworkData->ssid, ssid, ssidLen);
pNetworkData->ip.ipv4.addr_type = ADDR_TYPE_DHCP;
pNetworkData->ssid_specific = 1;
pNetworkData->security.type = WLAN_SECURITY_NONE;
if (keyLen > 0)
{
pNetworkData->security.type = WLAN_SECURITY_WILDCARD;
memcpy(pNetworkData->security.psk, key, keyLen);
pNetworkData->security.psk_len = keyLen;
}
ConnectNetworkTimerHandler(NULL, (void *) pNetworkData);
exit:
return ret;
#else
return CHIP_ERROR_NOT_IMPLEMENTED;
#endif
}
#if CHIP_DEVICE_CONFIG_ENABLE_WPA
void ConnectivityManagerImpl::ConnectNetworkTimerHandler(::chip::System::Layer * aLayer, void * context)
{
ChipDeviceEvent event;
/*
* Make sure to have the Wi-Fi station enabled before scheduling a connect event .
* Otherwise start a new timer to check again the status later.
*/
if (ConnectivityMgr().IsWiFiStationEnabled())
{
/* Post an event to start the connection asynchronously in the Matter task context */
event.Type = DeviceEventType::kPlatformNxpStartWlanConnectEvent;
event.Platform.pNetworkDataEvent = (struct wlan_network *) context;
(void) PlatformMgr().PostEvent(&event);
}
else
{
PlatformMgr().LockChipStack();
DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(CHIP_DEVICE_CONFIG_WIFI_STATION_RECONNECT_INTERVAL),
ConnectNetworkTimerHandler, context);
PlatformMgr().UnlockChipStack();
}
}
#endif
} // namespace DeviceLayer
} // namespace chip