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/.gitmodules b/.gitmodules index e85c2fcc..f7aa5ffc 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 @@ -76,3 +73,9 @@ [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 +[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/applications/freertos_iot_libraries_tests/CMakeLists.txt b/applications/freertos_iot_libraries_tests/CMakeLists.txt index 0c9142ab..1eb6d105 100644 --- a/applications/freertos_iot_libraries_tests/CMakeLists.txt +++ b/applications/freertos_iot_libraries_tests/CMakeLists.txt @@ -108,7 +108,7 @@ target_link_libraries(${CMAKE_PROJECT_NAME} freertos-ota-pal-psa fri-bsp helpers-events - ota-for-aws-iot-embedded-sdk + ota-update 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/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 */ diff --git a/applications/helpers/CMakeLists.txt b/applications/helpers/CMakeLists.txt index 7d12cd8b..83dbd25f 100644 --- a/applications/helpers/CMakeLists.txt +++ b/applications/helpers/CMakeLists.txt @@ -2,10 +2,12 @@ # # SPDX-License-Identifier: MIT +add_subdirectory(crt_helpers) 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/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; +} 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 59c84704..11318863 100644 --- a/applications/keyword_detection/CMakeLists.txt +++ b/applications/keyword_detection/CMakeLists.txt @@ -142,7 +142,7 @@ target_link_libraries(keyword-detection helpers-device-advisor helpers-events mbedtls - ota-for-aws-iot-embedded-sdk + ota-update 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/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 */ diff --git a/applications/object_detection/CMakeLists.txt b/applications/object_detection/CMakeLists.txt index 57493f09..01e1dc52 100644 --- a/applications/object_detection/CMakeLists.txt +++ b/applications/object_detection/CMakeLists.txt @@ -128,7 +128,7 @@ target_link_libraries(object-detection isp-config isp_platform_driver mbedtls - ota-for-aws-iot-embedded-sdk + ota-update 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/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 */ diff --git a/applications/speech_recognition/CMakeLists.txt b/applications/speech_recognition/CMakeLists.txt index 99abe185..46ffe5f3 100644 --- a/applications/speech_recognition/CMakeLists.txt +++ b/applications/speech_recognition/CMakeLists.txt @@ -154,7 +154,7 @@ target_link_libraries(speech-recognition fri-bsp helpers-events mbedtls - ota-for-aws-iot-embedded-sdk + ota-update 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/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 */ diff --git a/components/aws_iot/CMakeLists.txt b/components/aws_iot/CMakeLists.txt index 9b4577f7..2695d123 100644 --- a/components/aws_iot/CMakeLists.txt +++ b/components/aws_iot/CMakeLists.txt @@ -12,5 +12,6 @@ add_subdirectory(corepkcs11) if(CONNECTIVITY_STACK STREQUAL "FREERTOS_PLUS_TCP") add_subdirectory(coresntp) endif() -add_subdirectory(ota_for_aws_iot_embedded_sdk) +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..baa6e5fe --- /dev/null +++ b/components/aws_iot/aws_iot_core_mqtt_file_streams_embedded_c/CMakeLists.txt @@ -0,0 +1,23 @@ +# 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" + ) + + 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/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/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 + 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/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..250839f4 --- /dev/null +++ b/components/aws_iot/jobs_for_aws_iot_embedded_sdk/CMakeLists.txt @@ -0,0 +1,24 @@ +# 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" + ) + + 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/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/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 + 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/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/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 375f9a33..045309df 100644 --- a/components/security/freertos_ota_pal_psa/integration/CMakeLists.txt +++ b/components/security/freertos_ota_pal_psa/integration/CMakeLists.txt @@ -34,5 +34,6 @@ target_link_libraries(freertos-ota-pal-psa freertos_kernel helpers-logging mbedtls - ota-for-aws-iot-embedded-sdk + 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 + diff --git a/manifest.yml b/manifest.yml index a33ee930..4b8e6526 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" @@ -237,3 +228,21 @@ 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" + - 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" 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