From 42a7b0adfbdf154a8f75e32f001be224594dd047 Mon Sep 17 00:00:00 2001 From: Artur Tynecki <77382963+ATmobica@users.noreply.github.com> Date: Wed, 26 Jan 2022 05:46:24 +0100 Subject: [PATCH] [Mbed] Add OTA-requestor-app and DFU support (#12616) * Add Mbed OTA requestor app Add Mbed MCUboot to third_party Add Mbed bootloader application Add build applicaiton with bootloader option to mbed_example.sh script Improve mbed launch tasks in launch.json Move capsense lib including to common cmake file Update mbed-os-posix-socket module * Add debug Mbed bootloader option to launch.json * Mbed MCUboot update --- .github/.wordlist.txt | 2 + .github/workflows/examples-mbed.yaml | 11 +- .gitmodules | 3 + .vscode/launch.json | 65 ++- .vscode/tasks.json | 17 +- config/mbed/CMakeLists.txt | 71 +++ examples/all-clusters-app/mbed/README.md | 14 +- examples/lighting-app/mbed/CMakeLists.txt | 5 - examples/lighting-app/mbed/README.md | 14 +- examples/lock-app/mbed/CMakeLists.txt | 5 - examples/lock-app/mbed/README.md | 14 +- examples/ota-requestor-app/mbed/.gitignore | 2 + .../ota-requestor-app/mbed/CMakeLists.txt | 78 +++ examples/ota-requestor-app/mbed/README.md | 306 ++++++++++ examples/ota-requestor-app/mbed/config.in | 6 + .../ota-requestor-app/mbed/main/AppTask.cpp | 416 +++++++++++++ .../mbed/main/include/AppEvent.h | 47 ++ .../mbed/main/include/AppTask.h | 70 +++ .../mbed/main/include/CHIPProjectConfig.h | 41 ++ examples/ota-requestor-app/mbed/main/main.cpp | 112 ++++ examples/ota-requestor-app/mbed/mbed_app.json | 77 +++ examples/pigweed-app/mbed/README.md | 14 +- examples/platform/mbed/bootloader/.gitignore | 6 + .../platform/mbed/bootloader/CMakeLists.txt | 41 ++ .../platform/mbed/bootloader/default_bd.cpp | 78 +++ .../platform/mbed/bootloader/mbed_app.json | 38 ++ examples/shell/mbed/README.md | 14 +- scripts/examples/mbed_example.sh | 114 +++- src/platform/mbed/BUILD.gn | 2 +- src/platform/mbed/CHIPDevicePlatformConfig.h | 2 + src/platform/mbed/OTAImageProcessorImpl.cpp | 545 ++++++++++++++++++ src/platform/mbed/OTAImageProcessorImpl.h | 97 ++++ third_party/mbed-mcu-boot/repo | 1 + third_party/mbed-os-posix-socket/repo | 2 +- 34 files changed, 2296 insertions(+), 34 deletions(-) create mode 100644 examples/ota-requestor-app/mbed/.gitignore create mode 100644 examples/ota-requestor-app/mbed/CMakeLists.txt create mode 100644 examples/ota-requestor-app/mbed/README.md create mode 100644 examples/ota-requestor-app/mbed/config.in create mode 100644 examples/ota-requestor-app/mbed/main/AppTask.cpp create mode 100644 examples/ota-requestor-app/mbed/main/include/AppEvent.h create mode 100644 examples/ota-requestor-app/mbed/main/include/AppTask.h create mode 100644 examples/ota-requestor-app/mbed/main/include/CHIPProjectConfig.h create mode 100644 examples/ota-requestor-app/mbed/main/main.cpp create mode 100644 examples/ota-requestor-app/mbed/mbed_app.json create mode 100644 examples/platform/mbed/bootloader/.gitignore create mode 100644 examples/platform/mbed/bootloader/CMakeLists.txt create mode 100644 examples/platform/mbed/bootloader/default_bd.cpp create mode 100644 examples/platform/mbed/bootloader/mbed_app.json create mode 100644 src/platform/mbed/OTAImageProcessorImpl.cpp create mode 100644 src/platform/mbed/OTAImageProcessorImpl.h create mode 160000 third_party/mbed-mcu-boot/repo diff --git a/.github/.wordlist.txt b/.github/.wordlist.txt index 3fd0aede54f001..a9d259d718bf84 100644 --- a/.github/.wordlist.txt +++ b/.github/.wordlist.txt @@ -43,6 +43,7 @@ alloc Ameba amebad amebaiot +announcementReason AnnounceOTAProvider APIs apk @@ -783,6 +784,7 @@ pre preprocessor Presetup prj +providerNodeId ProductID ProductLabel ProductName diff --git a/.github/workflows/examples-mbed.yaml b/.github/workflows/examples-mbed.yaml index 8561c5cba1d5c6..71bc29df1018ea 100644 --- a/.github/workflows/examples-mbed.yaml +++ b/.github/workflows/examples-mbed.yaml @@ -26,7 +26,7 @@ concurrency: jobs: mbedos: name: Mbed OS examples building - timeout-minutes: 70 + timeout-minutes: 80 env: BUILD_TYPE: mbedos @@ -110,6 +110,15 @@ jobs: mbed $APP_TARGET+$APP_PROFILE shell \ examples/shell/mbed/build-CY8CPROTO_062_4343W/release/chip-mbed-shell-example.elf \ /tmp/bloat_reports/ + + - name: Build ota-requestor-app example + timeout-minutes: 10 + run: | + scripts/examples/mbed_example.sh -a=ota-requestor-app -b=$APP_TARGET -p=$APP_PROFILE + .environment/pigweed-venv/bin/python3 scripts/tools/memory/gh_sizes.py \ + mbed $APP_TARGET+$APP_PROFILE shell \ + examples/ota-requestor-app/mbed/build-CY8CPROTO_062_4343W/release/chip-mbed-ota-requestor-app-example.elf \ + /tmp/bloat_reports/ - name: Build unit tests timeout-minutes: 10 diff --git a/.gitmodules b/.gitmodules index 6a548e76dcb3b4..477eb44fbd22d3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -181,3 +181,6 @@ [submodule "cyw30739_sdk/tools"] path = third_party/cyw30739_sdk/repos/btsdk-tools url = https://github.com/Infineon/btsdk-tools.git +[submodule "third_party/mbed-mcu-boot/repo"] + path = third_party/mbed-mcu-boot/repo + url = https://github.com/ATmobica/mcuboot.git diff --git a/.vscode/launch.json b/.vscode/launch.json index f89d0eaf3c462b..dcd5881b5dfde2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -264,7 +264,7 @@ "configFiles": ["${input:mbedTarget}.tcl"], "overrideLaunchCommands": [ "monitor reset halt", - "monitor program {./build-${input:mbedTarget}/${input:mbedFlashProfile}/chip-mbed-unit-tests}", + "monitor program {./build-${input:mbedTarget}/${input:mbedFlashProfile}/chip-mbed-unit-tests.hex}", "monitor reset run", "quit" ], @@ -289,6 +289,66 @@ "quit" ], "showDevDebugOutput": false // When set to true, displays output of GDB. + }, + + { + "name": "Debug Mbed bootloader", + "type": "cortex-debug", + "request": "launch", + "cwd": "${workspaceRoot}/examples/platform/mbed/bootloader", + "executable": "./build-${input:mbedTarget}/${input:mbedDebugProfile}/chip-mbed-bootloader.elf", + "armToolchainPath": "${env:PW_ENVIRONMENT_ROOT}/cipd/pigweed/bin/", // Pigweed environment bootstraping required + "servertype": "openocd", + "openocdPath": "${env:OPENOCD_PATH/bin}", + "searchDir": [ + "${workspaceRoot}/config/mbed/scripts", + "${env:OPENOCD_PATH}/scripts" + ], + "configFiles": ["${input:mbedTarget}.tcl"], + "overrideLaunchCommands": [ + "-enable-pretty-printing", + "monitor program {./build-${input:mbedTarget}/${input:mbedDebugProfile}/chip-mbed-bootloader.hex}", + "monitor reset run", + "monitor sleep 200", + "monitor psoc6 reset_halt sysresetreq" + ], + "numberOfProcessors": 2, + "targetProcessor": 1, // Set to 0 for the CM0+, set to 1 for the CM4 + "overrideRestartCommands": [ + "monitor reset init", + "monitor reset run", + "monitor sleep 200", + "monitor psoc6 reset_halt sysresetreq" + ], + "runToMain": true, // if true, program will halt at main. Not used for a restart + "showDevDebugOutput": false // When set to true, displays output of GDB. + }, + + { + "name": "Debug Mbed bootloader [remote]", + "type": "cortex-debug", + "request": "launch", + "cwd": "${workspaceRoot}/examples/platform/mbed/bootloader", + "executable": "./build-${input:mbedTarget}/${input:mbedDebugProfile}/chip-mbed-bootloader.elf", + "armToolchainPath": "${env:PW_ENVIRONMENT_ROOT}/cipd/pigweed/bin/", // Pigweed environment bootstraping required + "servertype": "external", + "gdbTarget": "host.docker.internal:3334", //port 3333 for the CM0+, 3334 for the CM4 + "overrideLaunchCommands": [ + "-enable-pretty-printing", + "monitor reset halt", + "load ./build-${input:mbedTarget}/${input:mbedDebugProfile}/chip-mbed-bootloader.hex", + "monitor reset run", + "monitor sleep 200", + "monitor psoc6 reset_halt sysresetreq" + ], + "overrideRestartCommands": [ + "monitor reset init", + "monitor reset run", + "monitor sleep 200", + "monitor psoc6 reset_halt sysresetreq" + ], + "runToMain": true, // if true, program will halt at main. Not used for a restart + "showDevDebugOutput": false // When set to true, displays output of GDB. } ], "inputs": [ @@ -315,7 +375,8 @@ "lighting-app", "pigweed-app", "all-clusters-app", - "shell" + "shell", + "ota-requestor-app" ], "default": "lock-app" }, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index a0f49412f87aed..903b1cbfa4cad8 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -142,7 +142,8 @@ "-c=${input:mbedCommand}", "-a=${input:mbedApp}", "-b=${input:mbedTarget}", - "-p=${input:mbedProfile}" + "-p=${input:mbedProfile}", + "-T=${input:mbedAppType}" ], "group": "build", "problemMatcher": { @@ -200,7 +201,8 @@ "lighting-app", "pigweed-app", "all-clusters-app", - "shell" + "shell", + "ota-requestor-app" ], "default": "lock-app" }, @@ -215,8 +217,15 @@ "type": "pickString", "id": "mbedProfile", "description": "What mbed profile do you want to use?", - "options": ["develop", "release", "debug"], - "default": "develop" + "options": ["release", "develop", "debug"], + "default": "release" + }, + { + "type": "pickString", + "id": "mbedAppType", + "description": "What mbed application type do you want to use?", + "options": ["simple", "boot", "upgrade"], + "default": "simple" }, { "type": "promptString", diff --git a/config/mbed/CMakeLists.txt b/config/mbed/CMakeLists.txt index b3e362e6ece434..feae8df84b8802 100644 --- a/config/mbed/CMakeLists.txt +++ b/config/mbed/CMakeLists.txt @@ -279,6 +279,10 @@ if (CONFIG_CHIP_PW_RPC) chip_gn_arg_bool ("chip_build_pw_rpc_lighting_proto" CONFIG_CHIP_PW_RPC_LIGHTING_PROTO) chip_gn_arg_bool ("chip_build_pw_rpc_locking_proto" CONFIG_CHIP_PW_RPC_LOCKING_PROTO) endif(CONFIG_CHIP_PW_RPC) +if (CONFIG_CHIP_OTA_REQUESTOR) + chip_gn_arg_bool ("chip_enable_ota_requestor" CONFIG_CHIP_OTA_REQUESTOR) +endif(CONFIG_CHIP_OTA_REQUESTOR) + file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/args.gn CONTENT ${CHIP_GN_ARGS}) @@ -325,6 +329,12 @@ list(APPEND CHIP_INCLUDES) # CHIP defines list(APPEND CHIP_DEFINES) +# Target specific configuration +if("capsense" IN_LIST MBED_TARGET_LABELS) + add_subdirectory(${CHIP_ROOT}/third_party/mbed-os-cypress-capsense-button/repo ${CMAKE_BINARY_DIR}/capsense_build) + target_link_libraries(${APP_TARGET} capsense) +endif() + list(APPEND CHIP_INCLUDES ${CHIP_ROOT}/config/mbed/mbedtls ) @@ -442,6 +452,67 @@ endif(CONFIG_CHIP_PW_RPC_LOCKING_PROTO) endif(CONFIG_CHIP_PW_RPC) +if (CONFIG_CHIP_OTA_REQUESTOR) + target_include_directories(${APP_TARGET} PRIVATE + ${CHIP_ROOT}/zzz_generated/ota-requestor-app + ${CHIP_ROOT}/src/app/clusters/ota-requestor + ${CHIP_ROOT}/src/platform + ${CHIP_ROOT}/src/platform/mbed + ${CHIP_ROOT}/src/include/platform + ) + + target_sources(${APP_TARGET} PRIVATE + ${CHIP_ROOT}/zzz_generated/ota-requestor-app/zap-generated/callback-stub.cpp + ${CHIP_ROOT}/zzz_generated/ota-requestor-app/zap-generated/CHIPClusters.cpp + ${CHIP_ROOT}/zzz_generated/ota-requestor-app/zap-generated/IMClusterCommandHandler.cpp + + ${CHIP_ROOT}/src/app/clusters/ota-requestor/OTARequestor.cpp + ${CHIP_ROOT}/src/app/clusters/ota-requestor/BDXDownloader.cpp + ${CHIP_ROOT}/src/app/clusters/ota-requestor/ota-requestor-server.cpp + + ${CHIP_ROOT}/src/platform/mbed/OTAImageProcessorImpl.cpp + ) + + list(APPEND CHIP_DEFINES + CHIP_OTA_REQUESTOR=1 + ) +endif(CONFIG_CHIP_OTA_REQUESTOR) + +if(BOOT_ENABLED) + add_subdirectory(${MCUBOOT_PATH}/boot/bootutil/ ${CMAKE_BINARY_DIR}/mbed_mcu_boot_util_build) + add_subdirectory(${MCUBOOT_PATH}/boot/mbed/ ${CMAKE_BINARY_DIR}/mbed_mcu_boot_build) + + target_include_directories(${APP_TARGET} PRIVATE + ${MCUBOOT_PATH}/boot/mbed/include + ) + + target_sources(${APP_TARGET} PRIVATE + ${CHIP_ROOT}/examples/platform/mbed/bootloader/default_bd.cpp + ) + + target_include_directories(bootutil PUBLIC + ${CHIP_ROOT}/config/mbed/mbedtls + ) + + target_link_libraries(${APP_TARGET} mbed-mcuboot bootutil) + + file(READ ${APP_PATH}/mbed_app.json mbedAppJson) + string(JSON PRIMARY_SLOT_ADDRESS GET "${mbedAppJson}" target_overrides ${MBED_TARGET} mcuboot.primary-slot-address) + string(JSON HEADER_SIZE GET "${mbedAppJson}" target_overrides ${MBED_TARGET} mcuboot.header-size) + string(JSON SLOT_SIZE GET "${mbedAppJson}" target_overrides ${MBED_TARGET} mcuboot.slot-size) + math(EXPR APP_START "${PRIMARY_SLOT_ADDRESS} + ${HEADER_SIZE}" OUTPUT_FORMAT HEXADECIMAL) + math(EXPR APP_SIZE "${SLOT_SIZE} - 2 * ${HEADER_SIZE}" OUTPUT_FORMAT HEXADECIMAL) + target_compile_definitions(mbed-core + INTERFACE + "-DMBED_APP_START=${APP_START}" + "-DMBED_APP_SIZE=${APP_SIZE}" + ) + + list(APPEND CHIP_DEFINES + BOOT_ENABLED=1 + ) +endif() + target_include_directories(${APP_TARGET} PRIVATE ${CHIP_INCLUDES} diff --git a/examples/all-clusters-app/mbed/README.md b/examples/all-clusters-app/mbed/README.md index 45224ea31ecd8b..1a9d7ca76d3242 100644 --- a/examples/all-clusters-app/mbed/README.md +++ b/examples/all-clusters-app/mbed/README.md @@ -111,13 +111,13 @@ example ported to the mbed-os platform. - **by using generic vscode task**: ``` -Command Palette (F1) => Run Task... => Run Mbed Application => build => all-clusters-app => (board name) => (build profile)` +Command Palette (F1) => Run Task... => Run Mbed Application => build => all-clusters-app => (board name) => (build profile) => (build type) ``` - **by calling explicitly building script:** ``` -${MATTER_ROOT}/scripts/examples/mbed_example.sh -c=build -a=all-clusters-app -b= -p= +${MATTER_ROOT}/scripts/examples/mbed_example.sh -c=build -a=all-clusters-app -b= -p= -T= ``` Both approaches are limited to supported evaluation boards which are listed in @@ -127,6 +127,16 @@ Mbed OS defines three building profiles: _develop, debug_ and _release_. For more details please visit [ARM Mbed OS build profiles](https://os.mbed.com/docs/mbed-os/latest/program-setup/build-profiles-and-rules.html). +There are also three types of built application: _simple, boot_ and _upgrade_: + +- **simple** - standalone application, mainly for developing and testing + purpose (all building profiles are supported) +- **boot** - signed application + bootloader, it supports booting process and + can be use for firmware update (only _release_ building profiles is + supported) +- **update** - signed application, application image can be used for firmware + update (only _release_ building profiles is supported) + When using the building script, it is possible expand the list of acceptable targets; this may be useful for rapid testing of a new mbed-targets. diff --git a/examples/lighting-app/mbed/CMakeLists.txt b/examples/lighting-app/mbed/CMakeLists.txt index 73df019875d8ac..2f0f20a99142b7 100644 --- a/examples/lighting-app/mbed/CMakeLists.txt +++ b/examples/lighting-app/mbed/CMakeLists.txt @@ -110,11 +110,6 @@ if(MBED_TARGET STREQUAL "CY8CPROTO_062_4343W") target_link_libraries(${APP_TARGET} mbed-cy-psoc6-common-network) endif() -if("capsense" IN_LIST MBED_TARGET_LABELS) - add_subdirectory(${CHIP_ROOT}/third_party/mbed-os-cypress-capsense-button/repo ./capsense_build) - target_link_libraries(${APP_TARGET} capsense) -endif() - mbed_set_post_build(${APP_TARGET}) option(VERBOSE_BUILD "Have a verbose build process") diff --git a/examples/lighting-app/mbed/README.md b/examples/lighting-app/mbed/README.md index 768e62aa9f3aec..146c4a74b55740 100644 --- a/examples/lighting-app/mbed/README.md +++ b/examples/lighting-app/mbed/README.md @@ -124,13 +124,13 @@ example ported to the mbed-os platform. - **by using generic vscode task**: ``` -Command Palette (F1) => Run Task... => Run Mbed Application => build => lighting-app => (board name) => (build profile) +Command Palette (F1) => Run Task... => Run Mbed Application => build => lighting-app => (board name) => (build profile) => (build type) ``` - **by calling explicitly building script:** ``` -${MATTER_ROOT}/scripts/examples/mbed_example.sh -c=build -a=lighting-app -b= -p= +${MATTER_ROOT}/scripts/examples/mbed_example.sh -c=build -a=lighting-app -b= -p= -T= ``` Both approaches are limited to supported evaluation boards which are listed in @@ -140,6 +140,16 @@ Mbed OS defines three building profiles: _develop, debug_ and _release_. For more details please visit [ARM Mbed OS build profiles](https://os.mbed.com/docs/mbed-os/latest/program-setup/build-profiles-and-rules.html). +There are also three types of built application: _simple, boot_ and _upgrade_: + +- **simple** - standalone application, mainly for developing and testing + purpose (all building profiles are supported) +- **boot** - signed application + bootloader, it supports booting process and + can be use for firmware update (only _release_ building profiles is + supported) +- **update** - signed application, application image can be used for firmware + update (only _release_ building profiles is supported) + When using the building script, it is possible expand the list of acceptable targets; this may be useful for rapid testing of a new mbed-targets. diff --git a/examples/lock-app/mbed/CMakeLists.txt b/examples/lock-app/mbed/CMakeLists.txt index e83e6929bc485c..4bb1ba9c9267a4 100644 --- a/examples/lock-app/mbed/CMakeLists.txt +++ b/examples/lock-app/mbed/CMakeLists.txt @@ -102,11 +102,6 @@ if(MBED_TARGET STREQUAL "CY8CPROTO_062_4343W") target_link_libraries(${APP_TARGET} mbed-cy-psoc6-common-network) endif() -if("capsense" IN_LIST MBED_TARGET_LABELS) - add_subdirectory(${CHIP_ROOT}/third_party/mbed-os-cypress-capsense-button/repo ./capsense_build) - target_link_libraries(${APP_TARGET} capsense) -endif() - mbed_set_post_build(${APP_TARGET}) option(VERBOSE_BUILD "Have a verbose build process") diff --git a/examples/lock-app/mbed/README.md b/examples/lock-app/mbed/README.md index ca1b085a0815b2..55cefaa9adb926 100644 --- a/examples/lock-app/mbed/README.md +++ b/examples/lock-app/mbed/README.md @@ -114,13 +114,13 @@ ported to the mbed-os platform. - **by using generic vscode task**: ``` -Command Palette (F1) => Run Task... => Run Mbed Application => build => lock-app => (board name) => (build profile) +Command Palette (F1) => Run Task... => Run Mbed Application => build => lock-app => (board name) => (build profile) => (build type) ``` - **by calling explicitly building script:** ``` -${MATTER_ROOT}/scripts/examples/mbed_example.sh -c=build -a=lock-app -b= -p= +${MATTER_ROOT}/scripts/examples/mbed_example.sh -c=build -a=lock-app -b= -p= -T= ``` Both approaches are limited to supported evaluation boards which are listed in @@ -130,6 +130,16 @@ Mbed OS defines three building profiles: _develop, debug_ and _release_. For more details please visit [ARM Mbed OS build profiles](https://os.mbed.com/docs/mbed-os/latest/program-setup/build-profiles-and-rules.html). +There are also three types of built application: _simple, boot_ and _upgrade_: + +- **simple** - standalone application, mainly for developing and testing + purpose (all building profiles are supported) +- **boot** - signed application + bootloader, it supports booting process and + can be use for firmware update (only _release_ building profiles is + supported) +- **update** - signed application, application image can be used for firmware + update (only _release_ building profiles is supported) + When using the building script, it is possible expand the list of acceptable targets; this may be useful for rapid testing of a new mbed-targets. diff --git a/examples/ota-requestor-app/mbed/.gitignore b/examples/ota-requestor-app/mbed/.gitignore new file mode 100644 index 00000000000000..ef564644de9016 --- /dev/null +++ b/examples/ota-requestor-app/mbed/.gitignore @@ -0,0 +1,2 @@ +build-*/ +mcuboot diff --git a/examples/ota-requestor-app/mbed/CMakeLists.txt b/examples/ota-requestor-app/mbed/CMakeLists.txt new file mode 100644 index 00000000000000..5e956b4af0fd01 --- /dev/null +++ b/examples/ota-requestor-app/mbed/CMakeLists.txt @@ -0,0 +1,78 @@ +# Copyright (c) 2021 ARM Limited. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.19.0) + +get_filename_component(CHIP_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../../.. REALPATH) +get_filename_component(MBED_COMMON ${CHIP_ROOT}/examples/platform/mbed REALPATH) +get_filename_component(GEN_DIR ${CHIP_ROOT}/zzz_generated/ REALPATH) +get_filename_component(NLIO_DIR ${CHIP_ROOT}/third_party/nlio/repo/include REALPATH) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/config.in + ${CMAKE_CURRENT_BINARY_DIR}/chip_build/config + @ONLY +) + +set(MBED_PATH ${MBED_OS_PATH} CACHE INTERNAL "") +set(MBED_CONFIG_PATH ${CMAKE_CURRENT_BINARY_DIR} CACHE INTERNAL "") +set(MCUBOOT_PATH ${MBED_MCU_BOOT_PATH} CACHE INTERNAL "") +set(APP_PATH ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "") +set(APP_TYPE ${MBED_APP_TYPE} CACHE INTERNAL "") +set(BOOT_ENABLED FALSE) +set(APP_TARGET chip-mbed-ota-requestor-app-example) + +if(APP_TYPE STREQUAL "boot" OR APP_TYPE STREQUAL "upgrade") + set(BOOT_ENABLED TRUE) +endif() + +include(${MBED_PATH}/tools/cmake/app.cmake) +if(MBED_TARGET STREQUAL "CY8CPROTO_062_4343W" AND BOOT_ENABLED) + list(REMOVE_ITEM MBED_TARGET_LABELS CM0P_SLEEP) + list(REMOVE_ITEM MBED_TARGET_DEFINITIONS COMPONENT_CM0P_SLEEP=1) +endif() +include(${CHIP_ROOT}/src/app/chip_data_model.cmake) + +project(${APP_TARGET}) + +add_subdirectory(${MBED_PATH} ./mbed_build) +add_subdirectory(${MBED_OS_POSIX_SOCKET_PATH} ./mbed_os_posix_socket_build) + +add_executable(${APP_TARGET}) + +add_subdirectory(${CHIP_ROOT}/config/mbed ./chip_build) + +mbed_configure_app_target(${APP_TARGET}) + +target_include_directories(${APP_TARGET} PRIVATE + main/include/ + ${GEN_DIR}/app-common + ${MBED_COMMON}/util/include + ${NLIO_DIR} +) + +target_sources(${APP_TARGET} PRIVATE + main/main.cpp + main/AppTask.cpp + ${MBED_COMMON}/util/LEDWidget.cpp +) + +chip_configure_data_model(${APP_TARGET} + INCLUDE_SERVER + INCLUDE_CLIENT_CALLBACKS + ZAP_FILE ${CHIP_ROOT}/examples/ota-requestor-app/ota-requestor-common/ota-requestor-app.zap + GEN_DIR ${CHIP_ROOT}/zzz_generated/ota-requestor-app/zap-generated +) + +target_link_libraries(${APP_TARGET} mbed-os-posix-socket mbed-os mbed-ble mbed-events mbed-netsocket mbed-storage mbed-storage-kv-global-api mbed-mbedtls mbed-emac chip) + +if(MBED_TARGET STREQUAL "CY8CPROTO_062_4343W") + target_link_libraries(${APP_TARGET} mbed-cy-psoc6-common-network) +endif() + +mbed_set_post_build(${APP_TARGET}) + +option(VERBOSE_BUILD "Have a verbose build process") +if(VERBOSE_BUILD) + set(CMAKE_VERBOSE_MAKEFILE ON) +endif() diff --git a/examples/ota-requestor-app/mbed/README.md b/examples/ota-requestor-app/mbed/README.md new file mode 100644 index 00000000000000..3346c4581fc90b --- /dev/null +++ b/examples/ota-requestor-app/mbed/README.md @@ -0,0 +1,306 @@ +

+ ARM Mbed-OS logo +

+ +

Matter Arm Mbed OS Lock Example Application

+ +The Arm Mbed OS OTA Requestor Example demonstrates how to remotely trigger +update image downloading and apply it if needed. Full functionality of this +examples can be obtained with the addition of a Mbed bootloader that allows +launching the right application image form memory. The example takes advantage +of the IO available on board: + +- A buttons press confirm or reject firmware update steps. +- A LED shows the application state. + +You can use this example as a reference for creating your own application. + +The example is based on +[Matter](https://github.com/project-chip/connectedhomeip) and Arm Mbed OS, and +supports remote access and control of lock over a WiFi network. + +The example behaves as a Matter accessory, in other words a device that can be +paired into an existing Matter network and can be controlled by this network. + +
+ +- [Overview](#overview) + - [Bluetooth Low Energy advertising](#bluetooth-low-energy-advertising) + - [Bluetooth Low Energy rendezvous](#bluetooth-low-energy-rendezvous) + - [WiFi provisioning](#wifi-provisioning) +- [Run application](#run-application) + - [Environment setup](#environment-setup) + - [Building](#building) + - [Flashing](#flashing) + - [Debugging](#debugging) + - [Testing](#testing) + - [Serial port terminal](#serial-port-terminal) + - [CHIP Tools](#chip-tools) + - [Notes](#notes) + - [Supported devices](#supported-devices) + - [Notes](#notes-1) + - [Device UI](#device-ui) + +
+ +# Overview + +The Matter device that runs the lock application is controlled by the Matter +controller device over WiFi. By default, the Matter device is disconnected , and +it should be paired with Matter controller and get configuration from it. +Actions required before establishing full communication are described below. + +## Bluetooth Low Energy advertising + +To commission the device onto a Matter network, the device must be discoverable +over BLE. The BLE advertising starts automatically after device boot-up. + +## Bluetooth Low Energy rendezvous + +In Matter, the commissioning procedure (called rendezvous) is done over BLE +between a Matter device and the Matter controller, where the controller has the +commissioner role. + +To start the rendezvous, the controller must get the commissioning information +from the Matter device. The data payload is encoded within a QR code, printed to +the UART console. + +## WiFi provisioning + +The last part of the rendezvous procedure, provisioning involves sending the +network credentials from the Matter controller to the Matter device. As a +result, device is able to join the network and communicate with other devices in +the network. + +# Run application + +## Environment setup + +Before building the example, check out the Matter repository and sync submodules +using the following command: + + $ git submodule update --init + +Building the example application requires the use of **ARM Mbed-OS** sources and +the **arm-none-gnu-eabi** toolchain. The OpenOCD package is used for flashing +purpose.
Some additional packages may be needed, depending on selected +build target and its requirements. + +> **The VSCode devcontainer has these components pre-installed. Using the VSCode +> devcontainer is the recommended way to interact with Arm Mbed-OS port of the +> Matter Project.** +> +> **Please read this [README.md](../../..//docs/VSCODE_DEVELOPMENT.md) for more +> information about using VSCode in container.** + +To initialize the development environment, download all registered sub-modules +and activate the environment: + +``` +$ source ./scripts/bootstrap.sh +$ source ./scripts/activate.sh +``` + +If packages are already installed then you just need to activate the development +environment: + +``` +$ source ./scripts/activate.sh +``` + +## Building + +The OTA Requestor application can be built in the same way as any other Matter +example ported to the mbed-os platform. + +- **by using generic vscode task**: + +``` +Command Palette (F1) => Run Task... => Run Mbed Application => build => ota-requestor-app => (board name) => (build profile) => (build type) +``` + +- **by calling explicitly building script:** + +``` +${MATTER_ROOT}/scripts/examples/mbed_example.sh -c=build -a=ota-requestor-app -b= -p= -T= +``` + +Both approaches are limited to supported evaluation boards which are listed in +[Supported devices](#supported_devices) paragraph. + +Mbed OS defines three building profiles: _develop, debug_ and _release_. For +more details please visit +[ARM Mbed OS build profiles](https://os.mbed.com/docs/mbed-os/latest/program-setup/build-profiles-and-rules.html). + +There are also three types of built application: _simple, boot_ and _upgrade_: + +- **simple** - standalone application, mainly for developing and testing + purpose (all building profiles are supported) +- **boot** - signed application + bootloader, it supports booting process and + can be use for firmware update (only _release_ building profiles is + supported) +- **update** - signed application, application image can be used for firmware + update (only _release_ building profiles is supported) + +When using the building script, it is possible expand the list of acceptable +targets; this may be useful for rapid testing of a new mbed-targets. + +## Flashing + +The Lock application can be flashed in the same way as any other Matter example +ported to mbed-os platform. + +The [Open On-Chip Debugger](http://openocd.org/) is used to upload a binary +image and reset the device. + +- **by using VSCode task**: + +``` +Command Palette (F1) => Run Task... -> Run Mbed Application => flash => ota-requestor-app => (board name) => (build profile) +``` + +- **by calling explicitly building script:** + +``` +${MATTER_ROOT}/scripts/examples/mbed_example.sh -c=flash -a=ota-requestor-app -b= -p= +``` + +- **by using VSCode launch task**: + +``` +Run and Debug (Ctrl+Shift+D) => Flash Mbed examples => Start Debugging (F5) => (board name) => ota-requestor-app => (build profile) +``` + +The last option uses the Open On-Chip Debugger to open and manage the gdb-server +session. Then gdb-client (arm-none-eabi-gdb) upload binary image and reset +device. + +It is possible to connect to an external gdb-server session by using specific +**'Flash Mbed examples [remote]'** task. + +## Debugging + +Debugging can be performed in the same was as with any other Matter example +ported to mbed-os platform. + +The [Open On-Chip Debugger](http://openocd.org/) is used to to open and manage +the gdb-server session. Then gdb-client (arm-none-eabi-gdb) connect the server +to upload binary image and control debugging. + +``` +Run and Debug (Ctrl+Shift+D) => Debug Mbed examples => Start Debugging (F5) => (board name) => ota-requestor-app => (build profile) +``` + +It is possible to connect to an external gdb-server session by using specific +**'Debug Mbed examples [remote]'** task. + +## Testing + +The provider application is required to transfer image file to OTA requestor. +Mbed example is compatible with Linux version of OTA provider example. Read the +[OTAProvider](../../ota-provider-app/linux/README.md) to see how to build and +run the OTA provider. + +### Serial port terminal + +The application traces are streaming to serial output. To start communication +open a terminal session and connect to the serial port of the device. You can +use **mbed-tools** for this purpose +([mbed-tools](https://github.com/ARMmbed/mbed-tools)): + + mbed-tools sterm -p /dev/ttyACM0 -b 115200 -e off + +After device reset these lines should be visible: + + [INFO][CHIP]: [-]Mbed ota-requestor-app example application start + ... + +[INFO][chip]: [-]Mbed ota-requestor-app example application run + +The ota-requestor-app application launched correctly and you can follow traces +in the terminal. + +### CHIP Tools + +Read the [MbedCommissioning](../../../docs/guides/mbedos_commissioning.md) to +see how to use different CHIP tools to commission and control the application +within a WiFi network. + +After commissioning is successful, announce OTA provider's presence using +`OtaSoftwareUpdateRequestor` cluster with `AnnounceOtaProvider` command. On +receiving this command OTA requestor will query for OTA image: + + chip-device-ctrl > zcl OtaSoftwareUpdateRequestor AnnounceOtaProvider 1234 0 0 providerNodeId=1235 vendorId=9020 announcementReason=0 + +The OTA requestor should communicate with provider, download update image and +apply it. + +#### Notes + +- You have to provision the OTA Provider in the same Matter network. Use the + `connect -ip` command of Python Device Controller: + + chip-device-ctrl > connect -ip 127.0.0.1 20202021 1235 + +- POSIX CLI CHIPTool can be also used for testing this example. Use the + correct `chip-tool` arguments to perform above-mentioned steps. + +## Supported devices + +The example supports building and running on the following mbed-enabled devices: + +| Manufacturer | Hardware platform | Build target | Platform image | Status | Platform components | +| ----------------------------------------------------- | ------------------------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Cypress
Semiconductor](https://www.cypress.com/) | [CY8CPROTO-062-4343W](https://os.mbed.com/platforms/CY8CPROTO-062-4343W/) | `CY8CPROTO_062_4343W` |
CY8CPROTO-062-4343WCY8CPROTO-062-4343W
| :heavy_check_mark: |
LEDs
  • Board has only one usable LED (LED4) which corresponds to USER LED from UI.
  • Lock state LED should be an external component connected to PB9_6 pin (active high).
Buttons
  • SW2 push-button is not used in this example due to its interaction with WIFI module interrupt line.
  • Button 0 corresponds to BTN0 capacitive button.
  • Button 1 corresponds to BTN1 capacitive button.
Slider
  • Unused
| + +#### Notes + +- More details and guidelines about porting new hardware into the Matter + project with Mbed OS can be found in + [MbedNewTarget](../../../docs/guides/mbedos_add_new_target.md) +- Some useful information about HW platform specific settings can be found in + `ota-requestor-app/mbed/mbed_app.json`. + Information about this file syntax and its meaning in mbed-os project can be + found here: + [Mbed-Os configuration system](https://os.mbed.com/docs/mbed-os/latest/program-setup/advanced-configuration.html)) + +## Device UI + +This section lists the User Interface elements that you can use to control and +monitor the state of the device. These correspond to PCB components on the +platform image. + +**USER LED** shows the overall state of the device and its connectivity. The +following states are possible: + +- _Short Flash On (50 ms on/950 ms off)_ — The device is in the + unprovisioned (unpaired) state and is waiting for a commissioning + application to connect. + +- _Rapid Even Flashing (100 ms on/100 ms off)_ — The device is in the + unprovisioned state and a commissioning application is connected through + Bluetooth LE. + +- _Short Flash Off (950ms on/50ms off)_ — The device is fully + provisioned, but does not yet have full network or service connectivity. + +- _Solid On_ — The device is fully provisioned and has full network and + service connectivity. + +**Button 0** can be used for the following purposes: + +- _Pressed for 6 s_ — Initiates the factory reset of the device. + Releasing the button within the 6-second window cancels the factory reset + procedure. **LEDs 1-4** blink in unison when the factory reset procedure is + initiated. + +- _Pressed for less than 3 s_ — Initiates the OTA software update + process. This feature is not currently supported. + +**Button 1** — Pressing the button once delete all fabric IDs and start +BLE advertising. + +Some of the supported boards may not have sufficient number PCB components to +follow above description. In that case please refer to +[Supported devices](#Supported-devices) section and check board's 'Platform +components' column for additional information about the limitation. diff --git a/examples/ota-requestor-app/mbed/config.in b/examples/ota-requestor-app/mbed/config.in new file mode 100644 index 00000000000000..262822b1ca58df --- /dev/null +++ b/examples/ota-requestor-app/mbed/config.in @@ -0,0 +1,6 @@ +CONFIG_CHIP_BUILD_TESTS=n +CONFIG_CHIP_WITH_EXTERNAL_MBEDTLS=y +CONFIG_CHIP_PROJECT_CONFIG=main/include/CHIPProjectConfig.h +CONFIG_CHIP_BYPASS_RENDEZVOUS=n +CONFIG_MBED_BSD_SOCKET_TRACE=n +CONFIG_CHIP_OTA_REQUESTOR=y \ No newline at end of file diff --git a/examples/ota-requestor-app/mbed/main/AppTask.cpp b/examples/ota-requestor-app/mbed/main/AppTask.cpp new file mode 100644 index 00000000000000..21708959a44cb5 --- /dev/null +++ b/examples/ota-requestor-app/mbed/main/AppTask.cpp @@ -0,0 +1,416 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AppTask.h" +#include + +#include +#include +#include +#include +#include +#include +#include + +// mbed-os headers +#include "drivers/Timeout.h" +#include "events/EventQueue.h" + +#ifdef CAPSENSE_ENABLED +#include "capsense.h" +#endif + +#ifdef CHIP_OTA_REQUESTOR +#include "GenericOTARequestorDriver.h" +#include +#include +#include +#endif // CHIP_OTA_REQUESTOR + +#ifdef BOOT_ENABLED +#include "blockdevice/SlicingBlockDevice.h" +#include +#endif + +static bool sIsWiFiStationProvisioned = false; +static bool sIsWiFiStationEnabled = false; +static bool sIsWiFiStationConnected = false; +static bool sIsPairedToAccount = false; +static bool sHaveBLEConnections = false; + +static events::EventQueue sAppEventQueue; + +using namespace ::chip; +using namespace ::chip::Credentials; +using namespace ::chip::DeviceLayer; + +static LEDWidget sStatusLED(MBED_CONF_APP_SYSTEM_STATE_LED); + +#define FACTORY_RESET_TRIGGER_TIMEOUT (MBED_CONF_APP_FACTORY_RESET_TRIGGER_TIMEOUT) +#define FACTORY_RESET_CANCEL_WINDOW_TIMEOUT (MBED_CONF_APP_FACTORY_RESET_CANCEL_WINDOW_TIMEOUT) + +#define FUNCTION_BUTTON (MBED_CONF_APP_FUNCTION_BUTTON) +#define BLE_BUTTON (MBED_CONF_APP_BLE_BUTTON) +#define BUTTON_PUSH_EVENT 1 +#define BUTTON_RELEASE_EVENT 0 + +#ifdef CAPSENSE_ENABLED +static mbed::CapsenseButton CapFunctionButton(Capsense::getInstance(), 0); +static mbed::CapsenseButton CapBleButton(Capsense::getInstance(), 1); +#else +static mbed::InterruptIn sFunctionButton(FUNCTION_BUTTON); +static mbed::InterruptIn sBleButton(BLE_BUTTON); +#endif + +static mbed::Timeout sFunctionTimer; + +AppTask AppTask::sAppTask; + +int AppTask::Init() +{ + CHIP_ERROR error; + // Register the callback to init the MDNS server when connectivity is available + PlatformMgr().AddEventHandler( + [](const ChipDeviceEvent * event, intptr_t arg) { + // Restart the server whenever an ip address is renewed + if (event->Type == DeviceEventType::kInternetConnectivityChange) + { + if (event->InternetConnectivityChange.IPv4 == kConnectivity_Established || + event->InternetConnectivityChange.IPv6 == kConnectivity_Established) + { + chip::app::DnssdServer::Instance().StartServer(); + } + } + }, + 0); + + // Initialize buttons +#ifdef CAPSENSE_ENABLED + CapFunctionButton.fall(mbed::callback(this, &AppTask::FunctionButtonPressEventHandler)); + CapFunctionButton.rise(mbed::callback(this, &AppTask::FunctionButtonReleaseEventHandler)); + CapBleButton.fall(mbed::callback(this, &AppTask::BleButtonPressEventHandler)); +#else + sFunctionButton.fall(mbed::callback(this, &AppTask::FunctionButtonPressEventHandler)); + sFunctionButton.rise(mbed::callback(this, &AppTask::FunctionButtonReleaseEventHandler)); + sBleButton.fall(mbed::callback(this, &AppTask::BleButtonPressEventHandler)); +#endif + + ConnectivityMgrImpl().StartWiFiManagement(); + + // Init ZCL Data Model and start server + error = Server::GetInstance().Init(); + if (error != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Server initialization failed: %s", error.AsString()); + return EXIT_FAILURE; + } + + // Initialize device attestation config + SetDeviceAttestationCredentialsProvider(Examples::GetExampleDACProvider()); + ConfigurationMgr().LogDeviceConfig(); + // QR code will be used with CHIP Tool + PrintOnboardingCodes(chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE)); + +#ifdef CHIP_OTA_REQUESTOR + // Initialize the instance of the main Requestor Class + OTARequestor * requestor = new OTARequestor(); + if (requestor == nullptr) + { + ChipLogError(NotSpecified, "Create OTA Requestor core failed"); + return EXIT_FAILURE; + } + SetRequestorInstance(requestor); + + // Initialize an instance of the Requestor Driver + GenericOTARequestorDriver * requestorDriver = new GenericOTARequestorDriver; + if (requestorDriver == nullptr) + { + ChipLogError(NotSpecified, "Create OTA Requestor driver failed"); + return EXIT_FAILURE; + } + + // Initialize the Downloader object + BDXDownloader * downloader = new BDXDownloader(); + if (downloader == nullptr) + { + ChipLogError(NotSpecified, "Create OTA Downloader failed"); + return EXIT_FAILURE; + } + + // Initialize the Image Processor object + OTAImageProcessorImpl * imageProcessor = new OTAImageProcessorImpl; + if (imageProcessor == nullptr) + { + ChipLogError(NotSpecified, "Create OTA Image Processor failed"); + return EXIT_FAILURE; + } + + requestor->Init(&(chip::Server::GetInstance()), requestorDriver, downloader); + imageProcessor->SetOTADownloader(downloader); + downloader->SetImageProcessorDelegate(imageProcessor); + requestorDriver->Init(requestor, imageProcessor); +#endif // CHIP_OTA_REQUESTOR + + return 0; +} + +int AppTask::StartApp() +{ + int ret = Init(); + if (ret) + { + ChipLogError(NotSpecified, "AppTask.Init() failed"); + return ret; + } + + ChipLogProgress(NotSpecified, "Mbed ota-requestor-app example application run"); + + while (true) + { + sAppEventQueue.dispatch(100); + + // Collect connectivity and configuration state from the CHIP stack. Because the + // CHIP event loop is being run in a separate task, the stack must be locked + // while these values are queried. However we use a non-blocking lock request + // (TryLockChipStack()) to avoid blocking other UI activities when the CHIP + // task is busy (e.g. with a long crypto operation). + + if (PlatformMgr().TryLockChipStack()) + { + sIsWiFiStationProvisioned = ConnectivityMgr().IsWiFiStationProvisioned(); + sIsWiFiStationEnabled = ConnectivityMgr().IsWiFiStationEnabled(); + sIsWiFiStationConnected = ConnectivityMgr().IsWiFiStationConnected(); + sHaveBLEConnections = (ConnectivityMgr().NumBLEConnections() != 0); + PlatformMgr().UnlockChipStack(); + } + + // If system is connected to Wi-Fi station, keep the LED On constantly. + // + // If Wi-Fi is provisioned, but not connected to Wi-Fi station yet + // THEN blink the LED Off for a short period of time. + // + // If the system has ble connection(s) uptill the stage above, THEN blink the LEDs at an even + // rate of 100ms. + // + // Otherwise, blink the LED ON for a very short time. + if (sIsWiFiStationConnected) + { + sStatusLED.Set(true); + } + else if (sIsWiFiStationProvisioned && sIsWiFiStationEnabled && sIsPairedToAccount && !sIsWiFiStationConnected) + { + sStatusLED.Blink(950, 50); + } + else if (sHaveBLEConnections) + { + sStatusLED.Blink(100, 100); + } + else + { + sStatusLED.Blink(50, 950); + } + + sStatusLED.Animate(); + } +} + +void AppTask::PostEvent(AppEvent * aEvent) +{ + auto handle = sAppEventQueue.call([event = *aEvent, this] { DispatchEvent(&event); }); + if (!handle) + { + ChipLogError(NotSpecified, "Failed to post event to app task event queue: Not enough memory"); + } +} + +void AppTask::DispatchEvent(const AppEvent * aEvent) +{ + if (aEvent->Handler) + { + aEvent->Handler(const_cast(aEvent)); + } + else + { + ChipLogError(NotSpecified, "Event received with no handler. Dropping event."); + } +} + +void AppTask::BleButtonPressEventHandler() +{ + AppEvent button_event; + button_event.Type = AppEvent::kEventType_Button; + button_event.ButtonEvent.Pin = BLE_BUTTON; + button_event.ButtonEvent.Action = BUTTON_PUSH_EVENT; + button_event.Handler = BleHandler; + sAppTask.PostEvent(&button_event); +} + +void AppTask::FunctionButtonPressEventHandler() +{ + AppEvent button_event; + button_event.Type = AppEvent::kEventType_Button; + button_event.ButtonEvent.Pin = FUNCTION_BUTTON; + button_event.ButtonEvent.Action = BUTTON_PUSH_EVENT; + button_event.Handler = FunctionHandler; + sAppTask.PostEvent(&button_event); +} + +void AppTask::FunctionButtonReleaseEventHandler() +{ + AppEvent button_event; + button_event.Type = AppEvent::kEventType_Button; + button_event.ButtonEvent.Pin = FUNCTION_BUTTON; + button_event.ButtonEvent.Action = BUTTON_RELEASE_EVENT; + button_event.Handler = FunctionHandler; + sAppTask.PostEvent(&button_event); +} + +void AppTask::ButtonEventHandler(uint32_t id, bool pushed) +{ + if (id > 1) + { + ChipLogError(NotSpecified, "Wrong button ID"); + return; + } + + AppEvent button_event; + button_event.Type = AppEvent::kEventType_Button; + button_event.ButtonEvent.Pin = id == 0 ? BLE_BUTTON : FUNCTION_BUTTON; + button_event.ButtonEvent.Action = pushed ? BUTTON_PUSH_EVENT : BUTTON_RELEASE_EVENT; + + if (id == 0) + { + button_event.Handler = BleHandler; + } + else + { + button_event.Handler = FunctionHandler; + } + + sAppTask.PostEvent(&button_event); +} + +void AppTask::StartTimer(uint32_t aTimeoutInMs) +{ + auto chronoTimeoutMs = std::chrono::duration(aTimeoutInMs); + sFunctionTimer.attach(mbed::callback(this, &AppTask::TimerEventHandler), chronoTimeoutMs); + mFunctionTimerActive = true; +} + +void AppTask::CancelTimer() +{ + sFunctionTimer.detach(); + mFunctionTimerActive = false; +} + +void AppTask::TimerEventHandler() +{ + AppEvent event; + event.Type = AppEvent::kEventType_Timer; + event.Handler = FunctionTimerEventHandler; + sAppTask.PostEvent(&event); +} + +void AppTask::FunctionTimerEventHandler(AppEvent * aEvent) +{ + if (aEvent->Type != AppEvent::kEventType_Timer) + return; + + // If we reached here, the button was held past FACTORY_RESET_TRIGGER_TIMEOUT, initiate factory reset + if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_SoftwareUpdate) + { + ChipLogProgress(NotSpecified, "Factory Reset Triggered. Release button within %ums to cancel.", + FACTORY_RESET_CANCEL_WINDOW_TIMEOUT); + + // Start timer for FACTORY_RESET_CANCEL_WINDOW_TIMEOUT to allow user to + // cancel, if required. + sAppTask.StartTimer(FACTORY_RESET_CANCEL_WINDOW_TIMEOUT); + sAppTask.mFunction = kFunction_FactoryReset; + + // Turn off all LEDs before starting blink to make sure blink is co-ordinated. + sStatusLED.Set(false); + + sStatusLED.Blink(500); + } + else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset) + { + // Actually trigger Factory Reset + ChipLogProgress(NotSpecified, "Factory Reset initiated"); + sAppTask.mFunction = kFunction_NoneSelected; + ConfigurationMgr().InitiateFactoryReset(); + } +} + +void AppTask::FunctionHandler(AppEvent * aEvent) +{ + if (aEvent->ButtonEvent.Pin != FUNCTION_BUTTON) + return; + + // To trigger software update: press the FUNCTION_BUTTON button briefly (< FACTORY_RESET_TRIGGER_TIMEOUT) + // To initiate factory reset: press the FUNCTION_BUTTON for FACTORY_RESET_TRIGGER_TIMEOUT + FACTORY_RESET_CANCEL_WINDOW_TIMEOUT + // All LEDs start blinking after FACTORY_RESET_TRIGGER_TIMEOUT to signal factory reset has been initiated. + // To cancel factory reset: release the FUNCTION_BUTTON once all LEDs start blinking within the + // FACTORY_RESET_CANCEL_WINDOW_TIMEOUT + if (aEvent->ButtonEvent.Action == BUTTON_PUSH_EVENT) + { + if (!sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_NoneSelected) + { + sAppTask.StartTimer(FACTORY_RESET_TRIGGER_TIMEOUT); + + sAppTask.mFunction = kFunction_SoftwareUpdate; + } + } + else + { + // If the button was released before factory reset got initiated, trigger a software update. + if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_SoftwareUpdate) + { + sAppTask.CancelTimer(); + sAppTask.mFunction = kFunction_NoneSelected; + ChipLogError(NotSpecified, "Software Update not supported."); + } + else if (sAppTask.mFunctionTimerActive && sAppTask.mFunction == kFunction_FactoryReset) + { + sAppTask.CancelTimer(); + + // Change the function to none selected since factory reset has been canceled. + sAppTask.mFunction = kFunction_NoneSelected; + + ChipLogProgress(NotSpecified, "Factory Reset has been Canceled"); + } + } +} + +void AppTask::BleHandler(AppEvent * aEvent) +{ + if (aEvent->Type == AppEvent::kEventType_Button) + { + chip::Server::GetInstance().GetFabricTable().DeleteAllFabrics(); + + if (ConnectivityMgr().IsBLEAdvertisingEnabled()) + { + ChipLogProgress(NotSpecified, "BLE advertising is already enabled"); + return; + } + + if (chip::Server::GetInstance().GetCommissioningWindowManager().OpenBasicCommissioningWindow() != CHIP_NO_ERROR) + { + ChipLogProgress(NotSpecified, "OpenBasicCommissioningWindow() failed"); + } + } +} diff --git a/examples/ota-requestor-app/mbed/main/include/AppEvent.h b/examples/ota-requestor-app/mbed/main/include/AppEvent.h new file mode 100644 index 00000000000000..48e2344c71eb32 --- /dev/null +++ b/examples/ota-requestor-app/mbed/main/include/AppEvent.h @@ -0,0 +1,47 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2018 Nest Labs, Inc. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +struct AppEvent; +typedef void (*EventHandler)(AppEvent *); + +struct AppEvent +{ + enum AppEventTypes + { + kEventType_Button = 0, + kEventType_Timer + }; + + uint16_t Type; + + union + { + struct + { + int Pin; + uint8_t Action; + } ButtonEvent; + }; + + EventHandler Handler; +}; diff --git a/examples/ota-requestor-app/mbed/main/include/AppTask.h b/examples/ota-requestor-app/mbed/main/include/AppTask.h new file mode 100644 index 00000000000000..4aa3dbde0ca943 --- /dev/null +++ b/examples/ota-requestor-app/mbed/main/include/AppTask.h @@ -0,0 +1,70 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2019 Google LLC. + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "AppEvent.h" +#include + +class AppTask +{ +public: + int StartApp(); + + void PostEvent(AppEvent * aEvent); + + void ButtonEventHandler(uint32_t id, bool pushed); + +private: + friend AppTask & GetAppTask(void); + + int Init(); + + void DispatchEvent(const AppEvent * event); + + static void FunctionTimerEventHandler(AppEvent * aEvent); + static void FunctionHandler(AppEvent * aEvent); + static void BleHandler(AppEvent * aEvent); + + void BleButtonPressEventHandler(void); + void FunctionButtonPressEventHandler(void); + void FunctionButtonReleaseEventHandler(void); + + void StartTimer(uint32_t aTimeoutInMs); + void CancelTimer(void); + void TimerEventHandler(void); + + enum Function_t + { + kFunction_NoneSelected = 0, + kFunction_SoftwareUpdate = 0, + kFunction_FactoryReset, + + kFunction_Invalid + }; + + Function_t mFunction; + bool mFunctionTimerActive; + static AppTask sAppTask; +}; + +inline AppTask & GetAppTask(void) +{ + return AppTask::sAppTask; +} diff --git a/examples/ota-requestor-app/mbed/main/include/CHIPProjectConfig.h b/examples/ota-requestor-app/mbed/main/include/CHIPProjectConfig.h new file mode 100644 index 00000000000000..9282dfbceace05 --- /dev/null +++ b/examples/ota-requestor-app/mbed/main/include/CHIPProjectConfig.h @@ -0,0 +1,41 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file + * This is a place to put application or project-specific overrides + * to the default configuration values for general CHIP features. + * + */ + +#pragma once + +#define CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION 1 +#define CHIP_DEVICE_CONFIG_ENABLE_WIFI_AP 0 + +// Use a default pairing code if one hasn't been provisioned in flash. +#define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE 20202021 +#define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR 0xF00 + +#ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING MBED_CONF_APP_VERSION_NUMBER_STR +#endif + +#ifndef CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION +#define CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION MBED_CONF_APP_VERSION_NUMBER +#endif diff --git a/examples/ota-requestor-app/mbed/main/main.cpp b/examples/ota-requestor-app/mbed/main/main.cpp new file mode 100644 index 00000000000000..a91d0946ca06a1 --- /dev/null +++ b/examples/ota-requestor-app/mbed/main/main.cpp @@ -0,0 +1,112 @@ +/* + * + * Copyright (c) 2020 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AppTask.h" + +#include +#include +#include +#include +#include + +#ifdef CAPSENSE_ENABLED +#include "capsense.h" +#endif + +#ifdef BOOT_ENABLED +#include +#endif + +using namespace ::chip; +using namespace ::chip::DeviceLayer; +using namespace ::chip::Platform; +using namespace ::chip::Logging::Platform; + +int main() +{ + int ret = 0; + CHIP_ERROR err = CHIP_NO_ERROR; + + mbed_logging_init(); + +#ifdef CAPSENSE_ENABLED + Capsense::getInstance().init(); +#endif + + ChipLogProgress(SoftwareUpdate, "Mbed OTA Requestor example application start"); + +#ifdef BOOT_ENABLED + ret = boot_set_confirmed(); + if (ret == 0) + { + ChipLogProgress(NotSpecified, "Boot confirmed"); + } + else + { + ChipLogError(NotSpecified, "Failed to confirm boot: %d", ret); + } + ChipLogProgress(NotSpecified, "Current software version: [%ld] %s", uint32_t(CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION), + CHIP_DEVICE_CONFIG_DEVICE_SOFTWARE_VERSION_STRING); +#endif + + ret = mbedtls_platform_setup(NULL); + if (ret) + { + ChipLogError(SoftwareUpdate, "Mbed TLS platform initialization failed [%d]", ret); + goto exit; + } + + err = MemoryInit(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(SoftwareUpdate, "Memory initialization failed: %s", err.AsString()); + ret = EXIT_FAILURE; + goto exit; + } + + err = PlatformMgr().InitChipStack(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(SoftwareUpdate, "Chip stack initialization failed: %s", err.AsString()); + ret = EXIT_FAILURE; + goto exit; + } + +#ifdef MBED_CONF_APP_BLE_DEVICE_NAME + err = ConnectivityMgr().SetBLEDeviceName(MBED_CONF_APP_BLE_DEVICE_NAME); + if (err != CHIP_NO_ERROR) + { + ChipLogError(SoftwareUpdate, "Set BLE device name failed: %s", err.AsString()); + ret = EXIT_FAILURE; + goto exit; + } +#endif + + err = PlatformMgr().StartEventLoopTask(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Chip stack start failed: %s", err.AsString()); + ret = EXIT_FAILURE; + goto exit; + } + + ret = GetAppTask().StartApp(); + +exit: + ChipLogProgress(SoftwareUpdate, "Exited with code %d", ret); + return ret; +} diff --git a/examples/ota-requestor-app/mbed/mbed_app.json b/examples/ota-requestor-app/mbed/mbed_app.json new file mode 100644 index 00000000000000..8f0644e1d0a3e2 --- /dev/null +++ b/examples/ota-requestor-app/mbed/mbed_app.json @@ -0,0 +1,77 @@ +{ + "macros": ["MBEDTLS_USER_CONFIG_FILE=\"chip_mbedtls_config.h\""], + "target_overrides": { + "*": { + "platform.stdio-baud-rate": 115200, + "lwip.ipv6-enabled": true, + "lwip.raw-socket-enabled": true, + "lwip.netbuf-recvinfo-enabled": true, + "nsapi.default-wifi-security": "WPA_WPA2", + "nsapi.default-wifi-ssid": "\"YOUR_SSID\"", + "nsapi.default-wifi-password": "\"YOUR_PASSWORD\"", + "mbed-trace.max-level": "TRACE_LEVEL_DEBUG", + "mbed-trace.enable": true, + "target.printf_lib": "std", + "mcuboot.bootloader-build": false, + "mcuboot.log-enable": true, + "mcuboot.log-level": "MCUBOOT_LOG_LEVEL_ERROR" + }, + "CY8CPROTO_062_4343W": { + "target.network-default-interface-type": "WIFI", + "target.macros_add": [ + "MXCRYPTO_DISABLED", + "NL_ASSERT_LOG=NL_ASSERT_LOG_DEFAULT", + "NL_ASSERT_EXPECT_FLAGS=NL_ASSERT_FLAG_LOG", + "WHD_PRINT_DISABLE" + ], + "target.components_add": ["capsense"], + "ble-button": "USER_BUTTON", + "mcuboot.primary-slot-address": "0x10022000", + "mcuboot.slot-size": "0x140000", + "mcuboot.scratch-address": "0x10162000", + "mcuboot.scratch-size": "0x40000", + "mcuboot.max-img-sectors": "0xA00", + "mcuboot.header-size": "0x400" + } + }, + "config": { + "led-active-state": { + "help": "GPIO output to turn the LED on.", + "value": 0 + }, + "system-state-led": { + "help": "System status LED.", + "value": "LED1" + }, + "function-button": { + "help": "Function button pin.", + "value": "BUTTON1" + }, + "ble-button": { + "help": "BLE button pin.", + "value": "BUTTON2" + }, + "factory-reset-trigger-timeout": { + "help": "'function-button' press timeout to trigger a factory reset (in milliseconds).", + "value": 3000 + }, + "factory-reset-cancel-window-timeout": { + "help": "'function-button' press timeout to cancel a factory reset (in milliseconds). The total 'function-button' press time to have the factory reset actually initiated is equal to 'factory-reset-trigger-timeout' + 'factory-reset-cancel-window-timeout'.", + "value": 3000 + }, + "ble-device-name": { + "help": "Name used for BLE advertising.", + "value": "\"MBED-ota-req\"" + }, + "use-gatt-indication-ack-hack": { + "help": "Fake a TX transfer confirmation. Send a 'kCHIPoBLEIndicateConfirm' event as soon as data is sent, without waiting for the actual ACK from the GATT client. This hack has to stay until we provide a fix in the Mbed OS repo.", + "value": 1 + }, + "version-number": { + "value": "0" + }, + "version-number-str": { + "value": "\"0.1.0\"" + } + } +} diff --git a/examples/pigweed-app/mbed/README.md b/examples/pigweed-app/mbed/README.md index bb72edd0258ac0..544c10f19876e4 100644 --- a/examples/pigweed-app/mbed/README.md +++ b/examples/pigweed-app/mbed/README.md @@ -89,13 +89,13 @@ ported to the mbed-os platform. - **by using generic vscode task**: ``` -Command Palette (F1) => Run Task... => Run Mbed Application => build => pigweed-app => (board name) => (build profile) +Command Palette (F1) => Run Task... => Run Mbed Application => build => pigweed-app => (board name) => (build profile) => (build type) ``` - **by calling explicitly building script:** ``` -${MATTER_ROOT}/scripts/examples/mbed_example.sh -c=build -a=pigweed-app -b= -p= +${MATTER_ROOT}/scripts/examples/mbed_example.sh -c=build -a=pigweed-app -b= -p= -T= ``` Both approaches are limited to supported evaluation boards which are listed in @@ -105,6 +105,16 @@ Mbed OS defines three building profiles: _develop, debug_ and _release_. For more details please visit [ARM Mbed OS build profiles](https://os.mbed.com/docs/mbed-os/latest/program-setup/build-profiles-and-rules.html). +There are also three types of built application: _simple, boot_ and _upgrade_: + +- **simple** - standalone application, mainly for developing and testing + purpose (all building profiles are supported) +- **boot** - signed application + bootloader, it supports booting process and + can be use for firmware update (only _release_ building profiles is + supported) +- **update** - signed application, application image can be used for firmware + update (only _release_ building profiles is supported) + When using the building script, it is possible expand the list of acceptable targets; this may be useful for rapid testing of a new mbed-targets. diff --git a/examples/platform/mbed/bootloader/.gitignore b/examples/platform/mbed/bootloader/.gitignore new file mode 100644 index 00000000000000..37e820ec299c49 --- /dev/null +++ b/examples/platform/mbed/bootloader/.gitignore @@ -0,0 +1,6 @@ +build-*/ +dist/ +imgtool* +mcuboot +signing* +enc-key/ diff --git a/examples/platform/mbed/bootloader/CMakeLists.txt b/examples/platform/mbed/bootloader/CMakeLists.txt new file mode 100644 index 00000000000000..eecdaf64fa2e33 --- /dev/null +++ b/examples/platform/mbed/bootloader/CMakeLists.txt @@ -0,0 +1,41 @@ +# Copyright (c) 2021 ARM Limited. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.19.0 FATAL_ERROR) + +set(MBED_PATH ${MBED_OS_PATH} CACHE INTERNAL "") +set(MCUBOOT_PATH ${MBED_MCU_BOOT_PATH} CACHE INTERNAL "") +set(MBED_CONFIG_PATH ${CMAKE_CURRENT_BINARY_DIR} CACHE INTERNAL "") +set(APP_TARGET chip-mbed-bootloader) + +include(${MBED_PATH}/tools/cmake/app.cmake) + +project(${APP_TARGET}) + +add_subdirectory(${MBED_PATH} ./mbed_build) +add_subdirectory(${MCUBOOT_PATH}/boot/bootutil/ ./mbed_mcu_boot_util) +add_subdirectory(${MCUBOOT_PATH}/boot/mbed/ ./mbed_mcu_boot) # Mbed-MCUboot Port + +add_executable(${APP_TARGET}) + +target_sources(${APP_TARGET} + PUBLIC + default_bd.cpp + signing_keys.c +) + +target_link_libraries(${APP_TARGET} + PUBLIC + bootutil + mbed-mcuboot + mbed-storage-spif + mbed-storage-qspif + mbed-baremetal +) + +mbed_set_post_build(${APP_TARGET}) + +option(VERBOSE_BUILD "Have a verbose build process") +if(VERBOSE_BUILD) + set(CMAKE_VERBOSE_MAKEFILE ON) +endif() diff --git a/examples/platform/mbed/bootloader/default_bd.cpp b/examples/platform/mbed/bootloader/default_bd.cpp new file mode 100644 index 00000000000000..dc6dbbd4b1ba8c --- /dev/null +++ b/examples/platform/mbed/bootloader/default_bd.cpp @@ -0,0 +1,78 @@ +/* + * default_bd.cpp + * + * Created on: Jul 30, 2020 + * Author: gdbeckstein + */ + +#include "BlockDevice.h" + +#include "SlicingBlockDevice.h" + +#if COMPONENT_SPIF +#include "SPIFBlockDevice.h" +#endif + +#if COMPONENT_QSPIF +#include "QSPIFBlockDevice.h" +#endif + +#if COMPONENT_DATAFLASH +#include "DataFlashBlockDevice.h" +#endif + +#if COMPONENT_SD +#include "SDBlockDevice.h" + +#if (STATIC_PINMAP_READY) +const spi_pinmap_t static_spi_pinmap = get_spi_pinmap(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, NC); +#endif +#endif + +BlockDevice * BlockDevice::get_default_instance() +{ +#if COMPONENT_SPIF + + static SPIFBlockDevice default_bd; + + return &default_bd; + +#elif COMPONENT_QSPIF + + static QSPIFBlockDevice default_bd; + + return &default_bd; + +#elif COMPONENT_DATAFLASH + + static DataFlashBlockDevice default_bd; + + return &default_bd; + +#elif COMPONENT_SD + +#if (STATIC_PINMAP_READY) + static SDBlockDevice default_bd(static_spi_pinmap, MBED_CONF_SD_SPI_CS); +#else + static SDBlockDevice default_bd; +#endif + + return &default_bd; + +#else + + return NULL; + +#endif +} + +/** + * You can override this function to suit your hardware/memory configuration + * By default it simply returns what is returned by BlockDevice::get_default_instance(); + */ +mbed::BlockDevice * get_secondary_bd(void) +{ + mbed::BlockDevice * default_bd = mbed::BlockDevice::get_default_instance(); + static mbed::SlicingBlockDevice sliced_bd(default_bd, 0x0, MCUBOOT_SLOT_SIZE); + return &sliced_bd; +} diff --git a/examples/platform/mbed/bootloader/mbed_app.json b/examples/platform/mbed/bootloader/mbed_app.json new file mode 100644 index 00000000000000..a3a147fbc6334d --- /dev/null +++ b/examples/platform/mbed/bootloader/mbed_app.json @@ -0,0 +1,38 @@ +{ + "requires": [ + "bare-metal", + "mbedtls", + "mcuboot", + "flashiap-block-device", + "spif-driver", + "qspif", + "mbed-trace" + ], + "config": { + "serial-bootloader-enable": { + "help": "Build bootloader with serial update support", + "value": 0 + } + }, + "target_overrides": { + "*": { + "platform.stdio-baud-rate": 115200, + "target.restrict_size": "0x20000", + "target.c_lib": "small", + "mcuboot.log-enable": true, + "mcuboot.log-level": "MCUBOOT_LOG_LEVEL_INFO", + "mbed-trace.enable": true, + "mbed-trace.max-level": "TRACE_LEVEL_DEBUG", + "mbed-trace.fea-ipv6": false + }, + "CY8CPROTO_062_4343W": { + "target.components_remove": ["WHD"], + "mcuboot.primary-slot-address": "0x10022000", + "mcuboot.slot-size": "0x140000", + "mcuboot.scratch-address": "0x10162000", + "mcuboot.scratch-size": "0x40000", + "mcuboot.max-img-sectors": "0xA00", + "mcuboot.overwrite-only": true + } + } +} diff --git a/examples/shell/mbed/README.md b/examples/shell/mbed/README.md index 2bec23d6abb52f..cefc576929a4f4 100644 --- a/examples/shell/mbed/README.md +++ b/examples/shell/mbed/README.md @@ -82,13 +82,13 @@ ported to the mbed-os platform. - **by using generic vscode task**: ``` -Command Palette (F1) => Run Task... => Run Mbed Application => build => shell => (board name) => (build profile)` +Command Palette (F1) => Run Task... => Run Mbed Application => build => shell => (board name) => (build profile) => (build type) ``` - **by calling explicitly building script:** ``` -${MATTER_ROOT}/scripts/examples/mbed_example.sh -c=build -a=shell -b= -p= +${MATTER_ROOT}/scripts/examples/mbed_example.sh -c=build -a=shell -b= -p= -T= ``` Both approaches are limited to supported evaluation boards which are listed in @@ -98,6 +98,16 @@ Mbed OS defines three building profiles: _develop, debug_ and _release_. For more details please visit [ARM Mbed OS build profiles](https://os.mbed.com/docs/mbed-os/latest/program-setup/build-profiles-and-rules.html). +There are also three types of built application: _simple, boot_ and _upgrade_: + +- **simple** - standalone application, mainly for developing and testing + purpose (all building profiles are supported) +- **boot** - signed application + bootloader, it supports booting process and + can be use for firmware update (only _release_ building profiles is + supported) +- **update** - signed application, application image can be used for firmware + update (only _release_ building profiles is supported) + When using the building script, it is possible expand the list of acceptable targets; this may be useful for rapid testing of a new mbed-targets. diff --git a/scripts/examples/mbed_example.sh b/scripts/examples/mbed_example.sh index 9c26b5b3804e1b..cef30fde0164ea 100755 --- a/scripts/examples/mbed_example.sh +++ b/scripts/examples/mbed_example.sh @@ -22,15 +22,21 @@ cd "$CHIP_ROOT"/examples SUPPORTED_TOOLCHAIN=(GCC_ARM ARM) SUPPORTED_TARGET_BOARD=(CY8CPROTO_062_4343W) -SUPPORTED_APP=(lock-app lighting-app pigweed-app all-clusters-app shell) +SUPPORTED_APP=(lock-app lighting-app pigweed-app all-clusters-app shell ota-requestor-app) SUPPORTED_PROFILES=(release develop debug) SUPPORTED_COMMAND=(build flash build-flash) +SUPPORTED_TYPE=(simple boot upgrade) COMMAND=build APP=lock-app TARGET_BOARD=CY8CPROTO_062_4343W TOOLCHAIN=GCC_ARM PROFILE=release +TYPE=simple + +TARGET_MEMORY_ALIGN[CY8CPROTO_062_4343W]=8 +TARGET_BOOT_IMAGE_ERASE_VALUE[CY8CPROTO_062_4343W]=0 +TARGET_UPGRADE_IMAGE_ERASE_VALUE[CY8CPROTO_062_4343W]=0xff for i in "$@"; do case $i in @@ -54,6 +60,10 @@ for i in "$@"; do COMMAND="${i#*=}" shift ;; + -T=* | --type=*) + TYPE="${i#*=}" + shift + ;; *) # unknown option ;; @@ -85,6 +95,16 @@ if [[ ! " ${SUPPORTED_PROFILES[@]} " =~ " ${PROFILE} " ]]; then exit 1 fi +if [[ ! " ${SUPPORTED_TYPE[@]} " =~ " ${TYPE} " ]]; then + echo "ERROR: Type $TYPE not supported" + exit 1 +fi + +if [[ "$TYPE" == "boot" ]] && [[ "$PROFILE" == "debug" ]]; then + echo "ERROR: The $TYPE application type does not supprort ""$PROFILE profile" + exit 1 +fi + set -e # Exit immediately if a command exits with a non-zero status. # Activate Matter environment @@ -93,8 +113,17 @@ source "$CHIP_ROOT"/scripts/activate.sh # Build directory setup BUILD_DIRECTORY="$APP"/mbed/build-"$TARGET_BOARD"/"$PROFILE"/ +# Set bootloader root directory +BOOTLOADER_ROOT_DIRECTORY="$CHIP_ROOT"/examples/platform/mbed/bootloader + +# Set bootloader build directory +BOOTLOADER_BUILD_DIRECTORY="$BOOTLOADER_ROOT_DIRECTORY"/build-"$TARGET_BOARD"/"$PROFILE"/ + +# Set encryption key directory +ENC_KEY_DIRECTORY="$BOOTLOADER_ROOT_DIRECTORY"/enc-key + if [[ "$COMMAND" == *"build"* ]]; then - echo "Build $APP app for $TARGET_BOARD target with $TOOLCHAIN toolchain and $PROFILE profile" + echo "Build $TYPE $APP app for $TARGET_BOARD target with $TOOLCHAIN toolchain and $PROFILE profile" # Set Mbed OS path MBED_OS_PATH="$CHIP_ROOT"/third_party/mbed-os/repo @@ -102,6 +131,57 @@ if [[ "$COMMAND" == *"build"* ]]; then # Set Mbed OS posix socket submodule path MBED_OS_POSIX_SOCKET_PATH="$CHIP_ROOT"/third_party/mbed-os-posix-socket/repo + # Set Mbed MCU boot path + MBED_MCU_BOOT_PATH="$CHIP_ROOT"/third_party/mbed-mcu-boot/repo + + if [[ "$TYPE" == "boot" ]]; then + cd "$BOOTLOADER_ROOT_DIRECTORY" + + # Install mcuboot requirements (silently) + pip install -q -r "$MBED_MCU_BOOT_PATH"/scripts/requirements.txt + + # Run mcuboot setup script + python "$MBED_MCU_BOOT_PATH"/scripts/setup.py install + + # Check if encryption key exists, if not generate it + if [[ ! -f "$ENC_KEY_DIRECTORY"/enc-key.pem ]]; then + mkdir -p "$ENC_KEY_DIRECTORY" + "$MBED_MCU_BOOT_PATH"/scripts/imgtool.py keygen -k "$ENC_KEY_DIRECTORY"/enc-key.pem -t rsa-2048 + fi + + # Create the signing keys source fille + "$MBED_MCU_BOOT_PATH"/scripts/imgtool.py getpub -k "$ENC_KEY_DIRECTORY"/enc-key.pem >signing_keys.c + + ln -sfTr "$MBED_MCU_BOOT_PATH"/boot/mbed mcuboot + + # Generate config file for selected target, toolchain and hardware + mbed-tools configure -t "$TOOLCHAIN" -m "$TARGET_BOARD" -o "$BOOTLOADER_BUILD_DIRECTORY" --mbed-os-path "$MBED_OS_PATH" + + # Remove old artifacts to force linking + rm -rf "$BOOTLOADER_BUILD_DIRECTORY/chip-"* + + # Build application + cmake -S . -B "$BOOTLOADER_BUILD_DIRECTORY" -GNinja -DCMAKE_BUILD_TYPE="$PROFILE" -DMBED_OS_PATH="$MBED_OS_PATH" -DMBED_MCU_BOOT_PATH="$MBED_MCU_BOOT_PATH" + cmake --build "$BOOTLOADER_BUILD_DIRECTORY" + + cd "$CHIP_ROOT"/examples + fi + + if [[ "$TYPE" == "upgrade" ]]; then + # Check if encryption key exists + if [[ ! -f "$ENC_KEY_DIRECTORY"/enc-key.pem ]]; then + echo "ERROR: encryption key for upgrade image not exist" + exit 1 + fi + fi + + # Set Mbed OS posix socket submodule path + MBED_OS_POSIX_SOCKET_PATH="$CHIP_ROOT"/third_party/mbed-os-posix-socket/repo + + if [[ "$TYPE" == "boot" || "$TYPE" == "upgrade" ]]; then + ln -sfTr "$MBED_MCU_BOOT_PATH"/boot/mbed "$APP"/mbed/mcuboot + fi + # Generate config file for selected target, toolchain and hardware mbed-tools configure -t "$TOOLCHAIN" -m "$TARGET_BOARD" -p "$APP"/mbed/ -o "$BUILD_DIRECTORY" --mbed-os-path "$MBED_OS_PATH" @@ -109,17 +189,41 @@ if [[ "$COMMAND" == *"build"* ]]; then rm -rf "$BUILD_DIRECTORY/chip-"* # Build application - cmake -S "$APP/mbed" -B "$BUILD_DIRECTORY" -GNinja -DCMAKE_BUILD_TYPE="$PROFILE" -DMBED_OS_PATH="$MBED_OS_PATH" -DMBED_OS_POSIX_SOCKET_PATH="$MBED_OS_POSIX_SOCKET_PATH" + cmake -S "$APP/mbed" -B "$BUILD_DIRECTORY" -GNinja -DCMAKE_BUILD_TYPE="$PROFILE" -DMBED_OS_PATH="$MBED_OS_PATH" -DMBED_OS_POSIX_SOCKET_PATH="$MBED_OS_POSIX_SOCKET_PATH" -DMBED_MCU_BOOT_PATH="$MBED_MCU_BOOT_PATH" -DMBED_APP_TYPE="$TYPE" cmake --build "$BUILD_DIRECTORY" + + if [[ "$TYPE" == "boot" || "$TYPE" == "upgrade" ]]; then + APP_VERSION=$(jq '.config."version-number-str".value' "$APP"/mbed/mbed_app.json | tr -d '\\"') + HEADER_SIZE=$(jq '.target_overrides.'\""$TARGET_BOARD"\"'."mcuboot.header-size"' "$APP"/mbed/mbed_app.json | tr -d \") + SLOT_SIZE=$(jq '.target_overrides.'\""$TARGET_BOARD"\"'."mcuboot.slot-size"' "$APP"/mbed/mbed_app.json | tr -d \") + + if [[ "$TYPE" == "boot" ]]; then + # Signed the primary application + "$MBED_MCU_BOOT_PATH"/scripts/imgtool.py sign -k "$ENC_KEY_DIRECTORY"/enc-key.pem \ + --align "${TARGET_MEMORY_ALIGN[$TARGET_BOARD]}" -v "$APP_VERSION" --header-size $(($HEADER_SIZE)) --pad-header -S "$SLOT_SIZE" -R "${TARGET_BOOT_IMAGE_ERASE_VALUE[$TARGET_BOARD]}" \ + "$BUILD_DIRECTORY"/chip-mbed-"$APP"-example.hex "$BUILD_DIRECTORY"/chip-mbed-"$APP"-example-signed.hex + # Create the factory firmware (bootloader + signed primary application) + hexmerge.py -o "$BUILD_DIRECTORY"/chip-mbed-"$APP"-example.hex --no-start-addr "$BOOTLOADER_BUILD_DIRECTORY"/chip-mbed-bootloader.hex "$BUILD_DIRECTORY"/chip-mbed-"$APP"-example-signed.hex + elif [[ "$TYPE" == "upgrade" ]]; then + # Signed the secondary application + "$MBED_MCU_BOOT_PATH"/scripts/imgtool.py sign -k "$ENC_KEY_DIRECTORY"/enc-key.pem \ + --align "${TARGET_MEMORY_ALIGN[$TARGET_BOARD]}" -v "$APP_VERSION" --header-size $(($HEADER_SIZE)) --pad-header -S "$SLOT_SIZE" -R "${TARGET_UPGRADE_IMAGE_ERASE_VALUE[$TARGET_BOARD]}" \ + "$BUILD_DIRECTORY"/chip-mbed-"$APP"-example.hex "$BUILD_DIRECTORY"/chip-mbed-"$APP"-example-signed.hex + # Convert hex image to raw binary file + arm-none-eabi-objcopy -I ihex -O binary "$BUILD_DIRECTORY"/chip-mbed-"$APP"-example-signed.hex "$BUILD_DIRECTORY"/chip-mbed-"$APP"-example.bin + fi + fi fi if [[ "$COMMAND" == *"flash"* ]]; then - echo "Flash $APP app to $TARGET_BOARD target [$TOOLCHAIN toolchain, $PROFILE profile]" + echo "Flash $TYPE $APP app to $TARGET_BOARD target [$TOOLCHAIN toolchain, $PROFILE profile]" # Flash scripts path setup MBED_FLASH_SCRIPTS_PATH=$CHIP_ROOT/config/mbed/scripts + APP_PATH="$BUILD_DIRECTORY"/chip-mbed-"$APP"-example.elf + # Flash application - "$OPENOCD_PATH"/bin/openocd -f "$MBED_FLASH_SCRIPTS_PATH/$TARGET_BOARD".tcl -c "program $BUILD_DIRECTORY/chip-mbed-$APP-example.elf verify reset exit" + "$OPENOCD_PATH"/bin/openocd -f "$MBED_FLASH_SCRIPTS_PATH/$TARGET_BOARD".tcl -c "program $APP_PATH verify reset exit" fi diff --git a/src/platform/mbed/BUILD.gn b/src/platform/mbed/BUILD.gn index d590d96410b5a1..7484cff08ca0bf 100644 --- a/src/platform/mbed/BUILD.gn +++ b/src/platform/mbed/BUILD.gn @@ -44,6 +44,6 @@ static_library("mbed") { if (chip_enable_openthread && chip_mdns == "platform") { sources += [ "../OpenThread/DnssdImpl.cpp" ] - deps += [ "${chip_root}/src/lib/dnssd:platform_header" ] + public_deps += [ "${chip_root}/src/lib/dnssd:platform_header" ] } } diff --git a/src/platform/mbed/CHIPDevicePlatformConfig.h b/src/platform/mbed/CHIPDevicePlatformConfig.h index ce972c8e504944..7edd1c91a6755b 100644 --- a/src/platform/mbed/CHIPDevicePlatformConfig.h +++ b/src/platform/mbed/CHIPDevicePlatformConfig.h @@ -27,7 +27,9 @@ #define CHIP_DEVICE_CONFIG_ENABLE_THREAD 0 +#ifndef CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE #define CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE 1 +#endif #define CHIP_DEVICE_CONFIG_ENABLE_CHIP_TIME_SERVICE_TIME_SYNC 0 diff --git a/src/platform/mbed/OTAImageProcessorImpl.cpp b/src/platform/mbed/OTAImageProcessorImpl.cpp new file mode 100644 index 00000000000000..6e122e118508a7 --- /dev/null +++ b/src/platform/mbed/OTAImageProcessorImpl.cpp @@ -0,0 +1,545 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "OTAImageProcessorImpl.h" +#include +#include + +#ifdef BOOT_ENABLED +#include "bootutil/bootutil.h" +#include "flash_map_backend/secondary_bd.h" +#endif + +using namespace ::chip::DeviceLayer::Internal; + +namespace chip { + +OTAImageProcessorImpl::OTAImageProcessorImpl() +{ +#ifdef BOOT_ENABLED + // Set block device - memory for image update + mBlockDevice = get_secondary_bd(); +#endif +} + +int OTAImageProcessorImpl::MemoryTest() +{ + int ret = 0; + bd_size_t read_size; + bd_size_t program_size; + bd_size_t erase_size; + bd_size_t full_size; + size_t buffer_size; + char * buffer = nullptr; + + if (!mBlockDevice) + { + ChipLogError(NotSpecified, "Block device not set"); + return 1; + } + + // Initialize the block device + ret = mBlockDevice->init(); + if (ret) + { + ChipLogError(NotSpecified, "Block device initialization failed [%d]", ret); + goto exit; + } + + // Get the block device type + ChipLogProgress(NotSpecified, "Block device type: %s", mBlockDevice->get_type()); + + // Get device geometry + read_size = mBlockDevice->get_read_size(); + program_size = mBlockDevice->get_program_size(); + erase_size = mBlockDevice->get_erase_size(); + full_size = mBlockDevice->size(); + + ChipLogProgress(NotSpecified, "--- Block device geometry ---"); + ChipLogProgress(NotSpecified, "read_size: %lld B", read_size); + ChipLogProgress(NotSpecified, "program_size: %lld B", program_size); + ChipLogProgress(NotSpecified, "erase_size: %lld B", erase_size); + ChipLogProgress(NotSpecified, "size: %lld B", full_size); + ChipLogProgress(NotSpecified, "---\n"); + + // Allocate a block with enough space for our data, aligned to the + // nearest program_size. This is the minimum size necessary to write + // data to a block. + buffer_size = sizeof("Hello Storage!") + program_size - 1; + buffer_size = buffer_size - (buffer_size % program_size); + buffer = new char[buffer_size]; + + // Read what is currently stored on the block device. We haven't written + // yet so this may be garbage + ret = mBlockDevice->read(buffer, 0, buffer_size); + if (ret) + { + ChipLogError(NotSpecified, "Block device read failed [%d]", ret); + goto exit; + } + + ChipLogProgress(NotSpecified, "--- Currently stored data ---"); + for (size_t i = 0; i < buffer_size; i += 16) + { + for (size_t j = 0; j < 16; j++) + { + if (i + j < buffer_size) + { + ChipLogProgress(NotSpecified, "%02x ", buffer[i + j]); + } + else + { + ChipLogProgress(NotSpecified, " "); + } + } + ChipLogProgress(NotSpecified, " "); + } + ChipLogProgress(NotSpecified, "---\n"); + + // Write data to first block, write occurs in two parts, + // an erase followed by a program + ret = mBlockDevice->erase(0, erase_size); + if (ret) + { + ChipLogError(NotSpecified, "Block device erase failed [%d]", ret); + goto exit; + } + + // Clear the buffer so we don't get old data + memset(buffer, 0x0, buffer_size); + + // Read the data from the first block + ret = mBlockDevice->read(buffer, 0, buffer_size); + if (ret) + { + ChipLogError(NotSpecified, "Block device read failed [%d]", ret); + goto exit; + } + + ChipLogProgress(NotSpecified, "--- Stored data after erase ---"); + for (size_t i = 0; i < buffer_size; i += 16) + { + for (size_t j = 0; j < 16; j++) + { + if (i + j < buffer_size) + { + ChipLogProgress(NotSpecified, "%02x ", buffer[i + j]); + } + else + { + ChipLogProgress(NotSpecified, " "); + } + ChipLogProgress(NotSpecified, " "); + } + } + ChipLogProgress(NotSpecified, "---\n"); + + // Clear the buffer so we don't get old data + memset(buffer, 0x0, buffer_size); + // Update buffer with our string we want to store + strncpy(buffer, "Hello Storage!", buffer_size); + + ret = mBlockDevice->program(buffer, 0, buffer_size); + if (ret) + { + ChipLogError(NotSpecified, "Block device program failed [%d]", ret); + goto exit; + } + + // Clear the buffer so we don't get old data + memset(buffer, 0x0, buffer_size); + + // Read the data from the first block, note that the program_size must be + // a multiple of the read_size, so we don't have to check for alignment + ret = mBlockDevice->read(buffer, 0, buffer_size); + if (ret) + { + ChipLogError(NotSpecified, "Block device read failed [%d]", ret); + goto exit; + } + + ChipLogProgress(NotSpecified, "--- Stored data after write ---"); + for (size_t i = 0; i < buffer_size; i += 16) + { + for (size_t j = 0; j < 16; j++) + { + if (i + j < buffer_size) + { + ChipLogProgress(NotSpecified, "%02x ", buffer[i + j]); + } + else + { + ChipLogProgress(NotSpecified, " "); + } + } + + ChipLogProgress(NotSpecified, " %.*s", buffer_size - i, &buffer[i]); + } + ChipLogProgress(NotSpecified, "---\n"); + + ret = strcmp(buffer, "Hello Storage!"); + if (ret) + { + ChipLogError(NotSpecified, "Data compare failed"); + } + else + { + ChipLogProgress(NotSpecified, "--- MEMORY TEST PASS ---"); + } + +exit: + if (ret) + { + ChipLogError(NotSpecified, "--- MEMORY TEST FAILED ---"); + } + + if (buffer) + { + delete buffer; + } + // Deinitialize the block device + ret = mBlockDevice->deinit(); + if (ret) + { + ChipLogError(NotSpecified, "Block deinitialization read failed [%d]", ret); + goto exit; + } + + return ret; +} + +CHIP_ERROR OTAImageProcessorImpl::PrepareDownload() +{ + ChipLogProgress(SoftwareUpdate, "Prepare download"); + ClearDownloadParams(); + DeviceLayer::PlatformMgr().ScheduleWork(HandlePrepareDownload, reinterpret_cast(this)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::Finalize() +{ + ChipLogProgress(SoftwareUpdate, "Finalize"); + DeviceLayer::PlatformMgr().ScheduleWork(HandleFinalize, reinterpret_cast(this)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::Apply() +{ + ChipLogProgress(SoftwareUpdate, "Apply"); + DeviceLayer::PlatformMgr().ScheduleWork(HandleApply, reinterpret_cast(this)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::Abort() +{ + ChipLogProgress(SoftwareUpdate, "Abort"); + ClearDownloadParams(); + DeviceLayer::PlatformMgr().ScheduleWork(HandleAbort, reinterpret_cast(this)); + return CHIP_NO_ERROR; +}; + +CHIP_ERROR OTAImageProcessorImpl::ProcessBlock(ByteSpan & block) +{ + ChipLogProgress(SoftwareUpdate, "Process block"); + CHIP_ERROR err = SetBlock(block); + if (err != CHIP_NO_ERROR) + { + ChipLogError(SoftwareUpdate, "Cannot set block data: %" CHIP_ERROR_FORMAT, err.Format()); + return err; + } + DeviceLayer::PlatformMgr().ScheduleWork(HandleProcessBlock, reinterpret_cast(this)); + return CHIP_NO_ERROR; +} + +void OTAImageProcessorImpl::HandlePrepareDownload(intptr_t context) +{ + auto * imageProcessor = reinterpret_cast(context); + if (imageProcessor == nullptr) + { + ChipLogError(SoftwareUpdate, "ImageProcessor context is null"); + return; + } + else if (imageProcessor->mDownloader == nullptr) + { + ChipLogError(SoftwareUpdate, "mDownloader is null"); + return; + } + + auto ret = imageProcessor->PrepareMemory(); + if (ret) + { + ChipLogError(SoftwareUpdate, "Prepare download memory failed"); + imageProcessor->mDownloader->OnPreparedForDownload(CHIP_ERROR_INTERNAL); + return; + } + + ret = imageProcessor->ClearMemory(); + if (ret) + { + ChipLogError(SoftwareUpdate, "Clear download memory failed"); + imageProcessor->mDownloader->OnPreparedForDownload(CHIP_ERROR_INTERNAL); + return; + } + imageProcessor->mDownloader->OnPreparedForDownload(CHIP_NO_ERROR); +} + +void OTAImageProcessorImpl::HandleFinalize(intptr_t context) +{ + auto * imageProcessor = reinterpret_cast(context); + if (imageProcessor == nullptr) + { + ChipLogError(SoftwareUpdate, "ImageProcessor context is null"); + return; + } + + imageProcessor->mParams.totalFileBytes = imageProcessor->mParams.downloadedBytes; + imageProcessor->ReleaseBlock(); + auto ret = imageProcessor->CloseMemory(); + if (ret) + { + ChipLogError(SoftwareUpdate, "Close download memory failed"); + } + ChipLogProgress(SoftwareUpdate, "OTA image downloaded size %lldB", imageProcessor->mParams.totalFileBytes); +} + +void OTAImageProcessorImpl::HandleAbort(intptr_t context) +{ + auto * imageProcessor = reinterpret_cast(context); + if (imageProcessor == nullptr) + { + ChipLogError(SoftwareUpdate, "ImageProcessor context is null"); + return; + } + + auto ret = imageProcessor->ClearMemory(); + if (ret) + { + ChipLogError(SoftwareUpdate, "Clear download memory failed"); + } + + ret = imageProcessor->CloseMemory(); + if (ret) + { + ChipLogError(SoftwareUpdate, "Close download memory failed"); + } + + imageProcessor->ReleaseBlock(); +} + +void OTAImageProcessorImpl::HandleProcessBlock(intptr_t context) +{ + auto * imageProcessor = reinterpret_cast(context); + if (imageProcessor == nullptr) + { + ChipLogError(SoftwareUpdate, "ImageProcessor context is null"); + return; + } + else if (imageProcessor->mDownloader == nullptr) + { + ChipLogError(SoftwareUpdate, "mDownloader is null"); + return; + } + + auto ret = imageProcessor->ProgramMemory(); + if (ret) + { + ChipLogError(SoftwareUpdate, "Program download memory failed"); + } + imageProcessor->mDownloader->FetchNextData(); +} + +void OTAImageProcessorImpl::HandleApply(intptr_t context) +{ + auto * imageProcessor = reinterpret_cast(context); + if (imageProcessor == nullptr) + { + ChipLogError(SoftwareUpdate, "ImageProcessor context is null"); + return; + } + +#ifdef BOOT_ENABLED + ChipLogProgress(SoftwareUpdate, "Set secondary image pending"); + auto ret = boot_set_pending_multi(/*image index*/ 0, /*permanent*/ 0); + if (ret) + { + ChipLogError(SoftwareUpdate, "Setting the update candidate as pending failed: %d", ret); + } +#endif +} + +CHIP_ERROR OTAImageProcessorImpl::SetBlock(ByteSpan & block) +{ + if (!IsSpanUsable(block)) + { + ReleaseBlock(); + return CHIP_NO_ERROR; + } + if (mBlock.size() < block.size()) + { + if (!mBlock.empty()) + { + ReleaseBlock(); + } + + size_t buffer_size = 0; + if (mBlockDevice) + { + // Aligned to the nearest program_size. This is the minimum size necessary to write data to a block. + bd_size_t program_size = mBlockDevice->get_program_size(); + buffer_size = block.size() + program_size - 1; + buffer_size = buffer_size - (buffer_size % program_size); + } + else + { + buffer_size = block.size(); + } + + uint8_t * mBlock_ptr = static_cast(chip::Platform::MemoryAlloc(buffer_size)); + if (mBlock_ptr == nullptr) + { + return CHIP_ERROR_NO_MEMORY; + } + mBlock = MutableByteSpan(mBlock_ptr, buffer_size); + } + CHIP_ERROR err = CopySpanToMutableSpan(block, mBlock); + if (err != CHIP_NO_ERROR) + { + ChipLogError(SoftwareUpdate, "Cannot copy block data: %" CHIP_ERROR_FORMAT, err.Format()); + return err; + } + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::ReleaseBlock() +{ + if (mBlock.data() != nullptr) + { + chip::Platform::MemoryFree(mBlock.data()); + } + mBlock = MutableByteSpan(); + return CHIP_NO_ERROR; +} + +void OTAImageProcessorImpl::ClearDownloadParams() +{ + mParams.downloadedBytes = 0; + mParams.totalFileBytes = 0; +} + +int OTAImageProcessorImpl::PrepareMemory() +{ + int ret = 0; + + if (!mBlockDevice) + { + ChipLogError(NotSpecified, "Block device not set"); + return 1; + } + + // Initialize the block device + ret = mBlockDevice->init(); + if (ret) + { + ChipLogError(SoftwareUpdate, "Block device initialization failed [%d]", ret); + return ret; + } + ChipLogProgress(SoftwareUpdate, "Block device initialize"); + + // Initialization read from the block device + bd_size_t read_size = mBlockDevice->get_read_size(); + char buff[read_size]; + ret = mBlockDevice->read(&buff, 0, read_size); + if (ret) + { + ChipLogError(NotSpecified, "Block device read failed [%d]", ret); + return ret; + } + + return ret; +} + +int OTAImageProcessorImpl::CloseMemory() +{ + int ret = 0; + + if (!mBlockDevice) + { + ChipLogError(NotSpecified, "Block device not set"); + return 1; + } + + // Deinitialize the block device + ret = mBlockDevice->deinit(); + if (ret) + { + ChipLogError(SoftwareUpdate, "Block device deinitialization failed [%d]", ret); + return ret; + } + ChipLogProgress(SoftwareUpdate, "Block device deinitialization"); + + return ret; +} + +int OTAImageProcessorImpl::ClearMemory() +{ + int ret = 0; + + if (!mBlockDevice) + { + ChipLogError(NotSpecified, "Block device not set"); + return 1; + } + + // Erase memory of block device + ret = mBlockDevice->erase(0, mBlockDevice->size()); + if (ret) + { + ChipLogError(SoftwareUpdate, "Erase block device failed [%d]", ret); + return ret; + } + + return ret; +} + +int OTAImageProcessorImpl::ProgramMemory() +{ + int ret = 0; + + if (!mBlockDevice) + { + ChipLogError(NotSpecified, "Block device not set"); + return 1; + } + + // Write data to memory + ret = mBlockDevice->program(mBlock.data(), mParams.downloadedBytes, mBlock.size()); + if (ret) + { + ChipLogError(SoftwareUpdate, "Block device program failed [%d]", ret); + return ret; + } + ChipLogProgress(SoftwareUpdate, + "Secondary slot program with offset: " + "0x%" PRIx64, + mParams.downloadedBytes); + mParams.downloadedBytes += mBlock.size(); + + return ret; +} + +} // namespace chip diff --git a/src/platform/mbed/OTAImageProcessorImpl.h b/src/platform/mbed/OTAImageProcessorImpl.h new file mode 100644 index 00000000000000..3a9a82a06e13e1 --- /dev/null +++ b/src/platform/mbed/OTAImageProcessorImpl.h @@ -0,0 +1,97 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This file contains the declarations for OTAImageProcessor, a platform-agnostic + * interface for processing downloaded chunks of OTA image data. + * Each platform should provide an implementation of this interface. + */ + +#pragma once + +#include +#include +#include +#include + +#include "blockdevice/BlockDevice.h" + +namespace chip { + +class OTAImageProcessorImpl : public OTAImageProcessorInterface +{ +public: + OTAImageProcessorImpl(); + /** + * Called to prepare for an OTA image download. This may include but not limited to opening the file, finding a block of space + * in persistent memory, and allocating a buffer. This must not be a blocking call. + */ + CHIP_ERROR PrepareDownload(); + + /** + * Called when the OTA image download process has completed. This may include but not limited to closing the file and persistent + * storage. This must not be a blocking call. + */ + CHIP_ERROR Finalize(); + + /** + * Called when the OTA image should be applied. + */ + CHIP_ERROR Apply(); + + /** + * Called when the OTA image download process is incomplete or cannot continue. This may include but not limited to erasing + * everything that has been written and releasing buffers. This must not be a blocking call. + */ + CHIP_ERROR Abort(); + + /** + * Called to process a downloaded block of data. This must not be a blocking call to support cases that require IO to elements + * such as external peripherals/radios. This must not be a blocking call. + */ + CHIP_ERROR ProcessBlock(ByteSpan & block); + + /** + * Check if memory for update image is works correctly. + */ + int MemoryTest(); + + void SetOTADownloader(OTADownloader * downloader) { mDownloader = downloader; }; + + mbed::BlockDevice * mBlockDevice; + +private: + static void HandlePrepareDownload(intptr_t context); + static void HandleFinalize(intptr_t context); + static void HandleAbort(intptr_t context); + static void HandleProcessBlock(intptr_t context); + static void HandleApply(intptr_t context); + + CHIP_ERROR SetBlock(ByteSpan & block); + CHIP_ERROR ReleaseBlock(); + + int PrepareMemory(); + int ClearMemory(); + int ProgramMemory(); + int CloseMemory(); + void ClearDownloadParams(); + + OTADownloader * mDownloader = nullptr; + MutableByteSpan mBlock; +}; + +} // namespace chip diff --git a/third_party/mbed-mcu-boot/repo b/third_party/mbed-mcu-boot/repo new file mode 160000 index 00000000000000..46070d75e06058 --- /dev/null +++ b/third_party/mbed-mcu-boot/repo @@ -0,0 +1 @@ +Subproject commit 46070d75e06058a2e6a525d8be71b9b872178514 diff --git a/third_party/mbed-os-posix-socket/repo b/third_party/mbed-os-posix-socket/repo index 8797ebb01d5b3f..366a8994839c25 160000 --- a/third_party/mbed-os-posix-socket/repo +++ b/third_party/mbed-os-posix-socket/repo @@ -1 +1 @@ -Subproject commit 8797ebb01d5b3f96b59616eba65ee9b0d67e17c8 +Subproject commit 366a8994839c257f62b727f1700a02314957f2e2