diff --git a/.github/workflows/chef.yaml b/.github/workflows/chef.yaml index f5eff507755c00..598122472c8fc4 100644 --- a/.github/workflows/chef.yaml +++ b/.github/workflows/chef.yaml @@ -109,9 +109,9 @@ jobs: uses: ./.github/actions/checkout-submodules-and-bootstrap with: platform: telink - # - name: Update Zephyr to specific revision (for developers purpose) - # shell: bash - # run: scripts/run_in_build_env.sh "python3 scripts/tools/telink/update_zephyr.py ce4027fc0768b8509758af2e43f74e3fd2c7d58d" + - name: Update Zephyr to specific revision (for developers purpose) + shell: bash + run: scripts/run_in_build_env.sh "python3 scripts/tools/telink/update_zephyr.py f28fc3268ea9515d33d7c392b648df25af5d0789" - name: CI Examples Telink shell: bash run: | diff --git a/.github/workflows/examples-telink.yaml b/.github/workflows/examples-telink.yaml index 610f3a79cbb814..c92fd88bd409a7 100644 --- a/.github/workflows/examples-telink.yaml +++ b/.github/workflows/examples-telink.yaml @@ -57,8 +57,8 @@ jobs: with: gh-context: ${{ toJson(github) }} - # - name: Update Zephyr to specific revision (for developers purpose) - # run: scripts/run_in_build_env.sh "python3 scripts/tools/telink/update_zephyr.py ce4027fc0768b8509758af2e43f74e3fd2c7d58d" + - name: Update Zephyr to specific revision (for developers purpose) + run: scripts/run_in_build_env.sh "python3 scripts/tools/telink/update_zephyr.py f28fc3268ea9515d33d7c392b648df25af5d0789" - name: Build example Telink (B92 retention) Air Quality Sensor App # Run test for master and s07641069 PRs @@ -165,14 +165,14 @@ jobs: - name: clean out build output (keep tools) run: rm -rf ./out/telink* - - name: Build example Telink (tl321x) Lighting App with OTA, Shell, Factory Data + - name: Build example Telink (tl321x) Lighting App with OTA, Factory Data # Run test for master and all PRs run: | ./scripts/run_in_build_env.sh \ - "./scripts/build/build_examples.py --target 'telink-tl3218x-light-ota-shell-factory-data' build" + "./scripts/build/build_examples.py --target 'telink-tl3218x-light-ota-factory-data' build" .environment/pigweed-venv/bin/python3 scripts/tools/memory/gh_sizes.py \ - telink tl3218x light-app-ota-shell-factory-data \ - out/telink-tl3218x-light-ota-shell-factory-data/zephyr/zephyr.elf \ + telink tl3218x light-app-ota-factory-data \ + out/telink-tl3218x-light-ota-factory-data/zephyr/zephyr.elf \ /tmp/bloat_reports/ - name: clean out build output (keep tools) diff --git a/config/telink/app/bootloader.conf b/config/telink/app/bootloader.conf index f6895c54c08731..cdc60a11c7a8ce 100644 --- a/config/telink/app/bootloader.conf +++ b/config/telink/app/bootloader.conf @@ -29,6 +29,7 @@ CONFIG_BOOT_SWAP_USING_SCRATCH=n # With disabled option the only image magic is validated CONFIG_BOOT_VALIDATE_SLOT0=y +# Required for Zephyr 3.3 and replased with CONFIG_BOOT_MAX_IMG_SECTORS_AUTO in new versions # Maximum number of image sectors supported by the bootloader. # Maximum signed image size: 512 * 4096 = 2M Bytes CONFIG_BOOT_MAX_IMG_SECTORS=512 @@ -40,3 +41,5 @@ CONFIG_BOOT_MAX_IMG_SECTORS=512 # - INFO, LOG_LEVEL_INF # - DEBUG, LOG_LEVEL_DBG CONFIG_MCUBOOT_LOG_LEVEL_INF=y + +CONFIG_PICOLIBC=y diff --git a/config/telink/app/bootloader_compress_lzma.conf b/config/telink/app/bootloader_compress_lzma.conf index 7a8d9ec950faf4..6cab83229bae34 100644 --- a/config/telink/app/bootloader_compress_lzma.conf +++ b/config/telink/app/bootloader_compress_lzma.conf @@ -22,6 +22,7 @@ CONFIG_BOOT_UPGRADE_ONLY=y # With disabled option the only image magic is validated CONFIG_BOOT_VALIDATE_SLOT0=y +# Required for Zephyr 3.3 and replased with CONFIG_BOOT_MAX_IMG_SECTORS_AUTO in new versions # Maximum number of image sectors supported by the bootloader. # Maximum signed image size: 512 * 4096 = 2M Bytes CONFIG_BOOT_MAX_IMG_SECTORS=512 @@ -42,3 +43,5 @@ CONFIG_SIZE_OPTIMIZATIONS=y # Enable support LZMA compression CONFIG_COMPRESS_LZMA=y + +CONFIG_PICOLIBC=y diff --git a/config/telink/chip-module/CMakeLists.txt b/config/telink/chip-module/CMakeLists.txt index f6800790deddac..0b095a53913612 100644 --- a/config/telink/chip-module/CMakeLists.txt +++ b/config/telink/chip-module/CMakeLists.txt @@ -56,6 +56,7 @@ endif() matter_add_flags(-DNL_ASSERT_USE_FLAGS_DEFAULT=1) zephyr_include_directories(${CHIP_ROOT}/src/platform/telink/) +zephyr_include_directories(${CHIP_ROOT}/src/platform/telink/wifi/${ZEPHYR_VERSION_STRING}) zephyr_get_compile_flags(ZEPHYR_CFLAGS_C C) matter_add_cflags("${ZEPHYR_CFLAGS_C}") @@ -64,7 +65,7 @@ matter_add_cxxflags("${ZEPHYR_CFLAGS_CC}") zephyr_get_gnu_cpp_standard(ZEPHYR_GNU_CPP_STD) matter_add_cxxflags(${ZEPHYR_GNU_CPP_STD}) -matter_add_flags(-DMBEDTLS_USER_CONFIG_FILE=) +matter_add_flags(-DMBEDTLS_USER_CONFIG_FILE=<${CONFIG_MBEDTLS_CFG_FILE}>) # Set up custom OpenThread configuration diff --git a/config/telink/chip-module/Kconfig b/config/telink/chip-module/Kconfig index 4bf5c4928670c2..326643619bb196 100644 --- a/config/telink/chip-module/Kconfig +++ b/config/telink/chip-module/Kconfig @@ -46,7 +46,7 @@ config BOOTLOADER_MCUBOOT select IMG_MANAGER select STREAM_FLASH select STREAM_FLASH_ERASE - select TELINK_W91_N22_MATTER_OTA_LAYOUT if BOARD_TLSR9118BDK40D + select TELINK_W91_N22_MATTER_OTA_LAYOUT if BOARD_TLSR9118BDK40D || BOARD_TLSR9118BDK40D_V1 config CHIP_OTA_REQUESTOR_BUFFER_SIZE int "OTA Requestor image buffer size" diff --git a/config/telink/chip-module/Kconfig.defaults b/config/telink/chip-module/Kconfig.defaults index aec634c3544d45..17ae385a8d0e02 100644 --- a/config/telink/chip-module/Kconfig.defaults +++ b/config/telink/chip-module/Kconfig.defaults @@ -24,6 +24,10 @@ config CHIP_PROJECT_CONFIG string "Project configuration file for Matter" default "include/CHIPProjectConfig.h" +config ZEPHYR_VERSION_3_3 + bool "Using Zephyr version 3.3" + default n + config LOG default y @@ -63,6 +67,7 @@ config POSIX_MAX_FDS # Application stack size config MAIN_STACK_SIZE default 3240 if PM || SOC_RISCV_TELINK_TL321X + default 4608 if SOC_RISCV_TELINK_W91 && !ZEPHYR_VERSION_3_3 default 4096 config INIT_STACKS @@ -80,11 +85,12 @@ config SYSTEM_WORKQUEUE_STACK_SIZE default 616 if PM config HEAP_MEM_POOL_SIZE - default 256 + default 256 if ZEPHYR_VERSION_3_3 + default 1280 config COMMON_LIBC_MALLOC_ARENA_SIZE - default 20716 if SOC_RISCV_TELINK_TL321X || SOC_SERIES_RISCV_TELINK_B9X_RETENTION - default 16384 if SOC_RISCV_TELINK_TL721X + default 20716 if SOC_SERIES_RISCV_TELINK_B9X_RETENTION || (SOC_RISCV_TELINK_TL321X && ZEPHYR_VERSION_3_3) + default 16384 if SOC_RISCV_TELINK_TL721X || (SOC_RISCV_TELINK_TL321X && !ZEPHYR_VERSION_3_3) default 12288 config NET_IPV6_MLD @@ -172,7 +178,8 @@ config BT_DEVICE_NAME_GATT_WRITABLE if SOC_RISCV_TELINK_B9X || SOC_RISCV_TELINK_TLX config TL_BLE_CTRL_THREAD_STACK_SIZE - default 648 + default 648 if ZEPHYR_VERSION_3_3 + default 680 config TL_BLE_CTRL_MASTER_MAX_NUM default 0 @@ -194,13 +201,9 @@ endif # SOC_RISCV_TELINK_B9X || SOC_RISCV_TELINK_TLX endif # BT -# Board retention config -# Cut down the ram cost by matter's change,it can keep ramcode (driver). -# No need load the ramcode every time in thread mode (retention mode). -# If the ram is not enough , can change it back , initial setting is n. if BOARD_TLSR9528A_RETENTION || BOARD_TLSR9518ADK80D_RETENTION config SOC_SERIES_RISCV_TELINK_B9X_NON_RETENTION_RAM_CODE - default n if PM + default y if PM config TELINK_B9X_MATTER_RETENTION_LAYOUT default y if PM @@ -237,7 +240,7 @@ config CHIP_OTA_REQUESTOR default y config MCUBOOT_SIGNATURE_KEY_FILE - default "bootloader/mcuboot/root-ec-p256.pem" if BOARD_TLSR9118BDK40D + default "bootloader/mcuboot/root-ec-p256.pem" if BOARD_TLSR9118BDK40D || BOARD_TLSR9118BDK40D_V1 depends on BOOTLOADER_MCUBOOT # In current config/zephyr/Kconfig @@ -351,6 +354,8 @@ config CHIP_WIFI select NET_IPV6_ND # enable Neighbor Discovery to handle Router Advertisements select NET_IPV6_NBR_CACHE select NET_STATISTICS_USER_API + imply MBEDTLS_ENTROPY_C + imply MBEDTLS_KEY_EXCHANGE_RSA_ENABLED if CHIP_WIFI @@ -405,7 +410,14 @@ config CHIP_WIFI_CONNECTION_RECOVERY_JITTER within range [-JITTER; +JITTER]. config NET_MGMT_EVENT_STACK_SIZE - default 1128 + default 1128 if ZEPHYR_VERSION_3_3 + default 1250 + +if !ZEPHYR_VERSION_3_3 +config IPC_SERVICE_BACKEND_ICMSG_WQ_STACK_SIZE + int "Size of RX work queue stack" + default 2304 if TELINK_W91_IPC_DISPATCHER +endif endif # CHIP_WIFI @@ -420,12 +432,9 @@ config CHIP_ENABLE_PAIRING_AUTOSTART config MBEDTLS default y -config MBEDTLS_USER_CONFIG_ENABLE +config MBEDTLS_CIPHER_AES_ENABLED default y -config MBEDTLS_USER_CONFIG_FILE - default "telink-mbedtls-config.h" - config MBEDTLS_CIPHER_CCM_ENABLED default y @@ -444,10 +453,29 @@ config MBEDTLS_ECDH_C config MBEDTLS_ECDSA_C default y -# getopt version -config GETOPT_LONG +config MBEDTLS_PKCS5_C + default y + +config MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED + default y + +if !ZEPHYR_VERSION_3_3 +config MBEDTLS_ENTROPY_POLL_ZEPHYR + bool "Provide entropy data to Mbed TLS through entropy driver or random generator" + default y + +config MBEDTLS_HKDF_C + bool "HMAC-based Extract-and-Expand Key Derivation Function" default y +config MBEDTLS_X509_CSR_WRITE_C + bool "X.509 Certificate Signing Requests writing" + default y +endif # !ZEPHYR_VERSION_3_3 + +config GETOPT_LONG + default y if ZEPHYR_VERSION_3_3 + # Disable not used shell modules config SHELL_WILDCARD diff --git a/examples/platform/telink/common.cmake b/examples/platform/telink/common.cmake index b8da9a0dc876c8..5aa89a325ffc65 100644 --- a/examples/platform/telink/common.cmake +++ b/examples/platform/telink/common.cmake @@ -13,6 +13,19 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Generate Zephyr version files for backward compatibility +file(STRINGS "$ENV{ZEPHYR_BASE}/VERSION" ZEPHYR_VERSION_STRING REGEX "[0-9]+") +string(REGEX REPLACE "[^0-9;]" "" ZEPHYR_VERSION_STRING "${ZEPHYR_VERSION_STRING}") +string(REPLACE ";" "." ZEPHYR_VERSION_STRING "${ZEPHYR_VERSION_STRING}") +file(WRITE "${CMAKE_BINARY_DIR}/modules/chip-module/zephyr_version.gni" "ZEPHYR_VERSION_STRING = \"${ZEPHYR_VERSION_STRING}\"\n") +if(${ZEPHYR_VERSION_STRING} MATCHES "^3\\.3") + set(ZEPHYR_VERSION_OVERLAY_FILE "${CMAKE_BINARY_DIR}/zephyr_version.conf") + file(WRITE ${ZEPHYR_VERSION_OVERLAY_FILE} "CONFIG_ZEPHYR_VERSION_3_3=y\n") + + # Add required MbedTLS defines for Zephyr 3.3 + add_definitions(-DMBEDTLS_HKDF_C -DMBEDTLS_X509_CREATE_C -DMBEDTLS_X509_CSR_WRITE_C) +endif() + string(REPLACE "_retention" "" BASE_BOARD ${BOARD}) string(REGEX REPLACE "_v[0-9]+" "" BASE_BOARD ${BASE_BOARD}) @@ -117,7 +130,7 @@ else() endif() if(NOT CONF_FILE) - set(CONF_FILE ${USB_CONF_OVERLAY_FILE} ${MARS_CONF_OVERLAY_FILE} prj.conf) + set(CONF_FILE ${USB_CONF_OVERLAY_FILE} ${MARS_CONF_OVERLAY_FILE} ${ZEPHYR_VERSION_OVERLAY_FILE} prj.conf) endif() # Load NCS/Zephyr build system diff --git a/src/platform/Zephyr/InetUtils.cpp b/src/platform/Zephyr/InetUtils.cpp index b692cdafa80c59..032290ece6a345 100644 --- a/src/platform/Zephyr/InetUtils.cpp +++ b/src/platform/Zephyr/InetUtils.cpp @@ -43,15 +43,15 @@ net_if * GetInterface(Inet::InterfaceId ifaceId) #if CHIP_DEVICE_CONFIG_ENABLE_WIFI net_if * GetWiFiInterface() { -// TODO: Remove dependency after Telink Zephyr update -// net_if_get_first_wifi() is not available in Zephyr 3.3.99 -#if !defined(CONFIG_SOC_RISCV_TELINK_W91) - return net_if_get_first_wifi(); -#else +// TODO: Remove dependency after Telink Zephyr 3.3 support is removed +// net_if_get_first_wifi() is not available in Zephyr 3.3 +#if defined(CONFIG_SOC_RISCV_TELINK_W91) && defined(CONFIG_ZEPHYR_VERSION_3_3) return GetInterface(); +#else + return net_if_get_first_wifi(); #endif } -#endif +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI } // namespace InetUtils } // namespace DeviceLayer diff --git a/src/platform/telink/BLEManagerImpl.cpp b/src/platform/telink/BLEManagerImpl.cpp index 6752b8bc63811e..7844b62a47b6b0 100644 --- a/src/platform/telink/BLEManagerImpl.cpp +++ b/src/platform/telink/BLEManagerImpl.cpp @@ -40,7 +40,11 @@ #include #include #include +#if defined(CONFIG_ZEPHYR_VERSION_3_3) #include +#else +#include +#endif #include #include #include diff --git a/src/platform/telink/BUILD.gn b/src/platform/telink/BUILD.gn index f5fb715a50d314..911591e6b916d2 100644 --- a/src/platform/telink/BUILD.gn +++ b/src/platform/telink/BUILD.gn @@ -96,14 +96,15 @@ static_library("telink") { } if (chip_enable_wifi) { + import("$root_build_dir/zephyr_version.gni") sources += [ "OTAImageProcessorImplWiFi.h", - "wifi/ConnectivityManagerImplWiFi.cpp", + "wifi/" + ZEPHYR_VERSION_STRING + "/ConnectivityManagerImplWiFi.cpp", "wifi/ConnectivityManagerImplWiFi.h", - "wifi/TelinkWiFiDriver.cpp", + "wifi/" + ZEPHYR_VERSION_STRING + "/TelinkWiFiDriver.cpp", "wifi/TelinkWiFiDriver.h", - "wifi/WiFiManager.cpp", - "wifi/WiFiManager.h", + "wifi/" + ZEPHYR_VERSION_STRING + "/WiFiManager.cpp", + "wifi/" + ZEPHYR_VERSION_STRING + "/WiFiManager.h", ] } diff --git a/src/platform/telink/ConnectivityManagerImpl.cpp b/src/platform/telink/ConnectivityManagerImpl.cpp index 9b2b651dab93b8..10ef8699adef79 100644 --- a/src/platform/telink/ConnectivityManagerImpl.cpp +++ b/src/platform/telink/ConnectivityManagerImpl.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -76,7 +77,11 @@ CHIP_ERROR JoinLeaveMulticastGroup(net_if * iface, const Inet::IPAddress & addre if (maddr && !net_if_ipv6_maddr_is_joined(maddr)) { +#if defined(CONFIG_ZEPHYR_VERSION_3_3) net_if_ipv6_maddr_join(maddr); +#else + net_if_ipv6_maddr_join(iface, maddr); +#endif } } else if (operation == UDPEndPointImplSockets::MulticastOperation::kLeave) diff --git a/src/platform/telink/FactoryDataParser.c b/src/platform/telink/FactoryDataParser.c index 12260631262342..a2d4c26ede9902 100644 --- a/src/platform/telink/FactoryDataParser.c +++ b/src/platform/telink/FactoryDataParser.c @@ -43,7 +43,11 @@ static inline bool uint16_decode(zcbor_state_t * states, uint16_t * value) bool ParseFactoryData(uint8_t * buffer, uint16_t bufferSize, struct FactoryData * factoryData) { memset(factoryData, 0, sizeof(*factoryData)); +#if defined(CONFIG_ZEPHYR_VERSION_3_3) ZCBOR_STATE_D(states, MAX_FACTORY_DATA_NESTING_LEVEL, buffer, bufferSize, 1); +#else + ZCBOR_STATE_D(states, MAX_FACTORY_DATA_NESTING_LEVEL, buffer, bufferSize, 1, 0); +#endif bool res = zcbor_map_start_decode(states); struct zcbor_string currentString; diff --git a/src/platform/telink/SystemPlatformConfig.h b/src/platform/telink/SystemPlatformConfig.h index 9f77a54b223169..c2d677cd8e9adb 100644 --- a/src/platform/telink/SystemPlatformConfig.h +++ b/src/platform/telink/SystemPlatformConfig.h @@ -55,10 +55,15 @@ struct ChipDeviceEvent; #define CHIP_SYSTEM_CONFIG_USE_SOCKETS 1 // Reduce packet buffer pool size (default 15) to reduce ram consumption -#if defined CONFIG_PM || defined CONFIG_SOC_RISCV_TELINK_TL321X +#if defined(CONFIG_PM) || defined(CONFIG_SOC_RISCV_TELINK_TL321X) #define CHIP_SYSTEM_CONFIG_PACKETBUFFER_POOL_SIZE 0 #else #define CHIP_SYSTEM_CONFIG_PACKETBUFFER_POOL_SIZE 8 #endif // ========== Platform-specific Configuration Overrides ========= + +// Disable Zephyr Socket extensions module, as the Zephyr RTOS now implements recvmsg() +#if !defined(CONFIG_ZEPHYR_VERSION_3_3) +#define CHIP_SYSTEM_CONFIG_USE_ZEPHYR_SOCKET_EXTENSIONS 0 +#endif diff --git a/src/platform/telink/telink-mbedtls-config.h b/src/platform/telink/telink-mbedtls-config.h deleted file mode 100644 index c9a79ee88f32fb..00000000000000 --- a/src/platform/telink/telink-mbedtls-config.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * - * Copyright (c) 2022-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. - */ - -/** - * @file - * Telink mbedtls configuration file. - * - */ - -#ifndef MBEDTLS_TSLR9_CONF_H -#define MBEDTLS_TSLR9_CONF_H - -#ifdef MBEDTLS_PLATFORM_MEMORY -#undef MBEDTLS_PLATFORM_MEMORY -#endif - -#ifdef MBEDTLS_MEMORY_BUFFER_ALLOC_C -#undef MBEDTLS_MEMORY_BUFFER_ALLOC_C -#endif - -#define MBEDTLS_HKDF_C -#define MBEDTLS_PKCS5_C -#define MBEDTLS_X509_CREATE_C -#define MBEDTLS_X509_CSR_WRITE_C - -#undef MBEDTLS_ERROR_C - -#endif /* MBEDTLS_TSLR9_CONF_H */ diff --git a/src/platform/telink/tlsr9518adk80d_mars.overlay b/src/platform/telink/tlsr9518adk80d_mars.overlay index a8a130aa2cac85..6c00b211cd667d 100644 --- a/src/platform/telink/tlsr9518adk80d_mars.overlay +++ b/src/platform/telink/tlsr9518adk80d_mars.overlay @@ -28,7 +28,7 @@ color-mapping = ; - in-gpios = <&gpiob 4 0>; + gpios = <&gpiob 4 0>; }; }; diff --git a/src/platform/telink/wifi/ConnectivityManagerImplWiFi.cpp b/src/platform/telink/wifi/3.3.99.0/ConnectivityManagerImplWiFi.cpp similarity index 99% rename from src/platform/telink/wifi/ConnectivityManagerImplWiFi.cpp rename to src/platform/telink/wifi/3.3.99.0/ConnectivityManagerImplWiFi.cpp index 5bef79bb22fdc5..b2b4390fe7b3f2 100644 --- a/src/platform/telink/wifi/ConnectivityManagerImplWiFi.cpp +++ b/src/platform/telink/wifi/3.3.99.0/ConnectivityManagerImplWiFi.cpp @@ -21,7 +21,7 @@ #include #include -#include "ConnectivityManagerImplWiFi.h" +#include "../ConnectivityManagerImplWiFi.h" #include "WiFiManager.h" #if CHIP_DEVICE_CONFIG_ENABLE_WIFI diff --git a/src/platform/telink/wifi/TelinkWiFiDriver.cpp b/src/platform/telink/wifi/3.3.99.0/TelinkWiFiDriver.cpp similarity index 99% rename from src/platform/telink/wifi/TelinkWiFiDriver.cpp rename to src/platform/telink/wifi/3.3.99.0/TelinkWiFiDriver.cpp index f6c19a2c1f3030..9b78c5584767e1 100644 --- a/src/platform/telink/wifi/TelinkWiFiDriver.cpp +++ b/src/platform/telink/wifi/3.3.99.0/TelinkWiFiDriver.cpp @@ -15,7 +15,7 @@ * limitations under the License. */ -#include "TelinkWiFiDriver.h" +#include "../TelinkWiFiDriver.h" #include diff --git a/src/platform/telink/wifi/WiFiManager.cpp b/src/platform/telink/wifi/3.3.99.0/WiFiManager.cpp similarity index 100% rename from src/platform/telink/wifi/WiFiManager.cpp rename to src/platform/telink/wifi/3.3.99.0/WiFiManager.cpp diff --git a/src/platform/telink/wifi/WiFiManager.h b/src/platform/telink/wifi/3.3.99.0/WiFiManager.h similarity index 100% rename from src/platform/telink/wifi/WiFiManager.h rename to src/platform/telink/wifi/3.3.99.0/WiFiManager.h diff --git a/src/platform/telink/wifi/3.7.99.0/ConnectivityManagerImplWiFi.cpp b/src/platform/telink/wifi/3.7.99.0/ConnectivityManagerImplWiFi.cpp new file mode 100644 index 00000000000000..8f48a91285b5d8 --- /dev/null +++ b/src/platform/telink/wifi/3.7.99.0/ConnectivityManagerImplWiFi.cpp @@ -0,0 +1,177 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * 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 + +#include +#include + +#include "../ConnectivityManagerImplWiFi.h" +#include "WiFiManager.h" + +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI + +using namespace ::chip; +using namespace ::chip::Inet; +using namespace ::chip::System; + +namespace chip { +namespace DeviceLayer { + +CHIP_ERROR ConnectivityManagerImplWiFi::InitWiFi() +{ + return WiFiManager::Instance().Init(); +} + +ConnectivityManager::WiFiStationMode ConnectivityManagerImplWiFi::_GetWiFiStationMode(void) +{ + if (mStationMode != ConnectivityManager::WiFiStationMode::kWiFiStationMode_ApplicationControlled) + { + mStationMode = (WiFiManager::StationStatus::DISABLED == WiFiManager::Instance().GetStationStatus()) + ? ConnectivityManager::WiFiStationMode::kWiFiStationMode_Disabled + : ConnectivityManager::WiFiStationMode::kWiFiStationMode_Enabled; + } + return mStationMode; +} + +CHIP_ERROR ConnectivityManagerImplWiFi::_SetWiFiStationMode(ConnectivityManager::WiFiStationMode aMode) +{ + VerifyOrReturnError(ConnectivityManager::WiFiStationMode::kWiFiStationMode_NotSupported != aMode, CHIP_ERROR_INVALID_ARGUMENT); + + mStationMode = aMode; + + return CHIP_NO_ERROR; +} + +bool ConnectivityManagerImplWiFi::_IsWiFiStationEnabled(void) +{ + return (WiFiManager::StationStatus::DISABLED <= WiFiManager::Instance().GetStationStatus()); +} + +bool ConnectivityManagerImplWiFi::_IsWiFiStationApplicationControlled(void) +{ + return (ConnectivityManager::WiFiStationMode::kWiFiStationMode_ApplicationControlled == mStationMode); +} + +bool ConnectivityManagerImplWiFi::_IsWiFiStationConnected(void) +{ + return (WiFiManager::StationStatus::CONNECTED == WiFiManager::Instance().GetStationStatus()); +} + +System::Clock::Timeout ConnectivityManagerImplWiFi::_GetWiFiStationReconnectInterval(void) +{ + return mWiFiStationReconnectInterval; +} + +CHIP_ERROR ConnectivityManagerImplWiFi::_SetWiFiStationReconnectInterval(System::Clock::Timeout val) +{ + mWiFiStationReconnectInterval = val; + return CHIP_NO_ERROR; +} + +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()); +} + +void ConnectivityManagerImplWiFi::_ClearWiFiStationProvision(void) +{ + if (_IsWiFiStationProvisioned()) + { + if (CHIP_NO_ERROR != WiFiManager::Instance().ClearStationProvisioningData()) + { + ChipLogError(DeviceLayer, "Cannot clear WiFi station provisioning data"); + } + } +} + +bool ConnectivityManagerImplWiFi::_CanStartWiFiScan() +{ + return (WiFiManager::StationStatus::DISABLED != WiFiManager::Instance().GetStationStatus() && + WiFiManager::StationStatus::SCANNING != WiFiManager::Instance().GetStationStatus() && + WiFiManager::StationStatus::CONNECTING != WiFiManager::Instance().GetStationStatus()); +} + +void ConnectivityManagerImplWiFi::_OnWiFiStationProvisionChange() +{ + // do nothing +} + +void ConnectivityManagerImplWiFi::_OnWiFiScanDone() {} + +CHIP_ERROR ConnectivityManagerImplWiFi::_GetAndLogWiFiStatsCounters(void) +{ + // TODO: when network statistics are enabled + return CHIP_NO_ERROR; +} + +ConnectivityManager::WiFiAPMode ConnectivityManagerImplWiFi::_GetWiFiAPMode(void) +{ + /* AP mode is unsupported */ + return ConnectivityManager::WiFiAPMode::kWiFiAPMode_NotSupported; +} + +CHIP_ERROR ConnectivityManagerImplWiFi::_SetWiFiAPMode(ConnectivityManager::WiFiAPMode mode) +{ + /* AP mode is unsupported */ + VerifyOrReturnError(ConnectivityManager::WiFiAPMode::kWiFiAPMode_NotSupported == mode || + ConnectivityManager::WiFiAPMode::kWiFiAPMode_Disabled == mode, + CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + return CHIP_NO_ERROR; +} + +bool ConnectivityManagerImplWiFi::_IsWiFiAPActive(void) +{ + /* AP mode is unsupported */ + return false; +} + +bool ConnectivityManagerImplWiFi::_IsWiFiAPApplicationControlled(void) +{ + /* AP mode is unsupported */ + return false; +} + +void ConnectivityManagerImplWiFi::_DemandStartWiFiAP(void) +{ /* AP mode is unsupported */ +} + +void ConnectivityManagerImplWiFi::_StopOnDemandWiFiAP(void) +{ /* AP mode is unsupported */ +} + +void ConnectivityManagerImplWiFi::_MaintainOnDemandWiFiAP(void) +{ /* AP mode is unsupported */ +} + +System::Clock::Timeout ConnectivityManagerImplWiFi::_GetWiFiAPIdleTimeout(void) +{ + /* AP mode is unsupported */ + return System::Clock::kZero; +} + +void ConnectivityManagerImplWiFi::_SetWiFiAPIdleTimeout(System::Clock::Timeout val) +{ /* AP mode is unsupported */ +} + +} // namespace DeviceLayer +} // namespace chip + +#endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI diff --git a/src/platform/telink/wifi/3.7.99.0/TelinkWiFiDriver.cpp b/src/platform/telink/wifi/3.7.99.0/TelinkWiFiDriver.cpp new file mode 100644 index 00000000000000..6e704e76fe5f85 --- /dev/null +++ b/src/platform/telink/wifi/3.7.99.0/TelinkWiFiDriver.cpp @@ -0,0 +1,326 @@ +/* + * + * 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 "../TelinkWiFiDriver.h" + +#include + +#include + +#include +#include +#include +#include + +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 TelinkWiFiDriver::WiFiNetworkIterator::Count() +{ + VerifyOrReturnValue(mDriver != nullptr, 0); + return mDriver->mStagingNetwork.IsConfigured() ? 1 : 0; +} + +bool TelinkWiFiDriver::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(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 TelinkWiFiScanResponseIterator::Next(WiFiScanResponse & item) +{ + if (mResultId < mResultCount) + { + item = mResults[mResultId++]; + return true; + } + return false; +} + +void TelinkWiFiScanResponseIterator::Release() +{ + mResultId = mResultCount = 0; + Platform::MemoryFree(mResults); + mResults = nullptr; +} + +void TelinkWiFiScanResponseIterator::Add(const WiFiScanResponse & result) +{ + void * newResults = Platform::MemoryRealloc(mResults, (mResultCount + 1) * sizeof(WiFiScanResponse)); + + if (newResults) + { + mResults = static_cast(newResults); + mResults[mResultCount++] = result; + } +} + +CHIP_ERROR TelinkWiFiDriver::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 TelinkWiFiDriver::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(connStatus)) + : NullOptional); + } + + if (mpConnectCallback) + { + mpConnectCallback->OnResult(status, CharSpan(), 0); + mpConnectCallback = nullptr; + } +} + +void TelinkWiFiDriver::Shutdown() +{ + mpNetworkStatusChangeCallback = nullptr; +} + +CHIP_ERROR TelinkWiFiDriver::CommitConfiguration() +{ + ReturnErrorOnFailure(KeyValueStoreMgr().Put(kPassKey, mStagingNetwork.pass, mStagingNetwork.passLen)); + ReturnErrorOnFailure(KeyValueStoreMgr().Put(kSsidKey, mStagingNetwork.ssid, mStagingNetwork.ssidLen)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR TelinkWiFiDriver::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 TelinkWiFiDriver::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 TelinkWiFiDriver::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 TelinkWiFiDriver::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 TelinkWiFiDriver::ConnectNetwork(ByteSpan networkId, ConnectCallback * callback) +{ + Status status = Status::kSuccess; + WiFiManager::ConnectionHandling handling{ [](const wifi_conn_status & connStatus) { + Instance().OnNetworkConnStatusChanged(connStatus); + }, + System::Clock::Seconds32{ kWiFiConnectNetworkTimeoutSeconds } }; + + VerifyOrExit(mpConnectCallback == nullptr, status = Status::kUnknownError); + mpConnectCallback = callback; + + VerifyOrExit(WiFiManager::StationStatus::CONNECTING != WiFiManager::Instance().GetStationStatus(), + status = Status::kOtherConnectionFailure); + VerifyOrExit(networkId.data_equal(mStagingNetwork.GetSsidSpan()), status = Status::kNetworkIDNotFound); + + WiFiManager::Instance().Connect(mStagingNetwork.GetSsidSpan(), mStagingNetwork.GetPassSpan(), handling); + +exit: + if (status != Status::kSuccess && mpConnectCallback) + { + mpConnectCallback->OnResult(status, CharSpan(), 0); + mpConnectCallback = nullptr; + } +} + +void TelinkWiFiDriver::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 TelinkWiFiDriver::OnScanWiFiNetworkDone(const WiFiManager::ScanDoneStatus & status) +{ + VerifyOrReturn(mScanCallback != nullptr); + mScanCallback->OnFinished(status ? Status::kUnknownError : Status::kSuccess, CharSpan(), &mScanResponseIterator); + mScanCallback = nullptr; +} + +void TelinkWiFiDriver::OnScanWiFiNetworkResult(const WiFiScanResponse & response) +{ + mScanResponseIterator.Add(response); +} + +void TelinkWiFiDriver::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 TelinkWiFiDriver::GetSupportedWiFiBandsMask() const +{ + uint32_t bands = static_cast(1UL << chip::to_underlying(WiFiBandEnum::k2g4)); + return bands; +} + +void TelinkWiFiDriver::StartDefaultWiFiNetwork(void) +{ + chip::ByteSpan ssidSpan = ByteSpan(Uint8::from_const_char(CONFIG_DEFAULT_WIFI_SSID), strlen(CONFIG_DEFAULT_WIFI_SSID)); + chip::ByteSpan passwordSpan = + ByteSpan(Uint8::from_const_char(CONFIG_DEFAULT_WIFI_PASSWORD), strlen(CONFIG_DEFAULT_WIFI_PASSWORD)); + + char debugBuffer[1] = { 0 }; + MutableCharSpan debugText(debugBuffer, 0); + uint8_t outNetworkIndex = 0; + + AddOrUpdateNetwork(ssidSpan, passwordSpan, debugText, outNetworkIndex); + CommitConfiguration(); + RevertConfiguration(); +} + +} // namespace NetworkCommissioning +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/telink/wifi/3.7.99.0/WiFiManager.cpp b/src/platform/telink/wifi/3.7.99.0/WiFiManager.cpp new file mode 100644 index 00000000000000..f1877aafda24f2 --- /dev/null +++ b/src/platform/telink/wifi/3.7.99.0/WiFiManager.cpp @@ -0,0 +1,713 @@ +/* + * + * 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. + */ + +/** + * @file + * Provides the wrapper for Telink WiFi API + */ + +#include "WiFiManager.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace chip { +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; + default: + return app::Clusters::NetworkCommissioning::WiFiBandEnum::kUnknownEnumValue; + } +} + +NetworkCommissioning::WiFiScanResponse ToScanResponse(const wifi_scan_result * result) +{ + NetworkCommissioning::WiFiScanResponse response = {}; + + if (result != nullptr) + { + static_assert(sizeof(response.ssid) == sizeof(result->ssid), "SSID length mismatch"); + static_assert(sizeof(response.bssid) == sizeof(result->mac), "BSSID length mismatch"); + + // 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.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); + } + + return response; +} + +// Matter expectations towards Wi-Fi version codes are unaligned with +// what wpa_supplicant provides. This function maps supplicant codes +// to the ones defined in the Matter spec (11.14.5.2. WiFiVersionEnum) +app::Clusters::WiFiNetworkDiagnostics::WiFiVersionEnum MapToMatterWiFiVersionCode(wifi_link_mode wifiVersion) +{ + using app::Clusters::WiFiNetworkDiagnostics::WiFiVersionEnum; + + if (wifiVersion < WIFI_1 || wifiVersion > WIFI_6E) + { + ChipLogError(DeviceLayer, "Unsupported Wi-Fi version detected"); + return WiFiVersionEnum::kA; // let's return 'a' by default + } + + switch (wifiVersion) + { + case WIFI_1: + return WiFiVersionEnum::kB; + case WIFI_2: + return WiFiVersionEnum::kA; + case WIFI_6E: + return WiFiVersionEnum::kAx; // treat as 802.11ax + default: + break; + } + + return static_cast(wifiVersion - 1); +} + +// Matter expectations towards Wi-Fi security type codes are unaligned with +// what wpa_supplicant provides. This function maps supplicant codes +// to the ones defined in the Matter spec (11.14.3.1. SecurityType enum) +app::Clusters::WiFiNetworkDiagnostics::SecurityTypeEnum MapToMatterSecurityType(wifi_security_type securityType) +{ + using app::Clusters::WiFiNetworkDiagnostics::SecurityTypeEnum; + + switch (securityType) + { + case WIFI_SECURITY_TYPE_NONE: + return SecurityTypeEnum::kNone; + case WIFI_SECURITY_TYPE_PSK: + case WIFI_SECURITY_TYPE_PSK_SHA256: + return SecurityTypeEnum::kWpa2; + case WIFI_SECURITY_TYPE_SAE: + return SecurityTypeEnum::kWpa3; + default: + break; + } + + return SecurityTypeEnum::kUnspecified; +} + +} // namespace + +const Map + WiFiManager::sStatusMap({ { WIFI_STATE_DISCONNECTED, WiFiManager::StationStatus::DISCONNECTED }, + { WIFI_STATE_INTERFACE_DISABLED, WiFiManager::StationStatus::DISABLED }, + { WIFI_STATE_INACTIVE, WiFiManager::StationStatus::DISABLED }, + { WIFI_STATE_SCANNING, WiFiManager::StationStatus::SCANNING }, + { WIFI_STATE_AUTHENTICATING, WiFiManager::StationStatus::CONNECTING }, + { WIFI_STATE_ASSOCIATING, WiFiManager::StationStatus::CONNECTING }, + { WIFI_STATE_ASSOCIATED, WiFiManager::StationStatus::CONNECTED }, + { WIFI_STATE_4WAY_HANDSHAKE, WiFiManager::StationStatus::PROVISIONING }, + { 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 }, +}); + +void WiFiManager::WifiMgmtEventHandler(net_mgmt_event_callback * cb, uint32_t mgmtEvent, net_if * iface) +{ + 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), cb->info_length); + } +} + +void WiFiManager::IPv6MgmtEventHandler(net_mgmt_event_callback * cb, uint32_t mgmtEvent, net_if * iface) +{ + if (((mgmtEvent == NET_EVENT_IPV6_ADDR_ADD) || (mgmtEvent == NET_EVENT_IPV6_ADDR_DEL)) && cb->info) + { + IPv6AddressChangeHandler(cb->info); + } +} + +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"); + + return CHIP_NO_ERROR; +} +CHIP_ERROR WiFiManager::Scan(const ByteSpan & ssid, ScanResultCallback resultCallback, ScanDoneCallback doneCallback, + bool internalScan) +{ + mInternalScan = internalScan; + mScanResultCallback = resultCallback; + mScanDoneCallback = doneCallback; + mCachedWiFiState = mWiFiState; + mWiFiState = WIFI_STATE_SCANNING; + mSsidFound = false; + + wifi_scan_params * scanParams{ nullptr }; + size_t scanParamsSize{ 0 }; + + if (!ssid.empty()) + { + /* 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; +#if (CONFIG_WIFI_MGMT_SCAN_SSID_FILT_MAX > 1) + mScanParams.ssids[1] = nullptr; // indicate the end of ssids list +#endif + scanParams = &mScanParams; + scanParamsSize = sizeof(*scanParams); + } + if (0 != net_mgmt(NET_REQUEST_WIFI_SCAN, mNetIf, scanParams, scanParamsSize)) + { + ChipLogError(DeviceLayer, "Scan request failed"); + return CHIP_ERROR_INTERNAL; + } + + ChipLogDetail(DeviceLayer, "WiFi scanning started..."); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR WiFiManager::ClearStationProvisioningData() +{ + mWiFiParams.mRssi = std::numeric_limits::min(); + memset(&mWiFiParams.mParams, 0, sizeof(mWiFiParams.mParams)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR WiFiManager::Connect(const ByteSpan & ssid, const ByteSpan & credentials, const ConnectionHandling & handling) +{ + ChipLogDetail(DeviceLayer, "Connecting to WiFi network: %*s", ssid.size(), ssid.data()); + + mHandling = handling; + + mWiFiState = WIFI_STATE_ASSOCIATING; + + // Store SSID and credentials and perform the scan to detect the security mode supported by the AP. + // Zephyr WiFi connect request will be issued in the callback when we have the SSID match. + mWantedNetwork.Erase(); + memcpy(mWantedNetwork.ssid, ssid.data(), ssid.size()); + memcpy(mWantedNetwork.pass, credentials.data(), credentials.size()); + mWantedNetwork.ssidLen = ssid.size(); + mWantedNetwork.passLen = credentials.size(); + + return Scan(ssid, nullptr, nullptr, true /* internal scan */); +} + +CHIP_ERROR WiFiManager::Disconnect() +{ + mApplicationDisconnectRequested = true; + int status = net_mgmt(NET_REQUEST_WIFI_DISCONNECT, mNetIf, NULL, 0); + + if (status) + { + mApplicationDisconnectRequested = false; + if (status == -EALREADY) + { + ChipLogDetail(DeviceLayer, "Already disconnected"); + } + else + { + ChipLogDetail(DeviceLayer, "Disconnect request failed"); + return CHIP_ERROR_INTERNAL; + } + } + else + { + ChipLogDetail(DeviceLayer, "Disconnect requested"); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR WiFiManager::GetWiFiInfo(WiFiInfo & info) const +{ + wifi_iface_status status = { 0 }; + + if (net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, mNetIf, &status, sizeof(wifi_iface_status))) + { + ChipLogError(DeviceLayer, "Status request failed"); + return CHIP_ERROR_INTERNAL; + } + + if (status.state >= WIFI_STATE_ASSOCIATED) + { + info.mSecurityType = MapToMatterSecurityType(status.security); + info.mWiFiVersion = MapToMatterWiFiVersionCode(status.link_mode); + info.mRssi = static_cast(status.rssi); + info.mChannel = static_cast(status.channel); + info.mSsidLen = status.ssid_len; + memcpy(info.mSsid, status.ssid, status.ssid_len); + memcpy(info.mBssId, status.bssid, sizeof(status.bssid)); + + return CHIP_NO_ERROR; + } + + return CHIP_ERROR_INTERNAL; +} + +CHIP_ERROR WiFiManager::GetNetworkStatistics(NetworkStatistics & stats) const +{ + net_stats_wifi 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.unicast.rx; + stats.mPacketUnicastTxCount = data.unicast.tx; + stats.mBeaconsSuccessCount = data.sta_mgmt.beacons_rx; + stats.mBeaconsLostCount = data.sta_mgmt.beacons_miss; + + return CHIP_NO_ERROR; +} + +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 wifi_scan_result * scanResult = reinterpret_cast(data.get()); + + if (Instance().mInternalScan && + Instance().mWantedNetwork.GetSsidSpan().data_equal(ByteSpan(scanResult->ssid, scanResult->ssid_length))) + { + // Prepare the connection parameters + // In case there are many networks with the same SSID choose the one with the best RSSI + if (scanResult->rssi > Instance().mWiFiParams.mRssi) + { + Instance().ClearStationProvisioningData(); + Instance().mWiFiParams.mParams.ssid_length = static_cast(Instance().mWantedNetwork.ssidLen); + Instance().mWiFiParams.mParams.ssid = Instance().mWantedNetwork.ssid; + // Fallback to the WIFI_SECURITY_TYPE_PSK if the security is unknown + Instance().mWiFiParams.mParams.security = + scanResult->security <= WIFI_SECURITY_TYPE_MAX ? scanResult->security : WIFI_SECURITY_TYPE_PSK; + Instance().mWiFiParams.mParams.psk_length = static_cast(Instance().mWantedNetwork.passLen); + Instance().mWiFiParams.mParams.mfp = (scanResult->mfp == WIFI_MFP_REQUIRED) ? WIFI_MFP_REQUIRED : WIFI_MFP_OPTIONAL; + + // If the security is none, WiFi driver expects the psk to be nullptr + if (Instance().mWiFiParams.mParams.security == WIFI_SECURITY_TYPE_NONE) + { + Instance().mWiFiParams.mParams.psk = nullptr; + } + else + { + Instance().mWiFiParams.mParams.psk = Instance().mWantedNetwork.pass; + } + + Instance().mWiFiParams.mParams.timeout = Instance().mHandling.mConnectionTimeout.count(); + Instance().mWiFiParams.mParams.channel = WIFI_CHANNEL_ANY; + Instance().mWiFiParams.mRssi = scanResult->rssi; + Instance().mWiFiParams.mParams.band = WIFI_FREQ_BAND_UNKNOWN; + Instance().mSsidFound = true; + } + } + + if (Instance().mScanResultCallback && !Instance().mInternalScan) + { + Instance().mScanResultCallback(ToScanResponse(scanResult)); + } +} + +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); + ScanDoneStatus scanDoneStatus = status->status; + + if (scanDoneStatus) + { + ChipLogError(DeviceLayer, "Wi-Fi scan finalization failure (%d)", scanDoneStatus); + } + else + { + ChipLogProgress(DeviceLayer, "Wi-Fi scan done"); + } + + if (Instance().mScanDoneCallback && !Instance().mInternalScan) + { + Instance().mScanDoneCallback(scanDoneStatus); + // restore the connection state from before the scan request was issued + Instance().mWiFiState = Instance().mCachedWiFiState; + return; + } + + // Internal scan is supposed to be followed by a connection request if the SSID has been found + if (Instance().mInternalScan) + { + + if (!Instance().mSsidFound) + { + auto currentTimeout = Instance().CalculateNextRecoveryTime(); + ChipLogProgress(DeviceLayer, "Starting connection recover: re-scanning... (next attempt in %d ms)", + currentTimeout.count()); + DeviceLayer::SystemLayer().StartTimer(currentTimeout, Recover, nullptr); + return; + } + + Instance().mWiFiState = WIFI_STATE_ASSOCIATING; + + 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.mOnConnectionDone) + { + Instance().mHandling.mOnConnectionDone(WIFI_STATUS_CONN_FAIL); + } + Instance().mWiFiState = WIFI_STATE_DISCONNECTED; + return; + } + ChipLogProgress(DeviceLayer, "Connection to %*s requested [RSSI=%d]", Instance().mWiFiParams.mParams.ssid_length, + Instance().mWiFiParams.mParams.ssid, Instance().mWiFiParams.mRssi); + Instance().mInternalScan = false; + } + }); + + if (CHIP_NO_ERROR == err) + { + // the ownership has been transferred to the worker thread - release the buffer + data.release(); + } +} + +void WiFiManager::SendRouterSolicitation(System::Layer * layer, void * param) +{ + net_if_start_rs(Instance().mNetIf); + Instance().mRouterSolicitationCounter++; + if (Instance().mRouterSolicitationCounter < kRouterSolicitationMaxCount) + { + DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(kRouterSolicitationIntervalMs), SendRouterSolicitation, + nullptr); + } + else + { + Instance().mRouterSolicitationCounter = 0; + } +} + +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); + wifi_conn_status connStatus = status->conn_status; + + if (connStatus) + { + ChipLogProgress(DeviceLayer, "Connection to WiFi network failed or was terminated by another request"); + Instance().mWiFiState = WIFI_STATE_DISCONNECTED; + if (Instance().mHandling.mOnConnectionDone) + { + Instance().mHandling.mOnConnectionDone(connStatus); + } + + WiFiDiagnosticsDelegate * delegate = GetDiagnosticDataProvider().GetWiFiDiagnosticsDelegate(); + if (delegate) + { + 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. + { + // Workaround needed until sending Router Solicitation after connect will be done by the driver. + DeviceLayer::SystemLayer().StartTimer( + System::Clock::Milliseconds32(chip::Crypto::GetRandU16() % kMaxInitialRouterSolicitationDelayMs), + SendRouterSolicitation, nullptr); + + ChipLogProgress(DeviceLayer, "Connected to WiFi network"); + Instance().mWiFiState = WIFI_STATE_COMPLETED; + if (Instance().mHandling.mOnConnectionDone) + { + Instance().mHandling.mOnConnectionDone(connStatus); + } + Instance().PostConnectivityStatusChange(kConnectivity_Established); + + // Workaround needed to re-initialize mDNS server after Wi-Fi interface is operative + chip::DeviceLayer::ChipDeviceEvent event; + event.Type = chip::DeviceLayer::DeviceEventType::kDnssdInitialized; + + CHIP_ERROR error = chip::DeviceLayer::PlatformMgr().PostEvent(&event); + if (error != CHIP_NO_ERROR) + { + 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(); + }); + + if (CHIP_NO_ERROR == err) + { + // the ownership has been transferred to the worker thread - release the buffer + data.release(); + } +} + +void WiFiManager::DisconnectHandler(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); + uint16_t reason; + + switch (status->disconn_reason) + { + case WIFI_REASON_DISCONN_UNSPECIFIED: + reason = static_cast(WlanReason::UNSPECIFIED); + break; + case WIFI_REASON_DISCONN_USER_REQUEST: + reason = static_cast(WlanReason::DEAUTH_LEAVING); + break; + case WIFI_REASON_DISCONN_AP_LEAVING: + reason = static_cast(WlanReason::DEAUTH_LEAVING); + break; + case WIFI_REASON_DISCONN_INACTIVITY: + reason = static_cast(WlanReason::DISASSOC_DUE_TO_INACTIVITY); + break; + default: + reason = static_cast(WlanReason::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); + char buf[INET6_ADDRSTRLEN]; + + ChipLogProgress(DeviceLayer, "IP6 address %s", inet_ntop(AF_INET6, addr, buf, INET6_ADDRSTRLEN)); + + // 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()); + } + else + { + ChipLogProgress(DeviceLayer, "kDnssdRestartNeeded"); + } + } +} + +WiFiManager::StationStatus WiFiManager::GetStationStatus() const +{ + return WiFiManager::sStatusMap[mWiFiState]; +} + +void WiFiManager::PostConnectivityStatusChange(ConnectivityChange changeType) +{ + ChipDeviceEvent networkEvent{}; + networkEvent.Type = DeviceEventType::kWiFiConnectivityChange; + networkEvent.WiFiConnectivityChange.Result = changeType; + PlatformMgr().PostEventOrDie(&networkEvent); +} + +void WiFiManager::Recover(System::Layer *, void *) +{ + // Prevent scheduling recovery if we are already connected to the network. + if (Instance().mWiFiState == WIFI_STATE_COMPLETED) + { + Instance().AbortConnectionRecovery(); + return; + } + + // If kConnectionRecoveryMaxOverallInterval has a non-zero value prevent endless re-scan. + if (0 != kConnectionRecoveryMaxRetries && (++Instance().mConnectionRecoveryCounter >= kConnectionRecoveryMaxRetries)) + { + Instance().AbortConnectionRecovery(); + return; + } + + Instance().Scan(Instance().mWantedNetwork.GetSsidSpan(), nullptr, nullptr, true /* internal scan */); +} + +void WiFiManager::ResetRecoveryTime() +{ + mConnectionRecoveryTimeMs = kConnectionRecoveryMinIntervalMs; + mConnectionRecoveryCounter = 0; +} + +void WiFiManager::AbortConnectionRecovery() +{ + DeviceLayer::SystemLayer().CancelTimer(Recover, nullptr); + Instance().ResetRecoveryTime(); +} + +System::Clock::Milliseconds32 WiFiManager::CalculateNextRecoveryTime() +{ + if (mConnectionRecoveryTimeMs > kConnectionRecoveryMaxIntervalMs) + { + // Find the new random jitter value in range [-jitter, +jitter]. + int32_t jitter = chip::Crypto::GetRandU32() % (2 * jitter + 1) - jitter; + mConnectionRecoveryTimeMs = kConnectionRecoveryMaxIntervalMs + jitter; + return System::Clock::Milliseconds32(mConnectionRecoveryTimeMs); + } + else + { + uint32_t currentRecoveryTimeout = mConnectionRecoveryTimeMs; + mConnectionRecoveryTimeMs = mConnectionRecoveryTimeMs * 2; + return System::Clock::Milliseconds32(currentRecoveryTimeout); + } +} + +CHIP_ERROR WiFiManager::SetLowPowerMode(bool onoff) +{ + VerifyOrReturnError(nullptr != mNetIf, CHIP_ERROR_INTERNAL); + + wifi_ps_config 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; + } + + if ((currentConfig.ps_params.enabled == WIFI_PS_ENABLED && onoff == false) || + (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, mNetIf, ¶ms, sizeof(params))) + { + ChipLogError(DeviceLayer, "Set low power mode request failed"); + return CHIP_ERROR_INTERNAL; + } + ChipLogProgress(DeviceLayer, "Successfully set low power mode [%d]", onoff); + return CHIP_NO_ERROR; + } + + ChipLogDetail(DeviceLayer, "Low power mode is already in requested state [%d]", 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/telink/wifi/3.7.99.0/WiFiManager.h b/src/platform/telink/wifi/3.7.99.0/WiFiManager.h new file mode 100644 index 00000000000000..6674b5c6165f9f --- /dev/null +++ b/src/platform/telink/wifi/3.7.99.0/WiFiManager.h @@ -0,0 +1,261 @@ +/* + * + * 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. + */ + +/** + * @file + * Provides the wrapper for Telink wpa_supplicant API + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +struct net_if; +struct wpa_ssid; +using WpaNetwork = struct wpa_ssid; + +namespace chip { +namespace DeviceLayer { + +// emulation of dictionary - might be moved to utils +template +class Map +{ + struct Pair + { + T1 key; + T2 value; + }; + +public: + Map(const Pair (&list)[N]) + { + int idx{ 0 }; + for (const auto & pair : list) + { + mMap[idx++] = pair; + } + } + + T2 operator[](const T1 & key) const + { + for (const auto & it : mMap) + { + if (key == it.key) + return it.value; + } + + return T2{}; + } + + Map() = delete; + Map(const Map &) = delete; + Map(Map &&) = delete; + Map & operator=(const Map &) = delete; + Map & operator=(Map &&) = delete; + ~Map() = default; + +private: + Pair mMap[N]; +}; + +class WiFiManager +{ +public: + /* 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 (*)(const ScanDoneStatus &); + using ConnectionCallback = void (*)(const wifi_conn_status &); + + enum class StationStatus : uint8_t + { + NONE, + DISCONNECTED, + DISABLED, + SCANNING, + CONNECTING, + CONNECTED, + PROVISIONING, + FULLY_PROVISIONED, + UNKNOWN + }; + + enum class WlanReason : uint8_t + { + UNSPECIFIED = 1, + PREV_AUTH_NOT_VALID, + DEAUTH_LEAVING, + DISASSOC_DUE_TO_INACTIVITY, + DISASSOC_AP_BUSY + }; + + static WiFiManager & Instance() + { + static WiFiManager sInstance; + return sInstance; + } + + struct ConnectionHandling + { + ConnectionCallback mOnConnectionDone{}; + System::Clock::Seconds32 mConnectionTimeout{}; + }; + + struct WiFiInfo + { + uint8_t mBssId[DeviceLayer::Internal::kWiFiBSSIDLength]; + app::Clusters::WiFiNetworkDiagnostics::SecurityTypeEnum mSecurityType{}; + app::Clusters::WiFiNetworkDiagnostics::WiFiVersionEnum mWiFiVersion{}; + uint16_t mChannel{}; + int8_t mRssi{}; + uint8_t mSsid[DeviceLayer::Internal::kMaxWiFiSSIDLength]; + size_t mSsidLen{ 0 }; + }; + + struct NetworkStatistics + { + uint32_t mPacketMulticastRxCount{}; + uint32_t mPacketMulticastTxCount{}; + uint32_t mPacketUnicastRxCount{}; + uint32_t mPacketUnicastTxCount{}; + uint32_t mBeaconsSuccessCount{}; + uint32_t mBeaconsLostCount{}; + }; + + struct WiFiNetwork + { + uint8_t ssid[DeviceLayer::Internal::kMaxWiFiSSIDLength]; + size_t ssidLen = 0; + uint8_t pass[DeviceLayer::Internal::kMaxWiFiKeyLength]; + size_t passLen = 0; + + bool IsConfigured() const { return ssidLen > 0; } + ByteSpan GetSsidSpan() const { return ByteSpan(ssid, ssidLen); } + ByteSpan GetPassSpan() const { return ByteSpan(pass, passLen); } + void Clear() { ssidLen = 0; } + void Erase() + { + memset(ssid, 0, DeviceLayer::Internal::kMaxWiFiSSIDLength); + memset(pass, 0, DeviceLayer::Internal::kMaxWiFiKeyLength); + ssidLen = 0; + passLen = 0; + } + }; + + static constexpr uint16_t kRouterSolicitationIntervalMs = 4000; + static constexpr uint16_t kMaxInitialRouterSolicitationDelayMs = 1000; + static constexpr uint8_t kRouterSolicitationMaxCount = 3; + static constexpr uint32_t kConnectionRecoveryMinIntervalMs = CONFIG_CHIP_WIFI_CONNECTION_RECOVERY_MINIMUM_INTERVAL; + static constexpr uint32_t kConnectionRecoveryMaxIntervalMs = CONFIG_CHIP_WIFI_CONNECTION_RECOVERY_MAXIMUM_INTERVAL; + static constexpr uint32_t kConnectionRecoveryJitterMs = CONFIG_CHIP_WIFI_CONNECTION_RECOVERY_JITTER; + static constexpr uint32_t kConnectionRecoveryMaxRetries = CONFIG_CHIP_WIFI_CONNECTION_RECOVERY_MAX_RETRIES_NUMBER; + + CHIP_ERROR Init(); + CHIP_ERROR Scan(const ByteSpan & ssid, ScanResultCallback resultCallback, ScanDoneCallback doneCallback, + bool internalScan = false); + CHIP_ERROR Connect(const ByteSpan & ssid, const ByteSpan & credentials, const ConnectionHandling & handling); + StationStatus GetStationStatus() const; + 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, size_t); + + WiFiManager() = default; + ~WiFiManager() = default; + + struct ConnectionParams + { + wifi_connect_req_params mParams; + int8_t mRssi{ std::numeric_limits::min() }; + }; + + 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 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 + // a reboot or when a connection is lost. The following attempts will occur with increasing interval. + // The connection recovery interval starts from kConnectionRecoveryMinIntervalMs and is doubled + // with each occurrence until reaching kConnectionRecoveryMaxIntervalMs. + // When the connection recovery interval reaches the maximum value the randomized kConnectionRecoveryJitterMs + // from the range [-jitter, +jitter] is added to the value to avoid the periodicity. + // To avoid frequent recovery attempts when the signal to an access point is poor quality + // The connection recovery interval will be cleared after the defined delay in kConnectionRecoveryDelayToReset. + static void Recover(System::Layer * layer, void * param); + void ResetRecoveryTime(); + System::Clock::Milliseconds32 CalculateNextRecoveryTime(); + + net_if * mNetIf{ nullptr }; + ConnectionParams mWiFiParams{}; + 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{}; + bool mInternalScan{ false }; + uint8_t mRouterSolicitationCounter = 0; + bool mSsidFound{ false }; + uint32_t mConnectionRecoveryCounter{ 0 }; + uint32_t mConnectionRecoveryTimeMs{ kConnectionRecoveryMinIntervalMs }; + bool mApplicationDisconnectRequested{ false }; + uint16_t mLastDisconnectedReason = static_cast(WlanReason::UNSPECIFIED); + + static const Map sStatusMap; + static const Map sEventHandlerMap; +}; + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/telink/wifi/TelinkWiFiDriver.h b/src/platform/telink/wifi/TelinkWiFiDriver.h index 9c5b1e61d1f0e1..de513ebd71b60b 100644 --- a/src/platform/telink/wifi/TelinkWiFiDriver.h +++ b/src/platform/telink/wifi/TelinkWiFiDriver.h @@ -94,7 +94,11 @@ class TelinkWiFiDriver final : public WiFiDriver return sInstance; } +#if defined(CONFIG_ZEPHYR_VERSION_3_3) void OnNetworkStatusChanged(Status status); +#else + void OnNetworkConnStatusChanged(const wifi_conn_status & connStatus); +#endif void OnScanWiFiNetworkResult(const WiFiScanResponse & result); void OnScanWiFiNetworkDone(const WiFiManager::ScanDoneStatus & status);