From 1018755aa35fb167c2c2354177eae15e5e4bf8b2 Mon Sep 17 00:00:00 2001 From: Alex Tsitsiura Date: Wed, 15 May 2024 20:13:30 +0300 Subject: [PATCH] [Telink] Add W91 (TLSR9118BDK40D) base support (#33423) * [Telink] Add W91 (TLSR9118BDK40D) * [Telink] Fix w91 OTA * [Telink] w91 add pwm pool (#336) * add pwm pool to w91 platform * add pwm pool for w91 platform Signed-off-by: Dmytro Kashkarov --------- Signed-off-by: Dmytro Kashkarov * [Telink] disable debug part * [Telink] restyled --------- Signed-off-by: Dmytro Kashkarov Co-authored-by: Dmytro Kashkarov <114229220+UR6LAL@users.noreply.github.com> --- .github/workflows/chef.yaml | 2 +- .github/workflows/examples-telink.yaml | 32 +- config/telink/app/bootloader.conf | 7 +- config/telink/app/bootloader_usb.conf | 20 + config/telink/chip-module/CMakeLists.txt | 8 +- config/telink/chip-module/Kconfig | 2 +- config/telink/chip-module/Kconfig.defaults | 56 +- .../air-quality-sensor-app/telink/README.md | 2 +- examples/all-clusters-app/telink/README.md | 2 +- .../all-clusters-minimal-app/telink/README.md | 2 +- examples/bridge-app/telink/README.md | 2 +- examples/contact-sensor-app/telink/README.md | 2 +- examples/light-switch-app/telink/README.md | 2 +- examples/lighting-app/telink/README.md | 2 +- examples/lock-app/telink/README.md | 2 +- examples/ota-requestor-app/telink/README.md | 2 +- examples/platform/telink/common.cmake | 29 +- .../telink/common/src/AppTaskCommon.cpp | 16 +- .../platform/telink/common/src/mainCommon.cpp | 11 + examples/pump-app/telink/README.md | 2 +- examples/pump-controller-app/telink/README.md | 2 +- examples/shell/telink/README.md | 2 +- examples/smoke-co-alarm-app/telink/README.md | 2 +- .../telink/README.md | 2 +- examples/thermostat/telink/README.md | 2 +- examples/window-app/telink/README.md | 2 +- scripts/build/build/targets.py | 3 +- scripts/build/builders/telink.py | 7 +- .../build/testdata/all_targets_linux_x64.txt | 2 +- src/platform/Zephyr/InetUtils.cpp | 10 + src/platform/Zephyr/ThreadStackManagerImpl.h | 2 - src/platform/telink/BUILD.gn | 20 +- .../telink/CHIPDevicePlatformConfig.h | 8 +- .../telink/ConnectivityManagerImpl.cpp | 84 ++- src/platform/telink/ConnectivityManagerImpl.h | 10 +- .../telink/ThreadStackManagerImpl.cpp | 19 +- src/platform/telink/ThreadStackManagerImpl.h | 5 +- src/platform/telink/telink-mbedtls-config.h | 2 + src/platform/telink/tlsr9118bdk40d.overlay | 48 ++ .../telink/tlsr9118bdk40d_3m_flash.overlay | 35 ++ .../wifi/ConnectivityManagerImplWiFi.cpp | 178 ++++++ .../telink/wifi/ConnectivityManagerImplWiFi.h | 80 +++ src/platform/telink/wifi/TelinkWiFiDriver.cpp | 281 +++++++++ src/platform/telink/wifi/TelinkWiFiDriver.h | 112 ++++ src/platform/telink/wifi/WiFiManager.cpp | 560 ++++++++++++++++++ src/platform/telink/wifi/WiFiManager.h | 238 ++++++++ 46 files changed, 1831 insertions(+), 88 deletions(-) create mode 100644 config/telink/app/bootloader_usb.conf create mode 100644 src/platform/telink/tlsr9118bdk40d.overlay create mode 100644 src/platform/telink/tlsr9118bdk40d_3m_flash.overlay create mode 100644 src/platform/telink/wifi/ConnectivityManagerImplWiFi.cpp create mode 100644 src/platform/telink/wifi/ConnectivityManagerImplWiFi.h create mode 100644 src/platform/telink/wifi/TelinkWiFiDriver.cpp create mode 100644 src/platform/telink/wifi/TelinkWiFiDriver.h create mode 100644 src/platform/telink/wifi/WiFiManager.cpp create mode 100644 src/platform/telink/wifi/WiFiManager.h diff --git a/.github/workflows/chef.yaml b/.github/workflows/chef.yaml index 11f76711794ab3..4cbc381d931f69 100644 --- a/.github/workflows/chef.yaml +++ b/.github/workflows/chef.yaml @@ -110,7 +110,7 @@ jobs: 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 65dc1812431bf946dfc110682298acf83d63e27a" + # run: scripts/run_in_build_env.sh "python3 scripts/tools/telink/update_zephyr.py 68deadeb5c20b82d68700e720d4580e8003bf1d8" - name: CI Examples Telink shell: bash run: | diff --git a/.github/workflows/examples-telink.yaml b/.github/workflows/examples-telink.yaml index 63830b3d38e7ea..9d83015ecf3d0e 100644 --- a/.github/workflows/examples-telink.yaml +++ b/.github/workflows/examples-telink.yaml @@ -1,4 +1,4 @@ -# Copyright (c) 2022-2023 Project CHIP Authors +# 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. @@ -57,7 +57,7 @@ jobs: 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 65dc1812431bf946dfc110682298acf83d63e27a" + # run: scripts/run_in_build_env.sh "python3 scripts/tools/telink/update_zephyr.py 68deadeb5c20b82d68700e720d4580e8003bf1d8" - name: Build example Telink (B92 retention) Air Quality Sensor App run: | @@ -71,13 +71,13 @@ jobs: - name: clean out build output run: rm -rf ./out - - name: Build example Telink (B91) All Clusters App + - name: Build example Telink (W91) All Clusters App run: | ./scripts/run_in_build_env.sh \ - "./scripts/build/build_examples.py --target 'telink-tlsr9518adk80d-all-clusters' build" + "./scripts/build/build_examples.py --target 'telink-tlsr9118bdk40d-all-clusters' build" .environment/pigweed-venv/bin/python3 scripts/tools/memory/gh_sizes.py \ - telink tlsr9518adk80d all-clusters-app \ - out/telink-tlsr9518adk80d-all-clusters/zephyr/zephyr.elf \ + telink tlsr9118bdk40d all-clusters-app \ + out/telink-tlsr9118bdk40d-all-clusters/zephyr/zephyr.elf \ /tmp/bloat_reports/ - name: clean out build output @@ -129,6 +129,18 @@ jobs: - name: clean out build output (keep tools) run: rm -rf ./out/telink* + - name: Build example Telink (W91) Lighting App with OTA, Factory Data + run: | + ./scripts/run_in_build_env.sh \ + "./scripts/build/build_examples.py --target 'telink-tlsr9118bdk40d-light-ota-factory-data' build" + .environment/pigweed-venv/bin/python3 scripts/tools/memory/gh_sizes.py \ + telink tlsr9118bdk40d lighting-app-ota-factory-data \ + out/telink-tlsr9118bdk40d-light-ota-factory-data/zephyr/zephyr.elf \ + /tmp/bloat_reports/ + + - name: clean out build output (keep tools) + run: rm -rf ./out/telink* + - name: Build example Telink (B91) Lighting App with OTA, RPC, Factory Data and 4Mb flash run: | ./scripts/run_in_build_env.sh \ @@ -189,13 +201,13 @@ jobs: - name: clean out build output run: rm -rf ./out - - name: Build example Telink (B91) Pump Controller App + - name: Build example Telink (W91) Pump Controller App run: | ./scripts/run_in_build_env.sh \ - "./scripts/build/build_examples.py --target 'telink-tlsr9518adk80d-pump-controller' build" + "./scripts/build/build_examples.py --target 'telink-tlsr9118bdk40d-pump-controller' build" .environment/pigweed-venv/bin/python3 scripts/tools/memory/gh_sizes.py \ - telink tlsr9518adk80d pump-controller-app \ - out/telink-tlsr9518adk80d-pump-controller/zephyr/zephyr.elf \ + telink tlsr9118bdk40d pump-controller-app \ + out/telink-tlsr9118bdk40d-pump-controller/zephyr/zephyr.elf \ /tmp/bloat_reports/ - name: clean out build output diff --git a/config/telink/app/bootloader.conf b/config/telink/app/bootloader.conf index 2098bc7072cb3d..52937e1649e9af 100644 --- a/config/telink/app/bootloader.conf +++ b/config/telink/app/bootloader.conf @@ -1,5 +1,5 @@ # -# Copyright (c) 2023 Project CHIP Authors +# Copyright (c) 2023-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. @@ -41,8 +41,3 @@ CONFIG_BOOT_MAX_IMG_SECTORS=4096 # - 3 INFO, default to write LOG_LEVEL_INFO # - 4 DEBUG, default to write LOG_LEVEL_DBG CONFIG_LOG_DEFAULT_LEVEL=1 - -# USB DFU configuration -CONFIG_USB_DFU_WILL_DETACH=n -CONFIG_BOOT_USB_DFU_GPIO=y -CONFIG_MCUBOOT_INDICATION_LED=y diff --git a/config/telink/app/bootloader_usb.conf b/config/telink/app/bootloader_usb.conf new file mode 100644 index 00000000000000..b6b0098bfc31b1 --- /dev/null +++ b/config/telink/app/bootloader_usb.conf @@ -0,0 +1,20 @@ +# +# 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. +# + +# USB DFU configuration +CONFIG_USB_DFU_WILL_DETACH=n +CONFIG_BOOT_USB_DFU_GPIO=y +CONFIG_MCUBOOT_INDICATION_LED=y diff --git a/config/telink/chip-module/CMakeLists.txt b/config/telink/chip-module/CMakeLists.txt index 27f429b390d528..0f2e4edce0f8d1 100644 --- a/config/telink/chip-module/CMakeLists.txt +++ b/config/telink/chip-module/CMakeLists.txt @@ -46,6 +46,7 @@ include(${COMMON_CMAKE_SOURCE_DIR}/chip_gn_args.cmake) include(${COMMON_CMAKE_SOURCE_DIR}/chip_gn.cmake) # Prepare compiler flags +matter_add_flags(-isystem${ZEPHYR_BASE}/../modules/crypto/mbedtls/include/) if (CONFIG_POSIX_API) matter_add_flags(-D_DEFAULT_SOURCE) @@ -102,6 +103,7 @@ matter_add_gn_arg_bool ("chip_error_logging" CONFIG_MATTER_ matter_add_gn_arg_bool ("chip_progress_logging" CONFIG_MATTER_LOG_LEVEL GREATER_EQUAL 3) matter_add_gn_arg_bool ("chip_detail_logging" CONFIG_MATTER_LOG_LEVEL GREATER_EQUAL 4) matter_add_gn_arg_bool ("chip_automation_logging" FALSE) +matter_add_gn_arg_bool ("chip_enable_wifi" CONFIG_WIFI_W91) matter_add_gn_arg_bool ("chip_enable_icd_server" CONFIG_CHIP_ENABLE_ICD_SUPPORT) if (CONFIG_CHIP_ENABLE_ICD_SUPPORT) @@ -122,8 +124,12 @@ if (CONFIG_CHIP_ROTATING_DEVICE_ID) matter_add_gn_arg_bool("chip_enable_additional_data_advertising" "true") endif() -if (CONFIG_CHIP_ENABLE_DNSSD_SRP) +if (CONFIG_NET_L2_OPENTHREAD) matter_add_gn_arg_string("chip_mdns" "platform") +elseif(CONFIG_WIFI_W91) + matter_add_gn_arg_string("chip_mdns" "minimal") +else() + matter_add_gn_arg_string("chip_mdns" "none") endif() if (CONFIG_CHIP_PW_RPC) diff --git a/config/telink/chip-module/Kconfig b/config/telink/chip-module/Kconfig index b76e3962d2efbe..4444cc8e9e44ed 100644 --- a/config/telink/chip-module/Kconfig +++ b/config/telink/chip-module/Kconfig @@ -180,7 +180,7 @@ config CHIP_LOG_SIZE_OPTIMIZATION config CHIP_BUTTON_MANAGER_IRQ_MODE bool "Use GPIO in an IRQ mode instead of polling the GPIO" - default PM + default PM || BOARD_TLSR9118BDK40D help Use GPIO in an IRQ mode to avoid button polling loop and extend the battery lifetime by waking up by GPIO event. GPIO events are working only with GPIO IRQ. This option changes button matrix configuration. diff --git a/config/telink/chip-module/Kconfig.defaults b/config/telink/chip-module/Kconfig.defaults index 57236f57708418..e6554774187a18 100644 --- a/config/telink/chip-module/Kconfig.defaults +++ b/config/telink/chip-module/Kconfig.defaults @@ -108,6 +108,7 @@ config GPIO # Bluetooth Low Energy configs config BT + default n if BOARD_TLSR9118BDK40D default y if BT @@ -178,7 +179,7 @@ config PWM endif # Board non-retention config -if BOARD_TLSR9528A || BOARD_TLSR9258A || BOARD_TLSR9518ADK80D +if BOARD_TLSR9118BDK40D || BOARD_TLSR9528A || BOARD_TLSR9258A || BOARD_TLSR9518ADK80D config PWM default y endif @@ -224,7 +225,7 @@ config SETTINGS_NVS_SECTOR_COUNT # Enable OpenThread config NET_L2_OPENTHREAD - default y + default y if !WIFI if NET_L2_OPENTHREAD @@ -292,9 +293,58 @@ config NET_MAX_CONTEXTS config NET_CONFIG_INIT_TIMEOUT default 0 + +config CHIP_WIFI + bool "Enable Telink Wi-Fi support" + default y if BOARD_TLSR9118BDK40D + select WIFI_W91 + select WIFI + select NET_STATISTICS + select NET_L2_ETHERNET + select NET_IPV6_ND # enable Neighbor Discovery to handle Router Advertisements + select NET_IPV6_NBR_CACHE + select NET_STATISTICS_USER_API + +if CHIP_WIFI + +config CHIP_WIFI_CONNECTION_RECOVERY_MINIMUM_INTERVAL + int "Define the minimum connection recovery time interval in milliseconds" + depends on CHIP_WIFI + default 500 + help + Specifies the minimum connection recovery interval (in milliseconds). + +config CHIP_WIFI_CONNECTION_RECOVERY_MAXIMUM_INTERVAL + int "Define the maximum connection recovery time interval in milliseconds" + depends on CHIP_WIFI + default 3600000 # 1 hour + help + Specifies the maximum connection recovery interval (in milliseconds). + +config CHIP_WIFI_CONNECTION_RECOVERY_MAX_RETRIES_NUMBER + int "Define the maximum amount of connection recovery occurrences" + depends on CHIP_WIFI + default 0 + help + Specifies the maximum number of connection recovery attempts. + If set to 0, no limitation is applied and attempts + to recover the connection are performed indefinitely. + +config CHIP_WIFI_CONNECTION_RECOVERY_JITTER + int "Define the connection recovery jitter in milliseconds" + depends on CHIP_WIFI + default 2000 + help + Specifies the maximum connection recovery jitter interval (in milliseconds). + Once the wait time reaches the current maximum value (defined by CHIP_WIFI_CONNECTION_RECOVERY_MAXIMUM_INTERVAL), + a random jitter interval is added to it to avoid periodicity. The random jitter is selected + within range [-JITTER; +JITTER]. + +endif # CHIP_WIFI + config CHIP_ENABLE_PAIRING_AUTOSTART bool "Open commissioning window on boot" - default y + default y help Opens the commissioning window automatically at application boot time if the node is not yet commissioned. diff --git a/examples/air-quality-sensor-app/telink/README.md b/examples/air-quality-sensor-app/telink/README.md index 015e613d40896f..64b394c046b51a 100644 --- a/examples/air-quality-sensor-app/telink/README.md +++ b/examples/air-quality-sensor-app/telink/README.md @@ -25,7 +25,7 @@ You can use this example as a reference for creating your own application. ``` 3. In the example dir run (replace __ with your board name, for - example, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): + example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): ```bash $ west build -b diff --git a/examples/all-clusters-app/telink/README.md b/examples/all-clusters-app/telink/README.md index a5532487fa67a2..89d8221af75d3f 100644 --- a/examples/all-clusters-app/telink/README.md +++ b/examples/all-clusters-app/telink/README.md @@ -27,7 +27,7 @@ creating your own application. ``` 3. In the example dir run (replace __ with your board name, for - example, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): + example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): ```bash $ west build -b diff --git a/examples/all-clusters-minimal-app/telink/README.md b/examples/all-clusters-minimal-app/telink/README.md index 8e18bf4b9419b0..396afb1cc63091 100644 --- a/examples/all-clusters-minimal-app/telink/README.md +++ b/examples/all-clusters-minimal-app/telink/README.md @@ -27,7 +27,7 @@ for creating your own application. ``` 3. In the example dir run (replace __ with your board name, for - example, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): + example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): ```bash $ west build -b diff --git a/examples/bridge-app/telink/README.md b/examples/bridge-app/telink/README.md index f23d2aeb4c6f85..0855689cd1201f 100644 --- a/examples/bridge-app/telink/README.md +++ b/examples/bridge-app/telink/README.md @@ -104,7 +104,7 @@ defined: ``` 3. In the example dir run (replace __ with your board name, for - example, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): + example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): ```bash $ west build -b diff --git a/examples/contact-sensor-app/telink/README.md b/examples/contact-sensor-app/telink/README.md index 9b6a59ba0c086f..964b05a76ba109 100755 --- a/examples/contact-sensor-app/telink/README.md +++ b/examples/contact-sensor-app/telink/README.md @@ -25,7 +25,7 @@ You can use this example as a reference for creating your own application. ``` 3. In the example dir run (replace __ with your board name, for - example, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): + example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): ```bash $ west build -b diff --git a/examples/light-switch-app/telink/README.md b/examples/light-switch-app/telink/README.md index d6ba4b34cd6351..d222116f5f1a7f 100755 --- a/examples/light-switch-app/telink/README.md +++ b/examples/light-switch-app/telink/README.md @@ -30,7 +30,7 @@ creating your own application. ``` 3. In the example dir run (replace __ with your board name, for - example, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): + example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): ```bash $ west build -b diff --git a/examples/lighting-app/telink/README.md b/examples/lighting-app/telink/README.md index f4dfefe2b9795e..6c2269007e8ac3 100644 --- a/examples/lighting-app/telink/README.md +++ b/examples/lighting-app/telink/README.md @@ -28,7 +28,7 @@ a reference for creating your own application. ``` 3. In the example dir run (replace __ with your board name, for - example, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): + example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): ```bash $ west build -b diff --git a/examples/lock-app/telink/README.md b/examples/lock-app/telink/README.md index 27a95baffa0b6a..8386a524c48d41 100755 --- a/examples/lock-app/telink/README.md +++ b/examples/lock-app/telink/README.md @@ -28,7 +28,7 @@ a reference for creating your own application. ``` 3. In the example dir run (replace __ with your board name, for - example, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): + example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): ```bash $ west build -b diff --git a/examples/ota-requestor-app/telink/README.md b/examples/ota-requestor-app/telink/README.md index 81870b738f18df..c549804e42f627 100755 --- a/examples/ota-requestor-app/telink/README.md +++ b/examples/ota-requestor-app/telink/README.md @@ -21,7 +21,7 @@ ``` 3. In the example dir run (replace __ with your board name, for - example, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): + example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): ```bash $ west build -b diff --git a/examples/platform/telink/common.cmake b/examples/platform/telink/common.cmake index 314e74063be605..9b8149c7d02d6a 100644 --- a/examples/platform/telink/common.cmake +++ b/examples/platform/telink/common.cmake @@ -15,12 +15,14 @@ string(REPLACE "_retention" "" BASE_BOARD ${BOARD}) -if(FLASH_SIZE) - message(STATUS "Flash memory size is set to: " ${FLASH_SIZE} "b") -else() - set(FLASH_SIZE "2m") - message(STATUS "Flash memory size is set to: 2mb (default)") +if(NOT FLASH_SIZE) + if(${BASE_BOARD} MATCHES "tlsr9118bdk40d") + set(FLASH_SIZE "3m") + else() + set(FLASH_SIZE "2m") + endif() endif() +message(STATUS "Flash memory size is set to: " ${FLASH_SIZE} "b") if(${TLNK_MARS_BOARD} MATCHES y) set(MARS_BOOT_DTC_OVERLAY_FILE "${CHIP_ROOT}/src/platform/telink/${BASE_BOARD}_mars_boot.overlay") @@ -60,11 +62,22 @@ else() unset(USB_CONF_OVERLAY_FILE) endif() -set(GLOBAL_BOOT_CONF_OVERLAY_FILE "${CHIP_ROOT}/config/telink/app/bootloader.conf") -if(NOT EXISTS "${GLOBAL_BOOT_CONF_OVERLAY_FILE}") - message(FATAL_ERROR "${GLOBAL_BOOT_CONF_OVERLAY_FILE} doesn't exist") +set(BOOT_CONF_OVERLAY_FILE "${CHIP_ROOT}/config/telink/app/bootloader.conf") +if(NOT EXISTS "${BOOT_CONF_OVERLAY_FILE}") + message(FATAL_ERROR "${BOOT_CONF_OVERLAY_FILE} doesn't exist") endif() +if(${CONFIG_USB_TELINK_B9X} MATCHES y) + set(BOOT_USB_CONF_OVERLAY_FILE "${CHIP_ROOT}/config/telink/app/bootloader_usb.conf") + if(NOT EXISTS "${BOOT_USB_CONF_OVERLAY_FILE}") + message(FATAL_ERROR "${BOOT_USB_CONF_OVERLAY_FILE} doesn't exist") + endif() +else() + unset(BOOT_USB_CONF_OVERLAY_FILE) +endif() + +set(GLOBAL_BOOT_CONF_OVERLAY_FILE "${BOOT_CONF_OVERLAY_FILE} ${BOOT_USB_CONF_OVERLAY_FILE}") + set(LOCAL_DTC_OVERLAY_FILE "${CMAKE_CURRENT_SOURCE_DIR}/boards/${BASE_BOARD}.overlay") if(NOT EXISTS "${LOCAL_DTC_OVERLAY_FILE}") message(STATUS "${LOCAL_DTC_OVERLAY_FILE} doesn't exist") diff --git a/examples/platform/telink/common/src/AppTaskCommon.cpp b/examples/platform/telink/common/src/AppTaskCommon.cpp index 5ddf137eb36923..96bb3ca87463dc 100644 --- a/examples/platform/telink/common/src/AppTaskCommon.cpp +++ b/examples/platform/telink/common/src/AppTaskCommon.cpp @@ -218,7 +218,8 @@ CHIP_ERROR AppTaskCommon::StartApp(void) #endif #ifdef CONFIG_BOOTLOADER_MCUBOOT - if (!chip::DeviceLayer::ConnectivityMgr().IsThreadProvisioned()) + if (!chip::DeviceLayer::ConnectivityMgr().IsThreadProvisioned() && + !chip::DeviceLayer::ConnectivityMgr().IsWiFiStationProvisioned()) { LOG_INF("Confirm image."); OtaConfirmNewImage(); @@ -401,7 +402,8 @@ void AppTaskCommon::InitPwms() void AppTaskCommon::LinkPwms(PwmManager & pwmManager) { -#if CONFIG_WS2812_STRIP +#if CONFIG_WS2812_STRIP || \ + CONFIG_BOARD_TLSR9118BDK40D // TLSR9118BDK40D EVK buttons located on 4th PWM channel (see tlsr9118bdk40d.overlay) pwmManager.linkPwm(PwmManager::EAppPwm_Red, 0); pwmManager.linkPwm(PwmManager::EAppPwm_Green, 1); pwmManager.linkPwm(PwmManager::EAppPwm_Blue, 2); @@ -672,6 +674,16 @@ void AppTaskCommon::ChipEventHandler(const ChipDeviceEvent * event, intptr_t /* sIsNetworkProvisioned = ConnectivityMgr().IsThreadProvisioned(); sIsNetworkEnabled = ConnectivityMgr().IsThreadEnabled(); sIsThreadAttached = ConnectivityMgr().IsThreadAttached(); +#elif CHIP_DEVICE_CONFIG_ENABLE_WIFI + case DeviceEventType::kWiFiConnectivityChange: + sIsNetworkProvisioned = ConnectivityMgr().IsWiFiStationProvisioned(); + sIsNetworkEnabled = ConnectivityMgr().IsWiFiStationEnabled(); +#if CONFIG_CHIP_OTA_REQUESTOR + if (event->WiFiConnectivityChange.Result == kConnectivity_Established) + { + InitBasicOTARequestor(); + } +#endif #endif /* CHIP_DEVICE_CONFIG_ENABLE_THREAD */ #if CONFIG_CHIP_ENABLE_APPLICATION_STATUS_LED UpdateStatusLED(); diff --git a/examples/platform/telink/common/src/mainCommon.cpp b/examples/platform/telink/common/src/mainCommon.cpp index c80ab4c609e971..7018adaa1b8c7c 100644 --- a/examples/platform/telink/common/src/mainCommon.cpp +++ b/examples/platform/telink/common/src/mainCommon.cpp @@ -21,6 +21,11 @@ #include #include +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI +#include +#include +#endif + #include #ifdef CONFIG_USB_DEVICE_STACK @@ -37,6 +42,10 @@ using namespace ::chip; using namespace ::chip::Inet; using namespace ::chip::DeviceLayer; +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI +app::Clusters::NetworkCommissioning::Instance sWiFiCommissioningInstance(0, &(NetworkCommissioning::TelinkWiFiDriver::Instance())); +#endif + #ifdef CONFIG_CHIP_ENABLE_POWER_ON_FACTORY_RESET static constexpr uint32_t kFactoryResetOnBootMaxCnt = 5; static constexpr char kFactoryResetOnBootStoreKey[] = "TelinkFactoryResetOnBootCnt"; @@ -155,6 +164,8 @@ int main(void) LOG_ERR("SetThreadDeviceType fail"); goto exit; } +#elif CHIP_DEVICE_CONFIG_ENABLE_WIFI + sWiFiCommissioningInstance.Init(); #else return CHIP_ERROR_INTERNAL; #endif /* CHIP_DEVICE_CONFIG_ENABLE_THREAD */ diff --git a/examples/pump-app/telink/README.md b/examples/pump-app/telink/README.md index 487833f4fb7d30..885cf7230856bc 100755 --- a/examples/pump-app/telink/README.md +++ b/examples/pump-app/telink/README.md @@ -29,7 +29,7 @@ reference for creating your own pump application. ``` 3. In the example dir run (replace __ with your board name, for - example, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): + example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): ```bash $ west build -b diff --git a/examples/pump-controller-app/telink/README.md b/examples/pump-controller-app/telink/README.md index 5a9e95548f883a..a49b0999ddebac 100755 --- a/examples/pump-controller-app/telink/README.md +++ b/examples/pump-controller-app/telink/README.md @@ -30,7 +30,7 @@ your own pump application. ``` 3. In the example dir run (replace __ with your board name, for - example, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): + example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): ```bash $ west build -b diff --git a/examples/shell/telink/README.md b/examples/shell/telink/README.md index aa7023043e0b9f..0d445a3838ec3a 100755 --- a/examples/shell/telink/README.md +++ b/examples/shell/telink/README.md @@ -25,7 +25,7 @@ You can use this example as a reference for creating your own application. ``` 3. In the example dir run (replace __ with your board name, for - example, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): + example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): ```bash $ west build -b diff --git a/examples/smoke-co-alarm-app/telink/README.md b/examples/smoke-co-alarm-app/telink/README.md index 7f7138a7f14771..97a00f8d53a8ad 100755 --- a/examples/smoke-co-alarm-app/telink/README.md +++ b/examples/smoke-co-alarm-app/telink/README.md @@ -25,7 +25,7 @@ You can use this example as a reference for creating your own application. ``` 3. In the example dir run (replace __ with your board name, for - example, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): + example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): ```bash $ west build -b diff --git a/examples/temperature-measurement-app/telink/README.md b/examples/temperature-measurement-app/telink/README.md index 6f147721149b19..c78bd36ea8e4a6 100644 --- a/examples/temperature-measurement-app/telink/README.md +++ b/examples/temperature-measurement-app/telink/README.md @@ -29,7 +29,7 @@ creating your own application. ``` 3. In the example dir run (replace __ with your board name, for - example, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): + example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): ```bash $ west build -b diff --git a/examples/thermostat/telink/README.md b/examples/thermostat/telink/README.md index 85a98d8ce4862e..604a917ff18ce9 100755 --- a/examples/thermostat/telink/README.md +++ b/examples/thermostat/telink/README.md @@ -25,7 +25,7 @@ You can use this example as a reference for creating your own application. ``` 3. In the example dir run (replace __ with your board name, for - example, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): + example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): ```bash $ west build -b diff --git a/examples/window-app/telink/README.md b/examples/window-app/telink/README.md index c5500aa5c9cbd6..9d35cbc2d5a360 100644 --- a/examples/window-app/telink/README.md +++ b/examples/window-app/telink/README.md @@ -28,7 +28,7 @@ for creating your own application. ``` 3. In the example dir run (replace __ with your board name, for - example, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): + example, `tlsr9118bdk40d`, `tlsr9518adk80d`, `tlsr9528a` or `tlsr9258a`): ```bash $ west build -b diff --git a/scripts/build/build/targets.py b/scripts/build/build/targets.py index 8e3487f9cb1c48..94f0c7c598c07d 100755 --- a/scripts/build/build/targets.py +++ b/scripts/build/build/targets.py @@ -1,4 +1,4 @@ -# Copyright (c) 2021 Project CHIP Authors +# Copyright (c) 2021-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. @@ -738,6 +738,7 @@ def BuildTelinkTarget(): target = BuildTarget('telink', TelinkBuilder) target.AppendFixedTargets([ + TargetPart('tlsr9118bdk40d', board=TelinkBoard.TLRS9118BDK40D), TargetPart('tlsr9518adk80d', board=TelinkBoard.TLSR9518ADK80D), TargetPart('tlsr9528a', board=TelinkBoard.TLSR9528A), TargetPart('tlsr9528a_retention', board=TelinkBoard.TLSR9528A_RETENTION), diff --git a/scripts/build/builders/telink.py b/scripts/build/builders/telink.py index 9160c044a2be57..9b63635250dcf2 100644 --- a/scripts/build/builders/telink.py +++ b/scripts/build/builders/telink.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022 Project CHIP Authors +# 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. @@ -114,6 +114,7 @@ def AppNamePrefix(self): class TelinkBoard(Enum): + TLRS9118BDK40D = auto() TLSR9518ADK80D = auto() TLSR9528A = auto() TLSR9528A_RETENTION = auto() @@ -121,7 +122,9 @@ class TelinkBoard(Enum): TLSR9258A_RETENTION = auto() def GnArgName(self): - if self == TelinkBoard.TLSR9518ADK80D: + if self == TelinkBoard.TLRS9118BDK40D: + return 'tlsr9118bdk40d' + elif self == TelinkBoard.TLSR9518ADK80D: return 'tlsr9518adk80d' elif self == TelinkBoard.TLSR9528A: return 'tlsr9528a' diff --git a/scripts/build/testdata/all_targets_linux_x64.txt b/scripts/build/testdata/all_targets_linux_x64.txt index 97d3f2d2ed622f..a8ad9198f63da8 100644 --- a/scripts/build/testdata/all_targets_linux_x64.txt +++ b/scripts/build/testdata/all_targets_linux_x64.txt @@ -23,5 +23,5 @@ nuttx-x64-light qpg-qpg6105-{lock,light,shell,persistent-storage,light-switch,thermostat}[-updateimage] stm32-stm32wb5mm-dk-light tizen-arm-{all-clusters,all-clusters-minimal,chip-tool,light,tests}[-no-ble][-no-thread][-no-wifi][-asan][-ubsan][-with-ui] -telink-{tlsr9518adk80d,tlsr9528a,tlsr9528a_retention,tlsr9258a,tlsr9258a_retention}-{air-quality-sensor,all-clusters,all-clusters-minimal,bridge,contact-sensor,light,light-switch,lock,ota-requestor,pump,pump-controller,shell,smoke-co-alarm,temperature-measurement,thermostat,window-covering}[-ota][-dfu][-shell][-rpc][-factory-data][-4mb][-mars] +telink-{tlsr9118bdk40d,tlsr9518adk80d,tlsr9528a,tlsr9528a_retention,tlsr9258a,tlsr9258a_retention}-{air-quality-sensor,all-clusters,all-clusters-minimal,bridge,contact-sensor,light,light-switch,lock,ota-requestor,pump,pump-controller,shell,smoke-co-alarm,temperature-measurement,thermostat,window-covering}[-ota][-dfu][-shell][-rpc][-factory-data][-4mb][-mars] openiotsdk-{shell,lock}[-mbedtls][-psa] diff --git a/src/platform/Zephyr/InetUtils.cpp b/src/platform/Zephyr/InetUtils.cpp index 07d2a302dacbf3..a4879884da41f0 100644 --- a/src/platform/Zephyr/InetUtils.cpp +++ b/src/platform/Zephyr/InetUtils.cpp @@ -15,6 +15,8 @@ * limitations under the License. */ +#include + #include "InetUtils.h" #include @@ -38,10 +40,18 @@ net_if * GetInterface(Inet::InterfaceId ifaceId) return ifaceId.IsPresent() ? net_if_get_by_index(ifaceId.GetPlatformInterface()) : net_if_get_default(); } +#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_SERIES_RISCV_TELINK_W91) return net_if_get_first_wifi(); +#else + return GetInterface(); +#endif } +#endif } // namespace InetUtils } // namespace DeviceLayer diff --git a/src/platform/Zephyr/ThreadStackManagerImpl.h b/src/platform/Zephyr/ThreadStackManagerImpl.h index ec9feb8b121d3e..4368c23468a6f8 100644 --- a/src/platform/Zephyr/ThreadStackManagerImpl.h +++ b/src/platform/Zephyr/ThreadStackManagerImpl.h @@ -29,9 +29,7 @@ #include #include -#if !defined(CONFIG_SOC_SERIES_RISCV_TELINK_B9X) #include -#endif // !defined(CONFIG_SOC_SERIES_RISCV_TELINK_B9X) #include diff --git a/src/platform/telink/BUILD.gn b/src/platform/telink/BUILD.gn index 5561fb4e906ac9..787b45ca7c06eb 100644 --- a/src/platform/telink/BUILD.gn +++ b/src/platform/telink/BUILD.gn @@ -1,4 +1,4 @@ -# Copyright (c) 2021-2023 Project CHIP Authors +# Copyright (c) 2021-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. @@ -69,6 +69,13 @@ static_library("telink") { ] } + if (chip_enable_openthread || chip_enable_wifi) { + sources += [ + "../Zephyr/InetUtils.cpp", + "../Zephyr/InetUtils.h", + ] + } + if (chip_enable_openthread) { sources += [ "../OpenThread/OpenThreadUtils.cpp", @@ -86,6 +93,17 @@ static_library("telink") { } } + if (chip_enable_wifi) { + sources += [ + "wifi/ConnectivityManagerImplWiFi.cpp", + "wifi/ConnectivityManagerImplWiFi.h", + "wifi/TelinkWiFiDriver.cpp", + "wifi/TelinkWiFiDriver.h", + "wifi/WiFiManager.cpp", + "wifi/WiFiManager.h", + ] + } + if (chip_enable_ota_requestor) { sources += [ "OTAImageProcessorImpl.cpp", diff --git a/src/platform/telink/CHIPDevicePlatformConfig.h b/src/platform/telink/CHIPDevicePlatformConfig.h index f53be5b303f383..cc612b86499eba 100644 --- a/src/platform/telink/CHIPDevicePlatformConfig.h +++ b/src/platform/telink/CHIPDevicePlatformConfig.h @@ -93,14 +93,12 @@ #define CHIP_DEVICE_CONFIG_ENABLE_THREAD 0 #endif -#ifndef CHIP_DEVICE_CONFIG_ENABLE_WIFI -#define CHIP_DEVICE_CONFIG_ENABLE_WIFI 0 -#endif - -#if CHIP_DEVICE_CONFIG_ENABLE_WIFI +#ifdef CONFIG_WIFI_W91 +#define CHIP_DEVICE_CONFIG_ENABLE_WIFI CONFIG_WIFI_W91 #define CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION 1 #define CHIP_DEVICE_CONFIG_ENABLE_WIFI_AP 0 #else +#define CHIP_DEVICE_CONFIG_ENABLE_WIFI 0 #define CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION 0 #define CHIP_DEVICE_CONFIG_ENABLE_WIFI_AP 0 #endif diff --git a/src/platform/telink/ConnectivityManagerImpl.cpp b/src/platform/telink/ConnectivityManagerImpl.cpp index 601bac3f22fe71..9b2b651dab93b8 100644 --- a/src/platform/telink/ConnectivityManagerImpl.cpp +++ b/src/platform/telink/ConnectivityManagerImpl.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2021-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. @@ -17,11 +17,12 @@ #include -#include -#include - +#include #include #include +#include +#include +#include #include @@ -34,15 +35,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(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() @@ -50,6 +100,30 @@ CHIP_ERROR ConnectivityManagerImpl::_Init() #if CHIP_DEVICE_CONFIG_ENABLE_THREAD GenericConnectivityManagerImpl_Thread::_Init(); #endif +#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; } diff --git a/src/platform/telink/ConnectivityManagerImpl.h b/src/platform/telink/ConnectivityManagerImpl.h index ce6ae49a4283e7..e258c27d055044 100644 --- a/src/platform/telink/ConnectivityManagerImpl.h +++ b/src/platform/telink/ConnectivityManagerImpl.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2021-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. @@ -33,7 +33,11 @@ #else #include #endif +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI +#include "wifi/ConnectivityManagerImplWiFi.h" +#else #include +#endif #include @@ -65,7 +69,11 @@ class ConnectivityManagerImpl final : public ConnectivityManager, #else public Internal::GenericConnectivityManagerImpl_NoThread, #endif +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI + public ConnectivityManagerImplWiFi +#else public Internal::GenericConnectivityManagerImpl_NoWiFi +#endif { // Allow the ConnectivityManager interface class to delegate method calls to // the implementation methods provided by this class. diff --git a/src/platform/telink/ThreadStackManagerImpl.cpp b/src/platform/telink/ThreadStackManagerImpl.cpp index 2ec9b02ca0b67c..41d9189ab5f215 100644 --- a/src/platform/telink/ThreadStackManagerImpl.cpp +++ b/src/platform/telink/ThreadStackManagerImpl.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-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. @@ -27,16 +27,13 @@ #include #include -#include #include -#include #include namespace chip { namespace DeviceLayer { using namespace ::chip::DeviceLayer::Internal; -using namespace ::chip::Inet; ThreadStackManagerImpl ThreadStackManagerImpl::sInstance; @@ -48,20 +45,6 @@ CHIP_ERROR ThreadStackManagerImpl::_InitThreadStack() ReturnErrorOnFailure(GenericThreadStackManagerImpl_OpenThread::DoInit(instance)); - UDPEndPointImplSockets::SetMulticastGroupHandler( - [](InterfaceId, const IPAddress & address, UDPEndPointImplSockets::MulticastOperation operation) { - 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); - }); - #if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT k_sem_init(&mSrpClearAllSemaphore, 0, 1); #endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT diff --git a/src/platform/telink/ThreadStackManagerImpl.h b/src/platform/telink/ThreadStackManagerImpl.h index 21db9972f62dec..b06e33d9f5e0b2 100644 --- a/src/platform/telink/ThreadStackManagerImpl.h +++ b/src/platform/telink/ThreadStackManagerImpl.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2023-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. @@ -29,9 +29,6 @@ #include #include -#if !CONFIG_SOC_SERIES_RISCV_TELINK_B9X -#include -#endif // !CONFIG_SOC_SERIES_RISCV_TELINK_B9X #include diff --git a/src/platform/telink/telink-mbedtls-config.h b/src/platform/telink/telink-mbedtls-config.h index 23d7d97bc43c9a..921255d9425289 100644 --- a/src/platform/telink/telink-mbedtls-config.h +++ b/src/platform/telink/telink-mbedtls-config.h @@ -37,8 +37,10 @@ #define MBEDTLS_X509_CREATE_C #define MBEDTLS_X509_CSR_WRITE_C +#ifdef CONFIG_TELINK_B9X_MBEDTLS_HW_ACCELERATION #define MBEDTLS_AES_ALT #define MBEDTLS_ECP_ALT +#endif #undef MBEDTLS_ERROR_C diff --git a/src/platform/telink/tlsr9118bdk40d.overlay b/src/platform/telink/tlsr9118bdk40d.overlay new file mode 100644 index 00000000000000..6dddbe61c95a38 --- /dev/null +++ b/src/platform/telink/tlsr9118bdk40d.overlay @@ -0,0 +1,48 @@ +/ { + /* Short TL_Key1 (J20 pin 11) to ground (J20 pin 25-35) */ + key_pool { + compatible = "gpio-keys"; + + inp { + gpios = <&gpio0 16 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>, + <&gpio0 15 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + }; + }; + + key_matrix { + compatible = "gpio-keys"; + + col { + gpios = <&gpio0 18 GPIO_ACTIVE_HIGH>, + <&gpio0 17 GPIO_ACTIVE_HIGH>; + }; + + row { + gpios = <&gpio0 16 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>, + <&gpio0 15 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; + }; + }; + + led_pool{ + compatible = "gpio-leds"; + + out { + gpios = <&gpio0 19 GPIO_ACTIVE_HIGH>; + }; + }; + pwm_pool { + compatible = "pwm-leds"; + out { + pwms = <&pwm0 4 PWM_MSEC(1) PWM_POLARITY_NORMAL>, + <&pwm0 2 PWM_MSEC(1) PWM_POLARITY_NORMAL>, + <&pwm0 3 PWM_MSEC(1) PWM_POLARITY_NORMAL>; + }; + }; +}; + +&pwm0 { + /* On board RGB LEDs */ + pinctrl-ch4 = <&pwm_ch4_p20_default>; + pinctrl-ch2 = <&pwm_ch2_p17_default>; + pinctrl-ch3 = <&pwm_ch3_p18_default>; +}; \ No newline at end of file diff --git a/src/platform/telink/tlsr9118bdk40d_3m_flash.overlay b/src/platform/telink/tlsr9118bdk40d_3m_flash.overlay new file mode 100644 index 00000000000000..3e42145fb44370 --- /dev/null +++ b/src/platform/telink/tlsr9118bdk40d_3m_flash.overlay @@ -0,0 +1,35 @@ +&flash { + reg = <0x80000000 0x300000>; + + partitions { + /delete-node/ partition@0; + /delete-node/ partition@20000; + /delete-node/ partition@88000; + /delete-node/ partition@f0000; + /delete-node/ partition@f4000; + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0x20000>; + }; + slot0_partition: partition@20000 { + label = "image-0"; + reg = <0x20000 0xe8000>; + }; + factory_partition: partition@108000 { + label = "factory-data"; + reg = <0x108000 0x1000>; + }; + storage_partition: partition@109000 { + label = "storage"; + reg = <0x109000 0xf000>; + }; + slot1_partition: partition@118000 { + label = "image-1"; + reg = <0x118000 0xe8000>; + }; + reserved_partition: partition@200000 { + label = "vendor-data"; + reg = <0x200000 0x100000>; + }; + }; +}; diff --git a/src/platform/telink/wifi/ConnectivityManagerImplWiFi.cpp b/src/platform/telink/wifi/ConnectivityManagerImplWiFi.cpp new file mode 100644 index 00000000000000..3fefcf709e1b9b --- /dev/null +++ b/src/platform/telink/wifi/ConnectivityManagerImplWiFi.cpp @@ -0,0 +1,178 @@ +/* + * + * 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; +using namespace ::chip::TLV; + +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/ConnectivityManagerImplWiFi.h b/src/platform/telink/wifi/ConnectivityManagerImplWiFi.h new file mode 100644 index 00000000000000..ec9c1d12027af9 --- /dev/null +++ b/src/platform/telink/wifi/ConnectivityManagerImplWiFi.h @@ -0,0 +1,80 @@ +/* + * + * 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. + */ + +#pragma once + +#include + +#include + +#include + +namespace chip { +namespace Inet { +class IPAddress; +} // namespace Inet +} // namespace chip + +namespace chip { +namespace DeviceLayer { + +class ConnectivityManagerImplWiFi +{ + friend class ConnectivityManager; + +protected: + CHIP_ERROR InitWiFi(); + +private: + // Wi-Fi station + ConnectivityManager::WiFiStationMode _GetWiFiStationMode(void); + CHIP_ERROR _SetWiFiStationMode(ConnectivityManager::WiFiStationMode val); + bool _IsWiFiStationEnabled(void); + bool _IsWiFiStationApplicationControlled(void); + bool _IsWiFiStationConnected(void); + System::Clock::Timeout _GetWiFiStationReconnectInterval(void); + CHIP_ERROR _SetWiFiStationReconnectInterval(System::Clock::Timeout val); + bool _IsWiFiStationProvisioned(void); + void _ClearWiFiStationProvision(void); + CHIP_ERROR _GetAndLogWiFiStatsCounters(void); + bool _CanStartWiFiScan(); + void _OnWiFiScanDone(); + void _OnWiFiStationProvisionChange(); + + // Wi-Fi access point - not supported + ConnectivityManager::WiFiAPMode _GetWiFiAPMode(void); + CHIP_ERROR _SetWiFiAPMode(ConnectivityManager::WiFiAPMode val); + bool _IsWiFiAPActive(void); + bool _IsWiFiAPApplicationControlled(void); + void _DemandStartWiFiAP(void); + void _StopOnDemandWiFiAP(void); + void _MaintainOnDemandWiFiAP(void); + System::Clock::Timeout _GetWiFiAPIdleTimeout(void); + void _SetWiFiAPIdleTimeout(System::Clock::Timeout val); + + ConnectivityManager::WiFiStationMode mStationMode{ ConnectivityManager::WiFiStationMode::kWiFiStationMode_Disabled }; + ConnectivityManager::WiFiStationState mStationState{ ConnectivityManager::WiFiStationState::kWiFiStationState_NotConnected }; + System::Clock::Timeout mWiFiStationReconnectInterval{}; + + static const char * _WiFiStationModeToStr(ConnectivityManager::WiFiStationMode mode); + static const char * _WiFiAPModeToStr(ConnectivityManager::WiFiAPMode mode); + static const char * _WiFiStationStateToStr(ConnectivityManager::WiFiStationState state); + static const char * _WiFiAPStateToStr(ConnectivityManager::WiFiAPState state); +}; + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/telink/wifi/TelinkWiFiDriver.cpp b/src/platform/telink/wifi/TelinkWiFiDriver.cpp new file mode 100644 index 00000000000000..d6b47053839e35 --- /dev/null +++ b/src/platform/telink/wifi/TelinkWiFiDriver.cpp @@ -0,0 +1,281 @@ +/* + * + * 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 + +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{ [] { Instance().OnNetworkStatusChanged(Status::kSuccess); }, + [] { Instance().OnNetworkStatusChanged(Status::kUnknownError); }, + System::Clock::Seconds32{ kWiFiConnectNetworkTimeoutSeconds } }; + ReturnErrorOnFailure( + WiFiManager::Instance().Connect(mStagingNetwork.GetSsidSpan(), mStagingNetwork.GetPassSpan(), handling)); + } + + return CHIP_NO_ERROR; +} + +void TelinkWiFiDriver::OnNetworkStatusChanged(Status status) +{ + if (status == Status::kSuccess) + { + ConnectivityMgr().SetWiFiStationMode(ConnectivityManager::kWiFiStationMode_Enabled); + } + + if (mpNetworkStatusChangeCallback) + { + mpNetworkStatusChangeCallback->OnNetworkingStatusChange(status, NullOptional, 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{ [] { Instance().OnNetworkStatusChanged(Status::kSuccess); }, + [] { Instance().OnNetworkStatusChanged(Status::kUnknownError); }, + 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{ [] { Instance().OnNetworkStatusChanged(Status::kSuccess); }, + [] { Instance().OnNetworkStatusChanged(Status::kUnknownError); }, + 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(WiFiManager::WiFiRequestStatus status) +{ + VerifyOrReturn(mScanCallback != nullptr); + mScanCallback->OnFinished(status == WiFiManager::WiFiRequestStatus::SUCCESS ? Status::kSuccess : Status::kUnknownError, + 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); }, + [](WiFiManager::WiFiRequestStatus status) { Instance().OnScanWiFiNetworkDone(status); }); + + if (error != CHIP_NO_ERROR) + { + mScanCallback = nullptr; + callback->OnFinished(Status::kUnknownError, CharSpan(), nullptr); + } +} + +} // namespace NetworkCommissioning +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/telink/wifi/TelinkWiFiDriver.h b/src/platform/telink/wifi/TelinkWiFiDriver.h new file mode 100644 index 00000000000000..b9f9b6374c42e3 --- /dev/null +++ b/src/platform/telink/wifi/TelinkWiFiDriver.h @@ -0,0 +1,112 @@ +/* + * + * 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. + */ + +#pragma once + +#include "WiFiManager.h" + +#include + +namespace chip { +namespace DeviceLayer { +namespace NetworkCommissioning { + +inline constexpr uint8_t kMaxWiFiNetworks = 1; +inline constexpr uint8_t kWiFiScanNetworksTimeOutSeconds = 10; +inline constexpr uint8_t kWiFiConnectNetworkTimeoutSeconds = 35; + +class TelinkWiFiScanResponseIterator : public Iterator +{ +public: + size_t Count() override { return mResultCount; } + bool Next(WiFiScanResponse & item) override; + void Release() override; + void Add(const WiFiScanResponse & result); + +private: + size_t mResultId = 0; + size_t mResultCount = 0; + WiFiScanResponse * mResults = nullptr; +}; + +class TelinkWiFiDriver final : public WiFiDriver +{ +public: + // Define non-volatile storage keys for SSID and password. + // The naming convention is aligned with DefaultStorageKeyAllocator class. + static constexpr char kSsidKey[] = "g/wi/s"; + static constexpr char kPassKey[] = "g/wi/p"; + + class WiFiNetworkIterator final : public NetworkIterator + { + public: + WiFiNetworkIterator(TelinkWiFiDriver * aDriver) : mDriver(aDriver) {} + size_t Count() override; + bool Next(Network & item) override; + void Release() override { delete this; } + ~WiFiNetworkIterator() = default; + + private: + TelinkWiFiDriver * mDriver; + bool mExhausted{ false }; + }; + + // BaseDriver + NetworkIterator * GetNetworks() override { return new WiFiNetworkIterator(this); } + CHIP_ERROR Init(NetworkStatusChangeCallback * networkStatusChangeCallback) override; + void Shutdown() override; + + // WirelessDriver + uint8_t GetMaxNetworks() override { return kMaxWiFiNetworks; } + uint8_t GetScanNetworkTimeoutSeconds() override { return kWiFiScanNetworksTimeOutSeconds; } + uint8_t GetConnectNetworkTimeoutSeconds() override { return kWiFiConnectNetworkTimeoutSeconds; } + + CHIP_ERROR CommitConfiguration() override; + CHIP_ERROR RevertConfiguration() override; + + Status RemoveNetwork(ByteSpan networkId, MutableCharSpan & outDebugText, uint8_t & outNetworkIndex) override; + Status ReorderNetwork(ByteSpan networkId, uint8_t index, MutableCharSpan & outDebugText) override; + void ConnectNetwork(ByteSpan networkId, ConnectCallback * callback) override; + + // WiFiDriver + Status AddOrUpdateNetwork(ByteSpan ssid, ByteSpan credentials, MutableCharSpan & outDebugText, + uint8_t & outNetworkIndex) override; + void ScanNetworks(ByteSpan ssid, ScanCallback * callback) override; + + static TelinkWiFiDriver & Instance() + { + static TelinkWiFiDriver sInstance; + return sInstance; + } + + void OnNetworkStatusChanged(Status status); + void OnScanWiFiNetworkResult(const WiFiScanResponse & result); + void OnScanWiFiNetworkDone(WiFiManager::WiFiRequestStatus status); + +private: + void LoadFromStorage(); + + ConnectCallback * mpConnectCallback{ nullptr }; + NetworkStatusChangeCallback * mpNetworkStatusChangeCallback{ nullptr }; + WiFiManager::WiFiNetwork mStagingNetwork; + TelinkWiFiScanResponseIterator mScanResponseIterator; + ScanCallback * mScanCallback{ nullptr }; +}; + +} // namespace NetworkCommissioning +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/telink/wifi/WiFiManager.cpp b/src/platform/telink/wifi/WiFiManager.cpp new file mode 100644 index 00000000000000..88195fdbee2ab8 --- /dev/null +++ b/src/platform/telink/wifi/WiFiManager.cpp @@ -0,0 +1,560 @@ +/* + * + * 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 +#include + +namespace chip { +namespace DeviceLayer { + +namespace { + +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; + 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 } }); + +void WiFiManager::WifiMgmtEventHandler(net_mgmt_event_callback * cb, uint32_t mgmtEvent, net_if * iface) +{ + if (0 == strcmp(iface->if_dev->dev->name, "wlan0")) + { + Platform::UniquePtr eventData(new uint8_t[cb->info_length]); + VerifyOrReturn(eventData); + memcpy(eventData.get(), cb->info, cb->info_length); + sEventHandlerMap[mgmtEvent](std::move(eventData)); + } +} + +CHIP_ERROR WiFiManager::Init() +{ + net_mgmt_init_event_callback(&mWiFiMgmtClbk, WifiMgmtEventHandler, kWifiManagementEvents); + net_mgmt_add_event_callback(&mWiFiMgmtClbk); + + ChipLogDetail(DeviceLayer, "WiFiManager has been initialized"); + + return CHIP_NO_ERROR; +} +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; + mCachedWiFiState = mWiFiState; + mWiFiState = WIFI_STATE_SCANNING; + mSsidFound = false; + + if (0 != net_mgmt(NET_REQUEST_WIFI_SCAN, iface, NULL, 0)) + { + 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.mOnConnectionSuccess = handling.mOnConnectionSuccess; + mHandling.mOnConnectionFailed = handling.mOnConnectionFailed; + mHandling.mConnectionTimeout = handling.mConnectionTimeout; + + 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() +{ + net_if * iface = InetUtils::GetInterface(); + VerifyOrReturnError(nullptr != iface, CHIP_ERROR_INTERNAL); + + mApplicationDisconnectRequested = true; + int status = net_mgmt(NET_REQUEST_WIFI_DISCONNECT, iface, 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 +{ + net_if * iface = InetUtils::GetInterface(); + VerifyOrReturnError(nullptr != iface, CHIP_ERROR_INTERNAL); + struct wifi_iface_status status = { 0 }; + + if (net_mgmt(NET_REQUEST_WIFI_IFACE_STATUS, iface, &status, sizeof(struct 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, InetUtils::GetInterface(), &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; + + return CHIP_NO_ERROR; +} + +void WiFiManager::ScanResultHandler(Platform::UniquePtr data) +{ + // 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()); + + 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().mSsidFound = true; + } + } + + if (Instance().mScanResultCallback && !Instance().mInternalScan) + { + Instance().mScanResultCallback(ToScanResponse(scanResult)); + } +} + +void WiFiManager::ScanDoneHandler(Platform::UniquePtr data) +{ + 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); + + if (requestStatus == WiFiRequestStatus::FAILURE) + { + ChipLogError(DeviceLayer, "Wi-Fi scan finalization failure (%d)", status->status); + } + else + { + ChipLogProgress(DeviceLayer, "Wi-Fi scan done (%d)", status->status); + } + + if (Instance().mScanDoneCallback && !Instance().mInternalScan) + { + Instance().mScanDoneCallback(requestStatus); + // 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); + } + + Instance().mWiFiState = WIFI_STATE_ASSOCIATING; + 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))) + { + ChipLogError(DeviceLayer, "Connection request failed"); + if (Instance().mHandling.mOnConnectionFailed) + { + Instance().mHandling.mOnConnectionFailed(); + } + 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 * iface = InetUtils::GetInterface(); + if (iface && iface->if_dev->link_addr.type == NET_LINK_ETHERNET) + { + net_if_start_rs(iface); + 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) +{ + 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); + + if (requestStatus == WiFiRequestStatus::FAILURE || requestStatus == WiFiRequestStatus::TERMINATED) + { + ChipLogProgress(DeviceLayer, "Connection to WiFi network failed or was terminated by another request"); + Instance().mWiFiState = WIFI_STATE_DISCONNECTED; + if (Instance().mHandling.mOnConnectionFailed) + { + Instance().mHandling.mOnConnectionFailed(); + } + } + 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.mOnConnectionSuccess) + { + Instance().mHandling.mOnConnectionSuccess(); + } + 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)); + } + } + // 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) +{ + SystemLayer().ScheduleLambda([] { + ChipLogProgress(DeviceLayer, "WiFi station disconnected"); + Instance().mWiFiState = WIFI_STATE_DISCONNECTED; + Instance().PostConnectivityStatusChange(kConnectivity_Lost); + }); +} + +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) +{ + net_if * iface = InetUtils::GetInterface(); + VerifyOrReturnError(nullptr != iface, CHIP_ERROR_INTERNAL); + + wifi_ps_config currentConfig{}; + if (net_mgmt(NET_REQUEST_WIFI_PS_CONFIG, iface, ¤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, iface, ¶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; +} + +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/telink/wifi/WiFiManager.h b/src/platform/telink/wifi/WiFiManager.h new file mode 100644 index 00000000000000..f48d8f9b372469 --- /dev/null +++ b/src/platform/telink/wifi/WiFiManager.h @@ -0,0 +1,238 @@ +/* + * + * 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: + enum WiFiRequestStatus : int + { + SUCCESS = 0, + FAILURE = 1, + TERMINATED = 2 + }; + + using ScanResultCallback = void (*)(const NetworkCommissioning::WiFiScanResponse &); + using ScanDoneCallback = void (*)(WiFiRequestStatus); + using ConnectionCallback = void (*)(); + + enum class StationStatus : uint8_t + { + NONE, + DISCONNECTED, + DISABLED, + SCANNING, + CONNECTING, + CONNECTED, + PROVISIONING, + FULLY_PROVISIONED, + UNKNOWN + }; + + static WiFiManager & Instance() + { + static WiFiManager sInstance; + return sInstance; + } + + struct ConnectionHandling + { + ConnectionCallback mOnConnectionSuccess{}; + ConnectionCallback mOnConnectionFailed{}; + 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; + CHIP_ERROR GetNetworkStatistics(NetworkStatistics & stats) const; + void AbortConnectionRecovery(); + CHIP_ERROR SetLowPowerMode(bool onoff); + +private: + using NetEventHandler = void (*)(Platform::UniquePtr); + + 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; + + // 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 PostConnectivityStatusChange(ConnectivityChange changeType); + static void SendRouterSolicitation(System::Layer * layer, void * param); + + // 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(); + + ConnectionParams mWiFiParams{}; + ConnectionHandling mHandling; + wifi_iface_state mWiFiState; + wifi_iface_state mCachedWiFiState; + net_mgmt_event_callback mWiFiMgmtClbk{}; + 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 }; + + static const Map sStatusMap; + static const Map sEventHandlerMap; +}; + +} // namespace DeviceLayer +} // namespace chip