From 6d93840a9591ad373e744cdc13135038e12ee456 Mon Sep 17 00:00:00 2001 From: AniruddhaKanhere <60444055+AniruddhaKanhere@users.noreply.github.com> Date: Fri, 8 Mar 2024 22:05:02 -0800 Subject: [PATCH 1/5] First working commit --- .gitmodules | 9 +- .../MIMXRT1060_dir_part.xml | 10 + .../MIMXRT1060_list.xml | 4 + .../MIMXRT1062_internal.xml | 38 + .../MIMXRT1062xxxxB_support/crt_directory.dtd | 88 + .../MIMXRT1062xxxxB_support/crt_infolist.dtd | 279 ++++ .../MIMXRT1062xxxxB_support/info.properties | 7 + .../crt_directory.dtd | 88 + .../crt_directory.xml | 5 + .../crt_infolist.dtd | 279 ++++ .../info.properties | 5 + .mcuxpressoide_packages_support/readme.txt | 2 + GSG.md | 11 +- Middleware/AWS/jobs | 1 + Middleware/AWS/mqtt-stream | 1 + Middleware/AWS/ota | 1 - Middleware/FreeRTOS/lwip_osal/src/sys_arch.c | 2 +- examples/common/mqtt_agent/mqtt_agent_task.c | 131 +- examples/common/mqtt_agent/mqtt_agent_task.h | 8 + examples/common/mqtt_wrapper/mqtt_wrapper.c | 241 +++ examples/common/mqtt_wrapper/mqtt_wrapper.h | 41 + examples/common/ota/ota_demo.h | 133 ++ examples/common/ota/ota_os/ota_os_freertos.c | 123 ++ examples/common/ota/ota_os/ota_os_freertos.h | 103 ++ examples/common/ota/ota_pal.c | 113 +- examples/common/ota/ota_pal.h | 315 +++- .../common/ota/ota_signature_validation.c | 5 + examples/common/ota/ota_update.c | 1437 ++++++++--------- .../evkbmimxrt1060/bootloader/signing_key.pem | 28 + .../pubsub/include/FreeRTOSConfig.h | 3 + examples/evkbmimxrt1060/pubsub/main.c | 1 + projects/evkmimxrt1060/bootloader/.cproject | 85 +- .../.settings/language.settings.xml | 25 + .../.settings/org.eclipse.cdt.core.prefs | 6 + .../org.eclipse.core.resources.prefs | 2 + .../bootloader LinkServer Debug.launch | 103 ++ projects/evkmimxrt1060/defender/.cproject | 77 +- .../defender/.settings/language.settings.xml | 25 + .../org.eclipse.core.resources.prefs | 2 + projects/evkmimxrt1060/pubsub/.cproject | 109 +- projects/evkmimxrt1060/pubsub/.project | 42 +- .../pubsub/.settings/language.settings.xml | 25 + .../org.eclipse.core.resources.prefs | 2 + .../aws_iot_pubsub LinkServer Debug.launch | 104 ++ projects/evkmimxrt1060/shadow/.cproject | 77 +- .../shadow/.settings/language.settings.xml | 25 + .../org.eclipse.core.resources.prefs | 2 + projects/evkmimxrt1060/test/.cproject | 71 +- .../test/.settings/language.settings.xml | 25 + .../org.eclipse.core.resources.prefs | 2 + 50 files changed, 3172 insertions(+), 1149 deletions(-) create mode 100644 .mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/MIMXRT1060_dir_part.xml create mode 100644 .mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/MIMXRT1060_list.xml create mode 100644 .mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/MIMXRT1062_internal.xml create mode 100644 .mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/crt_directory.dtd create mode 100644 .mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/crt_infolist.dtd create mode 100644 .mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/info.properties create mode 100644 .mcuxpressoide_packages_support/crt_directory.dtd create mode 100644 .mcuxpressoide_packages_support/crt_directory.xml create mode 100644 .mcuxpressoide_packages_support/crt_infolist.dtd create mode 100644 .mcuxpressoide_packages_support/info.properties create mode 100644 .mcuxpressoide_packages_support/readme.txt create mode 160000 Middleware/AWS/jobs create mode 160000 Middleware/AWS/mqtt-stream delete mode 160000 Middleware/AWS/ota create mode 100644 examples/common/mqtt_wrapper/mqtt_wrapper.c create mode 100644 examples/common/mqtt_wrapper/mqtt_wrapper.h create mode 100644 examples/common/ota/ota_demo.h create mode 100644 examples/common/ota/ota_os/ota_os_freertos.c create mode 100644 examples/common/ota/ota_os/ota_os_freertos.h create mode 100644 examples/evkbmimxrt1060/bootloader/signing_key.pem create mode 100644 projects/evkmimxrt1060/bootloader/.settings/language.settings.xml create mode 100644 projects/evkmimxrt1060/bootloader/.settings/org.eclipse.cdt.core.prefs create mode 100644 projects/evkmimxrt1060/bootloader/.settings/org.eclipse.core.resources.prefs create mode 100644 projects/evkmimxrt1060/bootloader/bootloader LinkServer Debug.launch create mode 100644 projects/evkmimxrt1060/defender/.settings/language.settings.xml create mode 100644 projects/evkmimxrt1060/defender/.settings/org.eclipse.core.resources.prefs create mode 100644 projects/evkmimxrt1060/pubsub/.settings/language.settings.xml create mode 100644 projects/evkmimxrt1060/pubsub/.settings/org.eclipse.core.resources.prefs create mode 100644 projects/evkmimxrt1060/pubsub/aws_iot_pubsub LinkServer Debug.launch create mode 100644 projects/evkmimxrt1060/shadow/.settings/language.settings.xml create mode 100644 projects/evkmimxrt1060/shadow/.settings/org.eclipse.core.resources.prefs create mode 100644 projects/evkmimxrt1060/test/.settings/language.settings.xml create mode 100644 projects/evkmimxrt1060/test/.settings/org.eclipse.core.resources.prefs diff --git a/.gitmodules b/.gitmodules index 8ac0b83..ada8d8a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,9 +25,6 @@ [submodule "Middleware/FreeRTOS/pkcs11"] path = Middleware/FreeRTOS/pkcs11 url = https://github.com/amazon-freertos/pkcs11.git -[submodule "Middleware/AWS/ota"] - path = Middleware/AWS/ota - url = https://github.com/aws/ota-for-aws-iot-embedded-sdk [submodule "Middleware/tinycbor"] path = Middleware/tinycbor url = https://github.com/intel/tinycbor.git @@ -49,3 +46,9 @@ [submodule "Middleware/FreeRTOS/tests"] path = Middleware/FreeRTOS/tests url = https://github.com/FreeRTOS/FreeRTOS-Libraries-Integration-Tests.git +[submodule "Middleware/AWS/mqtt-stream"] + path = Middleware/AWS/mqtt-stream + url = https://github.com/aws/aws-iot-core-mqtt-file-streams-embedded-c +[submodule "Middleware/AWS/jobs"] + path = Middleware/AWS/jobs + url = https://github.com/aws/Jobs-for-AWS-IoT-embedded-sdk.git diff --git a/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/MIMXRT1060_dir_part.xml b/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/MIMXRT1060_dir_part.xml new file mode 100644 index 0000000..b7735f3 --- /dev/null +++ b/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/MIMXRT1060_dir_part.xml @@ -0,0 +1,10 @@ + + + + diff --git a/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/MIMXRT1060_list.xml b/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/MIMXRT1060_list.xml new file mode 100644 index 0000000..14ba535 --- /dev/null +++ b/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/MIMXRT1060_list.xml @@ -0,0 +1,4 @@ + + + + diff --git a/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/MIMXRT1062_internal.xml b/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/MIMXRT1062_internal.xml new file mode 100644 index 0000000..53ad435 --- /dev/null +++ b/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/MIMXRT1062_internal.xml @@ -0,0 +1,38 @@ + + + + + + MIMXRT1062xxxxB + MIMXRT1060 + NXP + + + + + + + + + Cortex-M7 + Cortex-M + + + diff --git a/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/crt_directory.dtd b/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/crt_directory.dtd new file mode 100644 index 0000000..1bf38a8 --- /dev/null +++ b/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/crt_directory.dtd @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + diff --git a/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/crt_infolist.dtd b/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/crt_infolist.dtd new file mode 100644 index 0000000..396f962 --- /dev/null +++ b/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/crt_infolist.dtd @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/info.properties b/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/info.properties new file mode 100644 index 0000000..97b0b28 --- /dev/null +++ b/.mcuxpressoide_packages_support/MIMXRT1062xxxxB_support/info.properties @@ -0,0 +1,7 @@ +#MCUXpresso IDE part support +#Thu Feb 29 14:14:00 PST 2024 +sdk.id=SDK_2.x_MIMXRT1060-EVKB +sdk.build=801 2024-01-15 +device.version=1.0.0 +sdk.version=2.15.000 +device.id=MIMXRT1062xxxxB diff --git a/.mcuxpressoide_packages_support/crt_directory.dtd b/.mcuxpressoide_packages_support/crt_directory.dtd new file mode 100644 index 0000000..1bf38a8 --- /dev/null +++ b/.mcuxpressoide_packages_support/crt_directory.dtd @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + diff --git a/.mcuxpressoide_packages_support/crt_directory.xml b/.mcuxpressoide_packages_support/crt_directory.xml new file mode 100644 index 0000000..a0b9670 --- /dev/null +++ b/.mcuxpressoide_packages_support/crt_directory.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/.mcuxpressoide_packages_support/crt_infolist.dtd b/.mcuxpressoide_packages_support/crt_infolist.dtd new file mode 100644 index 0000000..396f962 --- /dev/null +++ b/.mcuxpressoide_packages_support/crt_infolist.dtd @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.mcuxpressoide_packages_support/info.properties b/.mcuxpressoide_packages_support/info.properties new file mode 100644 index 0000000..55d7b8d --- /dev/null +++ b/.mcuxpressoide_packages_support/info.properties @@ -0,0 +1,5 @@ +#MCUXpresso IDE +#Thu Feb 29 14:14:00 PST 2024 +product.name=MCUXpresso IDE v11.9.0 [Build 2144] [2024-01-05] +product.version=11.9.0 +product.build=2144 diff --git a/.mcuxpressoide_packages_support/readme.txt b/.mcuxpressoide_packages_support/readme.txt new file mode 100644 index 0000000..00460b7 --- /dev/null +++ b/.mcuxpressoide_packages_support/readme.txt @@ -0,0 +1,2 @@ +This folder is automatically created and contains the SDK part support for the IDE +*** DO NOT REMOVE OR MODIFY, YOUR CHANGES WILL BE OVERWRITTEN ON SDK REFRESH *** \ No newline at end of file diff --git a/GSG.md b/GSG.md index ba101d4..68cade4 100644 --- a/GSG.md +++ b/GSG.md @@ -312,10 +312,9 @@ Follow the steps below to set up an AWS account and provision the device: ![Image](https://user-images.githubusercontent.com/45887168/161142361-68eac8fa-8482-439d-bf90-e602cc5a28cd.png) -1. Copy the PEM certificate from the terminal console and save to a file. Log into your AWS account, go to the AWS IoT Console, choose "Secure", choose - "Certificates", and then choose "Create". Next to "Use my certificate" choose "Get started". On - "Select a CA" choose "Next". On "Register existing device certificates", choose "Select certificates" and - select the PEM file you just created. Select "Activate all" then choose "Register certificates". +1. Copy the PEM certificate from the terminal console and save to a file. Log into your AWS account, go to the AWS IoT Console, choose "Security", choose + "Certificates", and then click on the drop down "Add Certificate" and choose "Register Certificate". Choose "CA is not registered with AWS IoT" and + select the PEM file you just created. Select "Activate" then choose "Register". For more detailed instructions, see [Register a client certificate signed by an unregistered CA (console)](https://docs.aws.amazon.com/iot/latest/developerguide/manual-cert-registration.html#manual-cert-registration-console-noca). ![Image](https://user-images.githubusercontent.com/45887168/161153139-dae3151c-48f3-4d42-a47a-8f839777b425.png) @@ -480,7 +479,7 @@ perform OTA updates. 1. Import the code-signing certificate, private key, and certificate chain into the AWS Certificate Manager. ``` - aws acm import-certificate --certificate fileb://ecdsasigner.crt --private-key fileb://ecdsasigner.key + aws acm import-certificate --certificate fileb://ecdsasigner.crt --private-key fileb://ecdsasigner.key --region= ``` 1. Confirm the ARN for your certificate. You need this ARN when you create an OTA update job. @@ -865,4 +864,4 @@ For more information, `.\devicetester_win_x86-64.exe help` will show all availab When you run IDT, a `results/uuid` directory is generated that will contain all the logs and other information associated with your test run. This allows you to debug any failures. -*Note: Please run qualification test without provision mode (set appmainPROVISIONING_MODE to 0 in [app_main.c](./examples/evkbmimxrt1060/test/app_main.c)).* \ No newline at end of file +*Note: Please run qualification test without provision mode (set appmainPROVISIONING_MODE to 0 in [app_main.c](./examples/evkbmimxrt1060/test/app_main.c)).* diff --git a/Middleware/AWS/jobs b/Middleware/AWS/jobs new file mode 160000 index 0000000..05f2afa --- /dev/null +++ b/Middleware/AWS/jobs @@ -0,0 +1 @@ +Subproject commit 05f2afaa83f92b82321206ba0f2a71d98b6ea47b diff --git a/Middleware/AWS/mqtt-stream b/Middleware/AWS/mqtt-stream new file mode 160000 index 0000000..2bd5fca --- /dev/null +++ b/Middleware/AWS/mqtt-stream @@ -0,0 +1 @@ +Subproject commit 2bd5fca06751f167061bfb45c841fb2bab924d5d diff --git a/Middleware/AWS/ota b/Middleware/AWS/ota deleted file mode 160000 index f976089..0000000 --- a/Middleware/AWS/ota +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f9760892ba152f2c9104d08192ea5ffbbf9fa8ea diff --git a/Middleware/FreeRTOS/lwip_osal/src/sys_arch.c b/Middleware/FreeRTOS/lwip_osal/src/sys_arch.c index 0ad48d4..aabeb1e 100644 --- a/Middleware/FreeRTOS/lwip_osal/src/sys_arch.c +++ b/Middleware/FreeRTOS/lwip_osal/src/sys_arch.c @@ -62,7 +62,7 @@ #include "core_pkcs11.h" #ifndef errno -int errno = 0; +volatile int errno = 0; #endif /* diff --git a/examples/common/mqtt_agent/mqtt_agent_task.c b/examples/common/mqtt_agent/mqtt_agent_task.c index 3729ec1..2c4952b 100644 --- a/examples/common/mqtt_agent/mqtt_agent_task.c +++ b/examples/common/mqtt_agent/mqtt_agent_task.c @@ -88,6 +88,12 @@ /* Includes MQTT Agent Task management APIs. */ #include "mqtt_agent_task.h" +/* Includes MQTT wrapper used in the OTA demo. */ +#include "mqtt_wrapper.h" + +/* Include required to pass unsolicited publishes to the OTA demo. */ +#include "ota_demo.h" + /** * @brief Dimensions the buffer used to serialize and deserialize MQTT packets. * @note Specified in bytes. Must be large enough to hold the maximum @@ -143,7 +149,8 @@ /** * @brief Socket send and receive timeouts to use. Specified in milliseconds. */ -#define mqttexampleTRANSPORT_SEND_RECV_TIMEOUT_MS ( 750 ) +#define mqttexampleTRANSPORT_SEND_TIMEOUT_MS ( 750 ) +#define mqttexampleTRANSPORT_RECV_TIMEOUT_MS ( 0 ) /** * @brief Configuration is used to turn on or off persistent sessions with MQTT broker. @@ -180,6 +187,8 @@ */ #define mqttexampleEVENT_BITS_ALL ( ( EventBits_t ) ( ( 1ULL << MQTT_AGENT_NUM_STATES ) - 1U ) ) + +#define MQTT_AGENT_NOTIFY_IDX ( 3U ) /*-----------------------------------------------------------*/ /** @@ -584,8 +593,8 @@ static BaseType_t prvCreateTLSConnection( NetworkContext_t * pxNetworkContext ) pcBrokerEndpoint, ulBrokerPort, &xNetworkCredentials, - mqttexampleTRANSPORT_SEND_RECV_TIMEOUT_MS, - mqttexampleTRANSPORT_SEND_RECV_TIMEOUT_MS ); + mqttexampleTRANSPORT_RECV_TIMEOUT_MS, + mqttexampleTRANSPORT_SEND_TIMEOUT_MS ); if( xNetworkStatus == TLS_TRANSPORT_SUCCESS ) { @@ -729,13 +738,21 @@ static void prvIncomingPublishCallback( MQTTAgentContext_t * pMqttAgentContext, * handle it as an unsolicited publish. */ if( xPublishHandled != true ) { - /* Ensure the topic string is terminated for printing. This will over- - * write the message ID, which is restored afterwards. */ - pcLocation = ( char * ) &( pxPublishInfo->pTopicName[ pxPublishInfo->topicNameLength ] ); - cOriginalChar = *pcLocation; - *pcLocation = 0x00; - LogWarn( ( "WARN: Received an unsolicited publish from topic %s", pxPublishInfo->pTopicName ) ); - *pcLocation = cOriginalChar; + xPublishHandled = otaDemo_handleIncomingMQTTMessage( pxPublishInfo->pTopicName, + pxPublishInfo->topicNameLength, + pxPublishInfo->pPayload, + pxPublishInfo->payloadLength ); + + if( xPublishHandled != true ) + { + /* Ensure the topic string is terminated for printing. This will over- + * write the message ID, which is restored afterwards. */ + pcLocation = ( char * ) &( pxPublishInfo->pTopicName[ pxPublishInfo->topicNameLength ] ); + cOriginalChar = *pcLocation; + *pcLocation = 0x00; + LogWarn( ( "WARN: Received an unsolicited publish from topic %s", pxPublishInfo->pTopicName ) ); + *pcLocation = cOriginalChar; + } } } @@ -760,6 +777,14 @@ static char * prvKVStoreGetString( KVStoreKey_t xKey ) return pcValue; } + +/*-----------------------------------------------------------*/ + +MQTTAgentContext_t * xGetMqttAgentHandle( void ) +{ + return &xGlobalMqttAgentContext; +} + /*-----------------------------------------------------------*/ void prvMQTTAgentTask( void * pvParameters ) @@ -842,6 +867,10 @@ void prvMQTTAgentTask( void * pvParameters ) * which could be a disconnect. If an error occurs the MQTT context on * which the error happened is returned so there is an attempt to * clean up and reconnect. */ + + /* Set the MQTT context to be used by the MQTT wrapper. */ + mqttWrapper_setCoreMqttContext( &( xGlobalMqttAgentContext.mqttContext ) ); + prvSetMQTTAgentState( MQTT_AGENT_STATE_CONNECTED ); xMQTTStatus = MQTTAgent_CommandLoop( &xGlobalMqttAgentContext ); @@ -1033,6 +1062,31 @@ static void prvSetMQTTAgentState( MQTTAgentState_t xAgentState ) /*-----------------------------------------------------------*/ +static void prvSubscribeRqCallback( MQTTAgentCommandContext_t * pxCommandContext, + MQTTAgentReturnInfo_t * pxReturnInfo ) +{ + TaskHandle_t xTaskHandle = ( struct tskTaskControlBlock * ) pxCommandContext; + + configASSERT( pxReturnInfo ); + + if( xTaskHandle != NULL ) + { + uint32_t ulNotifyValue = ( pxReturnInfo->returnCode & 0xFFFFFF ); + + if( pxReturnInfo->pSubackCodes ) + { + ulNotifyValue += ( pxReturnInfo->pSubackCodes[ 0 ] << 24 ); + } + + ( void ) xTaskNotifyIndexed( xTaskHandle, + MQTT_AGENT_NOTIFY_IDX, + ulNotifyValue, + eSetValueWithOverwrite ); + } +} + +/*-----------------------------------------------------------*/ + BaseType_t xMQTTAgentInit( configSTACK_DEPTH_TYPE uxStackSize, UBaseType_t uxPriority ) { @@ -1177,3 +1231,60 @@ void vRemoveMQTTTopicFilterCallback( const char * pcTopicFilter, } xSemaphoreGive( xSubscriptionsMutex ); } + +MQTTStatus_t MqttAgent_SubscribeSync( const char * pcTopicFilter, + uint16_t uxTopicFilterLength, + MQTTQoS_t xRequestedQoS, + IncomingPubCallback_t pxCallback, + void * pvCallbackCtx ) +{ + BaseType_t xMQTTCallbackAdded; + MQTTStatus_t xResult; + + xMQTTCallbackAdded = xAddMQTTTopicFilterCallback( pcTopicFilter, + uxTopicFilterLength, + pxCallback, + pvCallbackCtx, + pdFALSE ); + + if( xMQTTCallbackAdded == pdTRUE ) + { + MQTTSubscribeInfo_t xSubInfo = { .qos = xRequestedQoS, + .pTopicFilter = pcTopicFilter, + .topicFilterLength = uxTopicFilterLength }; + + MQTTAgentSubscribeArgs_t xSubArgs = { .pSubscribeInfo = &xSubInfo, + .numSubscriptions = 1 }; + + /* The block time can be 0 as the command loop is not running at this point. */ + MQTTAgentCommandInfo_t xCommandParams = { .blockTimeMs = portMAX_DELAY, + .cmdCompleteCallback = prvSubscribeRqCallback, + .pCmdCompleteCallbackContext = ( void * ) ( xTaskGetCurrentTaskHandle() ) }; + + ( void ) xTaskNotifyStateClearIndexed( NULL, MQTT_AGENT_NOTIFY_IDX ); + + /* Enqueue subscribe to the command queue. These commands will be processed only + * when command loop starts. */ + xResult = MQTTAgent_Subscribe( &xGlobalMqttAgentContext, &xSubArgs, &xCommandParams ); + + if( xResult == MQTTSuccess ) + { + uint32_t ulNotifyValue = 0; + + if( xTaskNotifyWaitIndexed( MQTT_AGENT_NOTIFY_IDX, + 0x0, + 0xFFFFFFFF, + &ulNotifyValue, + portMAX_DELAY ) ) + { + xResult = ( ulNotifyValue & 0x00FFFFFF ); + } + else + { + xResult = MQTTKeepAliveTimeout; + } + } + } + + return 0; +} diff --git a/examples/common/mqtt_agent/mqtt_agent_task.h b/examples/common/mqtt_agent/mqtt_agent_task.h index 0530388..160638e 100644 --- a/examples/common/mqtt_agent/mqtt_agent_task.h +++ b/examples/common/mqtt_agent/mqtt_agent_task.h @@ -127,4 +127,12 @@ void vRemoveMQTTTopicFilterCallback( const char * pcTopicFilter, uint16_t usTopicFilterLength ); +MQTTAgentContext_t * xGetMqttAgentHandle( void ); + +MQTTStatus_t MqttAgent_SubscribeSync( const char * pcTopicFilter, + uint16_t uxTopicFilterLength, + MQTTQoS_t xRequestedQoS, + IncomingPubCallback_t pxCallback, + void * pvCallbackCtx ); + #endif /* ifndef _MQTT_AGENT_TASK_H_ */ diff --git a/examples/common/mqtt_wrapper/mqtt_wrapper.c b/examples/common/mqtt_wrapper/mqtt_wrapper.c new file mode 100644 index 0000000..a32b6b1 --- /dev/null +++ b/examples/common/mqtt_wrapper/mqtt_wrapper.c @@ -0,0 +1,241 @@ +/* + * Copyright Amazon.com, Inc. and its affiliates. All Rights Reserved. + * 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. + */ + +#include +#include + +#include "mqtt_wrapper.h" +#include "ota_demo.h" + +#define MQTT_AGENT_NOTIFY_IDX ( 2 ) + +static MQTTContext_t * globalCoreMqttContext = NULL; + +#define MAX_THING_NAME_SIZE 128U +static char globalThingName[ MAX_THING_NAME_SIZE + 1 ]; +static size_t globalThingNameLength = 0U; + +/** + * @brief Defines the structure to use as the command callback context in this + * demo. + */ +struct MQTTAgentCommandContext +{ + MQTTStatus_t xReturnStatus; + TaskHandle_t xTaskToNotify; +}; + +static void handleIncomingMQTTMessage( char * topic, + size_t topicLength, + uint8_t * message, + size_t messageLength ) + +{ + bool messageHandled = otaDemo_handleIncomingMQTTMessage( topic, + topicLength, + message, + messageLength ); + if( !messageHandled ) + { + printf( "Unhandled incoming PUBLISH received on topic, message: " + "%.*s\n%.*s\n", + ( unsigned int ) topicLength, + topic, + ( unsigned int ) messageLength, + ( char * ) message ); + } +} + +void mqttWrapper_setCoreMqttContext( MQTTContext_t * mqttContext ) +{ + globalCoreMqttContext = mqttContext; +} + +MQTTContext_t * mqttWrapper_getCoreMqttContext( void ) +{ + assert( globalCoreMqttContext != NULL ); + return globalCoreMqttContext; +} + +void mqttWrapper_setThingName( char * thingName, size_t thingNameLength ) +{ + assert( thingNameLength <= MAX_THING_NAME_SIZE ); + strncpy( globalThingName, thingName, MAX_THING_NAME_SIZE ); + globalThingNameLength = thingNameLength; +} + +void mqttWrapper_getThingName( char * thingNameBuffer, + size_t * thingNameLength ) +{ + assert( globalThingName[ 0 ] != 0 ); + + memcpy( thingNameBuffer, globalThingName, globalThingNameLength ); + thingNameBuffer[ globalThingNameLength ] = '\0'; + *thingNameLength = globalThingNameLength; +} + +bool mqttWrapper_connect( char * thingName, size_t thingNameLength ) +{ + MQTTConnectInfo_t connectInfo = { 0 }; + MQTTStatus_t mqttStatus = MQTTSuccess; + bool sessionPresent = false; + + assert( globalCoreMqttContext != NULL ); + + connectInfo.pClientIdentifier = thingName; + connectInfo.clientIdentifierLength = thingNameLength; + connectInfo.pUserName = NULL; + connectInfo.userNameLength = 0U; + connectInfo.pPassword = NULL; + connectInfo.passwordLength = 0U; + connectInfo.keepAliveSeconds = 60U; + connectInfo.cleanSession = true; + mqttStatus = MQTT_Connect( globalCoreMqttContext, + &connectInfo, + NULL, + 5000U, + &sessionPresent ); + return mqttStatus == MQTTSuccess; +} + +bool mqttWrapper_isConnected( void ) +{ + bool isConnected = false; + assert( globalCoreMqttContext != NULL ); + isConnected = globalCoreMqttContext->connectStatus == MQTTConnected; + return isConnected; +} + +static void prvPublishCommandCallback( MQTTAgentCommandContext_t * pCmdCallbackContext, + MQTTAgentReturnInfo_t * pReturnInfo ) +{ + TaskHandle_t xTaskHandle = ( struct tskTaskControlBlock * ) pCmdCallbackContext->xTaskToNotify; + + + if( xTaskHandle != NULL ) + { + uint32_t ulNotifyValue = MQTTSuccess; // ( pxReturnInfo->returnCode & 0xFFFFFF ); +// +// if( pxReturnInfo->pSubackCodes ) +// { +// ulNotifyValue += ( pxReturnInfo->pSubackCodes[ 0 ] << 24 ); +// } + + ( void ) xTaskNotifyIndexed( xTaskHandle, + MQTT_AGENT_NOTIFY_IDX, + ulNotifyValue, + eSetValueWithOverwrite ); + } +} + +bool mqttWrapper_publish( char * topic, + size_t topicLength, + uint8_t * message, + size_t messageLength ) +{ + bool success = false; + assert( globalCoreMqttContext != NULL ); + + success = mqttWrapper_isConnected(); + if( success ) + { + MQTTStatus_t mqttStatus = MQTTSuccess; + /* TODO: This should be static or should we wait? */ + static MQTTPublishInfo_t pubInfo = { 0 }; + MQTTAgentContext_t * xAgentHandle = xGetMqttAgentHandle(); + pubInfo.qos = 0; + pubInfo.retain = false; + pubInfo.dup = false; + pubInfo.pTopicName = topic; + pubInfo.topicNameLength = topicLength; + pubInfo.pPayload = message; + pubInfo.payloadLength = messageLength; + + MQTTAgentCommandContext_t xCommandContext = + { + .xTaskToNotify = xTaskGetCurrentTaskHandle(), + .xReturnStatus = MQTTIllegalState, + }; + + MQTTAgentCommandInfo_t xCommandParams = + { + .blockTimeMs = 1000, + .cmdCompleteCallback = prvPublishCommandCallback, + .pCmdCompleteCallbackContext = &xCommandContext, + }; + + ( void ) xTaskNotifyStateClearIndexed( NULL, MQTT_AGENT_NOTIFY_IDX ); + + mqttStatus = MQTTAgent_Publish( xAgentHandle, + &pubInfo, + &xCommandParams ); + + if( mqttStatus == MQTTSuccess ) + { + uint32_t ulNotifyValue = 0; + + if( xTaskNotifyWaitIndexed( MQTT_AGENT_NOTIFY_IDX, + 0x0, + 0xFFFFFFFF, + &ulNotifyValue, + portMAX_DELAY ) ) + { + mqttStatus = ( ulNotifyValue & 0x00FFFFFF ); + } + else + { + mqttStatus = MQTTKeepAliveTimeout; + } + } + + + + success = mqttStatus == MQTTSuccess; + } + return success; +} + +void handleIncomingPublish( void * pvIncomingPublishCallbackContext, + MQTTPublishInfo_t * pxPublishInfo ) +{ + char * topic = NULL; + size_t topicLength = 0U; + uint8_t * message = NULL; + size_t messageLength = 0U; + + topic = ( char * ) pxPublishInfo->pTopicName; + topicLength = pxPublishInfo->topicNameLength; + message = ( uint8_t * ) pxPublishInfo->pPayload; + messageLength = pxPublishInfo->payloadLength; + handleIncomingMQTTMessage( topic, topicLength, message, messageLength ); +} + +bool mqttWrapper_subscribe( char * topic, size_t topicLength ) +{ + bool success = false; + assert( globalCoreMqttContext != NULL ); + + success = mqttWrapper_isConnected(); + if( success ) + { + MQTTStatus_t mqttStatus = MQTTSuccess; + + mqttStatus = MqttAgent_SubscribeSync( topic, + topicLength, + 0, + handleIncomingPublish, + NULL ); + + configASSERT( mqttStatus == MQTTSuccess ); + + success = mqttStatus == MQTTSuccess; + } + return success; +} + diff --git a/examples/common/mqtt_wrapper/mqtt_wrapper.h b/examples/common/mqtt_wrapper/mqtt_wrapper.h new file mode 100644 index 0000000..94958cb --- /dev/null +++ b/examples/common/mqtt_wrapper/mqtt_wrapper.h @@ -0,0 +1,41 @@ +/* + * Copyright Amazon.com, Inc. and its affiliates. All Rights Reserved. + * 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 MQTT_WRAPPER_H +#define MQTT_WRAPPER_H + +#include "FreeRTOS.h" +#include "task.h" + +#include "core_mqtt.h" +#include "core_mqtt_agent.h" +#include "mqtt_agent_task.h" + +void mqttWrapper_setCoreMqttContext( MQTTContext_t * mqttContext ); + +MQTTContext_t * mqttWrapper_getCoreMqttContext( void ); + +void mqttWrapper_setThingName( char * thingName, size_t thingNameLength ); + +void mqttWrapper_getThingName( char * thingNameBuffer, + size_t * thingNameLength ); + +bool mqttWrapper_connect( char * thingName, size_t thingNameLength ); + +bool mqttWrapper_isConnected( void ); + +bool mqttWrapper_publish( char * topic, + size_t topicLength, + uint8_t * message, + size_t messageLength ); + +bool mqttWrapper_subscribe( char * topic, size_t topicLength ); + +#endif + diff --git a/examples/common/ota/ota_demo.h b/examples/common/ota/ota_demo.h new file mode 100644 index 0000000..7c9911d --- /dev/null +++ b/examples/common/ota/ota_demo.h @@ -0,0 +1,133 @@ +/* + * Copyright Amazon.com, Inc. and its affiliates. All Rights Reserved. + * 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_DEMO_H +#define OTA_DEMO_H + +#include +#include + +#include "MQTTFileDownloader.h" + +#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 activate the new 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 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; + +/** + * @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; + + +void otaDemo_start( void ); + +bool otaDemo_handleIncomingMQTTMessage( char * topic, + size_t topicLength, + uint8_t * message, + size_t messageLength ); + +OtaState_t getOtaAgentState(); +#endif + diff --git a/examples/common/ota/ota_os/ota_os_freertos.c b/examples/common/ota/ota_os/ota_os_freertos.c new file mode 100644 index 0000000..4995222 --- /dev/null +++ b/examples/common/ota/ota_os/ota_os_freertos.c @@ -0,0 +1,123 @@ +/* + * Copyright Amazon.com, Inc. and its affiliates. All Rights Reserved. + * 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 Includes.*/ + +#include "ota_demo.h" +#include "ota_os_freertos.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; + +// printf( "Failed to create OTA Event Queue: " +// "xQueueCreateStatic returned error: " +// "OtaOsStatus_t=%d \n", +// ( int ) otaOsStatus ); + } + else + { +// printf( "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 ) + { +// printf( "OTA Event Sent.\n" ); + } + else + { + otaOsStatus = OtaOsEventQueueSendFailed; + +// printf( "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 ) + { +// printf( "OTA Event received \n" ); + } + else + { + otaOsStatus = OtaOsEventQueueReceiveFailed; + + // printf( "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 ); + +// printf( "OTA Event Queue Deleted. \n" ); + +} + diff --git a/examples/common/ota/ota_os/ota_os_freertos.h b/examples/common/ota/ota_os/ota_os_freertos.h new file mode 100644 index 0000000..01e54ca --- /dev/null +++ b/examples/common/ota/ota_os/ota_os_freertos.h @@ -0,0 +1,103 @@ +/* + * Copyright Amazon.com, Inc. and its affiliates. All Rights Reserved. + * 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 include. */ +#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/examples/common/ota/ota_pal.c b/examples/common/ota/ota_pal.c index 9400f51..d3960f3 100644 --- a/examples/common/ota/ota_pal.c +++ b/examples/common/ota/ota_pal.c @@ -37,13 +37,10 @@ #include "mcuboot_app_support.h" -/* Specify the OTA signature algorithm we support on this platform. */ -const char OTA_JsonFileSignatureKey[ OTA_FILE_SIG_KEY_STR_MAX_LENGTH ] = "sig-sha256-ecdsa"; - /* PAL file context structure */ typedef struct { - const OtaFileContext_t * FileXRef; + const AfrOtaJobDocumentFields_t * FileXRef; uint32_t partition_log_addr; uint32_t partition_phys_addr; uint32_t partition_size; @@ -54,44 +51,58 @@ typedef struct static PAL_FileContext_t prvPAL_CurrentFileContext; -static PAL_FileContext_t * prvPAL_GetPALFileContext( OtaFileContext_t * const C ) -{ - PAL_FileContext_t * PalFileContext; +OtaPalStatus_t xFlashPalValidateSignature( uint8_t * pMappedAddress, + size_t mappedLength, + char * pCertificatePath, + size_t certlength, + uint8_t * pSignature, + size_t signatureLength ); - if( ( C == NULL ) || ( C->pFile == NULL ) ) + +static PAL_FileContext_t * prvPAL_GetPALFileContext( AfrOtaJobDocumentFields_t * const pFileContext ) +{ + if( pFileContext == NULL ) { return NULL; } - - PalFileContext = ( PAL_FileContext_t * ) C->pFile; - - if( ( PalFileContext == NULL ) || ( PalFileContext->FileXRef != C ) ) + else if( prvPAL_CurrentFileContext.FileXRef != pFileContext ) { - return NULL; + return NULL; + } + else + { + return &prvPAL_CurrentFileContext; } - - return PalFileContext; } -OtaPalStatus_t xOtaPalAbort( OtaFileContext_t * const C ) +OtaPalStatus_t otaPal_Abort( AfrOtaJobDocumentFields_t * const pFileContext ) { OtaPalStatus_t result = OtaPalSuccess; LogInfo( ( "[OTA-NXP] Abort" ) ); - C->pFile = NULL; + pFileContext->fileId = 0; + pFileContext->filepath = NULL; return result; } -OtaPalStatus_t xOtaPalCreateFileForRx( OtaFileContext_t * const C ) +OtaPalJobDocProcessingResult_t otaPal_CreateFileForRx( AfrOtaJobDocumentFields_t * const pFileContext ) { partition_t update_partition; PAL_FileContext_t * PalFileContext = &prvPAL_CurrentFileContext; LogDebug( ( "[OTA-NXP] CreateFileForRx" ) ); + if( otaPal_GetPlatformImageState( pFileContext ) == OtaPalImageStateValid ) + { + /* TODO: Check here if anything is to be checked before sending the + * success message to IoT core. */ + return OtaPalNewImageBooted; + } + + if( bl_get_update_partition_info( &update_partition ) != kStatus_Success ) { LogError( ( "[OTA-NXP] Could not get update partition information" ) ); @@ -108,21 +119,21 @@ OtaPalStatus_t xOtaPalCreateFileForRx( OtaFileContext_t * const C ) if( PalFileContext->partition_phys_addr == MFLASH_INVALID_ADDRESS ) { LogError( ( "[OTA-NXP] Could not get update partition FLASH address" ) ); - return OTA_PAL_COMBINE_ERR( OtaPalRxFileCreateFailed, 0U ); + return OtaPalRxFileCreateFailed; } /* Check partition alignment */ if( !mflash_drv_is_sector_aligned( PalFileContext->partition_phys_addr ) || !mflash_drv_is_sector_aligned( PalFileContext->partition_size ) ) { LogError( ( "[OTA-NXP] Invalid update partition" ) ); - return OTA_PAL_COMBINE_ERR( OtaPalRxFileCreateFailed, 0U ); + return OtaPalRxFileCreateFailed; } /* Check whether the file fits at all */ - if( C->fileSize > update_partition.size ) + if( pFileContext->fileSize > update_partition.size ) { LogError( ( "[OTA-NXP] File too large" ) ); - return OTA_PAL_COMBINE_ERR( OtaPalRxFileTooLarge, 0U ); + return OtaPalRxFileTooLarge; } /* Actual size of the file according to data received */ @@ -134,14 +145,15 @@ OtaPalStatus_t xOtaPalCreateFileForRx( OtaFileContext_t * const C ) /* Pre-set address of area not erased so far */ PalFileContext->next_erase_addr = PalFileContext->partition_phys_addr; - PalFileContext->FileXRef = C; /* pointer cross reference for integrity check */ - C->pFile = ( uint8_t * ) PalFileContext; + PalFileContext->FileXRef = pFileContext; /* pointer cross reference for integrity check */ + + //C->pFile = ( uint8_t * ) PalFileContext; return OtaPalSuccess; } -OtaPalStatus_t xOtaPalCloseFile( OtaFileContext_t * const C ) +OtaPalStatus_t otaPal_CloseFile( AfrOtaJobDocumentFields_t * const pFileContext ) { OtaPalStatus_t result = OtaPalSuccess; PAL_FileContext_t * PalFileContext; @@ -149,14 +161,14 @@ OtaPalStatus_t xOtaPalCloseFile( OtaFileContext_t * const C ) LogDebug( ( "[OTA-NXP] CloseFile" ) ); - PalFileContext = prvPAL_GetPALFileContext( C ); + PalFileContext = prvPAL_GetPALFileContext( pFileContext ); if( PalFileContext == NULL ) { - return OTA_PAL_COMBINE_ERR( OtaPalFileClose, 0U ); + return OtaPalFileClose; } - if( PalFileContext->file_size != C->fileSize ) + if( PalFileContext->file_size != pFileContext->fileSize ) { LogWarn( ( "[OTA-NXP] Actual file size is not as expected" ) ); } @@ -165,14 +177,15 @@ OtaPalStatus_t xOtaPalCloseFile( OtaFileContext_t * const C ) if( file_data == NULL ) { - return OTA_PAL_COMBINE_ERR( OtaPalSignatureCheckFailed, 0U ); + return OtaPalSignatureCheckFailed; } result = xFlashPalValidateSignature( ( void * ) file_data, PalFileContext->file_size, - ( char * ) C->pCertFilepath, - C->pSignature->data, - C->pSignature->size ); + ( char * ) pFileContext->certfile, + pFileContext->certfileLen, + pFileContext->signature, + pFileContext->signatureLen ); if( result != OtaPalSuccess ) { @@ -198,12 +211,12 @@ OtaPalStatus_t xOtaPalCloseFile( OtaFileContext_t * const C ) } #endif /* ifndef DISABLE_OTA_CLOSE_FILE_HEADER_CHECK */ - C->pFile = NULL; - return OTA_PAL_COMBINE_ERR( result, 0U ); + pFileContext->fileId = 0; + return result; } -int16_t xOtaPalWriteBlock( OtaFileContext_t * const C, +int16_t otaPal_WriteBlock( AfrOtaJobDocumentFields_t * const pFileContext, uint32_t ulOffset, uint8_t * const pcData, uint32_t ulBlockSize ) @@ -219,7 +232,7 @@ int16_t xOtaPalWriteBlock( OtaFileContext_t * const C, LogDebug( ( "[OTA-NXP] WriteBlock 0x%x : 0x%x", ulOffset, ulBlockSize ) ); - PalFileContext = prvPAL_GetPALFileContext( C ); + PalFileContext = prvPAL_GetPALFileContext( pFileContext ); if( PalFileContext == NULL ) { @@ -315,31 +328,23 @@ int16_t xOtaPalWriteBlock( OtaFileContext_t * const C, } -OtaPalStatus_t xOtaPalActivateNewImage( OtaFileContext_t * const C ) +OtaPalStatus_t otaPal_ActivateNewImage( AfrOtaJobDocumentFields_t * const pFileContext ) { LogInfo( ( "[OTA-NXP] ActivateNewImage" ) ); - xOtaPalResetDevice( C ); /* go for reboot */ + otaPal_ResetDevice( pFileContext ); /* go for reboot */ return OtaPalSuccess; } -OtaPalStatus_t xOtaPalResetDevice( OtaFileContext_t * const C ) -{ - LogInfo( ( "[OTA-NXP] SystemReset" ) ); - vTaskDelay( 100 / portTICK_PERIOD_MS ); - NVIC_SystemReset(); /* this should never return */ -} - - -OtaPalStatus_t xOtaPalSetPlatformImageState( OtaFileContext_t * const C, +OtaPalStatus_t otaPal_SetPlatformImageState( AfrOtaJobDocumentFields_t * const pFileContext, OtaImageState_t eState ) { OtaPalStatus_t result = OtaPalSuccess; LogDebug( ( "[OTA-NXP] SetPlatformImageState %d", eState ) ); - if( xOtaPalGetPlatformImageState( C ) == OtaPalImageStatePendingCommit ) + if( otaPal_GetPlatformImageState( pFileContext ) == OtaPalImageStatePendingCommit ) { /* Device in test mode */ switch( eState ) @@ -414,11 +419,11 @@ OtaPalStatus_t xOtaPalSetPlatformImageState( OtaFileContext_t * const C, } } - return OTA_PAL_COMBINE_ERR( result, 0U ); + return result; } -OtaPalImageState_t xOtaPalGetPlatformImageState( OtaFileContext_t * const C ) +OtaPalImageState_t otaPal_GetPlatformImageState( AfrOtaJobDocumentFields_t * const pFileContext ) { uint32_t state; @@ -442,3 +447,11 @@ OtaPalImageState_t xOtaPalGetPlatformImageState( OtaFileContext_t * const C ) return OtaPalImageStateInvalid; } + + +OtaPalStatus_t otaPal_ResetDevice( AfrOtaJobDocumentFields_t * const pFileContext ) +{ + LogInfo( ( "[OTA-NXP] SystemReset" ) ); + vTaskDelay( 100 / portTICK_PERIOD_MS ); + NVIC_SystemReset(); /* this should never return */ +} diff --git a/examples/common/ota/ota_pal.h b/examples/common/ota/ota_pal.h index bcf5266..ef3e7d3 100644 --- a/examples/common/ota/ota_pal.h +++ b/examples/common/ota/ota_pal.h @@ -31,131 +31,276 @@ #ifndef OTA_PAL_H #define OTA_PAL_H -#include "ota.h" + +#include "jobs.h" +#include "job_parser.h" /** - * @brief Retrieve the current firmware image state from flash. + * @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. * - * @param[in] pFileContext Pointer to a context containing firmware image details. - * @return Appropriate state of the firmware image. + * If you want to abort an active OTA transfer, you may do so by calling the API + * @ref OTA_SetImageState ( OtaImageStateAborted ). */ -OtaPalImageState_t xOtaPalGetPlatformImageState( OtaFileContext_t * const pFileContext ); - -#define otaPal_GetPlatformImageState xOtaPalGetPlatformImageState +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; /** - * @brief Set the current firmware image state in flash. + * @ingroup ota_enum_types + * @brief OTA Platform Image State. * - * @param[in] pFileContext Pointer to a context containing firmware image details. - * @param[in] eState State to be set for the firmware image. - * @return OtaPalSuccess if successful, else OTA error code along with detailed PAL error code. + * The image state set by platform implementation. */ -OtaPalStatus_t xOtaPalSetPlatformImageState( OtaFileContext_t * const pFileContext, - OtaImageState_t eState ); - -#define otaPal_SetPlatformImageState xOtaPalSetPlatformImageState +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; /** - * @brief Resets the device with the current firmware image. - * The API can be invoked when aborting the current OTA update to fall back to old image or while activating - * new image after a successful update. + * @ingroup ota_enum_types + * @brief OTA Platform Image State. * - * @param[in] pFileContext Pointer to a context containing firmware image details. - * @return OtaPalSuccess if successful, else OTA error code along with detailed PAL error code. + * The image state set by platform implementation. */ -OtaPalStatus_t xOtaPalResetDevice( OtaFileContext_t * const pFileContext ); +typedef enum OtaPalJobDocProcessingResult +{ + OtaPalJobDocFileCreated = 0, + OtaPalJobDocFileCreateFailed, + OtaPalNewImageBooted, + OtaPalNewImageBootFailed, + OtaPalJobDocProcessingStateInvalid +} OtaPalJobDocProcessingResult_t; -#define otaPal_ResetDevice xOtaPalResetDevice +typedef enum OtaPalStatus +{ + OtaPalSuccess = 0, + OtaPalUninitialized, + OtaPalOutOfMemory, + OtaPalNullFileContext, + OtaPalSignatureCheckFailed, + OtaPalRxFileCreateFailed, + OtaPalRxFileTooLarge, + OtaPalBootInfoCreateFailed, + OtaPalBadSignerCert, + OtaPalBadImageState, + OtaPalAbortFailed, + OtaPalRejectFailed, + OtaPalCommitFailed, + OtaPalActivateFailed, + OtaPalFileAbort, + OtaPalFileClose +}OtaPalStatus_t; /** - * @brief Activates the device with the new firmware image. - * The API should prepare the new image to be booted up and reset the device to boot up with the new image. + * @brief Abort an OTA transfer. + * + * Aborts access to an existing open file represented by the OTA file context pFileContext. This is + * only valid for jobs that started successfully. + * + * @note The input OtaFileContext_t pFileContext is checked for NULL by the OTA agent before this + * function is called. * - * @param[in] pFileContext Pointer to a context containing firmware image details. - * @return OtaPalSuccess if successful, else OTA error code along with detailed PAL error code. + * This function may be called before the file is opened, so the file pointer pFileContext->fileHandle + * may be NULL when this function is called. + * + * @param[in] pFileContext OTA file context information. + * + * @return The OtaPalStatus_t error code is a combination of the main OTA PAL interface error and + * 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. + * + * Major error codes returned are: + * + * OtaPalSuccess: Aborting access to the open file was successful. + * OtaPalFileAbort: Aborting access to the open file context was unsuccessful. */ -OtaPalStatus_t xOtaPalActivateNewImage( OtaFileContext_t * const pFileContext ); - -#define otaPal_ActivateNewImage xOtaPalActivateNewImage +OtaPalStatus_t otaPal_Abort( AfrOtaJobDocumentFields_t * const pFileContext ); /** - * @brief Writes a block of the firmware image to flash. + * @brief Create a new receive file. + * + * @note Opens the file indicated in the OTA file context in the MCU file system. * - * @param[in] pFileContext Pointer to a context containing firmware image details. - * @param[in] offset Offset in bytes of the block with in the whole firmware image - * @param[in] pData Pointer to buffer containing the block. - * @param[in] blockSize Size of the block - * @return Number of bytes of block written or < 0 if there is an error. + * @note The previous image may be present in the designated image download partition or file, so the + * partition or file must be completely erased or overwritten in this routine. + * + * @note The input OtaFileContext_t pFileContext is checked for NULL by the OTA agent before this + * function is called. + * The device file path is a required field in the OTA job document, so pFileContext->pFilePath is + * checked for NULL by the OTA agent before this function is called. + * + * @param[in] pFileContext OTA file context information. + * + * @return The OtaPalStatus_t error code is a combination of the main OTA PAL interface error and + * 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. + * + * Major error codes returned are: + * + * OtaPalSuccess: File creation was successful. + * OtaPalRxFileTooLarge: The OTA receive file is too big for the platform to support. + * OtaPalBootInfoCreateFailed: The bootloader information file creation failed. + * OtaPalRxFileCreateFailed: Returned for other errors creating the file in the device's + * non-volatile memory. If this error is returned, then the sub error + * should be set to the appropriate platform specific value. */ -int16_t xOtaPalWriteBlock( OtaFileContext_t * const pFileContext, - uint32_t offset, - uint8_t * const pData, - uint32_t blockSize ); - -#define otaPal_WriteBlock xOtaPalWriteBlock +OtaPalJobDocProcessingResult_t otaPal_CreateFileForRx( AfrOtaJobDocumentFields_t * const pFileContext ); /** - * @brief Closes the firmware image after reading or writing. + * @brief Authenticate and close the underlying receive file in the specified OTA context. + * + * @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. + * The file signature key is required job document field in the OTA Agent, so pFileContext->pSignature will + * never be NULL. * - * @param[in] pFileContext Pointer to a context containing firmware image details. - * @return OtaPalSuccess if successful, else OTA error code along with detailed PAL error code. + * If the signature verification fails, file close should still be attempted. + * + * @param[in] pFileContext OTA file context information. + * + * @return The OtaPalStatus_t error code is a combination of the main OTA PAL interface error and + * 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. + * + * Major error codes returned are: + * + * OtaPalSuccess on success. + * OtaPalSignatureCheckFailed: The signature check failed for the specified file. + * OtaPalBadSignerCert: The signer certificate was not readable or zero length. + * OtaPalFileClose: Error in low level file close. */ -OtaPalStatus_t xOtaPalCloseFile( OtaFileContext_t * const pFileContext ); - -#define otaPal_CloseFile xOtaPalCloseFile +OtaPalStatus_t otaPal_CloseFile( AfrOtaJobDocumentFields_t * const pFileContext ); /** - * @brief Prepares a new firmware image to be written. + * @brief Write a block of data to the specified file at the given offset. + * + * @note The input OtaFileContext_t pFileContext is checked for NULL by the OTA agent before this + * function is called. + * The file pointer/handle pFileContext->pFile, is checked for NULL by the OTA agent before this + * function is called. + * pData is checked for NULL by the OTA agent before this function is called. + * blockSize is validated for range by the OTA agent before this function is called. + * offset is validated by the OTA agent before this function is called. * - * @param[in] pFileContext Pointer to a context containing firmware image details. - * @return OtaPalSuccess if successful, else OTA error code along with detailed PAL error code. + * @param[in] pFileContext OTA file context information. + * @param[in] ulOffset Byte offset to write to from the beginning of the file. + * @param[in] pData Pointer to the byte array of data to write. + * @param[in] ulBlockSize The number of bytes to write. + * + * @return The number of bytes written successfully, or a negative error code from the platform + * abstraction layer. */ -OtaPalStatus_t xOtaPalCreateFileForRx( OtaFileContext_t * const pFileContext ); - -#define otaPal_CreateFileForRx xOtaPalCreateFileForRx +int16_t otaPal_WriteBlock( AfrOtaJobDocumentFields_t * const pFileContext, + uint32_t ulOffset, + uint8_t * const pcData, + uint32_t ulBlockSize ); /** - * @brief Aborts the current firmware image being written. + * @brief Activate the newest MCU image received via OTA. + * + * This function shall take necessary actions to activate the newest MCU + * firmware received via OTA. It is typically just a reset of the device. + * + * @note This function SHOULD NOT return. If it does, the platform does not support + * an automatic reset or an error occurred. + * + * @param[in] pFileContext OTA file context information. + * + * @return The OtaPalStatus_t error code is a combination of the main OTA PAL interface error and + * 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. + * + * Major error codes returned are: * - * @param[in] pFileContext Pointer to a context containing firmware image details. - * @return OtaPalSuccess if successful, else OTA error code along with detailed PAL error code. + * OtaPalSuccess on success. + * OtaPalActivateFailed: The activation of the new OTA image failed. */ -OtaPalStatus_t xOtaPalAbort( OtaFileContext_t * const pFileContext ); - -#define otaPal_Abort xOtaPalAbort - +OtaPalStatus_t otaPal_ActivateNewImage( AfrOtaJobDocumentFields_t * const pFileContext ); /** - * @brief Opens the firmware image for reading - * The API is used to verify the image signature after it has been written. + * @brief Attempt to set the state of the OTA update image. + * + * Take required actions on the platform to Accept/Reject the OTA update image (or bundle). + * Refer to the PAL implementation to determine what happens on your platform. + * + * @param[in] pFileContext File context of type OtaFileContext_t. + * @param[in] eState The desired state of the OTA update image. + * + * @return The OtaPalStatus_t error code is a combination of the main OTA PAL interface error and + * 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. + * + * Major error codes returned are: * - * @param[in] pFileContext Pointer to a context containing firmware image details. - * @return OtaPalSuccess if successful, else OTA error code along with detailed PAL error code. + * OtaPalSuccess on success. + * OtaPalBadImageState: if you specify an invalid OtaImageState_t. No sub error code. + * OtaPalAbortFailed: failed to roll back the update image as requested by OtaImageStateAborted. + * 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 xOtaPalOpenFileForRead( OtaFileContext_t * const pContext ); - -#define otaPal_OpenFileForRead xOtaPalOpenFileForRead +OtaPalStatus_t otaPal_SetPlatformImageState( AfrOtaJobDocumentFields_t * const pFileContext, + OtaImageState_t eState ); /** - * @brief Reads a block of firmware image from flash + * @brief Get the state of the OTA update image. + * + * We read this at OTA_Init time and when the latest OTA job reports itself in self + * test. If the update image is in the "pending commit" state, we start a self test + * timer to assure that we can successfully connect to the OTA services and accept + * the OTA update image within a reasonable amount of time (user configurable). If + * we don't satisfy that requirement, we assume there is something wrong with the + * firmware and automatically reset the device, causing it to roll back to the + * previously known working code. + * + * If the update image state is not in "pending commit," the self test timer is + * not started. * - * @param[in] pFileContext Pointer to a context containing firmware image details. - * @param[in] offset Offset in bytes of the block with in the whole firmware image - * @param[in] pData Pointer to buffer where block is read to. - * @param[in] blockSize Size of the buffer - * @return Number of bytes of block read or < 0 if there is an error. + * @param[in] pFileContext File context of type OtaFileContext_t. + * + * @return An OtaPalImageState_t. One of the following: + * OtaPalImageStatePendingCommit (the new firmware image is in the self test phase) + * OtaPalImageStateValid (the new firmware image is already committed) + * OtaPalImageStateInvalid (the new firmware image is invalid or non-existent) + * + * NOTE: OtaPalImageStateUnknown should NEVER be returned and indicates an implementation error. */ -int32_t xOtaPalReadBlock( OtaFileContext_t * const pContext, - uint32_t offset, - uint8_t * pData, - uint16_t blockSize ); - -#define otaPal_ReadBlock xOtaPalReadBlock +OtaPalImageState_t otaPal_GetPlatformImageState( AfrOtaJobDocumentFields_t * const pFileContext ); -OtaPalStatus_t xFlashPalValidateSignature( uint8_t * pMappedAddress, - size_t mappedLength, - char * pCertificatePath, - uint8_t * pSignature, - size_t signatureLength ); +/** + * @brief Reset the device. + * + * This function shall reset the MCU and cause a reboot of the system. + * + * @note This function SHOULD NOT return. If it does, the platform does not support + * an automatic reset or an error occurred. + * + * @param[in] pFileContext OTA file context information. + * + * @return The OtaPalStatus_t error code is a combination of the main OTA PAL interface error and + * 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( AfrOtaJobDocumentFields_t * const pFileContext ); -#endif /* OTA_PAL_H */ +#endif /* ifndef OTA_PAL_H_ */ diff --git a/examples/common/ota/ota_signature_validation.c b/examples/common/ota/ota_signature_validation.c index 88cac9c..a80fda3 100644 --- a/examples/common/ota/ota_signature_validation.c +++ b/examples/common/ota/ota_signature_validation.c @@ -246,6 +246,7 @@ CK_RV xVerifyImageSignatureUsingPKCS11( CK_SESSION_HANDLE session, OtaPalStatus_t xFlashPalValidateSignature( uint8_t * pMappedAddress, size_t mappedLength, char * pCertificatePath, + size_t certlength, uint8_t * pSignature, size_t signatureLength ) { @@ -254,6 +255,10 @@ OtaPalStatus_t xFlashPalValidateSignature( uint8_t * pMappedAddress, CK_RV xPKCS11Status = CKR_OK; CK_OBJECT_HANDLE certHandle; uint8_t pkcs11Signature[ pkcs11ECDSA_P256_SIGNATURE_LENGTH ] = { 0 }; + /* TODO: Fix the label length. */ + char certfile[ 30 + 1 ] = {'\0'}; + + memcpy( certfile, pCertificatePath, certlength ); if( PKI_mbedTLSSignatureToPkcs11Signature( pkcs11Signature, pSignature ) != 0 ) { diff --git a/examples/common/ota/ota_update.c b/examples/common/ota/ota_update.c index 434e95a..6673a04 100644 --- a/examples/common/ota/ota_update.c +++ b/examples/common/ota/ota_update.c @@ -52,15 +52,24 @@ /* MQTT library includes. */ #include "core_mqtt_agent.h" -/* OTA Library include. */ -#include "ota.h" +/* MQTT streams Library include. */ +#include "MQTTFileDownloader.h" +#include "MQTTFileDownloader_base64.h" -/* OTA Library Interface include. */ -#include "ota_os_freertos.h" -#include "ota_mqtt_interface.h" +/* jobs Library include. */ +#include "jobs.h" + +/* OTA job parser include. */ +#include "job_parser.h" +#include "ota_job_processor.h" /* Include firmware version struct definition. */ -#include "ota_appversion32.h" +//#include "ota_appversion32.h" + +#include "ota_demo.h" +#include "ota_os_freertos.h" + +#include "mqtt_wrapper.h" /* Include platform abstraction header. */ #include "ota_pal.h" @@ -168,7 +177,19 @@ /** * @brief Maximum stack size of OTA agent task. */ -#define otaexampleAGENT_TASK_STACK_SIZE ( 4096 ) +#define otaexampleAGENT_TASK_STACK_SIZE ( 4096 * 2 ) + + +#define CONFIG_MAX_FILE_SIZE 200 /* TODO:!! */ +#define NUM_OF_BLOCKS_REQUESTED 1U +#define START_JOB_MSG_LENGTH 147U +#define MAX_THING_NAME_SIZE 128U +#define MAX_JOB_ID_LENGTH 64U +#define UPDATE_JOB_MSG_LENGTH 48U +#define MAX_NUM_OF_OTA_DATA_BUFFERS 2U + +/* Max bytes supported for a file signature (3072 bit RSA is 384 bytes). */ +#define OTA_MAX_SIGNATURE_SIZE ( 384U ) /** * @brief Defines the structure to use as the command callback context in this @@ -180,114 +201,22 @@ struct MQTTAgentCommandContext void * pArgs; }; -/** - * @brief Function used by OTA agent to publish control messages to the MQTT broker. - * - * The implementation uses MQTT agent to queue a publish request. It then waits - * for the request complete notification from the agent. The notification along with result of the - * operation is sent back to the caller task using xTaskNotify API. For publishes involving QOS 1 and - * QOS2 the operation is complete once an acknowledgment (PUBACK) is received. OTA agent uses this function - * to fetch new job, provide status update and send other control related messages to the MQTT broker. - * - * @param[in] pacTopic Topic to publish the control packet to. - * @param[in] topicLen Length of the topic string. - * @param[in] pMsg Message to publish. - * @param[in] msgSize Size of the message to publish. - * @param[in] qos Qos for the publish. - * @return OtaMqttSuccess if successful. Appropriate error code otherwise. - */ -static OtaMqttStatus_t prvMQTTPublish( const char * const pacTopic, - uint16_t topicLen, - const char * pMsg, - uint32_t msgSize, - uint8_t qos ); - - -/** - * @brief The callback invoked by the MQTT agent on a successful subscription of a topic filter - * with broker. - * The implementation adds a local subscription for the topic filter with the MQTT agent. - * - * @param pCommandContext Pointer to the command context passed from the caller. - * @param pxReturnInfo Pointer to the return status of the subscribe command. - */ -static void prvSubscribeCommandCallback( MQTTAgentCommandContext_t * pxCommandContext, - MQTTAgentReturnInfo_t * pxReturnInfo ); - -/** - * @brief The callback invoked by the MQTT agent on unsubscribing a topic filter - * with broker. - * The implementation removes the local subscription for the topic filter with the MQTT agent. - * - * @param pCommandContext Pointer to the command context passed from the caller. - * @param pxReturnInfo Pointer to the return status of the unsubscribe command. - */ -static void prvUnSubscribeCommandCallback( MQTTAgentCommandContext_t * pxCommandContext, - MQTTAgentReturnInfo_t * pxReturnInfo ); - -/** - * @brief Function used by OTA agent to subscribe for a control or data packet from the MQTT broker. - * - * The implementation queues a SUBSCRIBE request for the topic filter with the MQTT agent. It then waits for - * a notification of the request completion. Notification will be sent back to caller task, - * using xTaskNotify APIs. MQTT agent also stores a callback provided by this function with - * the associated topic filter. The callback will be used to - * route any data received on the matching topic to the OTA agent. OTA agent uses this function - * to subscribe to all topic filters necessary for receiving job related control messages as - * well as firmware image chunks from MQTT broker. - * - * @param[in] pTopicFilter The topic filter used to subscribe for packets. - * @param[in] topicFilterLength Length of the topic filter string. - * @param[in] ucQoS Intended qos value for the messages received on this topic. - * @return OtaMqttSuccess if successful. Appropriate error code otherwise. - */ -static OtaMqttStatus_t prvMQTTSubscribe( const char * pTopicFilter, - uint16_t topicFilterLength, - uint8_t ucQoS ); +static MqttFileDownloaderContext_t mqttFileDownloaderContext = { 0 }; +static uint32_t numOfBlocksRemaining = 0; +static uint32_t currentBlockOffset = 0; +static uint8_t currentFileId = 0; +static uint32_t totalBytesReceived = 0; +char globalJobId[ MAX_JOB_ID_LENGTH ] = { 0 }; -/** - * @brief Function is used by OTA agent to unsubscribe a topicfilter from MQTT broker. - * - * The implementation queues an UNSUBSCRIBE request for the topic filter with the MQTT agent. It then waits - * for a successful completion of the request from the agent. Notification along with results of - * operation is sent using xTaskNotify API to the caller task. MQTT agent also removes the topic filter - * subscription from its memory so any future - * packets on this topic will not be routed to the OTA agent. - * - * @param[in] pTopicFilter Topic filter to be unsubscribed. - * @param[in] topicFilterLength Length of the topic filter. - * @param[in] ucQos Qos value for the topic. - * @return OtaMqttSuccess if successful. Appropriate error code otherwise. - * - */ -static OtaMqttStatus_t prvMQTTUnsubscribe( const char * pTopicFilter, - uint16_t topicFilterLength, - uint8_t ucQoS ); +static SemaphoreHandle_t bufferSemaphore; -/** - * @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 unused buffer. NULL if there are no buffers available. - */ -static OtaEventData_t * prvOTAEventBufferGet( void ); +static OtaDataEvent_t dataBuffers[MAX_NUM_OF_OTA_DATA_BUFFERS] = { 0 }; +static OtaJobEventData_t jobDocBuffer = { 0 }; +static AfrOtaJobDocumentFields_t jobFields = { 0 }; +static uint8_t OtaImageSingatureDecoded[OTA_MAX_SIGNATURE_SIZE] = { 0 }; +static SemaphoreHandle_t bufferSemaphore; -/** - * @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 ); +static OtaState_t otaAgentState = OtaAgentStateInit; /** * @brief The function which runs the OTA agent task. @@ -314,101 +243,77 @@ static void prvOTAAgentTask( void * pvParam ); void vOTAUpdateTask( void * pvParam ); /** - * @brief Callback to receive either data or control messages on OTA topics. - * - * Function can get invoked for a job notification message or a data message from MQTT broker. - * Function matches the topic of the message with list of topic filters and routes the message to - * either control message or data message processing functions. - * - * @param[in] pxSubscriptionContext Context which is passed unmodified from the MQTT agent. - * @param[in] pPublishInfo Pointer to the structure containing the details of the MQTT packet. + * @brief This is in essence the OTA agent implementation. */ -static void prvProcessIncomingMessage( void * pxSubscriptionContext, - MQTTPublishInfo_t * pxPublishInfo ); +static void processOTAEvents( void ); /** - * @brief Callback invoked for data messages received from MQTT broker. - * - * Function gets invoked for the firmware image blocks received on OTA data stream topic. - * The function is registered with MQTT agent's subscription manger along with the - * topic filter for data stream. For each packet received, the - * function fetches a free event buffer from the pool and queues the firmware image chunk for - * OTA agent task processing. - * - * @param[in] pxSubscriptionContext Context which is passed unmodified from the MQTT agent. - * @param[in] pPublishInfo Pointer to the structure containing the details of the MQTT packet. + * @brief */ -static void prvProcessIncomingData( void * pxSubscriptionContext, - MQTTPublishInfo_t * pPublishInfo ); +static bool imageActivationHandler( void ); /** - * @brief Callback invoked for job control messages from MQTT broker. - * - * Callback gets invoked for any OTA job related control messages from the MQTT broker. - * The function is registered with MQTT agent's subscription manger along with the topic filter for - * job stream. The function fetches a free event buffer from the pool and queues the appropriate event type - * based on the control message received. - * - * @param[in] pxSubscriptionContext Context which is passed unmodified from the MQTT agent. - * @param[in] pPublishInfo Pointer to the structure containing the details of MQTT packet. + * @brief */ -static void prvProcessIncomingJobMessage( void * pxSubscriptionContext, - MQTTPublishInfo_t * pPublishInfo ); +static bool closeFileHandler( void ); /** - * @brief Matches a client identifier within an OTA topic. - * This function is used to validate that topic is valid and intended for this device thing name. - * - * @param[in] pTopic Pointer to the topic - * @param[in] topicNameLength length of the topic - * @param[in] pClientIdentifier Client identifier, should be null terminated. - * @param[in] clientIdentifierLength Length of the client identifier. - * @return pdTRUE if client identifier is found within the topic at the right index. + * @brief + */ +static OtaPalJobDocProcessingResult_t receivedJobDocumentHandler( OtaJobEventData_t * jobDoc ); + +/** + * @brief + */ +static uint16_t getFreeOTABuffers( void ); + +/** + * @brief + */ +static void freeOtaDataEventBuffer( OtaDataEvent_t * const pxBuffer ); + +/** + * @brief */ -static BaseType_t prvMatchClientIdentifierInTopic( const char * pTopic, - size_t topicNameLength, - const char * pClientIdentifier, - size_t clientIdentifierLength ); +static OtaDataEvent_t * getOtaDataEventBuffer( void ); +/** + * @brief + */ +static void requestDataBlock( void ); /** - * @brief Buffer used to store the firmware image file path. - * Buffer is passed to the OTA agent during initialization. + * @brief */ -static uint8_t updateFilePath[ otaexampleMAX_FILE_PATH_SIZE ]; +static int16_t handleMqttStreamsBlockArrived( uint8_t *data, + size_t dataLength ); /** - * @brief Buffer used to store the code signing certificate file path. - * Buffer is passed to the OTA agent during initialization. + * @brief */ -static uint8_t certFilePath[ otaexampleMAX_FILE_PATH_SIZE ]; +static bool convertSignatureToDER(AfrOtaJobDocumentFields_t *jobFields); /** - * @brief Buffer used to store the name of the data stream. - * Buffer is passed to the OTA agent during initialization. + * @brief */ -static uint8_t streamName[ otaexampleMAX_STREAM_NAME_SIZE ]; +static void initMqttDownloader( AfrOtaJobDocumentFields_t *jobFields ); /** - * @brief Buffer used decode the CBOR message from the MQTT payload. - * Buffer is passed to the OTA agent during initialization. + * @brief */ -static uint8_t decodeMem[ ( 1U << otaconfigLOG2_FILE_BLOCK_SIZE ) ]; +static void requestJobDocumentHandler( void ); /** - * @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. + * @brief */ -static uint8_t bitmap[ OTA_MAX_BLOCK_BITMAP_SIZE ]; +static bool jobDocumentParser( char * message, size_t messageLength, AfrOtaJobDocumentFields_t *jobFields ); + /** - * @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. + * @brief */ -static OtaEventData_t eventBuffer[ otaconfigMAX_NUM_OTA_DATA_BUFFERS ] = { 0 }; +static bool sendSuccessMessage( void ); + /* * @brief Mutex used to manage thread safe access of OTA event buffers. @@ -420,23 +325,6 @@ static SemaphoreHandle_t xBufferSemaphore; */ extern MQTTAgentContext_t xGlobalMqttAgentContext; -/** - * @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 Structure used for encoding firmware version. @@ -455,717 +343,745 @@ const AppVersion32_t appFirmwareVersion = static char * pcThingName = NULL; static size_t xThingNameLength = 0U; +/*-----------------------------------------------------------*/ -/*---------------------------------------------------------*/ - -static void prvOTAEventBufferFree( OtaEventData_t * const pxBuffer ) +static void prvOTAAgentTask( void * pvParam ) { - if( xSemaphoreTake( xBufferSemaphore, portMAX_DELAY ) == pdTRUE ) - { - pxBuffer->bufferUsed = false; - ( void ) xSemaphoreGive( xBufferSemaphore ); - } - else - { - LogError( ( "Failed to get buffer semaphore." ) ); - } -} + BaseType_t xResult; + size_t xValueLength = 0U; + char * pcValue = NULL; -/*-----------------------------------------------------------*/ + LogError( "Running OTA Agent task. Waiting..." ); -static OtaEventData_t * prvOTAEventBufferGet( void ) -{ - uint32_t ulIndex = 0; - OtaEventData_t * pFreeBuffer = NULL; + while( 1 ) + { + xResult = xWaitForMQTTAgentState( MQTT_AGENT_STATE_CONNECTED, + portMAX_DELAY ); - if( xSemaphoreTake( xBufferSemaphore, portMAX_DELAY ) == pdTRUE ) - { - for( ulIndex = 0; ulIndex < otaconfigMAX_NUM_OTA_DATA_BUFFERS; ulIndex++ ) - { - if( eventBuffer[ ulIndex ].bufferUsed == false ) - { - eventBuffer[ ulIndex ].bufferUsed = true; - pFreeBuffer = &eventBuffer[ ulIndex ]; - break; - } - } + if( xResult == pdTRUE ) + { + break; + } + } - ( void ) xSemaphoreGive( xBufferSemaphore ); - } - else - { - LogError( ( "Failed to get buffer semaphore." ) ); - } + /* Load broker thing name from the key store. */ + xValueLength = KVStore_getValueLength( KVS_CORE_THING_NAME ); - return pFreeBuffer; -} + if( xValueLength > 0 ) + { + pcValue = pvPortMalloc( xValueLength + 1 ); + + if( pcValue != NULL ) + { + ( void ) KVStore_getString( KVS_CORE_THING_NAME, pcValue, ( xValueLength + 1 ) ); + } + } + + mqttWrapper_setThingName( pcValue, xValueLength ); + + vPortFree( pcValue ); + + otaDemo_start(); -/*-----------------------------------------------------------*/ -static void prvOTAAgentTask( void * pvParam ) -{ - OTA_EventProcessingTask( pvParam ); vTaskDelete( NULL ); } /*-----------------------------------------------------------*/ -/** - * @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 aws_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 ) +void otaDemo_start( void ) { - OtaErr_t err = OtaErrUninitialized; + OtaEventMsg_t initEvent = { 0 }; - switch( event ) + if( !mqttWrapper_isConnected() ) { - 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. - */ - err = OTA_ActivateNewImage(); + LogInfo("MQTT not connected, exiting!"); + return; + } - /** - * 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." ) ); + bufferSemaphore = xSemaphoreCreateMutex(); - /* 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 ); + if( bufferSemaphore != NULL ) + { + memset( dataBuffers, 0x00, sizeof( dataBuffers ) ); + } + LogError("Starting OTA thread."); - break; + OtaInitEvent_FreeRTOS(); - case OtaJobEventFail: + initEvent.eventId = OtaAgentEventRequestJobDocument; + OtaSendEvent_FreeRTOS( &initEvent ); - /** - * No user action is needed here. OTA agent handles the job failure event. - */ - LogInfo( ( "Received an OtaJobEventFail notification from OTA Agent." ) ); + while (otaAgentState != OtaAgentStateStopped ) { + processOTAEvents(); + } +} - break; +/*-----------------------------------------------------------*/ - case OtaJobEventStartTest: +static void requestJobDocumentHandler( void ) +{ + char thingName[ MAX_THING_NAME_SIZE + 1 ] = { 0 }; + size_t thingNameLength = 0U; + char topicBuffer[ TOPIC_BUFFER_SIZE + 1 ] = { 0 }; + char messageBuffer[ START_JOB_MSG_LENGTH ] = { 0 }; + size_t topicLength = 0U; + mqttWrapper_getThingName( thingName, &thingNameLength ); - /* 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. */ + /* + * 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, + thingName, + ( uint16_t ) thingNameLength, + &topicLength); - LogInfo( ( "Received OtaJobEventStartTest callback from OTA Agent." ) ); + /* + * 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("test", + 4U, + messageBuffer, + START_JOB_MSG_LENGTH ); - err = OTA_SetImageState( OtaImageStateAccepted ); + mqttWrapper_publish(topicBuffer, + topicLength, + ( uint8_t * ) messageBuffer, + messageLength); - 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: +static void initMqttDownloader( AfrOtaJobDocumentFields_t *jobFields ) +{ + char thingName[ MAX_THING_NAME_SIZE + 1 ] = { 0 }; + size_t thingNameLength = 0U; - LogDebug( ( "OTA Event processing completed. Freeing the event buffer to pool." ) ); - configASSERT( pData != NULL ); - prvOTAEventBufferFree( ( OtaEventData_t * ) pData ); + 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; - break; + mqttWrapper_getThingName( thingName, &thingNameLength ); - case OtaJobEventSelfTestFailed: - LogDebug( ( "Received OtaJobEventSelfTestFailed callback from OTA Agent." ) ); + /* + * 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, + thingName, + thingNameLength, + DATA_TYPE_JSON ); + + mqttWrapper_subscribe( mqttFileDownloaderContext.topicStreamData, + mqttFileDownloaderContext.topicStreamDataLength ); +} - /* 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 ); +static bool convertSignatureToDER(AfrOtaJobDocumentFields_t *jobFields) +{ + bool returnVal = true; + size_t decodedSignatureLength = 0; - break; - default: - LogWarn( ( "Received an unhandled callback event from OTA Agent, event = %d", event ) ); + Base64Status_t xResult = base64_Decode( OtaImageSingatureDecoded, + sizeof(OtaImageSingatureDecoded), + &decodedSignatureLength, + jobFields->signature, + jobFields->signatureLen ); - break; + if( xResult == Base64Success ) + { + jobFields->signature = OtaImageSingatureDecoded; + jobFields->signatureLen = decodedSignatureLength; } + else + { + returnVal = false; + } + + return returnVal; } /*-----------------------------------------------------------*/ -static void prvProcessIncomingMessage( void * pxSubscriptionContext, - MQTTPublishInfo_t * pxPublishInfo ) +static int16_t handleMqttStreamsBlockArrived( uint8_t *data, + size_t dataLength ) { - bool isMatched = false; + int16_t writeblockRes = -1; - { - ( void ) MQTT_MatchTopic( pxPublishInfo->pTopicName, - pxPublishInfo->topicNameLength, - OTA_JOB_NOTIFY_TOPIC_FILTER, - OTA_JOB_NOTIFY_TOPIC_FILTER_LENGTH, - &isMatched ); + LogInfo( "Downloaded block %u of %u. \n", currentBlockOffset, (currentBlockOffset + numOfBlocksRemaining) ); - if( isMatched == true ) - { - prvProcessIncomingJobMessage( pxSubscriptionContext, pxPublishInfo ); - } - } + writeblockRes = otaPal_WriteBlock( &jobFields, + totalBytesReceived, + data, + dataLength ); - if( isMatched == false ) + if( writeblockRes > 0 ) { - ( void ) MQTT_MatchTopic( pxPublishInfo->pTopicName, - pxPublishInfo->topicNameLength, - OTA_DATA_STREAM_TOPIC_FILTER, - OTA_DATA_STREAM_TOPIC_FILTER_LENGTH, - &isMatched ); - - if( isMatched == true ) - { - prvProcessIncomingData( pxSubscriptionContext, pxPublishInfo ); - } + totalBytesReceived += writeblockRes; } + + return writeblockRes; } /*-----------------------------------------------------------*/ -static void prvProcessIncomingData( void * pxSubscriptionContext, - MQTTPublishInfo_t * pPublishInfo ) +static void requestDataBlock( void ) { - BaseType_t isMatch = pdFALSE; - OtaEventData_t * pData; - OtaEventMsg_t eventMsg = { 0 }; - + char getStreamRequest[ GET_STREAM_REQUEST_BUFFER_SIZE ]; + size_t getStreamRequestLength = 0U; - configASSERT( pPublishInfo != NULL ); - ( void ) pxSubscriptionContext; + /* + * 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 ); + + mqttWrapper_publish( mqttFileDownloaderContext.topicGetStream, + mqttFileDownloaderContext.topicGetStreamLength, + ( uint8_t * ) getStreamRequest, + getStreamRequestLength ); +} - isMatch = prvMatchClientIdentifierInTopic( pPublishInfo->pTopicName, - pPublishInfo->topicNameLength, - pcThingName, - xThingNameLength ); +/*-----------------------------------------------------------*/ - if( isMatch == pdTRUE ) - { - if( pPublishInfo->payloadLength <= OTA_DATA_BLOCK_SIZE ) - { - pData = prvOTAEventBufferGet(); +static bool closeFileHandler( void ) +{ + return ( OtaPalSuccess == otaPal_CloseFile( &jobFields ) ); +} - if( pData != NULL ) - { - memcpy( pData->data, pPublishInfo->pPayload, pPublishInfo->payloadLength ); - pData->dataLength = pPublishInfo->payloadLength; - eventMsg.eventId = OtaAgentEventReceivedFileBlock; - eventMsg.pEventData = pData; +/*-----------------------------------------------------------*/ - /* Send job document received event. */ - OTA_SignalEvent( &eventMsg ); - } - else - { - LogError( ( "Error: No OTA data buffers available.\r\n" ) ); - } - } - else - { - LogError( ( "Received OTA data block of size (%d) larger than maximum size(%d) defined. ", - pPublishInfo->payloadLength, - OTA_DATA_BLOCK_SIZE ) ); - } - } - else - { - LogWarn( ( "Received data block on an unsolicited topic, thing name does not match. topic: %.*s ", - pPublishInfo->topicNameLength, - pPublishInfo->pTopicName ) ); - } +static bool imageActivationHandler( void ) +{ + return ( OtaPalSuccess == otaPal_ActivateNewImage( &jobFields ) ); } /*-----------------------------------------------------------*/ -static void prvProcessIncomingJobMessage( void * pxSubscriptionContext, - MQTTPublishInfo_t * pPublishInfo ) +static OtaPalJobDocProcessingResult_t receivedJobDocumentHandler( OtaJobEventData_t * jobDoc ) { - OtaEventData_t * pData; - OtaEventMsg_t eventMsg = { 0 }; - BaseType_t isMatch = pdFALSE; + bool parseJobDocument = false; + bool handled = false; + char * jobId; + const char ** jobIdptr = &jobId; + size_t jobIdLength = 0U; + OtaPalJobDocProcessingResult_t xResult = OtaPalJobDocFileCreateFailed; - ( void ) pxSubscriptionContext; - configASSERT( pPublishInfo != NULL ); + memset( &jobFields,0 , sizeof(jobFields) ); - isMatch = prvMatchClientIdentifierInTopic( pPublishInfo->pTopicName, - pPublishInfo->topicNameLength, - pcThingName, - xThingNameLength ); + /* + * AWS IoT Jobs library: + * Extracting the job ID from the received OTA job document. + */ + jobIdLength = Jobs_GetJobId( (char *)jobDoc->jobData, jobDoc->jobDataLength, jobIdptr ); - if( isMatch == pdTRUE ) + if ( jobIdLength ) { - if( pPublishInfo->payloadLength <= OTA_DATA_BLOCK_SIZE ) + if ( strncmp( globalJobId, jobId, jobIdLength ) ) { - LogInfo( ( "Received OTA job message, size: %d.\n\n", pPublishInfo->payloadLength ) ); - pData = prvOTAEventBufferGet(); + parseJobDocument = true; + strncpy( globalJobId, jobId, jobIdLength ); + } + else + { + xResult = OtaPalJobDocFileCreated; + } + } - if( pData != NULL ) - { - memcpy( pData->data, pPublishInfo->pPayload, pPublishInfo->payloadLength ); - pData->dataLength = pPublishInfo->payloadLength; - eventMsg.eventId = OtaAgentEventReceivedJobDocument; - eventMsg.pEventData = pData; + if ( parseJobDocument ) + { + handled = jobDocumentParser( (char * )jobDoc->jobData, jobDoc->jobDataLength, &jobFields ); + if( handled ) + { + initMqttDownloader( &jobFields ); - /* Send job document received event. */ - OTA_SignalEvent( &eventMsg ); + /* AWS IoT core returns the signature in a PEM format. We need to + * convert it to DER format for image signature verification. */ + + handled = convertSignatureToDER( &jobFields ); + + if( handled ) + { + xResult = otaPal_CreateFileForRx( &jobFields ); } else { - LogError( ( "Error: No OTA data buffers available.\r\n" ) ); + LogError( "Failed to decode the image signature to DER format." ); } } - else - { - LogError( ( "Received OTA job message size (%d) is larger than the OTA maximum size (%d) defined.\n\n", pPublishInfo->payloadLength, OTA_DATA_BLOCK_SIZE ) ); - } - } - else - { - LogWarn( ( "Received a job message on an unsolicited topic, thing name does not match. topic: %.*s ", - pPublishInfo->topicNameLength, - pPublishInfo->pTopicName ) ); } -} + return xResult; +} /*-----------------------------------------------------------*/ -static BaseType_t prvMatchClientIdentifierInTopic( const char * pTopic, - size_t topicNameLength, - const char * pClientIdentifier, - size_t clientIdentifierLength ) +static uint16_t getFreeOTABuffers( void ) { - BaseType_t isMatch = pdFALSE; - size_t idx; - size_t matchIdx = 0; + uint32_t ulIndex = 0; + uint16_t freeBuffers = 0; - for( idx = OTA_TOPIC_CLIENT_IDENTIFIER_START_IDX; idx < topicNameLength; idx++ ) + if( xSemaphoreTake( bufferSemaphore, portMAX_DELAY ) == pdTRUE ) { - if( matchIdx == clientIdentifierLength ) - { - if( pTopic[ idx ] == '/' ) - { - isMatch = pdTRUE; - } - - break; - } - else + for( ulIndex = 0; ulIndex < MAX_NUM_OF_OTA_DATA_BUFFERS; ulIndex++ ) { - if( pClientIdentifier[ matchIdx ] != pTopic[ idx ] ) + if( dataBuffers[ ulIndex ].bufferUsed == false ) { - break; + freeBuffers++; } } - matchIdx++; + ( void ) xSemaphoreGive( bufferSemaphore ); + } + else + { + LogInfo("Failed to get buffer semaphore. \n" ); } - return isMatch; + return freeBuffers; } /*-----------------------------------------------------------*/ -static void prvCommandCallback( MQTTAgentCommandContext_t * pCommandContext, - MQTTAgentReturnInfo_t * pxReturnInfo ) +static void freeOtaDataEventBuffer( OtaDataEvent_t * const pxBuffer ) { - if( pCommandContext->xTaskToNotify != NULL ) + if( xSemaphoreTake( bufferSemaphore, portMAX_DELAY ) == pdTRUE ) { - xTaskNotify( pCommandContext->xTaskToNotify, ( uint32_t ) ( pxReturnInfo->returnCode ), eSetValueWithOverwrite ); + pxBuffer->bufferUsed = false; + ( void ) xSemaphoreGive( bufferSemaphore ); + } + else + { + LogInfo( "Failed to get buffer semaphore.\n" ); } } - /*-----------------------------------------------------------*/ -static void prvSubscribeCommandCallback( MQTTAgentCommandContext_t * pxCommandContext, - MQTTAgentReturnInfo_t * pxReturnInfo ) +/* Implemented for use by the MQTT library */ +bool otaDemo_handleIncomingMQTTMessage( char * topic, + size_t topicLength, + uint8_t * message, + size_t messageLength ) { - MQTTAgentSubscribeArgs_t * pxSubscribeArgs = ( MQTTAgentSubscribeArgs_t * ) pxCommandContext->pArgs; - BaseType_t xResult; - - if( pxReturnInfo->returnCode == MQTTSuccess ) - { - xResult = xAddMQTTTopicFilterCallback( pxSubscribeArgs->pSubscribeInfo[ 0 ].pTopicFilter, - pxSubscribeArgs->pSubscribeInfo[ 0 ].topicFilterLength, - prvProcessIncomingMessage, - NULL, - pdFALSE ); - configASSERT( xResult == pdTRUE ); - } + OtaEventMsg_t nextEvent = { 0 }; + char thingName[ MAX_THING_NAME_SIZE + 1 ] = { 0 }; + size_t thingNameLength = 0U; - if( pxCommandContext->xTaskToNotify != NULL ) - { - xTaskNotify( pxCommandContext->xTaskToNotify, ( uint32_t ) ( pxReturnInfo->returnCode ), eSetValueWithOverwrite ); - } + /* + * MQTT streams Library: + * Checks if the incoming message contains the requested data block. It is performed by + * comparing the incoming MQTT message topic with MQTT streams topics. + */ + bool handled = mqttDownloader_isDataBlockReceived(&mqttFileDownloaderContext, topic, topicLength); + + if (handled) + { + nextEvent.eventId = OtaAgentEventReceivedFileBlock; + OtaDataEvent_t * dataBuf = getOtaDataEventBuffer(); + memcpy(dataBuf->data, message, messageLength); + nextEvent.dataEvent = dataBuf; + dataBuf->dataLength = messageLength; + OtaSendEvent_FreeRTOS( &nextEvent ); + } + else + { + mqttWrapper_getThingName(thingName, &thingNameLength); + /* + * AWS IoT Jobs library: + * Checks if a message comes from the start-next/accepted reserved topic. + */ + handled = Jobs_IsStartNextAccepted( topic, + topicLength, + thingName, + thingNameLength ); + + if( handled ) + { + memcpy(jobDocBuffer.jobData, message, messageLength); + nextEvent.jobEvent = &jobDocBuffer; + jobDocBuffer.jobDataLength = messageLength; + nextEvent.eventId = OtaAgentEventReceivedJobDocument; + OtaSendEvent_FreeRTOS( &nextEvent ); + } + } + + return handled; } /*-----------------------------------------------------------*/ -static void prvUnSubscribeCommandCallback( MQTTAgentCommandContext_t * pxCommandContext, - MQTTAgentReturnInfo_t * pxReturnInfo ) +static bool sendSuccessMessage( void ) { - MQTTAgentSubscribeArgs_t * pxSubscribeArgs = ( MQTTAgentSubscribeArgs_t * ) pxCommandContext->pArgs; + char thingName[ MAX_THING_NAME_SIZE + 1 ] = { 0 }; + size_t thingNameLength = 0U; + char topicBuffer[ TOPIC_BUFFER_SIZE + 1 ] = { 0 }; + size_t topicBufferLength = 0U; + char messageBuffer[ UPDATE_JOB_MSG_LENGTH ] = { 0 }; + bool result = true; + JobsStatus_t jobStatusResult; + + mqttWrapper_getThingName( thingName, &thingNameLength ); - if( pxReturnInfo->returnCode == MQTTSuccess ) + /* + * AWS IoT Jobs library: + * Creating the MQTT topic to update the status of OTA job. + */ + jobStatusResult = Jobs_Update( topicBuffer, + TOPIC_BUFFER_SIZE, + thingName, + ( uint16_t ) thingNameLength, + globalJobId, + ( uint16_t ) strnlen( globalJobId, 1000U ), + &topicBufferLength ); + + if( jobStatusResult == JobsSuccess ) { - vRemoveMQTTTopicFilterCallback( pxSubscribeArgs->pSubscribeInfo[ 0 ].pTopicFilter, - pxSubscribeArgs->pSubscribeInfo[ 0 ].topicFilterLength ); + /* + * 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 ); + + result = mqttWrapper_publish( topicBuffer, + topicBufferLength, + ( uint8_t * ) messageBuffer, + messageBufferLength ); + + LogInfo( "\033[1;32mOTA Completed successfully!\033[0m\n" ); + globalJobId[ 0 ] = 0U; + + /* Clean up the job doc buffer so that it is ready for when we + * receive new job doc. */ + memset( &jobDocBuffer, 0, sizeof( jobDocBuffer ) ); } - - if( pxCommandContext->xTaskToNotify != NULL ) + else { - xTaskNotify( pxCommandContext->xTaskToNotify, ( uint32_t ) ( pxReturnInfo->returnCode ), eSetValueWithOverwrite ); + result = false; } + + return result; } /*-----------------------------------------------------------*/ - -static OtaMqttStatus_t prvMQTTSubscribe( const char * pTopicFilter, - uint16_t topicFilterLength, - uint8_t ucQoS ) +static bool jobDocumentParser( char * message, size_t messageLength, AfrOtaJobDocumentFields_t *jobFields ) { - MQTTStatus_t mqttStatus; - uint32_t ulNotifiedValue; - MQTTAgentSubscribeArgs_t xSubscribeArgs = { 0 }; - MQTTSubscribeInfo_t xSubscribeInfo = { 0 }; - BaseType_t result; - MQTTAgentCommandInfo_t xCommandParams = { 0 }; - MQTTAgentCommandContext_t xCommandContext = { 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; - - xCommandContext.pArgs = ( void * ) ( &xSubscribeArgs ); - xCommandContext.xTaskToNotify = xTaskGetCurrentTaskHandle(); - - xCommandParams.blockTimeMs = otaexampleMQTT_TIMEOUT_MS; - xCommandParams.cmdCompleteCallback = prvSubscribeCommandCallback; - xCommandParams.pCmdCompleteCallbackContext = ( void * ) &xCommandContext; + const char * jobDoc; + size_t jobDocLength = 0U; + int8_t fileIndex = 0; /* - * Wait for Agent to be connected before sending a subscribe message. + * AWS IoT Jobs library: + * Extracting the OTA job document from the jobs message recevied from AWS IoT core. */ - if( xGetMQTTAgentState() != MQTT_AGENT_STATE_CONNECTED ) + jobDocLength = Jobs_GetJobDocument( message, messageLength, &jobDoc ); + + if( jobDocLength != 0U ) { - ( void ) xWaitForMQTTAgentState( MQTT_AGENT_STATE_CONNECTED, portMAX_DELAY ); + 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 ); } - xTaskNotifyStateClear( NULL ); + /* File index will be -1 if an error occured, and 0 if all files were + * processed. */ + return fileIndex == 0; +} - mqttStatus = MQTTAgent_Subscribe( &xGlobalMqttAgentContext, - &xSubscribeArgs, - &xCommandParams ); +/*-----------------------------------------------------------*/ - /* Block 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, portMAX_DELAY ); +static OtaDataEvent_t * getOtaDataEventBuffer( void ) +{ + uint32_t ulIndex = 0; + OtaDataEvent_t * freeBuffer = NULL; - if( result == pdTRUE ) - { - mqttStatus = ( MQTTStatus_t ) ( ulNotifiedValue ); - } - else + if( xSemaphoreTake( bufferSemaphore, portMAX_DELAY ) == pdTRUE ) + { + for( ulIndex = 0; ulIndex < MAX_NUM_OF_OTA_DATA_BUFFERS; ulIndex++ ) { - mqttStatus = MQTTRecvFailed; + if( dataBuffers[ ulIndex ].bufferUsed == false ) + { + dataBuffers[ ulIndex ].bufferUsed = true; + freeBuffer = &dataBuffers[ ulIndex ]; + break; + } } - } - if( mqttStatus != MQTTSuccess ) - { - LogError( ( "Failed to SUBSCRIBE to topic with error = %u.", - mqttStatus ) ); - - otaRet = OtaMqttSubscribeFailed; + ( void ) xSemaphoreGive( bufferSemaphore ); } else { - LogInfo( ( "Subscribed to topic %.*s.\n\n", - topicFilterLength, - pTopicFilter ) ); - - otaRet = OtaMqttSuccess; + LogInfo("Failed to get buffer semaphore. \n" ); } - return otaRet; + return freeBuffer; } -static OtaMqttStatus_t prvMQTTPublish( const char * const pacTopic, - uint16_t topicLen, - const char * pMsg, - uint32_t msgSize, - uint8_t qos ) -{ - OtaMqttStatus_t otaRet = OtaMqttSuccess; - BaseType_t result; - MQTTStatus_t mqttStatus = MQTTBadParameter; - MQTTPublishInfo_t publishInfo = { MQTTQoS0 }; - MQTTAgentCommandInfo_t xCommandParams = { 0 }; - MQTTAgentCommandContext_t xCommandContext = { 0 }; - uint32_t ulNotifiedValue; - - configASSERT( qos <= MQTTQoS2 ); - - publishInfo.pTopicName = pacTopic; - publishInfo.topicNameLength = topicLen; - publishInfo.qos = ( MQTTQoS_t ) qos; - publishInfo.pPayload = pMsg; - publishInfo.payloadLength = msgSize; - - xCommandContext.xTaskToNotify = xTaskGetCurrentTaskHandle(); - - xCommandParams.blockTimeMs = otaexampleMQTT_TIMEOUT_MS; - xCommandParams.cmdCompleteCallback = prvCommandCallback; - xCommandParams.pCmdCompleteCallbackContext = ( void * ) &xCommandContext; - - /* - * Wait for Agent to be connected before sending a publish message. - */ - if( xGetMQTTAgentState() != MQTT_AGENT_STATE_CONNECTED ) - { - ( void ) xWaitForMQTTAgentState( MQTT_AGENT_STATE_CONNECTED, portMAX_DELAY ); - } - - xTaskNotifyStateClear( NULL ); - - mqttStatus = MQTTAgent_Publish( &xGlobalMqttAgentContext, - &publishInfo, - &xCommandParams ); +/*-----------------------------------------------------------*/ - /* Block for command to complete so MQTTPublishInfo_t remains in scope for the - * duration of the command. */ - if( mqttStatus == MQTTSuccess ) - { - result = xTaskNotifyWait( 0, otaexampleMAX_UINT32, &ulNotifiedValue, portMAX_DELAY ); +static void processOTAEvents( void ) +{ + OtaEventMsg_t recvEvent = { 0 }; + OtaEvent_t recvEventId = 0; + static OtaEvent_t lastRecvEventId = OtaAgentEventStart; + OtaEventMsg_t nextEvent = { 0 }; - if( result != pdTRUE ) - { - mqttStatus = MQTTSendFailed; - } - else - { - mqttStatus = ( MQTTStatus_t ) ( ulNotifiedValue ); - } - } + OtaReceiveEvent_FreeRTOS(&recvEvent); + recvEventId = recvEvent.eventId; - if( mqttStatus != MQTTSuccess ) + if( recvEventId != OtaAgentEventStart ) { - LogError( ( "Failed to send PUBLISH packet to broker with error = %u.", mqttStatus ) ); - otaRet = OtaMqttPublishFailed; + lastRecvEventId = recvEventId; } else { - LogInfo( ( "Sent PUBLISH packet to broker %.*s to broker.\n\n", - topicLen, - pacTopic ) ); - - otaRet = OtaMqttSuccess; + if( lastRecvEventId == OtaAgentEventRequestFileBlock ) + { + /* No current event and we have not received the new block + * since last timeout, try sending the request for block again. */ + recvEventId = lastRecvEventId; + + /* It is likely that the network was disconnected and reconnected, + * we should wait for the MQTT connection to go up. */ + while( !mqttWrapper_isConnected() ) + { + vTaskDelay( pdMS_TO_TICKS( 100 ) ); + } + } } - return otaRet; -} - -static OtaMqttStatus_t prvMQTTUnsubscribe( const char * pTopicFilter, - uint16_t topicFilterLength, - uint8_t ucQoS ) -{ - MQTTStatus_t mqttStatus; - uint32_t ulNotifiedValue; - MQTTAgentSubscribeArgs_t xSubscribeArgs = { 0 }; - MQTTSubscribeInfo_t xSubscribeInfo = { MQTTQoS0 }; - BaseType_t result; - MQTTAgentCommandInfo_t xCommandParams = { 0 }; - MQTTAgentCommandContext_t xCommandContext = { 0 }; - OtaMqttStatus_t otaRet = OtaMqttSuccess; - - configASSERT( pTopicFilter != NULL ); - configASSERT( topicFilterLength > 0 ); - configASSERT( ucQoS <= MQTTQoS2 ); - - xSubscribeInfo.pTopicFilter = pTopicFilter; - xSubscribeInfo.topicFilterLength = topicFilterLength; - xSubscribeInfo.qos = ( MQTTQoS_t ) ucQoS; - xSubscribeArgs.pSubscribeInfo = &xSubscribeInfo; - xSubscribeArgs.numSubscriptions = 1; + switch (recvEventId) + { + case OtaAgentEventRequestJobDocument: + LogInfo("Request Job Document event Received \n"); + LogInfo("-------------------------------------\n"); + requestJobDocumentHandler(); + otaAgentState = OtaAgentStateRequestingJob; + break; + + case OtaAgentEventReceivedJobDocument: + LogInfo("Received Job Document event Received \n"); + LogInfo("-------------------------------------\n"); + + if (otaAgentState == OtaAgentStateSuspended) + { + LogInfo("OTA-Agent is in Suspend State. Hence dropping Job Document. \n"); + break; + } + switch( receivedJobDocumentHandler(recvEvent.jobEvent) ) + { + case OtaPalJobDocFileCreated: + LogInfo( "Received OTA Job. \n" ); + nextEvent.eventId = OtaAgentEventRequestFileBlock; + OtaSendEvent_FreeRTOS( &nextEvent ); + otaAgentState = OtaAgentStateCreatingFile; + break; - xCommandContext.xTaskToNotify = xTaskGetCurrentTaskHandle(); - xCommandContext.pArgs = ( void * ) ( &xSubscribeArgs ); + case OtaPalJobDocFileCreateFailed: + case OtaPalNewImageBootFailed: + case OtaPalJobDocProcessingStateInvalid: + LogInfo("This is not an OTA job \n"); + break; - xCommandParams.blockTimeMs = otaexampleMQTT_TIMEOUT_MS; - xCommandParams.cmdCompleteCallback = prvUnSubscribeCommandCallback; - xCommandParams.pCmdCompleteCallbackContext = ( void * ) &xCommandContext; + case OtaPalNewImageBooted: + ( void ) sendSuccessMessage(); - LogInfo( ( " Unsubscribing to topic filter: %s", pTopicFilter ) ); - xTaskNotifyStateClear( NULL ); + /* Short delay before restarting the loop. */ + vTaskDelay( pdMS_TO_TICKS( 1000 ) ); + /* Get ready for new OTA job. */ + nextEvent.eventId = OtaAgentEventRequestJobDocument; + OtaSendEvent_FreeRTOS( &nextEvent ); + break; + } - mqttStatus = MQTTAgent_Unsubscribe( &xGlobalMqttAgentContext, - &xSubscribeArgs, - &xCommandParams ); + break; - /* Block 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, portMAX_DELAY ); + case OtaAgentEventRequestFileBlock: + otaAgentState = OtaAgentStateRequestingFileBlock; + LogInfo("Request File Block event Received \n"); + LogInfo("-----------------------------------\n"); + if (currentBlockOffset == 0) + { + LogInfo( "Starting The Download. \n" ); + } + requestDataBlock(); + LogInfo("ReqSent----------------------------\n"); + break; + + case OtaAgentEventReceivedFileBlock: + LogInfo("Received File Block event Received \n"); + LogInfo("---------------------------------------\n"); + if (otaAgentState == OtaAgentStateSuspended) + { + LogInfo("OTA-Agent is in Suspend State. Hence dropping File Block. \n"); + freeOtaDataEventBuffer(recvEvent.dataEvent); + break; + } + uint8_t decodedData[ mqttFileDownloader_CONFIG_BLOCK_SIZE ]; + size_t decodedDataLength = 0; + MQTTFileDownloaderStatus_t xReturnStatus; + int16_t result = -1; + int32_t fileId; + int32_t blockId; + int32_t blockSize; + static int32_t lastReceivedblockId = -1; - if( result == pdTRUE ) + /* + * MQTT streams Library: + * Extracting and decoding the received data block from the incoming MQTT message. + */ + xReturnStatus = mqttDownloader_processReceivedDataBlock( + &mqttFileDownloaderContext, + recvEvent.dataEvent->data, + recvEvent.dataEvent->dataLength, + &fileId, + &blockId, + &blockSize, + decodedData, + &decodedDataLength ); + + if( xReturnStatus != MQTTFileDownloaderSuccess ) + { + /* There was some failure in trying to decode the block. */ + } + else if( fileId != jobFields.fileId ) + { + /* Error - the file ID doesn't match with the one we received in the job document. */ + } + else if( blockSize > mqttFileDownloader_CONFIG_BLOCK_SIZE ) { - mqttStatus = ( MQTTStatus_t ) ( ulNotifiedValue ); + /* Error - the block size doesn't match with what we requested. It can be smaller as + * the last block may or may not be of exact size. */ + } + else if( blockId <= lastReceivedblockId ) + { + /* Ignore this block. */ } else { - mqttStatus = MQTTRecvFailed; + result = handleMqttStreamsBlockArrived(decodedData, decodedDataLength); + lastReceivedblockId = blockId; } - } - - 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\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 = xOtaPalGetPlatformImageState; - pOtaInterfaces->pal.setPlatformImageState = xOtaPalSetPlatformImageState; - pOtaInterfaces->pal.writeBlock = xOtaPalWriteBlock; - pOtaInterfaces->pal.activate = xOtaPalActivateNewImage; - pOtaInterfaces->pal.closeFile = xOtaPalCloseFile; - pOtaInterfaces->pal.reset = xOtaPalResetDevice; - pOtaInterfaces->pal.abort = xOtaPalAbort; - pOtaInterfaces->pal.createFile = xOtaPalCreateFileForRx; -} + freeOtaDataEventBuffer(recvEvent.dataEvent); -static void prvSuspendOTAUpdate( void ) -{ - if( ( OTA_GetState() != OtaAgentStateSuspended ) && ( OTA_GetState() != OtaAgentStateStopped ) ) - { - OTA_Suspend(); + if( result > 0 ) + { + numOfBlocksRemaining--; + currentBlockOffset++; + } - while( ( OTA_GetState() != OtaAgentStateSuspended ) && - ( OTA_GetState() != OtaAgentStateStopped ) ) + if( ( numOfBlocksRemaining % 10 ) == 0 ) { - vTaskDelay( pdMS_TO_TICKS( otaexampleTASK_DELAY_MS ) ); + LogInfo("Free OTA buffers %u", getFreeOTABuffers()); } - } -} -static void prvResumeOTAUpdate( void ) -{ - if( OTA_GetState() == OtaAgentStateSuspended ) - { - OTA_Resume(); + if( numOfBlocksRemaining == 0 ) + { + nextEvent.eventId = OtaAgentEventCloseFile; + OtaSendEvent_FreeRTOS( &nextEvent ); + } + else + { + if( currentBlockOffset % NUM_OF_BLOCKS_REQUESTED == 0 ) + { + nextEvent.eventId = OtaAgentEventRequestFileBlock; + OtaSendEvent_FreeRTOS( &nextEvent ); + } + } + break; - while( OTA_GetState() == OtaAgentStateSuspended ) + case OtaAgentEventCloseFile: + LogInfo("Close file event Received \n"); + LogInfo("-----------------------\n"); + if( closeFileHandler() == true ) { - vTaskDelay( pdMS_TO_TICKS( otaexampleTASK_DELAY_MS ) ); + nextEvent.eventId = OtaAgentEventActivateImage; + OtaSendEvent_FreeRTOS( &nextEvent ); } + break; + + case OtaAgentEventActivateImage: + LogInfo("Activate Image event Received \n"); + LogInfo("-----------------------\n"); + if( imageActivationHandler() == true ) + { + nextEvent.eventId = OtaAgentEventActivateImage; + OtaSendEvent_FreeRTOS( &nextEvent ); + } + + otaAgentState = OtaAgentStateStopped; + break; + + + case OtaAgentEventSuspend: + LogInfo("Suspend Event Received \n"); + LogInfo("-----------------------\n"); + otaAgentState = OtaAgentStateSuspended; + break; + + case OtaAgentEventResume: + LogInfo("Resume Event Received \n"); + LogInfo("---------------------\n"); + otaAgentState = OtaAgentStateRequestingJob; + nextEvent.eventId = OtaAgentEventRequestJobDocument; + OtaSendEvent_FreeRTOS( &nextEvent ); + + default: + break; } } +/*-----------------------------------------------------------*/ + void vOTAUpdateTask( void * pvParam ) { /* FreeRTOS APIs return status. */ BaseType_t xResult = pdPASS; - /* OTA library return status. */ - OtaErr_t otaRet = OtaErrNone; - - /* 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 }; - - ( void ) pvParam; - /* Set OTA Library interfaces.*/ - setOtaInterfaces( &otaInterfaces ); - LogInfo( ( "OTA over MQTT demo, Application version %u.%u.%u", appFirmwareVersion.u.x.major, appFirmwareVersion.u.x.minor, @@ -1204,36 +1120,6 @@ void vOTAUpdateTask( void * pvParam ) } } - if( xResult == pdPASS ) - { - /* - * Topic is an unsolicited topic, ie a subscription is not required with the MQTT broker. - * Messages on this topic is directly sent to client. Hence this topic filter is added as - * a static entry in the MQTT agent topic filter store. Last parameter indicates MQTT agent - * should not subscribe to topic on a reconnection. - */ - xResult = xAddMQTTTopicFilterCallback( OTA_JOB_ACCEPTED_RESPONSE_TOPIC_FILTER, - OTA_JOB_ACCEPTED_RESPONSE_TOPIC_FILTER_LENGTH, - prvProcessIncomingJobMessage, - NULL, - pdFALSE ); - } - - if( xResult == pdPASS ) - { - memset( eventBuffer, 0x00, sizeof( eventBuffer ) ); - - if( ( otaRet = OTA_Init( &otaBuffer, - &otaInterfaces, - ( const uint8_t * ) ( pcThingName ), - otaAppCallback ) ) != OtaErrNone ) - { - LogError( ( "Failed to initialize OTA Agent, exiting = %u.", - otaRet ) ); - xResult = pdFAIL; - } - } - if( xResult == pdPASS ) { if( ( xResult = xTaskCreate( prvOTAAgentTask, @@ -1249,45 +1135,10 @@ void vOTAUpdateTask( void * pvParam ) } } - /***************************Start OTA demo loop. ******************************/ - - if( xResult == pdPASS ) - { - /* Start the OTA Agent.*/ - eventMsg.eventId = OtaAgentEventStart; - OTA_SignalEvent( &eventMsg ); - - while( OTA_GetState() != OtaAgentStateStopped ) - { - /* Get OTA statistics for currently executing job. */ - OTA_GetStatistics( &otaStatistics ); - LogInfo( ( " Received: %u Queued: %u Processed: %u Dropped: %u", - otaStatistics.otaPacketsReceived, - otaStatistics.otaPacketsQueued, - otaStatistics.otaPacketsProcessed, - otaStatistics.otaPacketsDropped ) ); - - if( xWaitForMQTTAgentState( MQTT_AGENT_STATE_DISCONNECTED, - pdMS_TO_TICKS( otaexampleTASK_DELAY_MS ) ) == pdTRUE ) - { - /* Suspend ongoing OTA job if any until MQTT agent is reconnected. */ - prvSuspendOTAUpdate(); - - ( void ) xWaitForMQTTAgentState( MQTT_AGENT_STATE_CONNECTED, portMAX_DELAY ); - - /* Resume OTA Update so that agent checks for any new jobs during a lost connection. */ - prvResumeOTAUpdate(); - } - } - } + vTaskDelay( portMAX_DELAY ); LogInfo( ( "OTA agent task stopped. Exiting OTA demo." ) ); - - /* Unconditionally remove the static subscription. */ - vRemoveMQTTTopicFilterCallback( OTA_JOB_ACCEPTED_RESPONSE_TOPIC_FILTER, - OTA_JOB_ACCEPTED_RESPONSE_TOPIC_FILTER_LENGTH ); - if( pcThingName != NULL ) { vPortFree( pcThingName ); diff --git a/examples/evkbmimxrt1060/bootloader/signing_key.pem b/examples/evkbmimxrt1060/bootloader/signing_key.pem new file mode 100644 index 0000000..5bcfb0a --- /dev/null +++ b/examples/evkbmimxrt1060/bootloader/signing_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHhtP0f/G6/aRj +TzMTNZqh3DeHaZaNtSuCpjdsIG1zKDY9joGWheW4ki3Ys3uMNnTfQloryXGi0sXV +fWgCipHUNyhiFhDM8NyUGErHdKRijTJLsia9FH9QygPyjBt1RTa2F705BS6g3auq +rY+b5mpKvM7EI+lkvvsqmTJ35IMuocWJ230r0SvfzO6v7mC+qOsI5NAkVrPmfdnw +7JSJtgGuZufH06Gg0ZbXhhqRDPXae9LafdsjQtphwb8uviZHzsrX6LbCjm+o/O5T +KB6fMm+VqSeKPLTMynCnTcK+JCtSoeAkIO1SYKtq/p+PPPL5V+14xFOdwYhKVqiu +1Cs6JYlpAgMBAAECggEAPvlGd3JUTLTWSAPMtoDiI7j6C7KfMm2eZHdqoaVgdgqY +h/gs3WG4XfdBIhaP5XKgk4CBLjW/uGi2yQlyJO8wVJ7uD3swZUaWD+XU4Dvn0DUJ +TUvWnzHTdx+zfD+NQQeN244FinQgweQE1PM/krB4FsudpNlCxini4xCEFcQZRUN5 +lBtd1RFBh+ftub/hltR5v6JywQVHih53+aqhLF6Dh8mBN5hS7GemA9/Ktb0SmT6n +OArqdKjo6oOMGyTCbbLSagCsZVJToaMglP29SdE5zpAwXzqV9HciPmN3rgLJPnir +gBcVgtoOedzM4x8nJINrDGDj64caFQVily4inPEpcwKBgQD0ukM+FYdY+IPHrnoo +jWs8kMVZkwKVkfY81hsqL0RMzt9Adqsb0Pewtgl/TO3av4zdi0qSy3FKb7kQZoh7 +mhvczczX0YUUP/MmU1f3sK1T3sd9mPmaqBCPTvUFFbJTKM5GuQQfiHr/tuUEEXNR +vNnFzFm/QqhJ9ZMPjIe13U/wWwKBgQDQt5Kg18+mGUyie6UgfSg2sfFuMgeefT+A +Vx1YxDRbne2inN1EhgM39qEtgvvozB4GtUxDsEZG80WilsR7xbyuWkUUU8kE00lb +LzE17mVhORNE8hkZuNb7Fnh19Jp5FztoJp7RHmPedc/yMGp7nCSndmgJ5Uyyc4nW +CAVPGZiYiwKBgQDnpY+UqqgszT9fuYWj8Qi6kYZcXU0q6ribqUaZTZhm48JqsGkS +sWBjAkxHFThnwLzDz99MyAl6nfoczhEOfjc1cBAfkRFYs5eihW1U5QWLx+ootq49 +eCwEmoZOw9TB5nQqeWKvvm4+so5gpWv0yL+R/PZOlffZ2fvT0f97Y/sQ1QKBgAka +6QehQorBcclo0XRQYW+5ZYnkHtb4KcwEgOA9dvdENXixlSmHGx7W4IMmwqyOMwUh +5ZjSWM6BkFkecKXho3CLuZXAbfWK7hlPPrtWkg+iuMd6NEQd/yrm+QxURmTf8ZSC +ks0WoU60HwU2vX50+lhf3vwHh8gFKE8w/GJOAI0vAoGAX5xjZVoogGpsoWB/aGyB +cD4Sb9QT3c1w4UFahJnS3wlLvlag/0K5CT+hWJTZBM3r/H2UPMJOdvlzD4/cxJKP +0sqE/HKqnnPa+xeDx+yor3GnwRPClw03LkNzx1Tsc8RqoZ84K0HsX/o44mlWCmji +1/GPqY9vrOm4uchZOsPcYJU= +-----END PRIVATE KEY----- diff --git a/examples/evkbmimxrt1060/pubsub/include/FreeRTOSConfig.h b/examples/evkbmimxrt1060/pubsub/include/FreeRTOSConfig.h index 2629dd3..1fee882 100644 --- a/examples/evkbmimxrt1060/pubsub/include/FreeRTOSConfig.h +++ b/examples/evkbmimxrt1060/pubsub/include/FreeRTOSConfig.h @@ -76,6 +76,9 @@ extern void vLoggingPrintf( const char * pcFormat, #define configOVERRIDE_DEFAULT_TICK_CONFIGURATION 0 #define configRECORD_STACK_HIGH_ADDRESS 1 +/* Task notification settings. */ +#define configTASK_NOTIFICATION_ARRAY_ENTRIES ( 4 ) + /* Co-routine definitions. */ #define configUSE_CO_ROUTINES 0 #define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) diff --git a/examples/evkbmimxrt1060/pubsub/main.c b/examples/evkbmimxrt1060/pubsub/main.c index eabd49f..5ef34b1 100644 --- a/examples/evkbmimxrt1060/pubsub/main.c +++ b/examples/evkbmimxrt1060/pubsub/main.c @@ -325,6 +325,7 @@ int main( void ) } } + void vApplicationDaemonTaskStartupHook( void ) { /* Initialize file system. */ diff --git a/projects/evkmimxrt1060/bootloader/.cproject b/projects/evkmimxrt1060/bootloader/.cproject index 549607e..f2609e3 100644 --- a/projects/evkmimxrt1060/bootloader/.cproject +++ b/projects/evkmimxrt1060/bootloader/.cproject @@ -35,7 +35,7 @@ @@ -149,7 +150,7 @@