diff --git a/src/platform/Zephyr/wifi/ConnectivityManagerImplWiFi.cpp b/src/platform/Zephyr/wifi/ConnectivityManagerImplWiFi.cpp index 5bef79bb22fdc5..9b605c31560142 100644 --- a/src/platform/Zephyr/wifi/ConnectivityManagerImplWiFi.cpp +++ b/src/platform/Zephyr/wifi/ConnectivityManagerImplWiFi.cpp @@ -42,7 +42,7 @@ ConnectivityManager::WiFiStationMode ConnectivityManagerImplWiFi::_GetWiFiStatio { if (mStationMode != ConnectivityManager::WiFiStationMode::kWiFiStationMode_ApplicationControlled) { - mStationMode = (WiFiManager::StationStatus::DISABLED == WiFiManager().Instance().GetStationStatus()) + mStationMode = (WiFiManager::StationStatus::DISABLED == WiFiManager::Instance().GetStationStatus()) ? ConnectivityManager::WiFiStationMode::kWiFiStationMode_Disabled : ConnectivityManager::WiFiStationMode::kWiFiStationMode_Enabled; } @@ -60,7 +60,7 @@ CHIP_ERROR ConnectivityManagerImplWiFi::_SetWiFiStationMode(ConnectivityManager: bool ConnectivityManagerImplWiFi::_IsWiFiStationEnabled(void) { - return (WiFiManager::StationStatus::DISABLED <= WiFiManager().Instance().GetStationStatus()); + return (WiFiManager::StationStatus::DISABLED <= WiFiManager::Instance().GetStationStatus()); } bool ConnectivityManagerImplWiFi::_IsWiFiStationApplicationControlled(void) @@ -70,7 +70,7 @@ bool ConnectivityManagerImplWiFi::_IsWiFiStationApplicationControlled(void) bool ConnectivityManagerImplWiFi::_IsWiFiStationConnected(void) { - return (WiFiManager::StationStatus::CONNECTED == WiFiManager().Instance().GetStationStatus()); + return (WiFiManager::StationStatus::CONNECTED == WiFiManager::Instance().GetStationStatus()); } System::Clock::Timeout ConnectivityManagerImplWiFi::_GetWiFiStationReconnectInterval(void) @@ -88,14 +88,14 @@ bool ConnectivityManagerImplWiFi::_IsWiFiStationProvisioned(void) { // from Matter perspective `provisioned` means that the supplicant has been provided // with SSID and password (doesn't matter if valid or not) - return (WiFiManager::StationStatus::CONNECTING <= WiFiManager().Instance().GetStationStatus()); + return (WiFiManager::StationStatus::CONNECTING <= WiFiManager::Instance().GetStationStatus()); } void ConnectivityManagerImplWiFi::_ClearWiFiStationProvision(void) { if (_IsWiFiStationProvisioned()) { - if (CHIP_NO_ERROR != WiFiManager().Instance().ClearStationProvisioningData()) + if (CHIP_NO_ERROR != WiFiManager::Instance().ClearStationProvisioningData()) { ChipLogError(DeviceLayer, "Cannot clear WiFi station provisioning data"); } @@ -104,9 +104,9 @@ void ConnectivityManagerImplWiFi::_ClearWiFiStationProvision(void) bool ConnectivityManagerImplWiFi::_CanStartWiFiScan() { - return (WiFiManager::StationStatus::DISABLED != WiFiManager().Instance().GetStationStatus() && - WiFiManager::StationStatus::SCANNING != WiFiManager().Instance().GetStationStatus() && - WiFiManager::StationStatus::CONNECTING != WiFiManager().Instance().GetStationStatus()); + return (WiFiManager::StationStatus::DISABLED != WiFiManager::Instance().GetStationStatus() && + WiFiManager::StationStatus::SCANNING != WiFiManager::Instance().GetStationStatus() && + WiFiManager::StationStatus::CONNECTING != WiFiManager::Instance().GetStationStatus()); } void ConnectivityManagerImplWiFi::_OnWiFiStationProvisionChange() diff --git a/src/platform/Zephyr/wifi/WiFiManager.cpp b/src/platform/Zephyr/wifi/WiFiManager.cpp index 01d0d937b19b3a..6dbc4930314fb1 100644 --- a/src/platform/Zephyr/wifi/WiFiManager.cpp +++ b/src/platform/Zephyr/wifi/WiFiManager.cpp @@ -23,10 +23,9 @@ #include "WiFiManager.h" #include -#include -#include #include #include +#include #include #include @@ -50,6 +49,21 @@ namespace DeviceLayer { namespace { +app::Clusters::NetworkCommissioning::WiFiBandEnum ConvertBandEnum(uint8_t band) +{ + switch (band) + { + case WIFI_FREQ_BAND_2_4_GHZ: + return app::Clusters::NetworkCommissioning::WiFiBandEnum::k2g4; + case WIFI_FREQ_BAND_5_GHZ: + return app::Clusters::NetworkCommissioning::WiFiBandEnum::k5g; + case WIFI_FREQ_BAND_6_GHZ: + return app::Clusters::NetworkCommissioning::WiFiBandEnum::k6g; + default: + return app::Clusters::NetworkCommissioning::WiFiBandEnum::kUnknownEnumValue; + } +} + NetworkCommissioning::WiFiScanResponse ToScanResponse(const wifi_scan_result * result) { NetworkCommissioning::WiFiScanResponse response = {}; @@ -62,9 +76,10 @@ NetworkCommissioning::WiFiScanResponse ToScanResponse(const wifi_scan_result * r // TODO: Distinguish WPA versions response.security.Set(result->security == WIFI_SECURITY_TYPE_PSK ? NetworkCommissioning::WiFiSecurity::kWpaPersonal : NetworkCommissioning::WiFiSecurity::kUnencrypted); - response.channel = result->channel; - response.rssi = result->rssi; - response.ssidLen = result->ssid_length; + response.channel = result->channel; + response.rssi = result->rssi; + response.ssidLen = result->ssid_length; + response.wiFiBand = ConvertBandEnum(result->band); memcpy(response.ssid, result->ssid, result->ssid_length); // TODO: MAC/BSSID is not filled by the Wi-Fi driver memcpy(response.bssid, result->mac, result->mac_length); @@ -138,57 +153,43 @@ const Map { WIFI_STATE_GROUP_HANDSHAKE, WiFiManager::StationStatus::PROVISIONING }, { WIFI_STATE_COMPLETED, WiFiManager::StationStatus::FULLY_PROVISIONED } }); -const Map - WiFiManager::sEventHandlerMap({ { NET_EVENT_WIFI_SCAN_RESULT, WiFiManager::ScanResultHandler }, - { NET_EVENT_WIFI_SCAN_DONE, WiFiManager::ScanDoneHandler }, - { NET_EVENT_WIFI_CONNECT_RESULT, WiFiManager::ConnectHandler }, - { NET_EVENT_WIFI_DISCONNECT_RESULT, WiFiManager::DisconnectHandler }, - { NET_EVENT_WIFI_DISCONNECT_COMPLETE, WiFiManager::DisconnectHandler } }); +const Map WiFiManager::sEventHandlerMap({ + { NET_EVENT_WIFI_SCAN_RESULT, WiFiManager::ScanResultHandler }, + { NET_EVENT_WIFI_SCAN_DONE, WiFiManager::ScanDoneHandler }, + { NET_EVENT_WIFI_CONNECT_RESULT, WiFiManager::ConnectHandler }, + { NET_EVENT_WIFI_DISCONNECT_RESULT, WiFiManager::DisconnectHandler }, + { NET_EVENT_WIFI_DISCONNECT_COMPLETE, WiFiManager::DisconnectHandler }, +}); void WiFiManager::WifiMgmtEventHandler(net_mgmt_event_callback * cb, uint32_t mgmtEvent, net_if * iface) { - if (0 == strcmp(iface->if_dev->dev->name, InetUtils::GetInterface()->if_dev->dev->name)) + if (iface == Instance().mNetIf) { Platform::UniquePtr eventData(new uint8_t[cb->info_length]); VerifyOrReturn(eventData); memcpy(eventData.get(), cb->info, cb->info_length); - sEventHandlerMap[mgmtEvent](std::move(eventData)); + sEventHandlerMap[mgmtEvent](std::move(eventData), cb->info_length); } } -CHIP_ERROR WiFiManager::Init() +void WiFiManager::IPv6MgmtEventHandler(net_mgmt_event_callback * cb, uint32_t mgmtEvent, net_if * iface) { - // TODO: consider moving these to ConnectivityManagerImpl to be prepared for handling multiple interfaces on a single device. - Inet::UDPEndPointImplSockets::SetMulticastGroupHandler([](Inet::InterfaceId interfaceId, const Inet::IPAddress & address, - Inet::UDPEndPointImplSockets::MulticastOperation operation) { - const in6_addr addr = InetUtils::ToZephyrAddr(address); - net_if * iface = InetUtils::GetInterface(interfaceId); - VerifyOrReturnError(iface != nullptr, INET_ERROR_UNKNOWN_INTERFACE); - - if (operation == Inet::UDPEndPointImplSockets::MulticastOperation::kJoin) - { - net_if_mcast_addr * maddr = net_if_ipv6_maddr_add(iface, &addr); - - if (maddr && !net_if_ipv6_maddr_is_joined(maddr) && !net_ipv6_is_addr_mcast_link_all_nodes(&addr)) - { - net_if_ipv6_maddr_join(iface, maddr); - } - } - else if (operation == Inet::UDPEndPointImplSockets::MulticastOperation::kLeave) - { - VerifyOrReturnError(net_ipv6_is_addr_mcast_link_all_nodes(&addr) || net_if_ipv6_maddr_rm(iface, &addr), - CHIP_ERROR_INVALID_ADDRESS); - } - else - { - return CHIP_ERROR_INCORRECT_STATE; - } + if (((mgmtEvent == NET_EVENT_IPV6_ADDR_ADD) || (mgmtEvent == NET_EVENT_IPV6_ADDR_DEL)) && cb->info) + { + IPv6AddressChangeHandler(cb->info); + } +} - return CHIP_NO_ERROR; - }); +CHIP_ERROR WiFiManager::Init() +{ + mNetIf = InetUtils::GetWiFiInterface(); + VerifyOrReturnError(mNetIf != nullptr, INET_ERROR_UNKNOWN_INTERFACE); net_mgmt_init_event_callback(&mWiFiMgmtClbk, WifiMgmtEventHandler, kWifiManagementEvents); + net_mgmt_init_event_callback(&mIPv6MgmtClbk, IPv6MgmtEventHandler, kIPv6ManagementEvents); + net_mgmt_add_event_callback(&mWiFiMgmtClbk); + net_mgmt_add_event_callback(&mIPv6MgmtClbk); ChipLogDetail(DeviceLayer, "WiFiManager has been initialized"); @@ -197,9 +198,6 @@ CHIP_ERROR WiFiManager::Init() CHIP_ERROR WiFiManager::Scan(const ByteSpan & ssid, ScanResultCallback resultCallback, ScanDoneCallback doneCallback, bool internalScan) { - net_if * iface = InetUtils::GetInterface(); - VerifyOrReturnError(nullptr != iface, CHIP_ERROR_INTERNAL); - mInternalScan = internalScan; mScanResultCallback = resultCallback; mScanDoneCallback = doneCallback; @@ -207,17 +205,22 @@ CHIP_ERROR WiFiManager::Scan(const ByteSpan & ssid, ScanResultCallback resultCal mWiFiState = WIFI_STATE_SCANNING; mSsidFound = false; - /* If the ssid is not null, it means the scan must target a specific SSID, and only include this one in the scan - * result. To do so, we save the requested ssid and we will filter the scan results accordingly in the scan done - * handler. */ - if ((ssid.size() > 0) && (!mInternalScan)) + wifi_scan_params * scanParams{ nullptr }; + size_t scanParamsSize{ 0 }; + + if (!ssid.empty()) { - mNetworkToScan.Erase(); - memcpy(mNetworkToScan.ssid, ssid.data(), ssid.size()); - mNetworkToScan.ssidLen = ssid.size(); + /* We must assume that the ssid is handled as a NULL-terminated string. + Note that the mScanSsidBuffer is initialized with zeros. */ + VerifyOrReturnError(ssid.size() < sizeof(mScanSsidBuffer), CHIP_ERROR_INVALID_ARGUMENT); + memcpy(mScanSsidBuffer, ssid.data(), ssid.size()); + mScanSsidBuffer[ssid.size()] = 0; // indicate the end of ssid string + mScanParams.ssids[0] = mScanSsidBuffer; + mScanParams.ssids[1] = nullptr; // indicate the end of ssids list + scanParams = &mScanParams; + scanParamsSize = sizeof(*scanParams); } - - if (0 != net_mgmt(NET_REQUEST_WIFI_SCAN, iface, NULL, 0)) + if (0 != net_mgmt(NET_REQUEST_WIFI_SCAN, mNetIf, scanParams, scanParamsSize)) { ChipLogError(DeviceLayer, "Scan request failed"); return CHIP_ERROR_INTERNAL; @@ -239,9 +242,7 @@ CHIP_ERROR WiFiManager::Connect(const ByteSpan & ssid, const ByteSpan & credenti { ChipLogDetail(DeviceLayer, "Connecting to WiFi network: %.*s", ssid.size(), ssid.data()); - mHandling.mOnConnectionSuccess = handling.mOnConnectionSuccess; - mHandling.mOnConnectionFailed = handling.mOnConnectionFailed; - mHandling.mConnectionTimeout = handling.mConnectionTimeout; + mHandling = handling; mWiFiState = WIFI_STATE_ASSOCIATING; @@ -258,11 +259,8 @@ CHIP_ERROR WiFiManager::Connect(const ByteSpan & ssid, const ByteSpan & credenti CHIP_ERROR WiFiManager::Disconnect() { - net_if * iface = InetUtils::GetInterface(); - VerifyOrReturnError(nullptr != iface, CHIP_ERROR_INTERNAL); - mApplicationDisconnectRequested = true; - int status = net_mgmt(NET_REQUEST_WIFI_DISCONNECT, iface, NULL, 0); + int status = net_mgmt(NET_REQUEST_WIFI_DISCONNECT, mNetIf, NULL, 0); if (status) { @@ -287,11 +285,9 @@ CHIP_ERROR WiFiManager::Disconnect() CHIP_ERROR WiFiManager::GetWiFiInfo(WiFiInfo & info) const { - net_if * iface = InetUtils::GetInterface(); - VerifyOrReturnError(nullptr != iface, CHIP_ERROR_INTERNAL); - struct wifi_iface_status status = { 0 }; + wifi_iface_status status = { 0 }; - if (net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, iface, &status, sizeof(struct wifi_iface_status))) + if (net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, mNetIf, &status, sizeof(wifi_iface_status))) { ChipLogError(DeviceLayer, "Status request failed"); return CHIP_ERROR_INTERNAL; @@ -316,22 +312,31 @@ CHIP_ERROR WiFiManager::GetWiFiInfo(WiFiInfo & info) const CHIP_ERROR WiFiManager::GetNetworkStatistics(NetworkStatistics & stats) const { net_stats_wifi data{}; - net_mgmt(NET_REQUEST_STATS_GET_WIFI, InetUtils::GetInterface(), &data, sizeof(data)); + net_mgmt(NET_REQUEST_STATS_GET_WIFI, mNetIf, &data, sizeof(data)); stats.mPacketMulticastRxCount = data.multicast.rx; stats.mPacketMulticastTxCount = data.multicast.tx; - stats.mPacketUnicastRxCount = data.pkts.rx - data.multicast.rx - data.broadcast.rx; - stats.mPacketUnicastTxCount = data.pkts.tx - data.multicast.tx - data.broadcast.tx; - stats.mBeaconsSuccessCount = data.sta_mgmt.beacons_rx; - stats.mBeaconsLostCount = data.sta_mgmt.beacons_miss; +#if CONFIG_CHIP_NXP_PLATFORM + /* Zephyr 3.6 doesn't support the unicast stat in net_stats_wifi struct */ + stats.mPacketUnicastRxCount = data.pkts.rx - data.multicast.rx - data.broadcast.rx; + stats.mPacketUnicastRxCount = data.pkts.tx - data.multicast.tx - data.broadcast.tx; +#else + stats.mPacketUnicastRxCount = data.unicast.rx; + stats.mPacketUnicastTxCount = data.unicast.tx; +#endif + stats.mBeaconsSuccessCount = data.sta_mgmt.beacons_rx; + stats.mBeaconsLostCount = data.sta_mgmt.beacons_miss; return CHIP_NO_ERROR; } -void WiFiManager::ScanResultHandler(Platform::UniquePtr data) +void WiFiManager::ScanResultHandler(Platform::UniquePtr data, size_t length) { + // Validate that input data size matches the expected one. + VerifyOrReturn(length == sizeof(wifi_scan_result)); + // Contrary to other handlers, offload accumulating of the scan results from the CHIP thread to the caller's thread - const struct wifi_scan_result * scanResult = reinterpret_cast(data.get()); + const wifi_scan_result * scanResult = reinterpret_cast(data.get()); if (Instance().mInternalScan && Instance().mWantedNetwork.GetSsidSpan().data_equal(ByteSpan(scanResult->ssid, scanResult->ssid_length))) @@ -369,45 +374,33 @@ void WiFiManager::ScanResultHandler(Platform::UniquePtr data) if (Instance().mScanResultCallback && !Instance().mInternalScan) { - /* Here we need to check if the scan is targeting a specific network and filter the scan result accordingly, - * to make sure only the targeted SSID is reported */ - if (Instance().mNetworkToScan.GetSsidSpan().size() == 0) - { - Instance().mScanResultCallback(ToScanResponse(scanResult)); - } - else if (Instance().mNetworkToScan.GetSsidSpan().data_equal(ByteSpan(scanResult->ssid, scanResult->ssid_length))) - { - Instance().mScanResultCallback(ToScanResponse(scanResult)); - } + Instance().mScanResultCallback(ToScanResponse(scanResult)); } } -void WiFiManager::ScanDoneHandler(Platform::UniquePtr data) +void WiFiManager::ScanDoneHandler(Platform::UniquePtr data, size_t length) { + // Validate that input data size matches the expected one. + VerifyOrReturn(length == sizeof(wifi_status)); + CHIP_ERROR err = SystemLayer().ScheduleLambda([capturedData = data.get()] { Platform::UniquePtr safePtr(capturedData); - uint8_t * rawData = safePtr.get(); - const wifi_status * status = reinterpret_cast(rawData); - WiFiRequestStatus requestStatus = static_cast(status->status); - - /* Reset the specific network to scan */ - if (Instance().mNetworkToScan.GetSsidSpan().size() > 0) - { - Instance().mNetworkToScan.Erase(); - } + uint8_t * rawData = safePtr.get(); + const wifi_status * status = reinterpret_cast(rawData); + ScanDoneStatus scanDoneStatus = status->status; - if (requestStatus == WiFiRequestStatus::FAILURE) + if (scanDoneStatus) { - ChipLogError(DeviceLayer, "Wi-Fi scan finalization failure (%d)", status->status); + ChipLogError(DeviceLayer, "Wi-Fi scan finalization failure (%d)", scanDoneStatus); } else { - ChipLogProgress(DeviceLayer, "Wi-Fi scan done (%d)", status->status); + ChipLogProgress(DeviceLayer, "Wi-Fi scan done"); } if (Instance().mScanDoneCallback && !Instance().mInternalScan) { - Instance().mScanDoneCallback(requestStatus); + Instance().mScanDoneCallback(scanDoneStatus); // restore the connection state from before the scan request was issued Instance().mWiFiState = Instance().mCachedWiFiState; return; @@ -416,6 +409,7 @@ void WiFiManager::ScanDoneHandler(Platform::UniquePtr data) // Internal scan is supposed to be followed by a connection request if the SSID has been found if (Instance().mInternalScan) { + if (!Instance().mSsidFound) { ChipLogProgress(DeviceLayer, "No requested SSID found"); @@ -430,12 +424,13 @@ void WiFiManager::ScanDoneHandler(Platform::UniquePtr data) net_if * iface = InetUtils::GetInterface(); VerifyOrReturn(nullptr != iface, CHIP_ERROR_INTERNAL); - if (net_mgmt(NET_REQUEST_WIFI_CONNECT, iface, &(Instance().mWiFiParams.mParams), sizeof(wifi_connect_req_params))) + if (net_mgmt(NET_REQUEST_WIFI_CONNECT, Instance().mNetIf, &(Instance().mWiFiParams.mParams), + sizeof(wifi_connect_req_params))) { ChipLogError(DeviceLayer, "Connection request failed"); - if (Instance().mHandling.mOnConnectionFailed) + if (Instance().mHandling.mOnConnectionDone) { - Instance().mHandling.mOnConnectionFailed(); + Instance().mHandling.mOnConnectionDone(WIFI_STATUS_CONN_FAIL); } Instance().mWiFiState = WIFI_STATE_DISCONNECTED; return; @@ -455,38 +450,65 @@ void WiFiManager::ScanDoneHandler(Platform::UniquePtr data) void WiFiManager::SendRouterSolicitation(System::Layer * layer, void * param) { - net_if * iface = InetUtils::GetInterface(); - if (iface && iface->if_dev->link_addr.type == NET_LINK_ETHERNET) + net_if_start_rs(Instance().mNetIf); + Instance().mRouterSolicitationCounter++; + if (Instance().mRouterSolicitationCounter < kRouterSolicitationMaxCount) { - net_if_start_rs(iface); - Instance().mRouterSolicitationCounter++; - if (Instance().mRouterSolicitationCounter < kRouterSolicitationMaxCount) - { - DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(kRouterSolicitationIntervalMs), - SendRouterSolicitation, nullptr); - } - else - { - Instance().mRouterSolicitationCounter = 0; - } + DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(kRouterSolicitationIntervalMs), SendRouterSolicitation, + nullptr); + } + else + { + Instance().mRouterSolicitationCounter = 0; } } -void WiFiManager::ConnectHandler(Platform::UniquePtr data) +void WiFiManager::ConnectHandler(Platform::UniquePtr data, size_t length) { + using app::Clusters::WiFiNetworkDiagnostics::AssociationFailureCauseEnum; + + // Validate that input data size matches the expected one. + VerifyOrReturn(length == sizeof(wifi_status)); + CHIP_ERROR err = SystemLayer().ScheduleLambda([capturedData = data.get()] { Platform::UniquePtr safePtr(capturedData); - uint8_t * rawData = safePtr.get(); - const wifi_status * status = reinterpret_cast(rawData); - WiFiRequestStatus requestStatus = static_cast(status->status); + uint8_t * rawData = safePtr.get(); + const wifi_status * status = reinterpret_cast(rawData); + wifi_conn_status connStatus = status->conn_status; - if (requestStatus == WiFiRequestStatus::FAILURE || requestStatus == WiFiRequestStatus::TERMINATED) + if (connStatus) { ChipLogProgress(DeviceLayer, "Connection to WiFi network failed or was terminated by another request"); Instance().mWiFiState = WIFI_STATE_DISCONNECTED; - if (Instance().mHandling.mOnConnectionFailed) + if (Instance().mHandling.mOnConnectionDone) + { + Instance().mHandling.mOnConnectionDone(connStatus); + } + + WiFiDiagnosticsDelegate * delegate = GetDiagnosticDataProvider().GetWiFiDiagnosticsDelegate(); + if (delegate) { - Instance().mHandling.mOnConnectionFailed(); + uint16_t reason = Instance().GetLastDisconnectReason(); + uint8_t associationFailureCause; + + switch (connStatus) + { + case WIFI_STATUS_CONN_WRONG_PASSWORD: + associationFailureCause = to_underlying(AssociationFailureCauseEnum::kAuthenticationFailed); + break; + case WIFI_STATUS_CONN_FAIL: + case WIFI_STATUS_CONN_TIMEOUT: + associationFailureCause = to_underlying(AssociationFailureCauseEnum::kAssociationFailed); + break; + case WIFI_STATUS_CONN_AP_NOT_FOUND: + associationFailureCause = to_underlying(AssociationFailureCauseEnum::kSsidNotFound); + break; + default: + associationFailureCause = to_underlying(AssociationFailureCauseEnum::kUnknown); + break; + } + + delegate->OnAssociationFailureDetected(associationFailureCause, reason); } } else // The connection has been established successfully. @@ -498,9 +520,9 @@ void WiFiManager::ConnectHandler(Platform::UniquePtr data) ChipLogProgress(DeviceLayer, "Connected to WiFi network"); Instance().mWiFiState = WIFI_STATE_COMPLETED; - if (Instance().mHandling.mOnConnectionSuccess) + if (Instance().mHandling.mOnConnectionDone) { - Instance().mHandling.mOnConnectionSuccess(); + Instance().mHandling.mOnConnectionDone(connStatus); } Instance().PostConnectivityStatusChange(kConnectivity_Established); @@ -513,6 +535,13 @@ void WiFiManager::ConnectHandler(Platform::UniquePtr data) { ChipLogError(DeviceLayer, "Cannot post event [error: %s]", ErrorStr(error)); } + + WiFiDiagnosticsDelegate * delegate = GetDiagnosticDataProvider().GetWiFiDiagnosticsDelegate(); + if (delegate) + { + delegate->OnConnectionStatusChanged( + to_underlying(app::Clusters::WiFiNetworkDiagnostics::ConnectionStatusEnum::kConnected)); + } } // cleanup the provisioning data as it is configured per each connect request Instance().ClearStationProvisioningData(); @@ -525,13 +554,74 @@ void WiFiManager::ConnectHandler(Platform::UniquePtr data) } } -void WiFiManager::DisconnectHandler(Platform::UniquePtr) +void WiFiManager::DisconnectHandler(Platform::UniquePtr data, size_t length) { - SystemLayer().ScheduleLambda([] { + // Validate that input data size matches the expected one. + VerifyOrReturn(length == sizeof(wifi_status)); + + CHIP_ERROR err = SystemLayer().ScheduleLambda([capturedData = data.get()] { + Platform::UniquePtr safePtr(capturedData); + uint8_t * rawData = safePtr.get(); + const wifi_status * status = reinterpret_cast(rawData); + uint16_t reason; + + switch (status->disconn_reason) + { + case WIFI_REASON_DISCONN_UNSPECIFIED: + reason = WLAN_REASON_UNSPECIFIED; + break; + case WIFI_REASON_DISCONN_USER_REQUEST: + reason = WLAN_REASON_DEAUTH_LEAVING; + break; + case WIFI_REASON_DISCONN_AP_LEAVING: + reason = WLAN_REASON_DEAUTH_LEAVING; + break; + case WIFI_REASON_DISCONN_INACTIVITY: + reason = WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY; + break; + default: + reason = WLAN_REASON_UNSPECIFIED; + break; + } + Instance().SetLastDisconnectReason(reason); + ChipLogProgress(DeviceLayer, "WiFi station disconnected"); Instance().mWiFiState = WIFI_STATE_DISCONNECTED; Instance().PostConnectivityStatusChange(kConnectivity_Lost); + + WiFiDiagnosticsDelegate * delegate = GetDiagnosticDataProvider().GetWiFiDiagnosticsDelegate(); + if (delegate) + { + delegate->OnConnectionStatusChanged( + to_underlying(app::Clusters::WiFiNetworkDiagnostics::ConnectionStatusEnum::kNotConnected)); + delegate->OnDisconnectionDetected(reason); + } }); + + if (CHIP_NO_ERROR == err) + { + // the ownership has been transferred to the worker thread - release the buffer + data.release(); + } +} + +void WiFiManager::IPv6AddressChangeHandler(const void * data) +{ + const in6_addr * addr = reinterpret_cast(data); + + // Filter out link-local addresses that are not routable outside of a local network. + if (!net_ipv6_is_ll_addr(addr)) + { + // This is needed to send mDNS queries containing updated IPv6 addresses. + ChipDeviceEvent event; + event.Type = DeviceEventType::kDnssdRestartNeeded; + + CHIP_ERROR error = PlatformMgr().PostEvent(&event); + if (error != CHIP_NO_ERROR) + { + ChipLogError(DeviceLayer, "Cannot post event: %" CHIP_ERROR_FORMAT, error.Format()); + } + } } WiFiManager::StationStatus WiFiManager::GetStationStatus() const @@ -597,11 +687,10 @@ System::Clock::Milliseconds32 WiFiManager::CalculateNextRecoveryTime() CHIP_ERROR WiFiManager::SetLowPowerMode(bool onoff) { - net_if * iface = InetUtils::GetInterface(); - VerifyOrReturnError(nullptr != iface, CHIP_ERROR_INTERNAL); + VerifyOrReturnError(nullptr != mNetIf, CHIP_ERROR_INTERNAL); wifi_ps_config currentConfig{}; - if (net_mgmt(NET_REQUEST_WIFI_PS_CONFIG, iface, ¤tConfig, sizeof(currentConfig))) + if (net_mgmt(NET_REQUEST_WIFI_PS_CONFIG, mNetIf, ¤tConfig, sizeof(currentConfig))) { ChipLogError(DeviceLayer, "Get current low power mode config request failed"); return CHIP_ERROR_INTERNAL; @@ -611,7 +700,7 @@ CHIP_ERROR WiFiManager::SetLowPowerMode(bool onoff) (currentConfig.ps_params.enabled == WIFI_PS_DISABLED && onoff == true)) { wifi_ps_params params{ .enabled = onoff ? WIFI_PS_ENABLED : WIFI_PS_DISABLED }; - if (net_mgmt(NET_REQUEST_WIFI_PS, iface, ¶ms, sizeof(params))) + if (net_mgmt(NET_REQUEST_WIFI_PS, mNetIf, ¶ms, sizeof(params))) { ChipLogError(DeviceLayer, "Set low power mode request failed"); return CHIP_ERROR_INTERNAL; @@ -624,5 +713,15 @@ CHIP_ERROR WiFiManager::SetLowPowerMode(bool onoff) return CHIP_NO_ERROR; } +void WiFiManager::SetLastDisconnectReason(uint16_t reason) +{ + mLastDisconnectedReason = reason; +} + +uint16_t WiFiManager::GetLastDisconnectReason() +{ + return mLastDisconnectedReason; +} + } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/Zephyr/wifi/WiFiManager.h b/src/platform/Zephyr/wifi/WiFiManager.h index d48536fb3d0da3..99bf777547a800 100644 --- a/src/platform/Zephyr/wifi/WiFiManager.h +++ b/src/platform/Zephyr/wifi/WiFiManager.h @@ -88,16 +88,16 @@ class Map class WiFiManager { public: - enum WiFiRequestStatus : int - { - SUCCESS = 0, - FAILURE = 1, - TERMINATED = 2 - }; + /* No copy, nor move. */ + WiFiManager(const WiFiManager &) = delete; + WiFiManager & operator=(const WiFiManager &) = delete; + WiFiManager(WiFiManager &&) = delete; + WiFiManager & operator=(WiFiManager &&) = delete; + using ScanDoneStatus = decltype(wifi_status::status); using ScanResultCallback = void (*)(const NetworkCommissioning::WiFiScanResponse &); - using ScanDoneCallback = void (*)(WiFiRequestStatus); - using ConnectionCallback = void (*)(); + using ScanDoneCallback = void (*)(const ScanDoneStatus &); + using ConnectionCallback = void (*)(const wifi_conn_status &); enum class StationStatus : uint8_t { @@ -120,8 +120,7 @@ class WiFiManager struct ConnectionHandling { - ConnectionCallback mOnConnectionSuccess{}; - ConnectionCallback mOnConnectionFailed{}; + ConnectionCallback mOnConnectionDone{}; System::Clock::Seconds32 mConnectionTimeout{}; }; @@ -182,12 +181,18 @@ class WiFiManager CHIP_ERROR ClearStationProvisioningData(); CHIP_ERROR Disconnect(); CHIP_ERROR GetWiFiInfo(WiFiInfo & info) const; + const WiFiNetwork & GetWantedNetwork() const { return mWantedNetwork; } CHIP_ERROR GetNetworkStatistics(NetworkStatistics & stats) const; void AbortConnectionRecovery(); CHIP_ERROR SetLowPowerMode(bool onoff); + void SetLastDisconnectReason(uint16_t reason); + uint16_t GetLastDisconnectReason(); private: - using NetEventHandler = void (*)(Platform::UniquePtr); + using NetEventHandler = void (*)(Platform::UniquePtr, size_t); + + WiFiManager() = default; + ~WiFiManager() = default; struct ConnectionParams { @@ -198,14 +203,18 @@ class WiFiManager constexpr static uint32_t kWifiManagementEvents = NET_EVENT_WIFI_SCAN_RESULT | NET_EVENT_WIFI_SCAN_DONE | NET_EVENT_WIFI_CONNECT_RESULT | NET_EVENT_WIFI_DISCONNECT_RESULT | NET_EVENT_WIFI_IFACE_STATUS; + constexpr static uint32_t kIPv6ManagementEvents = NET_EVENT_IPV6_ADDR_ADD | NET_EVENT_IPV6_ADDR_DEL; + // Event handling static void WifiMgmtEventHandler(net_mgmt_event_callback * cb, uint32_t mgmtEvent, net_if * iface); - static void ScanResultHandler(Platform::UniquePtr data); - static void ScanDoneHandler(Platform::UniquePtr data); - static void ConnectHandler(Platform::UniquePtr data); - static void DisconnectHandler(Platform::UniquePtr data); + static void IPv6MgmtEventHandler(net_mgmt_event_callback * cb, uint32_t mgmtEvent, net_if * iface); + static void ScanResultHandler(Platform::UniquePtr data, size_t length); + static void ScanDoneHandler(Platform::UniquePtr data, size_t length); + static void ConnectHandler(Platform::UniquePtr data, size_t length); + static void DisconnectHandler(Platform::UniquePtr data, size_t length); static void PostConnectivityStatusChange(ConnectivityChange changeType); static void SendRouterSolicitation(System::Layer * layer, void * param); + static void IPv6AddressChangeHandler(const void * data); // Connection Recovery feature // This feature allows re-scanning and re-connecting the connection to the known network after @@ -220,21 +229,25 @@ class WiFiManager void ResetRecoveryTime(); System::Clock::Milliseconds32 CalculateNextRecoveryTime(); + net_if * mNetIf{ nullptr }; ConnectionParams mWiFiParams{}; - ConnectionHandling mHandling; + ConnectionHandling mHandling{}; + wifi_scan_params mScanParams{}; + char mScanSsidBuffer[DeviceLayer::Internal::kMaxWiFiSSIDLength + 1] = { 0 }; wifi_iface_state mWiFiState; wifi_iface_state mCachedWiFiState; net_mgmt_event_callback mWiFiMgmtClbk{}; + net_mgmt_event_callback mIPv6MgmtClbk{}; ScanResultCallback mScanResultCallback{ nullptr }; ScanDoneCallback mScanDoneCallback{ nullptr }; WiFiNetwork mWantedNetwork{}; - WiFiNetwork mNetworkToScan{}; bool mInternalScan{ false }; uint8_t mRouterSolicitationCounter = 0; bool mSsidFound{ false }; uint32_t mConnectionRecoveryCounter{ 0 }; uint32_t mConnectionRecoveryTimeMs{ kConnectionRecoveryMinIntervalMs }; bool mApplicationDisconnectRequested{ false }; + uint16_t mLastDisconnectedReason = WLAN_REASON_UNSPECIFIED; static const Map sStatusMap; static const Map sEventHandlerMap; diff --git a/src/platform/Zephyr/wifi/ZephyrWifiDriver.cpp b/src/platform/Zephyr/wifi/ZephyrWifiDriver.cpp index 53b7fa684a2213..7404a0272297be 100644 --- a/src/platform/Zephyr/wifi/ZephyrWifiDriver.cpp +++ b/src/platform/Zephyr/wifi/ZephyrWifiDriver.cpp @@ -17,10 +17,13 @@ #include "ZephyrWifiDriver.h" +#include + #include #include #include +#include #include using namespace ::chip; @@ -103,8 +106,9 @@ CHIP_ERROR ZephyrWifiDriver::Init(NetworkStatusChangeCallback * networkStatusCha if (mStagingNetwork.IsConfigured()) { - WiFiManager::ConnectionHandling handling{ [] { Instance().OnNetworkStatusChanged(Status::kSuccess); }, - [] { Instance().OnNetworkStatusChanged(Status::kUnknownError); }, + WiFiManager::ConnectionHandling handling{ [](const wifi_conn_status & connStatus) { + Instance().OnNetworkConnStatusChanged(connStatus); + }, System::Clock::Seconds32{ kWiFiConnectNetworkTimeoutSeconds } }; ReturnErrorOnFailure( WiFiManager::Instance().Connect(mStagingNetwork.GetSsidSpan(), mStagingNetwork.GetPassSpan(), handling)); @@ -113,8 +117,11 @@ CHIP_ERROR ZephyrWifiDriver::Init(NetworkStatusChangeCallback * networkStatusCha return CHIP_NO_ERROR; } -void ZephyrWifiDriver::OnNetworkStatusChanged(Status status) +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); @@ -122,7 +129,23 @@ void ZephyrWifiDriver::OnNetworkStatusChanged(Status status) if (mpNetworkStatusChangeCallback) { - mpNetworkStatusChangeCallback->OnNetworkingStatusChange(status, NullOptional, NullOptional); + 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(connStatus)) + : NullOptional); } if (mpConnectCallback) @@ -158,12 +181,15 @@ CHIP_ERROR ZephyrWifiDriver::RevertConfiguration() // we are already connected to this network, so return prematurely return CHIP_NO_ERROR; } + + WiFiManager::Instance().Disconnect(); } if (mStagingNetwork.IsConfigured()) { - WiFiManager::ConnectionHandling handling{ [] { Instance().OnNetworkStatusChanged(Status::kSuccess); }, - [] { Instance().OnNetworkStatusChanged(Status::kUnknownError); }, + WiFiManager::ConnectionHandling handling{ [](const wifi_conn_status & connStatus) { + Instance().OnNetworkConnStatusChanged(connStatus); + }, System::Clock::Seconds32{ kWiFiConnectNetworkTimeoutSeconds } }; ReturnErrorOnFailure( WiFiManager::Instance().Connect(mStagingNetwork.GetSsidSpan(), mStagingNetwork.GetPassSpan(), handling)); @@ -217,8 +243,9 @@ void ZephyrWifiDriver::ConnectNetwork(ByteSpan networkId, ConnectCallback * call { Status status = Status::kSuccess; WiFiManager::StationStatus stationStatus; - WiFiManager::ConnectionHandling handling{ [] { Instance().OnNetworkStatusChanged(Status::kSuccess); }, - [] { Instance().OnNetworkStatusChanged(Status::kUnknownError); }, + WiFiManager::ConnectionHandling handling{ [](const wifi_conn_status & connStatus) { + Instance().OnNetworkConnStatusChanged(connStatus); + }, System::Clock::Seconds32{ kWiFiConnectNetworkTimeoutSeconds } }; VerifyOrExit(mpConnectCallback == nullptr, status = Status::kUnknownError); @@ -262,11 +289,10 @@ void ZephyrWifiDriver::LoadFromStorage() mStagingNetwork = network; } -void ZephyrWifiDriver::OnScanWiFiNetworkDone(WiFiManager::WiFiRequestStatus status) +void ZephyrWifiDriver::OnScanWiFiNetworkDone(const WiFiManager::ScanDoneStatus & status) { VerifyOrReturn(mScanCallback != nullptr); - mScanCallback->OnFinished(status == WiFiManager::WiFiRequestStatus::SUCCESS ? Status::kSuccess : Status::kUnknownError, - CharSpan(), &mScanResponseIterator); + mScanCallback->OnFinished(status ? Status::kUnknownError : Status::kSuccess, CharSpan(), &mScanResponseIterator); mScanCallback = nullptr; } @@ -280,7 +306,7 @@ void ZephyrWifiDriver::ScanNetworks(ByteSpan ssid, WiFiDriver::ScanCallback * ca mScanCallback = callback; CHIP_ERROR error = WiFiManager::Instance().Scan( ssid, [](const WiFiScanResponse & response) { Instance().OnScanWiFiNetworkResult(response); }, - [](WiFiManager::WiFiRequestStatus status) { Instance().OnScanWiFiNetworkDone(status); }); + [](const WiFiManager::ScanDoneStatus & status) { Instance().OnScanWiFiNetworkDone(status); }); if (error != CHIP_NO_ERROR) { @@ -289,6 +315,13 @@ void ZephyrWifiDriver::ScanNetworks(ByteSpan ssid, WiFiDriver::ScanCallback * ca } } +uint32_t ZephyrWifiDriver::GetSupportedWiFiBandsMask() const +{ + uint32_t bands = static_cast(1UL << chip::to_underlying(WiFiBandEnum::k2g4)); + bands |= static_cast(1UL << chip::to_underlying(WiFiBandEnum::k5g)); + return bands; +} + } // namespace NetworkCommissioning } // namespace DeviceLayer } // namespace chip diff --git a/src/platform/Zephyr/wifi/ZephyrWifiDriver.h b/src/platform/Zephyr/wifi/ZephyrWifiDriver.h index 9096aa408b69bd..414311a07ec4bf 100644 --- a/src/platform/Zephyr/wifi/ZephyrWifiDriver.h +++ b/src/platform/Zephyr/wifi/ZephyrWifiDriver.h @@ -25,9 +25,9 @@ namespace chip { namespace DeviceLayer { namespace NetworkCommissioning { -constexpr uint8_t kMaxWiFiNetworks = 1; -constexpr uint8_t kWiFiScanNetworksTimeOutSeconds = 10; -constexpr uint8_t kWiFiConnectNetworkTimeoutSeconds = 35; +inline constexpr uint8_t kMaxWiFiNetworks = 1; +inline constexpr uint8_t kWiFiScanNetworksTimeOutSeconds = 10; +inline constexpr uint8_t kWiFiConnectNetworkTimeoutSeconds = 35; class ZephyrWifiScanResponseIterator : public Iterator { @@ -48,8 +48,8 @@ class ZephyrWifiDriver final : public WiFiDriver public: // Define non-volatile storage keys for SSID and password. // The naming convention is aligned with DefaultStorageKeyAllocator class. - static constexpr const char * kSsidKey = "g/wi/s"; - static constexpr const char * kPassKey = "g/wi/p"; + static constexpr char kSsidKey[] = "g/wi/s"; + static constexpr char kPassKey[] = "g/wi/p"; class WiFiNetworkIterator final : public NetworkIterator { @@ -86,6 +86,7 @@ class ZephyrWifiDriver final : public WiFiDriver Status AddOrUpdateNetwork(ByteSpan ssid, ByteSpan credentials, MutableCharSpan & outDebugText, uint8_t & outNetworkIndex) override; void ScanNetworks(ByteSpan ssid, ScanCallback * callback) override; + uint32_t GetSupportedWiFiBandsMask() const override; static ZephyrWifiDriver & Instance() { @@ -93,9 +94,9 @@ class ZephyrWifiDriver final : public WiFiDriver return sInstance; } - void OnNetworkStatusChanged(Status status); + void OnNetworkConnStatusChanged(const wifi_conn_status & connStatus); void OnScanWiFiNetworkResult(const WiFiScanResponse & result); - void OnScanWiFiNetworkDone(WiFiManager::WiFiRequestStatus status); + void OnScanWiFiNetworkDone(const WiFiManager::ScanDoneStatus & status); private: void LoadFromStorage(); diff --git a/src/platform/nxp/zephyr/ConnectivityManagerImpl.cpp b/src/platform/nxp/zephyr/ConnectivityManagerImpl.cpp index f927df2ac17480..4d183558c18953 100644 --- a/src/platform/nxp/zephyr/ConnectivityManagerImpl.cpp +++ b/src/platform/nxp/zephyr/ConnectivityManagerImpl.cpp @@ -17,11 +17,16 @@ #include +#include +#include +#include #include +#include #include -#include -#include +#ifndef CONFIG_ARCH_POSIX +#include +#endif #include @@ -34,15 +39,64 @@ #endif #if CHIP_DEVICE_CONFIG_ENABLE_THREAD +#include #include #endif -using namespace ::chip; +using namespace ::chip::Inet; using namespace ::chip::DeviceLayer::Internal; namespace chip { namespace DeviceLayer { +namespace { +CHIP_ERROR JoinLeaveMulticastGroup(net_if * iface, const Inet::IPAddress & address, + UDPEndPointImplSockets::MulticastOperation operation) +{ +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD + if (net_if_l2(iface) == &NET_L2_GET_NAME(OPENTHREAD)) + { + const otIp6Address otAddress = ToOpenThreadIP6Address(address); + const auto handler = operation == UDPEndPointImplSockets::MulticastOperation::kJoin ? otIp6SubscribeMulticastAddress + : otIp6UnsubscribeMulticastAddress; + otError error; + + ThreadStackMgr().LockThreadStack(); + error = handler(openthread_get_default_instance(), &otAddress); + ThreadStackMgr().UnlockThreadStack(); + + return MapOpenThreadError(error); + } +#endif + +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI + // The following code should also be valid for other interface types, such as Ethernet, + // but they are not officially supported, so for now enable it for Wi-Fi only. + const in6_addr in6Addr = InetUtils::ToZephyrAddr(address); + + if (operation == UDPEndPointImplSockets::MulticastOperation::kJoin) + { + net_if_mcast_addr * maddr = net_if_ipv6_maddr_add(iface, &in6Addr); + + if (maddr && !net_if_ipv6_maddr_is_joined(maddr)) + { + net_if_ipv6_maddr_join(iface, maddr); + } + } + else if (operation == UDPEndPointImplSockets::MulticastOperation::kLeave) + { + VerifyOrReturnError(net_if_ipv6_maddr_rm(iface, &in6Addr), CHIP_ERROR_INVALID_ADDRESS); + } + else + { + return CHIP_ERROR_INCORRECT_STATE; + } +#endif + + return CHIP_NO_ERROR; +} +} // namespace + ConnectivityManagerImpl ConnectivityManagerImpl::sInstance; CHIP_ERROR ConnectivityManagerImpl::_Init() @@ -53,6 +107,28 @@ CHIP_ERROR ConnectivityManagerImpl::_Init() #if CHIP_DEVICE_CONFIG_ENABLE_WIFI ReturnErrorOnFailure(InitWiFi()); #endif + +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD || CHIP_DEVICE_CONFIG_ENABLE_WIFI + UDPEndPointImplSockets::SetMulticastGroupHandler( + [](InterfaceId interfaceId, const IPAddress & address, UDPEndPointImplSockets::MulticastOperation operation) { + if (interfaceId.IsPresent()) + { + net_if * iface = InetUtils::GetInterface(interfaceId); + VerifyOrReturnError(iface != nullptr, INET_ERROR_UNKNOWN_INTERFACE); + + return JoinLeaveMulticastGroup(iface, address, operation); + } + + // If the interface is not specified, join or leave the multicast group on all interfaces. + for (int i = 1; net_if * iface = net_if_get_by_index(i); i++) + { + ReturnErrorOnFailure(JoinLeaveMulticastGroup(iface, address, operation)); + } + + return CHIP_NO_ERROR; + }); +#endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD || CHIP_DEVICE_CONFIG_ENABLE_WIFI + return CHIP_NO_ERROR; }