From 9e75b8f70228662a74a0a36850ac11159c7035c2 Mon Sep 17 00:00:00 2001 From: Chuyue Luo Date: Tue, 15 Oct 2024 13:28:19 +0000 Subject: [PATCH 01/12] components: Remove ota_for_aws_iot_embedded_sdk Remove the ota_for_aws_iot_embedded_sdk component, as we will be replacing it with the new modular OTA. Signed-off-by: Chuyue Luo --- .gitmodules | 3 - .../CMakeLists.txt | 1 - .../configs/aws_configs/CMakeLists.txt | 5 - applications/keyword_detection/CMakeLists.txt | 1 - .../configs/aws_configs/CMakeLists.txt | 7 +- applications/object_detection/CMakeLists.txt | 1 - .../configs/aws_configs/CMakeLists.txt | 5 - .../speech_recognition/CMakeLists.txt | 1 - .../configs/aws_configs/CMakeLists.txt | 7 +- components/aws_iot/CMakeLists.txt | 1 - .../CMakeLists.txt | 21 - .../integration/CMakeLists.txt | 38 - ...n-constant-appFirmwareVersion-extern.patch | 33 - .../integration/src/ota_agent_task.c | 1290 ----------------- .../integration/tests/CMakeLists.txt | 35 - .../tests/config_mocks/CMakeLists.txt | 17 - .../tests/config_mocks/inc/app_config.h | 14 - .../tests/config_mocks/inc/demo_config.h | 13 - .../inc/iot_default_root_certificates.h | 12 - .../tests/config_mocks/inc/ota_config.h | 14 - .../integration/tests/test_ota_agent_task.cpp | 1247 ---------------- .../ota_for_aws_iot_embedded_sdk/library | 1 - .../library_mocks/CMakeLists.txt | 16 - .../library_mocks/inc/ota.h | 130 -- .../library_mocks/inc/ota_appversion32.h | 30 - .../library_mocks/inc/ota_mqtt_interface.h | 57 - .../library_mocks/inc/ota_os_interface.h | 112 -- .../inc/ota_platform_interface.h | 75 - .../library_mocks/inc/ota_private.h | 73 - .../portable/os/ota_os_freertos.h | 30 - .../library_mocks/src/ota.c | 55 - .../integration/CMakeLists.txt | 1 - manifest.yml | 9 - 33 files changed, 2 insertions(+), 3353 deletions(-) delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/CMakeLists.txt delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/CMakeLists.txt delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/patches/0001-Add-non-constant-appFirmwareVersion-extern.patch delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/src/ota_agent_task.c delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/CMakeLists.txt delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/CMakeLists.txt delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/inc/app_config.h delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/inc/demo_config.h delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/inc/iot_default_root_certificates.h delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/inc/ota_config.h delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/test_ota_agent_task.cpp delete mode 160000 components/aws_iot/ota_for_aws_iot_embedded_sdk/library delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/CMakeLists.txt delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota.h delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_appversion32.h delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_mqtt_interface.h delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_os_interface.h delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_platform_interface.h delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_private.h delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/portable/os/ota_os_freertos.h delete mode 100644 components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/src/ota.c diff --git a/.gitmodules b/.gitmodules index e85c2fcc..9ced2395 100644 --- a/.gitmodules +++ b/.gitmodules @@ -43,9 +43,6 @@ [submodule "open_iot_sdk_toolchain"] path = components/tools/open_iot_sdk_toolchain/library url = https://git.gitlab.arm.com/iot/open-iot-sdk/toolchain.git -[submodule "ota_for_aws_iot_embedded_sdk"] - path = components/aws_iot/ota_for_aws_iot_embedded_sdk/library - url = https://github.com/aws/ota-for-aws-iot-embedded-sdk.git [submodule "tinycbor"] path = components/aws_iot/tinycbor/library url = https://github.com/intel/tinycbor.git diff --git a/applications/freertos_iot_libraries_tests/CMakeLists.txt b/applications/freertos_iot_libraries_tests/CMakeLists.txt index 0c9142ab..16dc2b07 100644 --- a/applications/freertos_iot_libraries_tests/CMakeLists.txt +++ b/applications/freertos_iot_libraries_tests/CMakeLists.txt @@ -108,7 +108,6 @@ target_link_libraries(${CMAKE_PROJECT_NAME} freertos-ota-pal-psa fri-bsp helpers-events - ota-for-aws-iot-embedded-sdk provisioning-lib mbedtls tfm-ns-interface diff --git a/applications/freertos_iot_libraries_tests/configs/aws_configs/CMakeLists.txt b/applications/freertos_iot_libraries_tests/configs/aws_configs/CMakeLists.txt index 86b10bb3..24bbfa08 100644 --- a/applications/freertos_iot_libraries_tests/configs/aws_configs/CMakeLists.txt +++ b/applications/freertos_iot_libraries_tests/configs/aws_configs/CMakeLists.txt @@ -31,8 +31,3 @@ target_include_directories(freertos-pkcs11-psa-config INTERFACE . ) - -target_include_directories(ota-for-aws-iot-embedded-sdk-config - INTERFACE - . -) diff --git a/applications/keyword_detection/CMakeLists.txt b/applications/keyword_detection/CMakeLists.txt index 59c84704..8fb1090b 100644 --- a/applications/keyword_detection/CMakeLists.txt +++ b/applications/keyword_detection/CMakeLists.txt @@ -142,7 +142,6 @@ target_link_libraries(keyword-detection helpers-device-advisor helpers-events mbedtls - ota-for-aws-iot-embedded-sdk provisioning-lib tfm-ns-interface toolchain-override diff --git a/applications/keyword_detection/configs/aws_configs/CMakeLists.txt b/applications/keyword_detection/configs/aws_configs/CMakeLists.txt index c89696ee..24bbfa08 100644 --- a/applications/keyword_detection/configs/aws_configs/CMakeLists.txt +++ b/applications/keyword_detection/configs/aws_configs/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2023 Arm Limited and/or its affiliates +# Copyright 2023-2024 Arm Limited and/or its affiliates # # SPDX-License-Identifier: MIT @@ -31,8 +31,3 @@ target_include_directories(freertos-pkcs11-psa-config INTERFACE . ) - -target_include_directories(ota-for-aws-iot-embedded-sdk-config - INTERFACE - . -) diff --git a/applications/object_detection/CMakeLists.txt b/applications/object_detection/CMakeLists.txt index 57493f09..f6dc578e 100644 --- a/applications/object_detection/CMakeLists.txt +++ b/applications/object_detection/CMakeLists.txt @@ -128,7 +128,6 @@ target_link_libraries(object-detection isp-config isp_platform_driver mbedtls - ota-for-aws-iot-embedded-sdk provisioning-lib tfm-ns-interface toolchain-override diff --git a/applications/object_detection/configs/aws_configs/CMakeLists.txt b/applications/object_detection/configs/aws_configs/CMakeLists.txt index d8c60fcf..f9c38824 100644 --- a/applications/object_detection/configs/aws_configs/CMakeLists.txt +++ b/applications/object_detection/configs/aws_configs/CMakeLists.txt @@ -31,8 +31,3 @@ target_include_directories(freertos-pkcs11-psa-config INTERFACE . ) - -target_include_directories(ota-for-aws-iot-embedded-sdk-config - INTERFACE - . -) diff --git a/applications/speech_recognition/CMakeLists.txt b/applications/speech_recognition/CMakeLists.txt index 99abe185..f26bc932 100644 --- a/applications/speech_recognition/CMakeLists.txt +++ b/applications/speech_recognition/CMakeLists.txt @@ -154,7 +154,6 @@ target_link_libraries(speech-recognition fri-bsp helpers-events mbedtls - ota-for-aws-iot-embedded-sdk provisioning-lib speexdsp tfm-ns-interface diff --git a/applications/speech_recognition/configs/aws_configs/CMakeLists.txt b/applications/speech_recognition/configs/aws_configs/CMakeLists.txt index c89696ee..24bbfa08 100644 --- a/applications/speech_recognition/configs/aws_configs/CMakeLists.txt +++ b/applications/speech_recognition/configs/aws_configs/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2023 Arm Limited and/or its affiliates +# Copyright 2023-2024 Arm Limited and/or its affiliates # # SPDX-License-Identifier: MIT @@ -31,8 +31,3 @@ target_include_directories(freertos-pkcs11-psa-config INTERFACE . ) - -target_include_directories(ota-for-aws-iot-embedded-sdk-config - INTERFACE - . -) diff --git a/components/aws_iot/CMakeLists.txt b/components/aws_iot/CMakeLists.txt index 9b4577f7..ad90bd61 100644 --- a/components/aws_iot/CMakeLists.txt +++ b/components/aws_iot/CMakeLists.txt @@ -12,5 +12,4 @@ add_subdirectory(corepkcs11) if(CONNECTIVITY_STACK STREQUAL "FREERTOS_PLUS_TCP") add_subdirectory(coresntp) endif() -add_subdirectory(ota_for_aws_iot_embedded_sdk) add_subdirectory(tinycbor) diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/CMakeLists.txt b/components/aws_iot/ota_for_aws_iot_embedded_sdk/CMakeLists.txt deleted file mode 100644 index ee65579d..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2023-2024 Arm Limited and/or its affiliates -# -# SPDX-License-Identifier: MIT - -if(BUILD_TESTING AND NOT CMAKE_CROSSCOMPILING) - add_subdirectory(library_mocks) -else() - set(ota_for_aws_iot_embedded_sdk_SOURCE_DIR - ${CMAKE_CURRENT_LIST_DIR}/library - CACHE INTERNAL - "Path to AWS IoT Over-the-air Update source code" - ) - - include(ApplyPatches) - - set(PATCH_FILES_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/integration/patches") - set(PATCH_FILES "${PATCH_FILES_DIRECTORY}/0001-Add-non-constant-appFirmwareVersion-extern.patch") - iot_reference_arm_corstone3xx_apply_patches("${ota_for_aws_iot_embedded_sdk_SOURCE_DIR}" "${PATCH_FILES}") -endif() - -add_subdirectory(integration) diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/CMakeLists.txt b/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/CMakeLists.txt deleted file mode 100644 index db3cb788..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 2023-2024 Arm Limited and/or its affiliates -# -# SPDX-License-Identifier: MIT - - -if(BUILD_TESTING AND NOT CMAKE_CROSSCOMPILING) - add_subdirectory(tests) -else() - include(${ota_for_aws_iot_embedded_sdk_SOURCE_DIR}/otaFilePaths.cmake) - - add_library(ota-for-aws-iot-embedded-sdk - ${OTA_SOURCES} - ${OTA_MQTT_SOURCES} - ${OTA_OS_FREERTOS_SOURCES} - src/ota_agent_task.c - ) - - target_include_directories(ota-for-aws-iot-embedded-sdk - PUBLIC - ${OTA_INCLUDE_PUBLIC_DIRS} - ${OTA_INCLUDE_OS_FREERTOS_DIRS} - ) - - add_library(ota-for-aws-iot-embedded-sdk-config INTERFACE) - - target_link_libraries(ota-for-aws-iot-embedded-sdk - PUBLIC - ota-for-aws-iot-embedded-sdk-config - PRIVATE - corejson - coremqtt - coremqtt-agent - freertos-ota-pal-psa - helpers-events - helpers-logging - tinycbor - ) -endif() diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/patches/0001-Add-non-constant-appFirmwareVersion-extern.patch b/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/patches/0001-Add-non-constant-appFirmwareVersion-extern.patch deleted file mode 100644 index 360a1b42..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/patches/0001-Add-non-constant-appFirmwareVersion-extern.patch +++ /dev/null @@ -1,33 +0,0 @@ -From aef862b0b2076720b6c651997516a0698d066e67 Mon Sep 17 00:00:00 2001 -From: Hugues Kamba-Mpiana -Date: Wed, 3 Jan 2024 14:16:58 +0000 -Subject: [PATCH] Add non-constant appFirmwareVersion extern - -The const qualifier version of appFirmwareVersion is not compatible -with freertos_ota_pal_psa definition - -Signed-off-by: Hugues Kamba-Mpiana ---- - source/include/ota_appversion32.h | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/source/include/ota_appversion32.h b/source/include/ota_appversion32.h -index 61ee83b..30b6f32 100644 ---- a/source/include/ota_appversion32.h -+++ b/source/include/ota_appversion32.h -@@ -75,7 +75,11 @@ typedef struct - } u; /*!< @brief Version based on configuration in big endian or little endian. */ - } AppVersion32_t; - --extern const AppVersion32_t appFirmwareVersion; /*!< @brief Making the version number available globally through external linkage. */ -+#if ( defined( OTA_USE_NONCONST_APPVERSION ) && ( OTA_USE_NONCONST_APPVERSION == 1 ) ) -+ extern AppVersion32_t appFirmwareVersion; /*!< @brief Making the version number available globally through external linkage, without const qualifier. */ -+#else -+ extern const AppVersion32_t appFirmwareVersion; /*!< @brief Making the version number available globally through external linkage, with const qualifier. */ -+#endif - - /* *INDENT-OFF* */ - #ifdef __cplusplus --- -2.34.1 - diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/src/ota_agent_task.c b/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/src/ota_agent_task.c deleted file mode 100644 index 9d42e2df..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/src/ota_agent_task.c +++ /dev/null @@ -1,1290 +0,0 @@ -/* - * FreeRTOS V202012.00 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * Copyright 2023 Arm Limited and/or its affiliates - * - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * https://www.FreeRTOS.org - * https://github.com/FreeRTOS - * - */ - -/* Standard includes. */ -#include -#include -#include -#include - -#include "app_config.h" - -#include "mqtt_agent_task.h" -#include "events.h" - -/* includes for TFM */ -#include "psa/fwu_config.h" -#include "psa/update.h" - -/* includes for OTA PAL PSA */ -#include "version/application_version.h" - -/* Kernel includes. */ -#include "FreeRTOS.h" -#include "task.h" -#include "semphr.h" - -/* Demo config includes. */ -#include "demo_config.h" -#include "iot_default_root_certificates.h" - -/* Library config includes. */ -#include "ota_config.h" - -/* Subscription manager header include. */ -#include "subscription_manager.h" - -/* OTA Library include. */ -#include "ota.h" - -/* OTA Library Interface include. */ -#include "ota_os_freertos.h" -#include "ota_mqtt_interface.h" -#include "ota_platform_interface.h" - -/* Include firmware version struct definition. */ -#include "ota_appversion32.h" - -/* Include platform abstraction header. */ -#include "ota_pal.h" - -/* Added for implicit inclusions */ -#include "core_mqtt.h" -#include "core_mqtt_agent.h" -#include "events.h" -#include "logging_stack.h" -#include "ota_os_interface.h" -#include "ota_mqtt_interface.h" -#include "ota_platform_interface.h" -#include "ota_private.h" -#include "subscription_manager.h" - -extern void vOtaNotActiveHook( void ); -extern void vOtaActiveHook( void ); - -/* Provides external linkage only when running unit test */ -#ifdef UNIT_TESTING - #define STATIC /* as nothing */ -#else /* ifdef UNIT_TESTING */ - #define STATIC static -#endif /* UNIT_TESTING */ - -/*------------- Demo configurations -------------------------*/ - -/** - * @brief The maximum size of the file paths used in the demo. - */ -#define otaexampleMAX_FILE_PATH_SIZE ( 260 ) - -/** - * @brief The maximum size of the stream name required for downloading update file - * from streaming service. - */ -#define otaexampleMAX_STREAM_NAME_SIZE ( 128 ) - -/** - * @brief The delay used in the OTA demo task to periodically output the OTA - * statistics like number of packets received, dropped, processed and queued per connection. - */ -#define otaexampleTASK_DELAY_MS ( 10000U ) - -/** - * @brief The maximum time for which OTA demo waits for an MQTT operation to be complete. - * This involves receiving an acknowledgment for broker for SUBSCRIBE, UNSUBSCRIBE and non - * QOS0 publishes. - */ -#define otaexampleMQTT_TIMEOUT_MS ( 5000U ) - -/** - * @brief The common prefix for all OTA topics. - * - * Thing name is substituted with a wildcard symbol `+`. OTA agent - * registers with MQTT broker with the thing name in the topic. This topic - * filter is used to match incoming packet received and route them to OTA. - * Thing name is not needed for this matching. - */ -#define OTA_TOPIC_PREFIX "$aws/things/+/" - -/** - * @brief Wildcard topic filter for job notification. - * The filter is used to match the constructed job notify topic filter from OTA agent and register - * appropriate callback for it. - */ -#define OTA_JOB_NOTIFY_TOPIC_FILTER OTA_TOPIC_PREFIX "jobs/notify-next" - -/** - * @brief Length of job notification topic filter. - */ -#define OTA_JOB_NOTIFY_TOPIC_FILTER_LENGTH ( ( uint16_t ) ( sizeof( OTA_JOB_NOTIFY_TOPIC_FILTER ) - 1 ) ) - -/** - * @brief Wildcard topic filter for matching job response messages. - * This topic filter is used to match the responses from OTA service for OTA agent job requests. THe - * topic filter is a reserved topic which is not subscribed with MQTT broker. - * - */ -#define OTA_JOB_ACCEPTED_RESPONSE_TOPIC_FILTER OTA_TOPIC_PREFIX "jobs/$next/get/accepted" - -/** - * @brief Length of job accepted response topic filter. - */ -#define OTA_JOB_ACCEPTED_RESPONSE_TOPIC_FILTER_LENGTH ( ( uint16_t ) ( sizeof( OTA_JOB_ACCEPTED_RESPONSE_TOPIC_FILTER ) - 1 ) ) - - -/** - * @brief Wildcard topic filter for matching OTA data packets. - * The filter is used to match the constructed data stream topic filter from OTA agent and register - * appropriate callback for it. - */ -#define OTA_DATA_STREAM_TOPIC_FILTER OTA_TOPIC_PREFIX "streams/#" - -/** - * @brief Length of data stream topic filter. - */ -#define OTA_DATA_STREAM_TOPIC_FILTER_LENGTH ( ( uint16_t ) ( sizeof( OTA_DATA_STREAM_TOPIC_FILTER ) - 1 ) ) - - -/** - * @brief Starting index of client identifier within OTA topic. - */ -#define OTA_TOPIC_CLIENT_IDENTIFIER_START_IDX ( 12U ) - -/** - * @brief Default topic filter for OTA. - * This is used to route all the packets for OTA reserved topics which OTA agent has not subscribed for. - */ -#define OTA_DEFAULT_TOPIC_FILTER OTA_TOPIC_PREFIX "jobs/#" - -/** - * @brief Length of default topic filter. - */ -#define OTA_DEFAULT_TOPIC_FILTER_LENGTH ( ( uint16_t ) ( sizeof( OTA_DEFAULT_TOPIC_FILTER ) - 1 ) ) - -/** - * @brief Used to clear bits in a task's notification value. - */ -#define otaexampleMAX_UINT32 ( 0xffffffff ) - -/** - * @brief Stack size required for OTA agent task. - */ -#define OTA_AGENT_TASK_STACK_SIZE ( 5000U ) - -/** - * @brief Priority required for OTA agent task. - */ -#define OTA_AGENT_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 ) - -/** - * @brief The timeout for waiting for the agent to get suspended after closing the - * connection. - * - * Timeout value should be large enough for OTA agent to finish any pending MQTT operations - * and suspend itself. - * - */ -#define OTA_SUSPEND_TIMEOUT_MS ( 10000U ) - -/*---------------------------------------------------------*/ - -/** - * @brief Structure used to store the topic filter to ota callback mappings. - */ -typedef struct OtaTopicFilterCallback -{ - const char * pTopicFilter; - uint16_t topicFilterLength; - IncomingPubCallback_t callback; -} OtaTopicFilterCallback_t; - -/*---------------------------------------------------------*/ - -/** - * @brief Buffer used to store the firmware image file path. - * Buffer is passed to the OTA agent during initialization. - */ - -static uint8_t updateFilePath[ otaexampleMAX_FILE_PATH_SIZE ]; - -/** - * @brief Buffer used to store the code signing certificate file path. - * Buffer is passed to the OTA agent during initialization. - */ -static uint8_t certFilePath[ otaexampleMAX_FILE_PATH_SIZE ]; - -/** - * @brief Buffer used to store the name of the data stream. - * Buffer is passed to the OTA agent during initialization. - */ -static uint8_t streamName[ otaexampleMAX_STREAM_NAME_SIZE ]; - -/** - * @brief Buffer used decode the CBOR message from the MQTT payload. - * Buffer is passed to the OTA agent during initialization. - */ -static uint8_t decodeMem[ ( 1U << otaconfigLOG2_FILE_BLOCK_SIZE ) ]; - -/** - * @brief Application buffer used to store the bitmap for requesting firmware image - * chunks from MQTT broker. Buffer is passed to the OTA agent during initialization. - */ -static uint8_t bitmap[ OTA_MAX_BLOCK_BITMAP_SIZE ]; - -/** - * @brief A statically allocated array of event buffers used by the OTA agent. - * Maximum number of buffers are determined by how many chunks are requested - * by OTA agent at a time along with an extra buffer to handle control message. - * The size of each buffer is determined by the maximum size of firmware image - * chunk, and other metadata send along with the chunk. - */ -static OtaEventData_t eventBuffer[ otaconfigMAX_NUM_OTA_DATA_BUFFERS ] = { 0 }; - -/* - * @brief Mutex used to manage thread safe access of OTA event buffers. - */ -static SemaphoreHandle_t xBufferSemaphore; - -/*---------------------------------------------------------*/ - -/** - * @brief The MQTT agent manages the MQTT contexts. This set the handle to the - * context used by this demo. - */ -extern MQTTAgentContext_t xGlobalMqttAgentContext; - -/*---------------------------------------------------------*/ - -/** - * @brief Fetch an unused OTA event buffer from the pool. - * - * Demo uses a simple statically allocated array of fixed size event buffers. The - * number of event buffers is configured by the param otaconfigMAX_NUM_OTA_DATA_BUFFERS - * within ota_config.h. This function is used to fetch a free buffer from the pool for processing - * by the OTA agent task. It uses a mutex for thread safe access to the pool. - * - * @return A pointer to an unusued buffer. NULL if there are no buffers available. - */ -STATIC OtaEventData_t * prvOTAEventBufferGet( void ); - -/** - * @brief Free an event buffer back to pool - * - * OTA demo uses a statically allocated array of fixed size event buffers . The - * number of event buffers is configured by the param otaconfigMAX_NUM_OTA_DATA_BUFFERS - * within ota_config.h. The function is used by the OTA application callback to free a buffer, - * after OTA agent has completed processing with the event. The access to the pool is made thread safe - * using a mutex. - * - * @param[in] pxBuffer Pointer to the buffer to be freed. - */ -STATIC void prvOTAEventBufferFree( OtaEventData_t * const pxBuffer ); - -/** - * @brief The function which runs the OTA agent task. - * - * The function runs the OTA Agent Event processing loop, which waits for - * any events for OTA agent and process them. The loop never returns until the OTA agent - * is shutdown. The tasks exits gracefully by freeing up all resources in the event of an - * OTA agent shutdown. - * - * @param[in] pvParam Any parameters to be passed to OTA agent task. - */ -STATIC void prvOTAAgentTask( void * pvParam ); - - -/** - * @brief The function which runs the OTA demo task. - * - * The demo task initializes the OTA agent an loops until OTA agent is shutdown. - * It reports OTA update statistics (which includes number of blocks received, processed and dropped), - * at regular intervals. - * - * @param[in] pvParam Any parameters to be passed to OTA Demo task. - */ -STATIC void vOtaDemoTask( void * pvParam ); - -/** - * @brief The function which implements the flow for OTA demo. - * - * @return pdPASS if success or pdFAIL. - */ -STATIC BaseType_t prvRunOTADemo( void ); - -/** - * @brief Callback registered with the OTA library that notifies the OTA agent - * of an incoming PUBLISH containing a job document. - * - * @param[in] pContext MQTT context which stores the connection. - * @param[in] pPublishInfo MQTT packet information which stores details of the - * job document. - */ -STATIC void prvMqttJobCallback( void * pContext, - MQTTPublishInfo_t * pPublish ); - - -/** - * @brief Callback that notifies the OTA library when a data block is received. - * - * @param[in] pContext MQTT context which stores the connection. - * @param[in] pPublishInfo MQTT packet that stores the information of the file block. - */ -STATIC void prvMqttDataCallback( void * pContext, - MQTTPublishInfo_t * pPublish ); - -/** - * @brief Default callback used to receive unsolicited messages for OTA. - * - * The callback is not subscribed with MQTT broker, but only with local subscription manager. - * A wildcard OTA job topic is used for subscription so that all unsolicited messages related to OTA is - * forwarded to this callback for filtration. Right now the callback is used to filter responses to job requests - * from the OTA service. - * - * @param[in] pvIncomingPublishCallbackContext MQTT context which stores the connection. - * @param[in] pPublishInfo MQTT packet that stores the information of the file block. - */ -STATIC void prvMqttDefaultCallback( void * pvIncomingPublishCallbackContext, - MQTTPublishInfo_t * pxPublishInfo ); - -/** - * @brief Register OTA callbacks with the subscription manager. - * - * @param[in] pTopicFilter The topic filter for which a callback needs to be registered for. - * @param[in] topicFilterLength length of the topic filter. - * - */ -STATIC void prvRegisterOTACallback( const char * pTopicFilter, - uint16_t topicFilterLength ); - -/** - * @brief Suspend OTA demo. - * - * @return pPASS or pdFAIL. - */ -STATIC BaseType_t prvSuspendOTA( void ); - -/** - * @brief Resume OTA demo. - * - * @return pPASS or pdFAIL. - */ -STATIC BaseType_t prvResumeOTA( void ); - -/** - * @brief Set OTA interfaces. - * - * @param[in] pOtaInterfaces pointer to OTA interface structure. - * - * @return None. - */ -STATIC void setOtaInterfaces( OtaInterfaces_t * pOtaInterfaces ); - -/** - * @brief Structure containing all application allocated buffers used by the OTA agent. - * Structure is passed to the OTA agent during initialization. - */ -static OtaAppBuffer_t otaBuffer = -{ - .pUpdateFilePath = updateFilePath, - .updateFilePathsize = otaexampleMAX_FILE_PATH_SIZE, - .pCertFilePath = certFilePath, - .certFilePathSize = otaexampleMAX_FILE_PATH_SIZE, - .pStreamName = streamName, - .streamNameSize = otaexampleMAX_STREAM_NAME_SIZE, - .pDecodeMemory = decodeMem, - .decodeMemorySize = ( 1U << otaconfigLOG2_FILE_BLOCK_SIZE ), - .pFileBitmap = bitmap, - .fileBitmapSize = OTA_MAX_BLOCK_BITMAP_SIZE -}; - -/** - * @brief Registry for all mqtt topic filters to their corresponding callbacks for OTA. - */ -static OtaTopicFilterCallback_t otaTopicFilterCallbacks[] = -{ - { - .pTopicFilter = OTA_JOB_NOTIFY_TOPIC_FILTER, - .topicFilterLength = OTA_JOB_NOTIFY_TOPIC_FILTER_LENGTH, - .callback = prvMqttJobCallback - }, - { - .pTopicFilter = OTA_DATA_STREAM_TOPIC_FILTER, - .topicFilterLength = OTA_DATA_STREAM_TOPIC_FILTER_LENGTH, - .callback = prvMqttDataCallback - }, - { - .pTopicFilter = OTA_DEFAULT_TOPIC_FILTER, - .topicFilterLength = OTA_DEFAULT_TOPIC_FILTER_LENGTH, - .callback = prvMqttDefaultCallback - } -}; - -/*-----------------------------------------------------------*/ - -STATIC void prvOTAEventBufferFree( OtaEventData_t * const pxBuffer ) -{ - if( xSemaphoreTake( xBufferSemaphore, portMAX_DELAY ) == pdTRUE ) - { - pxBuffer->bufferUsed = false; - ( void ) xSemaphoreGive( xBufferSemaphore ); - } -} - -/*-----------------------------------------------------------*/ - -STATIC OtaEventData_t * prvOTAEventBufferGet( void ) -{ - OtaEventData_t * pFreeBuffer = NULL; - - if( xSemaphoreTake( xBufferSemaphore, portMAX_DELAY ) == pdTRUE ) - { - uint32_t ulIndex; - - for( ulIndex = 0; ulIndex < otaconfigMAX_NUM_OTA_DATA_BUFFERS; ulIndex++ ) - { - if( eventBuffer[ ulIndex ].bufferUsed == false ) - { - eventBuffer[ ulIndex ].bufferUsed = true; - pFreeBuffer = &eventBuffer[ ulIndex ]; - pFreeBuffer->dataLength = sizeof( pFreeBuffer->data ); - break; - } - } - - ( void ) xSemaphoreGive( xBufferSemaphore ); - } - - return pFreeBuffer; -} - -/*-----------------------------------------------------------*/ - -/** - * @brief The OTA agent has completed the update job or it is in - * self test mode. If it was accepted, we want to activate the new image. - * This typically means we should reset the device to run the new firmware. - * If now is not a good time to reset the device, it may be activated later - * by your user code. If the update was rejected, just return without doing - * anything and we will wait for another job. If it reported that we should - * start test mode, normally we would perform some kind of system checks to - * make sure our new firmware does the basic things we think it should do - * but we will just go ahead and set the image as accepted for demo purposes. - * The accept function varies depending on your platform. Refer to the OTA - * PAL implementation for your platform in ota_pal.c to see what it - * does for you. - * - * @param[in] event Specify if this demo is running with the AWS IoT - * MQTT server. Set this to `false` if using another MQTT server. - * @param[in] pData Data associated with the event. - * @return None. - */ - -STATIC void otaAppCallback( OtaJobEvent_t event, - void * pData ) -{ - OtaErr_t err = OtaErrUninitialized; - - switch( event ) - { - case OtaJobEventActivate: - LogInfo( ( "Received OtaJobEventActivate callback from OTA Agent." ) ); - - /** - * Activate the new firmware image immediately. Applications can choose to postpone - * the activation to a later stage if needed. - */ - ( void ) OTA_ActivateNewImage(); - - /** - * Activation of the new image failed. This indicates an error that requires a follow - * up through manual activation by resetting the device. The demo reports the error - * and shuts down the OTA agent. - */ - LogError( ( "New image activation failed." ) ); - - /* Shutdown OTA Agent, if it is required that the unsubscribe operations are not - * performed while shutting down please set the second parameter to 0 instead of 1. */ - OTA_Shutdown( 0, 1 ); - - - break; - - case OtaJobEventFail: - - /** - * No user action is needed here. OTA agent handles the job failure event. - */ - LogInfo( ( "Received an OtaJobEventFail notification from OTA Agent." ) ); - - break; - - case OtaJobEventStartTest: - - /* This demo just accepts the image since it was a good OTA update and networking - * and services are all working (or we would not have made it this far). If this - * were some custom device that wants to test other things before validating new - * image, this would be the place to kick off those tests before calling - * OTA_SetImageState() with the final result of either accepted or rejected. */ - - LogInfo( ( "Received OtaJobEventStartTest callback from OTA Agent." ) ); - - err = OTA_SetImageState( OtaImageStateAccepted ); - - if( err == OtaErrNone ) - { - LogInfo( ( "New image validation succeeded in self test mode." ) ); - } - else - { - LogError( ( "Failed to set image state as accepted with error %d.", err ) ); - } - - break; - - case OtaJobEventProcessed: - - LogDebug( ( "OTA Event processing completed. Freeing the event buffer to pool." ) ); - configASSERT( pData != NULL ); - prvOTAEventBufferFree( ( OtaEventData_t * ) pData ); - - break; - - case OtaJobEventSelfTestFailed: - LogDebug( ( "Received OtaJobEventSelfTestFailed callback from OTA Agent." ) ); - - /* Requires manual activation of previous image as self-test for - * new image downloaded failed.*/ - LogError( ( "OTA Self-test failed for new image. shutting down OTA Agent." ) ); - - /* Shutdown OTA Agent, if it is required that the unsubscribe operations are not - * performed while shutting down please set the second parameter to 0 instead of 1. */ - OTA_Shutdown( 0, 1 ); - - break; - - case OtaJobEventReceivedJob: - LogDebug( ( "Received OtaJobEventReceivedJob callback from OTA Agent." ) ); - vOtaActiveHook(); - return; - - case OtaJobEventNoActiveJob: - LogDebug( ( "Received OtaJobEventNoActiveJob callback from OTA Agent." ) ); - vOtaNotActiveHook(); - return; - - default: - LogWarn( ( "Received an unhandled callback event from OTA Agent, event = %d", event ) ); - break; - } -} - -STATIC void prvMqttJobCallback( void * pvIncomingPublishCallbackContext, - MQTTPublishInfo_t * pxPublishInfo ) -{ - OtaEventData_t * pData; - OtaEventMsg_t eventMsg = { 0 }; - - configASSERT( pxPublishInfo != NULL ); - ( void ) pvIncomingPublishCallbackContext; - - LogInfo( ( "Received job message callback, size %ld.\n", pxPublishInfo->payloadLength ) ); - - pData = prvOTAEventBufferGet(); - - if( pData != NULL ) - { - if( ( size_t ) pData->dataLength >= pxPublishInfo->payloadLength ) - { - memcpy( pData->data, pxPublishInfo->pPayload, pxPublishInfo->payloadLength ); - pData->dataLength = pxPublishInfo->payloadLength; - eventMsg.eventId = OtaAgentEventReceivedJobDocument; - eventMsg.pEventData = pData; - - /* Send job document received event. */ - OTA_SignalEvent( &eventMsg ); - } - else - { - LogError( ( "Error: OTA data buffers are too small for the Job message provided.\n" ) ); - } - } - else - { - LogError( ( "Error: No OTA data buffers available.\n" ) ); - } -} - -/*-----------------------------------------------------------*/ - -STATIC void prvMqttDefaultCallback( void * pvIncomingPublishCallbackContext, - MQTTPublishInfo_t * pxPublishInfo ) -{ - bool isMatch = false; - - ( void ) MQTT_MatchTopic( pxPublishInfo->pTopicName, - pxPublishInfo->topicNameLength, - OTA_JOB_ACCEPTED_RESPONSE_TOPIC_FILTER, - OTA_JOB_ACCEPTED_RESPONSE_TOPIC_FILTER_LENGTH, - &isMatch ); - - if( isMatch == true ) - { - prvMqttJobCallback( pvIncomingPublishCallbackContext, pxPublishInfo ); - } -} - -/*-----------------------------------------------------------*/ - -STATIC void prvMqttDataCallback( void * pvIncomingPublishCallbackContext, - MQTTPublishInfo_t * pxPublishInfo ) -{ - OtaEventData_t * pxData; - OtaEventMsg_t eventMsg = { 0 }; - - configASSERT( pxPublishInfo != NULL ); - ( void ) pvIncomingPublishCallbackContext; - - LogInfo( ( "Received data message callback, size %zu.\n", pxPublishInfo->payloadLength ) ); - - pxData = prvOTAEventBufferGet(); - - if( pxData != NULL ) - { - if( ( size_t ) pxData->dataLength >= pxPublishInfo->payloadLength ) - { - memcpy( pxData->data, pxPublishInfo->pPayload, pxPublishInfo->payloadLength ); - pxData->dataLength = pxPublishInfo->payloadLength; - eventMsg.eventId = OtaAgentEventReceivedFileBlock; - eventMsg.pEventData = pxData; - - /* Send file block received event. */ - OTA_SignalEvent( &eventMsg ); - } - else - { - LogError( ( "Error: OTA data buffers are too small for the data message received.\n" ) ); - } - } - else - { - LogError( ( "Error: No OTA data buffers available.\n" ) ); - } -} - -/*-----------------------------------------------------------*/ - -STATIC void prvRegisterOTACallback( const char * pTopicFilter, - uint16_t topicFilterLength ) -{ - bool isMatch = false; - MQTTStatus_t mqttStatus = MQTTSuccess; - uint16_t index = 0U; - uint16_t numTopicFilters = sizeof( otaTopicFilterCallbacks ) / sizeof( OtaTopicFilterCallback_t ); - - - bool subscriptionAdded; - - ( void ) mqttStatus; - - /* Match the input topic filter against the wild-card pattern of topics filters - * relevant for the OTA Update service to determine the type of topic filter. */ - for( ; index < numTopicFilters; index++ ) - { - mqttStatus = MQTT_MatchTopic( pTopicFilter, - topicFilterLength, - otaTopicFilterCallbacks[ index ].pTopicFilter, - otaTopicFilterCallbacks[ index ].topicFilterLength, - &isMatch ); - assert( mqttStatus == MQTTSuccess ); - - if( isMatch ) - { - /* Add subscription so that incoming publishes are routed to the application callback. */ - subscriptionAdded = addSubscription( pTopicFilter, - topicFilterLength, - otaTopicFilterCallbacks[ index ].callback, - NULL ); - - if( subscriptionAdded == false ) - { - LogError( ( "Failed to register a publish callback for topic %.*s.", - pTopicFilter, - topicFilterLength ) ); - } - } - } -} - -STATIC void prvMQTTSubscribeCompleteCallback( MQTTAgentCommandContext_t * pxCommandContext, - MQTTAgentReturnInfo_t * pxReturnInfo ) -{ - if( pxReturnInfo->returnCode == MQTTSuccess ) - { - MQTTAgentSubscribeArgs_t * pSubscribeArgs = ( MQTTAgentSubscribeArgs_t * ) ( pxCommandContext->pArgs ); - prvRegisterOTACallback( pSubscribeArgs->pSubscribeInfo->pTopicFilter, pSubscribeArgs->pSubscribeInfo->topicFilterLength ); - } - - /* Store the result in the application defined context so the task that - * initiated the publish can check the operation's status. */ - pxCommandContext->xReturnStatus = pxReturnInfo->returnCode; - - if( pxCommandContext->xTaskToNotify != NULL ) - { - /* Send the context's ulNotificationValue as the notification value so - * the receiving task can check the value it set in the context matches - * the value it receives in the notification. */ - xTaskNotify( pxCommandContext->xTaskToNotify, ( uint32_t ) ( pxReturnInfo->returnCode ), eSetValueWithOverwrite ); - } -} - -STATIC void prvMQTTUnsubscribeCompleteCallback( MQTTAgentCommandContext_t * pxCommandContext, - MQTTAgentReturnInfo_t * pxReturnInfo ) -{ - /* Store the result in the application defined context so the task that - * initiated the publish can check the operation's status. */ - pxCommandContext->xReturnStatus = pxReturnInfo->returnCode; - - if( pxCommandContext->xTaskToNotify != NULL ) - { - /* Send the context's ulNotificationValue as the notification value so - * the receiving task can check the value it set in the context matches - * the value it receives in the notification. */ - xTaskNotify( pxCommandContext->xTaskToNotify, ( uint32_t ) ( pxReturnInfo->returnCode ), eSetValueWithOverwrite ); - } -} - -/* - * Precondition: pTopicFilter is not null. - */ -STATIC OtaMqttStatus_t prvMQTTSubscribe( const char * pTopicFilter, - uint16_t topicFilterLength, - uint8_t ucQoS ) -{ - MQTTStatus_t mqttStatus; - uint32_t ulNotifiedValue; - static MQTTAgentSubscribeArgs_t xSubscribeArgs = { 0 }; - static MQTTSubscribeInfo_t xSubscribeInfo = { 0 }; - BaseType_t result; - static MQTTAgentCommandInfo_t xCommandParams = { 0 }; - static MQTTAgentCommandContext_t xApplicationDefinedContext = { 0 }; - OtaMqttStatus_t otaRet = OtaMqttSuccess; - - configASSERT( pTopicFilter != NULL ); - configASSERT( topicFilterLength > 0 ); - - xSubscribeInfo.pTopicFilter = pTopicFilter; - xSubscribeInfo.topicFilterLength = topicFilterLength; - xSubscribeInfo.qos = ucQoS; - xSubscribeArgs.pSubscribeInfo = &xSubscribeInfo; - xSubscribeArgs.numSubscriptions = 1; - - xApplicationDefinedContext.xTaskToNotify = xTaskGetCurrentTaskHandle(); - xApplicationDefinedContext.pArgs = &xSubscribeArgs; - xApplicationDefinedContext.xReturnStatus = MQTTSendFailed; - - xCommandParams.blockTimeMs = otaexampleMQTT_TIMEOUT_MS; - xCommandParams.cmdCompleteCallback = prvMQTTSubscribeCompleteCallback; - xCommandParams.pCmdCompleteCallbackContext = ( void * ) &xApplicationDefinedContext; - - xTaskNotifyStateClear( NULL ); - - mqttStatus = MQTTAgent_Subscribe( &xGlobalMqttAgentContext, - &xSubscribeArgs, - &xCommandParams ); - - /* Wait for command to complete so MQTTSubscribeInfo_t remains in scope for the - * duration of the command. */ - if( mqttStatus == MQTTSuccess ) - { - result = xTaskNotifyWait( 0, otaexampleMAX_UINT32, &ulNotifiedValue, pdMS_TO_TICKS( otaexampleMQTT_TIMEOUT_MS ) ); - - if( result == pdTRUE ) - { - mqttStatus = xApplicationDefinedContext.xReturnStatus; - } - else - { - mqttStatus = MQTTRecvFailed; - } - } - - if( mqttStatus != MQTTSuccess ) - { - LogError( ( "Failed to SUBSCRIBE to topic with error = %u.", - mqttStatus ) ); - otaRet = OtaMqttSubscribeFailed; - } - else - { - LogInfo( ( "Subscribed to topic %.*s.\n", - topicFilterLength, - pTopicFilter ) ); - otaRet = OtaMqttSuccess; - } - - return otaRet; -} - -STATIC void prvOTAPublishCommandCallback( MQTTAgentCommandContext_t * pxCommandContext, - MQTTAgentReturnInfo_t * pxReturnInfo ) -{ - pxCommandContext->xReturnStatus = pxReturnInfo->returnCode; - - if( pxCommandContext->xTaskToNotify != NULL ) - { - xTaskNotify( pxCommandContext->xTaskToNotify, ( uint32_t ) ( pxReturnInfo->returnCode ), eSetValueWithOverwrite ); - } -} - -STATIC OtaMqttStatus_t prvMQTTPublish( const char * const pacTopic, - uint16_t topicLen, - const char * pMsg, - uint32_t msgSize, - uint8_t qos ) -{ - BaseType_t result; - MQTTStatus_t mqttStatus = MQTTBadParameter; - static MQTTPublishInfo_t publishInfo = { 0 }; - static MQTTAgentCommandInfo_t xCommandParams = { 0 }; - static MQTTAgentCommandContext_t xCommandContext = { 0 }; - OtaMqttStatus_t otaRet = OtaMqttSuccess; - - publishInfo.pTopicName = pacTopic; - publishInfo.topicNameLength = topicLen; - publishInfo.qos = qos; - publishInfo.pPayload = pMsg; - publishInfo.payloadLength = msgSize; - - xCommandContext.xTaskToNotify = xTaskGetCurrentTaskHandle(); - xTaskNotifyStateClear( NULL ); - - xCommandParams.blockTimeMs = otaexampleMQTT_TIMEOUT_MS; - xCommandParams.cmdCompleteCallback = prvOTAPublishCommandCallback; - xCommandParams.pCmdCompleteCallbackContext = ( void * ) &xCommandContext; - - mqttStatus = MQTTAgent_Publish( &xGlobalMqttAgentContext, - &publishInfo, - &xCommandParams ); - - /* Wait for command to complete so MQTTSubscribeInfo_t remains in scope for the - * duration of the command. */ - if( mqttStatus == MQTTSuccess ) - { - result = xTaskNotifyWait( 0, otaexampleMAX_UINT32, NULL, pdMS_TO_TICKS( otaexampleMQTT_TIMEOUT_MS ) ); - - if( result != pdTRUE ) - { - mqttStatus = MQTTSendFailed; - } - else - { - mqttStatus = xCommandContext.xReturnStatus; - } - } - - if( mqttStatus != MQTTSuccess ) - { - LogError( ( "Failed to send PUBLISH packet to broker with error = %u.", mqttStatus ) ); - otaRet = OtaMqttPublishFailed; - } - else - { - LogInfo( ( "Sent PUBLISH packet to broker %.*s to broker.\n", - topicLen, - pacTopic ) ); - otaRet = OtaMqttSuccess; - } - - return otaRet; -} - -STATIC OtaMqttStatus_t prvMQTTUnsubscribe( const char * pTopicFilter, - uint16_t topicFilterLength, - uint8_t ucQoS ) -{ - MQTTStatus_t mqttStatus; - uint32_t ulNotifiedValue; - static MQTTAgentSubscribeArgs_t xSubscribeArgs = { 0 }; - static MQTTSubscribeInfo_t xSubscribeInfo = { 0 }; - BaseType_t result; - static MQTTAgentCommandInfo_t xCommandParams = { 0 }; - static MQTTAgentCommandContext_t xApplicationDefinedContext = { 0 }; - OtaMqttStatus_t otaRet = OtaMqttSuccess; - - configASSERT( pTopicFilter != NULL ); - configASSERT( topicFilterLength > 0 ); - - xSubscribeInfo.pTopicFilter = pTopicFilter; - xSubscribeInfo.topicFilterLength = topicFilterLength; - xSubscribeInfo.qos = ucQoS; - xSubscribeArgs.pSubscribeInfo = &xSubscribeInfo; - xSubscribeArgs.numSubscriptions = 1; - - - xApplicationDefinedContext.xTaskToNotify = xTaskGetCurrentTaskHandle(); - - xCommandParams.blockTimeMs = otaexampleMQTT_TIMEOUT_MS; - xCommandParams.cmdCompleteCallback = prvMQTTUnsubscribeCompleteCallback; - xCommandParams.pCmdCompleteCallbackContext = ( void * ) &xApplicationDefinedContext; - - LogInfo( ( " Unsubscribing to topic filter: %s", pTopicFilter ) ); - xTaskNotifyStateClear( NULL ); - - - mqttStatus = MQTTAgent_Unsubscribe( &xGlobalMqttAgentContext, - &xSubscribeArgs, - &xCommandParams ); - - /* Wait for command to complete so MQTTSubscribeInfo_t remains in scope for the - * duration of the command. */ - if( mqttStatus == MQTTSuccess ) - { - result = xTaskNotifyWait( 0, otaexampleMAX_UINT32, &ulNotifiedValue, pdMS_TO_TICKS( otaexampleMQTT_TIMEOUT_MS ) ); - - if( result == pdTRUE ) - { - mqttStatus = xApplicationDefinedContext.xReturnStatus; - } - else - { - mqttStatus = MQTTRecvFailed; - } - } - - if( mqttStatus != MQTTSuccess ) - { - LogError( ( "Failed to UNSUBSCRIBE from topic %.*s with error = %u.", - topicFilterLength, - pTopicFilter, - mqttStatus ) ); - otaRet = OtaMqttUnsubscribeFailed; - } - else - { - LogInfo( ( "UNSUBSCRIBED from topic %.*s.\n", - topicFilterLength, - pTopicFilter ) ); - otaRet = OtaMqttSuccess; - } - - return otaRet; -} - -STATIC void setOtaInterfaces( OtaInterfaces_t * pOtaInterfaces ) -{ - configASSERT( pOtaInterfaces != NULL ); - - /* Initialize OTA library OS Interface. */ - pOtaInterfaces->os.event.init = OtaInitEvent_FreeRTOS; - pOtaInterfaces->os.event.send = OtaSendEvent_FreeRTOS; - pOtaInterfaces->os.event.recv = OtaReceiveEvent_FreeRTOS; - pOtaInterfaces->os.event.deinit = OtaDeinitEvent_FreeRTOS; - pOtaInterfaces->os.timer.start = OtaStartTimer_FreeRTOS; - pOtaInterfaces->os.timer.stop = OtaStopTimer_FreeRTOS; - pOtaInterfaces->os.timer.delete = OtaDeleteTimer_FreeRTOS; - pOtaInterfaces->os.mem.malloc = Malloc_FreeRTOS; - pOtaInterfaces->os.mem.free = Free_FreeRTOS; - - /* Initialize the OTA library MQTT Interface.*/ - pOtaInterfaces->mqtt.subscribe = prvMQTTSubscribe; - pOtaInterfaces->mqtt.publish = prvMQTTPublish; - pOtaInterfaces->mqtt.unsubscribe = prvMQTTUnsubscribe; - - /* Initialize the OTA library PAL Interface.*/ - pOtaInterfaces->pal.getPlatformImageState = otaPal_GetPlatformImageState; - pOtaInterfaces->pal.setPlatformImageState = otaPal_SetPlatformImageState; - pOtaInterfaces->pal.writeBlock = otaPal_WriteBlock; - pOtaInterfaces->pal.activate = otaPal_ActivateNewImage; - pOtaInterfaces->pal.closeFile = otaPal_CloseFile; - pOtaInterfaces->pal.reset = otaPal_ResetDevice; - pOtaInterfaces->pal.abort = otaPal_Abort; - pOtaInterfaces->pal.createFile = otaPal_CreateFileForRx; -} - -/*-----------------------------------------------------------*/ - -STATIC void prvOTAAgentTask( void * pParam ) -{ - /* Calling OTA agent task. */ - OTA_EventProcessingTask( pParam ); - LogInfo( ( "OTA Agent stopped." ) ); - - vTaskDelete( NULL ); -} - -STATIC BaseType_t prvSuspendOTA( void ) -{ - /* OTA library return status. */ - OtaErr_t otaRet = OtaErrNone; - BaseType_t status = pdPASS; - - otaRet = OTA_Suspend(); - - if( otaRet == OtaErrNone ) - { - uint32_t suspendTimeout = OTA_SUSPEND_TIMEOUT_MS; - - while( ( OTA_GetState() != OtaAgentStateSuspended ) && ( suspendTimeout > 0 ) ) - { - /* Wait for OTA Library state to suspend */ - vTaskDelay( pdMS_TO_TICKS( otaexampleTASK_DELAY_MS ) ); - suspendTimeout -= otaexampleTASK_DELAY_MS; - } - - if( OTA_GetState() != OtaAgentStateSuspended ) - { - LogError( ( "Failed to suspend OTA." ) ); - status = pdFAIL; - } - } - else - { - LogError( ( "Error while trying to suspend OTA agent %d", otaRet ) ); - status = pdFAIL; - } - - return status; -} - -STATIC BaseType_t prvResumeOTA( void ) -{ - /* OTA library return status. */ - OtaErr_t otaRet = OtaErrNone; - BaseType_t status = pdPASS; - - otaRet = OTA_Resume(); - - if( otaRet == OtaErrNone ) - { - uint32_t suspendTimeout = OTA_SUSPEND_TIMEOUT_MS; - - while( ( OTA_GetState() == OtaAgentStateSuspended ) && ( suspendTimeout > 0 ) ) - { - /* Wait for OTA Library state to suspend */ - vTaskDelay( pdMS_TO_TICKS( otaexampleTASK_DELAY_MS ) ); - suspendTimeout -= otaexampleTASK_DELAY_MS; - } - - if( OTA_GetState() == OtaAgentStateSuspended ) - { - LogError( ( "Failed to resume OTA." ) ); - status = pdFAIL; - } - } - else - { - LogError( ( "Error while trying to resume OTA agent %d", otaRet ) ); - status = pdFAIL; - } - - return status; -} - -STATIC BaseType_t prvRunOTADemo( void ) -{ - /* Status indicating a successful demo or not. */ - BaseType_t xStatus = pdPASS; - - /* OTA event message used for sending event to OTA Agent.*/ - OtaEventMsg_t eventMsg = { 0 }; - - /* OTA interface context required for library interface functions.*/ - OtaInterfaces_t otaInterfaces; - - /* OTA library packet statistics per job.*/ - OtaAgentStatistics_t otaStatistics = { 0 }; - - /* OTA Agent state returned from calling OTA_GetState.*/ - OtaState_t state; - - /* Set OTA Library interfaces.*/ - setOtaInterfaces( &otaInterfaces ); - - vWaitUntilMQTTAgentReady(); - vWaitUntilMQTTAgentConnected(); - - /****************************** Init OTA Library. ******************************/ - - if( xStatus == pdPASS ) - { - /* OTA library return status. */ - OtaErr_t otaRet; - - if( ( otaRet = OTA_Init( &otaBuffer, - &otaInterfaces, - ( const uint8_t * ) ( democonfigCLIENT_IDENTIFIER ), - otaAppCallback ) ) != OtaErrNone ) - { - LogError( ( "Failed to initialize OTA Agent, exiting = %u.", - otaRet ) ); - - xStatus = pdFAIL; - } - } - - /****************************** Create OTA Agent Task. ******************************/ - - if( xStatus == pdPASS ) - { - xStatus = xTaskCreate( prvOTAAgentTask, - "OTA Agent Task", - OTA_AGENT_TASK_STACK_SIZE, - NULL, - OTA_AGENT_TASK_PRIORITY, - NULL ); - - if( xStatus != pdPASS ) - { - LogError( ( "Failed to create OTA agent task:" ) ); - } - } - - /** - * Register a callback for receiving messages intended for OTA agent from broker, - * for which the topic has not been subscribed for. - */ - prvRegisterOTACallback( OTA_DEFAULT_TOPIC_FILTER, OTA_DEFAULT_TOPIC_FILTER_LENGTH ); - - /****************************** Start OTA ******************************/ - - if( xStatus == pdPASS ) - { - /* Send start event to OTA Agent.*/ - eventMsg.eventId = OtaAgentEventStart; - OTA_SignalEvent( &eventMsg ); - - /* Loop and display OTA statistics */ - while( ( state = OTA_GetState() ) != OtaAgentStateStopped ) - { - /* Get OTA statistics for currently executing job. */ - if( state != OtaAgentStateSuspended ) - { - OTA_GetStatistics( &otaStatistics ); - - LogInfo( ( " Received: %u Queued: %u Processed: %u Dropped: %u", - otaStatistics.otaPacketsReceived, - otaStatistics.otaPacketsQueued, - otaStatistics.otaPacketsProcessed, - otaStatistics.otaPacketsDropped ) ); - } - - if( !xIsMqttAgentConnected() ) - { - xStatus = prvSuspendOTA(); - configASSERT( xStatus == pdPASS ); - - LogInfo( ( "Suspended OTA agent." ) ); - } - else - { - if( OTA_GetState() == OtaAgentStateSuspended ) - { - xStatus = prvResumeOTA(); - configASSERT( xStatus == pdPASS ); - - LogInfo( ( "Resumed OTA agent." ) ); - } - } - - vTaskDelay( pdMS_TO_TICKS( otaexampleTASK_DELAY_MS ) ); - } - } - - /** - * Remove callback for receiving messages intended for OTA agent from broker, - * for which the topic has not been subscribed for. - */ - removeSubscription( OTA_DEFAULT_TOPIC_FILTER, - OTA_DEFAULT_TOPIC_FILTER_LENGTH ); - - return xStatus; -} - -/** - * @brief Entry point of Ota demo task. - * - * This example initializes the OTA library to enable OTA updates via the - * MQTT broker. It simply connects to the MQTT broker with the users - * credentials and spins in an indefinite loop to allow MQTT messages to be - * forwarded to the OTA agent for possible processing. The OTA agent does all - * of the real work; checking to see if the message topic is one destined for - * the OTA agent. If not, it is simply ignored. - * - */ -STATIC void vOtaDemoTask( void * pvParam ) -{ - ( void ) pvParam; - - if( GetImageVersionPSA( FWU_COMPONENT_ID_NONSECURE ) == 0 ) - { - LogInfo( ( "OTA over MQTT, Application version from appFirmwareVersion %u.%u.%u\n", - appFirmwareVersion.u.x.major, - appFirmwareVersion.u.x.minor, - appFirmwareVersion.u.x.build ) ); - } - else - { - LogError( ( "OTA over MQTT, unable to get application versions" ) ); - } - - /* Initialize semaphore for buffer operations. */ - xBufferSemaphore = xSemaphoreCreateMutex(); - - if( xBufferSemaphore == NULL ) - { - LogError( ( "Failed to initialize buffer semaphore." ) ); - } - else - { - /****************************** Start OTA Demo. ******************************/ - - /* Start OTA demo. The function returns only if OTA completes successfully and a - * shutdown of OTA is triggered for a manual restart of the device. */ - if( prvRunOTADemo() != pdPASS ) - { - LogError( ( "Failed to complete OTA successfully." ) ); - } - - /* / ****************************** Cleanup ****************************** / */ - - /* Cleanup semaphore created for buffer operations. */ - vSemaphoreDelete( xBufferSemaphore ); - } -} - -/* - * @brief Create the task that demonstrates the Ota demo. - */ -void vStartOtaTask( void ) -{ - xTaskCreate( vOtaDemoTask, /* Function that implements the task. */ - "OTA Task ", /* Text name for the task - only used for debugging. */ - appCONFIG_OTA_MQTT_AGENT_TASK_STACK_SIZE, /* Size of stack (in words, not bytes) to allocate for the task. */ - NULL, /* Optional - task parameter - not used in this case. */ - appCONFIG_OTA_MQTT_AGENT_TASK_PRIORITY, /* Task priority, must be between 0 and configMAX_PRIORITIES - 1. */ - NULL ); /* Optional - used to pass out a handle to the created task. */ -} diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/CMakeLists.txt b/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/CMakeLists.txt deleted file mode 100644 index 5fb403d1..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2024 Arm Limited and/or its affiliates -# -# SPDX-License-Identifier: MIT - -# application-specific mocks -add_subdirectory(config_mocks) - -add_executable(ota-agent-task-test - test_ota_agent_task.cpp - ../src/ota_agent_task.c -) -target_include_directories(ota-agent-task-test - PRIVATE - . - - ../../library_mocks/inc - - # ota_agent_task.c has no .h file, so we include 'src' not 'inc' - ../src -) -target_link_libraries(ota-agent-task-test - PRIVATE - coremqtt-agent-integration-mock - coremqtt-agent-mock - coremqtt-mock - fff - freertos-kernel-mock - freertos-ota-pal-psa-mock - helpers-events-mock - helpers-logging-mock - trusted-firmware-m-mock - ota-for-aws-iot-embedded-sdk-mock - ota-for-aws-iot-embedded-sdk-test-specific-mock -) -iot_reference_arm_corstone3xx_add_test(ota-agent-task-test) diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/CMakeLists.txt b/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/CMakeLists.txt deleted file mode 100644 index 9751fbc7..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2024 Arm Limited and/or its affiliates -# -# SPDX-License-Identifier: MIT - -# Add helpers for testing mqtt_agent_task.c. Helpers are application-specific. -# E.g. app_config.h is a helper since it varies by application. -add_library(ota-for-aws-iot-embedded-sdk-test-specific-mock - INTERFACE -) -target_include_directories(ota-for-aws-iot-embedded-sdk-test-specific-mock - INTERFACE - inc -) -target_link_libraries(ota-for-aws-iot-embedded-sdk-test-specific-mock - INTERFACE - fff -) diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/inc/app_config.h b/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/inc/app_config.h deleted file mode 100644 index 9324175d..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/inc/app_config.h +++ /dev/null @@ -1,14 +0,0 @@ -/* Copyright 2024 Arm Limited and/or its affiliates - * - * SPDX-License-Identifier: MIT - */ - -#ifndef APP_CONFIG_H -#define APP_CONFIG_H - -#define appCONFIG_MQTT_AGENT_TASK_STACK_SIZE 4096 -#define appCONFIG_MQTT_AGENT_TASK_PRIORITY 2 -#define appCONFIG_OTA_MQTT_AGENT_TASK_STACK_SIZE ( 4096 ) -#define appCONFIG_OTA_MQTT_AGENT_TASK_PRIORITY ( 1 ) - -#endif diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/inc/demo_config.h b/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/inc/demo_config.h deleted file mode 100644 index 86bb5cc6..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/inc/demo_config.h +++ /dev/null @@ -1,13 +0,0 @@ -/* Copyright 2024 Arm Limited and/or its affiliates - * - * SPDX-License-Identifier: MIT - */ - -#ifndef DEMO_CONFIG_H -#define DEMO_CONFIG_H - -#define democonfigMQTT_BROKER_ENDPOINT "dummy endpoint" -#define democonfigMQTT_BROKER_PORT 8883 -#define democonfigCLIENT_IDENTIFIER "dummy client identifier" - -#endif /* DEMO_CONFIG_H */ diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/inc/iot_default_root_certificates.h b/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/inc/iot_default_root_certificates.h deleted file mode 100644 index 8e0b0809..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/inc/iot_default_root_certificates.h +++ /dev/null @@ -1,12 +0,0 @@ -/* Copyright 2024 Arm Limited and/or its affiliates - * - * SPDX-License-Identifier: MIT - */ - -#ifndef __DEFAULT__ROOT__CERTIFICATES__H__ -#define __DEFAULT__ROOT__CERTIFICATES__H__ - -static const char tlsATS1_ROOT_CERTIFICATE_PEM[] = "dummy"; -static const uint32_t tlsATS1_ROOT_CERTIFICATE_LENGTH = sizeof( tlsATS1_ROOT_CERTIFICATE_PEM ); - -#endif /* ifndef __DEFAULT__ROOT__CERTIFICATES__H__ */ diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/inc/ota_config.h b/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/inc/ota_config.h deleted file mode 100644 index a8626810..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/config_mocks/inc/ota_config.h +++ /dev/null @@ -1,14 +0,0 @@ -/* Copyright 2024 Arm Limited and/or its affiliates - * - * SPDX-License-Identifier: MIT - */ - -#ifndef OTA_CONFIG_H_ -#define OTA_CONFIG_H_ - -#define otaconfigLOG2_FILE_BLOCK_SIZE 12UL - -#define otaconfigMAX_NUM_OTA_DATA_BUFFERS 10 - - -#endif /* OTA_CONFIG_H_ */ diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/test_ota_agent_task.cpp b/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/test_ota_agent_task.cpp deleted file mode 100644 index 88e0080a..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/integration/tests/test_ota_agent_task.cpp +++ /dev/null @@ -1,1247 +0,0 @@ -/* Copyright 2024 Arm Limited and/or its affiliates - * - * SPDX-License-Identifier: MIT - */ - -#include "fff.h" - -#include "gtest/gtest.h" - -#include - -using namespace std; -using namespace std::placeholders; - - -extern "C" { -/* Header files for types used in this test file */ -#include "application_version.h" -#include "core_mqtt_agent.h" -#include "core_mqtt_serializer.h" -#include "FreeRTOS.h" -#include "logging_stack.h" -#include "ota_mqtt_interface.h" -#include "ota_private.h" -#include "ota_config.h" -/* ota.h depends on ota_os_interface.h, which uses the reserved C++ keyword 'delete' */ -#define delete unreserved_delete -#include "ota.h" -#undef delete -#include "portmacro.h" -#include "semphr.h" -#include "subscription_manager.h" -#include "task.h" -#include "events.h" -/* File under test */ -/* Functions usually defined in main.c */ -DECLARE_FAKE_VOID_FUNC( vOtaActiveHook ); -DEFINE_FAKE_VOID_FUNC( vOtaActiveHook ); -DECLARE_FAKE_VOID_FUNC( vOtaNotActiveHook ); -DEFINE_FAKE_VOID_FUNC( vOtaNotActiveHook ); -/* Struct usually defined by the application. */ -struct MQTTAgentCommandContext -{ - MQTTStatus_t xReturnStatus; - TaskHandle_t xTaskToNotify; - void * pArgs; -}; -/* Static functions we are testing */ -extern void vStartOtaTask( void ); -extern OtaEventData_t * prvOTAEventBufferGet( void ); -extern void prvOTAEventBufferFree( OtaEventData_t * const ); -extern void prvOTAAgentTask( void * ); -extern void vOtaDemoTask( void * ); -extern BaseType_t prvRunOTADemo( void ); -extern BaseType_t prvResumeOTA( void ); -extern BaseType_t prvSuspendOTA( void ); -extern void setOtaInterfaces( OtaInterfaces_t * ); -extern OtaMqttStatus_t prvMQTTUnsubscribe( const char *, - uint16_t, - uint8_t ); -extern OtaMqttStatus_t prvMQTTPublish( const char * const, - uint16_t, - const char *, - uint32_t, - uint8_t ); -extern void prvOTAPublishCommandCallback( MQTTAgentCommandContext_t *, - MQTTAgentReturnInfo_t * ); -extern OtaMqttStatus_t prvMQTTSubscribe( const char *, - uint16_t, - uint8_t ); -extern void prvMQTTUnsubscribeCompleteCallback( MQTTAgentCommandContext_t *, - MQTTAgentReturnInfo_t * ); -extern void prvMQTTSubscribeCompleteCallback( MQTTAgentCommandContext_t *, - MQTTAgentReturnInfo_t * ); -extern void prvMqttJobCallback( void *, - MQTTPublishInfo_t * ); -extern void prvMqttDataCallback( void *, - MQTTPublishInfo_t * ); -extern void prvMqttDefaultCallback( void *, - MQTTPublishInfo_t * ); -extern void prvRegisterOTACallback( const char *, - uint16_t ); -extern void prvMqttDataCallback( void *, - MQTTPublishInfo_t * ); -extern void otaAppCallback( OtaJobEvent_t, - void * ); -extern OtaEventData_t * prvOTAEventBufferGet( void ); -extern void prvOTAEventBufferFree( OtaEventData_t * const ); -/* Mocks of .h files in the same submodule as ota_agent_task.c that are also used by ota_agent_task.c */ - -/* Functions usually defined by main.c */ -DEFINE_FAKE_VOID_FUNC( vAssertCalled, - const char *, - unsigned long ); -DEFINE_FAKE_VOID_FUNC_VARARG( SdkLogError, - const char *, - ... ); -DEFINE_FAKE_VOID_FUNC_VARARG( SdkLogWarn, - const char *, - ... ); -DEFINE_FAKE_VOID_FUNC_VARARG( SdkLogInfo, - const char *, - ... ); -DEFINE_FAKE_VOID_FUNC_VARARG( SdkLogDebug, - const char *, - ... ); -} - -DEFINE_FFF_GLOBALS - - -/* Being under this test class denotes a valid test that needs a corresponding fix, but we do not want clogging the testsuite. */ -class SkipTest : public ::testing::Test { -protected: - void SetUp() override - { - GTEST_SKIP() << "Skipping all tests under this suite"; - } -}; - -#define ASSERTION_FAILURE 1 -/* used to indicate a context switch should have happened, but cannot be mocked. */ -/* E.g. After Activating the OTA image we do not expect the otaAppCallback test code to continue running. */ -#define CONTEXT_SWITCH_FAKE 2 - - -/* Mock for vAssertCalled */ -void throw_assertion_failure( const char * pcFile, - unsigned long ulLine ) -{ - throw( ASSERTION_FAILURE ); /* stop tests running. */ -} - -/* Mock for SdkLogError */ -void fail_and_display_error( const char * msg, - va_list args ) -{ - char buf[ 1000 ]; - - vsnprintf( buf, 1000, msg, args ); - FAIL() << "Failed because of unexpected error message: '" << buf << "' \n"; -} -void do_nothing_if_an_error_occurs( const char * msg, - va_list args ) /* do nothing */ -{ -} - -/* Mock for xTaskNotifyWait */ -BaseType_t stop_the_rest_of_the_program_running( int unused1, - int unused2, - void * unused3, - TickType_t unused4 ) -{ - throw ( CONTEXT_SWITCH_FAKE ); - return pdPASS; -} - -/* Mocks for MQTT_MatchTopic */ -MQTTStatus_t set_match_true( const char * unused1, - const uint16_t unused2, - const char * unused3, - const uint16_t unused4, - bool * pIsMatch ) -{ - *pIsMatch = true; - return MQTTSuccess; -} -MQTTStatus_t set_match_false( const char * unused1, - const uint16_t unused2, - const char * unused3, - const uint16_t unused4, - bool * pIsMatch ) -{ - *pIsMatch = false; - return MQTTSuccess; -} - -/* We use a global variable because it is not possible to use C++ std::function alongside C function pointers. - * FFF uses C function pointers. - * So it not possible to use partial function application via std::bind to pass a parameter to a mock. */ -int global_counter = 0; -MQTTStatus_t match_while_global_counter_is_positive( const char * unused1, - const uint16_t unused2, - const char * unused3, - const uint16_t unused4, - bool * pIsMatch ) -{ - if( global_counter > 0 ) - { - *pIsMatch = true; - global_counter = global_counter - 1; - } - else - { - *pIsMatch = false; - } - - return MQTTSuccess; -} - -/* Mock for OTA_SignalEvent */ -bool expect_block_received_signal( const OtaEventMsg_t * const pEventMsg ) -{ - EXPECT_EQ( pEventMsg->eventId, OtaAgentEventReceivedFileBlock ); - return true; -} - -void expect_errors() -{ - EXPECT_NE( SdkLogError_fake.call_count, 0 ); -} - -/* Mock for MQTTAgent_Publish */ -MQTTStatus_t set_return_status_to_mqtt_success( const MQTTAgentContext_t * pMqttAgentContext, - MQTTPublishInfo_t * pPublishInfo, - const MQTTAgentCommandInfo_t * pCommandInfo ) -{ - MQTTAgentCommandContext_t * ctxt = ( MQTTAgentCommandContext_t * ) pCommandInfo->pCmdCompleteCallbackContext; - - ctxt->xReturnStatus = MQTTSuccess; - return MQTTSuccess; -} -MQTTStatus_t set_return_status_to_mqtt_bad_parameter( const MQTTAgentContext_t * pMqttAgentContext, - MQTTPublishInfo_t * pPublishInfo, - const MQTTAgentCommandInfo_t * pCommandInfo ) -{ - MQTTAgentCommandContext_t * ctxt = ( MQTTAgentCommandContext_t * ) pCommandInfo->pCmdCompleteCallbackContext; - - ctxt->xReturnStatus = MQTTBadParameter; /* the command returns failure */ - return MQTTSuccess; /* the publish itself succeeded. */ -} - -/* Mock for xTaskNotify */ -BaseType_t expect_task_to_notify_is_five( TaskHandle_t handle, - uint32_t unused1, - eNotifyAction unused2 ) -{ - EXPECT_EQ( *handle, 5 ); - return pdPASS; -} - -/* Mock for MQTTAgent_Subscribe and MQTTAgent_Unsubscribe */ -MQTTStatus_t fake_successful_subscription( const MQTTAgentContext_t * pMqttAgentContext, - MQTTAgentSubscribeArgs_t * pSubscriptionArgs, - const MQTTAgentCommandInfo_t * pCommandInfo ) -{ - MQTTAgentCommandContext_t * ctxt = ( MQTTAgentCommandContext_t * ) pCommandInfo->pCmdCompleteCallbackContext; - - ctxt->xReturnStatus = MQTTSuccess; - return MQTTSuccess; -} -MQTTStatus_t fake_bad_subscribe_parameter( const MQTTAgentContext_t * pMqttAgentContext, - MQTTAgentSubscribeArgs_t * pSubscriptionArgs, - const MQTTAgentCommandInfo_t * pCommandInfo ) -{ - MQTTAgentCommandContext_t * ctxt = ( MQTTAgentCommandContext_t * ) pCommandInfo->pCmdCompleteCallbackContext; - - ctxt->xReturnStatus = MQTTBadParameter; - return MQTTBadParameter; -} -MQTTStatus_t fake_subscribe_command_failure_only( const MQTTAgentContext_t * pMqttAgentContext, - MQTTAgentSubscribeArgs_t * pSubscriptionArgs, - const MQTTAgentCommandInfo_t * pCommandInfo ) -{ - MQTTAgentCommandContext_t * ctxt = ( MQTTAgentCommandContext_t * ) pCommandInfo->pCmdCompleteCallbackContext; - - ctxt->xReturnStatus = MQTTBadParameter; - return MQTTSuccess; -} - -/* Mock for MQTTAgent_Publish */ -MQTTStatus_t fake_successful_publish( const MQTTAgentContext_t * pMqttAgentContext, - MQTTPublishInfo_t * pPublishInfo, - const MQTTAgentCommandInfo_t * pCommandInfo ) -{ - MQTTAgentCommandContext_t * ctxt = ( MQTTAgentCommandContext_t * ) pCommandInfo->pCmdCompleteCallbackContext; - - ctxt->xReturnStatus = MQTTSuccess; - return MQTTSuccess; -} -MQTTStatus_t fake_bad_publish_parameter( const MQTTAgentContext_t * pMqttAgentContext, - MQTTPublishInfo_t * pPublishInfo, - const MQTTAgentCommandInfo_t * pCommandInfo ) -{ - MQTTAgentCommandContext_t * ctxt = ( MQTTAgentCommandContext_t * ) pCommandInfo->pCmdCompleteCallbackContext; - - ctxt->xReturnStatus = MQTTBadParameter; - return MQTTBadParameter; -} - -/* Mock for OTA_Suspend */ -OtaErr_t fake_successful_ota_suspend( void ) -{ - OTA_GetState_fake.return_val = OtaAgentStateSuspended; - return OtaErrNone; -} -OtaErr_t fake_unsuccessful_ota_suspend( void ) -{ - OTA_GetState_fake.return_val = OtaAgentStateWaitingForFileBlock; - return OtaErrUninitialized; -} -OtaErr_t fake_successful_ota_suspend_but_state_is_incorrect( void ) -{ - OTA_GetState_fake.return_val = OtaAgentStateShuttingDown; - return OtaErrNone; -} - -/* Mock for OTA_Resume */ -OtaErr_t fake_successful_ota_resume( void ) -{ - OTA_GetState_fake.return_val = OtaAgentStateWaitingForFileBlock; - return OtaErrNone; -} -OtaErr_t fake_unsuccessful_ota_resume( void ) -{ - OTA_GetState_fake.return_val = OtaAgentStateSuspended; - return OtaErrUninitialized; -} -OtaErr_t fake_successful_ota_resume_but_state_is_still_suspended( void ) -{ - OTA_GetState_fake.return_val = OtaAgentStateSuspended; - return OtaErrNone; -} - -class TestOtaAgentTask : public ::testing::Test { -public: - TestOtaAgentTask() - { - RESET_FAKE( addSubscription ); - RESET_FAKE( GetImageVersionPSA ); - RESET_FAKE( MQTTAgent_Subscribe ); - RESET_FAKE( MQTTAgent_Publish ); - RESET_FAKE( MQTTAgent_Unsubscribe ); - RESET_FAKE( MQTTAgent_Subscribe ); - RESET_FAKE( MQTT_MatchTopic ); - RESET_FAKE( OTA_ActivateNewImage ); - RESET_FAKE( OTA_EventProcessingTask ); - RESET_FAKE( OTA_GetState ); - RESET_FAKE( OTA_Init ); - RESET_FAKE( OTA_Resume ); - RESET_FAKE( OTA_SetImageState ); - RESET_FAKE( OTA_Shutdown ); - RESET_FAKE( OTA_SignalEvent ); - RESET_FAKE( OTA_Suspend ); - RESET_FAKE( removeSubscription ); - RESET_FAKE( SdkLogError ); - RESET_FAKE( SdkLogWarn ); - RESET_FAKE( SdkLogDebug ); - RESET_FAKE( vAssertCalled ); - RESET_FAKE( vOtaActiveHook ); - RESET_FAKE( vOtaNotActiveHook ); - RESET_FAKE( vTaskDelay ); - RESET_FAKE( vSemaphoreDelete ); - RESET_FAKE( vWaitUntilMQTTAgentConnected ); - RESET_FAKE( vWaitUntilMQTTAgentReady ); - RESET_FAKE( xIsMqttAgentConnected ); - RESET_FAKE( xSemaphoreCreateMutex ); - RESET_FAKE( xSemaphoreTake ); - RESET_FAKE( xSemaphoreGive ); - RESET_FAKE( xTaskGetCurrentTaskHandle ); - RESET_FAKE( xTaskNotify ) - RESET_FAKE( xTaskNotifyStateClear ); - RESET_FAKE( xTaskNotifyWait ); - RESET_FAKE( xTaskCreate ); - - /* Default values. - * Do not overwrite these here, instead use inheritance or - * overwrite the mock return values in individual tests. */ - addSubscription_fake.return_val = true; /* success. */ - MQTTAgent_Subscribe_fake.custom_fake = fake_successful_subscription; - MQTTAgent_Publish_fake.custom_fake = fake_successful_publish; - MQTTAgent_Unsubscribe_fake.return_val = MQTTSuccess; - MQTTAgent_Subscribe_fake.return_val = MQTTSuccess; - MQTT_MatchTopic_fake.return_val = MQTTSuccess; - OTA_ActivateNewImage_fake.return_val = OtaErrNone; - OTA_GetState_fake.return_val = OtaAgentStateReady; - OTA_Init_fake.return_val = OtaErrNone; - OTA_SetImageState_fake.return_val = OtaErrNone; - OTA_Shutdown_fake.return_val = OtaAgentStateShuttingDown; - OTA_SignalEvent_fake.return_val = true; - OTA_Suspend_fake.custom_fake = fake_successful_ota_suspend; - OTA_Resume_fake.custom_fake = fake_successful_ota_resume; - xIsMqttAgentConnected_fake.return_val = true; - xTaskGetCurrentTaskHandle_fake.return_val = nullptr; - xTaskNotify_fake.return_val = pdPASS; - xTaskNotifyStateClear_fake.return_val = pdTRUE; - xTaskNotifyWait_fake.return_val = pdPASS; - - /* - * These default semaphore values are relied on by nearly all tests. - * Do not change them here. - */ - xSemaphoreTake_fake.return_val = pdTRUE; - xSemaphoreGive_fake.return_val = pdTRUE; - xTaskCreate_fake.return_val = pdPASS; - - /* - * Wrap functions expected to fail an assertion in EXPECT_THROW from GoogleTest. - * Overwrite these in the test if you expect a test's inputs to cause an error. - * Note that overwriting a custom fake requires another custom fake, setting return_val - * is not sufficient. - */ - vAssertCalled_fake.custom_fake = throw_assertion_failure; - /* assume every unexpected error log is a problem. */ - SdkLogError_fake.custom_fake = fail_and_display_error; - } -}; - -/* Mock for OTA_SetImageState */ -OtaErr_t should_set_image_to_accepted_state( OtaImageState_t state ) -{ - /* Assume the image is valid. */ - EXPECT_EQ( state, OtaImageStateAccepted ); - return OtaErrNone; -} - -/* The file under test contains static functions which the tests in this file assume are made visible - * by conditional compiling macros. This test verifies these macros are defined. */ -TEST_F( TestOtaAgentTask, can_test_static_functions ) -{ - #ifndef UNIT_TESTING - FAIL() << "The macro UNIT_TESTING is not defined, please add this to your CMake compile definitions."; - #endif /* UNIT_TESTING */ -} - -/* Test prvOTAEventBufferFree */ -TEST_F( TestOtaAgentTask, freeing_ota_event_buffer_frees_buffer ) -{ - OtaEventData_t pxBuffer = { 0 }; - - pxBuffer.bufferUsed = true; - prvOTAEventBufferFree( &pxBuffer ); - EXPECT_FALSE( pxBuffer.bufferUsed ); -} - -/* Test prvOTAEventBufferGet */ -TEST_F( TestOtaAgentTask, buffer_size_is_sufficient_for_these_tests_to_run ) -{ - /* must have at least 2 buffers to for these tests to run. */ - EXPECT_GE( otaconfigMAX_NUM_OTA_DATA_BUFFERS, 2 ); -} - -TEST_F( TestOtaAgentTask, getting_a_single_buffer_does_not_return_nullpointer ) -{ - OtaEventData_t * buffer = prvOTAEventBufferGet(); - - EXPECT_NE( buffer, nullptr ); -} - -TEST_F( TestOtaAgentTask, the_agent_eventually_runs_out_of_data_buffers ) -{ - OtaEventData_t * buffer; - - for( int i = 0; i < otaconfigMAX_NUM_OTA_DATA_BUFFERS + 10; i++ ) - { - buffer = prvOTAEventBufferGet(); - } - - EXPECT_EQ( buffer, nullptr ); -} - -TEST_F( TestOtaAgentTask, tests_do_not_interfere_with_each_other ) -{ - OtaEventData_t * buffer = prvOTAEventBufferGet(); - - EXPECT_NE( buffer, nullptr ); -} - -TEST_F( TestOtaAgentTask, get_uses_all_available_buffers ) -{ - /* This test failing will mean some subsequent tests fail. */ - OtaEventData_t * buffer; - - for( int i = 0; i < otaconfigMAX_NUM_OTA_DATA_BUFFERS - 1; i++ ) - { - buffer = prvOTAEventBufferGet(); - } - - EXPECT_NE( buffer, nullptr ); - buffer = prvOTAEventBufferGet(); - EXPECT_NE( buffer, nullptr ); - buffer = prvOTAEventBufferGet(); - EXPECT_EQ( buffer, nullptr ); -} - -TEST_F( TestOtaAgentTask, getting_buffer_marks_it_as_in_use ) -{ - OtaEventData_t * buffer = prvOTAEventBufferGet(); - - EXPECT_TRUE( buffer->bufferUsed ); -} - -TEST_F( TestOtaAgentTask, freeing_a_buffer_marks_it_as_out_of_use ) -{ - OtaEventData_t * buffer = prvOTAEventBufferGet(); - - EXPECT_TRUE( buffer->bufferUsed ); - prvOTAEventBufferFree( buffer ); - EXPECT_FALSE( buffer->bufferUsed ); -} - -TEST_F( TestOtaAgentTask, buffers_are_reused ) -{ - /* Freeing then getting all buffers marks the initially freed buffer as in use */ - OtaEventData_t * buffer = prvOTAEventBufferGet(); - - EXPECT_TRUE( buffer->bufferUsed ); - prvOTAEventBufferFree( buffer ); - EXPECT_FALSE( buffer->bufferUsed ); - - for( int i = 0; i < otaconfigMAX_NUM_OTA_DATA_BUFFERS; i++ ) - { - OtaEventData_t * unused = prvOTAEventBufferGet(); - } - - EXPECT_TRUE( buffer->bufferUsed ); -} - -TEST_F( TestOtaAgentTask, freeing_a_buffer_causes_previously_failing_buffer_get_to_succeed ) -{ - OtaEventData_t * buffer_1; - - for( int i = 0; i < otaconfigMAX_NUM_OTA_DATA_BUFFERS; i++ ) - { - buffer_1 = prvOTAEventBufferGet(); - EXPECT_NE( buffer_1, nullptr ); - } - - OtaEventData_t * buffer_2 = prvOTAEventBufferGet(); - EXPECT_EQ( buffer_2, nullptr ); - /* Free the last buffer that was claimed. */ - prvOTAEventBufferFree( buffer_1 ); - /* Try to claim a buffer again. Expect to succeed this time. */ - buffer_2 = prvOTAEventBufferGet(); - EXPECT_NE( buffer_2, nullptr ) << "Expected to a buffer to become available after freeing"; -} - -/* Mock for OTA_ActivateImage */ -OtaErr_t stop_running_test_if_image_activated( void ) -{ - throw CONTEXT_SWITCH_FAKE; - return OtaErrNone; -} - -/* otaAppCallback */ -TEST_F( TestOtaAgentTask, agent_activates_image_if_image_is_authenticated_and_ready_to_activate ) -{ - OTA_ActivateNewImage_fake.custom_fake = stop_running_test_if_image_activated; - OTA_Shutdown_fake.return_val = OtaAgentStateShuttingDown; - OTA_SetImageState_fake.return_val = OtaErrNone; - OTA_GetState_fake.return_val = OtaAgentStateReady; - EXPECT_EQ( OTA_ActivateNewImage_fake.call_count, 0 ); - try { - otaAppCallback( OtaJobEventActivate, nullptr ); - } - catch( int num ) { - if( num != CONTEXT_SWITCH_FAKE ) - { - throw num; - } - } - EXPECT_NE( OTA_ActivateNewImage_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, agent_does_not_try_to_activate_image_if_update_rejected ) -{ - OTA_ActivateNewImage_fake.return_val = OtaErrNone; - OTA_Shutdown_fake.return_val = OtaAgentStateShuttingDown; - OTA_SetImageState_fake.return_val = OtaErrNone; - OTA_GetState_fake.return_val = OtaAgentStateReady; - EXPECT_EQ( OTA_ActivateNewImage_fake.call_count, 0 ); - otaAppCallback( OtaJobEventFail, nullptr ); - EXPECT_EQ( OTA_ActivateNewImage_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, agent_frees_event_buffer_once_event_is_processed ) -{ - OtaEventData_t * buffer = prvOTAEventBufferGet(); - - EXPECT_TRUE( buffer->bufferUsed ); - otaAppCallback( OtaJobEventProcessed, buffer ); - EXPECT_FALSE( buffer->bufferUsed ); -} -TEST_F( TestOtaAgentTask, agent_calls_not_active_hook_when_job_received ) -{ - EXPECT_EQ( vOtaActiveHook_fake.call_count, 0 ); - otaAppCallback( OtaJobEventReceivedJob, nullptr ); - EXPECT_NE( vOtaActiveHook_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, agent_calls_active_hook_when_no_job_is_received ) -{ - EXPECT_EQ( vOtaNotActiveHook_fake.call_count, 0 ); - otaAppCallback( OtaJobEventNoActiveJob, nullptr ); - EXPECT_NE( vOtaNotActiveHook_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, agent_tries_to_set_image_state_when_starting_test ) -{ - EXPECT_EQ( OTA_SetImageState_fake.call_count, 0 ); - otaAppCallback( OtaJobEventStartTest, nullptr ); - EXPECT_NE( OTA_SetImageState_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, agent_tries_to_set_image_state_as_accepted_when_starting_test ) -{ - /* Agent marks valid image as accepted */ - EXPECT_EQ( OTA_SetImageState_fake.call_count, 0 ); - otaAppCallback( OtaJobEventStartTest, nullptr ); - OTA_SetImageState_fake.custom_fake = should_set_image_to_accepted_state; -} -TEST_F( TestOtaAgentTask, agent_shuts_down_ota_with_error_message_if_self_test_is_failed ) -{ - /* 'Self test' refers to validating the image using signatures, and checking */ - /* that the version number has increased. */ - /* For more information, see AWS OTA documentation. */ - SdkLogError_fake.custom_fake = do_nothing_if_an_error_occurs; - EXPECT_EQ( SdkLogError_fake.call_count, 0 ); - EXPECT_EQ( OTA_Shutdown_fake.call_count, 0 ); - otaAppCallback( OtaJobEventSelfTestFailed, nullptr ); - EXPECT_NE( OTA_Shutdown_fake.call_count, 0 ); - EXPECT_NE( SdkLogError_fake.call_count, 0 ); -} - -/* prvMqttJobCallback */ -TEST_F( TestOtaAgentTask, job_callback_does_not_error_for_a_normal_call ) -{ - int payload = 15; - MQTTPublishInfo_t pubInfo = { MQTTQoS0, true, true, "topic name", 11, &payload, 1 }; - - prvMqttJobCallback( nullptr, &pubInfo ); -} -TEST_F( TestOtaAgentTask, job_callback_tries_to_send_event_for_valid_data ) -{ - /* Tries to send job document received event when job is done */ - EXPECT_EQ( OTA_SignalEvent_fake.call_count, 0 ); - int payload = 15; - MQTTPublishInfo_t pubInfo = { MQTTQoS0, true, true, "topic name", 11, &payload, 1 }; - prvMqttJobCallback( nullptr, &pubInfo ); - EXPECT_NE( OTA_SignalEvent_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, job_callback_does_not_try_to_send_event_for_too_large_a_mqtt_payload ) -{ - /* This needs to be sanitised because the user controls the payload. */ - /* This test tries to get the job callback to seg fault. E.g. by bad use of memcpy. */ - SdkLogError_fake.custom_fake = do_nothing_if_an_error_occurs; - try { - EXPECT_EQ( OTA_SignalEvent_fake.call_count, 0 ); - uint32_t * largePayload[ 1000 ]; - MQTTPublishInfo_t pubInfo = { MQTTQoS0, true, true, "topic name", 11, ( void * ) largePayload, 1000 }; - prvMqttJobCallback( nullptr, &pubInfo ); - EXPECT_EQ( OTA_SignalEvent_fake.call_count, 0 ); - } - catch( int num ) { - if( num == ASSERTION_FAILURE ) - { - EXPECT_EQ( OTA_SignalEvent_fake.call_count, 0 ); - } - else - { - throw ( num ); - } - } -} -TEST_F( TestOtaAgentTask, job_callback_does_not_send_event_signal_again_if_signal_was_received_correctly ) -{ - OTA_SignalEvent_fake.return_val = true; /* success */ - EXPECT_EQ( OTA_SignalEvent_fake.call_count, 0 ); - int payload = 15; - MQTTPublishInfo_t pubInfo = { MQTTQoS0, true, true, "topic name", 11, &payload, 1 }; - prvMqttJobCallback( nullptr, &pubInfo ); - EXPECT_EQ( OTA_SignalEvent_fake.call_count, 1 ); -} - -/* prvMqttDefaultCallback */ - -TEST_F( TestOtaAgentTask, mqtt_default_handler_redirects_publishes_to_handlers_based_on_topics ) -{ - int payload = 15; - MQTTPublishInfo_t pubInfo = { MQTTQoS0, true, true, "topic name", 11, &payload, 1 }; - - EXPECT_EQ( MQTT_MatchTopic_fake.call_count, 0 ); - prvMqttDefaultCallback( nullptr, &pubInfo ); - EXPECT_NE( MQTT_MatchTopic_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, mqtt_default_handler_handles_very_large_data_packet ) -{ - /* We are trying to break bad use of memcpy in this test. */ - SdkLogError_fake.custom_fake = do_nothing_if_an_error_occurs; - size_t tooLargeForBuffer = 1000; /* probably too big for the mqtt buffer. */ - int payload[ tooLargeForBuffer ]; - - for( int i = 0; i < tooLargeForBuffer; i++ ) - { - payload[ i ] = i; - } - - MQTTPublishInfo_t pubInfo = { MQTTQoS0, true, true, "topic name", 11, &payload, tooLargeForBuffer }; - MQTT_MatchTopic_fake.custom_fake = set_match_true; - prvMqttDefaultCallback( nullptr, &pubInfo ); - MQTT_MatchTopic_fake.custom_fake = set_match_false; - prvMqttDefaultCallback( nullptr, &pubInfo ); - /* Do not SegFault either way. */ -} - -/* prvMqttDataCallback */ - -TEST_F( TestOtaAgentTask, mqtt_data_handler_signals_if_successful ) -{ - EXPECT_EQ( OTA_SignalEvent_fake.call_count, 0 ); - int payload = 12; - MQTTPublishInfo_t pubInfo = { MQTTQoS0, true, true, "topic name", 11, &payload, 1 }; - prvMqttDataCallback( nullptr, &pubInfo ); - EXPECT_NE( OTA_SignalEvent_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, mqtt_data_handler_does_not_segfault_on_large_data_packets ) -{ - SdkLogError_fake.custom_fake = do_nothing_if_an_error_occurs; - size_t tooLargeForBuffer = 1000; /* probably too big for the mqtt buffer. */ - int payload[ tooLargeForBuffer ]; - - for( int i = 0; i < tooLargeForBuffer; i++ ) - { - payload[ i ] = i; - } - - MQTTPublishInfo_t pubInfo = { MQTTQoS0, true, true, "topic name", 11, &payload, tooLargeForBuffer }; - prvMqttDataCallback( nullptr, &pubInfo ); - - if( SdkLogError_fake.call_count > 0 ) - { - /* then expect no event signal to have been sent. */ - EXPECT_EQ( OTA_SignalEvent_fake.call_count, 0 ); - } -} -TEST_F( TestOtaAgentTask, mqtt_data_handler_signals_file_block_received_if_successful ) -{ - int payload = 12; - MQTTPublishInfo_t pubInfo = { MQTTQoS0, true, true, "topic name", 11, &payload, 1 }; - - prvMqttDataCallback( nullptr, &pubInfo ); - OTA_SignalEvent_fake.custom_fake = expect_block_received_signal; -} -TEST_F( TestOtaAgentTask, mqtt_data_handler_errors_if_out_of_ota_data_buffers ) -{ - for( int i = 0; i < otaconfigMAX_NUM_OTA_DATA_BUFFERS; i++ ) - { - OtaEventData_t * buffer = prvOTAEventBufferGet(); - EXPECT_NE( buffer, nullptr ); - } - - OtaEventData_t * buffer = prvOTAEventBufferGet(); - EXPECT_EQ( buffer, nullptr ); - /* Out of data buffers */ - int payload = 15; - MQTTPublishInfo_t pubInfo = { MQTTQoS0, true, true, "topic name", 11, &payload, 1 }; - SdkLogError_fake.custom_fake = do_nothing_if_an_error_occurs; - prvMqttDataCallback( nullptr, &pubInfo ); - expect_errors(); - EXPECT_EQ( OTA_SignalEvent_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, mqtt_data_handler_does_not_send_signal_if_out_of_ota_data_buffers ) -{ - SdkLogError_fake.custom_fake = do_nothing_if_an_error_occurs; /* being out of buffers will cause errors. */ - OtaEventData_t * buffer; - - for( int i = 0; i < otaconfigMAX_NUM_OTA_DATA_BUFFERS + 10; i++ ) - { - buffer = prvOTAEventBufferGet(); - } - - EXPECT_EQ( buffer, nullptr ); - int payload = 15; - MQTTPublishInfo_t pubInfo = { MQTTQoS0, true, true, "topic name", 11, &payload, 1 }; - prvMqttDataCallback( nullptr, &pubInfo ); - EXPECT_EQ( OTA_SignalEvent_fake.call_count, 0 ); -} - -/* prvRegisterOTACallback - * Is a callback for receiving messages intended for the OTA agent, from the broker. - */ - -TEST_F( TestOtaAgentTask, the_ota_callback_does_not_add_a_subscription_if_no_topics_match ) -{ - MQTT_MatchTopic_fake.custom_fake = set_match_false; - prvRegisterOTACallback( "dummy", 6 ); - EXPECT_EQ( addSubscription_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, the_ota_callback_adds_a_subscription_if_topics_match ) -{ - MQTT_MatchTopic_fake.custom_fake = set_match_true; - prvRegisterOTACallback( "dummy", 6 ); - EXPECT_NE( addSubscription_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, the_ota_callback_adds_a_subscription_per_matching_topic ) -{ - /* We also assume there are at least 2 matching topics for this test. */ - global_counter = 2; - int initial_counter_value = global_counter; - MQTT_MatchTopic_fake.custom_fake = match_while_global_counter_is_positive; - prvRegisterOTACallback( "dummy", 6 ); - EXPECT_EQ( addSubscription_fake.call_count, initial_counter_value ); -} - -/* prvMQTTSubscribeCompleteCallback */ - -TEST_F( TestOtaAgentTask, command_return_code_is_stored_in_mqtt_return_info ) -{ - MQTTAgentReturnInfo_t returnInfo; - MQTTSubscribeInfo_t info; - - info.pTopicFilter = "dummy"; - info.topicFilterLength = 6; - info.qos = MQTTQoS0; - MQTTAgentSubscribeArgs_t subscribeArgs; - subscribeArgs.pSubscribeInfo = &info; - MQTTAgentCommandContext_t cmndCtxt; - cmndCtxt.pArgs = &subscribeArgs; - returnInfo.returnCode = MQTTSuccess; - prvMQTTSubscribeCompleteCallback( &cmndCtxt, &returnInfo ); - EXPECT_EQ( cmndCtxt.xReturnStatus, returnInfo.returnCode ); - returnInfo.returnCode = MQTTBadParameter; - prvMQTTSubscribeCompleteCallback( &cmndCtxt, &returnInfo ); - EXPECT_EQ( cmndCtxt.xReturnStatus, returnInfo.returnCode ); -} -TEST_F( TestOtaAgentTask, if_there_is_a_task_to_notify_a_task_is_notified ) -{ - int task = 5; - MQTTAgentReturnInfo_t returnInfo; - MQTTSubscribeInfo_t info; - - info.pTopicFilter = "dummy"; - info.topicFilterLength = 6; - info.qos = MQTTQoS0; - MQTTAgentSubscribeArgs_t subscribeArgs; - subscribeArgs.pSubscribeInfo = &info; - MQTTAgentCommandContext_t cmndCtxt; - cmndCtxt.pArgs = &subscribeArgs; - cmndCtxt.xTaskToNotify = &task; - returnInfo.returnCode = MQTTSuccess; - EXPECT_EQ( xTaskNotify_fake.call_count, 0 ); - prvMQTTSubscribeCompleteCallback( &cmndCtxt, &returnInfo ); - EXPECT_NE( xTaskNotify_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, if_there_is_a_task_to_notify_the_correct_task_is_notified ) -{ - int task = 5; /* do not change this without modifying the fake for xTaskNotify */ - MQTTAgentReturnInfo_t returnInfo; - MQTTSubscribeInfo_t info; - - info.pTopicFilter = "dummy"; - info.topicFilterLength = 6; - info.qos = MQTTQoS0; - MQTTAgentSubscribeArgs_t subscribeArgs; - subscribeArgs.pSubscribeInfo = &info; - MQTTAgentCommandContext_t cmndCtxt; - cmndCtxt.pArgs = &subscribeArgs; - cmndCtxt.xTaskToNotify = &task; - returnInfo.returnCode = MQTTSuccess; - xTaskNotify_fake.custom_fake = expect_task_to_notify_is_five; - EXPECT_EQ( xTaskNotify_fake.call_count, 0 ); - prvMQTTSubscribeCompleteCallback( &cmndCtxt, &returnInfo ); - EXPECT_NE( xTaskNotify_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, if_there_is_no_task_then_task_notify_is_not_called ) -{ - MQTTAgentReturnInfo_t returnInfo; - MQTTSubscribeInfo_t info; - - info.pTopicFilter = "dummy"; - info.topicFilterLength = 6; - info.qos = MQTTQoS0; - MQTTAgentSubscribeArgs_t subscribeArgs; - subscribeArgs.pSubscribeInfo = &info; - MQTTAgentCommandContext_t cmndCtxt; - cmndCtxt.pArgs = &subscribeArgs; - cmndCtxt.xTaskToNotify = nullptr; - returnInfo.returnCode = MQTTSuccess; - prvMQTTSubscribeCompleteCallback( &cmndCtxt, &returnInfo ); - EXPECT_EQ( xTaskNotify_fake.call_count, 0 ); -} - -/* prvMQTTUnsubscribeCompleteCallback */ - -/* These tests duplicate code because it is poor practice to share variables between unit tests. */ -TEST_F( TestOtaAgentTask, command_return_code_is_stored_in_mqtt_return_info_2 ) -{ - MQTTAgentReturnInfo_t returnInfo; - MQTTSubscribeInfo_t info; - - info.pTopicFilter = "dummy"; - info.topicFilterLength = 6; - info.qos = MQTTQoS0; - MQTTAgentSubscribeArgs_t subscribeArgs; - subscribeArgs.pSubscribeInfo = &info; - MQTTAgentCommandContext_t cmndCtxt; - cmndCtxt.pArgs = &subscribeArgs; - returnInfo.returnCode = MQTTSuccess; - prvMQTTUnsubscribeCompleteCallback( &cmndCtxt, &returnInfo ); - EXPECT_EQ( cmndCtxt.xReturnStatus, returnInfo.returnCode ); - returnInfo.returnCode = MQTTBadParameter; - prvMQTTUnsubscribeCompleteCallback( &cmndCtxt, &returnInfo ); - EXPECT_EQ( cmndCtxt.xReturnStatus, returnInfo.returnCode ); -} -TEST_F( TestOtaAgentTask, if_there_is_a_task_to_notify_this_task_is_notified_2 ) -{ - int task = 5; - MQTTAgentReturnInfo_t returnInfo; - MQTTSubscribeInfo_t info; - - info.pTopicFilter = "dummy"; - info.topicFilterLength = 6; - info.qos = MQTTQoS0; - MQTTAgentSubscribeArgs_t subscribeArgs; - subscribeArgs.pSubscribeInfo = &info; - MQTTAgentCommandContext_t cmndCtxt; - cmndCtxt.pArgs = &subscribeArgs; - cmndCtxt.xTaskToNotify = &task; - returnInfo.returnCode = MQTTSuccess; - xTaskNotify_fake.custom_fake = expect_task_to_notify_is_five; - EXPECT_EQ( xTaskNotify_fake.call_count, 0 ); - prvMQTTUnsubscribeCompleteCallback( &cmndCtxt, &returnInfo ); - EXPECT_NE( xTaskNotify_fake.call_count, 0 ); -} - -/* prvMQTTSubscribe */ - -TEST_F( TestOtaAgentTask, mqtt_subscribe_subscribes_to_a_topic ) -{ - EXPECT_EQ( MQTTAgent_Subscribe_fake.call_count, 0 ); - prvMQTTSubscribe( "dummy", 6, 0 ); - EXPECT_NE( MQTTAgent_Subscribe_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, mqtt_subscribe_asks_for_the_current_task_to_be_notified ) -{ - int this_task = 5; - - xTaskGetCurrentTaskHandle_fake.return_val = &this_task; - xTaskNotify_fake.custom_fake = expect_task_to_notify_is_five; - EXPECT_EQ( xTaskGetCurrentTaskHandle_fake.call_count, 0 ); - prvMQTTSubscribe( "dummy", 6, 0 ); - EXPECT_NE( xTaskGetCurrentTaskHandle_fake.call_count, 0 ); -} -/* We do not test this function for null pointer inputs */ -/* because the topic filter not being null could be taken as a */ -/* pre-condition. */ -TEST_F( TestOtaAgentTask, mqtt_subscribe_returns_success_on_successful_subscription ) -{ - int this_task = 8; - - xTaskGetCurrentTaskHandle_fake.return_val = &this_task; - MQTTAgent_Subscribe_fake.custom_fake = fake_successful_subscription; - EXPECT_EQ( prvMQTTSubscribe( "dummy", 6, 0 ), OtaMqttSuccess ); -} -TEST_F( TestOtaAgentTask, mqtt_subscribe_returns_failure_on_unsuccessful_subscription ) -{ - SdkLogError_fake.custom_fake = do_nothing_if_an_error_occurs; - int this_task = 8; - xTaskGetCurrentTaskHandle_fake.return_val = &this_task; - MQTTAgent_Subscribe_fake.custom_fake = fake_bad_subscribe_parameter; - EXPECT_NE( prvMQTTSubscribe( "dummy", 6, 0 ), OtaMqttSuccess ); -} - -/* prvOTAPublishCommandCallback */ -TEST_F( TestOtaAgentTask, publish_callback_sets_return_code ) -{ - MQTTStatus_t expectedReturnStatus = MQTTSuccess; - MQTTAgentCommandContext_t cmndCtxt = { MQTTBadParameter }; - MQTTAgentReturnInfo_t retInfo = { expectedReturnStatus }; - - EXPECT_NE( cmndCtxt.xReturnStatus, expectedReturnStatus ); - prvOTAPublishCommandCallback( &cmndCtxt, &retInfo ); - EXPECT_EQ( cmndCtxt.xReturnStatus, expectedReturnStatus ); - /* Sasme functionality tested, different arguments. */ - expectedReturnStatus = MQTTRecvFailed; - retInfo = { expectedReturnStatus }; - prvOTAPublishCommandCallback( &cmndCtxt, &retInfo ); - EXPECT_EQ( cmndCtxt.xReturnStatus, expectedReturnStatus ); -} -TEST_F( TestOtaAgentTask, publish_callback_notifies_task_if_task_to_notify_is_set ) -{ - int task = 5; - - xTaskNotify_fake.return_val = pdPASS; - MQTTAgentCommandContext_t cmndCtxt = { MQTTBadParameter, &task }; - MQTTAgentReturnInfo_t retInfo = { MQTTSuccess }; - EXPECT_EQ( xTaskNotify_fake.call_count, 0 ); - prvOTAPublishCommandCallback( &cmndCtxt, &retInfo ); - EXPECT_NE( xTaskNotify_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, publish_callback_does_not_notify_task_if_task_to_notify_is_not_set ) -{ - MQTTAgentCommandContext_t cmndCtxt = { MQTTBadParameter, nullptr }; - MQTTAgentReturnInfo_t retInfo = { MQTTSuccess }; - - EXPECT_EQ( xTaskNotify_fake.call_count, 0 ); - prvOTAPublishCommandCallback( &cmndCtxt, &retInfo ); - EXPECT_EQ( xTaskNotify_fake.call_count, 0 ); -} - -/* prvMQTTPublish - * Handles sending a publish via MQTT - */ - -TEST_F( TestOtaAgentTask, publishing_calls_mqtt_publish_api ) -{ - EXPECT_EQ( MQTTAgent_Publish_fake.call_count, 0 ); - prvMQTTPublish( "dummy1", 7, "dummymsg", 9, MQTTQoS0 ); - EXPECT_NE( MQTTAgent_Publish_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, publishing_waits_on_mqtt_publish_to_finish_before_it_can_return_result_of_the_publish ) -{ - EXPECT_EQ( xTaskNotifyWait_fake.call_count, 0 ); - prvMQTTPublish( "dummy1", 7, "dummymsg", 9, MQTTQoS1 ); - EXPECT_NE( xTaskNotifyWait_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, publishing_returns_failure_if_mqtt_publish_fails ) -{ - SdkLogError_fake.custom_fake = do_nothing_if_an_error_occurs; - MQTTAgent_Publish_fake.custom_fake = fake_bad_publish_parameter; - EXPECT_EQ( prvMQTTPublish( "dummy1", 7, "dummymsg", 9, MQTTQoS1 ), OtaMqttPublishFailed ); -} -TEST_F( TestOtaAgentTask, publishing_returns_failure_if_waiting_on_the_publish_command_fails ) -{ - SdkLogError_fake.custom_fake = do_nothing_if_an_error_occurs; - /* i.e. xTaskNotifyWait returns failure, so notification has not happened. */ - xTaskNotifyWait_fake.return_val = pdFAIL; - EXPECT_EQ( prvMQTTPublish( "dummy1", 7, "dummymsg", 9, MQTTQoS1 ), OtaMqttPublishFailed ); -} -TEST_F( TestOtaAgentTask, publishing_returns_success_if_mqtt_publish_succeeds ) -{ - MQTTAgent_Publish_fake.custom_fake = set_return_status_to_mqtt_success; - EXPECT_EQ( prvMQTTPublish( "dummy1", 7, "dummymsg", 9, MQTTQoS1 ), OtaMqttSuccess ); -} -TEST_F( TestOtaAgentTask, publishing_returns_failure_if_scheduling_command_via_mqtt_publish_succeeds_but_the_command_fails ) -{ - SdkLogError_fake.custom_fake = do_nothing_if_an_error_occurs; - MQTTAgent_Publish_fake.custom_fake = set_return_status_to_mqtt_bad_parameter; - EXPECT_EQ( prvMQTTPublish( "dummy1", 7, "dummymsg", 9, MQTTQoS1 ), OtaMqttPublishFailed ); -} - -/* prvMQTTUnsubscribe */ - -TEST_F( TestOtaAgentTask, unsubscribe_calls_mqtt_unsubscribe ) -{ - EXPECT_EQ( MQTTAgent_Unsubscribe_fake.call_count, 0 ); - prvMQTTUnsubscribe( "dummy", 6, 0 ); - EXPECT_NE( MQTTAgent_Unsubscribe_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, unsubscribe_gets_current_task_handle ) -{ - /* presumably to notify the current task once a block arrives that matches a subscription. */ - EXPECT_EQ( xTaskGetCurrentTaskHandle_fake.call_count, 0 ); - prvMQTTUnsubscribe( "dummy", 6, 0 ); - EXPECT_NE( xTaskGetCurrentTaskHandle_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, unsubcribe_tries_to_wait_on_something ) -{ - /* This test is a pre-requisite for verifying that unsubscirbe waits on the MQTTAgent_Unsubscribe command */ - EXPECT_EQ( xTaskNotifyWait_fake.call_count, 0 ); - prvMQTTUnsubscribe( "dummy", 6, 0 ); - EXPECT_NE( xTaskNotifyWait_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, unsubcribe_waits_while_unsubscription_command_completes ) -{ - /* This behaviour is needed because otherwise we cannot return failure if the command fails. - * Nor can we cleanup resources allocated for the unsubscribe call. - * I.e. must call unsubscribe first, and then call wait */ - xTaskNotifyWait_fake.custom_fake = stop_the_rest_of_the_program_running; - EXPECT_EQ( xTaskNotifyWait_fake.call_count, 0 ); - try { - prvMQTTUnsubscribe( "dummy", 6, 0 ); - } - catch( int num ) { - if( num != CONTEXT_SWITCH_FAKE ) - { - throw ( num ); - } - } - /* I.e. Unsubscribe must be called before waiting. */ - EXPECT_NE( MQTTAgent_Unsubscribe_fake.call_count, 0 ); - EXPECT_NE( xTaskNotifyWait_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, unsubscribe_returns_failure_if_mqtt_unsubscribe_fails ) -{ - SdkLogError_fake.custom_fake = do_nothing_if_an_error_occurs; - MQTTAgent_Unsubscribe_fake.custom_fake = fake_bad_subscribe_parameter; - EXPECT_NE( prvMQTTUnsubscribe( "dummy", 6, 0 ), OtaMqttSuccess ); -} -TEST_F( TestOtaAgentTask, unsubscribe_returns_failure_if_mqtt_unsubscribe_succeeds_but_command_fails ) -{ - SdkLogError_fake.custom_fake = do_nothing_if_an_error_occurs; - MQTTAgent_Unsubscribe_fake.custom_fake = fake_subscribe_command_failure_only; - EXPECT_NE( prvMQTTUnsubscribe( "dummy", 6, 0 ), OtaMqttSuccess ); -} -TEST_F( TestOtaAgentTask, unsubscribe_returns_success_if_mqtt_unsubscribe_succeeds_and_command_succeeds ) -{ - MQTTAgent_Unsubscribe_fake.custom_fake = fake_successful_subscription; - EXPECT_EQ( prvMQTTUnsubscribe( "dummy", 6, 0 ), OtaMqttSuccess ); -} - -/* setOtaInterfaces */ -TEST_F( TestOtaAgentTask, setting_ota_interfaces_changes_the_passed_interface_values ) -{ - /* Just check a few fields */ - OtaInterfaces_t * interface = ( OtaInterfaces_t * ) calloc( 1, sizeof( OtaInterfaces_t ) ); - - EXPECT_NE( interface, nullptr ); - OtaFree_t original_free = interface->os.mem.free; - OtaMalloc_t original_malloc = interface->os.mem.malloc; - OtaMqttPublish_t original_publish = interface->mqtt.publish; - OtaPalWriteBlock_t original_writeBlock = interface->pal.writeBlock; - setOtaInterfaces( interface ); - EXPECT_NE( interface->os.mem.free, original_free ); - EXPECT_NE( interface->os.mem.malloc, original_malloc ); - EXPECT_NE( interface->mqtt.publish, original_publish ); - EXPECT_NE( interface->pal.writeBlock, original_writeBlock ); - free( interface ); -} - -/* prvOTAAgentTask */ -TEST_F( TestOtaAgentTask, ota_agent_task_gets_called ) -{ - EXPECT_EQ( OTA_EventProcessingTask_fake.call_count, 0 ); - prvOTAAgentTask( nullptr ); - EXPECT_NE( OTA_EventProcessingTask_fake.call_count, 0 ); -} - -/* prvSuspendOTA */ - -TEST_F( TestOtaAgentTask, suspending_ota_calls_ota_suspend ) -{ - OTA_Suspend_fake.custom_fake = fake_successful_ota_suspend; - EXPECT_EQ( OTA_Suspend_fake.call_count, 0 ); - prvSuspendOTA(); - EXPECT_NE( OTA_Suspend_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, suspend_returns_success_if_ota_suspend_call_does_not_error ) -{ - OTA_Suspend_fake.custom_fake = fake_successful_ota_suspend; - EXPECT_EQ( prvSuspendOTA(), pdPASS ); -} -TEST_F( TestOtaAgentTask, suspend_ota_returns_failure_if_ota_library_suspend_returns_failure ) -{ - SdkLogError_fake.custom_fake = do_nothing_if_an_error_occurs; - OTA_Suspend_fake.custom_fake = fake_unsuccessful_ota_suspend; - OTA_Suspend_fake.return_val = OtaErrUninitialized; - OTA_GetState_fake.return_val = OtaAgentStateShuttingDown; - EXPECT_EQ( prvSuspendOTA(), pdFAIL ); -} -TEST_F( TestOtaAgentTask, suspend_ota_returns_failure_if_ota_suspend_reports_success_but_the_current_ota_state_is_not_suspended ) -{ - SdkLogError_fake.custom_fake = do_nothing_if_an_error_occurs; - OTA_Suspend_fake.custom_fake = fake_successful_ota_suspend_but_state_is_incorrect; - EXPECT_EQ( prvSuspendOTA(), pdFAIL ); -} -/* We do not test what happens if ota suspend returns an unusual error code because */ -/* There are many possible correct behaviours. */ - -/* prvResumeOTA */ - -TEST_F( TestOtaAgentTask, resuming_ota_calls_ota_resume ) -{ - OTA_Resume_fake.return_val = OtaErrNone; - OTA_GetState_fake.return_val = OtaAgentStateWaitingForFileBlock; - EXPECT_EQ( OTA_Resume_fake.call_count, 0 ); - prvResumeOTA(); - EXPECT_NE( OTA_Resume_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, resume_returns_success_if_ota_resume_call_does_not_error ) -{ - OTA_Resume_fake.return_val = OtaErrNone; - OTA_GetState_fake.return_val = OtaAgentStateWaitingForFileBlock; - EXPECT_EQ( prvResumeOTA(), pdPASS ); -} -TEST_F( TestOtaAgentTask, resume_ota_returns_failure_if_resuming_fails ) -{ - SdkLogError_fake.custom_fake = do_nothing_if_an_error_occurs; - OTA_Resume_fake.custom_fake = fake_unsuccessful_ota_resume; - EXPECT_EQ( prvResumeOTA(), pdFAIL ); -} -TEST_F( TestOtaAgentTask, resume_ota_returns_failure_if_ota_resume_reports_success_but_the_current_ota_state_is_not_resumed ) -{ - SdkLogError_fake.custom_fake = do_nothing_if_an_error_occurs; - OTA_Resume_fake.custom_fake = fake_successful_ota_resume_but_state_is_still_suspended; - EXPECT_EQ( prvResumeOTA(), pdFAIL ); -} - -/* Test prvRunOTADemo */ - -TEST_F( TestOtaAgentTask, demo_waits_for_mqtt_agent ) -{ - OTA_GetState_fake.return_val = OtaAgentStateStopped; - EXPECT_EQ( vWaitUntilMQTTAgentReady_fake.call_count, 0 ); - EXPECT_EQ( vWaitUntilMQTTAgentConnected_fake.call_count, 0 ); - prvRunOTADemo(); - EXPECT_NE( vWaitUntilMQTTAgentReady_fake.call_count, 0 ); - EXPECT_NE( vWaitUntilMQTTAgentConnected_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, demo_tries_to_initialise_ota_library ) -{ - OTA_GetState_fake.return_val = OtaAgentStateStopped; - EXPECT_EQ( OTA_Init_fake.call_count, 0 ); - prvRunOTADemo(); - EXPECT_NE( OTA_Init_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, demo_returns_failure_if_cannot_initialise_ota_library ) -{ - SdkLogError_fake.custom_fake = do_nothing_if_an_error_occurs; - OTA_GetState_fake.return_val = OtaAgentStateStopped; - OTA_Init_fake.return_val = OtaErrUninitialized; - EXPECT_EQ( prvRunOTADemo(), pdFAIL ); -} -TEST_F( TestOtaAgentTask, demo_tries_to_create_ota_agent_task ) -{ - OTA_GetState_fake.return_val = OtaAgentStateStopped; - EXPECT_EQ( xTaskCreate_fake.call_count, 0 ); - prvRunOTADemo(); - EXPECT_NE( xTaskCreate_fake.call_count, 0 ); -} -TEST_F( TestOtaAgentTask, demo_removes_subscription_to_broker_once_finished ) -{ - OTA_GetState_fake.return_val = OtaAgentStateStopped; - EXPECT_EQ( removeSubscription_fake.call_count, 0 ); - prvRunOTADemo(); - EXPECT_NE( removeSubscription_fake.call_count, 0 ); -} - -/* - * We do not test this function further because it may use infinite loops, - * and testing these loops will cause the tests to be brittle. - */ - -/* vOtaDemoTask */ - -TEST_F( TestOtaAgentTask, demo_task_tries_to_read_image_version ) -{ - OTA_GetState_fake.return_val = OtaAgentStateStopped; - xSemaphoreCreateMutex_fake.return_val = 100; - GetImageVersionPSA_fake.return_val = 0; - EXPECT_EQ( GetImageVersionPSA_fake.call_count, 0 ); - vOtaDemoTask( nullptr ); - EXPECT_NE( GetImageVersionPSA_fake.call_count, 0 ); -} - -/* Test vStartOtaTask */ -TEST_F( TestOtaAgentTask, starting_ota_task_creates_task ) -{ - EXPECT_EQ( xTaskCreate_fake.call_count, 0 ); - vStartOtaTask(); - EXPECT_NE( xTaskCreate_fake.call_count, 0 ); -} diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library b/components/aws_iot/ota_for_aws_iot_embedded_sdk/library deleted file mode 160000 index f9760892..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f9760892ba152f2c9104d08192ea5ffbbf9fa8ea diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/CMakeLists.txt b/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/CMakeLists.txt deleted file mode 100644 index 8397fa17..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ - -add_library(ota-for-aws-iot-embedded-sdk-mock - src/ota.c -) - -target_include_directories(ota-for-aws-iot-embedded-sdk-mock - PUBLIC - inc - - portable/os -) - -target_link_libraries(ota-for-aws-iot-embedded-sdk-mock - PRIVATE - fff -) diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota.h b/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota.h deleted file mode 100644 index 03e0c4f0..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * AWS IoT Over-the-air Update v3.4.0 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * Copyright 2024 Arm Limited and/or its affiliates - * - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef OTA_H -#define OTA_H - -#include -#include "fff.h" -#include "ota_private.h" -#include "ota_os_interface.h" -#include "ota_mqtt_interface.h" -#include "ota_platform_interface.h" - -typedef struct OtaInterface -{ - OtaOSInterface_t os; - OtaMqttInterface_t mqtt; - OtaPalInterface_t pal; -} OtaInterfaces_t; - -typedef struct OtaAppBuffer -{ - uint8_t * pUpdateFilePath; - uint16_t updateFilePathsize; - uint8_t * pCertFilePath; - uint16_t certFilePathSize; - uint8_t * pStreamName; - uint16_t streamNameSize; - uint8_t * pDecodeMemory; - uint32_t decodeMemorySize; - uint8_t * pFileBitmap; - uint16_t fileBitmapSize; - uint8_t * pUrl; - uint16_t urlSize; - uint8_t * pAuthScheme; - uint16_t authSchemeSize; -} OtaAppBuffer_t; - -typedef enum OtaJobEvent -{ - OtaJobEventActivate = 0, - OtaJobEventFail = 1, - OtaJobEventStartTest = 2, - OtaJobEventProcessed = 3, - OtaJobEventSelfTestFailed = 4, - OtaJobEventParseCustomJob = 5, - OtaJobEventReceivedJob = 6, - OtaJobEventUpdateComplete = 7, - OtaJobEventNoActiveJob = 8, - OtaLastJobEvent = OtaJobEventStartTest -} OtaJobEvent_t; - -typedef enum OtaErr -{ - OtaErrNone = 0, - OtaErrUninitialized = 1 -} OtaErr_t; - -typedef enum OtaState -{ - OtaAgentStateNoTransition = 0, - OtaAgentStateInit = 1, - OtaAgentStateReady = 2, - OtaAgentStateRequestingJob = 3, - OtaAgentStateWaitingForJob = 4, - OtaAgentStateCreatingFile = 5, - OtaAgentStateRequestingFileBlock = 6, - OtaAgentStateWaitingForFileBlock = 7, - OtaAgentStateClosingFile = 8, - OtaAgentStateSuspended = 9, - OtaAgentStateShuttingDown = 10, - OtaAgentStateStopped = 11, - OtaAgentStateAll = 12 -} OtaState_t; - -typedef void (* OtaAppCallback_t)( OtaJobEvent_t eEvent, - void * pData ); - -DECLARE_FAKE_VALUE_FUNC( OtaErr_t, OTA_ActivateNewImage ); -DECLARE_FAKE_VALUE_FUNC( OtaState_t, OTA_GetState ); -DECLARE_FAKE_VALUE_FUNC( OtaState_t, - OTA_Shutdown, - uint32_t, - uint8_t ); -DECLARE_FAKE_VALUE_FUNC( OtaErr_t, - OTA_SetImageState, - OtaImageState_t ); -DECLARE_FAKE_VALUE_FUNC( bool, - OTA_SignalEvent, - const OtaEventMsg_t * const ); -DECLARE_FAKE_VALUE_FUNC( OtaErr_t, - OTA_Suspend ); -DECLARE_FAKE_VALUE_FUNC( OtaErr_t, - OTA_Resume ); -DECLARE_FAKE_VALUE_FUNC( OtaErr_t, - OTA_Init, - const OtaAppBuffer_t *, - const OtaInterfaces_t *, - const uint8_t *, - OtaAppCallback_t ); -DECLARE_FAKE_VALUE_FUNC( OtaErr_t, - OTA_GetStatistics, - OtaAgentStatistics_t * ); -DECLARE_FAKE_VOID_FUNC( OTA_EventProcessingTask, - const void * ); - -#endif /* OTA_H */ diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_appversion32.h b/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_appversion32.h deleted file mode 100644 index 501f9b38..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_appversion32.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * AWS IoT Over-the-air Update v3.4.0 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * Copyright 2024 Arm Limited and/or its affiliates - * - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef IOT_APPVERSION32_H -#define IOT_APPVERSION32_H - -#endif /* IOT_APPVERSION32_H */ diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_mqtt_interface.h b/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_mqtt_interface.h deleted file mode 100644 index 4afa2dbf..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_mqtt_interface.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * AWS IoT Over-the-air Update v3.4.0 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * Copyright 2024 Arm Limited and/or its affiliates - * - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef OTA_MQTT_INTERFACE_H -#define OTA_MQTT_INTERFACE_H - -typedef enum OtaMqttStatus -{ - OtaMqttSuccess = 0, - OtaMqttPublishFailed = 1, - OtaMqttSubscribeFailed = 2, - OtaMqttUnsubscribeFailed = 3 -} OtaMqttStatus_t; - -typedef OtaMqttStatus_t ( * OtaMqttSubscribe_t ) ( const char * pTopicFilter, - uint16_t topicFilterLength, - uint8_t ucQoS ); -typedef OtaMqttStatus_t ( * OtaMqttUnsubscribe_t ) ( const char * pTopicFilter, - uint16_t topicFilterLength, - uint8_t ucQoS ); -typedef OtaMqttStatus_t ( * OtaMqttPublish_t )( const char * const pacTopic, - uint16_t usTopicLen, - const char * pcMsg, - uint32_t ulMsgSize, - uint8_t ucQoS ); - -typedef struct OtaMqttInterface -{ - OtaMqttSubscribe_t subscribe; /*!< @brief Interface for subscribing to Mqtt topics. */ - OtaMqttUnsubscribe_t unsubscribe; /*!< @brief interface for unsubscribing to MQTT topics. */ - OtaMqttPublish_t publish; /*!< @brief Interface for publishing MQTT messages. */ -} OtaMqttInterface_t; - -#endif /* OTA_MQTT_INTERFACE_H */ diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_os_interface.h b/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_os_interface.h deleted file mode 100644 index 6e603acc..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_os_interface.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * AWS IoT Over-the-air Update v3.4.0 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * Copyright 2024 Arm Limited and/or its affiliates - * - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef OTA_OS_INTERFACE_H -#define OTA_OS_INTERFACE_H - -/* *INDENT-OFF* */ -#ifdef __cplusplus - extern "C" { -#endif -/* *INDENT-ON* */ - -/* Dummy enums added for mocking. Would normally be functions. */ -typedef enum -{ - OtaInitEvent_FreeRTOS = 1, -} OtaInitEvent_t; -typedef enum -{ - OtaSendEvent_FreeRTOS = 1, -} OtaSendEvent_t; -typedef enum -{ - OtaReceiveEvent_FreeRTOS = 1, -} OtaReceiveEvent_t; -typedef enum -{ - OtaDeinitEvent_FreeRTOS = 1, -} OtaDeinitEvent_t; - -typedef enum -{ - OtaStartTimer_FreeRTOS = 1, -} OtaStartTimer_t; -typedef enum -{ - OtaStopTimer_FreeRTOS = 1, -} OtaStopTimer_t; -typedef enum -{ - OtaDeleteTimer_FreeRTOS = 1, -} OtaDeleteTimer_t; - -typedef enum -{ - Malloc_FreeRTOS = 1, -} OtaMalloc_t; -typedef enum -{ - Free_FreeRTOS = 1, -} OtaFree_t; - -typedef struct OtaEventInterface -{ - OtaInitEvent_t init; - OtaSendEvent_t send; - OtaReceiveEvent_t recv; - OtaDeinitEvent_t deinit; -} OtaEventInterface_t; - -typedef struct OtaTimerInterface -{ - OtaStartTimer_t start; - OtaStopTimer_t stop; - OtaDeleteTimer_t delete; -} OtaTimerInterface_t; - -typedef struct OtaMallocInterface -{ - OtaMalloc_t malloc; - OtaFree_t free; -} OtaMallocInterface_t; - - -typedef struct OtaOSInterface -{ - OtaEventInterface_t event; - OtaTimerInterface_t timer; - OtaMallocInterface_t mem; -} OtaOSInterface_t; - - -/* *INDENT-OFF* */ -#ifdef __cplusplus - } -#endif -/* *INDENT-ON* */ - -#endif /* ifndef OTA_OS_INTERFACE_H */ diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_platform_interface.h b/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_platform_interface.h deleted file mode 100644 index 9e931170..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_platform_interface.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * AWS IoT Over-the-air Update v3.4.0 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * Copyright 2024 Arm Limited and/or its affiliates - * - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef OTA_PLATFORM_INTERFACE -#define OTA_PLATFORM_INTERFACE - -typedef enum -{ - otaPal_Abort = 1, -} OtaPalAbort_t; -typedef enum -{ - otaPal_CreateFileForRx = 1, -} OtaPalCreateFileForRx_t; -typedef enum -{ - otaPal_ResetDevice = 1, -} OtaPalResetDevice_t; -typedef enum -{ - otaPal_CloseFile = 1, -} OtaPalCloseFile_t; -typedef enum -{ - otaPal_ActivateNewImage = 1, -} OtaPalActivateNewImage_t; -typedef enum -{ - otaPal_WriteBlock = 1, -} OtaPalWriteBlock_t; -typedef enum -{ - otaPal_SetPlatformImageState = 1, -} OtaPalSetPlatformImageState_t; -typedef enum -{ - otaPal_GetPlatformImageState = 1, -} OtaPalGetPlatformImageState_t; - -typedef struct OtaPalInterface -{ - OtaPalAbort_t abort; - OtaPalCreateFileForRx_t createFile; - OtaPalCloseFile_t closeFile; - OtaPalWriteBlock_t writeBlock; - OtaPalActivateNewImage_t activate; - OtaPalResetDevice_t reset; - OtaPalSetPlatformImageState_t setPlatformImageState; - OtaPalGetPlatformImageState_t getPlatformImageState; -} OtaPalInterface_t; - -#endif /* OTA_PLATFORM_INTERFACE */ diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_private.h b/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_private.h deleted file mode 100644 index 2b4a919d..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/inc/ota_private.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * AWS IoT Over-the-air Update v3.4.0 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * Copyright 2024 Arm Limited and/or its affiliates - * - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef OTA_PRIVATE_H -#define OTA_PRIVATE_H - -#include -#include - -#define OTA_MAX_BLOCK_BITMAP_SIZE 128U - -typedef struct OtaAgentStatistics -{ - uint32_t otaPacketsReceived; - uint32_t otaPacketsQueued; - uint32_t otaPacketsProcessed; - uint32_t otaPacketsDropped; -} OtaAgentStatistics_t; - -typedef enum OtaImageState -{ - OtaImageStateUnknown = 0, - OtaImageStateTesting = 1, - OtaImageStateAccepted = 2, - OtaImageStateRejected = 3, - OtaImageStateAborted = 4, - OtaLastImageState = OtaImageStateAborted -} OtaImageState_t; - -typedef enum OtaEvent -{ - OtaAgentEventStart = 0, - OtaAgentEventReceivedJobDocument, - OtaAgentEventReceivedFileBlock -} OtaEvent_t; - -typedef struct OtaEventData -{ - uint8_t data[ 10 ]; - uint32_t dataLength; - bool bufferUsed; -} OtaEventData_t; - -typedef struct OtaEventMsg -{ - OtaEventData_t * pEventData; - OtaEvent_t eventId; -} OtaEventMsg_t; - -#endif /* OTA_PRIVATE_H */ diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/portable/os/ota_os_freertos.h b/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/portable/os/ota_os_freertos.h deleted file mode 100644 index 22e6b2a4..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/portable/os/ota_os_freertos.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * AWS IoT Over-the-air Update v3.4.0 - * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * Copyright 2024 Arm Limited and/or its affiliates - * - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef _OTA_OS_FREERTOS_H_ -#define _OTA_OS_FREERTOS_H_ - -#endif /* OTA_OS_FREERTOS_H_ */ diff --git a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/src/ota.c b/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/src/ota.c deleted file mode 100644 index 56ecd1cd..00000000 --- a/components/aws_iot/ota_for_aws_iot_embedded_sdk/library_mocks/src/ota.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * AWS IoT Over-the-air Update v3.4.0 - * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * Copyright 2024 Arm Limited and/or its affiliates - * - * - * SPDX-License-Identifier: MIT - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "ota.h" - -DEFINE_FAKE_VALUE_FUNC( OtaErr_t, OTA_ActivateNewImage ); -DEFINE_FAKE_VALUE_FUNC( OtaState_t, OTA_GetState ); -DEFINE_FAKE_VALUE_FUNC( OtaState_t, - OTA_Shutdown, - uint32_t, - uint8_t ); -DEFINE_FAKE_VALUE_FUNC( OtaErr_t, - OTA_SetImageState, - OtaImageState_t ); -DEFINE_FAKE_VALUE_FUNC( bool, - OTA_SignalEvent, - const OtaEventMsg_t * const ); -DEFINE_FAKE_VALUE_FUNC( OtaErr_t, - OTA_Suspend ); -DEFINE_FAKE_VALUE_FUNC( OtaErr_t, - OTA_Resume ); -DEFINE_FAKE_VALUE_FUNC( OtaErr_t, - OTA_Init, - const OtaAppBuffer_t *, - const OtaInterfaces_t *, - const uint8_t *, - OtaAppCallback_t ); -DEFINE_FAKE_VALUE_FUNC( OtaErr_t, - OTA_GetStatistics, - OtaAgentStatistics_t * ); -DEFINE_FAKE_VOID_FUNC( OTA_EventProcessingTask, - const void * ); diff --git a/components/security/freertos_ota_pal_psa/integration/CMakeLists.txt b/components/security/freertos_ota_pal_psa/integration/CMakeLists.txt index 375f9a33..f9984087 100644 --- a/components/security/freertos_ota_pal_psa/integration/CMakeLists.txt +++ b/components/security/freertos_ota_pal_psa/integration/CMakeLists.txt @@ -34,5 +34,4 @@ target_link_libraries(freertos-ota-pal-psa freertos_kernel helpers-logging mbedtls - ota-for-aws-iot-embedded-sdk ) diff --git a/manifest.yml b/manifest.yml index a33ee930..f534c660 100644 --- a/manifest.yml +++ b/manifest.yml @@ -129,15 +129,6 @@ dependencies: type: "git" url: "https://github.com/FreeRTOS/backoffAlgorithm.git" path: "components/connectivity/backoff_algorithm/library" - - name: "ota-for-aws-iot-embedded-sdk" - license: "MIT" - tpip-category: "category-2" - security-risk: "low" - version: "v3.4.0" - repository: - type: "git" - url: "https://github.com/aws/ota-for-aws-iot-embedded-sdk.git" - path: "components/aws_iot/ota_for_aws_iot_embedded_sdk/library" - name: "tinycbor" license: "MIT" tpip-category: "category-2" From f112937d93c27213e696c2c7f5aa69088a3143e5 Mon Sep 17 00:00:00 2001 From: Chuyue Luo Date: Tue, 26 Nov 2024 16:28:53 +0000 Subject: [PATCH 02/12] applications: Add C Runtime Helpers Add a crt-helpers/ directory within applicatons/helpers. Within this directory, add a custom implementation of `strnlen` (based on TF-M's `tfm_strnlen` implementation). This is required because the Arm Compiler for Embedded (v6.21) does not support `strnlen`. Signed-off-by: Chuyue Luo --- applications/helpers/CMakeLists.txt | 1 + .../helpers/crt_helpers/CMakeLists.txt | 18 ++++++++++ .../helpers/crt_helpers/inc/app_strnlen.h | 25 ++++++++++++++ .../helpers/crt_helpers/src/app_strnlen.c | 34 +++++++++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 applications/helpers/crt_helpers/CMakeLists.txt create mode 100644 applications/helpers/crt_helpers/inc/app_strnlen.h create mode 100644 applications/helpers/crt_helpers/src/app_strnlen.c diff --git a/applications/helpers/CMakeLists.txt b/applications/helpers/CMakeLists.txt index 7d12cd8b..633d74ac 100644 --- a/applications/helpers/CMakeLists.txt +++ b/applications/helpers/CMakeLists.txt @@ -2,6 +2,7 @@ # # SPDX-License-Identifier: MIT +add_subdirectory(crt_helpers) add_subdirectory(device_advisor) add_subdirectory(events) add_subdirectory(hdlcd) diff --git a/applications/helpers/crt_helpers/CMakeLists.txt b/applications/helpers/crt_helpers/CMakeLists.txt new file mode 100644 index 00000000..5a9ce58c --- /dev/null +++ b/applications/helpers/crt_helpers/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright 2024 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +if(BUILD_TESTING AND NOT CMAKE_CROSSCOMPILING) + # Left empty for future mocks. +else() + add_library(crt-helpers) + + target_include_directories(crt-helpers + PUBLIC + inc/) + + target_sources(crt-helpers + PUBLIC + src/app_strnlen.c + ) +endif() diff --git a/applications/helpers/crt_helpers/inc/app_strnlen.h b/applications/helpers/crt_helpers/inc/app_strnlen.h new file mode 100644 index 00000000..c6f852e5 --- /dev/null +++ b/applications/helpers/crt_helpers/inc/app_strnlen.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#ifndef APP_STRNLEN_H + +#include + +/** + * @brief Determine the length of a fixed-size string, excluding the terminating + * null byte ('\0'), and at most `maxlen`. + * + * @param[in] s The string to determine the length of. + * @param[in] maxlen The maximum number of characters of the string `s` that + * should be checked. + * + * @return The length of the string, up to `maxlen`. + */ +size_t app_strnlen( const char * s, + size_t maxlen ); + +#endif diff --git a/applications/helpers/crt_helpers/src/app_strnlen.c b/applications/helpers/crt_helpers/src/app_strnlen.c new file mode 100644 index 00000000..ace4e773 --- /dev/null +++ b/applications/helpers/crt_helpers/src/app_strnlen.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021-2024, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +/* + * This file is based on + * https://git.trustedfirmware.org/plugins/gitiles/TF-M/trusted-firmware-m.git/+/c9352b59f2a501b5af3f648b3fc91065993c002f/secure_fw/partitions/lib/runtime/crt_strnlen.c + */ + +#include + +size_t app_strnlen( const char * s, + size_t maxlen ) +{ + size_t idx; + + if( s == NULL ) + { + return 0; + } + + for( idx = 0; idx < maxlen; idx++ ) + { + if( s[ idx ] == '\0' ) + { + return idx; + } + } + + return idx; +} From 802def9f7c17199ce5288214906e27e8e0622f2e Mon Sep 17 00:00:00 2001 From: Chuyue Luo Date: Fri, 25 Oct 2024 14:39:33 +0000 Subject: [PATCH 03/12] components: Add Jobs-for-AWS-IoT-embedded-sdk component Add the Jobs-for-AWS-IoT-embedded-sdk repository as a submodule. This library is used to interact with AWS IoT Jobs (remote operations that are sent to and executed on devices connected to AWS IoT). It is one of the two libraries that must be integrated to allow the new modular OTA to be used - second library (aws-iot-core-mqtt-file-streams-embedded-c) is integrated in a later commit. In addition, the required integration CMake files to build the component are added. Signed-off-by: Chuyue Luo --- .gitmodules | 3 +++ components/aws_iot/CMakeLists.txt | 1 + .../CMakeLists.txt | 15 +++++++++++ .../integration/CMakeLists.txt | 25 +++++++++++++++++++ .../jobs_for_aws_iot_embedded_sdk/library | 1 + manifest.yml | 9 +++++++ 6 files changed, 54 insertions(+) create mode 100644 components/aws_iot/jobs_for_aws_iot_embedded_sdk/CMakeLists.txt create mode 100644 components/aws_iot/jobs_for_aws_iot_embedded_sdk/integration/CMakeLists.txt create mode 160000 components/aws_iot/jobs_for_aws_iot_embedded_sdk/library diff --git a/.gitmodules b/.gitmodules index 9ced2395..ca6739d1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -73,3 +73,6 @@ [submodule "googletest"] path = components/tools/googletest/library url = https://github.com/google/googletest.git +[submodule "components/aws_iot/jobs_for_aws_iot_embedded_sdk/library"] + path = components/aws_iot/jobs_for_aws_iot_embedded_sdk/library + url = https://github.com/aws/Jobs-for-AWS-IoT-embedded-sdk.git diff --git a/components/aws_iot/CMakeLists.txt b/components/aws_iot/CMakeLists.txt index ad90bd61..8c1cdd8f 100644 --- a/components/aws_iot/CMakeLists.txt +++ b/components/aws_iot/CMakeLists.txt @@ -12,4 +12,5 @@ add_subdirectory(corepkcs11) if(CONNECTIVITY_STACK STREQUAL "FREERTOS_PLUS_TCP") add_subdirectory(coresntp) endif() +add_subdirectory(jobs_for_aws_iot_embedded_sdk) add_subdirectory(tinycbor) diff --git a/components/aws_iot/jobs_for_aws_iot_embedded_sdk/CMakeLists.txt b/components/aws_iot/jobs_for_aws_iot_embedded_sdk/CMakeLists.txt new file mode 100644 index 00000000..fdeb8641 --- /dev/null +++ b/components/aws_iot/jobs_for_aws_iot_embedded_sdk/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright 2024 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +if(BUILD_TESTING AND NOT CMAKE_CROSSCOMPILING) + # Left empty for future mocks. +else() + set(jobs-for-aws-iot-embedded-sdk_SOURCE_DIR + ${CMAKE_CURRENT_LIST_DIR}/library + CACHE INTERNAL + "Path to AWS IoT Jobs source code" + ) + + add_subdirectory(integration) +endif() diff --git a/components/aws_iot/jobs_for_aws_iot_embedded_sdk/integration/CMakeLists.txt b/components/aws_iot/jobs_for_aws_iot_embedded_sdk/integration/CMakeLists.txt new file mode 100644 index 00000000..1230940b --- /dev/null +++ b/components/aws_iot/jobs_for_aws_iot_embedded_sdk/integration/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright 2024 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +add_library(jobs-for-aws-iot-embedded-sdk + ${jobs-for-aws-iot-embedded-sdk_SOURCE_DIR}/source/otaJobParser/job_parser.c + ${jobs-for-aws-iot-embedded-sdk_SOURCE_DIR}/source/otaJobParser/ota_job_handler.c + ${jobs-for-aws-iot-embedded-sdk_SOURCE_DIR}/source/jobs.c +) + +target_include_directories(jobs-for-aws-iot-embedded-sdk + PUBLIC + ${jobs-for-aws-iot-embedded-sdk_SOURCE_DIR}/source/include/ + ${jobs-for-aws-iot-embedded-sdk_SOURCE_DIR}/source/otaJobParser/include/ +) + +target_link_libraries(jobs-for-aws-iot-embedded-sdk + PUBLIC + freertos_kernel + corejson + coremqtt + coremqtt-agent + crt-helpers + tinycbor +) diff --git a/components/aws_iot/jobs_for_aws_iot_embedded_sdk/library b/components/aws_iot/jobs_for_aws_iot_embedded_sdk/library new file mode 160000 index 00000000..3ce91f56 --- /dev/null +++ b/components/aws_iot/jobs_for_aws_iot_embedded_sdk/library @@ -0,0 +1 @@ +Subproject commit 3ce91f56427653705891a8668568cb247b97905f diff --git a/manifest.yml b/manifest.yml index f534c660..20f5fae9 100644 --- a/manifest.yml +++ b/manifest.yml @@ -228,3 +228,12 @@ dependencies: type: "git" url: "https://github.com/google/googletest.git" path: "components/tools/googletest/library" + - name: "Jobs-for-AWS-IoT-embedded-sdk" + license: "MIT" + tpip-category: "category-2" + security-risk: "low" + version: "v1.5.1" + repository: + type: "git" + url: "https://github.com/aws/Jobs-for-AWS-IoT-embedded-sdk.git" + path: "components/aws_iot/jobs_for_aws_iot_embedded_sdk/library" From 701f1a557a816ed413b2bede58dcae1dc964311b Mon Sep 17 00:00:00 2001 From: Chuyue Luo Date: Wed, 30 Oct 2024 10:50:00 +0000 Subject: [PATCH 04/12] components: Add patches for Jobs-for-AWS-IoT-embedded-sdk library Two patches are added for the Jobs-for-AWS-IoT-embedded-sdk library: - The Jobs library assumes the OTA job is signed using ECDSA. However, we currently use RSA. Therefore, add a patch to change the check for an ECDSA signature to a check for an RSA signature. - The Jobs library contains calls to the `strnlen` function. However, this function is not supported by the Arm Compiler for Embedded (v6.21). Therefore, add a patch which replaces these calls with calls to our custom implementation `app_strnlen`. Signed-off-by: Chuyue Luo --- .../CMakeLists.txt | 9 +++ ...k-for-RSA-signature-instead-of-ECDSA.patch | 42 ++++++++++++++ ...02-Use-custom-strnlen-implementation.patch | 56 +++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 components/aws_iot/jobs_for_aws_iot_embedded_sdk/integration/patches/0001-Check-for-RSA-signature-instead-of-ECDSA.patch create mode 100644 components/aws_iot/jobs_for_aws_iot_embedded_sdk/integration/patches/0002-Use-custom-strnlen-implementation.patch diff --git a/components/aws_iot/jobs_for_aws_iot_embedded_sdk/CMakeLists.txt b/components/aws_iot/jobs_for_aws_iot_embedded_sdk/CMakeLists.txt index fdeb8641..250839f4 100644 --- a/components/aws_iot/jobs_for_aws_iot_embedded_sdk/CMakeLists.txt +++ b/components/aws_iot/jobs_for_aws_iot_embedded_sdk/CMakeLists.txt @@ -11,5 +11,14 @@ else() "Path to AWS IoT Jobs source code" ) + include(ApplyPatches) + + set(PATCH_FILES_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/integration/patches") + set(PATCH_FILES + "${PATCH_FILES_DIRECTORY}/0001-Check-for-RSA-signature-instead-of-ECDSA.patch" + "${PATCH_FILES_DIRECTORY}/0002-Use-custom-strnlen-implementation.patch" + ) + iot_reference_arm_corstone3xx_apply_patches("${jobs-for-aws-iot-embedded-sdk_SOURCE_DIR}" "${PATCH_FILES}") + add_subdirectory(integration) endif() diff --git a/components/aws_iot/jobs_for_aws_iot_embedded_sdk/integration/patches/0001-Check-for-RSA-signature-instead-of-ECDSA.patch b/components/aws_iot/jobs_for_aws_iot_embedded_sdk/integration/patches/0001-Check-for-RSA-signature-instead-of-ECDSA.patch new file mode 100644 index 00000000..ead71932 --- /dev/null +++ b/components/aws_iot/jobs_for_aws_iot_embedded_sdk/integration/patches/0001-Check-for-RSA-signature-instead-of-ECDSA.patch @@ -0,0 +1,42 @@ +From 6ea98c733a2ea3c9d8cfdf4d3b689598ffdf8d54 Mon Sep 17 00:00:00 2001 +From: Chuyue Luo +Date: Wed, 4 Dec 2024 15:20:34 +0000 +Subject: [PATCH 1/2] Check for RSA signature instead of ECDSA + +The Jobs-for-AWS-IoT-embedded-sdk library assumes the OTA job is signed +using ECDSA, but we currently use RSA. Thus, change the check for an +ECDSA signature to a check for an RSA signature. + +Signed-off-by: Chuyue Luo +--- + source/otaJobParser/job_parser.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/source/otaJobParser/job_parser.c b/source/otaJobParser/job_parser.c +index 4a1da8a..8638dc0 100644 +--- a/source/otaJobParser/job_parser.c ++++ b/source/otaJobParser/job_parser.c +@@ -1,6 +1,9 @@ + /* + * AWS IoT Jobs v1.5.1 + * Copyright (C) 2023 Amazon.com, Inc. and its affiliates. All Rights Reserved. ++ * Copyright 2024 Arm Limited and/or its affiliates ++ * ++ * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License. See the LICENSE accompanying this file +@@ -263,8 +266,8 @@ static JSONStatus_t populateCommonFields( const char * jobDoc, + if( jsonResult == JSONSuccess ) + { + buildIndexedFileQueryString( fileIndex, +- "sig-sha256-ecdsa", +- 16U, ++ "sig-sha256-rsa", ++ 14U, + queryString, + &queryStringLength ); + jsonResult = JSON_SearchConst( jobDoc, +-- +2.47.0 + diff --git a/components/aws_iot/jobs_for_aws_iot_embedded_sdk/integration/patches/0002-Use-custom-strnlen-implementation.patch b/components/aws_iot/jobs_for_aws_iot_embedded_sdk/integration/patches/0002-Use-custom-strnlen-implementation.patch new file mode 100644 index 00000000..5bb873fb --- /dev/null +++ b/components/aws_iot/jobs_for_aws_iot_embedded_sdk/integration/patches/0002-Use-custom-strnlen-implementation.patch @@ -0,0 +1,56 @@ +From 02b777ed41b393163c3f793d8ff3b608ac0b9634 Mon Sep 17 00:00:00 2001 +From: Chuyue Luo +Date: Wed, 4 Dec 2024 15:24:44 +0000 +Subject: [PATCH 2/2] Use custom `strnlen` implementation + +The Arm Compiler for Embedded (v6.21) does not support the `strnlen` +function. Therefore, use our own implementation (`app_strnlen`) instead. + +Signed-off-by: Chuyue Luo +--- + source/jobs.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/source/jobs.c b/source/jobs.c +index 9614d90..0f83c27 100644 +--- a/source/jobs.c ++++ b/source/jobs.c +@@ -1,6 +1,8 @@ + /* + * AWS IoT Jobs v1.5.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. ++ * Copyright 2024 Arm Limited and/or its affiliates ++ * + * + * SPDX-License-Identifier: MIT + * +@@ -36,6 +38,8 @@ + /* External Dependencies */ + #include "core_json.h" + ++#include "app_strnlen.h" ++ + /** @cond DO_NOT_DOCUMENT */ + + /** +@@ -575,7 +579,7 @@ static bool isThingnameTopicMatch( const char * topic, + writePreamble( expectedTopicBuffer, &start, TOPIC_BUFFER_SIZE, thingName, ( uint16_t ) thingNameLength ); + ( void ) strnAppend( expectedTopicBuffer, &start, TOPIC_BUFFER_SIZE, topicSuffix, topicSuffixLength ); + +- isMatch = ( size_t ) strnlen( expectedTopicBuffer, TOPIC_BUFFER_SIZE ) == ++ isMatch = ( size_t ) app_strnlen( expectedTopicBuffer, TOPIC_BUFFER_SIZE ) == + topicLength; + isMatch = isMatch && ( strncmp( expectedTopicBuffer, topic, topicLength ) == 0 ); + } +@@ -894,7 +898,7 @@ bool Jobs_IsJobUpdateStatus( const char * topic, + ( void ) strnAppend( suffixBuffer, &start, suffixBufferLength, "/update/", ( CONST_STRLEN( "/update/" ) ) ); + ( void ) strnAppend( suffixBuffer, &start, suffixBufferLength, jobUpdateStatusString[ expectedStatus ], jobUpdateStatusStringLengths[ expectedStatus ] ); + +- return isThingnameTopicMatch( topic, topicLength, suffixBuffer, strnlen( suffixBuffer, suffixBufferLength ), thingName, thingNameLength ); ++ return isThingnameTopicMatch( topic, topicLength, suffixBuffer, app_strnlen( suffixBuffer, suffixBufferLength ), thingName, thingNameLength ); + } + + size_t Jobs_GetJobId( const char * message, +-- +2.47.0 + From c7d30e82795133bf6e63ab09d1f288d66be9dc83 Mon Sep 17 00:00:00 2001 From: Chuyue Luo Date: Fri, 25 Oct 2024 14:45:25 +0000 Subject: [PATCH 05/12] components: Add aws-iot-core-mqtt-file-streams-embedded-c component Add the aws-iot-core-mqtt-file-streams-embedded-c repository as a submodule. This library allows files from a stream (an abstraction for a list of files) to be transferred to an IoT device. It is the second of the two libraries that must be integrated to allow the new modular OTA to be used. In addition, the required integration CMake files to build the component are added. Signed-off-by: Chuyue Luo --- .gitmodules | 3 +++ components/aws_iot/CMakeLists.txt | 1 + .../CMakeLists.txt | 15 ++++++++++++ .../integration/CMakeLists.txt | 23 +++++++++++++++++++ .../library | 1 + manifest.yml | 9 ++++++++ 6 files changed, 52 insertions(+) create mode 100644 components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/CMakeLists.txt create mode 100644 components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/integration/CMakeLists.txt create mode 160000 components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/library diff --git a/.gitmodules b/.gitmodules index ca6739d1..f7aa5ffc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -76,3 +76,6 @@ [submodule "components/aws_iot/jobs_for_aws_iot_embedded_sdk/library"] path = components/aws_iot/jobs_for_aws_iot_embedded_sdk/library url = https://github.com/aws/Jobs-for-AWS-IoT-embedded-sdk.git +[submodule "components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/library"] + path = components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/library + url = https://github.com/aws/aws-iot-core-mqtt-file-streams-embedded-c/ diff --git a/components/aws_iot/CMakeLists.txt b/components/aws_iot/CMakeLists.txt index 8c1cdd8f..2695d123 100644 --- a/components/aws_iot/CMakeLists.txt +++ b/components/aws_iot/CMakeLists.txt @@ -13,4 +13,5 @@ if(CONNECTIVITY_STACK STREQUAL "FREERTOS_PLUS_TCP") add_subdirectory(coresntp) endif() add_subdirectory(jobs_for_aws_iot_embedded_sdk) +add_subdirectory(aws_iot_core_mqtt_file_streams_embedded_c) add_subdirectory(tinycbor) diff --git a/components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/CMakeLists.txt b/components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/CMakeLists.txt new file mode 100644 index 00000000..74a34ac0 --- /dev/null +++ b/components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright 2024 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +if(BUILD_TESTING AND NOT CMAKE_CROSSCOMPILING) + # Left empty for future mocks. +else() + set(aws-iot-core-mqtt-file-streams-embedded-c_SOURCE_DIR + ${CMAKE_CURRENT_LIST_DIR}/library + CACHE INTERNAL + "Path to MQTT File Streams source code" + ) + + add_subdirectory(integration) +endif() diff --git a/components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/integration/CMakeLists.txt b/components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/integration/CMakeLists.txt new file mode 100644 index 00000000..95d39947 --- /dev/null +++ b/components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/integration/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright 2024 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +add_library(aws-iot-core-mqtt-file-streams-embedded-c + ${aws-iot-core-mqtt-file-streams-embedded-c_SOURCE_DIR}/source/MQTTFileDownloader_base64.c + ${aws-iot-core-mqtt-file-streams-embedded-c_SOURCE_DIR}/source/MQTTFileDownloader_cbor.c + ${aws-iot-core-mqtt-file-streams-embedded-c_SOURCE_DIR}/source/MQTTFileDownloader.c +) + +target_include_directories(aws-iot-core-mqtt-file-streams-embedded-c + PUBLIC + ${aws-iot-core-mqtt-file-streams-embedded-c_SOURCE_DIR}/source/include/ +) + +target_link_libraries(aws-iot-core-mqtt-file-streams-embedded-c + PUBLIC + corejson + coremqtt + coremqtt-agent + crt-helpers + tinycbor +) diff --git a/components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/library b/components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/library new file mode 160000 index 00000000..05ff5dc5 --- /dev/null +++ b/components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/library @@ -0,0 +1 @@ +Subproject commit 05ff5dc55a8591360664557f78ae1d71d696d201 diff --git a/manifest.yml b/manifest.yml index 20f5fae9..4b8e6526 100644 --- a/manifest.yml +++ b/manifest.yml @@ -237,3 +237,12 @@ dependencies: type: "git" url: "https://github.com/aws/Jobs-for-AWS-IoT-embedded-sdk.git" path: "components/aws_iot/jobs_for_aws_iot_embedded_sdk/library" + - name: "aws-iot-core-mqtt-file-streams-embedded-c" + license: "MIT" + tpip-category: "category-2" + security-risk: "low" + version: "v1.1.0" + repository: + type: "git" + url: "https://github.com/aws/aws-iot-core-mqtt-file-streams-embedded-c.git" + path: "components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/library" From 355eae8bd2b4fad17d7d176879f396c0eb4ea61a Mon Sep 17 00:00:00 2001 From: Chuyue Luo Date: Tue, 26 Nov 2024 16:24:43 +0000 Subject: [PATCH 06/12] components: Add patch for MQTT File Streams library The aws-iot-core-mqtt-file-streams-embedded-c library uses the `strnlen` function, which is not supported by the Arm Compiler for Embedded (v6.21). Therefore, add a patch which replaces the call to `strnlen` with a call to our custom implementation `app_strnlen`. Signed-off-by: Chuyue Luo --- .../CMakeLists.txt | 8 ++++ ...01-Use-custom-strnlen-implementation.patch | 48 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/integration/patches/0001-Use-custom-strnlen-implementation.patch diff --git a/components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/CMakeLists.txt b/components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/CMakeLists.txt index 74a34ac0..baa6e5fe 100644 --- a/components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/CMakeLists.txt +++ b/components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/CMakeLists.txt @@ -11,5 +11,13 @@ else() "Path to MQTT File Streams source code" ) + include(ApplyPatches) + + set(PATCH_FILES_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/integration/patches") + set(PATCH_FILES + "${PATCH_FILES_DIRECTORY}/0001-Use-custom-strnlen-implementation.patch" + ) + iot_reference_arm_corstone3xx_apply_patches("${aws-iot-core-mqtt-file-streams-embedded-c_SOURCE_DIR}" "${PATCH_FILES}") + add_subdirectory(integration) endif() diff --git a/components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/integration/patches/0001-Use-custom-strnlen-implementation.patch b/components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/integration/patches/0001-Use-custom-strnlen-implementation.patch new file mode 100644 index 00000000..78918a00 --- /dev/null +++ b/components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/integration/patches/0001-Use-custom-strnlen-implementation.patch @@ -0,0 +1,48 @@ +From e760c27b177175214a7e71c93e7d9940e7044c23 Mon Sep 17 00:00:00 2001 +From: Chuyue Luo +Date: Wed, 4 Dec 2024 15:08:01 +0000 +Subject: [PATCH] Use custom `strnlen` implementation + +The Arm Compiler for Embedded (v6.21) does not support the `strnlen` +function. Therefore, use our own implementation (`app_strnlen`) instead. + +Signed-off-by: Chuyue Luo +--- + source/MQTTFileDownloader.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/source/MQTTFileDownloader.c b/source/MQTTFileDownloader.c +index 93a4894..48edce3 100644 +--- a/source/MQTTFileDownloader.c ++++ b/source/MQTTFileDownloader.c +@@ -1,6 +1,9 @@ + /* + * AWS IoT Core MQTT File Streams Embedded C v1.1.0 + * Copyright (C) 2023 Amazon.com, Inc. and its affiliates. All Rights Reserved. ++ * Copyright 2024 Arm Limited and/or its affiliates ++ * ++ * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License. See the LICENSE accompanying this file +@@ -27,6 +30,8 @@ + #include "MQTTFileDownloader_cbor.h" + #include "core_json.h" + ++#include "app_strnlen.h" ++ + /** + * @brief Macro to check whether a character is an ASCII digit or not. + */ +@@ -320,7 +325,7 @@ size_t mqttDownloader_createGetDataBlockRequest( DataType_t dataType, + blockOffset, + numberOfBlocksRequested ); + +- requestLength = strnlen( getStreamRequest, ++ requestLength = app_strnlen( getStreamRequest, + GET_STREAM_REQUEST_BUFFER_SIZE ); + } + else +-- +2.47.0 + From ab09714efdd49f2ef36844cd391c562d56359834 Mon Sep 17 00:00:00 2001 From: Chuyue Luo Date: Tue, 19 Nov 2024 15:18:08 +0000 Subject: [PATCH 07/12] applications: Add MQTT File Downloader config for keyword detection THe MQTT File Streams library allows a MQTTFileDownloader_config.h file to be provided, which defines custom values for build configuration macros. This commit adds a MQTTFileDownloader_config.h file for the keyword detection example. This file defines the block size that should be used when downloading the firmware image. Signed-off-by: Chuyue Luo --- .../aws_configs/MQTTFileDownloader_config.h | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 applications/keyword_detection/configs/aws_configs/MQTTFileDownloader_config.h diff --git a/applications/keyword_detection/configs/aws_configs/MQTTFileDownloader_config.h b/applications/keyword_detection/configs/aws_configs/MQTTFileDownloader_config.h new file mode 100644 index 00000000..5090a98c --- /dev/null +++ b/applications/keyword_detection/configs/aws_configs/MQTTFileDownloader_config.h @@ -0,0 +1,31 @@ +/* + * Copyright Amazon.com, Inc. and its affiliates. All Rights Reserved. + * Copyright 2024 Arm Limited and/or its affiliates + * + * + * SPDX-License-Identifier: MIT + * Licensed under the MIT License. See the LICENSE accompanying this file + * for the specific language governing permissions and limitations under + * the License. + */ + +/** + * @file MQTTFileDownloader_config.h + * @brief MQTT File Streams options. + */ + +#ifndef MQTT_FILE_DOWNLOADER_CONFIG_H +#define MQTT_FILE_DOWNLOADER_CONFIG_H + +/* Standard includes */ +#include + +/** + * @ingroup mqtt_file_downloader_const_types + * @brief Configure the Maximum size of the data payload. The + * smallest value is 256 bytes, maximum is 128KB. For more see + * https://docs.aws.amazon.com/general/latest/gr/iot-core.html + */ +#define mqttFileDownloader_CONFIG_BLOCK_SIZE 4096U + +#endif /* MQTT_FILE_DOWNLOADER_CONFIG_H */ From 25b1075ab7b1faa5f3ace443e2304fe76de7399a Mon Sep 17 00:00:00 2001 From: Chuyue Luo Date: Mon, 28 Oct 2024 14:16:49 +0000 Subject: [PATCH 08/12] components: Add patch for FreeRTOS OTA PAL PSA Add a patch for FreeRTOS OTA PAL PSA to allow it to work with the new modular OTA structure. This patch does the following: - Update header includes to remove headers from the old ota-for-aws-iot-embedded-sdk library, replace these with headers from the new Jobs-for-AWS-IoT-embedded-sdk library - Remove usage of data structures from ota-for-aws-iot-embedded-sdk library, replace these with data structures from Jobs-for-AWS-IoT-embedded-sdk library. Signed-off-by: Chuyue Luo --- .../freertos_ota_pal_psa/CMakeLists.txt | 1 + .../integration/CMakeLists.txt | 2 + ...OTA-PAL-PSA-to-work-with-new-modular.patch | 766 ++++++++++++++++++ 3 files changed, 769 insertions(+) create mode 100644 components/security/freertos_ota_pal_psa/integration/patches/0002-Update-FreeRTOS-OTA-PAL-PSA-to-work-with-new-modular.patch diff --git a/components/security/freertos_ota_pal_psa/CMakeLists.txt b/components/security/freertos_ota_pal_psa/CMakeLists.txt index 8ce106b7..dc54dbe1 100644 --- a/components/security/freertos_ota_pal_psa/CMakeLists.txt +++ b/components/security/freertos_ota_pal_psa/CMakeLists.txt @@ -14,6 +14,7 @@ if(CMAKE_CROSSCOMPILING) set(PATCH_FILES_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/integration/patches") set(PATCH_FILES "${PATCH_FILES_DIRECTORY}/0001-ota-abort-Fix-successful-abortion-check.patch" + "${PATCH_FILES_DIRECTORY}/0002-Update-FreeRTOS-OTA-PAL-PSA-to-work-with-new-modular.patch" ) iot_reference_arm_corstone3xx_apply_patches("${freertos_ota_pal_psa_SOURCE_DIR}" "${PATCH_FILES}") diff --git a/components/security/freertos_ota_pal_psa/integration/CMakeLists.txt b/components/security/freertos_ota_pal_psa/integration/CMakeLists.txt index f9984087..045309df 100644 --- a/components/security/freertos_ota_pal_psa/integration/CMakeLists.txt +++ b/components/security/freertos_ota_pal_psa/integration/CMakeLists.txt @@ -34,4 +34,6 @@ target_link_libraries(freertos-ota-pal-psa freertos_kernel helpers-logging mbedtls + jobs-for-aws-iot-embedded-sdk + ota-update ) diff --git a/components/security/freertos_ota_pal_psa/integration/patches/0002-Update-FreeRTOS-OTA-PAL-PSA-to-work-with-new-modular.patch b/components/security/freertos_ota_pal_psa/integration/patches/0002-Update-FreeRTOS-OTA-PAL-PSA-to-work-with-new-modular.patch new file mode 100644 index 00000000..4e676bb7 --- /dev/null +++ b/components/security/freertos_ota_pal_psa/integration/patches/0002-Update-FreeRTOS-OTA-PAL-PSA-to-work-with-new-modular.patch @@ -0,0 +1,766 @@ +From 45ee1cd5754d0c445bc500ed137adb896e33497a Mon Sep 17 00:00:00 2001 +From: Chuyue Luo +Date: Thu, 5 Dec 2024 10:53:50 +0000 +Subject: [PATCH 2/2] Update FreeRTOS OTA PAL PSA to work with new modular OTA + +This patch is taken from +https://github.com/Linaro/freertos-ota-pal-psa/commit/bfef9705b92badfa5bcb388f4d1cf512b27c5a5f.patch + +This patch updates the FreeRTOS OTA PAL PSA to work with the new modular +OTA structure: +- Update header includes to remove headers from the old +ota-for-aws-iot-embedded-sdk library, replace these with headers from +the new Jobs-for-AWS-IoT-embedded-sdk library +- Remove usage of data structures from ota-for-aws-iot-embedded-sdk +library, replace these with data structures from +Jobs-for-AWS-IoT-embedded-sdk library. + +Signed-off-by: Chuyue Luo +--- + ota_pal.c | 172 +++++++++++++++++----------------- + ota_pal.h | 77 +++++++++++++-- + version/application_version.c | 38 +++++++- + version/application_version.h | 3 +- + 4 files changed, 192 insertions(+), 98 deletions(-) + +diff --git a/ota_pal.c b/ota_pal.c +index 165c146..9b7e6f4 100644 +--- a/ota_pal.c ++++ b/ota_pal.c +@@ -1,7 +1,7 @@ + /* + * AWS IoT Over-the-air Update v3.0.0 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +- * Copyright (c) 2021-2022 Arm Limited. All rights reserved. ++ * Copyright (c) 2021-2024 Arm Limited. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in +@@ -40,13 +40,14 @@ + #endif + + /* define LIBRARY_LOG_LEVEL here if you want to modify the logging level from the default */ +-#define LIBRARY_LOG_LEVEL LOG_INFO ++#ifndef LIBRARY_LOG_LEVEL ++ #define LIBRARY_LOG_LEVEL LOG_INFO ++#endif + + #include "logging_stack.h" + + /* To provide appFirmwareVersion for OTA library. */ + #include "ota_config.h" +-#include "ota_appversion32.h" + + /* OTA PAL Port include. */ + #include "ota_pal.h" +@@ -63,6 +64,8 @@ + + #define ECDSA_SHA256_RAW_SIGNATURE_LENGTH ( 64 ) + ++#define OTA_FILE_SIG_KEY_STR_MAX_LENGTH 32 ++ + /*********************************************************************** + * + * Structures +@@ -93,7 +96,7 @@ + * Keep track of system context between calls from the OTA Agent + * + */ +-const OtaFileContext_t * pxSystemContext = NULL; ++const AfrOtaJobDocumentFields_t * pxSystemContext = NULL; + static psa_fwu_component_t xOTAComponentID = FWU_COMPONENT_NUMBER; + + /* The key handle for OTA image verification. The key should be provisioned +@@ -111,12 +114,15 @@ extern psa_key_handle_t xOTACodeVerifyKeyHandle; + * + **********************************************************************/ + +-static bool prvConvertToRawECDSASignature( const uint8_t * pucEncodedSignature, uint8_t * pucRawSignature ) ++static bool prvConvertToRawECDSASignature( const uint8_t * pucEncodedSignature, size_t signatureLength, uint8_t * pucRawSignature ) + { + bool xReturn = true; + const uint8_t * pxNextLength = NULL; + uint8_t ucSigComponentLength; + ++ /* Suppress compiler warning about unused parameter. */ ++ ( void ) signatureLength; ++ + if( ( pucRawSignature == NULL ) || ( pucEncodedSignature == NULL ) ) + { + xReturn = false; +@@ -199,38 +205,38 @@ static bool prvConvertToRawECDSASignature( const uint8_t * pucEncodedSignature, + return xReturn; + } + +-static OtaPalStatus_t PortConvertFilePathtoPSAComponentID ( OtaFileContext_t * const pFileContext, +- psa_fwu_component_t * pxComponent ) ++static bool PortConvertFilePathtoPSAComponentID ( AfrOtaJobDocumentFields_t * const pFileContext, ++ psa_fwu_component_t * pxComponent ) + { +- if( pFileContext == NULL || pxComponent == NULL || pFileContext->pFilePath == NULL ) ++ if( pFileContext == NULL || pxComponent == NULL || pFileContext->filepath == NULL ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalUninitialized, 0 ); ++ return false; + } + + #ifdef FWU_COMPONENT_ID_SECURE + /* pFilePath field is got from the OTA server. */ +- if( memcmp( pFileContext->pFilePath, "secure image", strlen("secure image") ) == 0 ) ++ if( memcmp( pFileContext->filepath, "secure image", strlen("secure image") ) == 0 ) + { + *pxComponent = FWU_COMPONENT_ID_SECURE; +- return OTA_PAL_COMBINE_ERR( OtaPalSuccess, 0 ); ++ return true; + } + #endif + #ifdef FWU_COMPONENT_ID_NONSECURE +- if( memcmp( pFileContext->pFilePath, "non_secure image", strlen("non_secure image") ) == 0 ) ++ if( memcmp( pFileContext->filepath, "non_secure image", strlen("non_secure image") ) == 0 ) + { + *pxComponent = FWU_COMPONENT_ID_NONSECURE; +- return OTA_PAL_COMBINE_ERR( OtaPalSuccess, 0 ); ++ return true; + } + #endif + #ifdef FWU_COMPONENT_ID_FULL +- if( memcmp( pFileContext->pFilePath, "combined image", strlen("combined image") ) == 0 ) ++ if( memcmp( pFileContext->filepath, "combined image", strlen("combined image") ) == 0 ) + { + *pxComponent = FWU_COMPONENT_ID_FULL; +- return OTA_PAL_COMBINE_ERR( OtaPalSuccess, 0 ); ++ return true; + } + #endif + +- return OTA_PAL_COMBINE_ERR( OtaPalRxFileCreateFailed, 0 ); ++ return false; + } + + /** +@@ -256,25 +262,25 @@ static OtaPalStatus_t PortConvertFilePathtoPSAComponentID ( OtaFileContext_t * c + * OtaPalSuccess: Aborting access to the open file was successful. + * OtaPalFileAbort: Aborting access to the open file context was unsuccessful. + */ +-OtaPalStatus_t otaPal_Abort( OtaFileContext_t * const pFileContext ) ++bool otaPal_Abort( AfrOtaJobDocumentFields_t * const pFileContext ) + { +- OtaPalStatus_t retStatus = OTA_PAL_COMBINE_ERR( OtaPalSuccess, 0 ); ++ bool retStatus = true; + + if( ( pFileContext == NULL ) || ( ( pFileContext != pxSystemContext ) && ( pxSystemContext != NULL ) ) ) + { + LogWarn( ( "otaPal_Abort: pFileContext or pFileContext->pFile is NULL." ) ); +- retStatus = OTA_PAL_COMBINE_ERR( OtaPalAbortFailed, 0 ); ++ retStatus = false; + } +- else if( pFileContext->pFile == NULL ) ++ else if( pFileContext->fileId == 0 ) + { + /* Nothing to do. No open file associated with this context. */ + } + else if( ( pFileContext != pxSystemContext ) && ( pxSystemContext != NULL ) ) + { + LogWarn( ( "otaPal_Abort: pFileContext is different from pxSystemContext." ) ); +- retStatus = OTA_PAL_COMBINE_ERR( OtaPalAbortFailed, 0 ); ++ retStatus = false; + +- pFileContext->pFile = NULL; ++ pFileContext->fileId = 0; + } + else if( pxSystemContext == NULL ) + { +@@ -285,23 +291,23 @@ OtaPalStatus_t otaPal_Abort( OtaFileContext_t * const pFileContext ) + psa_status_t lPsaStatus = PSA_SUCCESS; + if( psa_fwu_cancel( xOTAComponentID ) != PSA_SUCCESS ) + { +- lPsaStatus = OTA_PAL_COMBINE_ERR( OtaPalAbortFailed, 0 ); ++ lPsaStatus = false; + } + if( psa_fwu_clean( xOTAComponentID ) != PSA_SUCCESS ) + { +- lPsaStatus = OTA_PAL_COMBINE_ERR( OtaPalAbortFailed, 0 ); ++ lPsaStatus = false; + } + /* psa_fwu_abort returns PSA_ERROR_INVALID_ARGUMENT if xOTAImageID was NOT written before abort. + * But we should return success if xOTAImageID was created. */ + if( ( lPsaStatus != PSA_SUCCESS ) && ( lPsaStatus != PSA_ERROR_INVALID_ARGUMENT ) ) + { + LogWarn( ( "otaPal_Abort: psa_fwu_abort fail with error %d.", lPsaStatus ) ); +- retStatus = OTA_PAL_COMBINE_ERR( OtaPalAbortFailed, 1 ); ++ retStatus = false; + } + + pxSystemContext = NULL; + xOTAComponentID = 0; +- pFileContext->pFile = NULL; ++ pFileContext->fileId = 0; + } + + return retStatus; +@@ -335,33 +341,33 @@ OtaPalStatus_t otaPal_Abort( OtaFileContext_t * const pFileContext ) + * non-volatile memory. If this error is returned, then the sub error + * should be set to the appropriate platform specific value. + */ +-OtaPalStatus_t otaPal_CreateFileForRx( OtaFileContext_t * const pFileContext ) ++OtaPalJobDocProcessingResult_t otaPal_CreateFileForRx( AfrOtaJobDocumentFields_t * const pFileContext ) + { + psa_fwu_component_t uxComponent; + +- if( pFileContext == NULL || pFileContext->pFilePath == NULL ) ++ if( pFileContext == NULL || pFileContext->filepath == NULL ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalRxFileCreateFailed, 0 ); ++ return OtaPalJobDocFileCreateFailed; + } + +- if( PortConvertFilePathtoPSAComponentID( pFileContext, &uxComponent ) != OTA_PAL_COMBINE_ERR( OtaPalSuccess, 0 ) ) ++ if( PortConvertFilePathtoPSAComponentID( pFileContext, &uxComponent ) != true ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalRxFileCreateFailed, 0 ); ++ return OtaPalJobDocFileCreateFailed; + } + + /* Trigger a FWU process. Image manifest is bundled within the image. */ + if( psa_fwu_start( uxComponent, NULL, 0 ) != PSA_SUCCESS ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalRxFileCreateFailed, 0 ); ++ return OtaPalJobDocFileCreateFailed; + } + + pxSystemContext = pFileContext; + xOTAComponentID = uxComponent; +- pFileContext->pFile = &xOTAComponentID; +- return OTA_PAL_COMBINE_ERR( OtaPalSuccess, 0 ); ++ pFileContext->fileId = ( uint32_t ) xOTAComponentID; ++ return OtaPalJobDocFileCreated; + } + +-static OtaPalStatus_t otaPal_CheckSignature( OtaFileContext_t * const pFileContext ) ++static bool otaPal_CheckSignature( AfrOtaJobDocumentFields_t * const pFileContext ) + { + psa_fwu_component_info_t xComponentInfo = { 0 }; + psa_status_t uxStatus; +@@ -373,28 +379,28 @@ static OtaPalStatus_t otaPal_CheckSignature( OtaFileContext_t * const pFileConte + uxStatus = psa_fwu_query( xOTAComponentID, &xComponentInfo ); + if( uxStatus != PSA_SUCCESS ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalSignatureCheckFailed, OTA_PAL_SUB_ERR( uxStatus ) ); ++ return false; + } + + + #if ( defined( OTA_PAL_SIGNATURE_FORMAT ) && ( OTA_PAL_SIGNATURE_FORMAT == OTA_PAL_SIGNATURE_ASN1_DER ) ) +- if( prvConvertToRawECDSASignature( pFileContext->pSignature->data, ucECDSARAWSignature ) == false ) ++ if( prvConvertToRawECDSASignature( pFileContext->signature, pFileContext->signatureLen, ucECDSARAWSignature ) == false ) + { + LogError( ( "Failed to decode ECDSA SHA256 signature." ) ); +- return OTA_PAL_COMBINE_ERR( OtaPalSignatureCheckFailed, 0 ); ++ return false; + } + + ucSigBuffer = &ucECDSARAWSignature; + usSigLength = ECDSA_SHA256_RAW_SIGNATURE_LENGTH; + #else +- ucSigBuffer = (uint8_t *) &pFileContext->pSignature->data; +- usSigLength = pFileContext->pSignature->size; ++ ucSigBuffer = (uint8_t *) pFileContext->signature; ++ usSigLength = pFileContext->signatureLen; + #endif /* defined( OTA_PAL_SIGNATURE_FORMAT ) && ( OTA_PAL_SIGNATURE_FORMAT == OTA_PAL_SIGNATURE_ASN1_DER ) */ + + uxStatus = psa_get_key_attributes( xOTACodeVerifyKeyHandle, &xKeyAttribute ); + if( uxStatus != PSA_SUCCESS ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalSignatureCheckFailed, OTA_PAL_SUB_ERR( uxStatus ) ); ++ return false; + } + + xKeyAlgorithm = psa_get_key_algorithm( &xKeyAttribute ); +@@ -407,10 +413,10 @@ static OtaPalStatus_t otaPal_CheckSignature( OtaFileContext_t * const pFileConte + + if( uxStatus != PSA_SUCCESS ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalSignatureCheckFailed, OTA_PAL_SUB_ERR( uxStatus ) ); ++ return false; + } + +- return OTA_PAL_COMBINE_ERR( OtaPalSuccess, 0 ); ++ return true; + } + + /** +@@ -419,7 +425,7 @@ static OtaPalStatus_t otaPal_CheckSignature( OtaFileContext_t * const pFileConte + * @note The input OtaFileContext_t pFileContext is checked for NULL by the OTA agent before this + * function is called. This function is called only at the end of block ingestion. + * otaPAL_CreateFileForRx() must succeed before this function is reached, so +- * pFileContext->fileHandle(or pFileContext->pFile) is never NULL. ++ * pFileContext->fileHandle is never NULL or pFileContext->fileId is never 0. + * The file signature key is required job document field in the OTA Agent, so pFileContext->pSignature will + * never be NULL. + * +@@ -438,8 +444,14 @@ static OtaPalStatus_t otaPal_CheckSignature( OtaFileContext_t * const pFileConte + * OtaPalBadSignerCert: The signer certificate was not readable or zero length. + * OtaPalFileClose: Error in low level file close. + */ +-OtaPalStatus_t otaPal_CloseFile( OtaFileContext_t * const pFileContext ) ++bool otaPal_CloseFile( AfrOtaJobDocumentFields_t * const pFileContext ) + { ++ /* Mark the image ready for installation. */ ++ if( psa_fwu_finish( xOTAComponentID ) != PSA_SUCCESS ) ++ { ++ return -1; ++ } ++ + /* Check the signature. */ + return otaPal_CheckSignature( pFileContext ); + } +@@ -463,7 +475,7 @@ OtaPalStatus_t otaPal_CloseFile( OtaFileContext_t * const pFileContext ) + * @return The number of bytes written successfully, or a negative error code from the platform + * abstraction layer. + */ +-int16_t otaPal_WriteBlock( OtaFileContext_t * const pFileContext, ++int16_t otaPal_WriteBlock( AfrOtaJobDocumentFields_t * const pFileContext, + uint32_t ulOffset, + uint8_t * const pcData, + uint32_t ulBlockSize ) +@@ -491,16 +503,6 @@ int16_t otaPal_WriteBlock( OtaFileContext_t * const pFileContext, + ulDoneLength += ulWriteLength; + } + +- /* If this is the last block, call 'psa_fwu_fnish()' to mark image ready for installation. */ +- if( pFileContext->blocksRemaining == 1 ) +- { +- LogDebug( ( "pFileContext->blocksRemaining == 1 ." ) ); +- if( psa_fwu_finish( xOTAComponentID ) != PSA_SUCCESS ) +- { +- return -1; +- } +- } +- + return ulDoneLength; + } + +@@ -524,13 +526,13 @@ int16_t otaPal_WriteBlock( OtaFileContext_t * const pFileContext, + * OtaPalSuccess on success. + * OtaPalActivateFailed: The activation of the new OTA image failed. + */ +-OtaPalStatus_t otaPal_ActivateNewImage( OtaFileContext_t * const pFileContext ) ++bool otaPal_ActivateNewImage( AfrOtaJobDocumentFields_t * const pFileContext ) + { + psa_status_t uxStatus; + + if( (pFileContext == NULL) || (pFileContext != pxSystemContext ) || ( xOTAComponentID >= FWU_COMPONENT_NUMBER ) ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalActivateFailed, 0 ); ++ return false; + } + + uxStatus = psa_fwu_install(); +@@ -539,15 +541,15 @@ OtaPalStatus_t otaPal_ActivateNewImage( OtaFileContext_t * const pFileContext ) + otaPal_ResetDevice( pFileContext ); + + /* Reset failure happened. */ +- return OTA_PAL_COMBINE_ERR( OtaPalActivateFailed, 0 ); ++ return false; + } + else if( uxStatus == PSA_SUCCESS ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalSuccess, 0 ); ++ return true; + } + else + { +- return OTA_PAL_COMBINE_ERR( OtaPalActivateFailed, OTA_PAL_SUB_ERR( uxStatus ) ); ++ return false; + } + } + +@@ -572,7 +574,7 @@ OtaPalStatus_t otaPal_ActivateNewImage( OtaFileContext_t * const pFileContext ) + * OtaPalRejectFailed: failed to roll back the update image as requested by OtaImageStateRejected. + * OtaPalCommitFailed: failed to make the update image permanent as requested by OtaImageStateAccepted. + */ +-OtaPalStatus_t otaPal_SetPlatformImageState( OtaFileContext_t * const pFileContext, ++bool otaPal_SetPlatformImageState( AfrOtaJobDocumentFields_t * const pFileContext, + OtaImageState_t eState ) + { + psa_fwu_component_t uxComponent; +@@ -585,22 +587,22 @@ OtaPalStatus_t otaPal_SetPlatformImageState( OtaFileContext_t * const pFileConte + switch ( eState ) + { + case OtaImageStateAccepted: +- if( PortConvertFilePathtoPSAComponentID( pFileContext, &uxComponent ) != OTA_PAL_COMBINE_ERR( OtaPalSuccess, 0 ) ) ++ if( PortConvertFilePathtoPSAComponentID( pFileContext, &uxComponent ) != true ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalCommitFailed, 0 ); ++ return false; + } + + /* Make this image as a permanent one. */ + uxStatus = psa_fwu_accept(); + if( uxStatus != PSA_SUCCESS ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalCommitFailed, OTA_PAL_SUB_ERR( uxStatus ) ); ++ return false; + } + /* Erase the secondary slot and update FWU component state to PSA_FWU_READY. */ + uxStatus = psa_fwu_clean(uxComponent); + if( uxStatus != PSA_SUCCESS ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalCommitFailed, OTA_PAL_SUB_ERR( uxStatus ) ); ++ return false; + } + break; + case OtaImageStateRejected: +@@ -609,26 +611,26 @@ OtaPalStatus_t otaPal_SetPlatformImageState( OtaFileContext_t * const pFileConte + uxStatus = psa_fwu_reject( PSA_ERROR_NOT_PERMITTED ); + if(( uxStatus != PSA_SUCCESS ) && ( uxStatus != PSA_SUCCESS_REBOOT )) + { +- return OTA_PAL_COMBINE_ERR( OtaPalRejectFailed, OTA_PAL_SUB_ERR( uxStatus ) ); ++ return false; + } + break; + case OtaImageStateTesting: +- if( PortConvertFilePathtoPSAComponentID( pFileContext, &uxComponent ) != OTA_PAL_COMBINE_ERR( OtaPalSuccess, 0 ) ) ++ if( PortConvertFilePathtoPSAComponentID( pFileContext, &uxComponent ) != true ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalCommitFailed, 0 ); ++ return false; + } + /* Check if the component is in TRIAL state. */ + uxStatus = psa_fwu_query( uxComponent, &xComponentInfo ); + if( ( uxStatus != PSA_SUCCESS ) || ( xComponentInfo.state != PSA_FWU_TRIAL ) ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalCommitFailed, OTA_PAL_SUB_ERR( uxStatus ) ); ++ return false; + } + break; + case OtaImageStateAborted: + /* The image download has been finished or has not been started.*/ + break; + default: +- return OTA_PAL_COMBINE_ERR( OtaPalBadImageState, 0 ); ++ return false; + } + } + else +@@ -637,42 +639,42 @@ OtaPalStatus_t otaPal_SetPlatformImageState( OtaFileContext_t * const pFileConte + { + case OtaImageStateAccepted: + /* The image can only be set as accepted after a reboot. So the pxSystemContext should be NULL. */ +- return OTA_PAL_COMBINE_ERR( OtaPalCommitFailed, 0 ); ++ return false; + case OtaImageStateRejected: + uxStatus = psa_fwu_query( uxComponent, &xComponentInfo ); + if( uxStatus != PSA_SUCCESS ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalRejectFailed, OTA_PAL_SUB_ERR( uxStatus ) ); ++ return false; + } + if( xComponentInfo.state != PSA_FWU_STAGED ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalBadImageState, 0 ); ++ return false; + } + uxStatus = psa_fwu_reject( PSA_ERROR_NOT_PERMITTED ); + if(( uxStatus != PSA_SUCCESS ) && ( uxStatus != PSA_SUCCESS_REBOOT )) + { +- return OTA_PAL_COMBINE_ERR( OtaPalRejectFailed, OTA_PAL_SUB_ERR( uxStatus ) ); ++ return false; + } + break; + case OtaImageStateAborted: + /* If the component is in TRIAL state, the image will be abandoned. Reboot will be carried + * out by OTA agent so there is no need to reboot here. */ +- if( PortConvertFilePathtoPSAComponentID( pFileContext, &uxComponent ) != OTA_PAL_COMBINE_ERR( OtaPalSuccess, 0 ) ) ++ if( PortConvertFilePathtoPSAComponentID( pFileContext, &uxComponent ) != true ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalRejectFailed, 0 ); ++ return false; + } + uxStatus = psa_fwu_cancel( uxComponent ); + if( uxStatus != PSA_SUCCESS ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalRejectFailed, OTA_PAL_SUB_ERR( uxStatus ) ); ++ return false; + } + if( psa_fwu_clean( xOTAComponentID ) != PSA_SUCCESS ) + { +- return OTA_PAL_COMBINE_ERR( OtaPalRejectFailed, 0 ); ++ return false; + } + break; + default: +- return OTA_PAL_COMBINE_ERR( OtaPalBadImageState, 0 ); ++ return false; + + /* The image is still downloading and the OTA process will not continue. The image is in + * the secondary slot and does not impact the later update process. So nothing to do in +@@ -680,7 +682,7 @@ OtaPalStatus_t otaPal_SetPlatformImageState( OtaFileContext_t * const pFileConte + */ + } + } +- return OTA_PAL_COMBINE_ERR( OtaPalSuccess, 0 ); ++ return true; + } + + /** +@@ -706,13 +708,13 @@ OtaPalStatus_t otaPal_SetPlatformImageState( OtaFileContext_t * const pFileConte + * + * NOTE: OtaPalImageStateUnknown should NEVER be returned and indicates an implementation error. + */ +-OtaPalImageState_t otaPal_GetPlatformImageState( OtaFileContext_t * const pFileContext ) ++OtaPalImageState_t otaPal_GetPlatformImageState( AfrOtaJobDocumentFields_t * const pFileContext ) + { + psa_status_t uxStatus; + psa_fwu_component_info_t xComponentInfo = { 0 }; + psa_fwu_component_t uxComponent; + +- if( PortConvertFilePathtoPSAComponentID( pFileContext, &uxComponent ) != OTA_PAL_COMBINE_ERR( OtaPalSuccess, 0 ) ) ++ if( PortConvertFilePathtoPSAComponentID( pFileContext, &uxComponent ) != true ) + { + return OtaPalImageStateInvalid; + } +@@ -752,8 +754,8 @@ OtaPalImageState_t otaPal_GetPlatformImageState( OtaFileContext_t * const pFileC + * the MCU specific sub error code. See ota_platform_interface.h for the OtaPalMainStatus_t + * error codes and your specific PAL implementation for the sub error code. + */ +-OtaPalStatus_t otaPal_ResetDevice( OtaFileContext_t * const pFileContext ) ++bool otaPal_ResetDevice( AfrOtaJobDocumentFields_t * const pFileContext ) + { + psa_fwu_request_reboot(); +- return OTA_PAL_COMBINE_ERR( OtaPalActivateFailed, 0 ); ++ return false; + } +diff --git a/ota_pal.h b/ota_pal.h +index c530620..4c95bda 100644 +--- a/ota_pal.h ++++ b/ota_pal.h +@@ -1,6 +1,8 @@ + /* + * FreeRTOS V202107.00 + * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. ++ * Copyright 2024 Arm Limited and/or its affiliates ++ * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in +@@ -31,7 +33,7 @@ + #ifndef OTA_PAL_H_ + #define OTA_PAL_H_ + +-#include "ota.h" ++#include "job_parser.h" + + /* OTA PAL signing algorithm configurations. */ + #define OTA_PAL_CODE_SIGNING_RSA ( 0 ) +@@ -54,6 +56,61 @@ + #endif + + ++/** ++ * @ingroup ota_enum_types ++ * @brief OTA Image states. ++ * ++ * After an OTA update image is received and authenticated, it is logically moved to ++ * the Self Test state by the OTA agent pending final acceptance. After the image is ++ * activated and tested by your user code, you should put it into either the Accepted ++ * or Rejected state by calling @ref OTA_SetImageState ( OtaImageStateAccepted ) or ++ * @ref OTA_SetImageState ( OtaImageStateRejected ). If the image is accepted, it becomes ++ * the main firmware image to be booted from then on. If it is rejected, the image is ++ * no longer valid and shall not be used, reverting to the last known good image. ++ * ++ * If you want to abort an active OTA transfer, you may do so by calling the API ++ * @ref OTA_SetImageState ( OtaImageStateAborted ). ++ */ ++typedef enum OtaImageState ++{ ++ OtaImageStateUnknown = 0, /*!< @brief The initial state of the OTA MCU Image. */ ++ OtaImageStateTesting = 1, /*!< @brief The state of the OTA MCU Image post successful download and reboot. */ ++ OtaImageStateAccepted = 2, /*!< @brief The state of the OTA MCU Image post successful download and successful self_test. */ ++ OtaImageStateRejected = 3, /*!< @brief The state of the OTA MCU Image when the job has been rejected. */ ++ OtaImageStateAborted = 4, /*!< @brief The state of the OTA MCU Image after a timeout publish to the stream request fails. ++ * Also if the OTA MCU image is aborted in the middle of a stream. */ ++ OtaLastImageState = OtaImageStateAborted ++} OtaImageState_t; ++ ++/** ++ * @ingroup ota_enum_types ++ * @brief OTA Platform Image State. ++ * ++ * The image state set by platform implementation. ++ */ ++typedef enum OtaPalImageState ++{ ++ OtaPalImageStateUnknown = 0, /*!< @brief The initial state of the OTA PAL Image. */ ++ OtaPalImageStatePendingCommit, /*!< @brief OTA PAL Image awaiting update. */ ++ OtaPalImageStateValid, /*!< @brief OTA PAL Image is valid. */ ++ OtaPalImageStateInvalid /*!< @brief OTA PAL Image is invalid. */ ++} OtaPalImageState_t; ++ ++/** ++ * @ingroup ota_enum_types ++ * @brief OTA Platform Image State. ++ * ++ * The image state set by platform implementation. ++ */ ++typedef enum OtaPalJobDocProcessingResult ++{ ++ OtaPalJobDocFileCreated = 0, ++ OtaPalJobDocFileCreateFailed, ++ OtaPalNewImageBooted, ++ OtaPalNewImageBootFailed, ++ OtaPalJobDocProcessingStateInvalid ++} OtaPalJobDocProcessingResult_t; ++ + /** + * @brief Abort an OTA transfer. + * +@@ -77,7 +134,7 @@ + * OtaPalSuccess: Aborting access to the open file was successful. + * OtaPalFileAbort: Aborting access to the open file context was unsuccessful. + */ +-OtaPalStatus_t otaPal_Abort( OtaFileContext_t * const pFileContext ); ++bool otaPal_Abort( AfrOtaJobDocumentFields_t * const pFileContext ); + + /** + * @brief Create a new receive file. +@@ -107,7 +164,7 @@ OtaPalStatus_t otaPal_Abort( OtaFileContext_t * const pFileContext ); + * non-volatile memory. If this error is returned, then the sub error + * should be set to the appropriate platform specific value. + */ +-OtaPalStatus_t otaPal_CreateFileForRx( OtaFileContext_t * const pFileContext ); ++OtaPalJobDocProcessingResult_t otaPal_CreateFileForRx( AfrOtaJobDocumentFields_t * const pFileContext ); + + /** + * @brief Authenticate and close the underlying receive file in the specified OTA context. +@@ -134,7 +191,7 @@ OtaPalStatus_t otaPal_CreateFileForRx( OtaFileContext_t * const pFileContext ); + * OtaPalBadSignerCert: The signer certificate was not readable or zero length. + * OtaPalFileClose: Error in low level file close. + */ +-OtaPalStatus_t otaPal_CloseFile( OtaFileContext_t * const pFileContext ); ++bool otaPal_CloseFile( AfrOtaJobDocumentFields_t * const pFileContext ); + + /** + * @brief Write a block of data to the specified file at the given offset. +@@ -155,9 +212,9 @@ OtaPalStatus_t otaPal_CloseFile( OtaFileContext_t * const pFileContext ); + * @return The number of bytes written successfully, or a negative error code from the platform + * abstraction layer. + */ +-int16_t otaPal_WriteBlock( OtaFileContext_t * const pFileContext, ++int16_t otaPal_WriteBlock( AfrOtaJobDocumentFields_t * const pFileContext, + uint32_t ulOffset, +- uint8_t * const pData, ++ uint8_t * const pcData, + uint32_t ulBlockSize ); + + /** +@@ -180,7 +237,7 @@ int16_t otaPal_WriteBlock( OtaFileContext_t * const pFileContext, + * OtaPalSuccess on success. + * OtaPalActivateFailed: The activation of the new OTA image failed. + */ +-OtaPalStatus_t otaPal_ActivateNewImage( OtaFileContext_t * const pFileContext ); ++bool otaPal_ActivateNewImage( AfrOtaJobDocumentFields_t * const pFileContext ); + + /** + * @brief Attempt to set the state of the OTA update image. +@@ -203,7 +260,7 @@ OtaPalStatus_t otaPal_ActivateNewImage( OtaFileContext_t * const pFileContext ); + * OtaPalRejectFailed: failed to roll back the update image as requested by OtaImageStateRejected. + * OtaPalCommitFailed: failed to make the update image permanent as requested by OtaImageStateAccepted. + */ +-OtaPalStatus_t otaPal_SetPlatformImageState( OtaFileContext_t * const pFileContext, ++bool otaPal_SetPlatformImageState( AfrOtaJobDocumentFields_t * const pFileContext, + OtaImageState_t eState ); + + /** +@@ -229,7 +286,7 @@ OtaPalStatus_t otaPal_SetPlatformImageState( OtaFileContext_t * const pFileConte + * + * NOTE: OtaPalImageStateUnknown should NEVER be returned and indicates an implementation error. + */ +-OtaPalImageState_t otaPal_GetPlatformImageState( OtaFileContext_t * const pFileContext ); ++OtaPalImageState_t otaPal_GetPlatformImageState( AfrOtaJobDocumentFields_t * const pFileContext ); + + /** + * @brief Reset the device. +@@ -245,5 +302,5 @@ OtaPalImageState_t otaPal_GetPlatformImageState( OtaFileContext_t * const pFileC + * the MCU specific sub error code. See ota_platform_interface.h for the OtaPalMainStatus_t + * error codes and your specific PAL implementation for the sub error code. + */ +-OtaPalStatus_t otaPal_ResetDevice( OtaFileContext_t * const pFileContext ); ++bool otaPal_ResetDevice( AfrOtaJobDocumentFields_t * const pFileContext ); + #endif /* ifndef OTA_PAL_H_ */ +diff --git a/version/application_version.c b/version/application_version.c +index 7b38c6a..68c715f 100644 +--- a/version/application_version.c ++++ b/version/application_version.c +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2020-2022 Arm Limited. All rights reserved. ++ * Copyright (c) 2020-2024 Arm Limited. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in +@@ -33,6 +33,42 @@ + #include "FreeRTOS.h" + #include "application_version.h" + ++/** ++ * @ingroup ota_struct_types ++ * @brief Application version structure. ++ * ++ */ ++typedef struct ++{ ++ /* MISRA Ref 19.2.1 [Unions] */ ++ /* More details at: https://github.com/aws/ota-for-aws-iot-embedded-sdk/blob/main/MISRA.md#rule-192 */ ++ /* coverity[misra_c_2012_rule_19_2_violation] */ ++ union ++ { ++ #if ( defined( __BYTE_ORDER__ ) && defined( __ORDER_LITTLE_ENDIAN__ ) && ( __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ ) ) || ( __little_endian__ == 1 ) || WIN32 || ( __BYTE_ORDER == __LITTLE_ENDIAN ) ++ struct version ++ { ++ uint16_t build; /*!< @brief Build of the firmware (Z in firmware version Z.Y.X). */ ++ uint8_t minor; /*!< @brief Minor version number of the firmware (Y in firmware version Z.Y.X). */ ++ ++ uint8_t major; /*!< @brief Major version number of the firmware (X in firmware version Z.Y.X). */ ++ } x; /*!< @brief Version number of the firmware. */ ++ #elif ( defined( __BYTE_ORDER__ ) && defined( __ORDER_BIG_ENDIAN__ ) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ ) || ( __big_endian__ == 1 ) || ( __BYTE_ORDER == __BIG_ENDIAN ) ++ struct version ++ { ++ uint8_t major; /*!< @brief Major version number of the firmware (X in firmware version X.Y.Z). */ ++ uint8_t minor; /*!< @brief Minor version number of the firmware (Y in firmware version X.Y.Z). */ ++ ++ uint16_t build; /*!< @brief Build of the firmware (Z in firmware version X.Y.Z). */ ++ } x; /*!< @brief Version number of the firmware. */ ++ #else /* if ( defined( __BYTE_ORDER__ ) && defined( __ORDER_LITTLE_ENDIAN__ ) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ ) || ( __little_endian__ == 1 ) || WIN32 || ( __BYTE_ORDER == __LITTLE_ENDIAN ) */ ++ #error "Unable to determine byte order!" ++ #endif /* if ( defined( __BYTE_ORDER__ ) && defined( __ORDER_LITTLE_ENDIAN__ ) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ ) || ( __little_endian__ == 1 ) || WIN32 || ( __BYTE_ORDER == __LITTLE_ENDIAN ) */ ++ uint32_t unsignedVersion32; ++ int32_t signedVersion32; ++ } u; /*!< @brief Version based on configuration in big endian or little endian. */ ++} AppVersion32_t; ++ + AppVersion32_t appFirmwareVersion; + + int GetImageVersionPSA( psa_fwu_component_t uxComponent ) +diff --git a/version/application_version.h b/version/application_version.h +index 7775910..a790df7 100644 +--- a/version/application_version.h ++++ b/version/application_version.h +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2020-2022 Arm Limited. All rights reserved. ++ * Copyright (c) 2020-2024 Arm Limited. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in +@@ -24,7 +24,6 @@ + #define APPLICATION_VERSION_H_ + + #include "ota_config.h" +-#include "ota_appversion32.h" + #include "psa/update.h" + + /** +-- +2.47.0 + From fd6ca962ad512be3dcb60291771125c98cb5483b Mon Sep 17 00:00:00 2001 From: Chuyue Luo Date: Mon, 28 Oct 2024 13:33:35 +0000 Subject: [PATCH 09/12] ota: Add OTA orchestrator Add an OTA orchestrator as a helper within the applications/ directory. The OTA orchestrator uses functionality from the Jobs and MQTT File Streaming libraries to enable OTA updates. In addition, update the keyword detection CMakeLists.txt to allow this example to use the new modular OTA. Signed-off-by: Chuyue Luo --- .github/.cSpellWords.txt | 2 + applications/helpers/CMakeLists.txt | 1 + .../helpers/ota_orchestrator/CMakeLists.txt | 33 + .../ota_orchestrator/inc/mqtt_helpers.h | 78 ++ .../ota_orchestrator/inc/ota_appversion32.h | 88 ++ .../inc/ota_orchestrator_helpers.h | 51 + .../ota_orchestrator/inc/ota_os_freertos.h | 104 ++ .../inc/ota_register_callback.h | 22 + .../inc/ota_types_definitions.h | 152 +++ .../ota_orchestrator/src/mqtt_helpers.c | 280 ++++ .../ota_orchestrator/src/ota_orchestrator.c | 1157 +++++++++++++++++ .../src/ota_orchestrator_helpers.c | 84 ++ .../ota_orchestrator/src/ota_os_freertos.c | 125 ++ applications/keyword_detection/CMakeLists.txt | 1 + release_changes/202411211131.change.md | 1 + 15 files changed, 2179 insertions(+) create mode 100644 applications/helpers/ota_orchestrator/CMakeLists.txt create mode 100644 applications/helpers/ota_orchestrator/inc/mqtt_helpers.h create mode 100644 applications/helpers/ota_orchestrator/inc/ota_appversion32.h create mode 100644 applications/helpers/ota_orchestrator/inc/ota_orchestrator_helpers.h create mode 100644 applications/helpers/ota_orchestrator/inc/ota_os_freertos.h create mode 100644 applications/helpers/ota_orchestrator/inc/ota_register_callback.h create mode 100644 applications/helpers/ota_orchestrator/inc/ota_types_definitions.h create mode 100644 applications/helpers/ota_orchestrator/src/mqtt_helpers.c create mode 100644 applications/helpers/ota_orchestrator/src/ota_orchestrator.c create mode 100644 applications/helpers/ota_orchestrator/src/ota_orchestrator_helpers.c create mode 100644 applications/helpers/ota_orchestrator/src/ota_os_freertos.c create mode 100644 release_changes/202411211131.change.md diff --git a/.github/.cSpellWords.txt b/.github/.cSpellWords.txt index cbcd3f31..083a427b 100644 --- a/.github/.cSpellWords.txt +++ b/.github/.cSpellWords.txt @@ -39,6 +39,7 @@ coef compatibil coremqtt COSE +coverity CLRF Cqqpk CSRS @@ -147,6 +148,7 @@ Merkle Mfcc MFCC MFLN +MISRA mlek mqtt mqttconfig diff --git a/applications/helpers/CMakeLists.txt b/applications/helpers/CMakeLists.txt index 633d74ac..83dbd25f 100644 --- a/applications/helpers/CMakeLists.txt +++ b/applications/helpers/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory(device_advisor) add_subdirectory(events) add_subdirectory(hdlcd) add_subdirectory(logging) +add_subdirectory(ota_orchestrator) add_subdirectory(provisioning) # sntp helper library depends on FreeRTOS-Plus-TCP connectivity stack as it # includes `FreeRTOS_IP.h` header file in one of its source files (sntp_client_task.c), diff --git a/applications/helpers/ota_orchestrator/CMakeLists.txt b/applications/helpers/ota_orchestrator/CMakeLists.txt new file mode 100644 index 00000000..5041bb40 --- /dev/null +++ b/applications/helpers/ota_orchestrator/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright 2024 Arm Limited and/or its affiliates +# +# SPDX-License-Identifier: MIT + +if(BUILD_TESTING AND NOT CMAKE_CROSSCOMPILING) + # Left empty for future mocks. +else() + add_library(ota-update + src/mqtt_helpers.c + src/ota_orchestrator_helpers.c + src/ota_os_freertos.c + src/ota_orchestrator.c + ) + + target_include_directories(ota-update + PUBLIC + inc/ + ) + + target_link_libraries(ota-update + jobs-for-aws-iot-embedded-sdk + aws-iot-core-mqtt-file-streams-embedded-c + freertos_kernel + corejson + coremqtt + coremqtt-agent + tinycbor + freertos-ota-pal-psa + helpers-events + backoff-algorithm + crt-helpers + ) +endif() diff --git a/applications/helpers/ota_orchestrator/inc/mqtt_helpers.h b/applications/helpers/ota_orchestrator/inc/mqtt_helpers.h new file mode 100644 index 00000000..3cf95305 --- /dev/null +++ b/applications/helpers/ota_orchestrator/inc/mqtt_helpers.h @@ -0,0 +1,78 @@ +/* Copyright 2023-2024 Arm Limited and/or its affiliates + * + * SPDX-License-Identifier: MIT + */ + +#ifndef MQTT_HELPERS_H +#define MQTT_HELPERS_H + +/* Standard includes. */ +#include +#include + +/** + * @ingroup ota_enum_types + * @brief The OTA MQTT interface return status. + */ +typedef enum OtaMqttStatus +{ + OtaMqttSuccess = 0, /*!< @brief OTA MQTT interface success. */ + OtaMqttPublishFailed = 0xa0, /*!< @brief Attempt to publish a MQTT message failed. */ + OtaMqttSubscribeFailed, /*!< @brief Failed to subscribe to a topic. */ + OtaMqttUnsubscribeFailed /*!< @brief Failed to unsubscribe from a topic. */ +} OtaMqttStatus_t; + +/** + * @brief Subscribe to a specified topic. + * + * Precondition: pTopicFilter is not null. + * + * @param[in] pTopicFilter The topic filter to subscribe to. + * @param[in] topicFilterLength Length of the topic filter. + * @param[in] ucQoS Quality of Service (QoS), the level of reliability for + * message delivery. Can be 0, 1 or 2. + * + * @return OtaMqttSuccess if the subscribe completed successfully, otherwise + * OtaMqttSubscribeFailed. + */ +OtaMqttStatus_t prvMQTTSubscribe( const char * pTopicFilter, + uint16_t topicFilterLength, + uint8_t ucQoS ); + +/** + * @brief Publish a message to a specified topic. + * * + * @param[in] pacTopic The topic that the message should be published to. + * @param[in] topicLen Length of the topic. + * @param[in] pMsg The message to be published. + * @param[in] msgSize Size of the message. + * @param[in] ucQoS Quality of Service (QoS), the level of reliability for + * message delivery. Can be 0, 1 or 2. + * + * @return OtaMqttSuccess if the subscribe completed successfully, otherwise + * OtaMqttSubscribeFailed. + */ +OtaMqttStatus_t prvMQTTPublish( const char * const pacTopic, + uint16_t topicLen, + const char * pMsg, + uint32_t msgSize, + uint8_t ucQoS ); + +/** + * @brief Unsubscribe from a specified topic. + * + * Precondition: pTopicFilter is not null. + * + * @param[in] pTopicFilter The topic filter to unsubscribe from. + * @param[in] topicFilterLength Length of the topic filter. + * @param[in] ucQoS Quality of Service (QoS), the level of reliability for + * message delivery. Can be 0, 1 or 2. + * + * @return OtaMqttSuccess if the unsubscribe completed successfully, otherwise + * OtaMqttSubscribeFailed. + */ +OtaMqttStatus_t prvMQTTUnsubscribe( const char * pTopicFilter, + uint16_t topicFilterLength, + uint8_t ucQoS ); + +#endif /* MQTT_HELPERS_H */ diff --git a/applications/helpers/ota_orchestrator/inc/ota_appversion32.h b/applications/helpers/ota_orchestrator/inc/ota_appversion32.h new file mode 100644 index 00000000..1e7d349b --- /dev/null +++ b/applications/helpers/ota_orchestrator/inc/ota_appversion32.h @@ -0,0 +1,88 @@ +/* + * AWS IoT Over-the-air Update v3.4.0 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright 2024 Arm Limited and/or its affiliates + * + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file ota_appversion32.h + * @brief Structure to represent the application build version. + */ + +#ifndef IOT_APPVERSION32_H +#define IOT_APPVERSION32_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/* Standard includes. */ +#include + +/** + * @ingroup ota_struct_types + * @brief Application version structure. + * + */ +typedef struct +{ + /* MISRA Ref 19.2.1 [Unions] */ + /* More details at: https://github.com/aws/ota-for-aws-iot-embedded-sdk/blob/main/MISRA.md#rule-192 */ + /* coverity[misra_c_2012_rule_19_2_violation] */ + union + { + #if ( defined( __BYTE_ORDER__ ) && defined( __ORDER_LITTLE_ENDIAN__ ) && ( __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ ) ) || ( __little_endian__ == 1 ) || WIN32 || ( __BYTE_ORDER == __LITTLE_ENDIAN ) + struct version + { + uint16_t build; /*!< @brief Build of the firmware (Z in firmware version Z.Y.X). */ + uint8_t minor; /*!< @brief Minor version number of the firmware (Y in firmware version Z.Y.X). */ + + uint8_t major; /*!< @brief Major version number of the firmware (X in firmware version Z.Y.X). */ + } x; /*!< @brief Version number of the firmware. */ + #elif ( defined( __BYTE_ORDER__ ) && defined( __ORDER_BIG_ENDIAN__ ) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ ) || ( __big_endian__ == 1 ) || ( __BYTE_ORDER == __BIG_ENDIAN ) + struct version + { + uint8_t major; /*!< @brief Major version number of the firmware (X in firmware version X.Y.Z). */ + uint8_t minor; /*!< @brief Minor version number of the firmware (Y in firmware version X.Y.Z). */ + + uint16_t build; /*!< @brief Build of the firmware (Z in firmware version X.Y.Z). */ + } x; /*!< @brief Version number of the firmware. */ + #else /* if ( defined( __BYTE_ORDER__ ) && defined( __ORDER_LITTLE_ENDIAN__ ) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ ) || ( __little_endian__ == 1 ) || WIN32 || ( __BYTE_ORDER == __LITTLE_ENDIAN ) */ + #error "Unable to determine byte order!" + #endif /* if ( defined( __BYTE_ORDER__ ) && defined( __ORDER_LITTLE_ENDIAN__ ) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ ) || ( __little_endian__ == 1 ) || WIN32 || ( __BYTE_ORDER == __LITTLE_ENDIAN ) */ + uint32_t unsignedVersion32; + int32_t signedVersion32; + } u; /*!< @brief Version based on configuration in big endian or little endian. */ +} AppVersion32_t; + +extern AppVersion32_t appFirmwareVersion; /*!< @brief Making the version number available globally through external linkage. */ + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } +#endif +/* *INDENT-ON* */ + +#endif /* ifndef IOT_APPVERSION32_H */ diff --git a/applications/helpers/ota_orchestrator/inc/ota_orchestrator_helpers.h b/applications/helpers/ota_orchestrator/inc/ota_orchestrator_helpers.h new file mode 100644 index 00000000..b6217e13 --- /dev/null +++ b/applications/helpers/ota_orchestrator/inc/ota_orchestrator_helpers.h @@ -0,0 +1,51 @@ +/* + * Copyright Amazon.com, Inc. and its affiliates. All Rights Reserved. + * Copyright 2024 Arm Limited and/or its affiliates + * + * + * SPDX-License-Identifier: MIT + * Licensed under the MIT License. See the LICENSE accompanying this file + * for the specific language governing permissions and limitations under + * the License. + */ + +#ifndef OTA_ORCHESTRATOR_HELPERS_H +#define OTA_ORCHESTRATOR_HELPERS_H + +/* Standard includes. */ +#include +#include + +#include "job_parser.h" + +/** + * @brief Convert a job document signature into DER format. + * + * @param[in] dest The destination buffer to be populated with the decoded + * signature + * @param[in] destLength Length of the dest buffer. + * @param[in] jobFields Pointer to the structure holding the job document + * parameters, which will be populated with the decoded signature. + * + * @return true if the signature was successfully decoded, otherwise false. + */ +bool convertSignatureToDER( uint8_t * dest, + size_t destLength, + AfrOtaJobDocumentFields_t * jobFields ); + +/** + * @brief Parse the job document to extract the parameters needed to download + * the new firmware. + * + * @param[in] message The jobs message received from AWS IoT core. + * @param[in] messageLength Length of the message. + * @param[in] jobFields Pointer to the structure to be populated with the job + * document fields. + * + * @return true if all files processed, otherwise false. + */ +bool jobDocumentParser( char * message, + size_t messageLength, + AfrOtaJobDocumentFields_t * jobFields ); + +#endif /* OTA_ORCHESTRATOR_HELPERS_H */ diff --git a/applications/helpers/ota_orchestrator/inc/ota_os_freertos.h b/applications/helpers/ota_orchestrator/inc/ota_os_freertos.h new file mode 100644 index 00000000..c97e9aa0 --- /dev/null +++ b/applications/helpers/ota_orchestrator/inc/ota_os_freertos.h @@ -0,0 +1,104 @@ +/* + * Copyright Amazon.com, Inc. and its affiliates. All Rights Reserved. + * Copyright 2024 Arm Limited and/or its affiliates + * + * + * SPDX-License-Identifier: MIT + * Licensed under the MIT License. See the LICENSE accompanying this file + * for the specific language governing permissions and limitations under + * the License. + */ + +/** + * @file ota_os_freertos.h + * @brief Function declarations for the example OTA OS Functional interface for + * FreeRTOS. + */ + +#ifndef _OTA_OS_FREERTOS_H_ +#define _OTA_OS_FREERTOS_H_ + +/* Standard library includes. */ +#include +#include +#include +#include + +/** + * @ingroup ota_enum_types + * @brief The OTA OS interface return status. + */ +typedef enum OtaOsStatus +{ + OtaOsSuccess = 0, /*!< @brief OTA OS interface success. */ + OtaOsEventQueueCreateFailed = 0x80U, /*!< @brief Failed to create the event + * queue. */ + OtaOsEventQueueSendFailed, /*!< @brief Posting event message to the event + * queue failed. */ + OtaOsEventQueueReceiveFailed, /*!< @brief Failed to receive from the event + * queue. */ + OtaOsEventQueueDeleteFailed, /*!< @brief Failed to delete the event queue. + */ +} OtaOsStatus_t; + +/** + * @brief Initialize the OTA events. + * + * This function initializes the OTA events mechanism for freeRTOS platforms. + * + * @param[pEventCtx] Pointer to the OTA event context. + * + * @return OtaOsStatus_t, OtaOsSuccess if success , other error + * code on failure. + */ +OtaOsStatus_t OtaInitEvent_FreeRTOS(); + +/** + * @brief Sends an OTA event. + * + * This function sends an event to OTA library event handler on FreeRTOS + * platforms. + * + * @param[pEventCtx] Pointer to the OTA event context. + * + * @param[pEventMsg] Event to be sent to the OTA handler. + * + * @param[timeout] The maximum amount of time (msec) the task should + * block. + * + * @return OtaOsStatus_t, OtaOsSuccess if success , other error + * code on failure. + */ +OtaOsStatus_t OtaSendEvent_FreeRTOS( const void * pEventMsg ); + +/** + * @brief Receive an OTA event. + * + * This function receives next event from the pending OTA events on FreeRTOS + * platforms. + * + * @param[pEventCtx] Pointer to the OTA event context. + * + * @param[pEventMsg] Pointer to store message. + * + * @param[timeout] The maximum amount of time the task should block. + * + * @return OtaOsStatus_t, OtaOsSuccess if success , other error + * code on failure. + */ +OtaOsStatus_t OtaReceiveEvent_FreeRTOS( void * pEventMsg ); + +/** + * @brief Deinitialize the OTA Events mechanism. + * + * This function deinitialize the OTA events mechanism and frees any resources + * used on FreeRTOS platforms. + * + * @param[pEventCtx] Pointer to the OTA event context. + * + * @return OtaOsStatus_t, OtaOsSuccess if success , other error + * code on failure. + */ +void OtaDeinitEvent_FreeRTOS(); + +#endif /* ifndef _OTA_OS_FREERTOS_H_ */ diff --git a/applications/helpers/ota_orchestrator/inc/ota_register_callback.h b/applications/helpers/ota_orchestrator/inc/ota_register_callback.h new file mode 100644 index 00000000..085738d0 --- /dev/null +++ b/applications/helpers/ota_orchestrator/inc/ota_register_callback.h @@ -0,0 +1,22 @@ +/* Copyright 2023-2024 Arm Limited and/or its affiliates + * + * SPDX-License-Identifier: MIT + */ + +#ifndef OTA_REGISTER_CALLBACK_H +#define OTA_REGISTER_CALLBACK_H + +/* Standard includes. */ +#include +#include + +/** + * @brief Register OTA callbacks with the subscription manager. + * + * @param[in] pTopicFilter The topic filter for which a callback needs to be registered for. + * @param[in] topicFilterLength Length of the topic filter. + */ +void prvRegisterOTACallback( const char * pTopicFilter, + uint16_t topicFilterLength ); + +#endif /* OTA_REGISTER_CALLBACK_H */ diff --git a/applications/helpers/ota_orchestrator/inc/ota_types_definitions.h b/applications/helpers/ota_orchestrator/inc/ota_types_definitions.h new file mode 100644 index 00000000..6db9ae04 --- /dev/null +++ b/applications/helpers/ota_orchestrator/inc/ota_types_definitions.h @@ -0,0 +1,152 @@ +/* + * Copyright Amazon.com, Inc. and its affiliates. All Rights Reserved. + * Copyright 2023-2024 Arm Limited and/or its affiliates + * + * + * SPDX-License-Identifier: MIT + * Licensed under the MIT License. See the LICENSE accompanying this file + * for the specific language governing permissions and limitations under + * the License. + */ + +#ifndef OTA_TYPES_DEFINITIONS_H +#define OTA_TYPES_DEFINITIONS_H + +/* Standard includes. */ +#include +#include +#include + +#include "MQTTFileDownloader.h" + +#include "demo_config.h" +#include "MQTTFileDownloader_config.h" + +/** + * @brief The maximum time for which OTA demo waits for an MQTT operation to be complete. + * This involves receiving an acknowledgment for broker for SUBSCRIBE, UNSUBSCRIBE and non + * QOS0 publishes. + */ +#define otaexampleMQTT_TIMEOUT_MS ( 5000U ) + +/** + * @brief The maximum size of the file paths used in the demo. + */ +#define otaexampleMAX_FILE_PATH_SIZE ( 260 ) + +/** + * @brief The maximum size of the stream name required for downloading update file + * from streaming service. + */ +#define otaexampleMAX_STREAM_NAME_SIZE ( 128 ) + +/** + * @brief The delay used in the OTA demo task to periodically output the OTA + * statistics like number of packets received, dropped, processed and queued per connection. + */ +#define otaexampleTASK_DELAY_MS ( 10000U ) + +/** + * @brief Used to clear bits in a task's notification value. + */ +#define otaexampleMAX_UINT32 ( 0xffffffff ) + +/** + * @brief Starting index of client identifier within OTA topic. + */ +/* #define OTA_TOPIC_CLIENT_IDENTIFIER_START_IDX ( 12U ) */ + +/** + * @brief Stack size required for OTA agent task. + */ +#define OTA_AGENT_TASK_STACK_SIZE ( 5000U ) + +/** + * @brief Priority required for OTA agent task. + */ +#define OTA_AGENT_TASK_PRIORITY ( tskIDLE_PRIORITY + 1 ) + +/* Max bytes supported for a file signature (3072 bit RSA is 384 bytes). */ +#define OTA_MAX_SIGNATURE_SIZE ( 384U ) + +/** + * @brief The timeout for waiting for the agent to get suspended after closing the + * connection. + * + * Timeout value should be large enough for OTA agent to finish any pending MQTT operations + * and suspend itself. + * + */ +#define OTA_SUSPEND_TIMEOUT_MS ( 10000U ) + +#define OTA_DATA_BLOCK_SIZE mqttFileDownloader_CONFIG_BLOCK_SIZE +#define JOB_DOC_SIZE 2048U + +typedef enum OtaEvent +{ + OtaAgentEventStart = 0, /*!< @brief Start the OTA state machine */ + OtaAgentEventRequestJobDocument, /*!< @brief Event for requesting job document. */ + OtaAgentEventReceivedJobDocument, /*!< @brief Event when job document is received. */ + OtaAgentEventCreateFile, /*!< @brief Event to create a file. */ + OtaAgentEventRequestFileBlock, /*!< @brief Event to request file blocks. */ + OtaAgentEventReceivedFileBlock, /*!< @brief Event to trigger when file block is received. */ + OtaAgentEventCloseFile, /*!< @brief Event to trigger closing file. */ + OtaAgentEventActivateImage, /*!< @brief Event to trigger activation of the image. */ + OtaAgentEventSuspend, /*!< @brief Event to suspend ota task */ + OtaAgentEventResume, /*!< @brief Event to resume suspended task */ + OtaAgentEventUserAbort, /*!< @brief Event triggered by user to stop agent. */ + OtaAgentEventShutdown, /*!< @brief Event to trigger ota shutdown */ + OtaAgentEventMax /*!< @brief Last event specifier */ +} OtaEvent_t; + +/** + * @brief OTA Agent states. + * + * The current state of the OTA Task (OTA Agent). + */ +typedef enum OtaState +{ + OtaAgentStateNoTransition = -1, + OtaAgentStateInit = 0, + OtaAgentStateReady, + OtaAgentStateRequestingJob, + OtaAgentStateWaitingForJob, + OtaAgentStateCreatingFile, + OtaAgentStateRequestingFileBlock, + OtaAgentStateWaitingForFileBlock, + OtaAgentStateClosingFile, + OtaAgentStateSuspended, + OtaAgentStateShuttingDown, + OtaAgentStateStopped, + OtaAgentStateAll +} OtaState_t; + +/** + * @brief The OTA Agent event and data structures. + */ + +typedef struct OtaDataEvent +{ + uint8_t data[ OTA_DATA_BLOCK_SIZE * 2 ]; /*!< Buffer for storing event information. */ + size_t dataLength; /*!< Total space required for the event. */ + bool bufferUsed; /*!< Flag set when buffer is used otherwise cleared. */ +} OtaDataEvent_t; + +typedef struct OtaJobEventData +{ + uint8_t jobData[ JOB_DOC_SIZE ]; + size_t jobDataLength; +} OtaJobEventData_t; + +/** + * @brief Stores information about the event message. + * + */ +typedef struct OtaEventMsg +{ + OtaDataEvent_t * dataEvent; /*!< Data Event message. */ + OtaJobEventData_t * jobEvent; /*!< Job Event message. */ + OtaEvent_t eventId; /*!< Identifier for the event. */ +} OtaEventMsg_t; + +#endif /* OTA_TYPES_DEFINITIONS_H */ diff --git a/applications/helpers/ota_orchestrator/src/mqtt_helpers.c b/applications/helpers/ota_orchestrator/src/mqtt_helpers.c new file mode 100644 index 00000000..53fcb08e --- /dev/null +++ b/applications/helpers/ota_orchestrator/src/mqtt_helpers.c @@ -0,0 +1,280 @@ +/* Copyright 2023-2024 Arm Limited and/or its affiliates + * + * SPDX-License-Identifier: MIT + */ + +/* Standard includes. */ +#include +#include +#include + +#include "mqtt_agent_task.h" + +#include "mqtt_helpers.h" +#include "ota_register_callback.h" +#include "ota_types_definitions.h" + +/* Provides external linkage only when running unit test */ +#ifdef UNIT_TESTING + #define STATIC /* as nothing */ +#else /* ifdef UNIT_TESTING */ + #define STATIC static +#endif /* UNIT_TESTING */ + +/** + * @brief The MQTT agent manages the MQTT contexts. It will set this handle to + * the context used by this demo. + */ +extern MQTTAgentContext_t xGlobalMqttAgentContext; + +STATIC void prvMQTTSubscribeCompleteCallback( MQTTAgentCommandContext_t * pxCommandContext, + MQTTAgentReturnInfo_t * pxReturnInfo ) +{ + if( pxReturnInfo->returnCode == MQTTSuccess ) + { + MQTTAgentSubscribeArgs_t * pSubscribeArgs = ( MQTTAgentSubscribeArgs_t * ) ( pxCommandContext->pArgs ); + prvRegisterOTACallback( pSubscribeArgs->pSubscribeInfo->pTopicFilter, pSubscribeArgs->pSubscribeInfo->topicFilterLength ); + } + + /* Store the result in the application defined context so the task that + * initiated the publish can check the operation's status. */ + pxCommandContext->xReturnStatus = pxReturnInfo->returnCode; + + if( pxCommandContext->xTaskToNotify != NULL ) + { + /* Send the context's ulNotificationValue as the notification value so + * the receiving task can check the value it set in the context matches + * the value it receives in the notification. */ + xTaskNotify( pxCommandContext->xTaskToNotify, ( uint32_t ) ( pxReturnInfo->returnCode ), eSetValueWithOverwrite ); + } +} + +STATIC void prvOTAPublishCommandCallback( MQTTAgentCommandContext_t * pxCommandContext, + MQTTAgentReturnInfo_t * pxReturnInfo ) +{ + pxCommandContext->xReturnStatus = pxReturnInfo->returnCode; + + if( pxCommandContext->xTaskToNotify != NULL ) + { + xTaskNotify( pxCommandContext->xTaskToNotify, ( uint32_t ) ( pxReturnInfo->returnCode ), eSetValueWithOverwrite ); + } +} + +STATIC void prvMQTTUnsubscribeCompleteCallback( MQTTAgentCommandContext_t * pxCommandContext, + MQTTAgentReturnInfo_t * pxReturnInfo ) +{ + /* Store the result in the application defined context so the task that + * initiated the publish can check the operation's status. */ + pxCommandContext->xReturnStatus = pxReturnInfo->returnCode; + + if( pxCommandContext->xTaskToNotify != NULL ) + { + /* Send the context's ulNotificationValue as the notification value so + * the receiving task can check the value it set in the context matches + * the value it receives in the notification. */ + xTaskNotify( pxCommandContext->xTaskToNotify, ( uint32_t ) ( pxReturnInfo->returnCode ), eSetValueWithOverwrite ); + } +} + +OtaMqttStatus_t prvMQTTSubscribe( const char * pTopicFilter, + uint16_t topicFilterLength, + uint8_t ucQoS ) +{ + MQTTStatus_t mqttStatus; + uint32_t ulNotifiedValue; + static MQTTAgentSubscribeArgs_t xSubscribeArgs = { 0 }; + static MQTTSubscribeInfo_t xSubscribeInfo = { 0 }; + BaseType_t result; + static MQTTAgentCommandInfo_t xCommandParams = { 0 }; + static MQTTAgentCommandContext_t xApplicationDefinedContext = { 0 }; + OtaMqttStatus_t otaRet = OtaMqttSuccess; + + configASSERT( pTopicFilter != NULL ); + configASSERT( topicFilterLength > 0 ); + + xSubscribeInfo.pTopicFilter = pTopicFilter; + xSubscribeInfo.topicFilterLength = topicFilterLength; + xSubscribeInfo.qos = ucQoS; + xSubscribeArgs.pSubscribeInfo = &xSubscribeInfo; + xSubscribeArgs.numSubscriptions = 1; + + xApplicationDefinedContext.xTaskToNotify = xTaskGetCurrentTaskHandle(); + xApplicationDefinedContext.pArgs = &xSubscribeArgs; + xApplicationDefinedContext.xReturnStatus = MQTTSendFailed; + + xCommandParams.blockTimeMs = otaexampleMQTT_TIMEOUT_MS; + xCommandParams.cmdCompleteCallback = prvMQTTSubscribeCompleteCallback; + xCommandParams.pCmdCompleteCallbackContext = ( void * ) &xApplicationDefinedContext; + + xTaskNotifyStateClear( NULL ); + + mqttStatus = MQTTAgent_Subscribe( &xGlobalMqttAgentContext, + &xSubscribeArgs, + &xCommandParams ); + + /* Wait for command to complete so MQTTSubscribeInfo_t remains in scope for the + * duration of the command. */ + if( mqttStatus == MQTTSuccess ) + { + result = xTaskNotifyWait( 0, otaexampleMAX_UINT32, &ulNotifiedValue, pdMS_TO_TICKS( otaexampleMQTT_TIMEOUT_MS ) ); + + if( result == pdTRUE ) + { + mqttStatus = xApplicationDefinedContext.xReturnStatus; + } + else + { + mqttStatus = MQTTRecvFailed; + } + } + + if( mqttStatus != MQTTSuccess ) + { + LogError( ( "Failed to SUBSCRIBE to topic with error = %u.", + mqttStatus ) ); + otaRet = OtaMqttSubscribeFailed; + } + else + { + LogInfo( ( "Subscribed to topic %.*s.\n", + topicFilterLength, + pTopicFilter ) ); + otaRet = OtaMqttSuccess; + } + + return otaRet; +} + +OtaMqttStatus_t prvMQTTPublish( const char * const pacTopic, + uint16_t topicLen, + const char * pMsg, + uint32_t msgSize, + uint8_t ucQoS ) +{ + BaseType_t result; + MQTTStatus_t mqttStatus = MQTTBadParameter; + static MQTTPublishInfo_t publishInfo = { 0 }; + static MQTTAgentCommandInfo_t xCommandParams = { 0 }; + static MQTTAgentCommandContext_t xCommandContext = { 0 }; + OtaMqttStatus_t otaRet = OtaMqttSuccess; + + publishInfo.pTopicName = pacTopic; + publishInfo.topicNameLength = topicLen; + publishInfo.qos = ucQoS; + publishInfo.pPayload = pMsg; + publishInfo.payloadLength = msgSize; + + xCommandContext.xTaskToNotify = xTaskGetCurrentTaskHandle(); + xTaskNotifyStateClear( NULL ); + + xCommandParams.blockTimeMs = otaexampleMQTT_TIMEOUT_MS; + xCommandParams.cmdCompleteCallback = prvOTAPublishCommandCallback; + xCommandParams.pCmdCompleteCallbackContext = ( void * ) &xCommandContext; + + mqttStatus = MQTTAgent_Publish( &xGlobalMqttAgentContext, + &publishInfo, + &xCommandParams ); + + /* Wait for command to complete so MQTTSubscribeInfo_t remains in scope for the + * duration of the command. */ + if( mqttStatus == MQTTSuccess ) + { + result = xTaskNotifyWait( 0, otaexampleMAX_UINT32, NULL, pdMS_TO_TICKS( otaexampleMQTT_TIMEOUT_MS ) ); + + if( result != pdTRUE ) + { + mqttStatus = MQTTSendFailed; + } + else + { + mqttStatus = xCommandContext.xReturnStatus; + } + } + + if( mqttStatus != MQTTSuccess ) + { + LogError( ( "Failed to send PUBLISH packet to broker with error = %u.", mqttStatus ) ); + otaRet = OtaMqttPublishFailed; + } + else + { + LogInfo( ( "Sent PUBLISH packet to broker %.*s to broker.\n", + topicLen, + pacTopic ) ); + otaRet = OtaMqttSuccess; + } + + return otaRet; +} + +OtaMqttStatus_t prvMQTTUnsubscribe( const char * pTopicFilter, + uint16_t topicFilterLength, + uint8_t ucQoS ) +{ + MQTTStatus_t mqttStatus; + uint32_t ulNotifiedValue; + static MQTTAgentSubscribeArgs_t xSubscribeArgs = { 0 }; + static MQTTSubscribeInfo_t xSubscribeInfo = { 0 }; + BaseType_t result; + static MQTTAgentCommandInfo_t xCommandParams = { 0 }; + static MQTTAgentCommandContext_t xApplicationDefinedContext = { 0 }; + OtaMqttStatus_t otaRet = OtaMqttSuccess; + + configASSERT( pTopicFilter != NULL ); + configASSERT( topicFilterLength > 0 ); + + xSubscribeInfo.pTopicFilter = pTopicFilter; + xSubscribeInfo.topicFilterLength = topicFilterLength; + xSubscribeInfo.qos = ucQoS; + xSubscribeArgs.pSubscribeInfo = &xSubscribeInfo; + xSubscribeArgs.numSubscriptions = 1; + + + xApplicationDefinedContext.xTaskToNotify = xTaskGetCurrentTaskHandle(); + + xCommandParams.blockTimeMs = otaexampleMQTT_TIMEOUT_MS; + xCommandParams.cmdCompleteCallback = prvMQTTUnsubscribeCompleteCallback; + xCommandParams.pCmdCompleteCallbackContext = ( void * ) &xApplicationDefinedContext; + + LogInfo( ( " Unsubscribing to topic filter: %s", pTopicFilter ) ); + xTaskNotifyStateClear( NULL ); + + + mqttStatus = MQTTAgent_Unsubscribe( &xGlobalMqttAgentContext, + &xSubscribeArgs, + &xCommandParams ); + + /* Wait for command to complete so MQTTSubscribeInfo_t remains in scope for the + * duration of the command. */ + if( mqttStatus == MQTTSuccess ) + { + result = xTaskNotifyWait( 0, otaexampleMAX_UINT32, &ulNotifiedValue, pdMS_TO_TICKS( otaexampleMQTT_TIMEOUT_MS ) ); + + if( result == pdTRUE ) + { + mqttStatus = xApplicationDefinedContext.xReturnStatus; + } + else + { + mqttStatus = MQTTRecvFailed; + } + } + + if( mqttStatus != MQTTSuccess ) + { + LogError( ( "Failed to UNSUBSCRIBE from topic %.*s with error = %u.", + topicFilterLength, + pTopicFilter, + mqttStatus ) ); + otaRet = OtaMqttUnsubscribeFailed; + } + else + { + LogInfo( ( "UNSUBSCRIBED from topic %.*s.\n", + topicFilterLength, + pTopicFilter ) ); + otaRet = OtaMqttSuccess; + } + + return otaRet; +} diff --git a/applications/helpers/ota_orchestrator/src/ota_orchestrator.c b/applications/helpers/ota_orchestrator/src/ota_orchestrator.c new file mode 100644 index 00000000..51364f7f --- /dev/null +++ b/applications/helpers/ota_orchestrator/src/ota_orchestrator.c @@ -0,0 +1,1157 @@ +/* + * Copyright Amazon.com, Inc. and its affiliates. All Rights Reserved. + * Copyright 2023-2024 Arm Limited and/or its affiliates + * + * + * SPDX-License-Identifier: MIT + * Licensed under the MIT License. See the LICENSE accompanying this file + * for the specific language governing permissions and limitations under + * the License. + */ + +/* Standard includes. */ +#include +#include +#include +#include + +#include "app_config.h" + +#include "app_strnlen.h" + +#include "mqtt_agent_task.h" +#include "events.h" + +/* Includes for TF-M */ +#include "psa/update.h" + +/* Includes for OTA PAL PSA */ +#include "version/application_version.h" + +/* Kernel includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" + +/* Demo config includes. */ +#include "demo_config.h" +#include "iot_default_root_certificates.h" +#include "MQTTFileDownloader_config.h" + +/* Library config includes. */ +#include "ota_config.h" + +/* Subscription manager header include. */ +#include "subscription_manager.h" + +/* Jobs Library includes. */ +#include "jobs.h" +#include "ota_job_processor.h" + +/* MQTT File Streams Library includes */ +#include "MQTTFileDownloader.h" +#include "MQTTFileDownloader_base64.h" + +/* OTA Library Interface include. */ +#include "ota_os_freertos.h" + +/* Include firmware version struct definition. */ +#include "ota_appversion32.h" + +/* Include platform abstraction header. */ +#include "ota_pal.h" + +/* OTA orchestrator includes*/ +#include "mqtt_helpers.h" +#include "ota_config.h" +#include "ota_orchestrator_helpers.h" +#include "ota_register_callback.h" +#include "ota_types_definitions.h" + +#define NUM_OF_BLOCKS_REQUESTED 1U +#define START_JOB_MSG_LENGTH 147U +#define MAX_JOB_ID_LENGTH 64U +#define UPDATE_JOB_MSG_LENGTH 48U + +extern void vOtaNotActiveHook( void ); +extern void vOtaActiveHook( void ); + +/* Provides external linkage only when running unit test */ +#ifdef UNIT_TESTING + #define STATIC /* as nothing */ +#else /* ifdef UNIT_TESTING */ + #define STATIC static +#endif /* UNIT_TESTING */ + +/* -------------------- Demo configurations ------------------------- */ + +/** + * @brief The name of the AWS Thing which will be updated with the new firmware + * image. + */ +#define OTA_THING_NAME clientcredentialIOT_THING_NAME + +/** + * @brief The common prefix for all OTA topics. + * + * Thing name is substituted with a wildcard symbol `+`. OTA agent + * registers with MQTT broker with the thing name in the topic. This topic + * filter is used to match incoming packet received and route them to OTA. + * Thing name is not needed for this matching. + */ +#define OTA_TOPIC_PREFIX "$aws/things/+/" + +/** + * @brief Wildcard topic filter for job notification. + * The filter is used to match the constructed job notify topic filter from OTA agent and register + * appropriate callback for it. + */ +#define OTA_JOB_NOTIFY_TOPIC_FILTER OTA_TOPIC_PREFIX "jobs/start-next" + +/** + * @brief Length of job notification topic filter. + */ +#define OTA_JOB_NOTIFY_TOPIC_FILTER_LENGTH ( ( uint16_t ) ( sizeof( OTA_JOB_NOTIFY_TOPIC_FILTER ) - 1 ) ) + +/** + * @brief Wildcard topic filter for matching job response messages. + * This topic filter is used to match the responses from OTA service for OTA agent job requests. THe + * topic filter is a reserved topic which is not subscribed with MQTT broker. + * + */ +#define OTA_JOB_ACCEPTED_RESPONSE_TOPIC_FILTER OTA_TOPIC_PREFIX "jobs/start-next/accepted" + +/** + * @brief Length of job accepted response topic filter. + */ +#define OTA_JOB_ACCEPTED_RESPONSE_TOPIC_FILTER_LENGTH ( ( uint16_t ) ( sizeof( OTA_JOB_ACCEPTED_RESPONSE_TOPIC_FILTER ) - 1 ) ) + +/** + * @brief Wildcard topic filter for matching OTA data packets. + * The filter is used to match the constructed data stream topic filter from OTA agent and register + * appropriate callback for it. + */ +#define OTA_DATA_STREAM_TOPIC_FILTER OTA_TOPIC_PREFIX "streams/#" + +/** + * @brief Length of data stream topic filter. + */ +#define OTA_DATA_STREAM_TOPIC_FILTER_LENGTH ( ( uint16_t ) ( sizeof( OTA_DATA_STREAM_TOPIC_FILTER ) - 1 ) ) + +/** + * @brief Default topic filter for OTA. + * This is used to route all the packets for OTA reserved topics which OTA agent has not subscribed for. + */ +#define OTA_DEFAULT_TOPIC_FILTER OTA_TOPIC_PREFIX "jobs/#" + +/** + * @brief Length of default topic filter. + */ +#define OTA_DEFAULT_TOPIC_FILTER_LENGTH ( ( uint16_t ) ( sizeof( OTA_DEFAULT_TOPIC_FILTER ) - 1 ) ) + +/* -------------------------------------------------------------------------- */ + +/** + * @brief Structure used to store the topic filter to ota callback mappings. + */ +typedef struct OtaTopicFilterCallback +{ + const char * pTopicFilter; + uint16_t topicFilterLength; + IncomingPubCallback_t callback; +} OtaTopicFilterCallback_t; + +/* -------------------------------------------------------------------------- */ + +/** + * @brief The current state of the OTA agent. + */ +static OtaState_t otaAgentState = OtaAgentStateInit; + +/** + * @brief The job ID of the OTA job currently being processed. + */ +char globalJobId[ MAX_JOB_ID_LENGTH ] = { 0 }; + +/** + * @brief Structure used to hold parameters of a received job document. + */ +static AfrOtaJobDocumentFields_t jobFields = { 0 }; + +/** + * @brief Structure used to hold data from a job document. + */ +static OtaJobEventData_t jobDocBuffer = { 0 }; + +/** + * @brief Structure used to hold information about a MQTT file stream. + */ +MqttFileDownloaderContext_t mqttFileDownloaderContext = { 0 }; + +/** + * @brief The file ID of the current file being downloaded. + */ +static uint8_t currentFileId = 0; + + +/** + * @brief Index of the current file block that is being streamed. + */ +static uint32_t currentBlockOffset = 0; + +/** + * @brief Number of file blocks left to be streamed. + */ +static uint32_t numOfBlocksRemaining = 0; + +/** + * @brief Number of bytes that have been received of the file being downloaded. + */ +static uint32_t totalBytesReceived = 0; + +/** + * @brief A statically allocated array of data buffers used by the OTA agent. + * Maximum number of buffers are determined by how many chunks are requested + * by OTA agent at a time along with an extra buffer to handle the control + * message. The size of each buffer is determined by the maximum size of the + * firmware image chunk, with space for other metadata sent along with the + * chunk. + */ +static OtaDataEvent_t dataBuffers[ otaconfigMAX_NUM_OTA_DATA_BUFFERS ] = { 0 }; + +/** + * @brief Mutex used to manage thread safe access of OTA event buffers. + */ +static SemaphoreHandle_t xBufferSemaphore; + +/** + * @brief Buffer to hold the decoded signature from the job document. + */ +static uint8_t OtaImageSignatureDecoded[ OTA_MAX_SIGNATURE_SIZE ] = { 0 }; + +/* -------------------------------------------------------------------------- */ + +/** + * @brief Put the OTA agent into the Stopped state. + */ +STATIC void otaAgentShutdown( void ); + +/** + * @brief Close the file that is currently being downloaded. + * + * @return true if the file was closed successfully, otherwise returns false. + */ +STATIC bool closeFile( void ); + +/** + * @brief Activate the downloaded firmware image. + * + * @return true if the image was activated successfully, otherwise returns + * false. + */ +STATIC bool activateImage( void ); + +/** + * @brief Send a message to notify that the firmware image was accepted. + */ +STATIC bool sendSuccessMessage( void ); + +/** + * @brief Send the necessary message to request a job document. + */ +STATIC void requestJobDocumentHandler( void ); + +/** + * @brief Process a job document by parsing its parameters and beginning the + * file streaming. + * + * @param[in] jobDoc Pointer to the job document data. + * + * @return The OTA platform image state after parsing the job document. + */ +STATIC OtaPalJobDocProcessingResult_t receivedJobDocumentHandler( OtaJobEventData_t * jobDoc ); + +/** + * @brief Initialize the MQTT streams downloader. + * + * @param[in] jobFields Pointer to the parameters extracted from the OTA job + * document. + */ +STATIC void initMqttDownloader( AfrOtaJobDocumentFields_t * jobFields ); + +/** + * @brief Free an event buffer back to pool + * + * OTA demo uses a statically allocated array of fixed size event buffers . The + * number of event buffers is configured by the param otaconfigMAX_NUM_OTA_DATA_BUFFERS + * within ota_config.h. The function is used by the OTA application callback to free a buffer, + * after OTA agent has completed processing with the event. The access to the pool is made thread safe + * using a mutex. + * + * @param[in] pxBuffer Pointer to the buffer to be freed. + */ +STATIC void freeOtaDataEventBuffer( OtaDataEvent_t * const pxBuffer ); + +/** + * @brief + * + * @param[in] pData Pointer to the received data block. + * @param[in] dataLength Length of the received data block. + * + * @return The number of bytes written successfully, or a negative error code from the platform + * abstraction layer. + */ +STATIC int16_t handleMqttStreamsBlockArrived( uint8_t * pData, + size_t dataLength ); + +/** + * @brief Request the next data block from the file being streamed. + */ +STATIC void requestDataBlock( void ); + +/** + * @brief Fetch an unused OTA event buffer from the pool. + * + * Demo uses a simple statically allocated array of fixed size event buffers. The + * number of event buffers is configured by the param otaconfigMAX_NUM_OTA_DATA_BUFFERS + * within ota_config.h. This function is used to fetch a free buffer from the pool for processing + * by the OTA agent task. It uses a mutex for thread safe access to the pool. + * + * @return A pointer to an unusued buffer. NULL if there are no buffers available. + */ +STATIC OtaDataEvent_t * getOtaDataEventBuffer( void ); + +/** + * @brief Receive an OTA event from the event queue and process it based on the + * type of event. + */ +STATIC void processOTAEvents( void ); + +/** + * @brief The function which runs the OTA agent task. + * + * The function runs the OTA Agent Event processing loop, which waits for + * any events for OTA agent and process them. The loop never returns until the OTA agent + * is shutdown. The tasks exits gracefully by freeing up all resources in the event of an + * OTA agent shutdown. + * + * @param[in] pvParam Any parameters to be passed to OTA agent task. + */ +STATIC void prvOTAAgentTask( void * pvParam ); + +/** + * @brief The function which runs the OTA demo task. + * + * The demo task initializes the OTA agent an loops until OTA agent is shutdown. + * It reports OTA update statistics (which includes number of blocks received, processed and dropped), + * at regular intervals. + * + * @param[in] pvParam Any parameters to be passed to OTA Demo task. + */ +STATIC void vOtaDemoTask( void * pvParam ); + +/** + * @brief The function which implements the flow for OTA demo. + * + * @return pdPASS if success or pdFAIL. + */ +STATIC BaseType_t prvRunOTADemo( void ); + +/** + * @brief Callback registered with the OTA library that notifies the OTA agent + * of an incoming PUBLISH containing a job document. + * + * @param[in] pContext MQTT context which stores the connection. + * @param[in] pPublishInfo MQTT packet information which stores details of the + * job document. + */ +STATIC void prvMqttJobCallback( void * pContext, + MQTTPublishInfo_t * pPublish ); + + +/** + * @brief Callback that notifies the OTA library when a data block is received. + * + * @param[in] pContext MQTT context which stores the connection. + * @param[in] pPublishInfo MQTT packet that stores the information of the file block. + */ +STATIC void prvMqttDataCallback( void * pContext, + MQTTPublishInfo_t * pPublish ); + +/** + * @brief Default callback used to receive unsolicited messages for OTA. + * + * The callback is not subscribed with MQTT broker, but only with local subscription manager. + * A wildcard OTA job topic is used for subscription so that all unsolicited messages related to OTA is + * forwarded to this callback for filtration. Right now the callback is used to filter responses to job requests + * from the OTA service. + * + * @param[in] pvIncomingPublishCallbackContext MQTT context which stores the connection. + * @param[in] pPublishInfo MQTT packet that stores the information of the file block. + */ +STATIC void prvMqttDefaultCallback( void * pvIncomingPublishCallbackContext, + MQTTPublishInfo_t * pxPublishInfo ); + +/** + * @brief Registry for all MQTT topic filters to their corresponding callbacks for OTA. + */ +static OtaTopicFilterCallback_t otaTopicFilterCallbacks[] = +{ + { + .pTopicFilter = OTA_JOB_NOTIFY_TOPIC_FILTER, + .topicFilterLength = OTA_JOB_NOTIFY_TOPIC_FILTER_LENGTH, + .callback = prvMqttJobCallback + }, + { + .pTopicFilter = OTA_DATA_STREAM_TOPIC_FILTER, + .topicFilterLength = OTA_DATA_STREAM_TOPIC_FILTER_LENGTH, + .callback = prvMqttDataCallback + }, + { + .pTopicFilter = OTA_DEFAULT_TOPIC_FILTER, + .topicFilterLength = OTA_DEFAULT_TOPIC_FILTER_LENGTH, + .callback = prvMqttDefaultCallback + } +}; + +/* -------------------------------------------------------------------------- */ + +/* + * Helper functions + */ + +STATIC void otaAgentShutdown( void ) +{ + OtaEventMsg_t eventMsg = { 0 }; + + eventMsg.eventId = OtaAgentEventShutdown; + + OtaSendEvent_FreeRTOS( &eventMsg ); +} + +STATIC bool closeFile( void ) +{ + return otaPal_CloseFile( &jobFields ); +} + +STATIC bool activateImage( void ) +{ + return otaPal_ActivateNewImage( &jobFields ); +} + +STATIC bool sendSuccessMessage( void ) +{ + char topicBuffer[ TOPIC_BUFFER_SIZE + 1 ] = { 0 }; + size_t topicBufferLength = 0U; + char messageBuffer[ UPDATE_JOB_MSG_LENGTH ] = { 0 }; + + /* + * AWS IoT Jobs library: + * Creating the MQTT topic to update the status of OTA job. + */ + Jobs_Update( topicBuffer, + TOPIC_BUFFER_SIZE, + OTA_THING_NAME, + ( uint16_t ) app_strnlen( OTA_THING_NAME, 1000U ), + globalJobId, + ( uint16_t ) app_strnlen( globalJobId, 1000U ), + &topicBufferLength ); + + /* + * AWS IoT Jobs library: + * Creating the message which contains the status of OTA job. + * It will be published on the topic created in the previous step. + */ + size_t messageBufferLength = Jobs_UpdateMsg( Succeeded, + "2", + 1U, + messageBuffer, + UPDATE_JOB_MSG_LENGTH ); + + prvMQTTPublish( topicBuffer, topicBufferLength, messageBuffer, messageBufferLength, 0 ); + LogInfo( ( "OTA update completed successfully.\n" ) ); + globalJobId[ 0 ] = 0U; +} + +/* -------------------------------------------------------------------------- */ + +/* + * Functions related to callbacks, which are called after an event is received. + */ + +STATIC void prvMqttJobCallback( void * pvIncomingPublishCallbackContext, + MQTTPublishInfo_t * pxPublishInfo ) +{ + OtaEventMsg_t eventMsg = { 0 }; + + configASSERT( pxPublishInfo != NULL ); + ( void ) pvIncomingPublishCallbackContext; + + LogInfo( ( "Received job message callback, size %ld.\n", pxPublishInfo->payloadLength ) ); + + bool handled = Jobs_IsStartNextAccepted( pxPublishInfo->pTopicName, + pxPublishInfo->topicNameLength, + OTA_THING_NAME, + strlen( OTA_THING_NAME ) ); + + if( handled ) + { + memcpy( jobDocBuffer.jobData, pxPublishInfo->pPayload, pxPublishInfo->payloadLength ); + eventMsg.jobEvent = &jobDocBuffer; + jobDocBuffer.jobDataLength = pxPublishInfo->payloadLength; + eventMsg.eventId = OtaAgentEventReceivedJobDocument; + OtaSendEvent_FreeRTOS( &eventMsg ); + } +} + +STATIC void prvMqttDefaultCallback( void * pvIncomingPublishCallbackContext, + MQTTPublishInfo_t * pxPublishInfo ) +{ + bool isMatch = false; + + ( void ) MQTT_MatchTopic( pxPublishInfo->pTopicName, + pxPublishInfo->topicNameLength, + OTA_JOB_ACCEPTED_RESPONSE_TOPIC_FILTER, + OTA_JOB_ACCEPTED_RESPONSE_TOPIC_FILTER_LENGTH, + &isMatch ); + + if( isMatch == true ) + { + prvMqttJobCallback( pvIncomingPublishCallbackContext, pxPublishInfo ); + } +} + +STATIC void prvMqttDataCallback( void * pvIncomingPublishCallbackContext, + MQTTPublishInfo_t * pxPublishInfo ) +{ + OtaDataEvent_t * pxData; + OtaEventMsg_t eventMsg = { 0 }; + + configASSERT( pxPublishInfo != NULL ); + ( void ) pvIncomingPublishCallbackContext; + + LogInfo( ( "Received data message callback, size %zu.\n", pxPublishInfo->payloadLength ) ); + + pxData = getOtaDataEventBuffer(); + + if( pxData != NULL ) + { + if( ( size_t ) pxData->dataLength >= pxPublishInfo->payloadLength ) + { + memcpy( pxData->data, pxPublishInfo->pPayload, pxPublishInfo->payloadLength ); + pxData->dataLength = pxPublishInfo->payloadLength; + eventMsg.eventId = OtaAgentEventReceivedFileBlock; + eventMsg.dataEvent = pxData; + + /* Send file block received event. */ + OtaSendEvent_FreeRTOS( &eventMsg ); + } + else + { + LogError( ( "Error: OTA data buffers are too small for the data message received.\n" ) ); + } + } + else + { + LogError( ( "Error: No OTA data buffers available.\n" ) ); + } +} + +void prvRegisterOTACallback( const char * pTopicFilter, + uint16_t topicFilterLength ) +{ + bool isMatch = false; + MQTTStatus_t mqttStatus = MQTTSuccess; + uint16_t index = 0U; + uint16_t numTopicFilters = sizeof( otaTopicFilterCallbacks ) / sizeof( OtaTopicFilterCallback_t ); + + bool subscriptionAdded; + + ( void ) mqttStatus; + + /* Match the input topic filter against the wild-card pattern of topics filters + * relevant for the OTA Update service to determine the type of topic filter. */ + for( ; index < numTopicFilters; index++ ) + { + mqttStatus = MQTT_MatchTopic( pTopicFilter, + topicFilterLength, + otaTopicFilterCallbacks[ index ].pTopicFilter, + otaTopicFilterCallbacks[ index ].topicFilterLength, + &isMatch ); + assert( mqttStatus == MQTTSuccess ); + + if( isMatch ) + { + /* Add subscription so that incoming publishes are routed to the application callback. */ + subscriptionAdded = addSubscription( pTopicFilter, + topicFilterLength, + otaTopicFilterCallbacks[ index ].callback, + NULL ); + + if( subscriptionAdded == false ) + { + LogError( ( "Failed to register a publish callback for topic %.*s.", + pTopicFilter, + topicFilterLength ) ); + } + } + } +} + +/* -------------------------------------------------------------------------- */ + +/* Functions related to requesting and handling job documents. */ + +STATIC void requestJobDocumentHandler() +{ + char topicBuffer[ TOPIC_BUFFER_SIZE + 1 ] = { 0 }; + char messageBuffer[ START_JOB_MSG_LENGTH ] = { 0 }; + size_t topicLength = 0U; + + /* + * AWS IoT Jobs library: + * Creates the topic string for a StartNextPendingJobExecution request. + * It used to check if any pending jobs are available. + */ + Jobs_StartNext( topicBuffer, + TOPIC_BUFFER_SIZE, + OTA_THING_NAME, + ( uint16_t ) strlen( OTA_THING_NAME ), + &topicLength ); + + /* + * AWS IoT Jobs library: + * Creates the message string for a StartNextPendingJobExecution request. + * It will be sent on the topic created in the previous step. + */ + size_t messageLength = Jobs_StartNextMsg( OTA_THING_NAME, + strlen( OTA_THING_NAME ), + messageBuffer, + START_JOB_MSG_LENGTH ); + + prvMQTTPublish( topicBuffer, topicLength, messageBuffer, messageLength, 0 ); +} + +STATIC OtaPalJobDocProcessingResult_t receivedJobDocumentHandler( OtaJobEventData_t * jobDoc ) +{ + bool parseJobDocument = false; + char * jobId; + const char ** jobIdptr = &jobId; + size_t jobIdLength = 0U; + OtaPalJobDocProcessingResult_t xResult = OtaPalJobDocFileCreateFailed; + + memset( &jobFields, 0, sizeof( jobFields ) ); + + /* + * AWS IoT Jobs library: + * Extracting the job ID from the received OTA job document. + */ + jobIdLength = Jobs_GetJobId( ( char * ) jobDoc->jobData, jobDoc->jobDataLength, jobIdptr ); + + if( jobIdLength ) + { + if( strncmp( globalJobId, jobId, jobIdLength ) ) + { + parseJobDocument = true; + memcpy( globalJobId, jobId, jobIdLength ); + } + else + { + xResult = OtaPalJobDocFileCreated; + } + } + + if( parseJobDocument ) + { + bool handled = jobDocumentParser( ( char * ) jobDoc->jobData, jobDoc->jobDataLength, &jobFields ); + + if( otaPal_GetPlatformImageState( &jobFields ) == OtaPalImageStatePendingCommit ) + { + ( void ) sendSuccessMessage(); + + otaAgentShutdown(); + } + + if( handled ) + { + initMqttDownloader( &jobFields ); + + /* AWS IoT core returns the signature in a PEM format. We need to + * convert it to DER format for image signature verification. */ + + handled = convertSignatureToDER( OtaImageSignatureDecoded, sizeof( OtaImageSignatureDecoded ), &jobFields ); + + if( handled ) + { + xResult = otaPal_CreateFileForRx( &jobFields ); + } + else + { + LogError( ( "Failed to decode the image signature to DER format." ) ); + } + } + } + + return xResult; +} + +/* -------------------------------------------------------------------------- */ + +/* Functions related to handling streamed file blocks. */ + +STATIC void initMqttDownloader( AfrOtaJobDocumentFields_t * jobFields ) +{ + numOfBlocksRemaining = jobFields->fileSize / + mqttFileDownloader_CONFIG_BLOCK_SIZE; + numOfBlocksRemaining += ( jobFields->fileSize % + mqttFileDownloader_CONFIG_BLOCK_SIZE > 0 ) ? 1 : 0; + currentFileId = ( uint8_t ) jobFields->fileId; + currentBlockOffset = 0; + totalBytesReceived = 0; + + /* + * MQTT streams Library: + * Initializing the MQTT streams downloader. Passing the + * parameters extracted from the AWS IoT OTA jobs document + * using OTA jobs parser. + */ + mqttDownloader_init( &mqttFileDownloaderContext, + jobFields->imageRef, + jobFields->imageRefLen, + OTA_THING_NAME, + strlen( OTA_THING_NAME ), + DATA_TYPE_JSON ); + + prvMQTTSubscribe( mqttFileDownloaderContext.topicStreamData, + mqttFileDownloaderContext.topicStreamDataLength, + 0 ); +} + +STATIC void requestDataBlock( void ) +{ + char getStreamRequest[ GET_STREAM_REQUEST_BUFFER_SIZE ]; + size_t getStreamRequestLength = 0U; + + /* + * MQTT streams Library: + * Creating the Get data block request. MQTT streams library only + * creates the get block request. To publish the request, MQTT libraries + * like coreMQTT are required. + */ + getStreamRequestLength = mqttDownloader_createGetDataBlockRequest( mqttFileDownloaderContext.dataType, + currentFileId, + mqttFileDownloader_CONFIG_BLOCK_SIZE, + ( uint16_t ) currentBlockOffset, + NUM_OF_BLOCKS_REQUESTED, + getStreamRequest, + GET_STREAM_REQUEST_BUFFER_SIZE ); + + prvMQTTPublish( mqttFileDownloaderContext.topicGetStream, + mqttFileDownloaderContext.topicGetStreamLength, + ( uint8_t * ) getStreamRequest, + getStreamRequestLength, + 0 ); +} + +/* Stores the received data blocks in the flash partition reserved for OTA */ +STATIC int16_t handleMqttStreamsBlockArrived( uint8_t * data, + size_t dataLength ) +{ + int16_t writeblockRes = -1; + + LogInfo( ( "Downloaded block %u of %u. \n", currentBlockOffset, ( currentBlockOffset + numOfBlocksRemaining ) ) ); + + writeblockRes = otaPal_WriteBlock( &jobFields, + totalBytesReceived, + data, + dataLength ); + + if( writeblockRes > 0 ) + { + totalBytesReceived += writeblockRes; + } + + return writeblockRes; +} + +/* -------------------------------------------------------------------------- */ + +/* Functions related to handling data buffers. */ + +STATIC OtaDataEvent_t * getOtaDataEventBuffer( void ) +{ + OtaDataEvent_t * freeBuffer = NULL; + + if( xSemaphoreTake( xBufferSemaphore, portMAX_DELAY ) == pdTRUE ) + { + for( uint32_t ulIndex = 0; ulIndex < otaconfigMAX_NUM_OTA_DATA_BUFFERS; ulIndex++ ) + { + if( dataBuffers[ ulIndex ].bufferUsed == false ) + { + dataBuffers[ ulIndex ].bufferUsed = true; + freeBuffer = &dataBuffers[ ulIndex ]; + freeBuffer->dataLength = sizeof( freeBuffer->data ); + break; + } + } + + ( void ) xSemaphoreGive( xBufferSemaphore ); + } + else + { + LogInfo( ( "Failed to get buffer semaphore. \n" ) ); + } + + return freeBuffer; +} + +STATIC void freeOtaDataEventBuffer( OtaDataEvent_t * const pxBuffer ) +{ + if( xSemaphoreTake( xBufferSemaphore, portMAX_DELAY ) == pdTRUE ) + { + pxBuffer->bufferUsed = false; + ( void ) xSemaphoreGive( xBufferSemaphore ); + } + else + { + LogInfo( ( "Failed to get buffer semaphore.\n" ) ); + } +} + +/* -------------------------------------------------------------------------- */ + +/* Functions related to running the OTA demo. */ + +STATIC void processOTAEvents() +{ + OtaEventMsg_t recvEvent = { 0 }; + OtaEvent_t recvEventId = 0; + OtaEventMsg_t nextEvent = { 0 }; + + OtaReceiveEvent_FreeRTOS( &recvEvent ); + recvEventId = recvEvent.eventId; + + switch( recvEventId ) + { + case OtaAgentEventRequestJobDocument: + LogInfo( ( "Requesting job document.\n" ) ); + requestJobDocumentHandler(); + otaAgentState = OtaAgentStateRequestingJob; + break; + + case OtaAgentEventReceivedJobDocument: + + if( otaAgentState == OtaAgentStateSuspended ) + { + LogInfo( ( "OTA agent is in Suspended state. Dropping job document. \n" ) ); + break; + } + else if( otaAgentState == OtaAgentStateStopped ) + { + LogInfo( ( "OTA agent is in Stopped state. Dropping job document. \n" ) ); + break; + } + + switch( receivedJobDocumentHandler( recvEvent.jobEvent ) ) + { + case OtaPalJobDocFileCreated: + LogInfo( ( "Received OTA job document.\n" ) ); + nextEvent.eventId = OtaAgentEventRequestFileBlock; + OtaSendEvent_FreeRTOS( &nextEvent ); + otaAgentState = OtaAgentStateCreatingFile; + vOtaActiveHook(); + break; + + case OtaPalJobDocFileCreateFailed: + case OtaPalNewImageBootFailed: + case OtaPalJobDocProcessingStateInvalid: + LogInfo( ( "No OTA job available. \n" ) ); + otaAgentShutdown(); + break; + } + + break; + + case OtaAgentEventRequestFileBlock: + otaAgentState = OtaAgentStateRequestingFileBlock; + LogInfo( ( "Requesting file block.\n" ) ); + + if( currentBlockOffset == 0 ) + { + LogInfo( ( "Starting the download. \n" ) ); + } + + requestDataBlock(); + + break; + + case OtaAgentEventReceivedFileBlock: + LogInfo( ( "Received file block.\n" ) ); + + if( otaAgentState == OtaAgentStateSuspended ) + { + LogInfo( ( "OTA agent is in Suspended State. Dropping file block. \n" ) ); + freeOtaDataEventBuffer( recvEvent.dataEvent ); + break; + } + else if( otaAgentState == OtaAgentStateStopped ) + { + LogInfo( ( "OTA Agent is in Stopped State. Dropping file block. \n" ) ); + freeOtaDataEventBuffer( recvEvent.dataEvent ); + break; + } + + int32_t fileId; + int32_t blockId; + int32_t blockSize; + uint8_t decodedData[ mqttFileDownloader_CONFIG_BLOCK_SIZE ]; + size_t decodedDataLength = 0; + int16_t result; + + /* + * MQTT streams Library: + * Extracting and decoding the received data block from the incoming MQTT message. + */ + mqttDownloader_processReceivedDataBlock( + &mqttFileDownloaderContext, + recvEvent.dataEvent->data, + recvEvent.dataEvent->dataLength, + &fileId, + &blockId, + &blockSize, + decodedData, + &decodedDataLength ); + + result = handleMqttStreamsBlockArrived( decodedData, decodedDataLength ); + freeOtaDataEventBuffer( recvEvent.dataEvent ); + + if( result > 0 ) + { + numOfBlocksRemaining--; + currentBlockOffset++; + } + + if( numOfBlocksRemaining == 0 ) + { + nextEvent.eventId = OtaAgentEventCloseFile; + OtaSendEvent_FreeRTOS( &nextEvent ); + } + else + { + nextEvent.eventId = OtaAgentEventRequestFileBlock; + OtaSendEvent_FreeRTOS( &nextEvent ); + } + + break; + + case OtaAgentEventCloseFile: + LogInfo( ( "Closing file.\n" ) ); + + if( closeFile() == true ) + { + nextEvent.eventId = OtaAgentEventActivateImage; + OtaSendEvent_FreeRTOS( &nextEvent ); + } + + break; + + case OtaAgentEventActivateImage: + LogInfo( ( "Attempting to activate image.\n" ) ); + + if( activateImage() == true ) + { + LogInfo( ( "Activated image.\n" ) ); + nextEvent.eventId = OtaAgentEventActivateImage; + OtaSendEvent_FreeRTOS( &nextEvent ); + } + + otaAgentState = OtaAgentStateStopped; + break; + + + case OtaAgentEventSuspend: + LogInfo( ( "Suspending OTA agent.\n" ) ); + otaAgentState = OtaAgentStateSuspended; + break; + + case OtaAgentEventResume: + LogInfo( ( "Resuming OTA agent.\n" ) ); + otaAgentState = OtaAgentStateRequestingJob; + nextEvent.eventId = OtaAgentEventRequestJobDocument; + OtaSendEvent_FreeRTOS( &nextEvent ); + break; + + case OtaAgentEventShutdown: + LogInfo( ( "Shutting down OTA agent.\n" ) ); + otaAgentState = OtaAgentStateStopped; + vOtaNotActiveHook(); + break; + + default: + break; + } +} + +STATIC void prvOTAAgentTask( void * pParam ) +{ + /* Start processing OTA events. */ + + otaAgentState = OtaAgentStateReady; + + while( otaAgentState != OtaAgentStateStopped ) + { + processOTAEvents(); + } + + LogInfo( ( "OTA Agent stopped." ) ); + + vTaskDelete( NULL ); +} + +STATIC BaseType_t prvRunOTADemo( void ) +{ + /* Status indicating a successful demo or not. */ + BaseType_t xStatus = pdPASS; + + /* OTA event message used for sending event to OTA Agent.*/ + OtaEventMsg_t eventMsg = { 0 }; + + OtaEventMsg_t initEvent = { 0 }; + + vWaitUntilMQTTAgentReady(); + vWaitUntilMQTTAgentConnected(); + + /****************************** Init OTA Library. ******************************/ + + if( xStatus == pdPASS ) + { + OtaInitEvent_FreeRTOS(); + } + + /****************************** Create OTA Agent Task. ******************************/ + + if( xStatus == pdPASS ) + { + xStatus = xTaskCreate( prvOTAAgentTask, + "OTA Agent Task", + OTA_AGENT_TASK_STACK_SIZE, + NULL, + OTA_AGENT_TASK_PRIORITY, + NULL ); + + if( xStatus != pdPASS ) + { + LogError( ( "Failed to create OTA agent task:" ) ); + } + } + + /** + * Register a callback for receiving messages intended for OTA agent from broker, + * for which the topic has not been subscribed for. + */ + prvRegisterOTACallback( OTA_DEFAULT_TOPIC_FILTER, OTA_DEFAULT_TOPIC_FILTER_LENGTH ); + + /****************************** Start OTA ******************************/ + + if( xStatus == pdPASS ) + { + initEvent.eventId = OtaAgentEventRequestJobDocument; + OtaSendEvent_FreeRTOS( &initEvent ); + + for( ; ; ) + { + if( !xIsMqttAgentConnected() ) + { + eventMsg.eventId = OtaAgentEventSuspend; + OtaSendEvent_FreeRTOS( &eventMsg ); + + LogInfo( ( "Suspended OTA agent." ) ); + } + else + { + if( otaAgentState == OtaAgentStateSuspended ) + { + eventMsg.eventId = OtaAgentEventResume; + OtaSendEvent_FreeRTOS( &eventMsg ); + + LogInfo( ( "Resumed OTA agent." ) ); + } + } + + vTaskDelay( pdMS_TO_TICKS( otaexampleTASK_DELAY_MS ) ); + } + } + + /** + * Remove callback for receiving messages intended for OTA agent from broker, + * for which the topic has not been subscribed for. + */ + removeSubscription( OTA_DEFAULT_TOPIC_FILTER, + OTA_DEFAULT_TOPIC_FILTER_LENGTH ); + + return xStatus; +} + +/** + * @brief Entry point of Ota demo task. + * + * This example initializes the OTA library to enable OTA updates via the + * MQTT broker. It simply connects to the MQTT broker with the users + * credentials and spins in an indefinite loop to allow MQTT messages to be + * forwarded to the OTA agent for possible processing. The OTA agent does all + * of the real work; checking to see if the message topic is one destined for + * the OTA agent. If not, it is simply ignored. + */ +STATIC void vOtaDemoTask( void * pvParam ) +{ + ( void ) pvParam; + + if( GetImageVersionPSA( FWU_COMPONENT_ID_NONSECURE ) == 0 ) + { + LogInfo( ( "OTA over MQTT, Application version from appFirmwareVersion %u.%u.%u\n", + appFirmwareVersion.u.x.major, + appFirmwareVersion.u.x.minor, + appFirmwareVersion.u.x.build ) ); + } + else + { + LogError( ( "OTA over MQTT, unable to get application versions" ) ); + } + + /* Initialize semaphore for buffer operations. */ + xBufferSemaphore = xSemaphoreCreateMutex(); + + if( xBufferSemaphore == NULL ) + { + LogError( ( "Failed to initialize buffer semaphore." ) ); + } + else + { + /****************************** Start OTA Demo. ******************************/ + + /* Start OTA demo. The function returns only if OTA completes successfully and a + * shutdown of OTA is triggered for a manual restart of the device. */ + if( prvRunOTADemo() != pdPASS ) + { + LogError( ( "Failed to complete OTA successfully." ) ); + } + + /* / ****************************** Cleanup ****************************** / */ + + /* Cleanup semaphore created for buffer operations. */ + vSemaphoreDelete( xBufferSemaphore ); + } +} + +/* + * @brief Create the task that demonstrates the Ota demo. + */ +void vStartOtaTask( void ) +{ + xTaskCreate( vOtaDemoTask, /* Function that implements the task. */ + "OTA Task", /* Text name for the task - only used for debugging. */ + appCONFIG_OTA_MQTT_AGENT_TASK_STACK_SIZE, /* Size of stack (in words, not bytes) to allocate for the task. */ + NULL, /* Optional - task parameter - not used in this case. */ + appCONFIG_OTA_MQTT_AGENT_TASK_PRIORITY, /* Task priority, must be between 0 and configMAX_PRIORITIES - 1. */ + NULL ); /* Optional - used to pass out a handle to the created task. */ +} diff --git a/applications/helpers/ota_orchestrator/src/ota_orchestrator_helpers.c b/applications/helpers/ota_orchestrator/src/ota_orchestrator_helpers.c new file mode 100644 index 00000000..1df5ca26 --- /dev/null +++ b/applications/helpers/ota_orchestrator/src/ota_orchestrator_helpers.c @@ -0,0 +1,84 @@ +/* + * Copyright Amazon.com, Inc. and its affiliates. All Rights Reserved. + * Copyright 2024 Arm Limited and/or its affiliates + * + * + * SPDX-License-Identifier: MIT + * Licensed under the MIT License. See the LICENSE accompanying this file + * for the specific language governing permissions and limitations under + * the License. + */ + +/* Standard library include. */ +#include +#include + +#include "jobs.h" +#include "ota_job_processor.h" + +#include "MQTTFileDownloader_base64.h" + +#include "ota_orchestrator_helpers.h" + + +bool convertSignatureToDER( uint8_t * dest, + size_t destLength, + AfrOtaJobDocumentFields_t * jobFields ) +{ + bool returnVal = true; + size_t decodedSignatureLength = 0; + + + Base64Status_t xResult = base64_Decode( dest, + destLength, + &decodedSignatureLength, + jobFields->signature, + jobFields->signatureLen ); + + if( xResult == Base64Success ) + { + jobFields->signature = dest; + jobFields->signatureLen = decodedSignatureLength; + } + else + { + returnVal = false; + } + + return returnVal; +} + +bool jobDocumentParser( char * message, + size_t messageLength, + AfrOtaJobDocumentFields_t * jobFields ) +{ + char * jobDoc; + size_t jobDocLength = 0U; + int8_t fileIndex = 0; + + /* + * AWS IoT Jobs library: + * Extracting the OTA job document from the jobs message received from AWS IoT core. + */ + jobDocLength = Jobs_GetJobDocument( message, messageLength, ( const char ** ) &jobDoc ); + + if( jobDocLength != 0U ) + { + do + { + /* + * AWS IoT Jobs library: + * Parsing the OTA job document to extract all of the parameters needed to download + * the new firmware. + */ + fileIndex = otaParser_parseJobDocFile( jobDoc, + jobDocLength, + fileIndex, + jobFields ); + } while( fileIndex > 0 ); + } + + /* File index will be -1 if an error occurred, and 0 if all files were + * processed */ + return fileIndex == 0; +} diff --git a/applications/helpers/ota_orchestrator/src/ota_os_freertos.c b/applications/helpers/ota_orchestrator/src/ota_os_freertos.c new file mode 100644 index 00000000..a07e0bbd --- /dev/null +++ b/applications/helpers/ota_orchestrator/src/ota_os_freertos.c @@ -0,0 +1,125 @@ +/* + * Copyright Amazon.com, Inc. and its affiliates. All Rights Reserved. + * Copyright 2024 Arm Limited and/or its affiliates + * + * + * SPDX-License-Identifier: MIT + * Licensed under the MIT License. See the LICENSE accompanying this file + * for the specific language governing permissions and limitations under + * the License. + */ + +/** + * @file ota_os_freertos.c + * @brief Example implementation of the OTA OS Functional Interface for + * FreeRTOS. + */ + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "queue.h" +#include "timers.h" + +/* OTA OS POSIX Interface include. */ +#include "ota_os_freertos.h" + +/* Demo config include. */ +#include "ota_config.h" + +#include "ota_types_definitions.h" + +/* OTA Event queue attributes.*/ +#define MAX_MESSAGES 20 +#define MAX_MSG_SIZE sizeof( OtaEventMsg_t ) + +/* Array containing pointer to the OTA event structures used to send events to + * the OTA task. */ +static OtaEventMsg_t queueData[ MAX_MESSAGES * MAX_MSG_SIZE ]; + +/* The queue control structure. .*/ +static StaticQueue_t staticQueue; + +/* The queue control handle. .*/ +static QueueHandle_t otaEventQueue; + +OtaOsStatus_t OtaInitEvent_FreeRTOS() +{ + OtaOsStatus_t otaOsStatus = OtaOsSuccess; + + otaEventQueue = xQueueCreateStatic( ( UBaseType_t ) MAX_MESSAGES, + ( UBaseType_t ) MAX_MSG_SIZE, + ( uint8_t * ) queueData, + &staticQueue ); + + if( otaEventQueue == NULL ) + { + otaOsStatus = OtaOsEventQueueCreateFailed; + + LogInfo( ( "Failed to create OTA Event queue: " + "xQueueCreateStatic returned error: " + "OtaOsStatus_t=%d \n", + ( int ) otaOsStatus ) ); + } + else + { + LogInfo( ( "OTA Event Queue created.\n" ) ); + } + + return otaOsStatus; +} + +OtaOsStatus_t OtaSendEvent_FreeRTOS( const void * pEventMsg ) +{ + OtaOsStatus_t otaOsStatus = OtaOsSuccess; + BaseType_t retVal = pdFALSE; + + /* Send the event to OTA event queue.*/ + retVal = xQueueSendToBack( otaEventQueue, pEventMsg, ( TickType_t ) 0 ); + + if( retVal == pdTRUE ) + { + LogInfo( ( "OTA Event sent.\n" ) ); + } + else + { + otaOsStatus = OtaOsEventQueueSendFailed; + + LogInfo( ( "Failed to send event to OTA Event Queue: " + "xQueueSendToBack returned error: " + "OtaOsStatus_t=%d \n", + ( int ) otaOsStatus ) ); + } + + return otaOsStatus; +} + +OtaOsStatus_t OtaReceiveEvent_FreeRTOS( void * pEventMsg ) +{ + OtaOsStatus_t otaOsStatus = OtaOsSuccess; + BaseType_t retVal = pdFALSE; + + retVal = xQueueReceive( otaEventQueue, ( OtaEventMsg_t * ) pEventMsg, pdMS_TO_TICKS( 3000U ) ); + + if( retVal == pdTRUE ) + { + LogInfo( ( "OTA Event received \n" ) ); + } + else + { + otaOsStatus = OtaOsEventQueueReceiveFailed; + + LogInfo( ( "Failed to receive event or timeout from OTA Event Queue: " + "xQueueReceive returned error: " + "OtaOsStatus_t=%d \n", + ( int ) otaOsStatus ) ); + } + + return otaOsStatus; +} + +void OtaDeinitEvent_FreeRTOS() +{ + vQueueDelete( otaEventQueue ); + + LogInfo( ( "OTA Event Queue Deleted. \n" ) ); +} diff --git a/applications/keyword_detection/CMakeLists.txt b/applications/keyword_detection/CMakeLists.txt index 8fb1090b..11318863 100644 --- a/applications/keyword_detection/CMakeLists.txt +++ b/applications/keyword_detection/CMakeLists.txt @@ -142,6 +142,7 @@ target_link_libraries(keyword-detection helpers-device-advisor helpers-events mbedtls + ota-update provisioning-lib tfm-ns-interface toolchain-override diff --git a/release_changes/202411211131.change.md b/release_changes/202411211131.change.md new file mode 100644 index 00000000..f811ddf0 --- /dev/null +++ b/release_changes/202411211131.change.md @@ -0,0 +1 @@ +ota: Update FRI to use new modular OTA structure From 8276cbba841f9b94230dae4af3c97ee18550b101 Mon Sep 17 00:00:00 2001 From: Chuyue Luo Date: Wed, 27 Nov 2024 10:09:41 +0000 Subject: [PATCH 10/12] applications: New modular OTA for speech recognition example Update speech recognition CMakeLists.txt and add MQTTFileDownloader_config.h file to allow this example to use the new modular OTA. Signed-off-by: Chuyue Luo --- .../speech_recognition/CMakeLists.txt | 1 + .../aws_configs/MQTTFileDownloader_config.h | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 applications/speech_recognition/configs/aws_configs/MQTTFileDownloader_config.h diff --git a/applications/speech_recognition/CMakeLists.txt b/applications/speech_recognition/CMakeLists.txt index f26bc932..46ffe5f3 100644 --- a/applications/speech_recognition/CMakeLists.txt +++ b/applications/speech_recognition/CMakeLists.txt @@ -154,6 +154,7 @@ target_link_libraries(speech-recognition fri-bsp helpers-events mbedtls + ota-update provisioning-lib speexdsp tfm-ns-interface diff --git a/applications/speech_recognition/configs/aws_configs/MQTTFileDownloader_config.h b/applications/speech_recognition/configs/aws_configs/MQTTFileDownloader_config.h new file mode 100644 index 00000000..5090a98c --- /dev/null +++ b/applications/speech_recognition/configs/aws_configs/MQTTFileDownloader_config.h @@ -0,0 +1,31 @@ +/* + * Copyright Amazon.com, Inc. and its affiliates. All Rights Reserved. + * Copyright 2024 Arm Limited and/or its affiliates + * + * + * SPDX-License-Identifier: MIT + * Licensed under the MIT License. See the LICENSE accompanying this file + * for the specific language governing permissions and limitations under + * the License. + */ + +/** + * @file MQTTFileDownloader_config.h + * @brief MQTT File Streams options. + */ + +#ifndef MQTT_FILE_DOWNLOADER_CONFIG_H +#define MQTT_FILE_DOWNLOADER_CONFIG_H + +/* Standard includes */ +#include + +/** + * @ingroup mqtt_file_downloader_const_types + * @brief Configure the Maximum size of the data payload. The + * smallest value is 256 bytes, maximum is 128KB. For more see + * https://docs.aws.amazon.com/general/latest/gr/iot-core.html + */ +#define mqttFileDownloader_CONFIG_BLOCK_SIZE 4096U + +#endif /* MQTT_FILE_DOWNLOADER_CONFIG_H */ From 25a86d4ac3360d80583ccce6adec650bd55ff826 Mon Sep 17 00:00:00 2001 From: Chuyue Luo Date: Wed, 27 Nov 2024 10:58:47 +0000 Subject: [PATCH 11/12] applications: New modular OTA for object detection example Update object detection CMakeLists.txt and add MQTTFileDownloader_config.h file to allow this example to use the new modular OTA. Signed-off-by: Chuyue Luo --- applications/object_detection/CMakeLists.txt | 1 + .../aws_configs/MQTTFileDownloader_config.h | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 applications/object_detection/configs/aws_configs/MQTTFileDownloader_config.h diff --git a/applications/object_detection/CMakeLists.txt b/applications/object_detection/CMakeLists.txt index f6dc578e..01e1dc52 100644 --- a/applications/object_detection/CMakeLists.txt +++ b/applications/object_detection/CMakeLists.txt @@ -128,6 +128,7 @@ target_link_libraries(object-detection isp-config isp_platform_driver mbedtls + ota-update provisioning-lib tfm-ns-interface toolchain-override diff --git a/applications/object_detection/configs/aws_configs/MQTTFileDownloader_config.h b/applications/object_detection/configs/aws_configs/MQTTFileDownloader_config.h new file mode 100644 index 00000000..5090a98c --- /dev/null +++ b/applications/object_detection/configs/aws_configs/MQTTFileDownloader_config.h @@ -0,0 +1,31 @@ +/* + * Copyright Amazon.com, Inc. and its affiliates. All Rights Reserved. + * Copyright 2024 Arm Limited and/or its affiliates + * + * + * SPDX-License-Identifier: MIT + * Licensed under the MIT License. See the LICENSE accompanying this file + * for the specific language governing permissions and limitations under + * the License. + */ + +/** + * @file MQTTFileDownloader_config.h + * @brief MQTT File Streams options. + */ + +#ifndef MQTT_FILE_DOWNLOADER_CONFIG_H +#define MQTT_FILE_DOWNLOADER_CONFIG_H + +/* Standard includes */ +#include + +/** + * @ingroup mqtt_file_downloader_const_types + * @brief Configure the Maximum size of the data payload. The + * smallest value is 256 bytes, maximum is 128KB. For more see + * https://docs.aws.amazon.com/general/latest/gr/iot-core.html + */ +#define mqttFileDownloader_CONFIG_BLOCK_SIZE 4096U + +#endif /* MQTT_FILE_DOWNLOADER_CONFIG_H */ From 1307458a1073c339dbcdc5c09a279446686f7e25 Mon Sep 17 00:00:00 2001 From: Chuyue Luo Date: Fri, 29 Nov 2024 11:38:07 +0000 Subject: [PATCH 12/12] applications: New modular OTA for FreeRTOS IoT Libraries Tests Update FreeRTOS IoT Libraries Tests CMakeLists.txt and add MQTTFileDownloader_config.h file to allow the tests to use the new modular OTA. Signed-off-by: Chuyue Luo --- .../CMakeLists.txt | 1 + .../aws_configs/MQTTFileDownloader_config.h | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 applications/freertos_iot_libraries_tests/configs/aws_configs/MQTTFileDownloader_config.h diff --git a/applications/freertos_iot_libraries_tests/CMakeLists.txt b/applications/freertos_iot_libraries_tests/CMakeLists.txt index 16dc2b07..1eb6d105 100644 --- a/applications/freertos_iot_libraries_tests/CMakeLists.txt +++ b/applications/freertos_iot_libraries_tests/CMakeLists.txt @@ -108,6 +108,7 @@ target_link_libraries(${CMAKE_PROJECT_NAME} freertos-ota-pal-psa fri-bsp helpers-events + ota-update provisioning-lib mbedtls tfm-ns-interface diff --git a/applications/freertos_iot_libraries_tests/configs/aws_configs/MQTTFileDownloader_config.h b/applications/freertos_iot_libraries_tests/configs/aws_configs/MQTTFileDownloader_config.h new file mode 100644 index 00000000..5090a98c --- /dev/null +++ b/applications/freertos_iot_libraries_tests/configs/aws_configs/MQTTFileDownloader_config.h @@ -0,0 +1,31 @@ +/* + * Copyright Amazon.com, Inc. and its affiliates. All Rights Reserved. + * Copyright 2024 Arm Limited and/or its affiliates + * + * + * SPDX-License-Identifier: MIT + * Licensed under the MIT License. See the LICENSE accompanying this file + * for the specific language governing permissions and limitations under + * the License. + */ + +/** + * @file MQTTFileDownloader_config.h + * @brief MQTT File Streams options. + */ + +#ifndef MQTT_FILE_DOWNLOADER_CONFIG_H +#define MQTT_FILE_DOWNLOADER_CONFIG_H + +/* Standard includes */ +#include + +/** + * @ingroup mqtt_file_downloader_const_types + * @brief Configure the Maximum size of the data payload. The + * smallest value is 256 bytes, maximum is 128KB. For more see + * https://docs.aws.amazon.com/general/latest/gr/iot-core.html + */ +#define mqttFileDownloader_CONFIG_BLOCK_SIZE 4096U + +#endif /* MQTT_FILE_DOWNLOADER_CONFIG_H */