diff --git a/.github/workflows/chef.yaml b/.github/workflows/chef.yaml index e45a60542d2e..3c286834ffad 100644 --- a/.github/workflows/chef.yaml +++ b/.github/workflows/chef.yaml @@ -177,9 +177,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 1f4804a19fe7ba792bfc315948043d5515e74cd2" + - 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 0858e43f05a91d84ab2fed77f3849ff589510b24" - name: CI Examples Telink shell: bash run: | diff --git a/.github/workflows/examples-telink.yaml b/.github/workflows/examples-telink.yaml index 0528cf45c1be..1c57b8a4c6df 100644 --- a/.github/workflows/examples-telink.yaml +++ b/.github/workflows/examples-telink.yaml @@ -93,8 +93,9 @@ jobs: with: gh-context: ${{ toJson(github) }} - # - name: Update Zephyr version 4.1.0 (develop) to specific revision (for developers purpose) - # run: scripts/run_in_build_env.sh "python3 scripts/tools/telink/update_zephyr.py 1f4804a19fe7ba792bfc315948043d5515e74cd2" + - name: Update Zephyr version 4.1.0 (develop) to specific revision (for developers purpose) + run: + scripts/run_in_build_env.sh "python3 scripts/tools/telink/update_zephyr.py https://github.com/telink-semi/zephyr.git 5a8e0e948faa5fbc0edbf1770d9e7a7d27d70999 --sdk-repo https://github.com/telink-semi/tl_ble_sdk_zephyr.git --sdk-hash f50d422d780efb73af939650ef7b8c6bf5a0b99b" - name: Download Factory Data tools uses: actions/download-artifact@v8 @@ -220,6 +221,32 @@ jobs: - name: clean out build output (keep tools) run: rm -rf ./out/telink* + - name: Build example Telink (tl323x) Lighting App with Factory Data, 4Mb flash + # Run test for master and all PRs + run: | + ./scripts/run_in_build_env.sh \ + "./scripts/build/build_examples.py --target 'telink-tl3238x-light-ota-factory-data-4mb' build" + .environment/pigweed-venv/bin/python3 scripts/tools/memory/gh_sizes.py \ + telink tl3238x light-app-ota-factory-data-4mb \ + out/telink-tl3238x-light-ota-factory-data-4mb/zephyr/zephyr.elf \ + /tmp/bloat_reports/ + + - name: clean out build output (keep tools) + run: rm -rf ./out/telink* + + - name: Build example Telink (tl323x_retention) Light Switch 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-tl3238x_retention-light-switch-ota-compress-lzma-factory-data' build" + .environment/pigweed-venv/bin/python3 scripts/tools/memory/gh_sizes.py \ + telink tl3238x_retention light-switch-app-ota-compress-lzma-factory-data \ + out/telink-tl3238x_retention-light-switch-ota-compress-lzma-factory-data/zephyr/zephyr.elf \ + /tmp/bloat_reports/ + + - name: clean out build output (keep tools) + run: rm -rf ./out/telink* + - name: Build example Telink (tl721x retention) Light Switch App with OTA (LZMA), Factory Data # Run test for master and all PRs run: | diff --git a/config/telink/app/bootloader.conf b/config/telink/app/bootloader.conf index cdc60a11c7a8..dd411b0a1a9b 100644 --- a/config/telink/app/bootloader.conf +++ b/config/telink/app/bootloader.conf @@ -16,7 +16,7 @@ # Enable this option in case if restoring the slot0 partition is expected from slot1 # partition in case if slot0 is not bootable or damaged -CONFIG_BOOT_BOOTSTRAP=y +CONFIG_BOOT_BOOTSTRAP=n # Enable this option in case if SWAP_MOVE logic need to be used CONFIG_BOOT_SWAP_USING_MOVE=y @@ -27,7 +27,8 @@ CONFIG_BOOT_SWAP_USING_SCRATCH=n # Enable this option in case if the whole slot0 image need to be validated # With disabled option the only image magic is validated -CONFIG_BOOT_VALIDATE_SLOT0=y +CONFIG_BOOT_VALIDATE_SLOT0=n +CONFIG_BOOT_VALIDATE_SLOT0_ONCE=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. @@ -42,4 +43,10 @@ CONFIG_BOOT_MAX_IMG_SECTORS=512 # - DEBUG, LOG_LEVEL_DBG CONFIG_MCUBOOT_LOG_LEVEL_INF=y +# Enable support LZMA compression +CONFIG_COMPRESS_LZMA=n + CONFIG_PICOLIBC=y + +# Disabling the banner is to allow cmd tool to better parse the chip ID, avoid interface. +CONFIG_BOOT_BANNER=n diff --git a/config/telink/app/bootloader_compress_lzma.conf b/config/telink/app/bootloader_compress_lzma.conf index 6cab83229bae..4c0e060715d5 100644 --- a/config/telink/app/bootloader_compress_lzma.conf +++ b/config/telink/app/bootloader_compress_lzma.conf @@ -20,7 +20,8 @@ CONFIG_BOOT_UPGRADE_ONLY=y # Enable this option in case if the whole slot0 image need to be validated # With disabled option the only image magic is validated -CONFIG_BOOT_VALIDATE_SLOT0=y +CONFIG_BOOT_VALIDATE_SLOT0=n +CONFIG_BOOT_VALIDATE_SLOT0_ONCE=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. @@ -45,3 +46,6 @@ CONFIG_SIZE_OPTIMIZATIONS=y CONFIG_COMPRESS_LZMA=y CONFIG_PICOLIBC=y + +# ### Disabling the banner is to allow cmd tool to better parse the chip ID, avoid interface. +CONFIG_BOOT_BANNER=n \ No newline at end of file diff --git a/config/telink/chip-module/CMakeLists.txt b/config/telink/chip-module/CMakeLists.txt index 4c64611023c5..f913a23cf34a 100644 --- a/config/telink/chip-module/CMakeLists.txt +++ b/config/telink/chip-module/CMakeLists.txt @@ -72,6 +72,9 @@ matter_add_flags(-DMBEDTLS_USER_CONFIG_FILE=<${CONFIG_MBEDTLS_CFG_FILE}>) if (CONFIG_OPENTHREAD_TELINK_LIBRARY) set(ZEPHYR_OPENTHREAD_MODULE_DIR ${ZEPHYR_BASE}/../modules/lib/openthread_telink_lib CACHE STRING INTERNAL) message(STATUS "OpenThread used as precompiled Telink library, path: ${ZEPHYR_OPENTHREAD_MODULE_DIR}") +else() + set(ZEPHYR_OPENTHREAD_MODULE_DIR ${ZEPHYR_BASE}/../modules/lib/openthread CACHE STRING INTERNAL) + message(STATUS "OpenThread used from source, path: ${ZEPHYR_OPENTHREAD_MODULE_DIR}") endif() if (CONFIG_CHIP_OPENTHREAD_CONFIG) diff --git a/config/telink/chip-module/Kconfig b/config/telink/chip-module/Kconfig index 50376e2c8842..a83041b82221 100644 --- a/config/telink/chip-module/Kconfig +++ b/config/telink/chip-module/Kconfig @@ -84,6 +84,9 @@ config CHIP_DFU_OVER_BT_SMP select MCUMGR_MGMT_NOTIFICATION_HOOKS select MCUMGR_GRP_IMG_UPLOAD_CHECK_HOOK select MCUMGR_GRP_IMG_STATUS_HOOKS + default y if BOARD_TL3238X || BOARD_TL3238X_RETENTION + default y if BOARD_TLSR9528A || BOARD_TLSR9528A_RETENTION + default n help Enables Device Firmware Upgrade over Bluetooth LE with SMP and configures the set of options related to that feature. @@ -249,8 +252,9 @@ config CHIP_USE_MARS_SENSOR default n config CHIP_PERSISTENT_SUBSCRIPTIONS - default n if BOARD_TL7218X_RETENTION - default y + default y if BOARD_TL3238X || BOARD_TL3238X_RETENTION + default y if BOARD_TLSR9528A || BOARD_TLSR9528A_RETENTION + default n # selecting experimental for this feature since there is an issue with multiple controllers. select EXPERIMENTAL diff --git a/config/telink/chip-module/Kconfig.defaults b/config/telink/chip-module/Kconfig.defaults index 30cc1df1ab2c..afa23a13d208 100644 --- a/config/telink/chip-module/Kconfig.defaults +++ b/config/telink/chip-module/Kconfig.defaults @@ -61,28 +61,29 @@ config POSIX_MAX_FDS # Application stack size config MAIN_STACK_SIZE - default 4864 if SOC_RISCV_TELINK_W91 || SOC_RISCV_TELINK_B91 - default 3560 if PM + default 4864 if SOC_RISCV_TELINK_W91 + default 3240 if PM || SOC_RISCV_TELINK_TL323X default 4096 config INIT_STACKS default y config IDLE_STACK_SIZE - default 560 if PM || SOC_RISCV_TELINK_W91 + default 560 if PM || SOC_RISCV_TELINK_TL323X || SOC_RISCV_TELINK_W91 config ISR_STACK_SIZE default 800 if PM || SOC_RISCV_TELINK_W91 + default 1024 if SOC_RISCV_TELINK_TL323X config SYSTEM_WORKQUEUE_STACK_SIZE - default 1024 if PM && !SOC_RISCV_TELINK_W91 + default 664 if PM && !SOC_RISCV_TELINK_W91 + default 2048 if SOC_RISCV_TELINK_TL323X config HEAP_MEM_POOL_SIZE - default 1796 if SOC_RISCV_TELINK_TL721X + default 1796 if SOC_RISCV_TELINK_TL323X || SOC_RISCV_TELINK_TL721X default 1280 config COMMON_LIBC_MALLOC_ARENA_SIZE - default 26584 if SOC_RISCV_TELINK_TL721X default 29448 if SOC_RISCV_TELINK_W91 default 20716 @@ -94,6 +95,7 @@ config NET_IF_UNICAST_IPV6_ADDR_COUNT default 6 config NET_IF_MCAST_IPV6_ADDR_COUNT + default 47 if SOC_RISCV_TELINK_TL323X default 8 if PM default 15 @@ -103,20 +105,20 @@ config NET_IF_IPV6_PREFIX_COUNT # Network buffers config NET_PKT_RX_COUNT default 16 if SOC_RISCV_TELINK_W91 - default 4 if PM + default 4 if PM || SOC_RISCV_TELINK_TL323X default 8 config NET_PKT_TX_COUNT default 16 if SOC_RISCV_TELINK_W91 - default 4 if PM + default 4 if PM || SOC_RISCV_TELINK_TL323X default 8 config NET_BUF_RX_COUNT - default 12 if PM && !SOC_RISCV_TELINK_W91 + default 12 if (PM && !SOC_RISCV_TELINK_W91) || SOC_RISCV_TELINK_TL323X default 32 config NET_BUF_TX_COUNT - default 12 if PM && !SOC_RISCV_TELINK_W91 + default 12 if (PM && !SOC_RISCV_TELINK_W91) || SOC_RISCV_TELINK_TL323X default 32 config GPIO @@ -164,12 +166,15 @@ config BT_MAX_CONN default 1 config BT_L2CAP_TX_MTU + default 103 if SOC_RISCV_TELINK_TL323X default 247 config BT_BUF_ACL_RX_SIZE + default 107 if SOC_RISCV_TELINK_TL323X default 251 config BT_BUF_ACL_TX_SIZE + default 107 if SOC_RISCV_TELINK_TL323X default 251 config BT_BUF_EVT_RX_COUNT @@ -196,6 +201,7 @@ if SOC_RISCV_TELINK_B9X || SOC_RISCV_TELINK_TLX config TL_BLE_CTRL_THREAD_STACK_SIZE default 1536 if SOC_RISCV_TELINK_TL721X + default 1536 if SOC_RISCV_TELINK_TL323X default 712 config TL_BLE_CTRL_MASTER_MAX_NUM @@ -229,12 +235,12 @@ config PWM endif -if BOARD_TL7218X_RETENTION +if BOARD_TL7218X_RETENTION || BOARD_TL3238X_RETENTION || BOARD_TL3238X config SOC_SERIES_RISCV_TELINK_TLX_NON_RETENTION_RAM_CODE default n if PM config TELINK_TLX_MATTER_RETENTION_LAYOUT - default y if PM + default y if PM || BOARD_TL3238X config PWM default n if PM @@ -243,7 +249,7 @@ endif # Board non-retention config if BOARD_TLSR9118BDK40D || BOARD_TLSR9118BDK40D_V1 || \ - BOARD_TLSR9528A || BOARD_TLSR9518ADK80D || BOARD_TL7218X + BOARD_TLSR9528A || BOARD_TLSR9518ADK80D || BOARD_TL3238X || BOARD_TL7218X config PWM default y endif @@ -290,6 +296,7 @@ config NVS_LOOKUP_CACHE default y config NVS_LOOKUP_CACHE_SIZE + default 400 if SOC_RISCV_TELINK_TL323X default 2048 if !PM || SOC_RISCV_TELINK_W91 # Set multiplicator of Name Value Storage (NVS) as 1 to reach NVS sector size 4KB @@ -341,14 +348,18 @@ config OPENTHREAD_CSMABACKOFF_OPTIMIZATION config IEEE802154_TLX_OPTIMIZATION bool "Optimize 802.15.4 RF performance for TLX SoCs" + default y if PM && SOC_RISCV_TELINK_TL323X default n help Improve RF performance in IEEE 802.15.4 communication on TLX SoCs. config OPENTHREAD_THREAD_STACK_SIZE default 2400 if PM + default 4096 if SOC_RISCV_TELINK_TL323X config OPENTHREAD_RADIO_WORKQUEUE_STACK_SIZE + default 2048 if PM && SOC_RISCV_TELINK_TL323X + default 608 if SOC_RISCV_TELINK_TL323X default 640 config OPENTHREAD_SLAAC @@ -357,11 +368,15 @@ config OPENTHREAD_SLAAC config OPENTHREAD_MANUAL_START default y +# 6 is the max rf level by Vant config OPENTHREAD_DEFAULT_TX_POWER - default 3 if PM + default 6 if PM && SOC_RISCV_TELINK_TL721X + default 3 if PM && SOC_RISCV_TELINK_TL323X + default 11 if SOC_RISCV_TELINK_TL323X default 9 config OPENTHREAD_IP6_MAX_EXT_MCAST_ADDRS + default 40 if SOC_RISCV_TELINK_TL323X default 2 if PM default 8 @@ -375,11 +390,12 @@ endif # NET_L2_OPENTHREAD config NET_TX_STACK_SIZE default 1200 if SOC_RISCV_TELINK_W91 - default 600 if PM + default 600 if PM || SOC_RISCV_TELINK_TL323X config NET_RX_STACK_SIZE default 2048 if SOC_RISCV_TELINK_W91 default 664 if PM + default 2048 if SOC_RISCV_TELINK_TL323X # Disable certain parts of Zephyr IPv6 stack config NET_IPV6_NBR_CACHE diff --git a/examples/contact-sensor-app/telink/CMakeLists.txt b/examples/contact-sensor-app/telink/CMakeLists.txt index 68c69a4aa340..20e73e34afe8 100755 --- a/examples/contact-sensor-app/telink/CMakeLists.txt +++ b/examples/contact-sensor-app/telink/CMakeLists.txt @@ -63,3 +63,8 @@ if(CONFIG_OPENTHREAD_SNTP_CLIENT) target_sources(app PRIVATE ${TELINK_COMMON}/util/src/ThreadTimeSync.cpp) endif() + +if(CONFIG_MCUMGR_TRANSPORT_BT) + zephyr_library_link_libraries(MCUBOOT_BOOTUTIL) + target_sources(app PRIVATE ${TELINK_COMMON}/util/src/DFUOverSMP.cpp) +endif() diff --git a/examples/contact-sensor-app/telink/boards/tl3238x_retention.conf b/examples/contact-sensor-app/telink/boards/tl3238x_retention.conf new file mode 100644 index 000000000000..d41f37a61a36 --- /dev/null +++ b/examples/contact-sensor-app/telink/boards/tl3238x_retention.conf @@ -0,0 +1,17 @@ +# Disable Matter OTA DFU +CONFIG_CHIP_OTA_REQUESTOR=y +CONFIG_CHIP_DFU_OVER_BT_SMP=y +CONFIG_CHIP_DFU_OVER_BT_SMP_BUILD=y + +# Enable ADC +CONFIG_ADC=y + +#compress ota +CONFIG_COMPRESS_LZMA=y +CONFIG_LZMA=y + +#matter switch mode +# config NORMAL_MODE 0 independent matter mode +# ACTION_DUAL_MODE 1 matter action switch mode ,such as by button +# AUTO_SWITCH_DUAL_MODE 2 matter auto-switch mode ,such as by scan QR code. +CONFIG_DUAL_MODE=1 diff --git a/examples/contact-sensor-app/telink/prj.conf b/examples/contact-sensor-app/telink/prj.conf index cfac50c71874..9b19e5400117 100755 --- a/examples/contact-sensor-app/telink/prj.conf +++ b/examples/contact-sensor-app/telink/prj.conf @@ -30,6 +30,9 @@ CONFIG_BT_DEVICE_NAME="TelinkSensor" # Disable Matter OTA DFU CONFIG_CHIP_OTA_REQUESTOR=n +CONFIG_CHIP_DFU_OVER_BT_SMP=n +CONFIG_CHIP_DFU_OVER_BT_SMP_BUILD=n + CONFIG_CHIP_DEVICE_SOFTWARE_VERSION=1 # Disable CHIP shell support @@ -39,6 +42,13 @@ CONFIG_CHIP_LIB_SHELL=n CONFIG_CHIP_FACTORY_DATA=n CONFIG_CHIP_FACTORY_DATA_BUILD=n CONFIG_CHIP_FACTORY_DATA_MERGE_WITH_FIRMWARE=n - +CONFIG_CHIP_CERTIFICATION_DECLARATION_STORAGE=n +# CONFIG_CHIP_DEVICE_DISCRIMINATOR=0xF00 # Enable Power Management CONFIG_PM=y + +#ICD SETTING INFO +CONFIG_CHIP_ICD_SLOW_POLL_INTERVAL=7500 +CONFIG_CHIP_ICD_FAST_POLLING_INTERVAL=200 +CONFIG_CHIP_ICD_ACTIVE_MODE_THRESHOLD=300 +CONFIG_CHIP_ICD_IDLE_MODE_DURATION=900 diff --git a/examples/light-switch-app/telink/boards/tl3238x_retention.conf b/examples/light-switch-app/telink/boards/tl3238x_retention.conf new file mode 100644 index 000000000000..2568dbd6c4f7 --- /dev/null +++ b/examples/light-switch-app/telink/boards/tl3238x_retention.conf @@ -0,0 +1,17 @@ +# Disable Matter OTA DFU +CONFIG_CHIP_OTA_REQUESTOR=y +CONFIG_CHIP_DFU_OVER_BT_SMP=y +CONFIG_CHIP_DFU_OVER_BT_SMP_BUILD=y + +# Enable ADC +CONFIG_ADC=y + +#compress ota +CONFIG_LZMA=y +CONFIG_COMPRESS_LZMA=y + +#matter switch mode +# config NORMAL_MODE 0 independent matter mode +# ACTION_DUAL_MODE 1 matter action switch mode ,such as by button +# AUTO_SWITCH_DUAL_MODE 2 matter auto-switch mode ,such as by scan QR code. +CONFIG_DUAL_MODE=1 diff --git a/examples/light-switch-app/telink/prj.conf b/examples/light-switch-app/telink/prj.conf index eb9ba62ea623..481577d08902 100755 --- a/examples/light-switch-app/telink/prj.conf +++ b/examples/light-switch-app/telink/prj.conf @@ -42,6 +42,12 @@ CONFIG_CHIP_FACTORY_DATA=n CONFIG_CHIP_FACTORY_DATA_BUILD=n CONFIG_CHIP_FACTORY_DATA_MERGE_WITH_FIRMWARE=n CONFIG_CHIP_CERTIFICATION_DECLARATION_STORAGE=n - +# CONFIG_CHIP_DEVICE_DISCRIMINATOR=0xF00 # Enable Power Management CONFIG_PM=y + +#ICD SETTING INFO +CONFIG_CHIP_ICD_SLOW_POLL_INTERVAL=7500 +CONFIG_CHIP_ICD_FAST_POLLING_INTERVAL=200 +CONFIG_CHIP_ICD_ACTIVE_MODE_THRESHOLD=300 +CONFIG_CHIP_ICD_IDLE_MODE_DURATION=900 diff --git a/examples/lighting-app/telink/boards/tl3238x.conf b/examples/lighting-app/telink/boards/tl3238x.conf new file mode 100644 index 000000000000..566b8844ee34 --- /dev/null +++ b/examples/lighting-app/telink/boards/tl3238x.conf @@ -0,0 +1,17 @@ +# Disable Matter OTA DFU +CONFIG_CHIP_OTA_REQUESTOR=y +CONFIG_CHIP_DFU_OVER_BT_SMP=y +CONFIG_CHIP_DFU_OVER_BT_SMP_BUILD=y + +#compress ota +CONFIG_LZMA=n +CONFIG_COMPRESS_LZMA=n + +# Enable ADC +CONFIG_ADC=y + +# matter switch mode +# config NORMAL_MODE 0 independent matter mode +# ACTION_DUAL_MODE 1 matter action switch mode ,such as by button +# AUTO_SWITCH_DUAL_MODE 2 matter auto-switch mode ,such as by scan QR code.should use 4m flash +CONFIG_DUAL_MODE=2 diff --git a/examples/lighting-app/telink/prj.conf b/examples/lighting-app/telink/prj.conf index d54ab61d181d..8e3188030ac2 100644 --- a/examples/lighting-app/telink/prj.conf +++ b/examples/lighting-app/telink/prj.conf @@ -46,6 +46,9 @@ CONFIG_CHIP_CERTIFICATION_DECLARATION_STORAGE=n # Enable PWM CONFIG_PWM=y - +CONFIG_CHIP_DEVICE_DISCRIMINATOR=0xF00 # Enable Power Management CONFIG_PM=n + +# Enable Independent Button +CONFIG_CHIP_BUTTON_MANAGER_IRQ_MODE=n diff --git a/examples/lock-app/telink/boards/tl3238x_retention.conf b/examples/lock-app/telink/boards/tl3238x_retention.conf new file mode 100644 index 000000000000..73253ec165ec --- /dev/null +++ b/examples/lock-app/telink/boards/tl3238x_retention.conf @@ -0,0 +1,9 @@ +#compress ota +CONFIG_LZMA=y +CONFIG_COMPRESS_LZMA=y + +#matter switch mode +# config NORMAL_MODE 0 independent matter mode +# ACTION_DUAL_MODE 1 matter action switch mode ,such as by button +# AUTO_SWITCH_DUAL_MODE 2 matter auto-switch mode ,such as by scan QR code. +CONFIG_DUAL_MODE=1 diff --git a/examples/lock-app/telink/prj.conf b/examples/lock-app/telink/prj.conf index 0180d1724522..d6771051592a 100755 --- a/examples/lock-app/telink/prj.conf +++ b/examples/lock-app/telink/prj.conf @@ -42,3 +42,9 @@ CONFIG_CHIP_FACTORY_DATA_MERGE_WITH_FIRMWARE=n # Enable Power Management CONFIG_PM=y + +#ICD SETTING INFO +CONFIG_CHIP_ICD_SLOW_POLL_INTERVAL=7500 +CONFIG_CHIP_ICD_FAST_POLLING_INTERVAL=200 +CONFIG_CHIP_ICD_ACTIVE_MODE_THRESHOLD=300 +CONFIG_CHIP_ICD_IDLE_MODE_DURATION=900 diff --git a/examples/platform/telink/build_info.cmake b/examples/platform/telink/build_info.cmake index f6a7ccfaee34..32c695db51f4 100644 --- a/examples/platform/telink/build_info.cmake +++ b/examples/platform/telink/build_info.cmake @@ -109,12 +109,66 @@ else() set(TELINK_HAL_LOCAL_STATUS "") endif() +# OpenThread info +if(CONFIG_OPENTHREAD_TELINK_LIBRARY) + set(OPENTHREAD_PATH "${ZEPHYR_BASE}/../modules/lib/openthread_telink_lib") +else() + set(OPENTHREAD_PATH "${ZEPHYR_BASE}/../modules/lib/openthread") +endif() + +execute_process( + COMMAND git -C ${OPENTHREAD_PATH} rev-parse HEAD + OUTPUT_VARIABLE OT_COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET +) + +execute_process( + COMMAND git -C ${OPENTHREAD_PATH} rev-parse --abbrev-ref HEAD + OUTPUT_VARIABLE OT_BRANCH_NAME + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET +) + +execute_process( + COMMAND git -C ${OPENTHREAD_PATH} show -s --format=%cd --date=format:%Y-%m-%d HEAD + OUTPUT_VARIABLE OT_COMMIT_DATE + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET +) + +execute_process( + COMMAND bash -c "git -C ${OPENTHREAD_PATH} remote get-url \$(git -C ${OPENTHREAD_PATH} remote | head -n 1)" + OUTPUT_VARIABLE OT_REMOTE_URL + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET +) + +execute_process( + COMMAND git -C ${OPENTHREAD_PATH} diff --quiet + RESULT_VARIABLE OT_LOCAL_STATUS + ERROR_QUIET +) + +if(OT_LOCAL_STATUS) + set(OT_LOCAL_STATUS "-dirty") +else() + set(OT_LOCAL_STATUS "") +endif() + execute_process( COMMAND git -C ${ZEPHYR_BASE}/../modules/hal/telink show -s --format=%cd --date=format:%Y-%m-%d OUTPUT_VARIABLE TELINK_HAL_COMMIT_DATE OUTPUT_STRIP_TRAILING_WHITESPACE ) +execute_process( + COMMAND git -C ${OPENTHREAD_PATH} describe --tags --exact-match + OUTPUT_VARIABLE OT_TAG + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET +) + target_compile_definitions(app PRIVATE MATTER_COMMIT_HASH="${MATTER_COMMIT_HASH}" MATTER_BRANCH="${MATTER_BRANCH_NAME}" @@ -132,6 +186,14 @@ target_compile_definitions(app PRIVATE TELINK_HAL_COMMIT_HASH="${TELINK_HAL_COMMIT_HASH}" TELINK_HAL_LOCAL_STATUS="${TELINK_HAL_LOCAL_STATUS}" TELINK_HAL_COMMIT_DATE="${TELINK_HAL_COMMIT_DATE}" + + OPENTHREAD_PATH="${OPENTHREAD_PATH}" + OT_COMMIT_HASH="${OT_COMMIT_HASH}" + OT_BRANCH="${OT_BRANCH_NAME}" + OT_COMMIT_DATE="${OT_COMMIT_DATE}" + OT_REMOTE_URL="${OT_REMOTE_URL}" + OT_LOCAL_STATUS="${OT_LOCAL_STATUS}" + OT_TAG="${OT_TAG}" ) message(STATUS "Matter revision:") @@ -146,3 +208,11 @@ message(STATUS " remote: ${ZEPHYR_REMOTE_URL}") message(STATUS "HAL revision:") message(STATUS " commit: ${TELINK_HAL_COMMIT_HASH} ${TELINK_HAL_COMMIT_DATE}") + +message(STATUS "OpenThread revision:") +message(STATUS " path: ${OPENTHREAD_PATH}") +message(STATUS " branch: ${OT_BRANCH_NAME} ${OT_COMMIT_HASH} ${OT_COMMIT_DATE}") +if(OT_TAG) + message(STATUS " tag: ${OT_TAG}") +endif() +message(STATUS " remote: ${OT_REMOTE_URL}") diff --git a/examples/platform/telink/common.cmake b/examples/platform/telink/common.cmake index f42a935bf3d2..75e90560a153 100644 --- a/examples/platform/telink/common.cmake +++ b/examples/platform/telink/common.cmake @@ -100,9 +100,11 @@ else() unset(USB_CONF_OVERLAY_FILE) endif() +set(FIRMWARE_COMPRESS_LZMA "") if(${CONFIG_COMPRESS_LZMA} MATCHES y) set(BOOT_CONF_OVERLAY_FILE "${CHIP_ROOT}/config/telink/app/bootloader_compress_lzma.conf") set(FLASH_LAYOUT_SUFFIX "_lzma") + set(FIRMWARE_COMPRESS_LZMA "lzma_") else() set(BOOT_CONF_OVERLAY_FILE "${CHIP_ROOT}/config/telink/app/bootloader.conf") set(FLASH_LAYOUT_SUFFIX "") @@ -128,6 +130,12 @@ if(NOT EXISTS "${LOCAL_DTC_OVERLAY_FILE}") unset(LOCAL_DTC_OVERLAY_FILE) endif() +set(LOCAL_BOARD_CONF_FILE "${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD}.conf") +if(NOT EXISTS "${LOCAL_BOARD_CONF_FILE}") + message(STATUS "${LOCAL_BOARD_CONF_FILE} doesn't exist") + unset(LOCAL_BOARD_CONF_FILE) +endif() + set(GLOBAL_BOOT_DTC_OVERLAY_FILE "${CHIP_ROOT}/src/platform/telink/${BASE_BOARD}.overlay") if(NOT EXISTS "${GLOBAL_BOOT_DTC_OVERLAY_FILE}") message(STATUS "${GLOBAL_BOOT_DTC_OVERLAY_FILE} doesn't exist") @@ -140,7 +148,11 @@ if(NOT EXISTS "${GLOBAL_DTC_OVERLAY_FILE}") unset(GLOBAL_DTC_OVERLAY_FILE) endif() -set(FLASH_DTC_OVERLAY_FILE "${CHIP_ROOT}/src/platform/telink/${BASE_BOARD}_${FLASH_SIZE}_flash${FLASH_LAYOUT_SUFFIX}.overlay") +if(${BASE_BOARD} MATCHES "tl3238x") + set(FLASH_DTC_OVERLAY_FILE "${CHIP_ROOT}/src/platform/telink/${BASE_BOARD}_${FLASH_SIZE}_${FIRMWARE_COMPRESS_LZMA}flash.overlay") +else() + set(FLASH_DTC_OVERLAY_FILE "${CHIP_ROOT}/src/platform/telink/${BASE_BOARD}_${FLASH_SIZE}_flash${FLASH_LAYOUT_SUFFIX}.overlay") +endif() if(NOT EXISTS "${FLASH_DTC_OVERLAY_FILE}") message(STATUS "${FLASH_DTC_OVERLAY_FILE} doesn't exist") unset(FLASH_DTC_OVERLAY_FILE) @@ -156,7 +168,7 @@ else() endif() if(NOT CONF_FILE) - set(CONF_FILE ${USB_CONF_OVERLAY_FILE} ${MARS_CONF_OVERLAY_FILE} prj.conf ${TFLM_CONF_OVERLAY_FILE} ${ML_CONF_OVERLAY_FILE}) + set(CONF_FILE ${USB_CONF_OVERLAY_FILE} ${MARS_CONF_OVERLAY_FILE} ${ZEPHYR_VERSION_OVERLAY_FILE} prj.conf ${TFLM_CONF_OVERLAY_FILE} ${ML_CONF_OVERLAY_FILE} ${LOCAL_BOARD_CONF_FILE}) endif() # Load NCS/Zephyr build system diff --git a/examples/platform/telink/common/include/AppTaskCommon.h b/examples/platform/telink/common/include/AppTaskCommon.h index 5ae561e522dc..71357e3274a7 100644 --- a/examples/platform/telink/common/include/AppTaskCommon.h +++ b/examples/platform/telink/common/include/AppTaskCommon.h @@ -143,6 +143,8 @@ class AppTaskCommon static void UpdateStatusLED(void); + static void OtaEventsHandler(const ChipDeviceEvent * event); + #if CONFIG_CHIP_FACTORY_DATA chip::DeviceLayer::FactoryDataProvider mFactoryDataProvider; #endif diff --git a/examples/platform/telink/common/src/AppTaskCommon.cpp b/examples/platform/telink/common/src/AppTaskCommon.cpp index c5de0742a2f0..de19306bdf75 100644 --- a/examples/platform/telink/common/src/AppTaskCommon.cpp +++ b/examples/platform/telink/common/src/AppTaskCommon.cpp @@ -99,6 +99,9 @@ bool sIsNetworkProvisioned = false; bool sIsNetworkEnabled = false; bool sIsNetworkAttached = false; bool sHaveBLEConnections = false; +#if defined CONFIG_IEEE802154_TLX_OPTIMIZATION +bool isThreadCommissioned = false; +#endif /* CONFIG_IEEE802154_TLX_OPTIMIZATION */ chip::DeviceLayer::DeviceInfoProviderImpl gExampleDeviceInfoProvider; @@ -266,6 +269,15 @@ void AppTaskCommon::PrintFirmwareInfo(void) LOG_DBG("\t branch: %s %.8s%s %s", ZEPHYR_BRANCH, ZEPHYR_COMMIT_HASH, ZEPHYR_LOCAL_STATUS, ZEPHYR_COMMIT_DATE); LOG_DBG("\t remote: %s", ZEPHYR_REMOTE_URL); LOG_DBG("\t HAL commit: %.8s%s %s", TELINK_HAL_COMMIT_HASH, TELINK_HAL_LOCAL_STATUS, TELINK_HAL_COMMIT_DATE); + + LOG_DBG("OpenThread revision: "); + LOG_DBG("\t path: %s", OPENTHREAD_PATH); + LOG_DBG("\t remote: %s", OT_REMOTE_URL); + if (strlen(OT_TAG) > 0) + { + LOG_DBG("\t tag: %s", OT_TAG); + } + LOG_DBG("\t branch: %s %.8s%s %s", OT_BRANCH, OT_COMMIT_HASH, OT_LOCAL_STATUS, OT_COMMIT_DATE); #endif } @@ -780,6 +792,36 @@ void AppTaskCommon::TriggerMicroSpeechEventHandler(AppEvent * aEvent) } #endif +void AppTaskCommon::OtaEventsHandler(const ChipDeviceEvent * event) +{ + switch (event->OtaStateChanged.newState) + { + case DeviceLayer::kOtaDownloadInProgress: + ChipLogProgress(DeviceLayer, "OTA image download in progress\n"); + break; + case DeviceLayer::kOtaDownloadComplete: + ChipLogProgress(DeviceLayer, "OTA image download complete\n"); + break; + case DeviceLayer::kOtaDownloadFailed: + ChipLogProgress(DeviceLayer, "OTA image download failed\n"); + break; + case DeviceLayer::kOtaDownloadAborted: + ChipLogProgress(DeviceLayer, "OTA image download aborted\n"); + break; + case DeviceLayer::kOtaApplyInProgress: + ChipLogProgress(DeviceLayer, "OTA image apply in progress\n"); + break; + case DeviceLayer::kOtaApplyComplete: + ChipLogProgress(DeviceLayer, "OTA image apply complete\n"); + break; + case DeviceLayer::kOtaApplyFailed: + ChipLogProgress(DeviceLayer, "OTA image apply failed\n"); + break; + default: + break; + } +} + void AppTaskCommon::ChipEventHandler(const ChipDeviceEvent * event, intptr_t /* arg */) { switch (event->Type) @@ -889,7 +931,7 @@ void AppTaskCommon::ChipEventHandler(const ChipDeviceEvent * event, intptr_t /* #endif sIsNetworkProvisioned = ConnectivityMgr().IsThreadProvisioned(); sIsNetworkEnabled = ConnectivityMgr().IsThreadEnabled(); - sIsNetworkAttached = ConnectivityMgr().IsThreadAttached(); + sIsNetworkAttached = ConnectivityMgr().sIsNetworkAttached(); #ifdef CONFIG_TFLM_FEATURE if (sIsNetworkProvisioned && sIsNetworkAttached) { @@ -905,6 +947,14 @@ void AppTaskCommon::ChipEventHandler(const ChipDeviceEvent * event, intptr_t /* } } #endif +#if defined CONFIG_IEEE802154_TLX_OPTIMIZATION + + if (sIsNetworkAttached && curRole != OT_DEVICE_ROLE_DISABLED && curRole != OT_DEVICE_ROLE_DETACHED) + { + if (isThreadCommissioned == false) + isThreadCommissioned = true; + } +#endif /* CONFIG_IEEE802154_TLX_OPTIMIZATION */ #elif CHIP_DEVICE_CONFIG_ENABLE_WIFI case DeviceEventType::kWiFiConnectivityChange: @@ -930,6 +980,9 @@ void AppTaskCommon::ChipEventHandler(const ChipDeviceEvent * event, intptr_t /* UpdateStatusLED(); #endif break; + case DeviceEventType::kOtaStateChanged: + AppTaskCommon::OtaEventsHandler(event); + break; default: break; } diff --git a/examples/platform/telink/project_include/OpenThreadConfig.h b/examples/platform/telink/project_include/OpenThreadConfig.h index eabe238820ae..8e4805491411 100644 --- a/examples/platform/telink/project_include/OpenThreadConfig.h +++ b/examples/platform/telink/project_include/OpenThreadConfig.h @@ -27,12 +27,17 @@ /* Number of message buffers reduced to save RAM */ #undef OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS -#if CONFIG_PM +#if CONFIG_PM || CONFIG_SOC_RISCV_TELINK_TL323X #define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS 22 #else #define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS 44 #endif +#if CONFIG_SOC_RISCV_TELINK_TL323X +#undef OPENTHREAD_CONFIG_MLE_MAX_CHILDREN +#define OPENTHREAD_CONFIG_MLE_MAX_CHILDREN 5 +#endif + #define OPENTHREAD_CONFIG_LOG_OUTPUT OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART #undef OPENTHREAD_CONFIG_LOG_LEVEL diff --git a/scripts/build/build/targets.py b/scripts/build/build/targets.py index 007237bfc248..0a5d4f66c28f 100644 --- a/scripts/build/build/targets.py +++ b/scripts/build/build/targets.py @@ -848,6 +848,9 @@ def BuildTelinkTarget(): TargetPart('tlsr9518adk80d', board=TelinkBoard.TLSR9518ADK80D), TargetPart('tlsr9528a', board=TelinkBoard.TLSR9528A), TargetPart('tlsr9528a_retention', board=TelinkBoard.TLSR9528A_RETENTION), + TargetPart('tl3238x', board=TelinkBoard.TL3238X), + TargetPart('tl3238x_ml3m', board=TelinkBoard.TL3238X_ML3M), + TargetPart('tl3238x_retention', board=TelinkBoard.TL3238X_RETENTION), TargetPart('tl7218x', board=TelinkBoard.TL7218X), TargetPart('tl7218x_ml7g', board=TelinkBoard.TL7218X_ML7G), TargetPart('tl7218x_ml7m', board=TelinkBoard.TL7218X_ML7M), diff --git a/scripts/build/builders/telink.py b/scripts/build/builders/telink.py index b8f77fe0ad97..5ef342560431 100644 --- a/scripts/build/builders/telink.py +++ b/scripts/build/builders/telink.py @@ -133,6 +133,9 @@ class TelinkBoard(Enum): TLSR9518ADK80D = auto() TLSR9528A = auto() TLSR9528A_RETENTION = auto() + TL3238X = auto() + TL3238X_ML3M = auto() + TL3238X_RETENTION = auto() TL7218X = auto() TL7218X_ML7G = auto() TL7218X_ML7M = auto() @@ -147,6 +150,12 @@ def GnArgName(self): return 'tlsr9528a' if self == TelinkBoard.TLSR9528A_RETENTION: return 'tlsr9528a_retention' + if self == TelinkBoard.TL3238X: + return 'tl3238x' + if self == TelinkBoard.TL3238X_ML3M: + return 'tl3238x_ml3m' + if self == TelinkBoard.TL3238X_RETENTION: + return 'tl3238x_retention' if self == TelinkBoard.TL7218X: return 'tl7218x' if self == TelinkBoard.TL7218X_ML7G: diff --git a/scripts/build/testdata/all_targets_linux_x64.txt b/scripts/build/testdata/all_targets_linux_x64.txt index 3b9473835a05..85c25623ebdc 100644 --- a/scripts/build/testdata/all_targets_linux_x64.txt +++ b/scripts/build/testdata/all_targets_linux_x64.txt @@ -21,4 +21,4 @@ qpg-qpg6200-{light,light-switch,lock,persistent-storage,shell,thermostat}[-updat realtek-{rtl8777g,rtl87x2g}-{all-clusters,light-switch,lighting,lock,ota-requestor,thermostat,window} stm32-{stm32wb5mm-dk,stm32wba65i-dk1}-light tizen-{arm,arm64}-{all-clusters,chip-tool,light,tests}[-asan][-coverage][-no-ble][-no-thread][-no-wifi][-ubsan][-with-ui] -telink-{tl7218x,tl7218x_ml7g,tl7218x_ml7m,tl7218x_retention,tlsr9118bdk40d,tlsr9518adk80d,tlsr9528a,tlsr9528a_retention}-{air-quality-sensor,all-clusters,all-clusters-minimal,all-devices,all-devices-aggregator,all-devices-air-purifier,all-devices-ambient-context-sensor,all-devices-bridged-node,all-devices-chime,all-devices-contact-sensor,all-devices-cooktop,all-devices-device-energy-management,all-devices-dimmable-light,all-devices-dimmable-plug-in-unit,all-devices-dishwasher,all-devices-electrical-sensor,all-devices-extractor-hood,all-devices-fan,all-devices-flow-sensor,all-devices-generic-switch,all-devices-humidity-sensor,all-devices-laundry-dryer,all-devices-laundry-washer,all-devices-light-sensor,all-devices-microwave-oven,all-devices-mode-select,all-devices-mounted-dimmable-load-control,all-devices-mounted-on-off-control,all-devices-occupancy-sensor,all-devices-on-off-light,all-devices-on-off-light-switch,all-devices-on-off-plug-in-unit,all-devices-oven,all-devices-power-source,all-devices-pressure-sensor,all-devices-proximity-ranger,all-devices-rain-sensor,all-devices-refrigerator,all-devices-robotic-vacuum-cleaner,all-devices-soil-sensor,all-devices-speaker,all-devices-temperature-sensor,all-devices-water-freeze-detector,all-devices-water-leak-detector,all-devices-water-valve,bridge,contact-sensor,light,light-switch,lock,ota-requestor,pump,pump-controller,shell,smoke-co-alarm,temperature-measurement,thermostat,window-covering}[-4mb][-compress-lzma][-concurrent-connection][-dfu-smp][-factory-data][-log-all][-log-error][-log-none][-log-progress][-mars][-nfc-payload][-ota][-precompiled-ot][-rpc][-shell][-tflm][-thread-analyzer][-usb] +telink-{tl3238x,tl3238x_ml3m,tl3238x_retention,tl7218x,tl7218x_ml7g,tl7218x_ml7m,tl7218x_retention,tlsr9118bdk40d,tlsr9518adk80d,tlsr9528a,tlsr9528a_retention}-{air-quality-sensor,all-clusters,all-clusters-minimal,all-devices,all-devices-aggregator,all-devices-air-purifier,all-devices-ambient-context-sensor,all-devices-bridged-node,all-devices-chime,all-devices-contact-sensor,all-devices-cooktop,all-devices-device-energy-management,all-devices-dimmable-light,all-devices-dimmable-plug-in-unit,all-devices-dishwasher,all-devices-electrical-sensor,all-devices-extractor-hood,all-devices-fan,all-devices-flow-sensor,all-devices-generic-switch,all-devices-humidity-sensor,all-devices-laundry-dryer,all-devices-laundry-washer,all-devices-light-sensor,all-devices-microwave-oven,all-devices-mode-select,all-devices-mounted-dimmable-load-control,all-devices-mounted-on-off-control,all-devices-occupancy-sensor,all-devices-on-off-light,all-devices-on-off-light-switch,all-devices-on-off-plug-in-unit,all-devices-oven,all-devices-power-source,all-devices-pressure-sensor,all-devices-proximity-ranger,all-devices-rain-sensor,all-devices-refrigerator,all-devices-robotic-vacuum-cleaner,all-devices-soil-sensor,all-devices-speaker,all-devices-temperature-sensor,all-devices-water-freeze-detector,all-devices-water-leak-detector,all-devices-water-valve,bridge,contact-sensor,light,light-switch,lock,ota-requestor,pump,pump-controller,shell,smoke-co-alarm,temperature-measurement,thermostat,window-covering}[-4mb][-compress-lzma][-concurrent-connection][-dfu][-dfu-smp][-factory-data][-log-all][-log-error][-log-none][-log-progress][-mars][-nfc-payload][-ota][-precompiled-ot][-rpc][-shell][-tflm][-thread-analyzer][-usb] diff --git a/scripts/tools/telink/manual_process.py b/scripts/tools/telink/manual_process.py new file mode 100755 index 000000000000..964453dfb797 --- /dev/null +++ b/scripts/tools/telink/manual_process.py @@ -0,0 +1,701 @@ +#!/usr/bin/env python3 +""" +Matter Telink Manual Firmware Processing Tool +Location: scripts/tools/telink/manual_process.py +Co-located with process_binaries.py for consistent project structure + +This script manually executes the complete firmware processing pipeline, +fully reusing existing project logic. All generated files are prefixed +with 'manual_' to avoid conflicts with automated build outputs. + +=== Usage Examples === + +Usage 1 (process raw firmware, full pipeline): + python3 manual_process.py zephyr.bin [--output-dir OUTPUT_DIR] + Example: + python3 manual_process.py \\ + /home/ubuntu/connectedhomeip/examples/light-switch-app/telink/build_tl3238x_retention_dual_sw_v1/zephyr/zephyr.bin \\ + /home/ubuntu/connectedhomeip/examples/light-switch-app/telink/build_tl3238x_retention_dual_sw_v1/zephyr/.config + +Usage 2 (process already signed firmware, start from compression): + python3 manual_process.py zephyr.signed.bin --already-signed + Example: + python3 manual_process.py \\ + /home/ubuntu/connectedhomeip/examples/light-switch-app/telink/build_tl3238x_retention_dual_sw_v1/zephyr/zephyr.signed.bin \\ + /home/ubuntu/connectedhomeip/examples/light-switch-app/telink/build_tl3238x_retention_dual_sw_v1/zephyr/.config --already-signed + +Usage 3 (with manual parameters, no .config file needed): + python3 manual_process.py [--output-dir OUTPUT_DIR] \\ + --rom-start-offset 512 --flash-load-size 1179648 \\ + --lzma-dict-size 32768 --signing-key bootloader/mcuboot/root-rsa-2048.pem \\ + --vendor-id 65521 --product-id 32772 [--already-signed] + +Usage 4 (only LZMA compression, single step): + python3 manual_process.py input.signed.bin --only-lzma --output-dir ./output --lzma-dict-size 32768 + +Usage 5 (4MB Flash chips: tlsr9528a/tlsr9268j/tlsr9268m, simplified pipeline): + python3 manual_process.py zephyr.bin --chip-family tlsr9528a + This executes only 2 steps: zephyr.bin -> zephyr.signed.bin -> matter.ota (skips LZMA) + +Processing Steps: +1. Input firmware -> manual_zephyr.signed.bin (First MCUBoot signature) +2. manual_zephyr.signed.bin -> manual_zephyr.signed.lzma.bin (LZMA compression) +3. manual_zephyr.signed.lzma.bin -> manual_zephyr.signed.lzma.signed.bin (Second MCUBoot signature) +4. manual_zephyr.signed.lzma.signed.bin -> manual_matter.ota (Final Matter OTA package) + +Manual step-by-step execution (for debugging): +If you want to manually execute each step of the pipeline, here are the exact commands: + +1. First MCUBoot signature (zephyr.bin -> manual_zephyr.signed.bin): + + python3 /home/ubuntu/zephyrproject/bootloader/mcuboot/scripts/imgtool.py sign \ + --version 0.0.0+0 \ + --align 1 \ + --header-size 512 \ + --slot-size 1179648 \ + --key /home/ubuntu/zephyrproject/bootloader/mcuboot/ecdsa_private_key.pem \ + ./zephyr.bin \ + ./output/manual_zephyr.signed.bin + +2. LZMA compression (manual_zephyr.signed.bin -> manual_zephyr.signed.lzma.bin): + +2.1 Manual method using Python directly + + python3 << 'EOF' + import lzma + import os + + input_file = "./output/manual_zephyr.signed.bin" + output_file = "./output/manual_zephyr.signed.lzma.bin" + + dict_size = 32768 + + with open(input_file, "rb") as f: + data = f.read() + + compressor = lzma.LZMACompressor( + format=lzma.FORMAT_RAW, + filters=[{ + "id": lzma.FILTER_LZMA1, + "dict_size": dict_size, + "lc": 1, + "lp": 2, + "pb": 0, + "mode": lzma.MODE_NORMAL, + "mf": lzma.MF_BT4, + "depth": 0 + }] + ) + + compressed = compressor.compress(data) + compressor.flush() + with open(output_file, "wb") as f: + f.write(compressed) + + original = os.path.getsize(input_file) + compressed_size = os.path.getsize(output_file) + print(f"LZMA compression completed: Original {original} Bytes -> Compressed {compressed_size} Bytes, save {((1-compressed_size/original)*100):.1f}%") + EOF + +2.2 Simple method using manual_process.py + + python3 manual_process.py \ + ./output/manual_zephyr.signed.bin \ + --only-lzma \ + --output-dir ./output \ + --lzma-dict-size 32768 + +3. Second MCUBoot signature for compressed firmware (adds --pad-header 0x200 or 512): + + python3 /home/ubuntu/zephyrproject/bootloader/mcuboot/scripts/imgtool.py sign \ + --pad-header \ + --version 0.0.0+0 \ + --align 1 \ + --header-size 512 \ + --slot-size 1179648 \ + --key /home/ubuntu/zephyrproject/bootloader/mcuboot/ecdsa_private_key.pem \ + ./output/manual_zephyr.signed.lzma.bin \ + ./output/manual_zephyr.signed.lzma.signed.bin + +4. Generate Matter OTA image: + + python3 /home/ubuntu/connectedhomeip/src/app/ota_image_tool.py create \ + --vendor-id 0xfff1 \ + --product-id 0x8004 \ + --version 2 \ + --version-str "2.0.0" \ + --digest-algorithm sha256 \ + ./output/manual_zephyr.signed.lzma.signed.bin \ + ./output/manual_matter.ota +""" + +import argparse +import lzma +import os +import subprocess +import sys + +# Configure tool paths here - edit these to match your environment +CONFIG = { + # Path to MCUBoot imgtool.py + 'imgtool_path': '/home/ubuntu/zephyrproject/bootloader/mcuboot/scripts/imgtool.py', + # Path to Matter ota_image_tool.py + 'ota_tool_path': '/home/ubuntu/connectedhomeip/src/app/ota_image_tool.py', +} + + +def parse_arguments(): + """Parse command line arguments""" + parser = argparse.ArgumentParser( + description='Manual Matter firmware processing tool for Telink platforms' + ) + parser.add_argument( + 'firmware_path', + help='Path to the input firmware file (can be raw zephyr.bin or already signed zephyr.signed.bin)' + ) + parser.add_argument( + 'config_path', + nargs='?', + help='Path to the .config file containing build configuration (optional if manual parameters are provided)' + ) + parser.add_argument( + '--output-dir', + default=None, + help='Directory to write output files (defaults to same directory as input firmware)' + ) + parser.add_argument( + '--already-signed', + action='store_true', + help='Input firmware is already signed (zephyr.signed.bin), skip first MCUBoot signature step' + ) + parser.add_argument( + '--only-lzma', + action='store_true', + help='Only perform LZMA compression: input.signed.bin -> output/manual_input.signed.lzma.bin' + ) + parser.add_argument( + '--chip-family', + choices=['tl3238c', 'tlsr9528a', 'tlsr9268j', 'tlsr9268m'], + default='tl3238c', + help='Chip family to process for: tl3238c (2MB Flash, full pipeline), tlsr9528a/tlsr9268m (4MB Flash, skip LZMA and second signature)' + ) + # Tool path overrides - can override the CONFIG defaults + parser.add_argument( + '--imgtool-path', + help='Override configured path to MCUBoot imgtool.py', + default=None + ) + parser.add_argument( + '--ota-tool-path', + help='Override configured path to Matter ota_image_tool.py', + default=None + ) + # Manual parameters - used if config_path is not provided + + def hex_or_int(value): + try: + return int(value, 0) + except ValueError: + raise argparse.ArgumentTypeError(f"invalid integer value: '{value}'") + + parser.add_argument( + '--rom-start-offset', + type=hex_or_int, + help='ROM start offset (CONFIG_ROM_START_OFFSET), supports decimal or 0x-prefixed hex' + ) + parser.add_argument( + '--flash-load-size', + type=hex_or_int, + help='Flash load size (CONFIG_FLASH_LOAD_SIZE), supports decimal or 0x-prefixed hex' + ) + parser.add_argument( + '--lzma-dict-size', + type=hex_or_int, + help='LZMA dictionary size (CONFIG_COMPRESS_LZMA_DICTIONARY_SIZE), supports decimal or 0x-prefixed hex' + ) + parser.add_argument( + '--signing-key', + help='MCUBoot signing key path (CONFIG_MCUBOOT_SIGNATURE_KEY_FILE)' + ) + parser.add_argument( + '--vendor-id', + type=hex_or_int, + help='Matter device vendor ID (CONFIG_CHIP_DEVICE_VENDOR_ID), supports decimal or 0x-prefixed hex' + ) + parser.add_argument( + '--product-id', + type=hex_or_int, + help='Matter device product ID (CONFIG_CHIP_DEVICE_PRODUCT_ID), supports decimal or 0x-prefixed hex' + ) + parser.add_argument( + '--software-version', + type=int, + default=1, + help='Matter device software version (CONFIG_CHIP_DEVICE_SOFTWARE_VERSION, default: 1)' + ) + parser.add_argument( + '--software-version-str', + default='1.0.0', + help='Matter device software version string (CONFIG_CHIP_DEVICE_SOFTWARE_VERSION_STRING, default: "1.0.0")' + ) + args = parser.parse_args() + + # Override CONFIG paths if command line arguments are provided + if args.imgtool_path: + CONFIG['imgtool_path'] = args.imgtool_path + if args.ota_tool_path: + CONFIG['ota_tool_path'] = args.ota_tool_path + + # Validate that if no config_path is provided, required manual parameters are present + if args.config_path is None: + if args.only_lzma: + # Only LZMA mode only requires lzma-dict-size + required_manual = [args.lzma_dict_size] + if not all(required_manual): + parser.error( + "When using --only-lzma and config_path is not provided, must specify --lzma-dict-size" + ) + else: + # Full pipeline requires all manual parameters + required_manual = [ + args.rom_start_offset, + args.flash_load_size, + args.lzma_dict_size, + args.signing_key, + args.vendor_id, + args.product_id + ] + if not all(required_manual): + parser.error( + "When config_path is not provided, all manual parameters must be specified: " + "--rom-start-offset, --flash-load-size, --lzma-dict-size, --signing-key, " + "--vendor-id, --product-id" + ) + + return args + + +def parse_dotconfig(config_path): + """Parse Kconfig .config file to extract all build configuration parameters""" + config = {} + if not os.path.exists(config_path): + raise FileNotFoundError(f"Config file not found: {config_path}") + + with open(config_path) as f: + for line in f: + line = line.strip() + if line.startswith('CONFIG_'): + key, value = line.split('=', 1) + # Strip quotes from string values + value = value.strip('"') + # Convert hexadecimal values + if value.startswith('0x'): + config[key] = int(value, 16) + # Convert integer values + elif value.isdigit(): + config[key] = int(value) + # String values remain as strings + else: + config[key] = value + return config + + +def merge_binaries(input_file1, input_file2, output_file, offset): + """Reuse merge_binaries function from the original process_binaries.py""" + with open(output_file, 'r+b' if os.path.exists(output_file) else 'wb') as outfile: + # Merge first input file at offset 0 + with open(input_file1, 'rb') as infile1: + data1 = infile1.read() + outfile.write(data1) + print(f"Merged {len(data1)} bytes from {input_file1} into {output_file} at offset 0") + + # Fill gaps with 0xFF if there's space between the two binaries + current_size = outfile.tell() + if current_size < offset: + gap_size = offset - current_size + outfile.write(bytearray([0xFF] * gap_size)) + print(f"Filled gap of {gap_size} bytes with 0xFF in {output_file}") + + # Merge second input file at the specified offset + with open(input_file2, 'rb') as infile2: + outfile.seek(offset) + data2 = infile2.read() + outfile.write(data2) + print(f"Merged {len(data2)} bytes from {input_file2} into {output_file} at offset {offset}") + + +def compress_lzma_firmware(input_file, output_file, dict_size): + """Reuse compress_lzma_firmware function from the original process_binaries.py""" + if not os.path.exists(input_file): + raise FileNotFoundError(f"Input firmware not found: {input_file}") + + with open(input_file, 'rb') as f: + firmware_data = f.read() + + # LZMA compression parameters (matched to device-side decoder) + lc = 1 # Literal context bits + lp = 2 # Literal position bits + pb = 0 # Position bits + + # Create LZMA compressor with raw format for compatibility with lzma_raw_decoder() + compressor = lzma.LZMACompressor( + format=lzma.FORMAT_RAW, + filters=[ + { + "id": lzma.FILTER_LZMA1, + "dict_size": dict_size, + "lc": lc, + "lp": lp, + "pb": pb, + "mode": lzma.MODE_NORMAL, + "mf": lzma.MF_BT4, + "depth": 0 + } + ] + ) + + # Perform compression + compressed_data = compressor.compress(firmware_data) + compressor.flush() + + # Write compressed binary to output file + with open(output_file, 'wb') as f: + f.write(compressed_data) + + print(f"LZMA compression completed: {input_file} -> {output_file}") + print( + f" Size reduction: {len(firmware_data)} -> {len(compressed_data)} bytes ({(1-len(compressed_data)/len(firmware_data))*100:.1f}% smaller)") + + +def run_imgtool_sign(input_file, output_file, rom_start_offset, flash_load_size, key_path, zephyr_base, pad_header=False): + """Reuse MCUBoot imgtool signing logic from CMakeLists.txt and process_binaries.py""" + if not os.path.exists(input_file): + raise FileNotFoundError(f"File to sign not found: {input_file}") + + # First try configured path, then fallback to auto-discovery + imgtool_path = CONFIG['imgtool_path'] + if not os.path.exists(imgtool_path): + # Fallback to original auto-discovery logic if configured path is invalid + imgtool_candidates = [ + os.path.join(zephyr_base, '../bootloader/mcuboot/scripts/imgtool.py'), + os.path.join(zephyr_base, 'bootloader/mcuboot/scripts/imgtool.py'), + os.path.join(os.getcwd(), 'bootloader/mcuboot/scripts/imgtool.py'), + os.path.join(os.path.dirname(__file__), 'bootloader/mcuboot/scripts/imgtool.py') + ] + for candidate in imgtool_candidates: + if os.path.exists(candidate): + imgtool_path = candidate + break + if not imgtool_path or not os.path.exists(imgtool_path): + raise FileNotFoundError( + f"MCUBoot imgtool not found. Check CONFIG['imgtool_path'] or use --imgtool-path. Last tried: {imgtool_path}") + + # Look for signing key in multiple possible locations + key_candidates = [ + os.path.join(zephyr_base, '../', key_path), + os.path.join(zephyr_base, key_path), + os.path.join(os.getcwd(), key_path), + os.path.join(os.getcwd(), os.path.basename(key_path)), + os.path.join(os.path.dirname(__file__), key_path), + os.path.join(os.path.dirname(__file__), os.path.basename(key_path)) + ] + full_key_path = None + for candidate in key_candidates: + if os.path.exists(candidate): + full_key_path = candidate + break + if not full_key_path: + raise FileNotFoundError(f"Signing key not found in any of: {key_candidates}") + if not os.path.exists(full_key_path): + raise FileNotFoundError(f"Signing key not found: {full_key_path}") + + # Build imgtool command + cmd = [ + sys.executable, imgtool_path, 'sign', + '--version', '0.0.0+0', + '--align', '1', + '--header-size', str(rom_start_offset), + '--slot-size', str(flash_load_size), + '--key', full_key_path + ] + + # Add --pad-header flag when signing compressed firmware + if pad_header: + cmd.append('--pad-header') + + cmd.extend([input_file, output_file]) + + print(f"Executing MCUBoot sign command: {' '.join(cmd)}") + result = subprocess.run(cmd, capture_output=True, text=True) + if result.returncode != 0: + print(f"imgtool stdout: {result.stdout}") + print(f"imgtool stderr: {result.stderr}") + raise subprocess.CalledProcessError(result.returncode, cmd) + print(f"MCUBoot signing completed: {input_file} -> {output_file}") + + +def run_ota_image_tool(input_file, output_file, vendor_id, product_id, version, version_str, chip_root): + """Reuse Matter OTA image tool logic from ota-image.cmake""" + if not os.path.exists(input_file): + raise FileNotFoundError(f"Signed firmware not found: {input_file}") + + # First try configured path, then fallback to auto-discovery + ota_tool_path = CONFIG['ota_tool_path'] + if not os.path.exists(ota_tool_path): + # Fallback to original auto-discovery logic if configured path is invalid + ota_tool_candidates = [ + os.path.join(chip_root, 'src/app/ota_image_tool.py'), + os.path.join(os.getcwd(), 'src/app/ota_image_tool.py'), + os.path.join(os.path.dirname(__file__), 'src/app/ota_image_tool.py'), + os.path.join(os.getcwd(), 'ota_image_tool.py'), + os.path.join(os.path.dirname(__file__), 'ota_image_tool.py') + ] + for candidate in ota_tool_candidates: + if os.path.exists(candidate): + ota_tool_path = candidate + break + if not ota_tool_path or not os.path.exists(ota_tool_path): + raise FileNotFoundError( + f"Matter OTA tool not found. Check CONFIG['ota_tool_path'] or use --ota-tool-path. Last tried: {ota_tool_path}") + + # Build Matter OTA image creation command + cmd = [ + sys.executable, ota_tool_path, 'create', + '--vendor-id', str(vendor_id), + '--product-id', str(product_id), + '--version', str(version), + '--version-str', version_str, + '--digest-algorithm', 'sha256', + input_file, + output_file + ] + + print(f"Executing Matter OTA packaging command: {' '.join(cmd)}") + result = subprocess.run(cmd, capture_output=True, text=True) + if result.returncode != 0: + print(f"ota_image_tool stdout: {result.stdout}") + print(f"ota_image_tool stderr: {result.stderr}") + raise subprocess.CalledProcessError(result.returncode, cmd) + print(f"Matter OTA packaging completed: {input_file} -> {output_file}") + + +def main(): + # Parse command line arguments + args = parse_arguments() + + # Get project root paths - support both project environment and standalone environment + # In standalone mode, all tools are relative to current working directory + script_dir = os.path.dirname(os.path.abspath(__file__)) + if os.path.exists(os.path.join(script_dir, '../../..', 'src/app/ota_image_tool.py')): + # Running from within the connectedhomeip project + chip_root = os.path.abspath(os.path.join(script_dir, '../../..')) + zephyr_base = os.environ.get('ZEPHYR_BASE', '/home/ubuntu/zephyr') + else: + # Running in standalone mode, all tools are in the current directory + chip_root = os.getcwd() + zephyr_base = os.getcwd() + + # Set output directory - default to same directory as input firmware + output_dir = args.output_dir if args.output_dir else os.path.dirname(args.firmware_path) + os.makedirs(output_dir, exist_ok=True) + + # Validate input firmware exists + if not os.path.exists(args.firmware_path): + raise FileNotFoundError(f"Firmware file not found: {args.firmware_path}") + + # Load configuration - either from .config file or manual parameters + config = {} + if args.config_path: + if not os.path.exists(args.config_path): + raise FileNotFoundError(f"Config file not found: {args.config_path}") + print("Parsing build configuration from .config file...") + config = parse_dotconfig(args.config_path) + else: + print("Using manually specified configuration parameters...") + config['CONFIG_ROM_START_OFFSET'] = args.rom_start_offset + config['CONFIG_FLASH_LOAD_SIZE'] = args.flash_load_size + config['CONFIG_COMPRESS_LZMA_DICTIONARY_SIZE'] = args.lzma_dict_size + config['CONFIG_MCUBOOT_SIGNATURE_KEY_FILE'] = args.signing_key + config['CONFIG_CHIP_DEVICE_VENDOR_ID'] = args.vendor_id + config['CONFIG_CHIP_DEVICE_PRODUCT_ID'] = args.product_id + config['CONFIG_CHIP_DEVICE_SOFTWARE_VERSION'] = args.software_version + config['CONFIG_CHIP_DEVICE_SOFTWARE_VERSION_STRING'] = args.software_version_str + + # Display loaded configuration + # Only print full configuration if not in only-lzma mode + if not args.only_lzma: + print("\n=== Loaded Configuration Parameters ===") + print(f"ROM_START_OFFSET: {config['CONFIG_ROM_START_OFFSET']} (0x{config['CONFIG_ROM_START_OFFSET']:x})") + print(f"FLASH_LOAD_SIZE: {config['CONFIG_FLASH_LOAD_SIZE']} (0x{config['CONFIG_FLASH_LOAD_SIZE']:x})") + print(f"LZMA_DICT_SIZE: {config['CONFIG_COMPRESS_LZMA_DICTIONARY_SIZE']}") + print(f"Signature key: {config['CONFIG_MCUBOOT_SIGNATURE_KEY_FILE']}") + print(f"Vendor ID: {config['CONFIG_CHIP_DEVICE_VENDOR_ID']} (0x{config['CONFIG_CHIP_DEVICE_VENDOR_ID']:x})") + print(f"Product ID: {config['CONFIG_CHIP_DEVICE_PRODUCT_ID']} (0x{config['CONFIG_CHIP_DEVICE_PRODUCT_ID']:x})") + print( + f"Software version: {config['CONFIG_CHIP_DEVICE_SOFTWARE_VERSION']} - {config['CONFIG_CHIP_DEVICE_SOFTWARE_VERSION_STRING']}") + print(f"Output directory: {output_dir}") + print("========================================") + else: + # Minimal output for only-lzma mode + print("\n=== LZMA Compression Configuration ===") + print(f"LZMA dictionary size: {config['CONFIG_COMPRESS_LZMA_DICTIONARY_SIZE']}") + print(f"Input file: {args.firmware_path}") + print(f"Output directory: {output_dir}") + print("======================================") + + # Define all output filenames with manual_ prefix + manual_signed_bin = os.path.join(output_dir, 'manual_zephyr.signed.bin') + manual_lzma_bin = os.path.join(output_dir, 'manual_zephyr.signed.lzma.bin') + manual_final_signed = os.path.join(output_dir, 'manual_zephyr.signed.lzma.signed.bin') + manual_matter_ota = os.path.join(output_dir, 'manual_matter.ota') + + # Handle only-lzma mode first + if args.only_lzma: + print("\n=== Only LZMA compression mode activated ===") + # Derive output filename from input to retain original naming prefix + input_filename = os.path.basename(args.firmware_path) + if input_filename.endswith('.bin'): + output_filename = input_filename[:-4] + '.lzma.bin' + else: + output_filename = input_filename + '.lzma.bin' + manual_lzma_bin = os.path.join(output_dir, output_filename) + print(f"Input file: {args.firmware_path}") + print(f"Output file: {manual_lzma_bin}") + compress_lzma_firmware( + input_file=args.firmware_path, + output_file=manual_lzma_bin, + dict_size=config['CONFIG_COMPRESS_LZMA_DICTIONARY_SIZE'] + ) + print("\n=== LZMA compression completed successfully! Generated file: ===") + size = os.path.getsize(manual_lzma_bin) + print(f"{manual_lzma_bin}: {size/1024:.1f} KB") + sys.exit(0) + + # Execute pipeline based on chip family + print(f"\n=== Chip family selected: {args.chip_family} ===") + if args.chip_family in ['tlsr9528a', 'tlsr9268j', 'tlsr9268m']: + # 4MB Flash chips: simplified pipeline - only two steps + print("\n=== Using simplified pipeline for 4MB Flash chips (skip LZMA and second signature) ===") + if args.already_signed: + print("\n=== Note: Input is already signed, skipping first MCUBoot signature step ===") + import shutil + shutil.copy2(args.firmware_path, manual_final_signed) + print(f"Copied signed input to: {manual_final_signed}") + input_for_ota = args.firmware_path + generated_files = [manual_final_signed, manual_matter_ota] + else: + print("\n=== Step 1: Only MCUBoot signature (4MB chips don't need compression) ===") + run_imgtool_sign( + input_file=args.firmware_path, + output_file=manual_final_signed, + rom_start_offset=config['CONFIG_ROM_START_OFFSET'], + flash_load_size=config['CONFIG_FLASH_LOAD_SIZE'], + key_path=config['CONFIG_MCUBOOT_SIGNATURE_KEY_FILE'], + zephyr_base=zephyr_base + ) + input_for_ota = manual_final_signed + generated_files = [manual_final_signed, manual_matter_ota] + + # Only generate OTA image as final step + print("\n=== Step 2: Generate final Matter OTA image ===") + run_ota_image_tool( + input_file=input_for_ota, + output_file=manual_matter_ota, + vendor_id=config['CONFIG_CHIP_DEVICE_VENDOR_ID'], + product_id=config['CONFIG_CHIP_DEVICE_PRODUCT_ID'], + version=config['CONFIG_CHIP_DEVICE_SOFTWARE_VERSION'], + version_str=config['CONFIG_CHIP_DEVICE_SOFTWARE_VERSION_STRING'], + chip_root=chip_root + ) + else: + # tl3238c: original full pipeline with LZMA compression + print("\n=== Using full pipeline for 2MB Flash chips (includes LZMA and dual signatures) ===") + # Determine starting point based on --already-signed flag + if args.already_signed: + # Input is already signed: zephyr.signed.bin -> skip first step + print("\n=== Note: Input is already signed, skipping first MCUBoot signature step ===") + input_for_compression = args.firmware_path + # Copy the already signed file to our manual_ prefix file for consistency + import shutil + shutil.copy2(args.firmware_path, manual_signed_bin) + print(f"Copied signed input to: {manual_signed_bin}") + step_start = 2 + else: + # Step 1: Input firmware -> manual_zephyr.signed.bin (First MCUBoot signature) + print("\n=== Step 1: First MCUBoot signature ===") + run_imgtool_sign( + input_file=args.firmware_path, + output_file=manual_signed_bin, + rom_start_offset=config['CONFIG_ROM_START_OFFSET'], + flash_load_size=config['CONFIG_FLASH_LOAD_SIZE'], + key_path=config['CONFIG_MCUBOOT_SIGNATURE_KEY_FILE'], + zephyr_base=zephyr_base + ) + input_for_compression = manual_signed_bin + step_start = 2 + + # Step 2: manual_zephyr.signed.bin -> manual_zephyr.signed.lzma.bin (LZMA compression) + print(f"\n=== Step {step_start}: LZMA compression ===") + compress_lzma_firmware( + input_file=input_for_compression, + output_file=manual_lzma_bin, + dict_size=config['CONFIG_COMPRESS_LZMA_DICTIONARY_SIZE'] + ) + + # Step 3: manual_zephyr.signed.lzma.bin -> manual_zephyr.signed.lzma.signed.bin (Second MCUBoot signature) + print(f"\n=== Step {step_start + 1}: Second MCUBoot signature (after compression) ===") + run_imgtool_sign( + input_file=manual_lzma_bin, + output_file=manual_final_signed, + rom_start_offset=config['CONFIG_ROM_START_OFFSET'], + flash_load_size=config['CONFIG_FLASH_LOAD_SIZE'], + key_path=config['CONFIG_MCUBOOT_SIGNATURE_KEY_FILE'], + zephyr_base=zephyr_base, + pad_header=True # --pad-header is REQUIRED for compressed firmware + ) + + # Step 4: manual_zephyr.signed.lzma.signed.bin -> manual_matter.ota (Matter OTA packaging) + print(f"\n=== Step {step_start + 2}: Generate final Matter OTA image ===") + run_ota_image_tool( + input_file=manual_final_signed, + output_file=manual_matter_ota, + vendor_id=config['CONFIG_CHIP_DEVICE_VENDOR_ID'], + product_id=config['CONFIG_CHIP_DEVICE_PRODUCT_ID'], + version=config['CONFIG_CHIP_DEVICE_SOFTWARE_VERSION'], + version_str=config['CONFIG_CHIP_DEVICE_SOFTWARE_VERSION_STRING'], + chip_root=chip_root + ) + generated_files = [manual_signed_bin, manual_lzma_bin, manual_final_signed, manual_matter_ota] + + print("\n=== All steps completed successfully! Generated files: ===") + for f in generated_files: + if os.path.exists(f): + size = os.path.getsize(f) + print(f"{f}: {size/1024:.1f} KB") + + +if __name__ == '__main__': + print("=== Matter Telink Manual Firmware Processing Tool ===") + print("\nUsage 1 (process raw firmware, full pipeline):") + print(" python3 manual_process.py zephyr.bin [--output-dir OUTPUT_DIR]") + print("Example 1:") + print(" python3 manual_process.py \\") + print(" /home/ubuntu/connectedhomeip/examples/light-switch-app/telink/build_tl3238x_retention_dual_sw_v1/zephyr/zephyr.bin \\") + print(" /home/ubuntu/connectedhomeip/examples/light-switch-app/telink/build_tl3238x_retention_dual_sw_v1/zephyr/.config\n") + print("Usage 2 (process already signed firmware, start from compression):") + print(" python3 manual_process.py zephyr.signed.bin --already-signed") + print("Example 2:") + print(" python3 manual_process.py \\") + print(" /home/ubuntu/connectedhomeip/examples/light-switch-app/telink/build_tl3238x_retention_dual_sw_v1/zephyr/zephyr.signed.bin \\") + print(" /home/ubuntu/connectedhomeip/examples/light-switch-app/telink/build_tl3238x_retention_dual_sw_v1/zephyr/.config --already-signed\n") + print("Usage 3 (with manual parameters):") + print(" python3 manual_process.py [--output-dir OUTPUT_DIR] \\") + print(" --rom-start-offset 512 --flash-load-size 1179648 \\") + print(" --lzma-dict-size 32768 --signing-key bootloader/mcuboot/root-rsa-2048.pem \\") + print(" --vendor-id 65521 --product-id 32772 [--already-signed]\n") + + try: + main() + except Exception as e: + import traceback + traceback.print_exc() + print(f"\nERROR: {str(e)}", file=sys.stderr) + sys.exit(1) diff --git a/scripts/tools/telink/mfg_tool.py b/scripts/tools/telink/mfg_tool.py index 56571a667a52..e7477e3f6e72 100755 --- a/scripts/tools/telink/mfg_tool.py +++ b/scripts/tools/telink/mfg_tool.py @@ -32,6 +32,8 @@ import cryptography.hazmat.backends import cryptography.x509 import pyqrcode +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from intelhex import IntelHex TOOLS = { @@ -456,6 +458,13 @@ def write_device_unique_data(args, out_dirs, pai_cert): else: dacs = generate_dac_cert(int(row['Index']), args, out_dirs, int(row['Discriminator']), int(row['PIN Code']), pai_cert['key_pem'], pai_cert['cert_pem']) + dac_cert_storage = read_der_file(dacs[0]) + dac_key_storage = read_key_bin_file(dacs[1]) + if not args.secure_programming_verification: + nvs_memory_append('dac_cert', dac_cert_storage) + nvs_memory_append('dac_key', dac_key_storage) + else: + log.info("Secure programming verification enabled; DAC and its keys are not stored directly into factory data") nvs_memory_append('dac_cert', read_der_file(dacs[0])) nvs_memory_append('dac_key', read_key_bin_file(dacs[1])) @@ -472,8 +481,45 @@ def write_device_unique_data(args, out_dirs, pai_cert): return dacs -def generate_partition(args, out_dirs): - log.info("Generating partition image: offset: 0x%08X size: 0x%08X", args.offset, args.size) +def aes_encrypt(key, data): + # Ensure data is 16 bytes (AES block size) + assert len(data) == 16, "Data block for AES must be 16 bytes." + assert len(key) == 16, "AES key must be 16 bytes (128 bits)." + + cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=default_backend()) + encryptor = cipher.encryptor() + return encryptor.update(data) + encryptor.finalize() + + +def save_dac_cert_and_keys(dac_cert, dac_key, chip_id, file_path): + with open(file_path, 'wb') as f: + # Write DAC private key length (2 bytes, little-endian) + dac_key_len = len(dac_key) + f.write(dac_key_len.to_bytes(2, 'little')) + + # Encrypt DAC private key in two 16-byte segments + encrypted_key_part1 = aes_encrypt(chip_id, dac_key[:16]) + encrypted_key_part2 = aes_encrypt(chip_id, dac_key[16:32]) + + # Write the encrypted DAC private key parts (32 bytes total) + f.write(encrypted_key_part1) + f.write(encrypted_key_part2) + + # Pad the file with 0xFF to reach the 100th byte + f.write(b'\xFF' * (100 - f.tell())) + + # Write DAC certificate length (2 bytes, little-endian) + dac_cert_len = len(dac_cert) + f.write(dac_cert_len.to_bytes(2, 'little')) + + # Write DAC certificate data + f.write(dac_cert) + + print(f"DAC certificate and key have been saved to {file_path}") + + +def generate_partition(args, dacs_cert, out_dirs): + log.info(f'Generating partition image: offset: 0x{args.offset:X} size: 0x{args.size:X}') cbor_data = cbor.dumps(NVS_MEMORY) # Create hex file if len(cbor_data) > args.size: @@ -544,7 +590,10 @@ def add_additional_kv(args, serial_num): nvs_memory_append('rd_uid', args.rd_id_uid) # Add the serial-num - nvs_memory_append('sn', serial_num) + if args.disable_serial_num_storage: + log.info("Secure programming verification enabled; skipping serial-num") + else: + nvs_memory_append('sn', serial_num) nvs_memory_append('version', FACTORY_DATA_VERSION) @@ -646,6 +695,13 @@ def base64_str(s): return base64.b64decode(s) help='Partition offset - an address in devices NVM memory, where factory data will be stored') part_gen_args.add_argument('--size', type=allow_any_int, help='The maximum partition size') + secure_args = parser.add_argument_group('Secure programming verification options') + secure_args.add_argument("--secure-programming-verification", action="store_true", + help="Enable secure programming mode. When set, the script will perform additional steps for secure programming verification.") + secure_args.add_argument("--chip-id", required=False, type=str, help="Chip ID in hex format (32 hex characters).") + secure_args.add_argument("--disable_serial_num_storage", action="store_true", + help="Disable storage of serial-num in factorydata.") + args = parser.parse_args() # Validate in-tree parameter @@ -728,14 +784,14 @@ def main(): out_dir_top = os.path.realpath(args.output) os.makedirs(out_dir_top, exist_ok=True) - with open(os.sep.join([out_dir_top, "device_sn.csv"]), "w") as f: - f.write(DEV_SN_CSV_HDR) + with open(os.sep.join([out_dir_top, "device_sn.csv"]), "w") as dev_sn_file: + dev_sn_file.write(DEV_SN_CSV_HDR) for i in range(args.count): pai_cert = {} serial_num_str = format(serial_num_int + i, 'x') - log.info("Generating for '%s'", serial_num_str) - f.write(serial_num_str + '\n') + log.info(f"Generating for {serial_num_str}") + dev_sn_file.write(serial_num_str + '\n') out_dirs = setup_out_dir(out_dir_top, args, serial_num_str) add_additional_kv(args, serial_num_str) generate_passcode(args, out_dirs) @@ -743,7 +799,7 @@ def main(): if args.paa or args.pai: pai_cert = setup_root_certificates(args, out_dirs) dacs_cert = write_device_unique_data(args, out_dirs, pai_cert) - generate_partition(args, out_dirs) + generate_partition(args, dacs_cert, out_dirs) generate_json_summary(args, out_dirs, pai_cert, dacs_cert, serial_num_str) diff --git a/scripts/tools/telink/process_binaries.py b/scripts/tools/telink/process_binaries.py index fd6d05c64bcb..7908e59485cd 100755 --- a/scripts/tools/telink/process_binaries.py +++ b/scripts/tools/telink/process_binaries.py @@ -179,7 +179,6 @@ def compress_lzma_firmware(input_file, output_file): print(f"Image footer is written to {dfu_output_name}") print("Image for DFU over BLE SMP is ready!") - os.remove('empty.txt') if build_conf.getboolean('CONFIG_TELINK_OTA_BUTTON_TEST'): merge_binaries('merged.bin', build_conf['CONFIG_SIGNED_OTA_IMAGE_FILE_NAME'], diff --git a/scripts/tools/telink/update_zephyr.py b/scripts/tools/telink/update_zephyr.py index fe735df0ccaa..58b198b28601 100755 --- a/scripts/tools/telink/update_zephyr.py +++ b/scripts/tools/telink/update_zephyr.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -# Copyright (c) 2023 Project CHIP Authors +# Copyright (c) 2023-2026 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. @@ -22,37 +22,146 @@ import sys +def fetch_telink_ble_sdk(repo_url, commit_hash): + """ + Fetch Telink BLE SDK from specified repository and commit. + + Args: + repo_url: Git repository URL for the BLE SDK + commit_hash: Specific commit hash to checkout + """ + zephyr_base = os.getenv("ZEPHYR_BASE") + if not zephyr_base: + raise RuntimeError("ZEPHYR_BASE environment variable not set") + + # fetch_sdk.sh is located in modules/hal/telink/hal_v2 under ZEPHYR_BASE + sdk_script_dir = os.path.join(zephyr_base, "../modules/hal/telink/hal_v2") + sdk_script = os.path.join(sdk_script_dir, "fetch_sdk.sh") + + print(f"ZEPHYR_BASE: {zephyr_base}") + print(f"Looking for fetch_sdk.sh at: {sdk_script}") + + # Change to the directory containing fetch_sdk.sh + os.chdir(sdk_script_dir) + print(f"Current directory: {os.getcwd()}") + print(f"Directory contents: {os.listdir('.')}") + + # Check if fetch_sdk.sh exists + if not os.path.exists("fetch_sdk.sh"): + raise RuntimeError(f"fetch_sdk.sh not found in {sdk_script_dir}") + + # Execute fetch_sdk.sh + print(f"Fetching Telink BLE SDK with commit {commit_hash}...") + # subprocess.run(["chmod", "+x", "fetch_sdk.sh"], check=True) + subprocess.run(["./fetch_sdk.sh", repo_url, commit_hash], check=True) + + print("Telink BLE SDK fetch completed.") + + +def update_zephyr(remote_url, commit_hash): + """ + Update Zephyr repository to specific commit. + + Args: + remote_url: Git repository URL for Zephyr + commit_hash: Specific commit hash to checkout + """ + zephyr_base = os.getenv("ZEPHYR_BASE") + if not zephyr_base: + raise RuntimeError("ZEPHYR_BASE environment variable not set") + + remote_name = 'custom' + + # Check if remote already exists, if so, update it + result = subprocess.run(['git', '-C', zephyr_base, 'remote', 'get-url', remote_name], + capture_output=True, text=True) + if result.returncode == 0: + # Remote exists, update its URL + print(f"Remote '{remote_name}' already exists, updating URL...") + subprocess.run(['git', '-C', zephyr_base, 'remote', 'set-url', remote_name, remote_url], check=True) + else: + # Remote doesn't exist, add it + print(f"Adding remote: {remote_url}") + subprocess.run(['git', '-C', zephyr_base, 'remote', 'add', remote_name, remote_url], check=True) + + # Fetch updates from remote + print(f"Fetching from remote: {remote_name}") + command = ['git', '-C', zephyr_base, 'fetch', remote_name] + subprocess.run(command, check=True) + + # Hard reset to specified commit + print(f"Resetting to commit: {commit_hash}") + command = ['git', '-C', zephyr_base, 'reset', commit_hash, '--hard'] + subprocess.run(command, check=True) + + # Update west modules + print("Updating west modules...") + command = ['west', 'update', '-o=--depth=1', '-n', '-f', 'smart'] + subprocess.run(command, check=True) + + # Fetch blobs + print("Fetching hal_telink blobs...") + command = ['west', 'blobs', 'fetch', 'hal_telink'] + subprocess.run(command, check=True) + + def main(): + """Main entry point for the script.""" + parser = argparse.ArgumentParser( + description='Update Telink Zephyr to specific revision and fetch BLE SDK.' + ) + parser.add_argument("remote", + default="https://github.com/telink-semi/zephyr", + help="New remote URL for the Zephyr repository.") + parser.add_argument("hash", + help="Update Telink Zephyr to specific revision.") + parser.add_argument("--sdk-repo", + default="https://github.com/telink-semi/tl_ble_sdk_zephyr.git", + help="Repository URL for Telink BLE SDK (default: tl_ble_sdk_zephyr.git)") + parser.add_argument("--sdk-hash", + default="af03f86610f3de8b1f2c12215bbb21f5decf97e3", + help="Specific commit hash for BLE SDK") + parser.add_argument("--skip-sdk", + action="store_true", + help="Skip fetching Telink BLE SDK") + + args = parser.parse_args() try: + # Set ZEPHYR_BASE environment variable zephyr_base = os.getenv("ZEPHYR_BASE") if not zephyr_base: zephyr_base = os.getenv("TELINK_ZEPHYR_BASE") - os.environ['ZEPHYR_BASE'] = zephyr_base + if zephyr_base: + os.environ['ZEPHYR_BASE'] = zephyr_base + if not zephyr_base: raise RuntimeError( - "No ZEPHYR_BASE environment variable found, please set ZEPHYR_BASE to a zephyr repository path.") - - parser = argparse.ArgumentParser( - description='Script helping to update Telink Zephyr to specific revision.') - parser.add_argument("hash", help="Update Telink Zephyr to specific revision.") - - args = parser.parse_args() - - command = ['git', '-C', zephyr_base, 'fetch'] - subprocess.run(command, check=True) + "No ZEPHYR_BASE or TELINK_ZEPHYR_BASE environment variable found. " + "Please set to a zephyr repository path." + ) - command = ['git', '-C', zephyr_base, 'reset', args.hash, '--hard'] - subprocess.run(command, check=True) + # Update Zephyr repository + print("=" * 60) + print("Step 1: Updating Zephyr repository...") + print("=" * 60) + update_zephyr(args.remote, args.hash) - command = ['west', 'update', '-o=--depth=1', '-n', '-f', 'smart'] - subprocess.run(command, check=True) + # Fetch BLE SDK (unless skipped) + if not args.skip_sdk: + print("\n" + "=" * 60) + print("Step 2: Fetching Telink BLE SDK...") + print("=" * 60) + fetch_telink_ble_sdk(args.sdk_repo, args.sdk_hash) + else: + print("\nSkipping BLE SDK fetch as requested.") - command = ['west', 'blobs', 'fetch', 'hal_telink'] - subprocess.run(command, check=True) + print("\n" + "=" * 60) + print("All operations completed successfully!") + print("=" * 60) except (RuntimeError, subprocess.CalledProcessError) as e: - print(e) + print(f"\nError: {e}") sys.exit(1) diff --git a/src/platform/Zephyr/BLEManagerImpl.cpp b/src/platform/Zephyr/BLEManagerImpl.cpp index 23545178bc0a..8042380fd082 100644 --- a/src/platform/Zephyr/BLEManagerImpl.cpp +++ b/src/platform/Zephyr/BLEManagerImpl.cpp @@ -61,7 +61,6 @@ extern "C" { extern __attribute__((noinline)) int b9x_bt_blc_mac_init(uint8_t * bt_mac); #elif defined(CONFIG_BT_TLX) extern __attribute__((noinline)) int tlx_bt_blc_mac_init(uint8_t * bt_mac); -extern __attribute__((noinline)) void tlx_bt_802154_dual_mode_start(void); #elif defined(CONFIG_BT_W91) extern __attribute__((noinline)) void telink_bt_blc_mac_init(uint8_t * bt_mac); #endif @@ -260,8 +259,6 @@ CHIP_ERROR BLEManagerImpl::_Init() // Telink TLX: start a minimal BLE advertisement before dual-mode so Thread's tlx_start_radio() does not block. int adv_err = StartMinimalBLEAdvertisement(); VerifyOrReturnError(adv_err == 0, MapErrorZephyr(adv_err)); - - tlx_bt_802154_dual_mode_start(); #endif TEMPORARY_RETURN_IGNORED BLEAdvertisingArbiter::Init(static_cast(id)); diff --git a/src/platform/telink/CHIPDevicePlatformConfig.h b/src/platform/telink/CHIPDevicePlatformConfig.h index 692743cf0b3a..bc1ade605cf6 100644 --- a/src/platform/telink/CHIPDevicePlatformConfig.h +++ b/src/platform/telink/CHIPDevicePlatformConfig.h @@ -108,7 +108,7 @@ #define CHIP_DEVICE_CONFIG_ENABLE_ETHERNET 0 #endif // CHIP_DEVICE_CONFIG_ENABLE_ETHERNET -#ifdef CONFIG_SOC_SERIES_RISCV_TELINK_B9X_RETENTION +#if CONFIG_SOC_RISCV_TELINK_TL323X || CONFIG_SOC_SERIES_RISCV_TELINK_B9X_RETENTION #define CHIP_DEVICE_CONFIG_EVENT_LOGGING_CRIT_BUFFER_SIZE (256) #define CHIP_DEVICE_CONFIG_EVENT_LOGGING_INFO_BUFFER_SIZE (256) #define CHIP_DEVICE_CONFIG_EVENT_LOGGING_DEBUG_BUFFER_SIZE (256) diff --git a/src/platform/telink/CHIPPlatformConfig.h b/src/platform/telink/CHIPPlatformConfig.h index 909ec261f5cf..9761e955306d 100644 --- a/src/platform/telink/CHIPPlatformConfig.h +++ b/src/platform/telink/CHIPPlatformConfig.h @@ -62,7 +62,19 @@ #define CHIP_CONFIG_BDX_MAX_NUM_TRANSFERS 1 #endif // CHIP_CONFIG_BDX_MAX_NUM_TRANSFERS -#if defined CONFIG_PM +#if defined(CONFIG_SOC_RISCV_TELINK_TL323X) + +#ifndef CHIP_CONFIG_MAX_GROUP_DATA_PEERS +#define CHIP_CONFIG_MAX_GROUP_DATA_PEERS 5 +#endif // CHIP_CONFIG_MAX_GROUP_DATA_PEERS + +#ifndef CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_ENTRIES_PER_FABRIC +#define CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_ENTRIES_PER_FABRIC 4 +#endif // CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_ENTRIES_PER_FABRIC + +#endif // defined(CONFIG_SOC_RISCV_TELINK_TL323X) + +#if defined CONFIG_PM || defined CONFIG_SOC_RISCV_TELINK_TL323X #ifndef CHIP_CONFIG_MAX_GROUP_DATA_PEERS #define CHIP_CONFIG_MAX_GROUP_DATA_PEERS 7 @@ -98,6 +110,14 @@ #define CHIP_CONFIG_MAX_FABRICS CONFIG_CHIP_MAX_FABRICS #endif // CHIP_CONFIG_MAX_FABRICS +#ifndef CHIP_CONFIG_MAX_GROUP_ENDPOINTS_PER_FABRIC +#define CHIP_CONFIG_MAX_GROUP_ENDPOINTS_PER_FABRIC 2 +#endif // CHIP_CONFIG_MAX_GROUP_ENDPOINTS_PER_FABRIC + +#ifndef CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_SUBJECTS_PER_ENTRY +#define CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_SUBJECTS_PER_ENTRY (4 * CHIP_CONFIG_MAX_GROUP_ENDPOINTS_PER_FABRIC) +#endif // CHIP_CONFIG_EXAMPLE_ACCESS_CONTROL_MAX_SUBJECTS_PER_ENTR + #ifdef CONFIG_CHIP_MAX_ACTIVE_CASE_CLIENTS #define CHIP_CONFIG_DEVICE_MAX_ACTIVE_CASE_CLIENTS CONFIG_CHIP_MAX_ACTIVE_CASE_CLIENTS #endif // CONFIG_CHIP_MAX_ACTIVE_CASE_CLIENTS diff --git a/src/platform/telink/FactoryDataParser.c b/src/platform/telink/FactoryDataParser.c index f271b8bfdcfb..5b5a3532db17 100644 --- a/src/platform/telink/FactoryDataParser.c +++ b/src/platform/telink/FactoryDataParser.c @@ -96,17 +96,29 @@ bool ParseFactoryData(uint8_t * buffer, uint16_t bufferSize, struct FactoryData } else if (strncmp("date", (const char *) currentString.value, currentString.len) == 0) { - // Date format is YYYY-MM-DD, so format needs to be validated and string parse to integer parts. - struct zcbor_string date; - res = res && zcbor_bstr_decode(states, &date); - if (date.len == 10 && isdigit(date.value[0]) && isdigit(date.value[1]) && isdigit(date.value[2]) && - isdigit(date.value[3]) && date.value[4] == '-' && isdigit(date.value[5]) && isdigit(date.value[6]) && - date.value[7] == '-' && isdigit(date.value[8]) && isdigit(date.value[9])) + // Format needs to be validated and string parse to integer parts. + struct zcbor_string * date = (struct zcbor_string *) &factoryData->mfg_date_str; + res = res && zcbor_bstr_decode(states, date); + + // First check for YYYY-MM-DD format (10 digits with hyphens) + if (date->len == 10 && isdigit(date->value[0]) && isdigit(date->value[1]) && isdigit(date->value[2]) && + isdigit(date->value[3]) && date->value[4] == '-' && isdigit(date->value[5]) && isdigit(date->value[6]) && + date->value[7] == '-' && isdigit(date->value[8]) && isdigit(date->value[9])) + { + factoryData->date_year = 1000 * (date->value[0] - '0') + 100 * (date->value[1] - '0') + + 10 * (date->value[2] - '0') + date->value[3] - '0'; + factoryData->date_month = 10 * (date->value[5] - '0') + date->value[6] - '0'; + factoryData->date_day = 10 * (date->value[8] - '0') + date->value[9] - '0'; + } + // Then check for YYYYMMDD or extended formats (8-16 digits) + else if ((date->len >= 8 && date->len <= 16) && isdigit(date->value[0]) && isdigit(date->value[1]) && + isdigit(date->value[2]) && isdigit(date->value[3]) && isdigit(date->value[4]) && isdigit(date->value[5]) && + isdigit(date->value[6]) && isdigit(date->value[7])) { - factoryData->date_year = - 1000 * (date.value[0] - '0') + 100 * (date.value[1] - '0') + 10 * (date.value[2] - '0') + date.value[3] - '0'; - factoryData->date_month = 10 * (date.value[5] - '0') + date.value[6] - '0'; - factoryData->date_day = 10 * (date.value[8] - '0') + date.value[9] - '0'; + factoryData->date_year = 1000 * (date->value[0] - '0') + 100 * (date->value[1] - '0') + + 10 * (date->value[2] - '0') + date->value[3] - '0'; + factoryData->date_month = 10 * (date->value[4] - '0') + date->value[5] - '0'; + factoryData->date_day = 10 * (date->value[6] - '0') + date->value[7] - '0'; } else { @@ -124,10 +136,16 @@ bool ParseFactoryData(uint8_t * buffer, uint16_t bufferSize, struct FactoryData else if (strncmp("dac_cert", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->dac_cert); + // LOG_INF("DAC cert len=%u", currentString.len); + // LOG_HEXDUMP_INF(currentString.value, + // currentString.len, + // "DAC CERT"); } else if (strncmp("dac_key", (const char *) currentString.value, currentString.len) == 0) { res = res && zcbor_bstr_decode(states, (struct zcbor_string *) &factoryData->dac_priv_key); + // LOG_INF("DAC priv key decrypted, len=%u", currentString.len); + // LOG_HEXDUMP_INF(currentString.value, currentString.len, "DAC PRIV KEY"); } else if (strncmp("pai_cert", (const char *) currentString.value, currentString.len) == 0) { @@ -181,3 +199,65 @@ bool ParseFactoryData(uint8_t * buffer, uint16_t bufferSize, struct FactoryData return res && zcbor_list_map_end_force_decode(states); } + +bool LoadDACCertAndKey(uint8_t * base_buffer, struct FactoryData * factoryData) +{ + size_t dac_priv_key_len; + uint8_t ieee_addr[8] = { 0 }; + uint8_t chip_id[16] = { 0 }; + /* get the dac key pair info form the flash directly*/ + uint8_t buffer[34] = { 0 }; + const struct device * mFlashDevice = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller)); + int ret = flash_read(mFlashDevice, FIXED_PARTITION_OFFSET(dac_keypair_partition), buffer, sizeof(buffer)); + + dac_priv_key_len = buffer[0]; + dac_priv_key_len |= (uint16_t) buffer[1] << 8; + factoryData->dac_priv_key.len = dac_priv_key_len; + if (!factoryData->dac_priv_key.len) + { + return false; + } + + if (efuse_get_ieee_addr(ieee_addr) == DRV_API_SUCCESS) + { + LOG_HEXDUMP_INF(ieee_addr, 8, "IEEE address"); + + memcpy(chip_id, ieee_addr, 8); + LOG_HEXDUMP_INF(chip_id, 16, "chip_id with IEEE address and zero padding"); + +#if CONFIG_SOC_RISCV_TELINK_TL323X + ske_dig_en(); + aes_decryption_be(chip_id, buffer + 2, dac_key_decrypt); + aes_decryption_be(chip_id, buffer + 18, dac_key_decrypt + 16); +#else + aes_decrypt(chip_id, buffer + 2, dac_key_decrypt); + aes_decrypt(chip_id, buffer + 18, dac_key_decrypt + 16); +#endif + + LOG_INF("[LoadDACCertAndKey]DAC priv key decrypted, len=%u", dac_priv_key_len); + LOG_HEXDUMP_INF(buffer + 2, dac_priv_key_len, "[LoadDACCertAndKey]Encrypted DAC PRIV KEY"); + LOG_HEXDUMP_INF(dac_key_decrypt, dac_priv_key_len, "[LoadDACCertAndKey]DAC PRIV KEY"); + factoryData->dac_priv_key.data = dac_key_decrypt; + } + else + { + LOG_ERR("Failed to get chip ID."); + return false; + } + + size_t dac_cert_len = 0; + flash_read(mFlashDevice, FIXED_PARTITION_OFFSET(dac_keypair_partition) + 100, buffer, 2); + dac_cert_len = buffer[0]; + dac_cert_len |= (uint16_t) buffer[1] << 8; + factoryData->dac_cert.len = dac_cert_len; + if (!factoryData->dac_cert.len) + { + return false; + } + factoryData->dac_cert.data = (uint8_t *) (0x20000000 + FIXED_PARTITION_OFFSET(dac_keypair_partition) + 102); + // LOG_INF("[LoadDACCertAndKey]DAC cert len=%u", dac_cert_len); + // LOG_HEXDUMP_INF(factoryData->dac_cert.data, factoryData->dac_cert.len, "DAC CERT"); + + return true; +} +#endif diff --git a/src/platform/telink/FactoryDataParser.h b/src/platform/telink/FactoryDataParser.h index de8f7c72da83..a1bee09651c8 100644 --- a/src/platform/telink/FactoryDataParser.h +++ b/src/platform/telink/FactoryDataParser.h @@ -21,6 +21,8 @@ #include #include +#include "CHIPDevicePlatformConfig.h" + #ifdef __cplusplus extern "C" { #endif @@ -46,6 +48,7 @@ struct FactoryData struct FactoryDataString product_url; struct FactoryDataString product_label; uint16_t hw_ver; + struct FactoryDataString mfg_date_str; struct FactoryDataString hw_ver_str; struct FactoryDataString rd_uid; struct FactoryDataString dac_cert; diff --git a/src/platform/telink/FactoryDataProvider.cpp b/src/platform/telink/FactoryDataProvider.cpp index de375d98a48f..d607b447e0ad 100644 --- a/src/platform/telink/FactoryDataProvider.cpp +++ b/src/platform/telink/FactoryDataProvider.cpp @@ -27,6 +27,8 @@ #include #include +LOG_MODULE_DECLARE(app, CONFIG_MATTER_LOG_LEVEL); + namespace chip { namespace { @@ -62,7 +64,6 @@ CHIP_ERROR GetFactoryDataString(const FactoryDataString & str, char * buf, size_ } // namespace namespace DeviceLayer { - template CHIP_ERROR FactoryDataProvider::Init() { @@ -98,15 +99,23 @@ CHIP_ERROR FactoryDataProvider::Init() if (error != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "Failed to read factory data partition"); + free(ptr); // Only free on failure return error; } if (!ParseFactoryData(factoryData, factoryDataSize, &mFactoryData)) { ChipLogError(DeviceLayer, "Failed to parse factory data"); + free(ptr); // Only free on failure return CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND; } + LOG_INF("[ParseFactoryData - ParseFactoryData] DAC priv key len=%u", mFactoryData.dac_priv_key.len); + LOG_HEXDUMP_INF(mFactoryData.dac_priv_key.data, mFactoryData.dac_priv_key.len, "DAC PRIV KEY"); + + LOG_INF("[ParseFactoryData - ParseFactoryData] DAC cert len=%u", mFactoryData.dac_cert.len); + LOG_HEXDUMP_INF(mFactoryData.dac_cert.data, mFactoryData.dac_cert.len, "DAC CERT"); + // Release the memory of mFactoryDataBuffer after complete parse free(ptr); @@ -197,11 +206,20 @@ CHIP_ERROR FactoryDataProvider::SignWithDeviceAttestationKey(c GetFactoryData(P_DACCert, mFactoryData.dac_cert.data, mFactoryData.dac_cert.len); + LOG_INF("[SignWithDeviceAttestationKey] DAC cert len=%u", mFactoryData.dac_cert.len); + LOG_HEXDUMP_INF(mFactoryData.dac_cert.data, mFactoryData.dac_cert.len, "mFactoryData.dac_cert.data"); + LOG_HEXDUMP_INF(P_DACCert, mFactoryData.dac_cert.len, "DAC CERT - P_DACCert"); + // Extract public key from DAC cert. ByteSpan dacCertSpan{ reinterpret_cast(P_DACCert), mFactoryData.dac_cert.len }; + LOG_INF("[SignWithDeviceAttestationKey] DAC cert len=%u", dacCertSpan.size()); + LOG_HEXDUMP_INF(dacCertSpan.data(), dacCertSpan.size(), "DAC CERT"); chip::Crypto::P256PublicKey dacPublicKey; error = chip::Crypto::ExtractPubkeyFromX509Cert(dacCertSpan, dacPublicKey); + LOG_INF("[SignWithDeviceAttestationKey] DAC pub key len=%u", dacPublicKey.Length()); + LOG_HEXDUMP_INF(dacPublicKey.Bytes(), dacPublicKey.Length(), "DAC PUB KEY"); + free(P_DACCert); if (error != CHIP_NO_ERROR) { @@ -221,6 +239,10 @@ CHIP_ERROR FactoryDataProvider::SignWithDeviceAttestationKey(c GetFactoryData(P_DACPrivKey, mFactoryData.dac_priv_key.data, mFactoryData.dac_priv_key.len); + LOG_INF("[SignWithDeviceAttestationKey] DAC priv key len=%u", mFactoryData.dac_priv_key.len); + LOG_HEXDUMP_INF(mFactoryData.dac_priv_key.data, mFactoryData.dac_priv_key.len, "mFactoryData.dac_priv_key.data"); + LOG_HEXDUMP_INF(P_DACPrivKey, mFactoryData.dac_priv_key.len, "DAC CERT - P_DACPrivKey"); + // Load keypair from raw. error = LoadKeypairFromRaw(ByteSpan(reinterpret_cast(P_DACPrivKey), mFactoryData.dac_priv_key.len), ByteSpan(dacPublicKey.Bytes(), dacPublicKey.Length()), keypair); @@ -362,6 +384,12 @@ CHIP_ERROR FactoryDataProvider::GetManufacturingDate(uint16_t return CHIP_NO_ERROR; } +template +CHIP_ERROR FactoryDataProvider::GetManufacturingDateString(char * buf, size_t bufSize) +{ + return GetFactoryDataString(mFactoryData.mfg_date_str, buf, bufSize); +} + template CHIP_ERROR FactoryDataProvider::GetHardwareVersion(uint16_t & hardwareVersion) { diff --git a/src/platform/telink/SystemPlatformConfig.h b/src/platform/telink/SystemPlatformConfig.h index 259129af951d..9d4f10b16fc1 100644 --- a/src/platform/telink/SystemPlatformConfig.h +++ b/src/platform/telink/SystemPlatformConfig.h @@ -44,8 +44,8 @@ struct ChipDeviceEvent; #define CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME 1 #endif // CHIP_SYSTEM_CONFIG_USE_POSIX_TIME_FUNCTS -#if defined(CONFIG_SOC_RISCV_TELINK_TL721X) || defined(CONFIG_SOC_SERIES_RISCV_TELINK_B9X_RETENTION) || \ - defined(CONFIG_SOC_RISCV_TELINK_W91) +#if defined(CONFIG_SOC_RISCV_TELINK_TL323X) || defined(CONFIG_SOC_RISCV_TELINK_TL721X) || \ + defined(CONFIG_SOC_SERIES_RISCV_TELINK_B9X_RETENTION) || defined(CONFIG_SOC_RISCV_TELINK_W91 #define CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_HEAP 1 #define CHIP_SYSTEM_PACKETBUFFER_FROM_CHIP_POOL 0 #define CHIP_SYSTEM_CONFIG_POOL_USE_HEAP 1 @@ -56,7 +56,8 @@ 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_TL721X) || defined(CONFIG_SOC_RISCV_TELINK_W91) +#if defined(CONFIG_PM) || defined(CONFIG_SOC_RISCV_TELINK_TL323X) || defined(CONFIG_SOC_RISCV_TELINK_TL721X) || \ + defined(CONFIG_SOC_RISCV_TELINK_W91) #define CHIP_SYSTEM_CONFIG_PACKETBUFFER_POOL_SIZE 0 #else #define CHIP_SYSTEM_CONFIG_PACKETBUFFER_POOL_SIZE 8 diff --git a/src/platform/telink/tl3238x_2m_flash.overlay b/src/platform/telink/tl3238x_2m_flash.overlay new file mode 100644 index 000000000000..d08d0e4ff4a6 --- /dev/null +++ b/src/platform/telink/tl3238x_2m_flash.overlay @@ -0,0 +1,35 @@ +&flash { + reg = <0x20000000 0x200000>; + + /delete-node/ partitions; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0xf000>; + }; + slot0_partition: partition@f000 { + label = "image-0"; + reg = <0xf000 0xf1000>; + }; + factory_partition: partition@100000 { + label = "factory-data"; + reg = <0x100000 0x1000>; + }; + storage_partition: partition@101000 { + label = "storage"; + reg = <0x101000 0xc000>; + }; + slot1_partition: partition@10d000 { + label = "image-1"; + reg = <0x10d000 0xf1000>; + }; + vendor_partition: partition@1fe000 { + label = "vendor-data"; + reg = <0x1fe000 0x2000>; + }; + }; +}; diff --git a/src/platform/telink/tl3238x_2m_flash_backup.overlay b/src/platform/telink/tl3238x_2m_flash_backup.overlay new file mode 100644 index 000000000000..e4535fa5782e --- /dev/null +++ b/src/platform/telink/tl3238x_2m_flash_backup.overlay @@ -0,0 +1,55 @@ +&flash { + reg = <0x20000000 0x200000>; + + /delete-node/ partitions; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; +// reserve 20k for software bootloader , 64k for mcuboot,need to disable log for mcuboot; + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0x15000>; + }; + slot0_partition: partition@15000 { + label = "image-0"; + reg = <0x15000 0xe5000>; // total slot0 is 916k + }; + slot1_partition: partition@fa000 { + label = "image-1"; + reg = <0xfa000 0xe6000>; // total slot1 is 916k + }; + user_rfu_partition: partition@1e0000 { + label = "user_rfu"; + reg = <0x1e0000 0x4000>; + }; + storage_partition: partition@1e4000 { + label = "matter-nvs"; + reg = <0x1e4000 0x8000>; // matter nvs part , total is 32k. + }; + zigbee_nvs_partition: partition@1ec000 { + label = "zigbee-nvs"; + reg = <0x1ec000 0xc000>; // zigbee nvs part , total is 48k. + }; + secure_partition: partition@1f8000 { + label = "secure"; + reg = <0x1f8000 0x1000>; // secure info ,reserved for secure boot .if not use , can beused by other way . + }; + user_config_partition: partition@1f9000 { + label = "user_config"; + reg = <0x1f9000 0x1000>; // user_config ,for the secure-boot sign logic changes ,it can release for user . + }; + dac_keypair_partition: partition@1fc000 { + label = "dac-keypair"; + reg = <0x1fc000 0x1000>; // store dac and key pair. + }; + factory_partition: partition@1fd000 { + label = "factory-data"; + reg = <0x1fd000 0x1000>; // factory data info + }; + vendor_partition: partition@1fe000 { + label = "vendor-data"; + reg = <0x1fe000 0x2000>; // mac and rf info. + }; + }; +}; diff --git a/src/platform/telink/tl3238x_2m_lzma_flash.overlay b/src/platform/telink/tl3238x_2m_lzma_flash.overlay new file mode 100644 index 000000000000..a57f71985fbb --- /dev/null +++ b/src/platform/telink/tl3238x_2m_lzma_flash.overlay @@ -0,0 +1,75 @@ +&flash { + reg = <0x20000000 0x200000>; + + /delete-node/ partitions; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; +// reserve 20k for software bootloader , 64k for mcuboot,need to disable log for mcuboot; + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0x15000>; + }; +// currently the reset flash is 0x1e0000- 0x15000() = 0x1cb000 = 1836k (the total of slot0,and slot1) +// tralor = 20k +// slot0_fw = (1836-20)/1.6/4*4 = 1132 (suppose as LZMA:60%) +// slot0 area = 1132+20 = 1152(0x120000) + slot0_partition: partition@15000 { + label = "image-0"; + reg = <0x15000 0x120000>; // total slot0 is 1152k + }; +// add information , increase part for matter & zigbee +// suppose the matter application layer is 30k, zigbee is 60k +// currently , basic matter fw is 813k, zigbee is 140k , so just suppose matter is 916k, and zigbee is 216, reserve 20k for matter ota trailor + slot0_matter_partition: partition@15000_1 { + label = "image-0-matter"; + reg = <0x15000 0xe5000>; // suppose matter is 916k (0xe5000) + }; + slot0_zb_partition: partition@fa000 { + label = "image-0-zb"; + reg = <0xfa000 0x36000>; // suppose zigbee is 216k (0x36000) + }; + slot0_trailor_partition: partition@130000 { + label = "image-0-trailor"; + reg = <0x130000 0x5000>; // suppose trailor is 20k + }; +// the total fw area is 1836k, and slot0 cost 1152, so the value is 1836 - 1152 = 684k (0xac000) */ + slot1_partition: partition@135000 { + label = "image-1"; + reg = <0x135000 0xab000>; + }; + user_rfu_partition: partition@1e0000 { + label = "user_rfu"; + reg = <0x1e0000 0x4000>; + }; + storage_partition: partition@1e4000 { + label = "matter-nvs"; + reg = <0x1e4000 0x8000>; // matter nvs part , total is 32k. + }; + zigbee_nvs_partition: partition@1ec000 { + label = "zigbee-nvs"; + reg = <0x1ec000 0xc000>; // zigbee nvs part , total is 48k. + }; + secure_partition: partition@1f8000 { + label = "secure"; + reg = <0x1f8000 0x1000>; // secure info ,reserved for secure boot .if not use , can beused by other way . + }; + user_config_partition: partition@1f9000 { + label = "user_config"; + reg = <0x1f9000 0x1000>; // user_config ,for the secure-boot sign logic changes ,it can release for user . + }; + dac_keypair_partition: partition@1fc000 { + label = "dac-keypair"; + reg = <0x1fc000 0x1000>; // store dac and key pair. + }; + factory_partition: partition@1fd000 { + label = "factory-data"; + reg = <0x1fd000 0x1000>; // factory data info + }; + vendor_partition: partition@1fe000 { + label = "vendor-data"; + reg = <0x1fe000 0x2000>; // mac and rf info. + }; + }; +}; diff --git a/src/platform/telink/tl3238x_4m_flash.overlay b/src/platform/telink/tl3238x_4m_flash.overlay new file mode 100644 index 000000000000..4097acea3e58 --- /dev/null +++ b/src/platform/telink/tl3238x_4m_flash.overlay @@ -0,0 +1,70 @@ +&flash { + reg = <0x20000000 0x400000>; + + /delete-node/ partitions; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; +// reserve 20k for software bootloader , 64k for mcuboot,need to disable log for mcuboot; + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0x16000>; + }; + slot0_partition: partition@16000 { + label = "image-0"; + reg = <0x16000 0x1e5000>; // total slot0 is 1940k + }; +// add information , increase part for matter & zigbee +// currently , matter slot0 is 1640k, zigbee is 276k ,reserve 20k for matter ota trailor + slot0_matter_partition: partition@16000_1 { + label = "image-0-matter"; + reg = <0x16000 0x19a000>; // suppose matter is 1640k (0x19a000) + }; + slot0_zb_partition: partition@1b0000 { + label = "image-0-zb"; + reg = <0x1b0000 0x46000>; // suppose zigbee is 280k (0x46000) + }; + slot0_trailor_partition: partition@1f6000 { + label = "image-0-trailor"; + reg = <0x1f6000 0x5000>; // suppose trailor is 20k + }; +// the total fw area is 1940k */ + slot1_partition: partition@1fb000 { + label = "image-1"; + reg = <0x1fb000 0x1e5000>; + }; + user_rfu_partition: partition@3e0000 { + label = "user_rfu"; + reg = <0x3e0000 0x4000>; + }; + storage_partition: partition@3e4000 { + label = "matter-nvs"; + reg = <0x3e4000 0x8000>; // matter nvs part , total is 32k. + }; + zigbee_nvs_partition: partition@3ec000 { + label = "zigbee-nvs"; + reg = <0x3ec000 0xc000>; // zigbee nvs part , total is 48k. + }; + secure_partition: partition@3f8000 { + label = "secure"; + reg = <0x3f8000 0x1000>; // secure info ,reserved for secure boot .if not use , can beused by other way . + }; + user_config_partition: partition@3f9000 { + label = "user_config"; + reg = <0x3f9000 0x1000>; // user_config ,for the secure-boot sign logic changes ,it can release for user . + }; + dac_keypair_partition: partition@3fc000 { + label = "dac-keypair"; + reg = <0x3fc000 0x1000>; // store dac and key pair. + }; + factory_partition: partition@3fd000 { + label = "factory-data"; + reg = <0x3fd000 0x1000>; // factory data info + }; + vendor_partition: partition@3fe000 { + label = "vendor-data"; + reg = <0x3fe000 0x2000>; // mac and rf info. + }; + }; +}; diff --git a/src/platform/telink/tl3238x_4m_lzma_flash.overlay b/src/platform/telink/tl3238x_4m_lzma_flash.overlay new file mode 100644 index 000000000000..4097acea3e58 --- /dev/null +++ b/src/platform/telink/tl3238x_4m_lzma_flash.overlay @@ -0,0 +1,70 @@ +&flash { + reg = <0x20000000 0x400000>; + + /delete-node/ partitions; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; +// reserve 20k for software bootloader , 64k for mcuboot,need to disable log for mcuboot; + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0x16000>; + }; + slot0_partition: partition@16000 { + label = "image-0"; + reg = <0x16000 0x1e5000>; // total slot0 is 1940k + }; +// add information , increase part for matter & zigbee +// currently , matter slot0 is 1640k, zigbee is 276k ,reserve 20k for matter ota trailor + slot0_matter_partition: partition@16000_1 { + label = "image-0-matter"; + reg = <0x16000 0x19a000>; // suppose matter is 1640k (0x19a000) + }; + slot0_zb_partition: partition@1b0000 { + label = "image-0-zb"; + reg = <0x1b0000 0x46000>; // suppose zigbee is 280k (0x46000) + }; + slot0_trailor_partition: partition@1f6000 { + label = "image-0-trailor"; + reg = <0x1f6000 0x5000>; // suppose trailor is 20k + }; +// the total fw area is 1940k */ + slot1_partition: partition@1fb000 { + label = "image-1"; + reg = <0x1fb000 0x1e5000>; + }; + user_rfu_partition: partition@3e0000 { + label = "user_rfu"; + reg = <0x3e0000 0x4000>; + }; + storage_partition: partition@3e4000 { + label = "matter-nvs"; + reg = <0x3e4000 0x8000>; // matter nvs part , total is 32k. + }; + zigbee_nvs_partition: partition@3ec000 { + label = "zigbee-nvs"; + reg = <0x3ec000 0xc000>; // zigbee nvs part , total is 48k. + }; + secure_partition: partition@3f8000 { + label = "secure"; + reg = <0x3f8000 0x1000>; // secure info ,reserved for secure boot .if not use , can beused by other way . + }; + user_config_partition: partition@3f9000 { + label = "user_config"; + reg = <0x3f9000 0x1000>; // user_config ,for the secure-boot sign logic changes ,it can release for user . + }; + dac_keypair_partition: partition@3fc000 { + label = "dac-keypair"; + reg = <0x3fc000 0x1000>; // store dac and key pair. + }; + factory_partition: partition@3fd000 { + label = "factory-data"; + reg = <0x3fd000 0x1000>; // factory data info + }; + vendor_partition: partition@3fe000 { + label = "vendor-data"; + reg = <0x3fe000 0x2000>; // mac and rf info. + }; + }; +};