From 67aeced8b8dd492aefa4a01a8c7822a40d5ec6e6 Mon Sep 17 00:00:00 2001 From: Tiangang Song Date: Fri, 26 Jun 2020 12:08:05 -0700 Subject: [PATCH] Sync OTA library from AFR 202007.00 release to lts-development (#91) * Sync OTA library from AFR 202006.00 release * Support suspend and resume in OTA demo * Sync OTA library from AFR 202007.00 release * Update OTA config files for other projects * In OTA demo, block thread if fail to activate image * Disable downgrade in OTA by default * Update OTA demo config to match from AFR * Add an idle hook to OTA demo to prevent 100% CPU * Fix OTA config format and a mistake from last commit --- .../DemoTasks/aws_iot_ota_update_demo.c | 221 +- .../FreeRTOS_IoT_Libraries/ota/common/main.c | 7 + .../ota/ota_code_signing/FreeRTOSConfig.h | 2 +- .../ota_code_signing/aws_ota_agent_config.h | 12 +- .../ota_code_signing_http/FreeRTOSConfig.h | 2 +- .../aws_ota_agent_config.h | 12 +- .../ota/ota_no_code_signing/FreeRTOSConfig.h | 2 +- .../aws_ota_agent_config.h | 12 +- .../c_sdk/aws/ota/include/aws_iot_ota_agent.h | 723 +++--- .../c_sdk/aws/ota/include/aws_iot_ota_types.h | 2 +- .../c_sdk/aws/ota/src/aws_iot_ota_agent.c | 2260 ++++++++++------- .../aws/ota/src/aws_iot_ota_agent_internal.h | 68 +- .../c_sdk/aws/ota/src/aws_iot_ota_interface.c | 6 +- .../c_sdk/aws/ota/src/aws_iot_ota_interface.h | 2 +- .../c_sdk/aws/ota/src/aws_iot_ota_pal.h | 2 +- .../c_sdk/aws/ota/src/http/aws_iot_ota_http.c | 2 +- .../c_sdk/aws/ota/src/http/aws_iot_ota_http.h | 2 +- .../c_sdk/aws/ota/src/mqtt/aws_iot_ota_cbor.c | 2 +- .../c_sdk/aws/ota/src/mqtt/aws_iot_ota_cbor.h | 2 +- .../ota/src/mqtt/aws_iot_ota_cbor_internal.h | 2 +- .../c_sdk/aws/ota/src/mqtt/aws_iot_ota_mqtt.c | 522 ++-- .../c_sdk/aws/ota/src/mqtt/aws_iot_ota_mqtt.h | 2 +- .../config_files/aws_ota_agent_config.h | 10 + 23 files changed, 2282 insertions(+), 1595 deletions(-) diff --git a/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/common/DemoTasks/aws_iot_ota_update_demo.c b/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/common/DemoTasks/aws_iot_ota_update_demo.c index 13c2053072..bf937fa209 100755 --- a/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/common/DemoTasks/aws_iot_ota_update_demo.c +++ b/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/common/DemoTasks/aws_iot_ota_update_demo.c @@ -49,6 +49,7 @@ /* IoT SDK includes. */ #include "iot_mqtt.h" #include "platform/iot_network_freertos.h" +#include "platform/iot_clock.h" /* Required to get the broker address and port. */ #include "aws_iot_demo_profile.h" @@ -73,13 +74,51 @@ static void App_OTACompleteCallback( OTA_JobEvent_t eEvent ); */ static void prvInitialiseLibraries( void ); +/** + * @brief Delay before retrying network connection up to a maximum interval. + */ +static void _connectionRetryDelay( void ); + +/** + * @brief Initialize the libraries required for OTA demo. + * + * @return `EXIT_SUCCESS` if all libraries were successfully initialized; + * `EXIT_FAILURE` otherwise. + */ + +static void prvNetworkDisconnectCallback( void * param, + IotMqttCallbackParam_t * mqttCallbackParams ); + + +/** + * @brief Establish a new connection to the MQTT server. + * + * @param[in] awsIotMqttMode Specify if this demo is running with the AWS IoT + * MQTT server. Set this to `false` if using another MQTT server. + * @param[in] pIdentifier NULL-terminated MQTT client identifier. + * @param[in] pNetworkServerInfo Passed to the MQTT connect function when + * establishing the MQTT connection. + * @param[in] pNetworkCredentialInfo Passed to the MQTT connect function when + * establishing the MQTT connection. + * @param[in] pNetworkInterface The network interface to use for this demo. + * @param[out] pMqttConnection Set to the handle to the new MQTT connection. + * + * @return `EXIT_SUCCESS` if the connection is successfully established; `EXIT_FAILURE` + * otherwise. + */ +static int _establishMqttConnection( bool awsIotMqttMode, + IotMqttConnection_t * pMqttConnection ); /*-----------------------------------------------------------*/ -#define otaDemoCONN_TIMEOUT_MS ( 2000UL ) +#define otaDemoCONN_TIMEOUT_MS ( 2000UL ) -#define otaDemoKEEPALIVE_SECONDS ( 1200 ) +#define otaDemoKEEPALIVE_SECONDS ( 120 ) -#define myappONE_SECOND_DELAY_IN_TICKS pdMS_TO_TICKS( 1000UL ) +#define otaDemoCONN_RETRY_BASE_INTERVAL_SECONDS ( 4U ) + +#define otaDemoCONN_RETRY_MAX_INTERVAL_SECONDS ( 360U ) + +#define otaDemoTASK_DELAY_SECONDS ( 1UL ) /** * @brief OTA state machine string. @@ -143,19 +182,34 @@ static IotMqttNetworkInfo_t xNetworkInfo = /* Setup the callback which is called when the MQTT connection is * disconnected. The task handle is passed as the callback context which * is used by the callback to send a task notification to this task.*/ - .disconnectCallback.function = NULL + .disconnectCallback.function = prvNetworkDisconnectCallback }; /** * @brief The MQTT connection handle used in this example. */ static IotMqttConnection_t xMQTTConnection = IOT_MQTT_CONNECTION_INITIALIZER; + +/** + * @brief The MQTT connection info used for MQTT connection. + */ +IotMqttConnectInfo_t xConnectInfo = IOT_MQTT_CONNECT_INFO_INITIALIZER; + +/** + * @brief Flag used to unset, during disconnection of currently connected network. This will + * trigger a reconnection from the OTA demo task. + */ +volatile static bool _networkConnected = false; + +/** + * @brief Connection retry interval in seconds. + */ +static int _retryInterval = otaDemoCONN_RETRY_BASE_INTERVAL_SECONDS; /*-----------------------------------------------------------*/ void vOTAUpdateDemoTask( void * pvParameters ) { -IotMqttConnectInfo_t xConnectInfo = IOT_MQTT_CONNECT_INFO_INITIALIZER; OTA_State_t eState; OTA_ConnectionContext_t xOTAConnectionCtx = { 0 }; @@ -176,44 +230,71 @@ OTA_ConnectionContext_t xOTAConnectionCtx = { 0 }; for( ; ; ) { configPRINTF( ( "Connecting to broker...\r\n" ) ); - memset( &xConnectInfo, 0, sizeof( xConnectInfo ) ); - xConnectInfo.awsIotMqttMode = true; - xConnectInfo.keepAliveSeconds = otaDemoKEEPALIVE_SECONDS; - - xConnectInfo.cleanSession = true; - xConnectInfo.clientIdentifierLength = ( uint16_t ) strlen( awsiotdemoprofileCLIENT_IDENTIFIER ); - xConnectInfo.pClientIdentifier = awsiotdemoprofileCLIENT_IDENTIFIER; - - /* Connect to the broker. */ - if( IotMqtt_Connect( &( xNetworkInfo ), - &xConnectInfo, - otaDemoCONN_TIMEOUT_MS, &xMQTTConnection ) == IOT_MQTT_SUCCESS ) + /* Establish a new MQTT connection. */ + if( _establishMqttConnection( true, + &xMQTTConnection ) == IOT_MQTT_SUCCESS ) { configPRINTF( ( "Connected to broker.\r\n" ) ); xOTAConnectionCtx.pvControlClient = xMQTTConnection; xOTAConnectionCtx.pxNetworkInterface = ( void * ) IOT_NETWORK_INTERFACE_FREERTOS; xOTAConnectionCtx.pvNetworkCredentials = &xNetworkSecurityCredentials; + /* Set the base interval for connection retry.*/ + _retryInterval = otaDemoCONN_RETRY_BASE_INTERVAL_SECONDS; + + /* Update the connection available flag.*/ + _networkConnected = true; + + /* Check if OTA Agent is suspended and resume.*/ + if( ( eState = OTA_GetAgentState() ) == eOTA_AgentState_Suspended ) + { + OTA_Resume( &xOTAConnectionCtx ); + } + + /* Check if OTA Agent is suspended and resume.*/ + if( ( eState = OTA_GetAgentState() ) == eOTA_AgentState_Suspended ) + { + OTA_Resume( &xOTAConnectionCtx ); + } + + /* Initialize the OTA Agent , if it is resuming the OTA statistics will be cleared for new connection.*/ OTA_AgentInit( ( void * ) ( &xOTAConnectionCtx ), ( const uint8_t * ) ( awsiotdemoprofileCLIENT_IDENTIFIER ), App_OTACompleteCallback, ( TickType_t ) ~0 ); - while( ( eState = OTA_GetAgentState() ) != eOTA_AgentState_Stopped ) + while( ( ( eState = OTA_GetAgentState() ) != eOTA_AgentState_Stopped ) && _networkConnected ) { /* Wait forever for OTA traffic but allow other tasks to run and output statistics only once per second. */ - vTaskDelay( myappONE_SECOND_DELAY_IN_TICKS ); + IotClock_SleepMs( otaDemoTASK_DELAY_SECONDS * 1000 ); + configPRINTF( ( "State: %s Received: %u Queued: %u Processed: %u Dropped: %u\r\n", pcStateStr[ eState ], OTA_GetPacketsReceived(), OTA_GetPacketsQueued(), OTA_GetPacketsProcessed(), OTA_GetPacketsDropped() ) ); } - IotMqtt_Disconnect( xMQTTConnection, false ); + /* Check if we got network disconnect callback and suspend OTA Agent.*/ + if( _networkConnected == false ) + { + /* Suspend OTA agent.*/ + if( OTA_Suspend() == kOTA_Err_None ) + { + while( ( eState = OTA_GetAgentState() ) != eOTA_AgentState_Suspended ) + { + /* Wait for OTA Agent to process the suspend event. */ + IotClock_SleepMs( otaDemoTASK_DELAY_SECONDS * 1000 ); + } + } + } + else + { + IotMqtt_Disconnect( xMQTTConnection, false ); + } } else { configPRINTF( ( "ERROR: Failed to connect to MQTT broker.\r\n" ) ); } - /* After failure to connect or a disconnect, wait an arbitrary one second before retry. */ - vTaskDelay( myappONE_SECOND_DELAY_IN_TICKS ); + /* After failure to connect or a disconnect, delay for retrying connection. */ + _connectionRetryDelay(); } } @@ -243,6 +324,12 @@ OTA_Err_t xErr = kOTA_Err_Uninitialized; configPRINTF( ( "Received eOTA_JobEvent_Activate callback from OTA Agent.\r\n" ) ); IotMqtt_Disconnect( xMQTTConnection, 0 ); OTA_ActivateNewImage(); + + /* We should never get here as new image activation must reset the device.*/ + for( ; ; ) + { + __debugbreak(); + } } else if( eEvent == eOTA_JobEvent_Fail ) { @@ -293,3 +380,93 @@ IotNetworkError_t xNetworkResult; configASSERT( xResult == IOT_MQTT_SUCCESS ); } /*-----------------------------------------------------------*/ + +static void _connectionRetryDelay( void ) +{ +unsigned int retryIntervalwithJitter = 0; + + if( ( _retryInterval * 2 ) >= otaDemoCONN_RETRY_MAX_INTERVAL_SECONDS ) + { + /* Retry interval is already max.*/ + _retryInterval = otaDemoCONN_RETRY_MAX_INTERVAL_SECONDS; + } + else + { + /* Double the retry interval time.*/ + _retryInterval *= 2; + } + + /* Add random jitter upto current retry interval .*/ + retryIntervalwithJitter = _retryInterval + ( rand() % _retryInterval ); + + configPRINTF( ( "Retrying network connection in %d Secs ", retryIntervalwithJitter ) ); + + /* Delay for the calculated time interval .*/ + IotClock_SleepMs( retryIntervalwithJitter * 1000 ); +} +/*-----------------------------------------------------------*/ + +static void prvNetworkDisconnectCallback( void * param, + IotMqttCallbackParam_t * mqttCallbackParams ) +{ + ( void ) param; + + /* Log the reason for MQTT disconnect.*/ + switch( mqttCallbackParams->u.disconnectReason ) + { + case IOT_MQTT_DISCONNECT_CALLED: + configPRINTF( ( "Mqtt disconnected due to invoking diconnect function.\r\n" ) ); + break; + + case IOT_MQTT_BAD_PACKET_RECEIVED: + configPRINTF( ( "Mqtt disconnected due to invalid packet received from the network.\r\n" ) ); + break; + + case IOT_MQTT_KEEP_ALIVE_TIMEOUT: + configPRINTF( ( "Mqtt disconnected due to Keep-alive response not received.\r\n" ) ); + break; + + default: + configPRINTF( ( "Mqtt disconnected due to unknown reason." ) ); + break; + } + + /* Clear the flag for network connection status.*/ + _networkConnected = false; +} + +/*-----------------------------------------------------------*/ + +static IotMqttError_t _establishMqttConnection( bool awsIotMqttMode, + IotMqttConnection_t * pMqttConnection ) +{ +IotMqttError_t connectStatus = IOT_MQTT_STATUS_PENDING; + + /* Set the members of the connection info not set by the initializer. */ + memset( &xConnectInfo, 0, sizeof( xConnectInfo ) ); + xConnectInfo.awsIotMqttMode = awsIotMqttMode; + xConnectInfo.cleanSession = true; + xConnectInfo.keepAliveSeconds = otaDemoKEEPALIVE_SECONDS; + xConnectInfo.clientIdentifierLength = ( uint16_t ) strlen( awsiotdemoprofileCLIENT_IDENTIFIER ); + xConnectInfo.pClientIdentifier = awsiotdemoprofileCLIENT_IDENTIFIER; + + /* Establish the MQTT connection. */ + configPRINTF( ( "MQTT demo client identifier is %.*s (length %hu).", + xConnectInfo.clientIdentifierLength, + xConnectInfo.pClientIdentifier, + xConnectInfo.clientIdentifierLength ) ); + + connectStatus = IotMqtt_Connect( &xNetworkInfo, + &xConnectInfo, + otaDemoCONN_TIMEOUT_MS, + pMqttConnection ); + + if( connectStatus != IOT_MQTT_SUCCESS ) + { + configPRINTF( ( "MQTT CONNECT returned error %s.", + IotMqtt_strerror( connectStatus ) ) ); + } + + return connectStatus; +} +/*-----------------------------------------------------------*/ diff --git a/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/common/main.c b/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/common/main.c index 0c7affd755..ddde0132ad 100755 --- a/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/common/main.c +++ b/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/common/main.c @@ -175,6 +175,13 @@ static BaseType_t xTasksAlreadyCreated = pdFALSE; } /*-----------------------------------------------------------*/ +void vApplicationIdleHook( void ) +{ + const uint32_t ulMSToSleep = 1; + Sleep( ulMSToSleep ); +} +/*-----------------------------------------------------------*/ + void vAssertCalled( const char * pcFile, uint32_t ulLine ) { diff --git a/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_code_signing/FreeRTOSConfig.h b/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_code_signing/FreeRTOSConfig.h index f503071ada..fcef78a988 100755 --- a/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_code_signing/FreeRTOSConfig.h +++ b/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_code_signing/FreeRTOSConfig.h @@ -65,7 +65,7 @@ /* Hook function related definitions. */ #define configUSE_TICK_HOOK 0 -#define configUSE_IDLE_HOOK 0 +#define configUSE_IDLE_HOOK 1 #define configUSE_MALLOC_FAILED_HOOK 0 #define configCHECK_FOR_STACK_OVERFLOW 0 /* Not applicable to the Win32 port. */ diff --git a/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_code_signing/aws_ota_agent_config.h b/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_code_signing/aws_ota_agent_config.h index f04a7b564f..c08c203c38 100755 --- a/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_code_signing/aws_ota_agent_config.h +++ b/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_code_signing/aws_ota_agent_config.h @@ -84,7 +84,7 @@ * Please note that this must be set larger than zero. * */ -#define otaconfigMAX_NUM_BLOCKS_REQUEST 32U +#define otaconfigMAX_NUM_BLOCKS_REQUEST 1U /** * @brief The maximum number of requests allowed to send without a response before we abort. @@ -103,6 +103,16 @@ */ #define otaconfigMAX_NUM_OTA_DATA_BUFFERS 4U +/** + * @brief Allow update to same or lower version. + * + * Set this to 1 to allow downgrade or same version update. This configurations parameter + * disables version check and allows update to a same or lower version.This is provided for + * testing purpose and it is recommended to always update to higher version and keep this + * configuration disabled. + */ +#define otaconfigAllowDowngrade 0U + /** * @brief The protocol selected for OTA control operations. * diff --git a/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_code_signing_http/FreeRTOSConfig.h b/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_code_signing_http/FreeRTOSConfig.h index f503071ada..fcef78a988 100755 --- a/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_code_signing_http/FreeRTOSConfig.h +++ b/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_code_signing_http/FreeRTOSConfig.h @@ -65,7 +65,7 @@ /* Hook function related definitions. */ #define configUSE_TICK_HOOK 0 -#define configUSE_IDLE_HOOK 0 +#define configUSE_IDLE_HOOK 1 #define configUSE_MALLOC_FAILED_HOOK 0 #define configCHECK_FOR_STACK_OVERFLOW 0 /* Not applicable to the Win32 port. */ diff --git a/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_code_signing_http/aws_ota_agent_config.h b/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_code_signing_http/aws_ota_agent_config.h index c9bae22ed4..85e7bae3dc 100755 --- a/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_code_signing_http/aws_ota_agent_config.h +++ b/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_code_signing_http/aws_ota_agent_config.h @@ -84,7 +84,7 @@ * Please note that this must be set larger than zero. * */ -#define otaconfigMAX_NUM_BLOCKS_REQUEST 32U +#define otaconfigMAX_NUM_BLOCKS_REQUEST 1U /** * @brief The maximum number of requests allowed to send without a response before we abort. @@ -103,6 +103,16 @@ */ #define otaconfigMAX_NUM_OTA_DATA_BUFFERS 4U +/** + * @brief Allow update to same or lower version. + * + * Set this to 1 to allow downgrade or same version update. This configurations parameter + * disables version check and allows update to a same or lower version.This is provided for + * testing purpose and it is recommended to always update to higher version and keep this + * configuration disabled. + */ +#define otaconfigAllowDowngrade 0U + /** * @brief The protocol selected for OTA control operations. * diff --git a/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_no_code_signing/FreeRTOSConfig.h b/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_no_code_signing/FreeRTOSConfig.h index f503071ada..fcef78a988 100755 --- a/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_no_code_signing/FreeRTOSConfig.h +++ b/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_no_code_signing/FreeRTOSConfig.h @@ -65,7 +65,7 @@ /* Hook function related definitions. */ #define configUSE_TICK_HOOK 0 -#define configUSE_IDLE_HOOK 0 +#define configUSE_IDLE_HOOK 1 #define configUSE_MALLOC_FAILED_HOOK 0 #define configCHECK_FOR_STACK_OVERFLOW 0 /* Not applicable to the Win32 port. */ diff --git a/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_no_code_signing/aws_ota_agent_config.h b/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_no_code_signing/aws_ota_agent_config.h index e09c12590d..2039ebea0b 100755 --- a/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_no_code_signing/aws_ota_agent_config.h +++ b/FreeRTOS-Plus/Demo/FreeRTOS_IoT_Libraries/ota/ota_no_code_signing/aws_ota_agent_config.h @@ -84,7 +84,7 @@ * Please note that this must be set larger than zero. * */ -#define otaconfigMAX_NUM_BLOCKS_REQUEST 32U +#define otaconfigMAX_NUM_BLOCKS_REQUEST 1U /** * @brief The maximum number of requests allowed to send without a response before we abort. @@ -103,6 +103,16 @@ */ #define otaconfigMAX_NUM_OTA_DATA_BUFFERS 4U +/** + * @brief Allow update to same or lower version. + * + * Set this to 1 to allow downgrade or same version update. This configurations parameter + * disables version check and allows update to a same or lower version.This is provided for + * testing purpose and it is recommended to always update to higher version and keep this + * configuration disabled. + */ +#define otaconfigAllowDowngrade 0U + /** * @brief The protocol selected for OTA control operations. * diff --git a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/include/aws_iot_ota_agent.h b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/include/aws_iot_ota_agent.h index ca59628382..b929a8b029 100755 --- a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/include/aws_iot_ota_agent.h +++ b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/include/aws_iot_ota_agent.h @@ -1,5 +1,5 @@ /* - * FreeRTOS OTA V1.1.1 + * FreeRTOS OTA V1.2.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -34,9 +34,7 @@ /* Standard includes. */ /* For FILE type in OTA_FileContext_t.*/ #include - -/* Type definitions for OTA Agent */ -#include "aws_iot_ota_types.h" +#include /* Includes required by the FreeRTOS timers structure. */ #include "FreeRTOS.h" @@ -81,7 +79,14 @@ extern const char cOTA_JSON_FileSignatureKey[ OTA_FILE_SIG_KEY_STR_MAX_LENGTH ]; #define OTA_LOG_L3( ... ) #endif +/*-------------------------- OTA enumerated types --------------------------*/ + /** + * @enums{ota,OTA library} + */ + +/** + * @ingroup ota_datatypes_enums * @brief OTA Agent states. * * The current state of the OTA Task (OTA Agent). @@ -99,12 +104,14 @@ typedef enum eOTA_AgentState_RequestingFileBlock, eOTA_AgentState_WaitingForFileBlock, eOTA_AgentState_ClosingFile, + eOTA_AgentState_Suspended, eOTA_AgentState_ShuttingDown, eOTA_AgentState_Stopped, eOTA_AgentState_All } OTA_State_t; /** + * @ingroup ota_datatypes_enums * @brief OTA Agent Events. * * The events sent to OTA agent. @@ -120,11 +127,259 @@ typedef enum eOTA_AgentEvent_ReceivedFileBlock, eOTA_AgentEvent_RequestTimer, eOTA_AgentEvent_CloseFile, + eOTA_AgentEvent_Suspend, + eOTA_AgentEvent_Resume, eOTA_AgentEvent_UserAbort, eOTA_AgentEvent_Shutdown, eOTA_AgentEvent_Max } OTA_Event_t; +/** + * @ingroup ota_datatypes_enums + * @brief OTA Platform Image State. + * + * The image state set by platform implementation. + */ +typedef enum +{ + eOTA_PAL_ImageState_Unknown = 0, + eOTA_PAL_ImageState_PendingCommit, + eOTA_PAL_ImageState_Valid, + eOTA_PAL_ImageState_Invalid, +} OTA_PAL_ImageState_t; + +/** + * @ingroup ota_datatypes_enums + * @brief OTA job document parser error codes. + */ +typedef enum +{ + eOTA_JobParseErr_Unknown = -1, /* The error code has not yet been set by a logic path. */ + eOTA_JobParseErr_None = 0, /* Signifies no error has occurred. */ + eOTA_JobParseErr_BusyWithExistingJob, /* We're busy with a job but received a new job document. */ + eOTA_JobParseErr_NullJob, /* A null job was reported (no job ID). */ + eOTA_JobParseErr_UpdateCurrentJob, /* We're already busy with the reported job ID. */ + eOTA_JobParseErr_ZeroFileSize, /* Job document specified a zero sized file. This is not allowed. */ + eOTA_JobParseErr_NonConformingJobDoc, /* The job document failed to fulfill the model requirements. */ + eOTA_JobParseErr_BadModelInitParams, /* There was an invalid initialization parameter used in the document model. */ + eOTA_JobParseErr_NoContextAvailable /* There wasn't an OTA context available. */ +} OTA_JobParseErr_t; + + +/** + * @ingroup ota_datatypes_enums + * @brief OTA Job callback events. + * + * After an OTA update image is received and authenticated, the agent calls the user + * callback (set with the @ref ota_function_init API) with the value eOTA_JobEvent_Activate to + * signal that the device must be rebooted to activate the new image. When the device + * boots, if the OTA job status is in self test mode, the agent calls the user callback + * with the value eOTA_JobEvent_StartTest, signaling that any additional self tests + * should be performed. + * + * If the OTA receive fails for any reason, the agent calls the user callback with + * the value eOTA_JobEvent_Fail instead to allow the user to log the failure and take + * any action deemed appropriate by the user code. + * + * See the OTA_ImageState_t type for more information. + */ +typedef enum +{ + eOTA_JobEvent_Activate = 0, /*!< OTA receive is authenticated and ready to activate. */ + eOTA_JobEvent_Fail = 1, /*!< OTA receive failed. Unable to use this update. */ + eOTA_JobEvent_StartTest = 2, /*!< OTA job is now in self test, perform user tests. */ + eOTA_LastJobEvent = eOTA_JobEvent_StartTest +} OTA_JobEvent_t; + + +/** + * @ingroup ota_datatypes_enums + * @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_function_setimagestate( eOTA_ImageState_Accepted ) or + * @ref ota_function_setimagestate( eOTA_ImageState_Rejected ). If the image is accepted, it becomes + * the main firmware image to be booted from then on. If it is rejected, the image is + * no longer valid and shall not be used, reverting to the last known good image. + * + * If you want to abort an active OTA transfer, you may do so by calling the API + * @ref ota_function_setimagestate( eOTA_ImageState_Aborted ). + */ +typedef enum +{ + eOTA_ImageState_Unknown = 0, /*!< The initial state of the OTA MCU Image. */ + eOTA_ImageState_Testing = 1, /*!< The state of the OTA MCU Image post successful download and reboot. */ + eOTA_ImageState_Accepted = 2, /*!< The state of the OTA MCU Image post successful download and successful self_test. */ + eOTA_ImageState_Rejected = 3, /*!< The state of the OTA MCU Image when the job has been rejected. */ + eOTA_ImageState_Aborted = 4, /*!< 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. */ + eOTA_LastImageState = eOTA_ImageState_Aborted +} OTA_ImageState_t; + + +/*------------------------- OTA callbacks --------------------------*/ + +/** + * @functionpointers{ota,OTA library} + */ + +/* Forward delcaration of OTA_FileContext_t. */ +typedef struct OTA_FileContext OTA_FileContext_t; + +/** + * @brief OTA Error type. + */ +typedef uint32_t OTA_Err_t; + +/** + * @ingroup ota_datatypes_functionpointers + * @brief OTA update complete callback function typedef. + * + * The user may register a callback function when initializing the OTA Agent. This + * callback is used to notify the main application when the OTA update job is complete. + * Typically, it is used to reset the device after a successful update by calling + * @ref ota_function_activatenewimage and may also be used to kick off user specified self tests + * during the Self Test phase. If the user does not supply a custom callback function, + * a default callback handler is used that automatically calls @ref ota_function_activatenewimage + * after a successful update. + * + * The callback function is called with one of the following arguments: + * + * eOTA_JobEvent_Activate OTA update is authenticated and ready to activate. + * eOTA_JobEvent_Fail OTA update failed. Unable to use this update. + * eOTA_JobEvent_StartTest OTA job is now ready for optional user self tests. + * + * When eOTA_JobEvent_Activate is received, the job status details have been updated with + * the state as ready for Self Test. After reboot, the new firmware will (normally) be + * notified that it is in the Self Test phase via the callback and the application may + * then optionally run its own tests before committing the new image. + * + * If the callback function is called with a result of eOTA_JobEvent_Fail, the OTA update + * job has failed in some way and should be rejected. + * + * @param[in] eEvent An OTA update event from the OTA_JobEvent_t enum. + */ +typedef void (* pxOTACompleteCallback_t)( OTA_JobEvent_t eEvent ); + +/** + * @ingroup ota_datatypes_functionpointers + * @brief OTA abort callback function typedef. + * + * The user may register a callback function when initializing the OTA Agent. This + * callback is used to override the behavior of how a job is aborted. + * + * @param[in] C File context of the job being aborted + */ +typedef OTA_Err_t (* pxOTAPALAbortCallback_t)( OTA_FileContext_t * const C ); + +/** + * @ingroup ota_datatypes_functionpointers + * @brief OTA new image received callback function typedef. + * + * The user may register a callback function when initializing the OTA Agent. This + * callback is used to override the behavior of what happens when a new image is + * activated. + * + * @param[in] ulServerFileID File ID of the image received + */ +typedef OTA_Err_t (* pxOTAPALActivateNewImageCallback_t)( uint32_t ulServerFileID ); + +/** + * @ingroup ota_datatypes_functionpointers + * @brief OTA close file callback function typedef. + * + * The user may register a callback function when initializing the OTA Agent. This + * callback is used to override the behavior of what happens when a file is closed. + * + * @param[in] C File context of the job being aborted + */ +typedef OTA_Err_t (* pxOTAPALCloseFileCallback_t)( OTA_FileContext_t * const C ); + +/** + * @ingroup ota_datatypes_functionpointers + * @brief OTA create file to store received data callback function typedef. + * + * The user may register a callback function when initializing the OTA Agent. This + * callback is used to override the behavior of how a new file is created. + * + * @param[in] C File context of the job being aborted + */ +typedef OTA_Err_t (* pxOTAPALCreateFileForRxCallback_t)( OTA_FileContext_t * const C ); + +/** + * @ingroup ota_datatypes_functionpointers + * @brief OTA Get Platform Image State callback function typedef. + * + * The user may register a callback function when initializing the OTA Agent. This + * callback is used to override the behavior of returning the platform image state. + * + * @param[in] ulServerFileID File ID of the image received + */ +typedef OTA_PAL_ImageState_t (* pxOTAPALGetPlatformImageStateCallback_t)( uint32_t ulServerFileID ); + +/** + * @ingroup ota_datatypes_functionpointers + * @brief OTA Reset Device callback function typedef. + * + * The user may register a callback function when initializing the OTA Agent. This + * callback is used to override the behavior of what happens when the OTA agent resets the device. + * + * @param[in] ulServerFileID File ID of the image received + */ +typedef OTA_Err_t (* pxOTAPALResetDeviceCallback_t)( uint32_t ulServerFileID ); + +/** + * @ingroup ota_datatypes_functionpointers + * @brief OTA Set Platform Image State callback function typedef. + * + * The user may register a callback function when initializing the OTA Agent. This + * callback is used to override the behavior of how a platform image state is stored. + * + * @param[in] ulServerFileID File ID of the image received + * @param[in] eState Platform Image State to be state + */ +typedef OTA_Err_t (* pxOTAPALSetPlatformImageStateCallback_t)( uint32_t ulServerFileID, + OTA_ImageState_t eState ); + +/** + * @ingroup ota_datatypes_functionpointers + * @brief OTA Write Block callback function typedef. + * + * The user may register a callback function when initializing the OTA Agent. This + * callback is used to override the behavior of how a block is written to a file. + * + * @param[in] C File context of the job being aborted + * @param[in] iOffset Offset into the file to write the data + * @param[in] pacData Data to be written at the offset + * @param[in] iBlocksize Block size of the data to be written + */ +typedef int16_t (* pxOTAPALWriteBlockCallback_t)( OTA_FileContext_t * const C, + uint32_t iOffset, + uint8_t * const pacData, + uint32_t iBlockSize ); + +/** + * @ingroup ota_datatypes_functionpointers + * @brief Custom Job callback function typedef. + * + * The user may register a callback function when initializing the OTA Agent. This + * callback will be called when the OTA agent cannot parse a job document. + * + * @param[in] pcJSON Pointer to the json document received by the OTA agent + * @param[in] ulMsgLen Length of the json document received by the agent + */ +typedef OTA_JobParseErr_t (* pxOTACustomJobCallback_t)( const char * pcJSON, + uint32_t ulMsgLen ); + + +/*--------------------------- OTA structs ----------------------------*/ + +/** + * @structs{ota,OTA library} + */ + /* A composite cryptographic signature structure able to hold our largest supported signature. */ #define kOTA_MaxSignatureSize 256 /* Max bytes supported for a file signature (2048 bit RSA is 256 bytes). */ @@ -136,26 +391,95 @@ typedef struct } Sig256_t; /** - * @brief OTA Error type. + * @ingroup ota_datatypes_structs + * @brief OTA File Context Information. + * + * Information about an OTA Update file that is to be streamed. This structure is filled in from a + * job notification MQTT message. Currently only one file context can be streamed at time. */ -typedef uint32_t OTA_Err_t; +typedef struct OTA_FileContext +{ + uint8_t * pucFilePath; /*!< Local file pathname. */ + union + { + int32_t lFileHandle; /*!< Device internal file pointer or handle. + * File type is handle after file is open for write. */ + #if WIN32 + FILE * pxFile; /*!< File type is stdio FILE structure after file is open for write. */ + #endif + uint8_t * pucFile; /*!< File type is RAM/Flash image pointer after file is open for write. */ + }; + uint32_t ulFileSize; /*!< The size of the file in bytes. */ + uint32_t ulBlocksRemaining; /*!< How many blocks remain to be received (a code optimization). */ + uint32_t ulFileAttributes; /*!< Flags specific to the file being received (e.g. secure, bundle, archive). */ + uint32_t ulServerFileID; /*!< The file is referenced by this numeric ID in the OTA job. */ + uint8_t * pucJobName; /*!< The job name associated with this file from the job service. */ + uint8_t * pucStreamName; /*!< The stream associated with this file from the OTA service. */ + Sig256_t * pxSignature; /*!< Pointer to the file's signature structure. */ + uint8_t * pucRxBlockBitmap; /*!< Bitmap of blocks received (for de-duping and missing block request). */ + uint8_t * pucCertFilepath; /*!< Pathname of the certificate file used to validate the receive file. */ + uint8_t * pucUpdateUrlPath; /*!< Url for the file. */ + uint8_t * pucAuthScheme; /*!< Authorization scheme. */ + uint32_t ulUpdaterVersion; /*!< Used by OTA self-test detection, the version of FW that did the update. */ + bool bIsInSelfTest; /*!< True if the job is in self test mode. */ + uint8_t * pucProtocols; /*!< Authorization scheme. */ +} OTA_FileContext_t; -/* - * OTA Error code helper constant for extracting the error code from the OTA error returned. +/** + * @ingroup ota_datatypes_structs + * @brief OTA Connection context. + * + * Connection information that the user provides to initialize control and data transfer for OTA. + */ +typedef struct +{ + void * pvControlClient; + const void * pxNetworkInterface; + void * pvNetworkCredentials; +} OTA_ConnectionContext_t; + +/** + * @ingroup ota_datatypes_structs + * @brief OTA PAL callback structure + */ +typedef struct +{ + pxOTAPALAbortCallback_t xAbort; /* OTA Abort callback pointer */ + pxOTAPALActivateNewImageCallback_t xActivateNewImage; /* OTA Activate New Image callback pointer */ + pxOTAPALCloseFileCallback_t xCloseFile; /* OTA Close File callback pointer */ + pxOTAPALCreateFileForRxCallback_t xCreateFileForRx; /* OTA Create File for Receive callback pointer */ + pxOTAPALGetPlatformImageStateCallback_t xGetPlatformImageState; /* OTA Get Platform Image State callback pointer */ + pxOTAPALResetDeviceCallback_t xResetDevice; /* OTA Reset Device callback pointer */ + pxOTAPALSetPlatformImageStateCallback_t xSetPlatformImageState; /* OTA Set Platform Image State callback pointer */ + pxOTAPALWriteBlockCallback_t xWriteBlock; /* OTA Write Block callback pointer */ + pxOTACompleteCallback_t xCompleteCallback; /* OTA Job Completed callback pointer */ + pxOTACustomJobCallback_t xCustomJobCallback; /* OTA Custom Job callback pointer */ +} OTA_PAL_Callbacks_t; + + +/*------------------------- OTA defined constants --------------------------*/ + +/** + * @constantspage{ota,OTA library} + * + * @section ota_constants_err_codes OTA Error Codes + * @brief OTA Agent error codes returned by OTA agent API. + * + * @snippet this define_ota_err_codes + * + * OTA agent error codes are in the upper 8 bits of the 32 bit OTA error word, OTA_Err_t. + * + * @section ota_constants_err_code_helpers OTA Error Code Helper constants + * @brief OTA Error code helper constant for extracting the error code from the OTA error returned. + * + * @snippet this define_ota_err_code_helpers * * OTA error codes consist of an agent code in the upper 8 bits of a 32 bit word and sometimes * merged with a platform specific code in the lower 24 bits. You must refer to the platform PAL * layer in use to determine the meaning of the lower 24 bits. */ -#define kOTA_PAL_ErrMask 0xffffffUL /*!< The PAL layer uses the signed low 24 bits of the OTA error code. */ -#define kOTA_Main_ErrMask 0xff000000UL /*!< Mask out all but the OTA Agent error code (high 8 bits). */ -#define kOTA_MainErrShiftDownBits 24U /*!< The OTA Agent error code is the highest 8 bits of the word. */ -/* - * OTA Agent error codes returned by OTA agent API. - * - * OTA agent error codes are in the upper 8 bits of the 32 bit OTA error word, OTA_Err_t. - */ +/* @[define_ota_err_codes] */ #define kOTA_Err_Panic 0xfe000000UL /*!< Unrecoverable FW error. Probably should log error and reboot. */ #define kOTA_Err_Uninitialized 0xff000000UL /*!< The error code has not yet been set by a logic path. */ #define kOTA_Err_None 0x00000000UL @@ -191,339 +515,99 @@ typedef uint32_t OTA_Err_t; #define kOTA_Err_SelfTestTimerFailed 0x2b000000UL /*!< Attempt to start self-test timer faield. */ #define kOTA_Err_EventQueueSendFailed 0x2c000000UL /*!< Posting event message to the event queue failed. */ #define kOTA_Err_InvalidDataProtocol 0x2d000000UL /*!< Job does not have a valid protocol for data transfer. */ +#define kOTA_Err_OTAAgentStopped 0x2e000000UL /*!< Returned when operations are performed that requires OTA Agent running & its stopped. */ +/* @[define_ota_err_codes] */ + +/* @[define_ota_err_code_helpers] */ +#define kOTA_PAL_ErrMask 0xffffffUL /*!< The PAL layer uses the signed low 24 bits of the OTA error code. */ +#define kOTA_Main_ErrMask 0xff000000UL /*!< Mask out all but the OTA Agent error code (high 8 bits). */ +#define kOTA_MainErrShiftDownBits 24U /*!< The OTA Agent error code is the highest 8 bits of the word. */ +/* @[define_ota_err_code_helpers] */ + + +/*------------------------- OTA Public API --------------------------*/ /** - * @brief OTA Job callback events. - * - * After an OTA update image is received and authenticated, the agent calls the user - * callback (set with the OTA_AgentInit API) with the value eOTA_JobEvent_Activate to - * signal that the device must be rebooted to activate the new image. When the device - * boots, if the OTA job status is in self test mode, the agent calls the user callback - * with the value eOTA_JobEvent_StartTest, signaling that any additional self tests - * should be performed. - * - * If the OTA receive fails for any reason, the agent calls the user callback with - * the value eOTA_JobEvent_Fail instead to allow the user to log the failure and take - * any action deemed appropriate by the user code. - * - * See the OTA_ImageState_t type for more information. + * @functionspage{ota,OTA library} + * - @functionname{ota_function_init} + * - @functionname{ota_function_shutdown} + * - @functionname{ota_function_getagentstate} + * - @functionname{ota_function_activatenewimage} + * - @functionname{ota_function_setimagestate} + * - @functionname{ota_function_getimagestate} + * - @functionname{ota_function_checkforupdate} + * - @functionname{ota_function_suspend} + * - @functionname{ota_function_resume} + * - @functionname{ota_function_getpacketsreceived} + * - @functionname{ota_function_getpacketsqueued} + * - @functionname{ota_function_getpacketsprocessed} + * - @functionname{ota_function_getpacketsdropped} */ -typedef enum -{ - eOTA_JobEvent_Activate = 0, /*!< OTA receive is authenticated and ready to activate. */ - eOTA_JobEvent_Fail = 1, /*!< OTA receive failed. Unable to use this update. */ - eOTA_JobEvent_StartTest = 2, /*!< OTA job is now in self test, perform user tests. */ - eOTA_LastJobEvent = eOTA_JobEvent_StartTest -} OTA_JobEvent_t; - /** - * @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 OTA_SetImageState( eOTA_ImageState_Accepted ) or - * OTA_SetImageState( eOTA_ImageState_Rejected ). If the image is accepted, it becomes - * the main firmware image to be booted from then on. If it is rejected, the image is - * no longer valid and shall not be used, reverting to the last known good image. - * - * If you want to abort an active OTA transfer, you may do so by calling the API - * OTA_SetImageState( eOTA_ImageState_Aborted ). + * @functionpage{OTA_AgentInit,ota,init} + * @functionpage{OTA_AgentShutdown,ota,shutdown} + * @functionpage{OTA_GetAgentState,ota,getagentstate} + * @functionpage{OTA_ActivateNewImage,ota,activatenewimage} + * @functionpage{OTA_SetImageState,ota,setimagestate} + * @functionpage{OTA_GetImageState,ota,getimagestate} + * @functionpage{OTA_CheckForUpdate,ota,checkforupdate} + * @functionpage{OTA_Suspend,ota,suspend} + * @functionpage{OTA_Resume,ota,resume} + * @functionpage{OTA_GetPacketsReceived,ota,getpacketsreceived} + * @functionpage{OTA_GetPacketsQueued,ota,getpacketsqueued} + * @functionpage{OTA_GetPacketsProcessed,ota,getpacketsprocessed} + * @functionpage{OTA_GetPacketsDropped,ota,getpacketsdropped} */ -typedef enum -{ - eOTA_ImageState_Unknown = 0, /*!< The initial state of the OTA MCU Image. */ - eOTA_ImageState_Testing = 1, /*!< The state of the OTA MCU Image post successful download and reboot. */ - eOTA_ImageState_Accepted = 2, /*!< The state of the OTA MCU Image post successful download and successful self_test. */ - eOTA_ImageState_Rejected = 3, /*!< The state of the OTA MCU Image when the job has been rejected. */ - eOTA_ImageState_Aborted = 4, /*!< 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. */ - eOTA_LastImageState = eOTA_ImageState_Aborted -} OTA_ImageState_t; - - -/** - * @brief OTA File Context Information. - * - * Information about an OTA Update file that is to be streamed. This structure is filled in from a - * job notification MQTT message. Currently only one file context can be streamed at time. - */ -typedef struct -{ - uint8_t * pucFilePath; /*!< Local file pathname. */ - union - { - int32_t lFileHandle; /*!< Device internal file pointer or handle. - * File type is handle after file is open for write. */ - #if WIN32 - FILE * pxFile; /*!< File type is stdio FILE structure after file is open for write. */ - #endif - uint8_t * pucFile; /*!< File type is RAM/Flash image pointer after file is open for write. */ - }; - uint32_t ulFileSize; /*!< The size of the file in bytes. */ - uint32_t ulBlocksRemaining; /*!< How many blocks remain to be received (a code optimization). */ - uint32_t ulFileAttributes; /*!< Flags specific to the file being received (e.g. secure, bundle, archive). */ - uint32_t ulServerFileID; /*!< The file is referenced by this numeric ID in the OTA job. */ - uint8_t * pucJobName; /*!< The job name associated with this file from the job service. */ - uint8_t * pucStreamName; /*!< The stream associated with this file from the OTA service. */ - Sig256_t * pxSignature; /*!< Pointer to the file's signature structure. */ - uint8_t * pucRxBlockBitmap; /*!< Bitmap of blocks received (for de-duping and missing block request). */ - uint8_t * pucCertFilepath; /*!< Pathname of the certificate file used to validate the receive file. */ - uint8_t * pucUpdateUrlPath; /*!< Url for the file. */ - uint8_t * pucAuthScheme; /*!< Authorization scheme. */ - uint32_t ulUpdaterVersion; /*!< Used by OTA self-test detection, the version of FW that did the update. */ - bool_t xIsInSelfTest; /*!< True if the job is in self test mode. */ - uint8_t * pucProtocols; /*!< Authorization scheme. */ -} OTA_FileContext_t; - -/** - * @brief OTA Connection context. - * - * Connection information that user provides to initialize control and data transfer for OTA. - */ -typedef struct -{ - void * pvControlClient; - const void * pxNetworkInterface; - void * pvNetworkCredentials; -} OTA_ConnectionContext_t; - - -/** - * @brief OTA update complete callback function typedef. - * - * The user may register a callback function when initializing the OTA Agent. This - * callback is used to notify the main application when the OTA update job is complete. - * Typically, it is used to reset the device after a successful update by calling - * OTA_ActivateNewImage() and may also be used to kick off user specified self tests - * during the Self Test phase. If the user does not supply a custom callback function, - * a default callback handler is used that automatically calls OTA_ActivateNewImage() - * after a successful update. - * - * @note: - * - * The callback function is called with one of the following arguments: - * - * eOTA_JobEvent_Activate OTA update is authenticated and ready to activate. - * eOTA_JobEvent_Fail OTA update failed. Unable to use this update. - * eOTA_JobEvent_StartTest OTA job is now ready for optional user self tests. - * - * When eOTA_JobEvent_Activate is received, the job status details have been updated with - * the state as ready for Self Test. After reboot, the new firmware will (normally) be - * notified that it is in the Self Test phase via the callback and the application may - * then optionally run its own tests before committing the new image. - * - * If the callback function is called with a result of eOTA_JobEvent_Fail, the OTA update - * job has failed in some way and should be rejected. - * - * @param[in] eEvent An OTA update event from the OTA_JobEvent_t enum. - */ -typedef void (* pxOTACompleteCallback_t)( OTA_JobEvent_t eEvent ); - - - -typedef enum -{ - eOTA_PAL_ImageState_Unknown = 0, - eOTA_PAL_ImageState_PendingCommit, - eOTA_PAL_ImageState_Valid, - eOTA_PAL_ImageState_Invalid, -} OTA_PAL_ImageState_t; - - - -/* OTA job document parser error codes. */ - -typedef enum -{ - eOTA_JobParseErr_Unknown = -1, /* The error code has not yet been set by a logic path. */ - eOTA_JobParseErr_None = 0, /* Signifies no error has occurred. */ - eOTA_JobParseErr_BusyWithExistingJob, /* We're busy with a job but received a new job document. */ - eOTA_JobParseErr_NullJob, /* A null job was reported (no job ID). */ - eOTA_JobParseErr_UpdateCurrentJob, /* We're already busy with the reported job ID. */ - eOTA_JobParseErr_ZeroFileSize, /* Job document specified a zero sized file. This is not allowed. */ - eOTA_JobParseErr_NonConformingJobDoc, /* The job document failed to fulfill the model requirements. */ - eOTA_JobParseErr_BadModelInitParams, /* There was an invalid initialization parameter used in the document model. */ - eOTA_JobParseErr_NoContextAvailable /* There wasn't an OTA context available. */ -} OTA_JobParseErr_t; - -/** - * @brief OTA abort callback function typedef. - * - * The user may register a callback function when initializing the OTA Agent. This - * callback is used to override the behavior of how a job is aborted. - * - * @param[in] C File context of the job being aborted - */ -typedef OTA_Err_t (* pxOTAPALAbortCallback_t)( OTA_FileContext_t * const C ); - - -/** - * @brief OTA new image received callback function typedef. - * - * The user may register a callback function when initializing the OTA Agent. This - * callback is used to override the behavior of what happens when a new image is - * activated. - * - * @param[in] ulServerFileID File ID of the image received - */ -typedef OTA_Err_t (* pxOTAPALActivateNewImageCallback_t)( uint32_t ulServerFileID ); - -/** - * @brief OTA close file callback function typedef. - * - * The user may register a callback function when initializing the OTA Agent. This - * callback is used to override the behavior of what happens when a file is closed. - * - * @param[in] C File context of the job being aborted - */ -typedef OTA_Err_t (* pxOTAPALCloseFileCallback_t)( OTA_FileContext_t * const C ); - - -/** - * @brief OTA create file to store received data callback function typedef. - * - * The user may register a callback function when initializing the OTA Agent. This - * callback is used to override the behavior of how a new file is created. - * - * @param[in] C File context of the job being aborted - */ -typedef OTA_Err_t (* pxOTAPALCreateFileForRxCallback_t)( OTA_FileContext_t * const C ); - - -/** - * @brief OTA Get Platform Image State callback function typedef. - * - * The user may register a callback function when initializing the OTA Agent. This - * callback is used to override the behavior of returning the platform image state - * - * @param[in] ulServerFileID File ID of the image received - */ -typedef OTA_PAL_ImageState_t (* pxOTAPALGetPlatformImageStateCallback_t)( uint32_t ulServerFileID ); - -/** - * @brief OTA Reset Device callback function typedef. - * - * The user may register a callback function when initializing the OTA Agent. This - * callback is used to override the behavior of what happens when the OTA agent resets the device. - * - * @param[in] ulServerFileID File ID of the image received - */ -typedef OTA_Err_t (* pxOTAPALResetDeviceCallback_t)( uint32_t ulServerFileID ); - -/** - * @brief OTA Set Platform Image State callback function typedef. - * - * The user may register a callback function when initializing the OTA Agent. This - * callback is used to override the behavior of how a platform image state is stored - * - * @param[in] ulServerFileID File ID of the image received - * @param[in] eState Platform Image State to be state - */ -typedef OTA_Err_t (* pxOTAPALSetPlatformImageStateCallback_t)( uint32_t ulServerFileID, - OTA_ImageState_t eState ); - -/** - * @brief OTA Write Block callback function typedef. - * - * The user may register a callback function when initializing the OTA Agent. This - * callback is used to override the behavior of how a block is written to a file. - * - * @param[in] C File context of the job being aborted - * @param[in] iOffset Offset into the file to write the data - * @param[in] pacData Data to be written at the offset - * @param[in] iBlocksize Block size of the data to be written - */ -typedef int16_t (* pxOTAPALWriteBlockCallback_t)( OTA_FileContext_t * const C, - uint32_t iOffset, - uint8_t * const pacData, - uint32_t iBlockSize ); - -/** - * @brief Custom Job callback function typedef. - * - * The user may register a callback function when initializing the OTA Agent. This - * callback will be called when the OTA agent cannot parse a job document. - * - * @param[in] pcJSON Pointer to the json document received by the OTA agent - * @param[in] ulMsgLen Length of the json document received by the agent - */ -typedef OTA_JobParseErr_t (* pxOTACustomJobCallback_t)( const char * pcJSON, - uint32_t ulMsgLen ); - -/* OTA PAL callback structure */ -typedef struct -{ - pxOTAPALAbortCallback_t xAbort; /* OTA Abort callback pointer */ - pxOTAPALActivateNewImageCallback_t xActivateNewImage; /* OTA Activate New Image callback pointer */ - pxOTAPALCloseFileCallback_t xCloseFile; /* OTA Close File callback pointer */ - pxOTAPALCreateFileForRxCallback_t xCreateFileForRx; /* OTA Create File for Receive callback pointer */ - pxOTAPALGetPlatformImageStateCallback_t xGetPlatformImageState; /* OTA Get Platform Image State callback pointer */ - pxOTAPALResetDeviceCallback_t xResetDevice; /* OTA Reset Device callback pointer */ - pxOTAPALSetPlatformImageStateCallback_t xSetPlatformImageState; /* OTA Set Platform Image State callback pointer */ - pxOTAPALWriteBlockCallback_t xWriteBlock; /* OTA Write Block callback pointer */ - pxOTACompleteCallback_t xCompleteCallback; /* OTA Job Completed callback pointer */ - pxOTACustomJobCallback_t xCustomJobCallback; /* OTA Custom Job callback pointer */ -} OTA_PAL_Callbacks_t; - - - -/*---------------------------------------------------------------------------*/ -/* Public API */ -/*---------------------------------------------------------------------------*/ /** * @brief OTA Agent initialization function. * * Initialize the OTA engine by starting the OTA Agent ("OTA Task") in the system. This function must - * be called with the connection client context before calling OTA_CheckForUpdate(). Only one + * be called with the connection client context before calling @ref ota_function_checkforupdate. Only one * OTA Agent may exist. * - * @param[in] pvClient The messaging protocol client context (e.g. an MQTT context). + * @param[in] pvConnectionContext A pointer to a OTA_ConnectionContext_t object. * @param[in] pucThingName A pointer to a C string holding the Thing name. * @param[in] xFunc Static callback function for when an OTA job is complete. This function will have * input of the state of the OTA image after download and during self-test. * @param[in] xTicksToWait The number of ticks to wait until the OTA Task signals that it is ready. * If this is set to zero, then the function will return immediately after creating the OTA task but - * the OTA task may not be ready to operate yet. The state may be queried with OTA_GetAgentState(). + * the OTA task may not be ready to operate yet. The state may be queried with @ref ota_function_getagentstate. * * @return The state of the OTA Agent upon return from the OTA_State_t enum. * If the agent was successfully initialized and ready to operate, the state will be * eOTA_AgentState_Ready. Otherwise, it will be one of the other OTA_State_t enum values. */ -OTA_State_t OTA_AgentInit( void * pvClient, +OTA_State_t OTA_AgentInit( void * pvConnectionContext, const uint8_t * pucThingName, pxOTACompleteCallback_t xFunc, TickType_t xTicksToWait ); - /** * @brief Internal OTA Agent initialization function. * * Initialize the OTA engine by starting the OTA Agent ("OTA Task") in the system. This function must - * be called with the MQTT messaging client context before calling OTA_CheckForUpdate(). Only one + * be called with the MQTT messaging client context before calling @ref ota_function_checkforupdate. Only one * OTA Agent may exist. * - * @param[in] pvClient The messaging protocol client context (e.g. an MQTT context). + * @param[in] pvConnectionContext A pointer to a OTA_ConnectionContext_t object. * @param[in] pucThingName A pointer to a C string holding the Thing name. - * @param[in] xCallbacks Static callback structure for various OTA events. This function will have + * @param[in] pxCallbacks Static callback structure for various OTA events. This function will have * input of the state of the OTA image after download and during self-test. * @param[in] xTicksToWait The number of ticks to wait until the OTA Task signals that it is ready. * If this is set to zero, then the function will return immediately after creating the OTA task but - * the OTA task may not be ready to operate yet. The state may be queried with OTA_GetAgentState(). + * the OTA task may not be ready to operate yet. The state may be queried with @ref ota_function_getagentstate. * * @return The state of the OTA Agent upon return from the OTA_State_t enum. * If the agent was successfully initialized and ready to operate, the state will be * eOTA_AgentState_Ready. Otherwise, it will be one of the other OTA_State_t enum values. */ -OTA_State_t OTA_AgentInit_internal( void * pvClient, +OTA_State_t OTA_AgentInit_internal( void * pvConnectionContext, const uint8_t * pucThingName, - OTA_PAL_Callbacks_t * xCallbacks, + const OTA_PAL_Callbacks_t * pxCallbacks, TickType_t xTicksToWait ); - - /** * @brief Signal to the OTA Agent to shut down. * @@ -552,7 +636,7 @@ OTA_State_t OTA_GetAgentState( void ); * This function should reset the MCU and cause a reboot of the system to execute the newly updated * firmware. It should be called by the user code sometime after the eOTA_JobEvent_Activate event * is passed to the users application via the OTA Job Complete Callback mechanism. Refer to the - * OTA_AgentInit() function for more information about configuring the callback. + * @ref ota_function_init function for more information about configuring the callback. * * @return kOTA_Err_None if successful, otherwise an error code prefixed with 'kOTA_Err_' from the * list above. @@ -583,13 +667,32 @@ OTA_Err_t OTA_SetImageState( OTA_ImageState_t eState ); */ OTA_ImageState_t OTA_GetImageState( void ); -/* @brief Request for the next available OTA job from the job service. +/** + * @brief Request for the next available OTA job from the job service. * * @return kOTA_Err_None if successful, otherwise an error code prefixed with 'kOTA_Err_' from the * list above. */ OTA_Err_t OTA_CheckForUpdate( void ); +/** + * @brief Suspend OTA agent operations . + * + * @return kOTA_Err_None if successful, otherwise an error code prefixed with 'kOTA_Err_' from the + * list above. + */ +OTA_Err_t OTA_Suspend( void ); + +/** + * @brief Resume OTA agent operations . + * + * @param[in] pxConnection Update connection context. + * + * @return kOTA_Err_None if successful, otherwise an error code prefixed with 'kOTA_Err_' from the + * list above. + */ +OTA_Err_t OTA_Resume( void * pxConnection ); + /*---------------------------------------------------------------------------*/ /* Statistics API */ /*---------------------------------------------------------------------------*/ @@ -597,7 +700,7 @@ OTA_Err_t OTA_CheckForUpdate( void ); /** * @brief Get the number of OTA message packets received by the OTA agent. * - * @note Calling OTA_AgentInit() will reset this statistic. + * @note Calling @ref ota_function_init will reset this statistic. * * @return The number of OTA packets that have been received but not * necessarily queued for processing by the OTA agent. @@ -607,7 +710,7 @@ uint32_t OTA_GetPacketsReceived( void ); /** * @brief Get the number of OTA message packets queued by the OTA agent. * - * @note Calling OTA_AgentInit() will reset this statistic. + * @note Calling @ref ota_function_init will reset this statistic. * * @return The number of OTA packets that have been queued for processing. * This implies there was a free message queue entry so it can be passed @@ -618,7 +721,7 @@ uint32_t OTA_GetPacketsQueued( void ); /** * @brief Get the number of OTA message packets processed by the OTA agent. * - * @note Calling OTA_AgentInit() will reset this statistic. + * @note Calling @ref ota_function_init will reset this statistic. * * @return the number of OTA packets that have actually been processed. * @@ -628,7 +731,7 @@ uint32_t OTA_GetPacketsProcessed( void ); /** * @brief Get the number of OTA message packets dropped by the OTA agent. * - * @note Calling OTA_AgentInit() will reset this statistic. + * @note Calling @ref ota_function_init will reset this statistic. * * @return the number of OTA packets that have been dropped because * of either no queue or at shutdown cleanup. diff --git a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/include/aws_iot_ota_types.h b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/include/aws_iot_ota_types.h index d078492dd0..b79512c6b6 100755 --- a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/include/aws_iot_ota_types.h +++ b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/include/aws_iot_ota_types.h @@ -1,5 +1,5 @@ /* - * FreeRTOS OTA V1.1.1 + * FreeRTOS OTA V1.2.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_agent.c b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_agent.c index 3da83c30a7..2d63d128ac 100755 --- a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_agent.c +++ b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_agent.c @@ -1,5 +1,5 @@ /* - * FreeRTOS OTA V1.1.1 + * FreeRTOS OTA V1.2.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -27,6 +27,7 @@ #include "iot_config.h" /* Standard library includes. */ +#include #include #include #include @@ -75,16 +76,6 @@ typedef struct OTAStateTableEntry OTA_State_t xNextState; } OTAStateTableEntry_t; -/* - * Returns the byte offset of the element 'e' in the typedef structure 't'. - * Setting an arbitrarily large base of 0x10000 and masking off that base allows - * us to do the same thing as a zero offset without the lint warnings of using a - * null pointer. No structure is anywhere near 64K in size. - */ -/*lint -emacro((923,9078),OFFSET_OF) Intentionally cast pointer to uint32_t because we are using it as an offset. */ - -#define OFFSET_OF( t, e ) ( ( uint32_t ) ( &( ( t * ) 0x10000UL )->e ) & 0xffffUL ) - /* * This union allows us to access document model parameter addresses as their * actual type without casting every time we access a parameter. @@ -96,7 +87,7 @@ typedef union MultiParmPtr const char ** ppccPtr; uint32_t * pulPtr; uint32_t ulVal; - bool_t * pxBoolPtr; + bool * pbBoolPtr; Sig256_t ** ppxSig256Ptr; void ** ppvPtr; } MultiParmPtr_t; @@ -122,9 +113,9 @@ static OTA_DataInterface_t xOTA_DataInterface; * it is the same or not. */ -bool_t JSON_IsCStringEqual( const char * pcJSONString, - uint32_t ulLen, - const char * pcCString ); +static bool JSON_IsCStringEqual( const char * pcJSONString, + uint32_t ulLen, + const char * pcCString ); /* OTA agent private function prototypes. */ @@ -182,11 +173,11 @@ static DocParseErr_t prvParseJSONbyModel( const char * pcJSON, static OTA_FileContext_t * prvParseJobDoc( const char * pcJSON, uint32_t ulMsgLen, - bool_t * pbUpdateJob ); + bool * pbUpdateJob ); /* Close an open OTA file context and free it. */ -static bool_t prvOTA_Close( OTA_FileContext_t * const C ); +static bool prvOTA_Close( OTA_FileContext_t * const C ); /* Internal function to set the image state including an optional reason code. */ @@ -198,25 +189,25 @@ static OTA_Err_t prvSetImageStateWithReason( OTA_ImageState_t eState, static void prvDefaultOTACompleteCallback( OTA_JobEvent_t eEvent ); -/* Default Custom Callback handler if not provided to OTA_AgentInit_internal() */ +/* Default Custom Callback handler if not provided to OTA_AgentInit() */ static OTA_JobParseErr_t prvDefaultCustomJobCallback( const char * pcJSON, uint32_t ulMsgLen ); -/* Default Reset Device handler if not provided to OTA_AgentInit_internal() */ +/* Default Reset Device handler if not provided to OTA_AgentInit() */ static OTA_Err_t prvPAL_DefaultResetDevice( uint32_t ulServerFileID ); -/* Default Get Platform Image State handler if not provided to OTA_AgentInit_internal() */ +/* Default Get Platform Image State handler if not provided to OTA_AgentInit() */ static OTA_PAL_ImageState_t prvPAL_DefaultGetPlatformImageState( uint32_t ulServerFileID ); -/* Default Set Platform Image State handler if not provided to OTA_AgentInit_internal() */ +/* Default Set Platform Image State handler if not provided to OTA_AgentInit() */ static OTA_Err_t prvPAL_DefaultSetPlatformImageState( uint32_t ulServerFileID, OTA_ImageState_t eState ); -/* Default Activate New Image handler if not provided to OTA_AgentInit_internal() */ +/* Default Activate New Image handler if not provided to OTA_AgentInit() */ static OTA_Err_t prvPAL_DefaultActivateNewImage( uint32_t ulServerFileID ); @@ -248,7 +239,11 @@ static OTA_Err_t prvResetDevice( void ); /* Check if the platform is in self-test. */ -static bool_t prvInSelftest( void ); +static bool prvInSelftest( void ); + +/* Function to handle events that were unexpected in the current state. */ + +static void prvHandleUnexpectedEvents( OTA_EventMsg_t * pxEventMsg ); /* OTA state event handler functions. */ @@ -262,6 +257,8 @@ static OTA_Err_t prvRequestDataHandler( OTA_EventData_t * pxEventData ); static OTA_Err_t prvShutdownHandler( OTA_EventData_t * pxEventData ); static OTA_Err_t prvCloseFileHandler( OTA_EventData_t * pxEventData ); static OTA_Err_t prvUserAbortHandler( OTA_EventData_t * pxEventData ); +static OTA_Err_t prvSuspendHandler( OTA_EventData_t * pxEventData ); +static OTA_Err_t prvResumeHandler( OTA_EventData_t * pxEventData ); /* OTA default callback initializer. */ @@ -301,7 +298,7 @@ static OTA_AgentContext_t xOTA_Agent = .ulRequestMomentum = 0 }; -OTAStateTableEntry_t OTATransitionTable[] = +static OTAStateTableEntry_t OTATransitionTable[] = { /*STATE , EVENT , ACTION , NEXT STATE */ { eOTA_AgentState_Ready, eOTA_AgentEvent_Start, prvStartHandler, eOTA_AgentState_RequestingJob }, @@ -318,11 +315,13 @@ OTAStateTableEntry_t OTATransitionTable[] = { eOTA_AgentState_WaitingForFileBlock, eOTA_AgentEvent_RequestFileBlock, prvRequestDataHandler, eOTA_AgentState_WaitingForFileBlock }, { eOTA_AgentState_WaitingForFileBlock, eOTA_AgentEvent_RequestJobDocument, prvRequestJobHandler, eOTA_AgentState_WaitingForJob }, { eOTA_AgentState_WaitingForFileBlock, eOTA_AgentEvent_CloseFile, prvCloseFileHandler, eOTA_AgentState_WaitingForJob }, + { eOTA_AgentState_Suspended, eOTA_AgentEvent_Resume, prvResumeHandler, eOTA_AgentState_RequestingJob }, + { eOTA_AgentState_All, eOTA_AgentEvent_Suspend, prvSuspendHandler, eOTA_AgentState_Suspended }, { eOTA_AgentState_All, eOTA_AgentEvent_UserAbort, prvUserAbortHandler, eOTA_AgentState_WaitingForJob }, { eOTA_AgentState_All, eOTA_AgentEvent_Shutdown, prvShutdownHandler, eOTA_AgentState_ShuttingDown }, }; -const char * pcOTA_AgentState_Strings[ eOTA_AgentState_All ] = +static const char * pcOTA_AgentState_Strings[ eOTA_AgentState_All ] = { "Init", "Ready", @@ -332,11 +331,12 @@ const char * pcOTA_AgentState_Strings[ eOTA_AgentState_All ] = "RequestingFileBlock", "WaitingForFileBlock", "ClosingFile", + "Suspended", "ShuttingDown", "Stopped" }; -const char * pcOTA_Event_Strings[ eOTA_AgentEvent_Max ] = +static const char * pcOTA_Event_Strings[ eOTA_AgentEvent_Max ] = { "Start", "StartSelfTest", @@ -347,6 +347,8 @@ const char * pcOTA_Event_Strings[ eOTA_AgentEvent_Max ] = "ReceivedFileBlock", "RequestTimer", "CloseFile", + "Suspend", + "Resume", "UserAbort", "Shutdown" }; @@ -354,19 +356,19 @@ const char * pcOTA_Event_Strings[ eOTA_AgentEvent_Max ] = /* * This is a private function which checks if the platform is in self-test. */ -static bool_t prvInSelftest( void ) +static bool prvInSelftest( void ) { - bool_t xSelfTest = false; + bool bSelfTest = false; /* * Get the platform state from the OTA pal layer. */ if( xOTA_Agent.xPALCallbacks.xGetPlatformImageState( xOTA_Agent.ulServerFileID ) == eOTA_PAL_ImageState_PendingCommit ) { - xSelfTest = true; + bSelfTest = true; } - return xSelfTest; + return bSelfTest; } /* @@ -459,7 +461,7 @@ static void prvRequestTimer_Callback( TimerHandle_t T ) * Send event to OTA agent task. */ xEventMsg.xEventId = eOTA_AgentEvent_RequestTimer; - OTA_SignalEvent( &xEventMsg ); + ( void ) OTA_SignalEvent( &xEventMsg ); } /* Create and start or reset the OTA request timer to kick off the process if needed. @@ -478,7 +480,7 @@ static void prvStartRequestTimer( uint32_t xPeriodMS ) xOTA_Agent.xRequestTimer = xTimerCreate( pcTimerName, pdMS_TO_TICKS( xPeriodMS ), pdFALSE, - ( void * ) xOTA_Agent.ulServerFileID, + NULL, prvRequestTimer_Callback ); if( xOTA_Agent.xRequestTimer != NULL ) @@ -514,112 +516,84 @@ static void prvStopRequestTimer( void ) } } -static OTA_Err_t prvSetImageStateWithReason( OTA_ImageState_t eState, - uint32_t ulReason ) +static OTA_Err_t prvUpdateJobStatusFromImageState( OTA_ImageState_t eState, + int32_t lSubReason ) { - DEFINE_OTA_METHOD_NAME( "prvSetImageStateWithReason" ); - OTA_Err_t xErr = kOTA_Err_Uninitialized; + int32_t lReason = 0; - if( ( eState > eOTA_ImageState_Unknown ) && ( eState <= eOTA_LastImageState ) ) + if( eState == eOTA_ImageState_Testing ) { - /* - * Call the platform specific code to set the image state. - */ - xErr = xOTA_Agent.xPALCallbacks.xSetPlatformImageState( xOTA_Agent.ulServerFileID, eState ); - - /* - * If the platform image state couldn't be set correctly, force fail the update. - */ - if( xErr != kOTA_Err_None ) - { - /* - * Maintain Aborted since it's also a failed OTA and we want the initial failure type. - */ - if( eState != eOTA_ImageState_Aborted ) - { - eState = eOTA_ImageState_Rejected; /*lint !e9044 intentionally override eState since we failed within this function. */ - - if( ulReason == kOTA_Err_None ) - { - /* - * Capture the failure reason if not already set (and we're not already Aborted as checked above). - */ - ulReason = ( uint32_t ) xErr; /*lint !e9044 intentionally override lReason since we failed within this function. */ - } - else - { - /* - * Keep the original reject reason code since it is possible for the PAL - * to fail to update the image state in some cases (e.g. a reset already - * caused the bundle rollback and we failed to rollback again). - */ - } - } - else - { - /* - * If it was aborted, keep the original abort reason code. That's more useful - * to the OTA operator. - */ - } - } - - /* - * Update the image state in OTA context. - */ - xOTA_Agent.eImageState = eState; - - if( xOTA_Agent.pcOTA_Singleton_ActiveJobName != NULL ) - { - if( eState == eOTA_ImageState_Testing ) - { - /* - * We discovered we're ready for test mode, put job status in self_test active. - */ - xOTA_ControlInterface.prvUpdateJobStatus( &xOTA_Agent, eJobStatus_InProgress, ( int32_t ) eJobReason_SelfTestActive, ( int32_t ) NULL ); - } - else - { - if( eState == eOTA_ImageState_Accepted ) - { - /* - * Now that we've accepted the firmware update, we can complete the job. - */ - prvStopSelfTestTimer(); - xOTA_ControlInterface.prvUpdateJobStatus( &xOTA_Agent, eJobStatus_Succeeded, ( int32_t ) eJobReason_Accepted, xAppFirmwareVersion.u.lVersion32 ); - } - else if( eState == eOTA_ImageState_Rejected ) - { - /* - * The firmware update was rejected, complete the job as FAILED (Job service - * doesn't allow us to set REJECTED after the job has been started already). - */ - xOTA_ControlInterface.prvUpdateJobStatus( &xOTA_Agent, eJobStatus_Failed, ( int32_t ) eJobReason_Rejected, ( int32_t ) ulReason ); - } - else /* All other states have been checked so it must be ABORTED. */ - { - /* Complete the job as FAILED. */ - xOTA_ControlInterface.prvUpdateJobStatus( &xOTA_Agent, eJobStatus_Failed, ( int32_t ) eJobReason_Aborted, ( int32_t ) ulReason ); - } - - /* - * We don't need the job name memory anymore since we're done with this job. - */ - vPortFree( xOTA_Agent.pcOTA_Singleton_ActiveJobName ); - xOTA_Agent.pcOTA_Singleton_ActiveJobName = NULL; - } - - xErr = kOTA_Err_None; - } - else - { - xErr = kOTA_Err_NoActiveJob; - } + /* We discovered we're ready for test mode, put job status in self_test active. */ + xErr = xOTA_ControlInterface.prvUpdateJobStatus( &xOTA_Agent, eJobStatus_InProgress, eJobReason_SelfTestActive, 0 ); } else { - xErr = kOTA_Err_BadImageState; + if( eState == eOTA_ImageState_Accepted ) + { + /* Now that we've accepted the firmware update, we can complete the job. */ + prvStopSelfTestTimer(); + xErr = xOTA_ControlInterface.prvUpdateJobStatus( &xOTA_Agent, eJobStatus_Succeeded, eJobReason_Accepted, xAppFirmwareVersion.u.lVersion32 ); + } + else + { + /* + * The firmware update was either rejected or aborted, complete the job as FAILED (Job service + * doesn't allow us to set REJECTED after the job has been started already). + */ + lReason = ( eState == eOTA_ImageState_Rejected ) ? eJobReason_Rejected : eJobReason_Aborted; + xErr = xOTA_ControlInterface.prvUpdateJobStatus( &xOTA_Agent, eJobStatus_Failed, lReason, lSubReason ); + } + + /* + * We don't need the job name memory anymore since we're done with this job. + */ + vPortFree( xOTA_Agent.pcOTA_Singleton_ActiveJobName ); + xOTA_Agent.pcOTA_Singleton_ActiveJobName = NULL; + } + + return xErr; +} + +static OTA_Err_t prvSetImageStateWithReason( OTA_ImageState_t eState, + uint32_t ulReason ) +{ + OTA_Err_t xErr = kOTA_Err_Uninitialized; + + configASSERT( ( eState > eOTA_ImageState_Unknown ) && ( eState <= eOTA_LastImageState ) ); + + /* Call the platform specific code to set the image state. */ + xErr = xOTA_Agent.xPALCallbacks.xSetPlatformImageState( xOTA_Agent.ulServerFileID, eState ); + + /* + * If the platform image state couldn't be set correctly, force fail the update by setting the + * image state to "Rejected" unless it's already in "Aborted". + */ + if( ( xErr != kOTA_Err_None ) && ( eState != eOTA_ImageState_Aborted ) ) + { + eState = eOTA_ImageState_Rejected; /*lint !e9044 intentionally override eState since we failed within this function. */ + + /* + * Capture the failure reason if not already set (and we're not already Aborted as checked above). Otherwise Keep + * the original reject reason code since it is possible for the PAL to fail to update the image state in some + * cases (e.g. a reset already caused the bundle rollback and we failed to rollback again). + */ + if( ulReason == kOTA_Err_None ) + { + ulReason = xErr; /*lint !e9044 intentionally override ulReason since we failed within this function. */ + } + } + + /* Now update the image state and job status on service side. */ + xOTA_Agent.eImageState = eState; + + if( xOTA_Agent.pcOTA_Singleton_ActiveJobName != NULL ) + { + xErr = prvUpdateJobStatusFromImageState( eState, ( int32_t ) ulReason ); + } + else + { + xErr = kOTA_Err_NoActiveJob; } return xErr; @@ -719,6 +693,101 @@ static OTA_JobParseErr_t prvDefaultCustomJobCallback( const char * pcJSON, return eOTA_JobParseErr_NonConformingJobDoc; } +static void prvSetPALCallbacks( const OTA_PAL_Callbacks_t * pxCallbacks ) +{ + configASSERT( pxCallbacks != NULL ); + + if( pxCallbacks->xAbort != NULL ) + { + xOTA_Agent.xPALCallbacks.xAbort = pxCallbacks->xAbort; + } + else + { + xOTA_Agent.xPALCallbacks.xAbort = prvPAL_Abort; + } + + if( pxCallbacks->xActivateNewImage != NULL ) + { + xOTA_Agent.xPALCallbacks.xActivateNewImage = pxCallbacks->xActivateNewImage; + } + else + { + xOTA_Agent.xPALCallbacks.xActivateNewImage = prvPAL_DefaultActivateNewImage; + } + + if( pxCallbacks->xCloseFile != NULL ) + { + xOTA_Agent.xPALCallbacks.xCloseFile = pxCallbacks->xCloseFile; + } + else + { + xOTA_Agent.xPALCallbacks.xCloseFile = prvPAL_CloseFile; + } + + if( pxCallbacks->xCreateFileForRx != NULL ) + { + xOTA_Agent.xPALCallbacks.xCreateFileForRx = pxCallbacks->xCreateFileForRx; + } + else + { + xOTA_Agent.xPALCallbacks.xCreateFileForRx = prvPAL_CreateFileForRx; + } + + if( pxCallbacks->xGetPlatformImageState != NULL ) + { + xOTA_Agent.xPALCallbacks.xGetPlatformImageState = pxCallbacks->xGetPlatformImageState; + } + else + { + xOTA_Agent.xPALCallbacks.xGetPlatformImageState = prvPAL_DefaultGetPlatformImageState; + } + + if( pxCallbacks->xResetDevice != NULL ) + { + xOTA_Agent.xPALCallbacks.xResetDevice = pxCallbacks->xResetDevice; + } + else + { + xOTA_Agent.xPALCallbacks.xResetDevice = prvPAL_DefaultResetDevice; + } + + if( pxCallbacks->xSetPlatformImageState != NULL ) + { + xOTA_Agent.xPALCallbacks.xSetPlatformImageState = pxCallbacks->xSetPlatformImageState; + } + else + { + xOTA_Agent.xPALCallbacks.xSetPlatformImageState = prvPAL_DefaultSetPlatformImageState; + } + + if( pxCallbacks->xWriteBlock != NULL ) + { + xOTA_Agent.xPALCallbacks.xWriteBlock = pxCallbacks->xWriteBlock; + } + else + { + xOTA_Agent.xPALCallbacks.xWriteBlock = prvPAL_WriteBlock; + } + + if( pxCallbacks->xCompleteCallback != NULL ) + { + xOTA_Agent.xPALCallbacks.xCompleteCallback = pxCallbacks->xCompleteCallback; + } + else + { + xOTA_Agent.xPALCallbacks.xCompleteCallback = prvDefaultOTACompleteCallback; + } + + if( pxCallbacks->xCustomJobCallback != NULL ) + { + xOTA_Agent.xPALCallbacks.xCustomJobCallback = pxCallbacks->xCustomJobCallback; + } + else + { + xOTA_Agent.xPALCallbacks.xCustomJobCallback = prvDefaultCustomJobCallback; + } +} + static OTA_Err_t prvStartHandler( OTA_EventData_t * pxEventData ) { DEFINE_OTA_METHOD_NAME( "prvStartHandler" ); @@ -727,11 +796,13 @@ static OTA_Err_t prvStartHandler( OTA_EventData_t * pxEventData ) OTA_Err_t xReturn = kOTA_Err_None; OTA_EventMsg_t xEventMsg = { 0 }; + /* Start self-test timer, if platform is in self-test. */ + prvStartSelfTestTimer(); /* Send event to OTA task to get job document. */ xEventMsg.xEventId = eOTA_AgentEvent_RequestJobDocument; - if( OTA_SignalEvent( &xEventMsg ) != pdTRUE ) + if( !OTA_SignalEvent( &xEventMsg ) ) { xReturn = kOTA_Err_EventQueueSendFailed; } @@ -748,7 +819,7 @@ static OTA_Err_t prvInSelfTestHandler( OTA_EventData_t * pxEventData ) OTA_LOG_L1( "[%s] prvInSelfTestHandler, platform is in self-test.\r\n", OTA_METHOD_NAME ); /* Check the platform's OTA update image state. It should also be in self test. */ - if( prvStartSelfTestTimer() == pdTRUE ) + if( prvInSelftest() == true ) { /* Callback for application specific self-test. */ xOTA_Agent.xPALCallbacks.xCompleteCallback( eOTA_JobEvent_StartTest ); @@ -796,13 +867,19 @@ static OTA_Err_t prvRequestJobHandler( OTA_EventData_t * pxEventData ) /* Send shutdown event to the OTA Agent task. */ xEventMsg.xEventId = eOTA_AgentEvent_Shutdown; - OTA_SignalEvent( &xEventMsg ); - /* - * Too many requests have been sent without a response or too many failures - * when trying to publish the request message. Abort. Store attempt count in low bits. - */ - xReturn = ( uint32_t ) kOTA_Err_MomentumAbort | ( otaconfigMAX_NUM_REQUEST_MOMENTUM & ( uint32_t ) kOTA_PAL_ErrMask ); + if( !OTA_SignalEvent( &xEventMsg ) ) + { + xReturn = kOTA_Err_EventQueueSendFailed; + } + else + { + /* + * Too many requests have been sent without a response or too many failures + * when trying to publish the request message. Abort. Store attempt count in low bits. + */ + xReturn = ( uint32_t ) kOTA_Err_MomentumAbort | ( otaconfigMAX_NUM_REQUEST_MOMENTUM & ( uint32_t ) kOTA_PAL_ErrMask ); + } } } else @@ -846,9 +923,15 @@ static OTA_Err_t prvProcessJobHandler( OTA_EventData_t * pxEventData ) { /* Send event to OTA task to start self-test. */ xEventMsg.xEventId = eOTA_AgentEvent_StartSelfTest; - OTA_SignalEvent( &xEventMsg ); - xReturn = kOTA_Err_None; + if( !OTA_SignalEvent( &xEventMsg ) ) + { + xReturn = kOTA_Err_EventQueueSendFailed; + } + else + { + xReturn = kOTA_Err_None; + } } else { @@ -876,7 +959,10 @@ static OTA_Err_t prvProcessJobHandler( OTA_EventData_t * pxEventData ) xEventMsg.xEventId = eOTA_AgentEvent_CreateFile; /*Send the event to OTA Agent task. */ - OTA_SignalEvent( &xEventMsg ); + if( !OTA_SignalEvent( &xEventMsg ) ) + { + xReturn = kOTA_Err_EventQueueSendFailed; + } } else { @@ -918,11 +1004,17 @@ static OTA_Err_t prvInitFileHandler( OTA_EventData_t * pxEventData ) /* Send shutdown event. */ xEventMsg.xEventId = eOTA_AgentEvent_Shutdown; - OTA_SignalEvent( &xEventMsg ); - /* Too many requests have been sent without a response or too many failures - * when trying to publish the request message. Abort. Store attempt count in low bits. */ - xErr = ( uint32_t ) kOTA_Err_MomentumAbort | ( otaconfigMAX_NUM_REQUEST_MOMENTUM & ( uint32_t ) kOTA_PAL_ErrMask ); + if( !OTA_SignalEvent( &xEventMsg ) ) + { + xErr = kOTA_Err_EventQueueSendFailed; + } + else + { + /* Too many requests have been sent without a response or too many failures + * when trying to publish the request message. Abort. Store attempt count in low bits. */ + xErr = ( uint32_t ) kOTA_Err_MomentumAbort | ( otaconfigMAX_NUM_REQUEST_MOMENTUM & ( uint32_t ) kOTA_PAL_ErrMask ); + } } } else @@ -931,7 +1023,11 @@ static OTA_Err_t prvInitFileHandler( OTA_EventData_t * pxEventData ) xOTA_Agent.ulRequestMomentum = 0; xEventMsg.xEventId = eOTA_AgentEvent_RequestFileBlock; - OTA_SignalEvent( &xEventMsg ); + + if( !OTA_SignalEvent( &xEventMsg ) ) + { + xErr = kOTA_Err_EventQueueSendFailed; + } } return xErr; @@ -967,14 +1063,20 @@ static OTA_Err_t prvRequestDataHandler( OTA_EventData_t * pxEventData ) /* Send shutdown event. */ xEventMsg.xEventId = eOTA_AgentEvent_Shutdown; - OTA_SignalEvent( &xEventMsg ); - /* Too many requests have been sent without a response or too many failures - * when trying to publish the request message. Abort. Store attempt count in low bits. */ - xErr = ( uint32_t ) kOTA_Err_MomentumAbort | ( otaconfigMAX_NUM_REQUEST_MOMENTUM & ( uint32_t ) kOTA_PAL_ErrMask ); + if( !OTA_SignalEvent( &xEventMsg ) ) + { + xErr = kOTA_Err_EventQueueSendFailed; + } + else + { + /* Too many requests have been sent without a response or too many failures + * when trying to publish the request message. Abort. Store attempt count in low bits. */ + xErr = ( uint32_t ) kOTA_Err_MomentumAbort | ( otaconfigMAX_NUM_REQUEST_MOMENTUM & ( uint32_t ) kOTA_PAL_ErrMask ); - /* Reset the request momentum. */ - xOTA_Agent.ulRequestMomentum = 0; + /* Reset the request momentum. */ + xOTA_Agent.ulRequestMomentum = 0; + } } } @@ -1010,7 +1112,12 @@ static OTA_Err_t prvProcessDataHandler( OTA_EventData_t * pxEventData ) if( xResult == eIngest_Result_FileComplete ) { /* File receive is complete and authenticated. Update the job status with the self_test ready identifier. */ - xOTA_ControlInterface.prvUpdateJobStatus( &xOTA_Agent, eJobStatus_InProgress, ( int32_t ) eJobReason_SigCheckPassed, ( int32_t ) NULL ); + xErr = xOTA_ControlInterface.prvUpdateJobStatus( &xOTA_Agent, eJobStatus_InProgress, eJobReason_SigCheckPassed, 0 ); + + if( xErr != kOTA_Err_None ) + { + OTA_LOG_L2( "[%s] Failed to update job status %d\r\n", OTA_METHOD_NAME, xErr ); + } } else { @@ -1023,18 +1130,23 @@ static OTA_Err_t prvProcessDataHandler( OTA_EventData_t * pxEventData ) { OTA_LOG_L2( "[%s] Error trying to set platform image state (0x%08x)\r\n", OTA_METHOD_NAME, ( int32_t ) xErr ); } - else - { - /* Nothing special to do on success. */ - } /* Update the job status with the with failure code. */ - xOTA_ControlInterface.prvUpdateJobStatus( &xOTA_Agent, eJobStatus_FailedWithVal, ( int32_t ) xCloseResult, ( int32_t ) xResult ); + xErr = xOTA_ControlInterface.prvUpdateJobStatus( &xOTA_Agent, eJobStatus_FailedWithVal, ( int32_t ) xCloseResult, ( int32_t ) xResult ); + + if( xErr != kOTA_Err_None ) + { + OTA_LOG_L2( "[%s] Failed to update job status %d\r\n", OTA_METHOD_NAME, xErr ); + } } /* Send event to close file. */ xEventMsg.xEventId = eOTA_AgentEvent_CloseFile; - OTA_SignalEvent( &xEventMsg ); + + if( !OTA_SignalEvent( &xEventMsg ) ) + { + OTA_LOG_L2( "[%s] Failed to singal OTA agent to close file.", OTA_METHOD_NAME ); + } /* Let main application know of our result. */ xOTA_Agent.xPALCallbacks.xCompleteCallback( ( xResult == eIngest_Result_FileComplete ) ? eOTA_JobEvent_Activate : eOTA_JobEvent_Fail ); @@ -1053,10 +1165,15 @@ static OTA_Err_t prvProcessDataHandler( OTA_EventData_t * pxEventData ) /* We're actively receiving a file so update the job status as needed. */ /* First reset the momentum counter since we received a good block. */ xOTA_Agent.ulRequestMomentum = 0; - xOTA_ControlInterface.prvUpdateJobStatus( &xOTA_Agent, eJobStatus_InProgress, ( int32_t ) eJobReason_Receiving, ( int32_t ) NULL ); + xErr = xOTA_ControlInterface.prvUpdateJobStatus( &xOTA_Agent, eJobStatus_InProgress, eJobReason_Receiving, 0 ); + + if( xErr != kOTA_Err_None ) + { + OTA_LOG_L2( "[%s] Failed to update job status %d\r\n", OTA_METHOD_NAME, xErr ); + } } - if( xOTA_Agent.ulNumOfBlocksToReceive > 1 ) + if( xOTA_Agent.ulNumOfBlocksToReceive > 1U ) { xOTA_Agent.ulNumOfBlocksToReceive--; } @@ -1065,7 +1182,11 @@ static OTA_Err_t prvProcessDataHandler( OTA_EventData_t * pxEventData ) prvStartRequestTimer( otaconfigFILE_REQUEST_WAIT_MS ); xEventMsg.xEventId = eOTA_AgentEvent_RequestFileBlock; - OTA_SignalEvent( &xEventMsg ); + + if( !OTA_SignalEvent( &xEventMsg ) ) + { + OTA_LOG_L2( "[%s] Failed to signal OTA agent to close file.", OTA_METHOD_NAME ); + } } } @@ -1109,6 +1230,7 @@ static OTA_Err_t prvUserAbortHandler( OTA_EventData_t * pxEventData ) static OTA_Err_t prvShutdownHandler( OTA_EventData_t * pxEventData ) { + DEFINE_OTA_METHOD_NAME( "prvShutdownHandler" ); ( void ) pxEventData; OTA_LOG_L2( "[%s] Shutting Down OTA Agent. %d\r\n", OTA_METHOD_NAME ); @@ -1117,7 +1239,7 @@ static OTA_Err_t prvShutdownHandler( OTA_EventData_t * pxEventData ) prvAgentShutdownCleanup(); /* Clear the entire agent context. This includes the OTA agent state. */ - memset( &xOTA_Agent, 0, sizeof( xOTA_Agent ) ); + ( void ) memset( &xOTA_Agent, 0, sizeof( xOTA_Agent ) ); xOTA_Agent.eState = eOTA_AgentState_Stopped; @@ -1127,6 +1249,43 @@ static OTA_Err_t prvShutdownHandler( OTA_EventData_t * pxEventData ) return kOTA_Err_None; } +static OTA_Err_t prvSuspendHandler( OTA_EventData_t * pxEventData ) +{ + DEFINE_OTA_METHOD_NAME( "prvSuspendHandler" ); + + ( void ) pxEventData; + OTA_Err_t xErr = kOTA_Err_None; + + /* Log the state change to suspended state.*/ + OTA_LOG_L1( "[%s] OTA Agent is suspended.\r\n", OTA_METHOD_NAME ); + + return xErr; +} + +static OTA_Err_t prvResumeHandler( OTA_EventData_t * pxEventData ) +{ + DEFINE_OTA_METHOD_NAME( "prvResumeHandler" ); + + ( void ) pxEventData; + + OTA_EventMsg_t xEventMsg = { 0 }; + + /* + * Update the connection handle before resuming the OTA process. + */ + + OTA_LOG_L2( "[%s] Updating the connection handle. %d\r\n", OTA_METHOD_NAME ); + + xOTA_Agent.pvConnectionContext = pxEventData; + + /* + * Send signal to request job document. + */ + xEventMsg.xEventId = eOTA_AgentEvent_RequestJobDocument; + + return OTA_SignalEvent( &xEventMsg ) ? kOTA_Err_None : kOTA_Err_EventQueueSendFailed; +} + /* * This is a private function only meant to be called by the OTA agent after the * currently running image that is in the self test phase rejects the update. @@ -1156,10 +1315,12 @@ static OTA_Err_t prvResetDevice( void ) void prvOTAEventBufferFree( OTA_EventData_t * const pxBuffer ) { + DEFINE_OTA_METHOD_NAME( "prvOTAEventBufferFree" ); + if( xSemaphoreTake( xOTA_Agent.xOTA_ThreadSafetyMutex, portMAX_DELAY ) == pdPASS ) { pxBuffer->bBufferUsed = false; - xSemaphoreGive( xOTA_Agent.xOTA_ThreadSafetyMutex ); + ( void ) xSemaphoreGive( xOTA_Agent.xOTA_ThreadSafetyMutex ); } else { @@ -1169,6 +1330,8 @@ void prvOTAEventBufferFree( OTA_EventData_t * const pxBuffer ) OTA_EventData_t * prvOTAEventBufferGet( void ) { + DEFINE_OTA_METHOD_NAME( "prvOTAEventBufferGet" ); + uint32_t ulIndex = 0; OTA_EventData_t * pxOTAFreeMsg = NULL; @@ -1185,7 +1348,7 @@ OTA_EventData_t * prvOTAEventBufferGet( void ) } } - xSemaphoreGive( xOTA_Agent.xOTA_ThreadSafetyMutex ); + ( void ) xSemaphoreGive( xOTA_Agent.xOTA_ThreadSafetyMutex ); } else { @@ -1257,11 +1420,11 @@ static void prvOTA_FreeContext( OTA_FileContext_t * const C ) /* Close an existing OTA context and free its resources. */ -static bool_t prvOTA_Close( OTA_FileContext_t * const C ) +static bool prvOTA_Close( OTA_FileContext_t * const C ) { DEFINE_OTA_METHOD_NAME( "prvOTA_Close" ); - bool_t xResult = pdFALSE; + bool bResult = false; OTA_LOG_L1( "[%s] Context->0x%p\r\n", OTA_METHOD_NAME, C ); @@ -1276,12 +1439,12 @@ static bool_t prvOTA_Close( OTA_FileContext_t * const C ) prvOTA_FreeContext( C ); /* Clear the entire structure now that it is free. */ - memset( C, 0, sizeof( OTA_FileContext_t ) ); + ( void ) memset( C, 0, sizeof( OTA_FileContext_t ) ); - xResult = pdTRUE; + bResult = true; } - return xResult; + return bResult; } @@ -1299,7 +1462,7 @@ static OTA_FileContext_t * prvGetFreeContext( void ) if( ulIndex != OTA_MAX_FILES ) { - memset( &xOTA_Agent.pxOTA_Files[ ulIndex ], 0, sizeof( OTA_FileContext_t ) ); + ( void ) memset( &xOTA_Agent.pxOTA_Files[ ulIndex ], 0, sizeof( OTA_FileContext_t ) ); C = &xOTA_Agent.pxOTA_Files[ ulIndex ]; xOTA_Agent.ulFileIndex = ulIndex; } @@ -1311,24 +1474,24 @@ static OTA_FileContext_t * prvGetFreeContext( void ) return C; } -bool_t JSON_IsCStringEqual( const char * pcJSONString, - uint32_t ulLen, - const char * pcCString ) +static bool JSON_IsCStringEqual( const char * pcJSONString, + uint32_t ulLen, + const char * pcCString ) { - bool_t xResult; + bool bResult; if( ( ulLen <= OTA_MAX_JSON_STR_LEN ) && ( strncmp( pcJSONString, pcCString, ulLen ) == 0 ) && /*lint !e9007 I see no side effect. Possibly a lint bug. */ ( pcCString[ ulLen ] == '\0' ) ) { - xResult = pdTRUE; + bResult = true; } else { - xResult = pdFALSE; + bResult = false; } - return xResult; + return bResult; } @@ -1345,19 +1508,19 @@ static DocParseErr_t prvSearchModelForTokenKey( JSON_DocModel_t * pxDocModel, for( usParamIndex = 0; usParamIndex < pxDocModel->usNumModelParams; usParamIndex++ ) { if( JSON_IsCStringEqual( pcJSONString, ulStrLen, - pxDocModel->pxBodyDef[ usParamIndex ].pcSrcKey ) == ( bool_t ) pdTRUE ) + pxDocModel->pxBodyDef[ usParamIndex ].pcSrcKey ) ) { /* Per Security, don't allow multiple entries of the same parameter. */ - if( ( pxDocModel->ulParamsReceivedBitmap & ( 1U << usParamIndex ) ) != 0U ) /*lint !e9032 usParamIndex will never be greater than kDocModel_MaxParams, which is the the size of the bitmap. */ + if( ( pxDocModel->ulParamsReceivedBitmap & ( ( uint32_t ) 1U << usParamIndex ) ) != 0U ) /*lint !e9032 usParamIndex will never be greater than kDocModel_MaxParams, which is the the size of the bitmap. */ { eErr = eDocParseErr_DuplicatesNotAllowed; } else { /* Mark parameter as received in the bitmap. */ - pxDocModel->ulParamsReceivedBitmap |= ( 1U << usParamIndex ); /*lint !e9032 usParamIndex will never be greater than kDocModel_MaxParams, which is the the size of the bitmap. */ - *pulMatchingIndexResult = usParamIndex; /* Save result index for caller. */ - eErr = eDocParseErr_None; /* We found a matching key in the document model. */ + pxDocModel->ulParamsReceivedBitmap |= ( ( uint32_t ) 1U << usParamIndex ); /*lint !e9032 usParamIndex will never be greater than kDocModel_MaxParams, which is the the size of the bitmap. */ + *pulMatchingIndexResult = usParamIndex; /* Save result index for caller. */ + eErr = eDocParseErr_None; /* We found a matching key in the document model. */ } break; /* We found a key match so stop searching. */ @@ -1375,323 +1538,344 @@ static DocParseErr_t prvParseJSONbyModel( const char * pcJSON, { DEFINE_OTA_METHOD_NAME( "prvParseJSONbyModel" ); - const JSON_DocParam_t * pxModelParam; + const JSON_DocParam_t * pxModelParam = NULL; jsmn_parser xParser; - jsmntok_t * pxTokens, * pxValTok; - uint32_t ulNumTokens, ulTokenLen; + jsmntok_t * pxTokens = NULL; + const jsmntok_t * pxValTok = NULL; + int32_t jsmn_result = 0; + uint32_t ulNumTokens = 0, ulTokenLen = 0; MultiParmPtr_t xParamAddr; /*lint !e9018 We intentionally use this union to cast the parameter address to the proper type. */ - uint32_t ulIndex; - uint16_t usModelParamIndex; - uint32_t ulScanIndex; - DocParseErr_t eErr = eDocParseErr_Unknown; - + uint32_t ulIndex = 0; + uint16_t usModelParamIndex = 0; + uint32_t ulScanIndex = 0; + DocParseErr_t eErr = eDocParseErr_None; /* Reset the Jasmine tokenizer. */ jsmn_init( &xParser ); - /* Validate some initial parameters. */ + /* Check if document model is valid. */ if( pxDocModel == NULL ) { OTA_LOG_L1( "[%s] The pointer to the document model is NULL.\r\n", OTA_METHOD_NAME ); eErr = eDocParseErr_NullModelPointer; } - else if( pxDocModel->pxBodyDef == NULL ) + + /* Check if document model body ponter is valid.*/ + if( eErr == eDocParseErr_None ) { - OTA_LOG_L1( "[%s] Document model 0x%08x body pointer is NULL.\r\n", OTA_METHOD_NAME, pxDocModel ); - eErr = eDocParseErr_NullBodyPointer; + if( pxDocModel->pxBodyDef == NULL ) + { + OTA_LOG_L1( "[%s] Document model 0x%08x body pointer is NULL.\r\n", OTA_METHOD_NAME, pxDocModel ); + eErr = eDocParseErr_NullBodyPointer; + } } - else if( pxDocModel->usNumModelParams > OTA_DOC_MODEL_MAX_PARAMS ) + + /* Check number of parameters is valid.*/ + if( eErr == eDocParseErr_None ) { - OTA_LOG_L1( "[%s] Model has too many parameters (%u).\r\n", OTA_METHOD_NAME, pxDocModel->usNumModelParams ); - eErr = eDocParseErr_TooManyParams; + if( pxDocModel->usNumModelParams > OTA_DOC_MODEL_MAX_PARAMS ) + { + OTA_LOG_L1( "[%s] Model has too many parameters (%u).\r\n", OTA_METHOD_NAME, pxDocModel->usNumModelParams ); + eErr = eDocParseErr_TooManyParams; + } } - else if( pcJSON == NULL ) + + /* Check JSON document pointer is valid.*/ + if( eErr == eDocParseErr_None ) { - OTA_LOG_L1( "[%s] JSON document pointer is NULL!\r\n", OTA_METHOD_NAME ); - eErr = eDocParseErr_NullDocPointer; + if( pcJSON == NULL ) + { + OTA_LOG_L1( "[%s] JSON document pointer is NULL!\r\n", OTA_METHOD_NAME ); + eErr = eDocParseErr_NullDocPointer; + } } - else + + /* Check if token numbe is valid. */ + if( eErr == eDocParseErr_None ) { pxModelParam = pxDocModel->pxBodyDef; /* Count the total number of tokens in our JSON document. */ - ulNumTokens = ( uint32_t ) jsmn_parse( &xParser, pcJSON, ( size_t ) ulMsgLen, NULL, 1UL ); + jsmn_result = jsmn_parse( &xParser, pcJSON, ( size_t ) ulMsgLen, NULL, 1UL ); + ulNumTokens = jsmn_result < 0 ? 0 : ( uint32_t ) jsmn_result; - if( ulNumTokens > 0U ) - { - /* If the JSON document isn't too big for our token array... */ - if( ulNumTokens <= OTA_MAX_JSON_TOKENS ) - { - /* Allocate space for the document JSON tokens. */ - void * pvTokenArray = pvPortMalloc( ulNumTokens * sizeof( jsmntok_t ) ); /* Allocate space on heap for temporary token array. */ - pxTokens = ( jsmntok_t * ) pvTokenArray; /*lint !e9079 !e9087 heap allocations return void* so we allow casting to a pointer to the actual type. */ - - if( pxTokens != NULL ) - { - /* Reset Jasmine again and tokenize the document for real. */ - jsmn_init( &xParser ); - ulIndex = ( uint32_t ) jsmn_parse( &xParser, pcJSON, ulMsgLen, pxTokens, ulNumTokens ); - - if( ulIndex == ulNumTokens ) - { - /* Start the parser in an error free state. */ - eErr = eDocParseErr_None; - - /* Examine each JSON token, searching for job parameters based on our document model. */ - for( ulIndex = 0U; ( eErr == eDocParseErr_None ) && ( ulIndex < ulNumTokens ); ulIndex++ ) - { - /* All parameter keys are JSON strings. */ - if( pxTokens[ ulIndex ].type == JSMN_STRING ) - { - /* Search the document model to see if it matches the current key. */ - ulTokenLen = ( uint32_t ) pxTokens[ ulIndex ].end - ( uint32_t ) pxTokens[ ulIndex ].start; - eErr = prvSearchModelForTokenKey( pxDocModel, &pcJSON[ pxTokens[ ulIndex ].start ], ulTokenLen, &usModelParamIndex ); - - /* If we didn't find a match in the model, skip over it and its descendants. */ - if( eErr == eDocParseErr_ParamKeyNotInModel ) - { - int32_t iRoot = ( int32_t ) ulIndex; /* Create temp root from the unrecognized tokens index. Use signed int since the parent index is signed. */ - ulIndex++; /* Skip the active key since it's the one we don't recognize. */ - - /* Skip tokens whose parents are equal to or deeper than the unrecognized temporary root token level. */ - while( ( ulIndex < ulNumTokens ) && ( pxTokens[ ulIndex ].parent >= iRoot ) ) - { - ulIndex++; /* Skip over all descendants of the unknown parent. */ - } - - --ulIndex; /* Adjust for outer for-loop increment. */ - eErr = eDocParseErr_None; /* Unknown key structures are simply skipped so clear the error state to continue. */ - } - else if( eErr == eDocParseErr_None ) - { - /* We found the parameter key in the document model. */ - - /* Get the value field (i.e. the following token) for the parameter. */ - pxValTok = &pxTokens[ ulIndex + 1UL ]; - - /* Verify the field type is what we expect for this parameter. */ - if( pxValTok->type != pxModelParam[ usModelParamIndex ].eJasmineType ) - { - ulTokenLen = ( uint32_t ) ( pxValTok->end ) - ( uint32_t ) ( pxValTok->start ); - OTA_LOG_L1( "[%s] parameter type mismatch [ %s : %.*s ] type %u, expected %u\r\n", - OTA_METHOD_NAME, pxModelParam[ usModelParamIndex ].pcSrcKey, ulTokenLen, - &pcJSON[ pxValTok->start ], - pxValTok->type, pxModelParam[ usModelParamIndex ].eJasmineType ); - eErr = eDocParseErr_FieldTypeMismatch; - /* break; */ - } - else if( OTA_DONT_STORE_PARAM == pxModelParam[ usModelParamIndex ].ulDestOffset ) - { - /* Nothing to do with this parameter since we're not storing it. */ - continue; - } - else - { - /* Get destination offset to parameter storage location. */ - - /* If it's within the models context structure, add in the context instance base address. */ - if( pxModelParam[ usModelParamIndex ].ulDestOffset < pxDocModel->ulContextSize ) - { - xParamAddr.ulVal = pxDocModel->ulContextBase + pxModelParam[ usModelParamIndex ].ulDestOffset; - } - else - { - /* It's a raw pointer so keep it as is. */ - xParamAddr.ulVal = pxModelParam[ usModelParamIndex ].ulDestOffset; - } - - if( eModelParamType_StringCopy == pxModelParam[ usModelParamIndex ].xModelParamType ) - { - /* Malloc memory for a copy of the value string plus a zero terminator. */ - ulTokenLen = ( uint32_t ) ( pxValTok->end ) - ( uint32_t ) ( pxValTok->start ); - void * pvStringCopy = pvPortMalloc( ulTokenLen + 1U ); - - if( pvStringCopy != NULL ) - { - *xParamAddr.ppvPtr = pvStringCopy; - char * pcStringCopy = *xParamAddr.ppcPtr; - /* Copy parameter string into newly allocated memory. */ - memcpy( pcStringCopy, &pcJSON[ pxValTok->start ], ulTokenLen ); - /* Zero terminate the new string. */ - pcStringCopy[ ulTokenLen ] = '\0'; - OTA_LOG_L1( "[%s] Extracted parameter [ %s: %s ]\r\n", - OTA_METHOD_NAME, - pxModelParam[ usModelParamIndex ].pcSrcKey, - pcStringCopy ); - } - else - { /* Stop processing on error. */ - eErr = eDocParseErr_OutOfMemory; - /* break; */ - } - } - else if( eModelParamType_StringInDoc == pxModelParam[ usModelParamIndex ].xModelParamType ) - { - /* Copy pointer to source string instead of duplicating the string. */ - const char * pcStringInDoc = &pcJSON[ pxValTok->start ]; - *xParamAddr.ppccPtr = pcStringInDoc; - ulTokenLen = ( uint32_t ) ( pxValTok->end ) - ( uint32_t ) ( pxValTok->start ); - OTA_LOG_L1( "[%s] Extracted parameter [ %s: %.*s ]\r\n", - OTA_METHOD_NAME, - pxModelParam[ usModelParamIndex ].pcSrcKey, - ulTokenLen, pcStringInDoc ); - } - else if( eModelParamType_UInt32 == pxModelParam[ usModelParamIndex ].xModelParamType ) - { - char * pEnd; - const char * pStart = &pcJSON[ pxValTok->start ]; - *xParamAddr.pulPtr = strtoul( pStart, &pEnd, 0 ); - - if( pEnd == &pcJSON[ pxValTok->end ] ) - { - OTA_LOG_L1( "[%s] Extracted parameter [ %s: %u ]\r\n", - OTA_METHOD_NAME, - pxModelParam[ usModelParamIndex ].pcSrcKey, - *xParamAddr.pulPtr ); - } - else - { - eErr = eDocParseErr_InvalidNumChar; - } - } - else if( eModelParamType_SigBase64 == pxModelParam[ usModelParamIndex ].xModelParamType ) - { - /* Allocate space for and decode the base64 signature. */ - void * pvSignature = pvPortMalloc( sizeof( Sig256_t ) ); - - if( pvSignature != NULL ) - { - size_t xActualLen = 0; - *xParamAddr.ppvPtr = pvSignature; - Sig256_t * pxSig256 = *xParamAddr.ppxSig256Ptr; - ulTokenLen = ( uint32_t ) ( pxValTok->end ) - ( uint32_t ) ( pxValTok->start ); - - if( mbedtls_base64_decode( pxSig256->ucData, sizeof( pxSig256->ucData ), &xActualLen, - ( const uint8_t * ) &pcJSON[ pxValTok->start ], ulTokenLen ) != 0 ) - { /* Stop processing on error. */ - OTA_LOG_L1( "[%s] mbedtls_base64_decode failed.\r\n", OTA_METHOD_NAME ); - eErr = eDocParseErr_Base64Decode; - /* break; */ - } - else - { - pxSig256->usSize = ( uint16_t ) xActualLen; - OTA_LOG_L1( "[%s] Extracted parameter [ %s: %.32s... ]\r\n", - OTA_METHOD_NAME, - pxModelParam[ usModelParamIndex ].pcSrcKey, - &pcJSON[ pxValTok->start ] ); - } - } - else - { - /* We failed to allocate needed memory. Everything will be freed below upon failure. */ - eErr = eDocParseErr_OutOfMemory; - } - } - else if( eModelParamType_Ident == pxModelParam[ usModelParamIndex ].xModelParamType ) - { - OTA_LOG_L1( "[%s] Identified parameter [ %s ]\r\n", - OTA_METHOD_NAME, - pxModelParam[ usModelParamIndex ].pcSrcKey ); - *xParamAddr.pxBoolPtr = pdTRUE; - } - - if( eModelParamType_ArrayCopy == pxModelParam[ usModelParamIndex ].xModelParamType ) - { - /* Malloc memory for a copy of the value string plus a zero terminator. */ - ulTokenLen = ( uint32_t ) ( pxValTok->end ) - ( uint32_t ) ( pxValTok->start ); - void * pvStringCopy = pvPortMalloc( ulTokenLen + 1U ); - - if( pvStringCopy != NULL ) - { - *xParamAddr.ppvPtr = pvStringCopy; - char * pcStringCopy = *xParamAddr.ppcPtr; - /* Copy parameter string into newly allocated memory. */ - memcpy( pcStringCopy, &pcJSON[ pxValTok->start ], ulTokenLen ); - /* Zero terminate the new string. */ - pcStringCopy[ ulTokenLen ] = '\0'; - OTA_LOG_L1( "[%s] Extracted parameter [ %s: %s ]\r\n", - OTA_METHOD_NAME, - pxModelParam[ usModelParamIndex ].pcSrcKey, - pcStringCopy ); - } - else - { /* Stop processing on error. */ - eErr = eDocParseErr_OutOfMemory; - /* break; */ - } - } - else - { - /* Ignore invalid document model type. */ - } - } - } - else - { - /* Nothing special to do. The error will break us out of the loop. */ - } - } - else - { - /* Ignore tokens that are not strings and move on to the next. */ - } - } - - /* Free the token memory. */ - vPortFree( pxTokens ); /*lint !e850 ulIndex is intentionally modified within the loop to skip over unknown tags. */ - - if( eErr == eDocParseErr_None ) - { - uint32_t ulMissingParams = ( pxDocModel->ulParamsReceivedBitmap & pxDocModel->ulParamsRequiredBitmap ) - ^ pxDocModel->ulParamsRequiredBitmap; - - if( ulMissingParams != 0U ) - { - /* The job document did not have all required document model parameters. */ - for( ulScanIndex = 0UL; ulScanIndex < pxDocModel->usNumModelParams; ulScanIndex++ ) - { - if( ( ulMissingParams & ( 1UL << ulScanIndex ) ) != 0UL ) - { - OTA_LOG_L1( "[%s] parameter not present: %s\r\n", - OTA_METHOD_NAME, - pxModelParam[ ulScanIndex ].pcSrcKey ); - } - } - - eErr = eDocParseErr_MalformedDoc; - } - } - else - { - OTA_LOG_L1( "[%s] Error (%d) parsing JSON document.\r\n", OTA_METHOD_NAME, ( int32_t ) eErr ); - } - } - else - { - OTA_LOG_L1( "[%s] jsmn_parse didn't match token count when parsing.\r\n", OTA_METHOD_NAME ); - eErr = eDocParseErr_JasmineCountMismatch; - } - } - else - { - OTA_LOG_L1( "[%s] No memory for JSON tokens.\r\n", OTA_METHOD_NAME ); - eErr = eDocParseErr_OutOfMemory; - } - } - else - { - OTA_LOG_L1( "[%s] Document has too many keys.\r\n", OTA_METHOD_NAME ); - eErr = eDocParseErr_TooManyTokens; - } - } - else + if( ulNumTokens == 0 ) { OTA_LOG_L1( "[%s] Invalid JSON document. No tokens parsed. \r\n", OTA_METHOD_NAME ); eErr = eDocParseErr_NoTokens; } } + /* Check if the JSON document isn't too big for our token array. */ + if( eErr == eDocParseErr_None ) + { + if( ulNumTokens > OTA_MAX_JSON_TOKENS ) + { + OTA_LOG_L1( "[%s] Document has too many keys.\r\n", OTA_METHOD_NAME ); + eErr = eDocParseErr_TooManyTokens; + } + } + + /* Allocate space on heap for temporary token array. */ + if( eErr == eDocParseErr_None ) + { + /* Allocate space for the document JSON tokens. */ + pxTokens = ( jsmntok_t * ) pvPortMalloc( ulNumTokens * sizeof( jsmntok_t ) ); + + if( pxTokens == NULL ) + { + OTA_LOG_L1( "[%s] No memory for JSON tokens.\r\n", OTA_METHOD_NAME ); + eErr = eDocParseErr_OutOfMemory; + } + } + + /* Init Jasmine and check number of tokens.*/ + if( eErr == eDocParseErr_None ) + { + /* Reset Jasmine again and tokenize the document for real. */ + jsmn_init( &xParser ); + ulIndex = ( uint32_t ) jsmn_parse( &xParser, pcJSON, ulMsgLen, pxTokens, ulNumTokens ); + + if( ulIndex != ulNumTokens ) + { + OTA_LOG_L1( "[%s] jsmn_parse didn't match token count when parsing.\r\n", OTA_METHOD_NAME ); + eErr = eDocParseErr_JasmineCountMismatch; + } + } + + /* Process JSON tokens. */ + if( eErr == eDocParseErr_None ) + { + /* Examine each JSON token, searching for job parameters based on our document model. */ + for( ulIndex = 0U; ( eErr == eDocParseErr_None ) && ( ulIndex < ulNumTokens ); ulIndex++ ) + { + /* All parameter keys are JSON strings. */ + if( pxTokens[ ulIndex ].type == JSMN_STRING ) + { + /* Search the document model to see if it matches the current key. */ + ulTokenLen = ( uint32_t ) pxTokens[ ulIndex ].end - ( uint32_t ) pxTokens[ ulIndex ].start; + eErr = prvSearchModelForTokenKey( pxDocModel, &pcJSON[ pxTokens[ ulIndex ].start ], ulTokenLen, &usModelParamIndex ); + + /* If we didn't find a match in the model, skip over it and its descendants. */ + if( eErr == eDocParseErr_ParamKeyNotInModel ) + { + int32_t iRoot = ( int32_t ) ulIndex; /* Create temp root from the unrecognized tokens index. Use signed int since the parent index is signed. */ + ulIndex++; /* Skip the active key since it's the one we don't recognize. */ + + /* Skip tokens whose parents are equal to or deeper than the unrecognized temporary root token level. */ + while( ( ulIndex < ulNumTokens ) && ( pxTokens[ ulIndex ].parent >= iRoot ) ) + { + ulIndex++; /* Skip over all descendants of the unknown parent. */ + } + + --ulIndex; /* Adjust for outer for-loop increment. */ + eErr = eDocParseErr_None; /* Unknown key structures are simply skipped so clear the error state to continue. */ + } + else if( eErr == eDocParseErr_None ) + { + /* We found the parameter key in the document model. */ + + /* Get the value field (i.e. the following token) for the parameter. */ + pxValTok = &pxTokens[ ulIndex + 1UL ]; + + /* Verify the field type is what we expect for this parameter. */ + if( pxValTok->type != pxModelParam[ usModelParamIndex ].eJasmineType ) + { + ulTokenLen = ( uint32_t ) ( pxValTok->end ) - ( uint32_t ) ( pxValTok->start ); + OTA_LOG_L1( "[%s] parameter type mismatch [ %s : %.*s ] type %u, expected %u\r\n", + OTA_METHOD_NAME, pxModelParam[ usModelParamIndex ].pcSrcKey, ulTokenLen, + &pcJSON[ pxValTok->start ], + pxValTok->type, pxModelParam[ usModelParamIndex ].eJasmineType ); + eErr = eDocParseErr_FieldTypeMismatch; + } + else if( OTA_DONT_STORE_PARAM == pxModelParam[ usModelParamIndex ].ulDestOffset ) + { + /* Nothing to do with this parameter since we're not storing it. */ + continue; + } + else + { + /* Get destination offset to parameter storage location. */ + + /* If it's within the models context structure, add in the context instance base address. */ + if( pxModelParam[ usModelParamIndex ].ulDestOffset < pxDocModel->ulContextSize ) + { + xParamAddr.ulVal = pxDocModel->ulContextBase + pxModelParam[ usModelParamIndex ].ulDestOffset; + } + else + { + /* It's a raw pointer so keep it as is. */ + xParamAddr.ulVal = pxModelParam[ usModelParamIndex ].ulDestOffset; + } + + if( eModelParamType_StringCopy == pxModelParam[ usModelParamIndex ].xModelParamType ) + { + /* Malloc memory for a copy of the value string plus a zero terminator. */ + ulTokenLen = ( uint32_t ) ( pxValTok->end ) - ( uint32_t ) ( pxValTok->start ); + void * pvStringCopy = pvPortMalloc( ulTokenLen + 1U ); + + if( pvStringCopy != NULL ) + { + *xParamAddr.ppvPtr = pvStringCopy; + char * pcStringCopy = *xParamAddr.ppcPtr; + /* Copy parameter string into newly allocated memory. */ + ( void ) memcpy( pcStringCopy, &pcJSON[ pxValTok->start ], ulTokenLen ); + /* Zero terminate the new string. */ + pcStringCopy[ ulTokenLen ] = '\0'; + OTA_LOG_L1( "[%s] Extracted parameter [ %s: %s ]\r\n", + OTA_METHOD_NAME, + pxModelParam[ usModelParamIndex ].pcSrcKey, + pcStringCopy ); + } + else + { /* Stop processing on error. */ + eErr = eDocParseErr_OutOfMemory; + } + } + else if( eModelParamType_StringInDoc == pxModelParam[ usModelParamIndex ].xModelParamType ) + { + /* Copy pointer to source string instead of duplicating the string. */ + const char * pcStringInDoc = &pcJSON[ pxValTok->start ]; + *xParamAddr.ppccPtr = pcStringInDoc; + ulTokenLen = ( uint32_t ) ( pxValTok->end ) - ( uint32_t ) ( pxValTok->start ); + OTA_LOG_L1( "[%s] Extracted parameter [ %s: %.*s ]\r\n", + OTA_METHOD_NAME, + pxModelParam[ usModelParamIndex ].pcSrcKey, + ulTokenLen, pcStringInDoc ); + } + else if( eModelParamType_UInt32 == pxModelParam[ usModelParamIndex ].xModelParamType ) + { + char * pEnd; + const char * pStart = &pcJSON[ pxValTok->start ]; + *xParamAddr.pulPtr = strtoul( pStart, &pEnd, 0 ); + + if( pEnd == &pcJSON[ pxValTok->end ] ) + { + OTA_LOG_L1( "[%s] Extracted parameter [ %s: %u ]\r\n", + OTA_METHOD_NAME, + pxModelParam[ usModelParamIndex ].pcSrcKey, + *xParamAddr.pulPtr ); + } + else + { + eErr = eDocParseErr_InvalidNumChar; + } + } + else if( eModelParamType_SigBase64 == pxModelParam[ usModelParamIndex ].xModelParamType ) + { + /* Allocate space for and decode the base64 signature. */ + void * pvSignature = pvPortMalloc( sizeof( Sig256_t ) ); + + if( pvSignature != NULL ) + { + size_t xActualLen = 0; + *xParamAddr.ppvPtr = pvSignature; + Sig256_t * pxSig256 = *xParamAddr.ppxSig256Ptr; + ulTokenLen = ( uint32_t ) ( pxValTok->end ) - ( uint32_t ) ( pxValTok->start ); + + if( mbedtls_base64_decode( pxSig256->ucData, sizeof( pxSig256->ucData ), &xActualLen, + ( const uint8_t * ) &pcJSON[ pxValTok->start ], ulTokenLen ) != 0 ) + { /* Stop processing on error. */ + OTA_LOG_L1( "[%s] mbedtls_base64_decode failed.\r\n", OTA_METHOD_NAME ); + eErr = eDocParseErr_Base64Decode; + } + else + { + pxSig256->usSize = ( uint16_t ) xActualLen; + OTA_LOG_L1( "[%s] Extracted parameter [ %s: %.32s... ]\r\n", + OTA_METHOD_NAME, + pxModelParam[ usModelParamIndex ].pcSrcKey, + &pcJSON[ pxValTok->start ] ); + } + } + else + { + /* We failed to allocate needed memory. Everything will be freed below upon failure. */ + eErr = eDocParseErr_OutOfMemory; + } + } + else if( eModelParamType_Ident == pxModelParam[ usModelParamIndex ].xModelParamType ) + { + OTA_LOG_L1( "[%s] Identified parameter [ %s ]\r\n", + OTA_METHOD_NAME, + pxModelParam[ usModelParamIndex ].pcSrcKey ); + *xParamAddr.pbBoolPtr = true; + } + + if( eModelParamType_ArrayCopy == pxModelParam[ usModelParamIndex ].xModelParamType ) + { + /* Malloc memory for a copy of the value string plus a zero terminator. */ + ulTokenLen = ( uint32_t ) ( pxValTok->end ) - ( uint32_t ) ( pxValTok->start ); + void * pvStringCopy = pvPortMalloc( ulTokenLen + 1U ); + + if( pvStringCopy != NULL ) + { + *xParamAddr.ppvPtr = pvStringCopy; + char * pcStringCopy = *xParamAddr.ppcPtr; + /* Copy parameter string into newly allocated memory. */ + ( void ) memcpy( pcStringCopy, &pcJSON[ pxValTok->start ], ulTokenLen ); + /* Zero terminate the new string. */ + pcStringCopy[ ulTokenLen ] = '\0'; + OTA_LOG_L1( "[%s] Extracted parameter [ %s: %s ]\r\n", + OTA_METHOD_NAME, + pxModelParam[ usModelParamIndex ].pcSrcKey, + pcStringCopy ); + } + else + { /* Stop processing on error. */ + eErr = eDocParseErr_OutOfMemory; + } + } + else + { + /* Ignore invalid document model type. */ + } + } + } + else + { + /* Nothing special to do. The error will break us out of the loop. */ + } + } + else + { + /* Ignore tokens that are not strings and move on to the next. */ + } + } + } + + if( pxTokens != NULL ) + { + /* Free the token memory. */ + vPortFree( pxTokens ); /*lint !e850 ulIndex is intentionally modified within the loop to skip over unknown tags. */ + } + + if( eErr == eDocParseErr_None ) + { + uint32_t ulMissingParams = ( pxDocModel->ulParamsReceivedBitmap & pxDocModel->ulParamsRequiredBitmap ) + ^ pxDocModel->ulParamsRequiredBitmap; + + if( ulMissingParams != 0U ) + { + /* The job document did not have all required document model parameters. */ + for( ulScanIndex = 0UL; ulScanIndex < pxDocModel->usNumModelParams; ulScanIndex++ ) + { + if( ( ulMissingParams & ( 1UL << ulScanIndex ) ) != 0UL ) + { + OTA_LOG_L1( "[%s] parameter not present: %s\r\n", + OTA_METHOD_NAME, + pxModelParam[ ulScanIndex ].pcSrcKey ); + } + } + + eErr = eDocParseErr_MalformedDoc; + } + } + else + { + OTA_LOG_L1( "[%s] Error (%d) parsing JSON document.\r\n", OTA_METHOD_NAME, ( int32_t ) eErr ); + } + configASSERT( eErr != eDocParseErr_Unknown ); return eErr; } @@ -1740,7 +1924,7 @@ static DocParseErr_t prvInitDocModel( JSON_DocModel_t * pxDocModel, /* Scan the model and detect all required parameters (i.e. not optional). */ for( ulScanIndex = 0; ulScanIndex < pxDocModel->usNumModelParams; ulScanIndex++ ) { - if( pxDocModel->pxBodyDef[ ulScanIndex ].bRequired == ( bool_t ) pdTRUE ) + if( pxDocModel->pxBodyDef[ ulScanIndex ].bRequired ) { /* Add parameter to the required bitmap. */ pxDocModel->ulParamsRequiredBitmap |= ( 1UL << ulScanIndex ); @@ -1753,13 +1937,60 @@ static DocParseErr_t prvInitDocModel( JSON_DocModel_t * pxDocModel, return eErr; } +/* + * Validate the version of the update received. + */ +static OTA_Err_t prvValidateUpdateVersion( OTA_FileContext_t * C ) +{ + DEFINE_OTA_METHOD_NAME( "prvValidateUpdateVersion" ); + + OTA_Err_t xErr = kOTA_Err_Uninitialized; + + /* Only check for versions if the target is self */ + if( xOTA_Agent.ulServerFileID == 0 ) + { + /* Check if update version received is newer than current version.*/ + if( C->ulUpdaterVersion < xAppFirmwareVersion.u.ulVersion32 ) + { + OTA_LOG_L1( "[%s] The update version is newer than the version on device.\r\n", OTA_METHOD_NAME ); + + xErr = kOTA_Err_None; + } + /* Check if update version received is older than current version.*/ + else if( C->ulUpdaterVersion > xAppFirmwareVersion.u.ulVersion32 ) + { + OTA_LOG_L1( "[%s] The update version is older than the version on device.\r\n", OTA_METHOD_NAME ); + + xErr = kOTA_Err_DowngradeNotAllowed; + } + /* Check if version reported is the same as the running version. */ + else if( C->ulUpdaterVersion == xAppFirmwareVersion.u.ulVersion32 ) + { + /* The version is the same so either we're not actually the new firmware or + * someone messed up and sent firmware with the same version. In either case, + * this is a failure of the OTA update so reject the job. + */ + OTA_LOG_L1( "[%s] We rebooted and the version is still the same.\r\n", OTA_METHOD_NAME ); + + xErr = kOTA_Err_SameFirmwareVersion; + } + } + else + { + /* For any other ulServerFileID.*/ + xErr = kOTA_Err_None; + } + + return xErr; +} + /* Parse the OTA job document and validate. Return the populated * OTA context if valid otherwise return NULL. */ static OTA_FileContext_t * prvParseJobDoc( const char * pcJSON, uint32_t ulMsgLen, - bool_t * pbUpdateJob ) + bool * pbUpdateJob ) { DEFINE_OTA_METHOD_NAME( "prvParseJobDoc" ); @@ -1768,34 +1999,33 @@ static OTA_FileContext_t * prvParseJobDoc( const char * pcJSON, /* Namely union initialization and pointers converted to values. */ static const JSON_DocParam_t xOTA_JobDocModelParamStructure[ OTA_NUM_JOB_PARAMS ] = { - { pcOTA_JSON_ClientTokenKey, OTA_JOB_PARAM_OPTIONAL, { ( uint32_t ) &xOTA_Agent.pcClientTokenFromJob }, eModelParamType_StringInDoc, JSMN_STRING }, /*lint !e9078 !e923 Get address of token as value. */ - { pcOTA_JSON_ExecutionKey, OTA_JOB_PARAM_REQUIRED, { OTA_DONT_STORE_PARAM }, eModelParamType_Object, JSMN_OBJECT }, - { pcOTA_JSON_JobIDKey, OTA_JOB_PARAM_REQUIRED, { OFFSET_OF( OTA_FileContext_t, pucJobName ) }, eModelParamType_StringCopy, JSMN_STRING }, - { pcOTA_JSON_StatusDetailsKey, OTA_JOB_PARAM_OPTIONAL, { OTA_DONT_STORE_PARAM }, eModelParamType_Object, JSMN_OBJECT }, - { pcOTA_JSON_SelfTestKey, OTA_JOB_PARAM_OPTIONAL, { OFFSET_OF( OTA_FileContext_t, xIsInSelfTest ) }, eModelParamType_Ident, JSMN_STRING }, - { pcOTA_JSON_UpdatedByKey, OTA_JOB_PARAM_OPTIONAL, { OFFSET_OF( OTA_FileContext_t, ulUpdaterVersion )}, eModelParamType_UInt32, JSMN_STRING }, - { pcOTA_JSON_JobDocKey, OTA_JOB_PARAM_REQUIRED, { OTA_DONT_STORE_PARAM }, eModelParamType_Object, JSMN_OBJECT }, - { pcOTA_JSON_OTAUnitKey, OTA_JOB_PARAM_REQUIRED, { OTA_DONT_STORE_PARAM }, eModelParamType_Object, JSMN_OBJECT }, - { pcOTA_JSON_StreamNameKey, OTA_JOB_PARAM_OPTIONAL, { OFFSET_OF( OTA_FileContext_t, pucStreamName ) }, eModelParamType_StringCopy, JSMN_STRING }, - { pcOTA_JSON_ProtocolsKey, OTA_JOB_PARAM_REQUIRED, { OFFSET_OF( OTA_FileContext_t, pucProtocols ) }, eModelParamType_ArrayCopy, JSMN_ARRAY }, - { pcOTA_JSON_FileGroupKey, OTA_JOB_PARAM_REQUIRED, { OTA_DONT_STORE_PARAM }, eModelParamType_Array, JSMN_ARRAY }, - { pcOTA_JSON_FilePathKey, OTA_JOB_PARAM_REQUIRED, { OFFSET_OF( OTA_FileContext_t, pucFilePath ) }, eModelParamType_StringCopy, JSMN_STRING }, - { pcOTA_JSON_FileSizeKey, OTA_JOB_PARAM_REQUIRED, { OFFSET_OF( OTA_FileContext_t, ulFileSize ) }, eModelParamType_UInt32, JSMN_PRIMITIVE }, - { pcOTA_JSON_FileIDKey, OTA_JOB_PARAM_REQUIRED, { OFFSET_OF( OTA_FileContext_t, ulServerFileID )}, eModelParamType_UInt32, JSMN_PRIMITIVE }, - { pcOTA_JSON_FileCertNameKey, OTA_JOB_PARAM_REQUIRED, { OFFSET_OF( OTA_FileContext_t, pucCertFilepath )}, eModelParamType_StringCopy, JSMN_STRING }, - { pcOTA_JSON_UpdateDataUrlKey, OTA_JOB_PARAM_OPTIONAL, { OFFSET_OF( OTA_FileContext_t, pucUpdateUrlPath )}, eModelParamType_StringCopy, JSMN_STRING }, - { pcOTA_JSON_AuthSchemeKey, OTA_JOB_PARAM_OPTIONAL, { OFFSET_OF( OTA_FileContext_t, pucAuthScheme ) }, eModelParamType_StringCopy, JSMN_STRING }, - { cOTA_JSON_FileSignatureKey, OTA_JOB_PARAM_REQUIRED, { OFFSET_OF( OTA_FileContext_t, pxSignature ) }, eModelParamType_SigBase64, JSMN_STRING }, - { pcOTA_JSON_FileAttributeKey, OTA_JOB_PARAM_OPTIONAL, { OFFSET_OF( OTA_FileContext_t, ulFileAttributes )}, eModelParamType_UInt32, JSMN_PRIMITIVE }, + { OTA_JSON_CLIENT_TOKEN_KEY, OTA_JOB_PARAM_OPTIONAL, { ( uint32_t ) &xOTA_Agent.pcClientTokenFromJob }, eModelParamType_StringInDoc, JSMN_STRING }, /*lint !e9078 !e923 Get address of token as value. */ + { OTA_JSON_EXECUTION_KEY, OTA_JOB_PARAM_REQUIRED, { OTA_DONT_STORE_PARAM }, eModelParamType_Object, JSMN_OBJECT }, + { OTA_JSON_JOB_ID_KEY, OTA_JOB_PARAM_REQUIRED, { offsetof( OTA_FileContext_t, pucJobName ) }, eModelParamType_StringCopy, JSMN_STRING }, + { OTA_JSON_STATUS_DETAILS_KEY, OTA_JOB_PARAM_OPTIONAL, { OTA_DONT_STORE_PARAM }, eModelParamType_Object, JSMN_OBJECT }, + { OTA_JSON_SELF_TEST_KEY, OTA_JOB_PARAM_OPTIONAL, { offsetof( OTA_FileContext_t, bIsInSelfTest ) }, eModelParamType_Ident, JSMN_STRING }, + { OTA_JSON_UPDATED_BY_KEY, OTA_JOB_PARAM_OPTIONAL, { offsetof( OTA_FileContext_t, ulUpdaterVersion )}, eModelParamType_UInt32, JSMN_STRING }, + { OTA_JSON_JOB_DOC_KEY, OTA_JOB_PARAM_REQUIRED, { OTA_DONT_STORE_PARAM }, eModelParamType_Object, JSMN_OBJECT }, + { OTA_JSON_OTA_UNIT_KEY, OTA_JOB_PARAM_REQUIRED, { OTA_DONT_STORE_PARAM }, eModelParamType_Object, JSMN_OBJECT }, + { OTA_JSON_STREAM_NAME_KEY, OTA_JOB_PARAM_OPTIONAL, { offsetof( OTA_FileContext_t, pucStreamName ) }, eModelParamType_StringCopy, JSMN_STRING }, + { OTA_JSON_PROTOCOLS_KEY, OTA_JOB_PARAM_REQUIRED, { offsetof( OTA_FileContext_t, pucProtocols ) }, eModelParamType_ArrayCopy, JSMN_ARRAY }, + { OTA_JSON_FILE_GROUP_KEY, OTA_JOB_PARAM_REQUIRED, { OTA_DONT_STORE_PARAM }, eModelParamType_Array, JSMN_ARRAY }, + { OTA_JSON_FILE_PATH_KEY, OTA_JOB_PARAM_REQUIRED, { offsetof( OTA_FileContext_t, pucFilePath ) }, eModelParamType_StringCopy, JSMN_STRING }, + { OTA_JSON_FILE_SIZE_KEY, OTA_JOB_PARAM_REQUIRED, { offsetof( OTA_FileContext_t, ulFileSize ) }, eModelParamType_UInt32, JSMN_PRIMITIVE }, + { OTA_JSON_FILE_ID_KEY, OTA_JOB_PARAM_REQUIRED, { offsetof( OTA_FileContext_t, ulServerFileID ) }, eModelParamType_UInt32, JSMN_PRIMITIVE }, + { OTA_JSON_FILE_CERT_NAME_KEY, OTA_JOB_PARAM_REQUIRED, { offsetof( OTA_FileContext_t, pucCertFilepath )}, eModelParamType_StringCopy, JSMN_STRING }, + { OTA_JSON_UPDATE_DATA_URL_KEY, OTA_JOB_PARAM_OPTIONAL, { offsetof( OTA_FileContext_t, pucUpdateUrlPath )}, eModelParamType_StringCopy, JSMN_STRING }, + { OTA_JSON_AUTH_SCHEME_KEY, OTA_JOB_PARAM_OPTIONAL, { offsetof( OTA_FileContext_t, pucAuthScheme ) }, eModelParamType_StringCopy, JSMN_STRING }, + { cOTA_JSON_FileSignatureKey, OTA_JOB_PARAM_REQUIRED, { offsetof( OTA_FileContext_t, pxSignature ) }, eModelParamType_SigBase64, JSMN_STRING }, + { OTA_JSON_FILE_ATTRIBUTE_KEY, OTA_JOB_PARAM_OPTIONAL, { offsetof( OTA_FileContext_t, ulFileAttributes )}, eModelParamType_UInt32, JSMN_PRIMITIVE }, }; + OTA_Err_t xOTAErr = kOTA_Err_None; OTA_JobParseErr_t eErr = eOTA_JobParseErr_Unknown; OTA_FileContext_t * pxFinalFile = NULL; OTA_FileContext_t xFileContext = { 0 }; OTA_FileContext_t * C = &xFileContext; - - OTA_LOG_L1( "[%s] Size of OTA_FileContext_t [%d]\r\n", OTA_METHOD_NAME, sizeof( xFileContext ) ); - + OTA_Err_t xErrVersionCheck = kOTA_Err_Uninitialized; JSON_DocModel_t xOTA_JobDocModel; @@ -1876,60 +2106,32 @@ static OTA_FileContext_t * prvParseJobDoc( const char * pcJSON, * test or an incorrect image was sent by the OTA * operator. */ - if( C->xIsInSelfTest == ( bool_t ) pdTRUE ) + if( C->bIsInSelfTest ) { OTA_LOG_L1( "[%s] In self test mode.\r\n", OTA_METHOD_NAME ); - /* Only check for versions if the target is self */ - if( xOTA_Agent.ulServerFileID == 0 ) - { - if( C->ulUpdaterVersion < xAppFirmwareVersion.u.ulVersion32 ) - { - /* The running firmware version is newer than the firmware that performed - * the update so this means we're ready to start the self test phase. - * - * Set image state accordingly and update job status with self test identifier. - */ - ( void ) prvSetImageStateWithReason( eOTA_ImageState_Testing, ( uint32_t ) NULL ); - } - else - { - if( C->ulUpdaterVersion > xAppFirmwareVersion.u.ulVersion32 ) - { - /* The running firmware is older than the firmware that performed the update so reject the job. */ - OTA_LOG_L1( "[%s] Rejecting image because version is older than previous.\r\n", OTA_METHOD_NAME ); - ( void ) prvSetImageStateWithReason( eOTA_ImageState_Rejected, kOTA_Err_DowngradeNotAllowed ); - } - else /* Version reported is the same as the running version. */ - { - if( ( xOTA_Agent.pcClientTokenFromJob == NULL ) || - ( strtoul( ( const char * ) xOTA_Agent.pcClientTokenFromJob, NULL, 0 ) == 0U ) ) /*lint !e9007 We don't provide a modifiable variable to strtoul. */ - { - /* The version is the same so either we're not actually the new firmware or - * someone messed up and sent firmware with the same version. In either case, - * this is a failure of the OTA update so reject the job. */ - OTA_LOG_L1( "[%s] Failing job. We rebooted and the version is still the same.\r\n", OTA_METHOD_NAME ); - ( void ) prvSetImageStateWithReason( eOTA_ImageState_Rejected, kOTA_Err_SameFirmwareVersion ); - } - else - { - OTA_LOG_L1( "[%s] Ignoring job. Device must be rebooted first.\r\n", OTA_METHOD_NAME ); - } - } + /* Validate version of the update received.*/ + xErrVersionCheck = prvValidateUpdateVersion( C ); - /* All reject cases must reset the device. */ - ( void ) prvResetDevice(); /* Ignore return code since there's nothing we can do if we can't force reset. */ - } - } - else + if( otaconfigAllowDowngrade || ( xErrVersionCheck == kOTA_Err_None ) ) { /* The running firmware version is newer than the firmware that performed - * the update so this means we're ready to start the self test phase. + * the update or downgrade is allowed so this means we're ready to start + * the self test phase. * * Set image state accordingly and update job status with self test identifier. */ OTA_LOG_L1( "[%s] Setting image state to Testing for file ID %d\r\n", OTA_METHOD_NAME, xOTA_Agent.ulServerFileID ); - ( void ) prvSetImageStateWithReason( eOTA_ImageState_Testing, ( uint32_t ) NULL ); + + ( void ) prvSetImageStateWithReason( eOTA_ImageState_Testing, xErrVersionCheck ); + } + else + { + OTA_LOG_L1( "[%s] Downgrade or same version not allowed, rejecting the update & rebooting.\r\n", OTA_METHOD_NAME ); + ( void ) prvSetImageStateWithReason( eOTA_ImageState_Rejected, xErrVersionCheck ); + + /* All reject cases must reset the device. */ + ( void ) prvResetDevice(); /* Ignore return code since there's nothing we can do if we can't force reset. */ } } else @@ -1966,7 +2168,16 @@ static OTA_FileContext_t * prvParseJobDoc( const char * pcJSON, { xOTA_Agent.pcOTA_Singleton_ActiveJobName = C->pucJobName; C->pucJobName = NULL; - xOTA_ControlInterface.prvUpdateJobStatus( &xOTA_Agent, eJobStatus_Succeeded, ( int32_t ) eJobReason_Accepted, 0 ); + xOTAErr = xOTA_ControlInterface.prvUpdateJobStatus( &xOTA_Agent, + eJobStatus_Succeeded, + eJobReason_Accepted, + 0 ); + + if( xOTAErr != kOTA_Err_None ) + { + OTA_LOG_L2( "[%s] Failed to update job status %d\r\n", OTA_METHOD_NAME, xOTAErr ); + } + /* We don't need the job name memory anymore since we're done with this job. */ vPortFree( xOTA_Agent.pcOTA_Singleton_ActiveJobName ); xOTA_Agent.pcOTA_Singleton_ActiveJobName = NULL; @@ -1992,7 +2203,16 @@ static OTA_FileContext_t * prvParseJobDoc( const char * pcJSON, /* Assume control of the job name from the context. */ xOTA_Agent.pcOTA_Singleton_ActiveJobName = C->pucJobName; C->pucJobName = NULL; - xOTA_ControlInterface.prvUpdateJobStatus( &xOTA_Agent, eJobStatus_FailedWithVal, ( int32_t ) kOTA_Err_JobParserError, ( int32_t ) eErr ); + xOTAErr = xOTA_ControlInterface.prvUpdateJobStatus( &xOTA_Agent, + eJobStatus_FailedWithVal, + ( int32_t ) kOTA_Err_JobParserError, + ( int32_t ) eErr ); + + if( xOTAErr != kOTA_Err_None ) + { + OTA_LOG_L2( "[%s] Failed to update job status %d\r\n", OTA_METHOD_NAME, xOTAErr ); + } + /* We don't need the job name memory anymore since we're done with this job. */ vPortFree( xOTA_Agent.pcOTA_Singleton_ActiveJobName ); xOTA_Agent.pcOTA_Singleton_ActiveJobName = NULL; @@ -2003,10 +2223,14 @@ static OTA_FileContext_t * prvParseJobDoc( const char * pcJSON, } } - /* If we failed, free the reserved file context (C) to make it available again. */ + /* If we failed, close the open files. */ if( pxFinalFile == NULL ) { - ( void ) prvOTA_Close( C ); + /* Free the current reserved file context. */ + prvOTA_FreeContext( C ); + + /* Close any open files. */ + ( void ) prvOTA_Close( &xOTA_Agent.pxOTA_Files[ xOTA_Agent.ulFileIndex ] ); } /* Return pointer to populated file context or NULL if it failed. */ @@ -2023,86 +2247,109 @@ static OTA_FileContext_t * prvParseJobDoc( const char * pcJSON, static OTA_FileContext_t * prvGetFileContextFromJob( const char * pcRawMsg, uint32_t ulMsgLen ) { + DEFINE_OTA_METHOD_NAME( "prvGetFileContextFromJob" ); + uint32_t ulIndex; uint32_t ulNumBlocks; /* How many data pages are in the expected update image. */ uint32_t ulBitmapLen; /* Length of the file block bitmap in bytes. */ OTA_FileContext_t * pstUpdateFile; /* Pointer to an OTA update context. */ OTA_Err_t xErr = kOTA_Err_Uninitialized; - bool_t bUpdateJob = false; - - DEFINE_OTA_METHOD_NAME( "prvGetFileContextFromJob" ); + bool bUpdateJob = false; /* Populate an OTA file context from the OTA job document. */ pstUpdateFile = prvParseJobDoc( pcRawMsg, ulMsgLen, &bUpdateJob ); - if( !bUpdateJob ) - { - if( ( pstUpdateFile != NULL ) && ( prvInSelftest() == false ) ) - { - if( pstUpdateFile->pucRxBlockBitmap != NULL ) - { - vPortFree( pstUpdateFile->pucRxBlockBitmap ); /* Free any previously allocated bitmap. */ - pstUpdateFile->pucRxBlockBitmap = NULL; - } - - /* Calculate how many bytes we need in our bitmap for tracking received blocks. - * The below calculation requires power of 2 page sizes. */ - - ulNumBlocks = ( pstUpdateFile->ulFileSize + ( OTA_FILE_BLOCK_SIZE - 1U ) ) >> otaconfigLOG2_FILE_BLOCK_SIZE; - ulBitmapLen = ( ulNumBlocks + ( BITS_PER_BYTE - 1U ) ) >> LOG2_BITS_PER_BYTE; - pstUpdateFile->pucRxBlockBitmap = ( uint8_t * ) pvPortMalloc( ulBitmapLen ); /*lint !e9079 FreeRTOS malloc port returns void*. */ - - if( pstUpdateFile->pucRxBlockBitmap != NULL ) - { - /* Set all bits in the bitmap to the erased state (we use 1 for erased just like flash memory). */ - memset( pstUpdateFile->pucRxBlockBitmap, ( int ) OTA_ERASED_BLOCKS_VAL, ulBitmapLen ); - - /* Mark as used any pages in the bitmap that are out of range, based on the file size. - * This keeps us from requesting those pages during retry processing or if using a windowed - * block request. It also avoids erroneously accepting an out of range data block should it - * get past any safety checks. - * Files aren't always a multiple of 8 pages (8 bits/pages per byte) so some bits of the - * last byte may be out of range and those are the bits we want to clear. */ - - uint8_t ulBit = 1U << ( BITS_PER_BYTE - 1U ); - uint32_t ulNumOutOfRange = ( ulBitmapLen * BITS_PER_BYTE ) - ulNumBlocks; - - for( ulIndex = 0U; ulIndex < ulNumOutOfRange; ulIndex++ ) - { - pstUpdateFile->pucRxBlockBitmap[ ulBitmapLen - 1U ] &= ~ulBit; - ulBit >>= 1U; - } - - pstUpdateFile->ulBlocksRemaining = ulNumBlocks; /* Initialize our blocks remaining counter. */ - - /* Create/Open the OTA file on the file system. */ - xErr = xOTA_Agent.xPALCallbacks.xCreateFileForRx( pstUpdateFile ); - - if( xErr != kOTA_Err_None ) - { - ( void ) prvSetImageStateWithReason( eOTA_ImageState_Aborted, xErr ); - ( void ) prvOTA_Close( pstUpdateFile ); /* Ignore false result since we're setting the pointer to null on the next line. */ - pstUpdateFile = NULL; - } - } - else - { - /* Can't receive the image without enough memory. */ - ( void ) prvOTA_Close( pstUpdateFile ); - pstUpdateFile = NULL; - } - } - } - else + if( bUpdateJob ) { OTA_LOG_L1( "[%s] We receive a job update.\r\n", OTA_METHOD_NAME ); } + if( ( bUpdateJob == false ) && ( pstUpdateFile != NULL ) && ( prvInSelftest() == false ) ) + { + if( pstUpdateFile->pucRxBlockBitmap != NULL ) + { + vPortFree( pstUpdateFile->pucRxBlockBitmap ); /* Free any previously allocated bitmap. */ + pstUpdateFile->pucRxBlockBitmap = NULL; + } + + /* Calculate how many bytes we need in our bitmap for tracking received blocks. + * The below calculation requires power of 2 page sizes. */ + + ulNumBlocks = ( pstUpdateFile->ulFileSize + ( OTA_FILE_BLOCK_SIZE - 1U ) ) >> otaconfigLOG2_FILE_BLOCK_SIZE; + ulBitmapLen = ( ulNumBlocks + ( BITS_PER_BYTE - 1U ) ) >> LOG2_BITS_PER_BYTE; + pstUpdateFile->pucRxBlockBitmap = ( uint8_t * ) pvPortMalloc( ulBitmapLen ); /*lint !e9079 FreeRTOS malloc port returns void*. */ + + if( pstUpdateFile->pucRxBlockBitmap != NULL ) + { + /* Set all bits in the bitmap to the erased state (we use 1 for erased just like flash memory). */ + ( void ) memset( pstUpdateFile->pucRxBlockBitmap, ( int32_t ) OTA_ERASED_BLOCKS_VAL, ulBitmapLen ); + + /* Mark as used any pages in the bitmap that are out of range, based on the file size. + * This keeps us from requesting those pages during retry processing or if using a windowed + * block request. It also avoids erroneously accepting an out of range data block should it + * get past any safety checks. + * Files aren't always a multiple of 8 pages (8 bits/pages per byte) so some bits of the + * last byte may be out of range and those are the bits we want to clear. */ + + uint8_t ulBit = 1U << ( BITS_PER_BYTE - 1U ); + uint32_t ulNumOutOfRange = ( ulBitmapLen * BITS_PER_BYTE ) - ulNumBlocks; + + for( ulIndex = 0U; ulIndex < ulNumOutOfRange; ulIndex++ ) + { + pstUpdateFile->pucRxBlockBitmap[ ulBitmapLen - 1U ] &= ~ulBit; + ulBit >>= 1U; + } + + pstUpdateFile->ulBlocksRemaining = ulNumBlocks; /* Initialize our blocks remaining counter. */ + + /* Create/Open the OTA file on the file system. */ + xErr = xOTA_Agent.xPALCallbacks.xCreateFileForRx( pstUpdateFile ); + + if( xErr != kOTA_Err_None ) + { + ( void ) prvSetImageStateWithReason( eOTA_ImageState_Aborted, xErr ); + ( void ) prvOTA_Close( pstUpdateFile ); /* Ignore false result since we're setting the pointer to null on the next line. */ + pstUpdateFile = NULL; + } + } + else + { + /* Can't receive the image without enough memory. */ + ( void ) prvOTA_Close( pstUpdateFile ); + pstUpdateFile = NULL; + } + } + return pstUpdateFile; /* Return the OTA file context. */ } +/* + * prvValidateDataBlock + * + * Validate the block index and size. If it is NOT the last block, it MUST be equal to a full block size. + * If it IS the last block, it MUST be equal to the expected remainder. If the block ID is out of range, + * that's an error. + */ +static bool prvValidateDataBlock( const OTA_FileContext_t * C, + uint32_t ulBlockIndex, + uint32_t ulBlockSize ) +{ + bool bRet = false; + uint32_t ulLastBlock = 0; + + ulLastBlock = ( ( C->ulFileSize + ( OTA_FILE_BLOCK_SIZE - 1U ) ) >> otaconfigLOG2_FILE_BLOCK_SIZE ) - 1U; + + if( ( ( ulBlockIndex < ulLastBlock ) && ( ulBlockSize == OTA_FILE_BLOCK_SIZE ) ) || + ( ( ulBlockIndex == ulLastBlock ) && ( ulBlockSize == ( C->ulFileSize - ( ulLastBlock * OTA_FILE_BLOCK_SIZE ) ) ) ) ) + { + bRet = true; + } + + return bRet; +} + /* * prvIngestDataBlock * @@ -2122,154 +2369,177 @@ static IngestResult_t prvIngestDataBlock( OTA_FileContext_t * C, IngestResult_t eIngestResult = eIngest_Result_Uninitialized; int32_t lFileId = 0; + int32_t lBlockSize = 0; + int32_t lBlockIndex = 0; uint32_t ulBlockSize = 0; uint32_t ulBlockIndex = 0; uint8_t * pucPayload = NULL; size_t xPayloadSize = 0; + uint32_t ulByte = 0; + uint8_t ucBitMask = 0; - if( C != NULL ) + /* Check if the file context is NULL. */ + if( C == NULL ) { - if( pxCloseResult != NULL ) + eIngestResult = eIngest_Result_NullContext; + } + + /* Check if the result pointer is NULL. */ + if( eIngestResult == eIngest_Result_Uninitialized ) + { + if( pxCloseResult == NULL ) { - *pxCloseResult = kOTA_Err_GenericIngestError; /* Default to a generic ingest function error until we prove success. */ + eIngestResult = eIngest_Result_NullResultPointer; + } + else + { + /* Default to a generic ingest function error until we prove success. */ + *pxCloseResult = kOTA_Err_GenericIngestError; + } + } - /* If we have a block bitmap available then process the message. */ - if( C->pucRxBlockBitmap && ( C->ulBlocksRemaining > 0U ) ) + /* Decode the received data block. */ + if( eIngestResult == eIngest_Result_Uninitialized ) + { + /* If we have a block bitmap available then process the message. */ + if( ( C->pucRxBlockBitmap != NULL ) && ( C->ulBlocksRemaining > 0U ) ) + { + /* Reset or start the firmware request timer. */ + prvStartRequestTimer( otaconfigFILE_REQUEST_WAIT_MS ); + + /* Decode the file block received. */ + if( kOTA_Err_None != xOTA_DataInterface.prvDecodeFileBlock( + pcRawMsg, + ulMsgSize, + &lFileId, + &lBlockIndex, + &lBlockSize, + &pucPayload, + &xPayloadSize ) ) { - /* Reset or start the firmware request timer. */ - prvStartRequestTimer( otaconfigFILE_REQUEST_WAIT_MS ); - - /* Decode the file block received. */ - if( kOTA_Err_None != xOTA_DataInterface.prvDecodeFileBlock( - pcRawMsg, - ulMsgSize, - &lFileId, - ( int32_t * ) &ulBlockIndex, - ( int32_t * ) &ulBlockSize, - &pucPayload, - &xPayloadSize ) ) - { - eIngestResult = eIngest_Result_BadData; - } - else - { - /* Validate the block index and size. */ - /* If it is NOT the last block, it MUST be equal to a full block size. */ - /* If it IS the last block, it MUST be equal to the expected remainder. */ - /* If the block ID is out of range, that's an error so abort. */ - uint32_t iLastBlock = ( ( C->ulFileSize + ( OTA_FILE_BLOCK_SIZE - 1U ) ) >> otaconfigLOG2_FILE_BLOCK_SIZE ) - 1U; - - if( ( ( ulBlockIndex < iLastBlock ) && ( ulBlockSize == OTA_FILE_BLOCK_SIZE ) ) || - ( ( ulBlockIndex == iLastBlock ) && ( ulBlockSize == ( C->ulFileSize - ( iLastBlock * OTA_FILE_BLOCK_SIZE ) ) ) ) ) - { - OTA_LOG_L1( "[%s] Received file block %u, size %u\r\n", OTA_METHOD_NAME, ulBlockIndex, ulBlockSize ); - - /* Create bit mask for use in our bitmap. */ - uint8_t ulBitMask = 1U << ( ulBlockIndex % BITS_PER_BYTE ); /*lint !e9031 The composite expression will never be greater than BITS_PER_BYTE(8). */ - /* Calculate byte offset into bitmap. */ - uint32_t ulByte = ulBlockIndex >> LOG2_BITS_PER_BYTE; - - if( ( C->pucRxBlockBitmap[ ulByte ] & ulBitMask ) == 0U ) /* If we've already received this block... */ - { - OTA_LOG_L1( "[%s] block %u is a DUPLICATE. %u blocks remaining.\r\n", OTA_METHOD_NAME, - ulBlockIndex, - C->ulBlocksRemaining ); - eIngestResult = eIngest_Result_Duplicate_Continue; - *pxCloseResult = kOTA_Err_None; /* This is a success path. */ - } - else /* Otherwise, process it normally... */ - { - if( C->pucFile != NULL ) - { - int32_t iBytesWritten = xOTA_Agent.xPALCallbacks.xWriteBlock( C, ( ulBlockIndex * OTA_FILE_BLOCK_SIZE ), pucPayload, ulBlockSize ); - - if( iBytesWritten < 0 ) - { - OTA_LOG_L1( "[%s] Error (%d) writing file block\r\n", OTA_METHOD_NAME, iBytesWritten ); - eIngestResult = eIngest_Result_WriteBlockFailed; - } - else - { - C->pucRxBlockBitmap[ ulByte ] &= ~ulBitMask; /* Mark this block as received in our bitmap. */ - C->ulBlocksRemaining--; - eIngestResult = eIngest_Result_Accepted_Continue; - *pxCloseResult = kOTA_Err_None; /* This is a success path. */ - } - } - else - { - OTA_LOG_L1( "[%s] Error: Unable to write block, file handle is NULL.\r\n", OTA_METHOD_NAME ); - eIngestResult = eIngest_Result_BadFileHandle; - } - - if( C->ulBlocksRemaining == 0U ) - { - OTA_LOG_L1( "[%s] Received final expected block of file.\r\n", OTA_METHOD_NAME ); - prvStopRequestTimer(); /* Don't request any more since we're done. */ - vPortFree( C->pucRxBlockBitmap ); /* Free the bitmap now that we're done with the download. */ - C->pucRxBlockBitmap = NULL; - - if( C->pucFile != NULL ) - { - *pxCloseResult = xOTA_Agent.xPALCallbacks.xCloseFile( C ); - - if( *pxCloseResult == kOTA_Err_None ) - { - OTA_LOG_L1( "[%s] File receive complete and signature is valid.\r\n", OTA_METHOD_NAME ); - eIngestResult = eIngest_Result_FileComplete; - } - else - { - uint32_t ulCloseResult = ( uint32_t ) *pxCloseResult; - OTA_LOG_L1( "[%s] Error (%u:0x%06x) closing OTA file.\r\n", - OTA_METHOD_NAME, - ulCloseResult >> kOTA_MainErrShiftDownBits, - ulCloseResult & ( uint32_t ) kOTA_PAL_ErrMask ); - - if( ( ulCloseResult & kOTA_Main_ErrMask ) == kOTA_Err_SignatureCheckFailed ) - { - eIngestResult = eIngest_Result_SigCheckFail; - } - else - { - eIngestResult = eIngest_Result_FileCloseFail; - } - } - - C->pucFile = NULL; /* File is now closed so clear the file handle in the context. */ - } - else - { - OTA_LOG_L1( "[%s] Error: File handle is NULL after last block received.\r\n", OTA_METHOD_NAME ); - eIngestResult = eIngest_Result_BadFileHandle; - } - } - else - { - OTA_LOG_L1( "[%s] Remaining: %u\r\n", OTA_METHOD_NAME, C->ulBlocksRemaining ); - } - } - } - else - { - OTA_LOG_L1( "[%s] Error! Block %u out of expected range! Size %u\r\n", OTA_METHOD_NAME, ulBlockIndex, ulBlockSize ); - eIngestResult = eIngest_Result_BlockOutOfRange; - } - } + eIngestResult = eIngest_Result_BadData; } else { - eIngestResult = eIngest_Result_UnexpectedBlock; + ulBlockIndex = ( uint32_t ) lBlockIndex; + ulBlockSize = ( uint32_t ) lBlockSize; } } else { - eIngestResult = eIngest_Result_NullResultPointer; + eIngestResult = eIngest_Result_UnexpectedBlock; } } - else + + /* Validate the received data block.*/ + if( eIngestResult == eIngest_Result_Uninitialized ) { - eIngestResult = eIngest_Result_NullContext; + if( prvValidateDataBlock( C, ulBlockIndex, ulBlockSize ) ) + { + OTA_LOG_L1( "[%s] Received file block %u, size %u\r\n", OTA_METHOD_NAME, ulBlockIndex, ulBlockSize ); + + /* Create bit mask for use in our bitmap. */ + ucBitMask = 1U << ( ulBlockIndex % BITS_PER_BYTE ); /*lint !e9031 The composite expression will never be greater than BITS_PER_BYTE(8). */ + + /* Calculate byte offset into bitmap. */ + ulByte = ulBlockIndex >> LOG2_BITS_PER_BYTE; + + /* Check if we've already received this block. */ + if( ( ( C->pucRxBlockBitmap[ ulByte ] ) & ucBitMask ) == 0U ) + { + OTA_LOG_L1( "[%s] block %u is a DUPLICATE. %u blocks remaining.\r\n", OTA_METHOD_NAME, + ulBlockIndex, + C->ulBlocksRemaining ); + + eIngestResult = eIngest_Result_Duplicate_Continue; + *pxCloseResult = kOTA_Err_None; /* This is a success path. */ + } + } + else + { + OTA_LOG_L1( "[%s] Error! Block %u out of expected range! Size %u\r\n", OTA_METHOD_NAME, ulBlockIndex, ulBlockSize ); + eIngestResult = eIngest_Result_BlockOutOfRange; + } + } + + /* Process the received data block. */ + if( eIngestResult == eIngest_Result_Uninitialized ) + { + if( C->pucFile != NULL ) + { + int32_t iBytesWritten = xOTA_Agent.xPALCallbacks.xWriteBlock( C, ( ulBlockIndex * OTA_FILE_BLOCK_SIZE ), pucPayload, ulBlockSize ); + + if( iBytesWritten < 0 ) + { + OTA_LOG_L1( "[%s] Error (%d) writing file block\r\n", OTA_METHOD_NAME, iBytesWritten ); + eIngestResult = eIngest_Result_WriteBlockFailed; + } + else + { + C->pucRxBlockBitmap[ ulByte ] &= ~ucBitMask; /* Mark this block as received in our bitmap. */ + C->ulBlocksRemaining--; + eIngestResult = eIngest_Result_Accepted_Continue; + *pxCloseResult = kOTA_Err_None; + } + } + else + { + OTA_LOG_L1( "[%s] Error: Unable to write block, file handle is NULL.\r\n", OTA_METHOD_NAME ); + eIngestResult = eIngest_Result_BadFileHandle; + } + } + + /* Close the file and cleanup.*/ + if( eIngestResult == eIngest_Result_Accepted_Continue ) + { + if( C->ulBlocksRemaining == 0U ) + { + OTA_LOG_L1( "[%s] Received final expected block of file.\r\n", OTA_METHOD_NAME ); + + prvStopRequestTimer(); /* Don't request any more since we're done. */ + vPortFree( C->pucRxBlockBitmap ); /* Free the bitmap now that we're done with the download. */ + C->pucRxBlockBitmap = NULL; + + if( C->pucFile != NULL ) + { + *pxCloseResult = xOTA_Agent.xPALCallbacks.xCloseFile( C ); + + if( *pxCloseResult == kOTA_Err_None ) + { + OTA_LOG_L1( "[%s] File receive complete and signature is valid.\r\n", OTA_METHOD_NAME ); + eIngestResult = eIngest_Result_FileComplete; + } + else + { + uint32_t ulCloseResult = ( uint32_t ) *pxCloseResult; + + OTA_LOG_L1( "[%s] Error (%u:0x%06x) closing OTA file.\r\n", + OTA_METHOD_NAME, + ulCloseResult >> kOTA_MainErrShiftDownBits, + ulCloseResult & ( uint32_t ) kOTA_PAL_ErrMask ); + + if( ( ( ulCloseResult ) & ( kOTA_Main_ErrMask ) ) == kOTA_Err_SignatureCheckFailed ) + { + eIngestResult = eIngest_Result_SigCheckFail; + } + else + { + eIngestResult = eIngest_Result_FileCloseFail; + } + } + + C->pucFile = NULL; /* File is now closed so clear the file handle in the context. */ + } + else + { + OTA_LOG_L1( "[%s] Error: File handle is NULL after last block received.\r\n", OTA_METHOD_NAME ); + eIngestResult = eIngest_Result_BadFileHandle; + } + } + else + { + OTA_LOG_L1( "[%s] Remaining: %u\r\n", OTA_METHOD_NAME, C->ulBlocksRemaining ); + } } return eIngestResult; @@ -2309,7 +2579,7 @@ static void prvAgentShutdownCleanup( void ) /* Cleanup related to selected protocol. */ if( xOTA_DataInterface.prvCleanup != NULL ) { - xOTA_DataInterface.prvCleanup( &xOTA_Agent ); + ( void ) xOTA_DataInterface.prvCleanup( &xOTA_Agent ); } /* @@ -2350,14 +2620,89 @@ static void prvAgentShutdownCleanup( void ) } } -static void prvOTAAgentTask( void * pUnused ) +/* + * Handle any events that were unexpected in the current state. + */ +static void prvHandleUnexpectedEvents( OTA_EventMsg_t * pxEventMsg ) +{ + DEFINE_OTA_METHOD_NAME( "prvHandleUnexpectedEvents" ); + + configASSERT( pxEventMsg ); + + OTA_LOG_L1( "[%s] Unexpected Event. Current State [%s] Received Event [%s] \n", + OTA_METHOD_NAME, + pcOTA_AgentState_Strings[ xOTA_Agent.eState ], + pcOTA_Event_Strings[ pxEventMsg->xEventId ] ); + + /* Perform any cleanup operations required for specifc unhandled events.*/ + switch( pxEventMsg->xEventId ) + { + case eOTA_AgentEvent_ReceivedJobDocument: + + /* Received job event is not handled , release the buffer.*/ + prvOTAEventBufferFree( pxEventMsg->pxEventData ); + + break; + + case eOTA_AgentEvent_ReceivedFileBlock: + + /* Received file data event is not handled , release the buffer.*/ + prvOTAEventBufferFree( pxEventMsg->pxEventData ); + + break; + + default: + + /* Nothing to do here.*/ + break; + } +} + +/* + * Execute the handler for selected index from the transition table. + */ +static void prvExecuteHandler( uint32_t index, + const OTA_EventMsg_t * const pxEventMsg ) +{ + DEFINE_OTA_METHOD_NAME( "prvExecuteHandler" ); + + OTA_Err_t xErr = kOTA_Err_Uninitialized; + + if( OTATransitionTable[ index ].xHandler ) + { + xErr = OTATransitionTable[ index ].xHandler( pxEventMsg->pxEventData ); + + if( xErr == kOTA_Err_None ) + { + OTA_LOG_L1( "[%s] Called handler. Current State [%s] Event [%s] New state [%s] \n", + OTA_METHOD_NAME, + pcOTA_AgentState_Strings[ xOTA_Agent.eState ], + pcOTA_Event_Strings[ pxEventMsg->xEventId ], + pcOTA_AgentState_Strings[ OTATransitionTable[ index ].xNextState ] ); + + /* + * Update the current state in OTA agent context. + */ + xOTA_Agent.eState = OTATransitionTable[ index ].xNextState; + } + else + { + OTA_LOG_L1( "[%s] Handler failed. Current State [%s] Event [%s] Error Code [%d] \n", + OTA_METHOD_NAME, + pcOTA_AgentState_Strings[ xOTA_Agent.eState ], + pcOTA_Event_Strings[ pxEventMsg->xEventId ], + xErr ); + } + } +} + +static void prvOTAAgentTask( void * pvUnused ) { DEFINE_OTA_METHOD_NAME( "prvOTAAgentTask" ); - ( void ) pUnused; + ( void ) pvUnused; OTA_EventMsg_t xEventMsg = { 0 }; - OTA_Err_t xErr = kOTA_Err_Uninitialized; uint32_t ulTransitionTableLen = sizeof( OTATransitionTable ) / sizeof( OTATransitionTable[ 0 ] ); uint32_t i = 0; @@ -2373,61 +2718,115 @@ static void prvOTAAgentTask( void * pUnused ) */ if( xQueueReceive( xOTA_Agent.xOTA_EventQueue, &xEventMsg, portMAX_DELAY ) == pdTRUE ) { + /* + * Search for the state and event from the table. + */ for( i = 0; i < ulTransitionTableLen; i++ ) { - if( ( OTATransitionTable[ i ].xCurrentState == xOTA_Agent.eState ) || ( OTATransitionTable[ i ].xCurrentState == eOTA_AgentState_All ) ) + if( ( ( OTATransitionTable[ i ].xCurrentState == xOTA_Agent.eState ) || + ( OTATransitionTable[ i ].xCurrentState == eOTA_AgentState_All ) ) && + ( OTATransitionTable[ i ].xEventId == xEventMsg.xEventId ) ) { - OTA_LOG_L3( "[%s] , State matched [%s]\n", OTA_METHOD_NAME, pcOTA_AgentState_Strings[ i ] ); + OTA_LOG_L3( "[%s] , State matched [%s], Event matched [%s]\n", + OTA_METHOD_NAME, + pcOTA_AgentState_Strings[ i ] + pcOTA_Event_Strings[ i ] ); - if( OTATransitionTable[ i ].xEventId == xEventMsg.xEventId ) - { - OTA_LOG_L3( "[%s] , Event matched [%s]\n", OTA_METHOD_NAME, pcOTA_Event_Strings[ i ] ); - - if( OTATransitionTable[ i ].xHandler ) - { - xErr = OTATransitionTable[ i ].xHandler( xEventMsg.pxEventData ); - - if( xErr == kOTA_Err_None ) - { - OTA_LOG_L1( "[%s] Called handler. Current State [%s] Event [%s] New state [%s] \n", - OTA_METHOD_NAME, - pcOTA_AgentState_Strings[ xOTA_Agent.eState ], - pcOTA_Event_Strings[ xEventMsg.xEventId ], - pcOTA_AgentState_Strings[ OTATransitionTable[ i ].xNextState ] ); - - /* - * Update the current state in OTA agent context. - */ - xOTA_Agent.eState = OTATransitionTable[ i ].xNextState; - } - else - { - OTA_LOG_L1( "[%s] Handler failed. Current State [%s] Event [%s] Error Code [%d] \n", - OTA_METHOD_NAME, - pcOTA_AgentState_Strings[ xOTA_Agent.eState ], - pcOTA_Event_Strings[ xEventMsg.xEventId ], - xErr ); - } - } - - break; - } + /* + * Execute the handler function. + */ + prvExecuteHandler( i, &xEventMsg ); + break; } } if( i == ulTransitionTableLen ) { - OTA_LOG_L1( "[%s] Unexpected Event. Current State [%s] Event [%s] \n", - OTA_METHOD_NAME, - pcOTA_AgentState_Strings[ xOTA_Agent.eState ], - pcOTA_Event_Strings[ xEventMsg.xEventId ] ); + /* + * Handle unexpected events. + */ + prvHandleUnexpectedEvents( &xEventMsg ); } } } } -BaseType_t OTA_SignalEvent( const OTA_EventMsg_t * const pxEventMsg ) +static BaseType_t prvStartOTAAgentTask( void * pvConnectionContext, + TickType_t xTicksToWait ) { + BaseType_t xReturn = 0; + uint32_t ulIndex = 0; + + /* + * The actual OTA Task and queue control structure. Only created once. + */ + static TaskHandle_t pxOTA_TaskHandle; + static StaticQueue_t xStaticQueue; + + portENTER_CRITICAL(); + + /* + * The current OTA image state as set by the OTA agent. + */ + xOTA_Agent.eImageState = eOTA_ImageState_Unknown; + + /* + * Save the current connection context provided by the user. + */ + xOTA_Agent.pvConnectionContext = pvConnectionContext; + + /* + * Create the queue used to pass event messages to the OTA task. + */ + xOTA_Agent.xOTA_EventQueue = xQueueCreateStatic( ( UBaseType_t ) OTA_NUM_MSG_Q_ENTRIES, ( UBaseType_t ) sizeof( OTA_EventMsg_t ), ( uint8_t * ) xQueueData, &xStaticQueue ); + configASSERT( xOTA_Agent.xOTA_EventQueue != NULL ); + + /* + * Create the queue used to pass event messages to the OTA task. + */ + xOTA_Agent.xOTA_ThreadSafetyMutex = xSemaphoreCreateMutex(); + configASSERT( xOTA_Agent.xOTA_ThreadSafetyMutex != NULL ); + + /* + * Initialize all file paths to NULL. + */ + for( ulIndex = 0; ulIndex < OTA_MAX_FILES; ulIndex++ ) + { + xOTA_Agent.pxOTA_Files[ ulIndex ].pucFilePath = NULL; + } + + /* + * Make sure OTA event buffers are clear. + */ + for( ulIndex = 0; ulIndex < otaconfigMAX_NUM_OTA_DATA_BUFFERS; ulIndex++ ) + { + xEventBuffer[ ulIndex ].bBufferUsed = false; + } + + xReturn = xTaskCreate( prvOTAAgentTask, "OTA Agent Task", otaconfigSTACK_SIZE, NULL, otaconfigAGENT_PRIORITY, &pxOTA_TaskHandle ); + + portEXIT_CRITICAL(); /* Protected elements are initialized. It's now safe to context switch. */ + + /* + * If task creation succeed, wait for the OTA agent to be ready before proceeding. Otherwise, + * let it fall through to exit. + */ + if( xReturn == pdPASS ) + { + while( ( xTicksToWait-- > 0U ) && ( xOTA_Agent.eState != eOTA_AgentState_Ready ) ) + { + vTaskDelay( 1 ); + } + } + + return xReturn; +} + +bool OTA_SignalEvent( const OTA_EventMsg_t * const pxEventMsg ) +{ + DEFINE_OTA_METHOD_NAME( "OTA_SignalEvent" ); + + bool bReturn = false; BaseType_t xErr = pdFALSE; /* @@ -2440,14 +2839,16 @@ BaseType_t OTA_SignalEvent( const OTA_EventMsg_t * const pxEventMsg ) if( xErr == pdTRUE ) { + bReturn = true; OTA_LOG_L3( "Success: Pushed event message to queue.\r\n" ); } else { + bReturn = false; OTA_LOG_L1( "Error: Could not push event message to queue.\r\n" ); } - return xErr; + return bReturn; } /* @@ -2459,24 +2860,23 @@ BaseType_t OTA_SignalEvent( const OTA_EventMsg_t * const pxEventMsg ) * successfully. */ OTA_State_t OTA_AgentInit( void * pvConnectionContext, - const uint8_t * pcThingName, + const uint8_t * pucThingName, pxOTACompleteCallback_t xFunc, TickType_t xTicksToWait ) { - DEFINE_OTA_METHOD_NAME( "OTA_AgentInit" ); - OTA_State_t xState; if( xOTA_Agent.eState == eOTA_AgentState_Stopped ) { /* Init default OTA pal callbacks. */ - OTA_PAL_Callbacks_t xDefaultCallbacks = OTA_JOB_CALLBACK_DEFAULT_INITIALIZER; + OTA_PAL_Callbacks_t xPALCallbacks = OTA_JOB_CALLBACK_DEFAULT_INITIALIZER; /* Set the OTA complete callback. */ - xDefaultCallbacks.xCompleteCallback = xFunc; + xPALCallbacks.xCompleteCallback = xFunc; - xState = OTA_AgentInit_internal( pvConnectionContext, pcThingName, &xDefaultCallbacks, xTicksToWait ); + xState = OTA_AgentInit_internal( pvConnectionContext, pucThingName, &xPALCallbacks, xTicksToWait ); } + /* If OTA agent is already running, just update the CompleteCallback and reset the statistics. */ else { if( xFunc != NULL ) @@ -2484,7 +2884,7 @@ OTA_State_t OTA_AgentInit( void * pvConnectionContext, xOTA_Agent.xPALCallbacks.xCompleteCallback = xFunc; } - memset( &xOTA_Agent.xStatistics, 0, sizeof( xOTA_Agent.xStatistics ) ); + ( void ) memset( &xOTA_Agent.xStatistics, 0, sizeof( xOTA_Agent.xStatistics ) ); xState = xOTA_Agent.eState; } @@ -2492,22 +2892,15 @@ OTA_State_t OTA_AgentInit( void * pvConnectionContext, } OTA_State_t OTA_AgentInit_internal( void * pvConnectionContext, - const uint8_t * pcThingName, - OTA_PAL_Callbacks_t * xCallbacks, + const uint8_t * pucThingName, + const OTA_PAL_Callbacks_t * pxCallbacks, TickType_t xTicksToWait ) { DEFINE_OTA_METHOD_NAME( "OTA_AgentInit_internal" ); - static TaskHandle_t pxOTA_TaskHandle; - uint32_t ulIndex; BaseType_t xReturn = 0; OTA_EventMsg_t xEventMsg = { 0 }; - /* - * The actual OTA queue control structure. Only created once. - */ - static StaticQueue_t xStaticQueue; - /* * OTA Task is not running yet so update the state to init direclty in OTA context. */ @@ -2518,98 +2911,7 @@ OTA_State_t OTA_AgentInit_internal( void * pvConnectionContext, * The OTA agent context is initialized with the prvPAL values. So, if null is passed in, don't * do anything and just use the defaults in the OTA structure. */ - if( xCallbacks != NULL ) - { - if( xCallbacks->xAbort != NULL ) - { - xOTA_Agent.xPALCallbacks.xAbort = xCallbacks->xAbort; - } - else - { - xOTA_Agent.xPALCallbacks.xAbort = prvPAL_Abort; - } - - if( xCallbacks->xActivateNewImage != NULL ) - { - xOTA_Agent.xPALCallbacks.xActivateNewImage = xCallbacks->xActivateNewImage; - } - else - { - xOTA_Agent.xPALCallbacks.xActivateNewImage = prvPAL_DefaultActivateNewImage; - } - - if( xCallbacks->xCloseFile != NULL ) - { - xOTA_Agent.xPALCallbacks.xCloseFile = xCallbacks->xCloseFile; - } - else - { - xOTA_Agent.xPALCallbacks.xCloseFile = prvPAL_CloseFile; - } - - if( xCallbacks->xCreateFileForRx != NULL ) - { - xOTA_Agent.xPALCallbacks.xCreateFileForRx = xCallbacks->xCreateFileForRx; - } - else - { - xOTA_Agent.xPALCallbacks.xCreateFileForRx = prvPAL_CreateFileForRx; - } - - if( xCallbacks->xGetPlatformImageState != NULL ) - { - xOTA_Agent.xPALCallbacks.xGetPlatformImageState = xCallbacks->xGetPlatformImageState; - } - else - { - xOTA_Agent.xPALCallbacks.xGetPlatformImageState = prvPAL_DefaultGetPlatformImageState; - } - - if( xCallbacks->xResetDevice != NULL ) - { - xOTA_Agent.xPALCallbacks.xResetDevice = xCallbacks->xResetDevice; - } - else - { - xOTA_Agent.xPALCallbacks.xResetDevice = prvPAL_DefaultResetDevice; - } - - if( xCallbacks->xSetPlatformImageState != NULL ) - { - xOTA_Agent.xPALCallbacks.xSetPlatformImageState = xCallbacks->xSetPlatformImageState; - } - else - { - xOTA_Agent.xPALCallbacks.xSetPlatformImageState = prvPAL_DefaultSetPlatformImageState; - } - - if( xCallbacks->xWriteBlock != NULL ) - { - xOTA_Agent.xPALCallbacks.xWriteBlock = xCallbacks->xWriteBlock; - } - else - { - xOTA_Agent.xPALCallbacks.xWriteBlock = prvPAL_WriteBlock; - } - - if( xCallbacks->xCompleteCallback != NULL ) - { - xOTA_Agent.xPALCallbacks.xCompleteCallback = xCallbacks->xCompleteCallback; - } - else - { - xOTA_Agent.xPALCallbacks.xCompleteCallback = prvDefaultOTACompleteCallback; - } - - if( xCallbacks->xCustomJobCallback != NULL ) - { - xOTA_Agent.xPALCallbacks.xCustomJobCallback = xCallbacks->xCustomJobCallback; - } - else - { - xOTA_Agent.xPALCallbacks.xCustomJobCallback = prvDefaultCustomJobCallback; - } - } + prvSetPALCallbacks( pxCallbacks ); /* * Initialize the OTA control interface based on the application protocol @@ -2625,88 +2927,23 @@ OTA_State_t OTA_AgentInit_internal( void * pvConnectionContext, xOTA_Agent.xStatistics.ulOTA_PacketsQueued = 0; xOTA_Agent.xStatistics.ulOTA_PacketsProcessed = 0; - if( pcThingName != NULL ) + if( pucThingName == NULL ) { - uint32_t ulStrLen = strlen( ( const char * ) pcThingName ); + OTA_LOG_L1( "[%s]Error: Thing name is NULL.\r\n", OTA_METHOD_NAME ); + } + else + { + uint32_t ulStrLen = strlen( ( const char * ) pucThingName ); if( ulStrLen <= otaconfigMAX_THINGNAME_LEN ) { /* * Store the Thing name to be used for topics later. */ - memcpy( xOTA_Agent.pcThingName, pcThingName, ulStrLen + 1UL ); /* Include zero terminator when saving the Thing name. */ + ( void ) memcpy( xOTA_Agent.pcThingName, pucThingName, ulStrLen + 1UL ); /* Include zero terminator when saving the Thing name. */ } - portENTER_CRITICAL(); - - if( xOTA_Agent.eState == eOTA_AgentState_Init ) - { - /* - * The current OTA image state as set by the OTA agent. - */ - xOTA_Agent.eImageState = eOTA_ImageState_Unknown; - - /* - * Save the current connection context provided by the user. - */ - xOTA_Agent.pvConnectionContext = pvConnectionContext; - - /* - * Create the queue used to pass event messages to the OTA task. - */ - xOTA_Agent.xOTA_EventQueue = xQueueCreateStatic( ( UBaseType_t ) OTA_NUM_MSG_Q_ENTRIES, ( UBaseType_t ) sizeof( OTA_EventMsg_t ), ( uint8_t * ) xQueueData, &xStaticQueue ); - configASSERT( xOTA_Agent.xOTA_EventQueue ); - - /* - * Create the queue used to pass event messages to the OTA task. - */ - xOTA_Agent.xOTA_ThreadSafetyMutex = xSemaphoreCreateMutex(); - configASSERT( xOTA_Agent.xOTA_ThreadSafetyMutex ); - - /* - * Initialize all file paths to NULL. - */ - for( ulIndex = 0; ulIndex < OTA_MAX_FILES; ulIndex++ ) - { - xOTA_Agent.pxOTA_Files[ ulIndex ].pucFilePath = NULL; - } - - /* - * Make sure OTA event buffers are clear. - */ - for( ulIndex = 0; ulIndex < otaconfigMAX_NUM_OTA_DATA_BUFFERS; ulIndex++ ) - { - xEventBuffer[ ulIndex ].bBufferUsed = false; - } - - xReturn = xTaskCreate( prvOTAAgentTask, "OTA Agent Task", otaconfigSTACK_SIZE, NULL, otaconfigAGENT_PRIORITY, &pxOTA_TaskHandle ); - portEXIT_CRITICAL(); /* Protected elements are initialized. It's now safe to context switch. */ - - if( xReturn == pdPASS ) - { - /* - * Wait for the OTA agent to be ready before proceeding. - */ - while( ( xTicksToWait-- > 0U ) && ( xOTA_Agent.eState != eOTA_AgentState_Ready ) ) - { - vTaskDelay( 1 ); - } - } - else - { - /* - * Task creation failed so fall through to exit. - */ - } - } - else - { - portEXIT_CRITICAL(); - } - } - else - { - OTA_LOG_L1( "[%s]Error: Thing name is NULL.\r\n", OTA_METHOD_NAME ); + xReturn = prvStartOTAAgentTask( pvConnectionContext, xTicksToWait ); } if( xOTA_Agent.eState == eOTA_AgentState_Ready ) @@ -2719,7 +2956,10 @@ OTA_State_t OTA_AgentInit_internal( void * pvConnectionContext, xEventMsg.xEventId = eOTA_AgentEvent_Start; /* Send signal to OTA task. */ - OTA_SignalEvent( &xEventMsg ); + if( !OTA_SignalEvent( &xEventMsg ) ) + { + OTA_LOG_L1( "[%s] Failed to signal the OTA agent to start.", OTA_METHOD_NAME ); + } } else { @@ -2749,15 +2989,22 @@ OTA_State_t OTA_AgentShutdown( TickType_t xTicksToWait ) * Send shutdown signal to OTA Agent task. */ xEventMsg.xEventId = eOTA_AgentEvent_Shutdown; - OTA_SignalEvent( &xEventMsg ); - /* - * Wait for the OTA agent to complete shutdown, if requested. - */ - while( ( xTicksToWait > 0U ) && ( xOTA_Agent.eState != eOTA_AgentState_Stopped ) ) + /* Send signal to OTA task. */ + if( !OTA_SignalEvent( &xEventMsg ) ) { - vTaskDelay( 1 ); - xTicksToWait--; + OTA_LOG_L1( "[%s] Failed to signal the OTA agent to shutdown.", OTA_METHOD_NAME ); + } + else + { + /* + * Wait for the OTA agent to complete shutdown, if requested. + */ + while( ( xTicksToWait > 0U ) && ( xOTA_Agent.eState != eOTA_AgentState_Stopped ) ) + { + vTaskDelay( 1 ); + xTicksToWait--; + } } } else @@ -2824,7 +3071,7 @@ OTA_Err_t OTA_CheckForUpdate( void ) */ xEventMsg.xEventId = eOTA_AgentEvent_RequestJobDocument; - if( OTA_SignalEvent( &xEventMsg ) != pdTRUE ) + if( !OTA_SignalEvent( &xEventMsg ) ) { xReturn = kOTA_Err_EventQueueSendFailed; } @@ -2892,9 +3139,7 @@ OTA_Err_t OTA_SetImageState( OTA_ImageState_t eState ) /* * Send the event, xOTA_Agent.eImageState will be set later when the event is processed. */ - OTA_SignalEvent( &xEventMsg ); - - xErr = kOTA_Err_None; + xErr = OTA_SignalEvent( &xEventMsg ) ? kOTA_Err_None : kOTA_Err_EventQueueSendFailed; } else { @@ -2910,7 +3155,7 @@ OTA_Err_t OTA_SetImageState( OTA_ImageState_t eState ) /* * Set the image state as rejected. */ - xErr = prvSetImageStateWithReason( eState, ( uint32_t ) NULL ); + xErr = prvSetImageStateWithReason( eState, 0 ); break; @@ -2919,7 +3164,7 @@ OTA_Err_t OTA_SetImageState( OTA_ImageState_t eState ) /* * Set the image state as accepted. */ - xErr = prvSetImageStateWithReason( eState, ( uint32_t ) NULL ); + xErr = prvSetImageStateWithReason( eState, 0 ); break; @@ -2942,6 +3187,69 @@ OTA_ImageState_t OTA_GetImageState( void ) return xOTA_Agent.eImageState; } +/* + * Suspend OTA Agent task. + */ +OTA_Err_t OTA_Suspend( void ) +{ + DEFINE_OTA_METHOD_NAME( "OTA_Suspend" ); + + OTA_Err_t xErr = kOTA_Err_Uninitialized; + OTA_EventMsg_t xEventMsg = { 0 }; + + /* Stop the request timer. */ + prvStopRequestTimer(); + + /* Check if OTA Agent is running. */ + if( xOTA_Agent.eState != eOTA_AgentState_Stopped ) + { + /* + * Send event to OTA agent task. + */ + xEventMsg.xEventId = eOTA_AgentEvent_Suspend; + xErr = OTA_SignalEvent( &xEventMsg ) ? kOTA_Err_None : kOTA_Err_EventQueueSendFailed; + } + else + { + OTA_LOG_L1( "[%s] Error: OTA Agent is not running, cannot suspend.\r\n", OTA_METHOD_NAME ); + + xErr = kOTA_Err_OTAAgentStopped; + } + + return xErr; +} + +/* + * Resume OTA Agent task. + */ +OTA_Err_t OTA_Resume( void * pxConnection ) +{ + DEFINE_OTA_METHOD_NAME( "OTA_Resume" ); + + OTA_Err_t xErr = kOTA_Err_Uninitialized; + OTA_EventMsg_t xEventMsg = { 0 }; + + xEventMsg.pxEventData = pxConnection; + + /* Check if OTA Agent is running. */ + if( xOTA_Agent.eState != eOTA_AgentState_Stopped ) + { + /* + * Send event to OTA agent task. + */ + xEventMsg.xEventId = eOTA_AgentEvent_Resume; + xErr = OTA_SignalEvent( &xEventMsg ) ? kOTA_Err_None : kOTA_Err_EventQueueSendFailed; + } + else + { + OTA_LOG_L1( "[%s] Error: OTA Agent is not running, cannot resume.\r\n", OTA_METHOD_NAME ); + + xErr = kOTA_Err_OTAAgentStopped; + } + + return xErr; +} + /*-----------------------------------------------------------*/ /* Provide access to private members for testing. */ diff --git a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_agent_internal.h b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_agent_internal.h index 7bb5b602d3..6c03fb41fb 100755 --- a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_agent_internal.h +++ b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_agent_internal.h @@ -1,5 +1,5 @@ /* - * FreeRTOS OTA V1.1.1 + * FreeRTOS OTA V1.2.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -41,9 +41,6 @@ #include "queue.h" #include "semphr.h" -/* Type definitions for OTA Agent */ -#include "aws_iot_ota_types.h" - /* General constants. */ #define LOG2_BITS_PER_BYTE 3UL /* Log base 2 of bits per byte. */ #define BITS_PER_BYTE ( 1UL << LOG2_BITS_PER_BYTE ) /* Number of bits in a byte. This is used by the block bitmap implementation. */ @@ -60,13 +57,13 @@ #endif /* Job document parser constants. */ -#define OTA_MAX_JSON_TOKENS 64U /* Number of JSON tokens supported in a single parser call. */ -#define OTA_MAX_JSON_STR_LEN 256U /* Limit our JSON string compares to something small to avoid going into the weeds. */ -#define OTA_DOC_MODEL_MAX_PARAMS 32U /* The parameter list is backed by a 32 bit longword bitmap by design. */ -#define OTA_JOB_PARAM_REQUIRED ( ( bool_t ) pdTRUE ) /* Used to denote a required document model parameter. */ -#define OTA_JOB_PARAM_OPTIONAL ( ( bool_t ) pdFALSE ) /* Used to denote an optional document model parameter. */ -#define OTA_DONT_STORE_PARAM 0xffffffffUL /* If ulDestOffset in the model is 0xffffffff, do not store the value. */ -#define OTA_DATA_BLOCK_SIZE ( ( 1U << otaconfigLOG2_FILE_BLOCK_SIZE ) + OTA_REQUEST_URL_MAX_SIZE + 30 ) /* Header is 19 bytes.*/ +#define OTA_MAX_JSON_TOKENS 64U /* Number of JSON tokens supported in a single parser call. */ +#define OTA_MAX_JSON_STR_LEN 256U /* Limit our JSON string compares to something small to avoid going into the weeds. */ +#define OTA_DOC_MODEL_MAX_PARAMS 32U /* The parameter list is backed by a 32 bit longword bitmap by design. */ +#define OTA_JOB_PARAM_REQUIRED true /* Used to denote a required document model parameter. */ +#define OTA_JOB_PARAM_OPTIONAL false /* Used to denote an optional document model parameter. */ +#define OTA_DONT_STORE_PARAM 0xffffffffUL /* If ulDestOffset in the model is 0xffffffff, do not store the value. */ +#define OTA_DATA_BLOCK_SIZE ( ( 1U << otaconfigLOG2_FILE_BLOCK_SIZE ) + OTA_REQUEST_URL_MAX_SIZE + 30 ) /* Header is 19 bytes.*/ /* OTA Agent task event flags. */ @@ -150,8 +147,8 @@ typedef enum */ typedef struct { - const char * pcSrcKey; /* Expected key name. */ - const bool_t bRequired; /* If true, this parameter must exist in the document. */ + const char * pcSrcKey; /* Expected key name. */ + const bool bRequired; /* If true, this parameter must exist in the document. */ union { const uint32_t ulDestOffset; /* Pointer or offset to where we'll store the value, if not ~0. */ @@ -208,28 +205,27 @@ enum * size, attributes, etc. The following value specifies the number of parameters * that are included in the job document model although some may be optional. */ -#define OTA_NUM_JOB_PARAMS ( 19 ) /* Number of parameters in the job document. */ -/* We need the following string to match in a couple places in the code so use a #define. */ -#define OTA_JSON_UPDATED_BY_KEY "updatedBy" +#define OTA_NUM_JOB_PARAMS ( 19 ) /* Number of parameters in the job document. */ -static const char pcOTA_JSON_ClientTokenKey[] = "clientToken"; -static const char pcOTA_JSON_ExecutionKey[] = "execution"; -static const char pcOTA_JSON_JobIDKey[] = "jobId"; -static const char pcOTA_JSON_StatusDetailsKey[] = "statusDetails"; -static const char pcOTA_JSON_SelfTestKey[] = "self_test"; -static const char pcOTA_JSON_UpdatedByKey[] = OTA_JSON_UPDATED_BY_KEY; -static const char pcOTA_JSON_JobDocKey[] = "jobDocument"; -static const char pcOTA_JSON_OTAUnitKey[] = "afr_ota"; -static const char pcOTA_JSON_ProtocolsKey[] = "protocols"; -static const char pcOTA_JSON_FileGroupKey[] = "files"; -static const char pcOTA_JSON_StreamNameKey[] = "streamname"; -static const char pcOTA_JSON_FilePathKey[] = "filepath"; -static const char pcOTA_JSON_FileSizeKey[] = "filesize"; -static const char pcOTA_JSON_FileIDKey[] = "fileid"; -static const char pcOTA_JSON_FileAttributeKey[] = "attr"; -static const char pcOTA_JSON_FileCertNameKey[] = "certfile"; -static const char pcOTA_JSON_UpdateDataUrlKey[] = "update_data_url"; -static const char pcOTA_JSON_AuthSchemeKey[] = "auth_scheme"; +/* Keys in OTA job doc . */ +#define OTA_JSON_CLIENT_TOKEN_KEY "clientToken" +#define OTA_JSON_EXECUTION_KEY "execution" +#define OTA_JSON_JOB_ID_KEY "jobId" +#define OTA_JSON_STATUS_DETAILS_KEY "statusDetails" +#define OTA_JSON_SELF_TEST_KEY "self_test" +#define OTA_JSON_UPDATED_BY_KEY "updatedBy" +#define OTA_JSON_JOB_DOC_KEY "jobDocument" +#define OTA_JSON_OTA_UNIT_KEY "afr_ota" +#define OTA_JSON_PROTOCOLS_KEY "protocols" +#define OTA_JSON_FILE_GROUP_KEY "files" +#define OTA_JSON_STREAM_NAME_KEY "streamname" +#define OTA_JSON_FILE_PATH_KEY "filepath" +#define OTA_JSON_FILE_SIZE_KEY "filesize" +#define OTA_JSON_FILE_ID_KEY "fileid" +#define OTA_JSON_FILE_ATTRIBUTE_KEY "attr" +#define OTA_JSON_FILE_CERT_NAME_KEY "certfile" +#define OTA_JSON_UPDATE_DATA_URL_KEY "update_data_url" +#define OTA_JSON_AUTH_SCHEME_KEY "auth_scheme" /* This is the OTA statistics structure to hold useful info. */ @@ -270,7 +266,7 @@ typedef struct { uint8_t ucData[ OTA_DATA_BLOCK_SIZE ]; uint32_t ulDataLength; - bool_t bBufferUsed; + bool bBufferUsed; } OTA_EventData_t; typedef struct @@ -295,6 +291,6 @@ void prvOTAEventBufferFree( OTA_EventData_t * const pxBuffer ); * This function adds the event to the back of event queue and used * by internal OTA modules to signal agent task. */ -BaseType_t OTA_SignalEvent( const OTA_EventMsg_t * const pxEvent ); +bool OTA_SignalEvent( const OTA_EventMsg_t * const pxEventMsg ); #endif /* ifndef _AWS_IOT_OTA_AGENT_INTERNAL_H_ */ diff --git a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_interface.c b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_interface.c index 5c721c2d6f..2f9b81ead9 100755 --- a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_interface.c +++ b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_interface.c @@ -1,5 +1,5 @@ /* - * FreeRTOS OTA V1.1.1 + * FreeRTOS OTA V1.2.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -55,13 +55,13 @@ * than one protocol is selected while creating OTA job. */ #if ( configOTA_PRIMARY_DATA_PROTOCOL == OTA_DATA_OVER_MQTT ) - const char * pcProtocolPriority[ OTA_DATA_NUM_PROTOCOLS ] = + static const char * pcProtocolPriority[ OTA_DATA_NUM_PROTOCOLS ] = { "MQTT", "HTTP" }; #elif ( configOTA_PRIMARY_DATA_PROTOCOL == OTA_DATA_OVER_HTTP ) - const char * pcProtocolPriority[ OTA_DATA_NUM_PROTOCOLS ] = + static const char * pcProtocolPriority[ OTA_DATA_NUM_PROTOCOLS ] = { "HTTP", "MQTT" diff --git a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_interface.h b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_interface.h index 549fc60ff1..0ab5abb5af 100755 --- a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_interface.h +++ b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_interface.h @@ -1,5 +1,5 @@ /* - * FreeRTOS OTA V1.1.1 + * FreeRTOS OTA V1.2.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_pal.h b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_pal.h index bccb2b99b4..3b2f73f2af 100755 --- a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_pal.h +++ b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/aws_iot_ota_pal.h @@ -1,5 +1,5 @@ /* - * FreeRTOS OTA V1.1.1 + * FreeRTOS OTA V1.2.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/http/aws_iot_ota_http.c b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/http/aws_iot_ota_http.c index e138c8f366..d1a6b6b541 100755 --- a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/http/aws_iot_ota_http.c +++ b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/http/aws_iot_ota_http.c @@ -1,5 +1,5 @@ /* - * FreeRTOS OTA V1.1.1 + * FreeRTOS OTA V1.2.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/http/aws_iot_ota_http.h b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/http/aws_iot_ota_http.h index 3cbe931519..e7e775a785 100755 --- a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/http/aws_iot_ota_http.h +++ b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/http/aws_iot_ota_http.h @@ -1,5 +1,5 @@ /* - * FreeRTOS OTA V1.1.1 + * FreeRTOS OTA V1.2.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_cbor.c b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_cbor.c index 4f127312fe..8bfa8a1016 100755 --- a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_cbor.c +++ b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_cbor.c @@ -1,5 +1,5 @@ /* - * FreeRTOS OTA V1.1.1 + * FreeRTOS OTA V1.2.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_cbor.h b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_cbor.h index b42f846e0f..9e87e5790c 100755 --- a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_cbor.h +++ b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_cbor.h @@ -1,5 +1,5 @@ /* - * FreeRTOS OTA V1.1.1 + * FreeRTOS OTA V1.2.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_cbor_internal.h b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_cbor_internal.h index 2690da7e3e..80fce43b7e 100755 --- a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_cbor_internal.h +++ b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_cbor_internal.h @@ -1,5 +1,5 @@ /* - * FreeRTOS OTA V1.1.1 + * FreeRTOS OTA V1.2.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_mqtt.c b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_mqtt.c index fa743cef7d..21ab6b0102 100755 --- a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_mqtt.c +++ b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_mqtt.c @@ -1,5 +1,5 @@ /* - * FreeRTOS OTA V1.1.1 + * FreeRTOS OTA V1.2.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of @@ -121,11 +121,11 @@ static void prvDataPublishCallback( void * pvCallbackContext, /* Subscribe to the jobs notification topic (i.e. New file version available). */ -static bool_t prvSubscribeToJobNotificationTopics( const OTA_AgentContext_t * pxAgentCtx ); +static bool prvSubscribeToJobNotificationTopics( const OTA_AgentContext_t * pxAgentCtx ); /* UnSubscribe from the firmware update receive topic. */ -static bool_t prvUnSubscribeFromDataStream( const OTA_AgentContext_t * pxAgentCtx ); +static bool prvUnSubscribeFromDataStream( const OTA_AgentContext_t * pxAgentCtx ); /* UnSubscribe from the jobs notification topic. */ @@ -142,62 +142,73 @@ static IotMqttError_t prvPublishMessage( const OTA_AgentContext_t * pxAgentCtx, /* Subscribe to the OTA job notification topics. */ -static bool_t prvSubscribeToJobNotificationTopics( const OTA_AgentContext_t * pxAgentCtx ) +static bool prvSubscribeToJobNotificationTopics( const OTA_AgentContext_t * pxAgentCtx ) { DEFINE_OTA_METHOD_NAME( "prvSubscribeToJobNotificationTopics" ); - bool_t bResult = pdFALSE; + bool bResult = false; char pcJobTopic[ OTA_MAX_TOPIC_LEN ]; IotMqttSubscription_t stJobsSubscription; + IotMqttError_t eResult = IOT_MQTT_STATUS_PENDING; + uint16_t usTopicLen = 0; OTA_ConnectionContext_t * pvConnContext = pxAgentCtx->pvConnectionContext; + /* Build the first topic. */ + usTopicLen = ( uint16_t ) snprintf( pcJobTopic, /*lint -e586 Intentionally using snprintf. */ + sizeof( pcJobTopic ), + pcOTA_JobsGetNextAccepted_TopicTemplate, + pxAgentCtx->pcThingName ); + /* Clear subscription struct and set common parameters for job topics used by OTA. */ memset( &stJobsSubscription, 0, sizeof( stJobsSubscription ) ); stJobsSubscription.qos = IOT_MQTT_QOS_1; - stJobsSubscription.pTopicFilter = ( const char * ) pcJobTopic; /* Point to local string storage. Built below. */ - stJobsSubscription.callback.pCallbackContext = ( void * ) pxAgentCtx; /*lint !e923 The publish callback context is implementing data hiding with a void* type.*/ + stJobsSubscription.pTopicFilter = ( const char * ) pcJobTopic; /* Point to local string storage. Built below. */ + stJobsSubscription.callback.pCallbackContext = ( void * ) pxAgentCtx; /*lint !e923 The publish callback context is implementing data hiding with a void* type.*/ stJobsSubscription.callback.function = prvJobPublishCallback; - stJobsSubscription.topicFilterLength = ( uint16_t ) snprintf( pcJobTopic, /*lint -e586 Intentionally using snprintf. */ - sizeof( pcJobTopic ), - pcOTA_JobsGetNextAccepted_TopicTemplate, - pxAgentCtx->pcThingName ); + stJobsSubscription.topicFilterLength = usTopicLen; - if( ( stJobsSubscription.topicFilterLength > 0U ) && ( stJobsSubscription.topicFilterLength < sizeof( pcJobTopic ) ) ) + if( ( usTopicLen > 0U ) && ( usTopicLen < sizeof( pcJobTopic ) ) ) { /* Subscribe to the first of two jobs topics. */ - if( IotMqtt_TimedSubscribe( pvConnContext->pvControlClient, - &stJobsSubscription, - 1, /* Subscriptions count */ - 0, /* flags */ - OTA_SUBSCRIBE_WAIT_MS ) != IOT_MQTT_SUCCESS ) + eResult = IotMqtt_TimedSubscribe( pvConnContext->pvControlClient, + &stJobsSubscription, + 1, /* Subscriptions count */ + 0, /* flags */ + OTA_SUBSCRIBE_WAIT_MS ); + + if( eResult == IOT_MQTT_SUCCESS ) { - OTA_LOG_L1( "[%s] Failed: %s\n\r", OTA_METHOD_NAME, stJobsSubscription.pTopicFilter ); + OTA_LOG_L1( "[%s] OK: %s\n\r", OTA_METHOD_NAME, stJobsSubscription.pTopicFilter ); + /* Build the second topic. */ + usTopicLen = ( uint16_t ) snprintf( pcJobTopic, /*lint -e586 Intentionally using snprintf. */ + sizeof( pcJobTopic ), + pcOTA_JobsNotifyNext_TopicTemplate, + pxAgentCtx->pcThingName ); } else { - OTA_LOG_L1( "[%s] OK: %s\n\r", OTA_METHOD_NAME, stJobsSubscription.pTopicFilter ); - stJobsSubscription.topicFilterLength = ( uint16_t ) snprintf( pcJobTopic, /*lint -e586 Intentionally using snprintf. */ - sizeof( pcJobTopic ), - pcOTA_JobsNotifyNext_TopicTemplate, - pxAgentCtx->pcThingName ); + OTA_LOG_L1( "[%s] Failed: %s\n\r", OTA_METHOD_NAME, stJobsSubscription.pTopicFilter ); + } + } - if( ( stJobsSubscription.topicFilterLength > 0U ) && ( stJobsSubscription.topicFilterLength < sizeof( pcJobTopic ) ) ) - { - /* Subscribe to the second of two jobs topics. */ - if( IotMqtt_TimedSubscribe( pvConnContext->pvControlClient, - &stJobsSubscription, - 1, /* Subscriptions count */ - 0, /* flags */ - OTA_SUBSCRIBE_WAIT_MS ) != IOT_MQTT_SUCCESS ) - { - OTA_LOG_L1( "[%s] Failed: %s\n\r", OTA_METHOD_NAME, stJobsSubscription.pTopicFilter ); - } - else - { - OTA_LOG_L1( "[%s] OK: %s\n\r", OTA_METHOD_NAME, stJobsSubscription.pTopicFilter ); - bResult = pdTRUE; - } - } + if( ( eResult == IOT_MQTT_SUCCESS ) && ( usTopicLen > 0U ) && ( usTopicLen < sizeof( pcJobTopic ) ) ) + { + /* Subscribe to the second of two jobs topics. */ + stJobsSubscription.topicFilterLength = usTopicLen; + eResult = IotMqtt_TimedSubscribe( pvConnContext->pvControlClient, + &stJobsSubscription, + 1, /* Subscriptions count */ + 0, /* flags */ + OTA_SUBSCRIBE_WAIT_MS ); + + if( eResult == IOT_MQTT_SUCCESS ) + { + OTA_LOG_L1( "[%s] OK: %s\n\r", OTA_METHOD_NAME, stJobsSubscription.pTopicFilter ); + bResult = true; + } + else + { + OTA_LOG_L1( "[%s] Failed: %s\n\r", OTA_METHOD_NAME, stJobsSubscription.pTopicFilter ); } } @@ -207,13 +218,13 @@ static bool_t prvSubscribeToJobNotificationTopics( const OTA_AgentContext_t * px /* * UnSubscribe from the OTA data stream topic. */ -static bool_t prvUnSubscribeFromDataStream( const OTA_AgentContext_t * pxAgentCtx ) +static bool prvUnSubscribeFromDataStream( const OTA_AgentContext_t * pxAgentCtx ) { DEFINE_OTA_METHOD_NAME( "prvUnSubscribeFromDataStream" ); IotMqttSubscription_t xUnSub; - bool_t bResult = pdFALSE; + bool bResult = false; char pcOTA_RxStreamTopic[ OTA_MAX_TOPIC_LEN ]; xUnSub.qos = IOT_MQTT_QOS_0; @@ -243,7 +254,7 @@ static bool_t prvUnSubscribeFromDataStream( const OTA_AgentContext_t * pxAgentCt else { OTA_LOG_L1( "[%s] OK: %s\n\r", OTA_METHOD_NAME, pcOTA_RxStreamTopic ); - bResult = pdTRUE; + bResult = true; } } else @@ -361,6 +372,173 @@ static IotMqttError_t prvPublishMessage( const OTA_AgentContext_t * pxAgentCtx, return eResult; } +/* + * Publish a message to the job status topic. + */ +static void prvPublishStatusMessage( OTA_AgentContext_t * pxAgentCtx, + OTA_JobStatus_t eStatus, + const char * pcMsg, + uint32_t ulMsgSize, + IotMqttQos_t eQOS ) +{ + DEFINE_OTA_METHOD_NAME( "prvPublishStatusMessage" ); + + uint32_t ulTopicLen = 0; + IotMqttError_t eResult; + char pcTopicBuffer[ OTA_MAX_TOPIC_LEN ]; + + /* Try to build the dynamic job status topic . */ + ulTopicLen = ( uint32_t ) snprintf( pcTopicBuffer, /*lint -e586 Intentionally using snprintf. */ + sizeof( pcTopicBuffer ), + pcOTA_JobStatus_TopicTemplate, + pxAgentCtx->pcThingName, + pxAgentCtx->pcOTA_Singleton_ActiveJobName ); + + /* If the topic name was built, try to publish the status message to it. */ + if( ( ulTopicLen > 0UL ) && ( ulTopicLen < sizeof( pcTopicBuffer ) ) ) + { + OTA_LOG_L1( "[%s] Msg: %s\r\n", OTA_METHOD_NAME, pcMsg ); + eResult = prvPublishMessage( + pxAgentCtx, + pcTopicBuffer, + ( uint16_t ) ulTopicLen, + &pcMsg[ 0 ], + ulMsgSize, + eQOS ); + + if( eResult != IOT_MQTT_SUCCESS ) + { + OTA_LOG_L1( "[%s] Failed: %s\r\n", OTA_METHOD_NAME, pcTopicBuffer ); + } + else + { + OTA_LOG_L1( "[%s] '%s' to %s\r\n", OTA_METHOD_NAME, pcOTA_JobStatus_Strings[ eStatus ], pcTopicBuffer ); + } + } + else + { + OTA_LOG_L1( "[%s] Failed to build job status topic!\r\n", OTA_METHOD_NAME ); + } +} + +static uint32_t prvBuildStatusMessageReceiving( char * pcMsgBuffer, + size_t xMsgBufferSize, + OTA_JobStatus_t eStatus, + OTA_FileContext_t * pxOTAFileCtx ) +{ + DEFINE_OTA_METHOD_NAME( "prvBuildStatusMessageReceiving" ); + + uint32_t ulNumBlocks = 0; + uint32_t ulReceived = 0; + uint32_t ulMsgSize = 0; + + if( pxOTAFileCtx != NULL ) + { + ulNumBlocks = ( pxOTAFileCtx->ulFileSize + ( OTA_FILE_BLOCK_SIZE - 1U ) ) >> otaconfigLOG2_FILE_BLOCK_SIZE; + ulReceived = ulNumBlocks - pxOTAFileCtx->ulBlocksRemaining; + + if( ( ulReceived % OTA_UPDATE_STATUS_FREQUENCY ) == 0U ) /* Output a status update once in a while. */ + { + ulMsgSize = ( uint32_t ) snprintf( pcMsgBuffer, /*lint -e586 Intentionally using snprintf. */ + xMsgBufferSize, + pcOTA_JobStatus_StatusTemplate, + pcOTA_JobStatus_Strings[ eStatus ] ); + ulMsgSize += ( uint32_t ) snprintf( &pcMsgBuffer[ ulMsgSize ], /*lint -e586 Intentionally using snprintf. */ + xMsgBufferSize - ulMsgSize, + pcOTA_JobStatus_ReceiveDetailsTemplate, + pcOTA_String_Receive, + ulReceived, + ulNumBlocks ); + } + } + else + { + OTA_LOG_L1( "[%s] Error: null context pointer!\r\n", OTA_METHOD_NAME ); + } + + return ulMsgSize; +} + +static uint32_t prvBuildStatusMessageSelfTest( char * pcMsgBuffer, + size_t xMsgBufferSize, + OTA_JobStatus_t eStatus, + int32_t lReason ) +{ + uint32_t ulMsgSize = 0; + + ulMsgSize = ( uint32_t ) snprintf( pcMsgBuffer, /*lint -e586 Intentionally using snprintf. */ + xMsgBufferSize, + pcOTA_JobStatus_StatusTemplate, + pcOTA_JobStatus_Strings[ eStatus ] ); + ulMsgSize += ( uint32_t ) snprintf( &pcMsgBuffer[ ulMsgSize ], /*lint -e586 Intentionally using snprintf. */ + xMsgBufferSize - ulMsgSize, + pcOTA_JobStatus_SelfTestDetailsTemplate, + OTA_JSON_SELF_TEST_KEY, + pcOTA_JobReason_Strings[ lReason ], + xAppFirmwareVersion.u.ulVersion32 ); + + return ulMsgSize; +} + +static uint32_t prvBuildStatusMessageFinish( char * pcMsgBuffer, + size_t xMsgBufferSize, + OTA_JobStatus_t eStatus, + int32_t lReason, + int32_t lSubReason ) +{ + uint32_t ulMsgSize = 0; + + ulMsgSize = ( uint32_t ) snprintf( pcMsgBuffer, /*lint -e586 Intentionally using snprintf. */ + xMsgBufferSize, + pcOTA_JobStatus_StatusTemplate, + pcOTA_JobStatus_Strings[ eStatus ] ); + + /* FailedWithVal uses a numeric OTA error code and sub-reason code to cover the case where there + * may be too many description strings to reasonably include in the code. + */ + if( eStatus == eJobStatus_FailedWithVal ) + { + ulMsgSize += ( uint32_t ) snprintf( &pcMsgBuffer[ ulMsgSize ], /*lint -e586 Intentionally using snprintf. */ + xMsgBufferSize - ulMsgSize, + pcOTA_JobStatus_ReasonValTemplate, + lReason, + lSubReason ); + } + + /* If the status update is for "Succeeded," we are identifying the version of firmware + * that has been accepted. This makes it easy to find the version associated with each + * device (aka Thing) when examining the OTA jobs on the service side via the CLI or + * possibly with some console tool. + */ + else if( eStatus == eJobStatus_Succeeded ) + { + AppVersion32_t xNewVersion; + + xNewVersion.u.lVersion32 = lSubReason; + ulMsgSize += ( uint32_t ) snprintf( &pcMsgBuffer[ ulMsgSize ], /*lint -e586 Intentionally using snprintf. */ + xMsgBufferSize - ulMsgSize, + pcOTA_JobStatus_SucceededStrTemplate, + pcOTA_JobReason_Strings[ lReason ], + xNewVersion.u.x.ucMajor, + xNewVersion.u.x.ucMinor, + xNewVersion.u.x.usBuild ); + } + + /* Status updates that are NOT "InProgress" or "Succeeded" or "FailedWithVal" map status and + * reason codes to a string plus a sub-reason code. + */ + else + { + ulMsgSize += ( uint32_t ) snprintf( &pcMsgBuffer[ ulMsgSize ], /*lint -e586 Intentionally using snprintf. */ + xMsgBufferSize - ulMsgSize, + pcOTA_JobStatus_ReasonStrTemplate, + pcOTA_JobReason_Strings[ lReason ], + lSubReason ); + } + + return ulMsgSize; +} + /* * This function queues callback events for processing. */ @@ -466,7 +644,7 @@ OTA_Err_t prvRequestJob_Mqtt( OTA_AgentContext_t * pxAgentCtx ) char pcMsg[ CONST_STRLEN( pcOTA_GetNextJob_MsgTemplate ) + U32_MAX_PLACES + otaconfigMAX_THINGNAME_LEN ]; /* Subscribe to the OTA job notification topic. */ - if( prvSubscribeToJobNotificationTopics( pxAgentCtx ) == ( bool_t ) pdTRUE ) + if( prvSubscribeToJobNotificationTopics( pxAgentCtx ) ) { OTA_LOG_L1( "[%s] Request #%u\r\n", OTA_METHOD_NAME, ulReqCounter ); /*lint -e586 Intentionally using snprintf. */ @@ -523,170 +701,49 @@ OTA_Err_t prvUpdateJobStatus_Mqtt( OTA_AgentContext_t * pxAgentCtx, { DEFINE_OTA_METHOD_NAME( "prvUpdateJobStatus_Mqtt" ); - uint32_t ulTopicLen, ulNumBlocks, ulReceived, ulMsgSize; - IotMqttError_t eResult; - IotMqttQos_t eQOS; - char pcMsg[ OTA_STATUS_MSG_MAX_SIZE ]; - char pcTopicBuffer[ OTA_MAX_TOPIC_LEN ]; - - /* - * Get the current file context. - */ - OTA_FileContext_t * C = &( pxAgentCtx->pxOTA_Files[ pxAgentCtx->ulFileIndex ] ); - - /* All job state transitions except streaming progress use QOS 1 since it is required to have status in the job document. */ - eQOS = IOT_MQTT_QOS_1; - /* A message size of zero means don't publish anything. */ - ulMsgSize = 0UL; + uint32_t ulMsgSize = 0; + /* All job state transitions except streaming progress use QOS 1 since it is required to have status in the job document. */ + IotMqttQos_t eQOS = IOT_MQTT_QOS_1; + char pcMsg[ OTA_STATUS_MSG_MAX_SIZE ]; + + /* Get the current file context. */ + OTA_FileContext_t * C = &( pxAgentCtx->pxOTA_Files[ pxAgentCtx->ulFileIndex ] ); if( eStatus == eJobStatus_InProgress ) { if( lReason == ( int32_t ) eJobReason_Receiving ) { - if( C != NULL ) - { - ulNumBlocks = ( C->ulFileSize + ( OTA_FILE_BLOCK_SIZE - 1U ) ) >> otaconfigLOG2_FILE_BLOCK_SIZE; - ulReceived = ulNumBlocks - C->ulBlocksRemaining; + ulMsgSize = prvBuildStatusMessageReceiving( pcMsg, sizeof( pcMsg ), eStatus, C ); - if( ( ulReceived % OTA_UPDATE_STATUS_FREQUENCY ) == 0U ) /* Output a status update once in a while. */ - { - /* Downgrade Progress updates to QOS 0 to avoid overloading MQTT buffers during active streaming. */ - eQOS = IOT_MQTT_QOS_0; - ulMsgSize = ( uint32_t ) snprintf( pcMsg, /*lint -e586 Intentionally using snprintf. */ - sizeof( pcMsg ), - pcOTA_JobStatus_StatusTemplate, - pcOTA_JobStatus_Strings[ eStatus ] ); - ulMsgSize += ( uint32_t ) snprintf( &pcMsg[ ulMsgSize ], /*lint -e586 Intentionally using snprintf. */ - sizeof( pcMsg ) - ulMsgSize, - pcOTA_JobStatus_ReceiveDetailsTemplate, - pcOTA_String_Receive, - ulReceived, - ulNumBlocks ); - } - else - { /* Don't send a status update yet. */ - ulMsgSize = 0UL; - } - } - else + if( ulMsgSize > 0 ) { - /* Can't send a status update without data from the OTA context. Some calls intentionally - * don't use a context structure but never with this reason code so log this error. */ - OTA_LOG_L1( "[%s] Error: null context pointer!\r\n", OTA_METHOD_NAME ); - ulMsgSize = 0UL; + /* Downgrade Progress updates to QOS 0 to avoid overloading MQTT buffers during active streaming. */ + eQOS = IOT_MQTT_QOS_0; } } else { /* We're no longer receiving but we're still In Progress so we are implicitly in the Self * Test phase. Prepare to update the job status with the self_test phase (ready or active). */ - ulMsgSize = ( uint32_t ) snprintf( pcMsg, /*lint -e586 Intentionally using snprintf. */ - sizeof( pcMsg ), - pcOTA_JobStatus_StatusTemplate, - pcOTA_JobStatus_Strings[ eStatus ] ); - ulMsgSize += ( uint32_t ) snprintf( &pcMsg[ ulMsgSize ], /*lint -e586 Intentionally using snprintf. */ - sizeof( pcMsg ) - ulMsgSize, - pcOTA_JobStatus_SelfTestDetailsTemplate, - pcOTA_JSON_SelfTestKey, - pcOTA_JobReason_Strings[ lReason ], - xAppFirmwareVersion.u.ulVersion32 ); + ulMsgSize = prvBuildStatusMessageSelfTest( pcMsg, sizeof( pcMsg ), eStatus, lReason ); } } else { if( eStatus < eNumJobStatusMappings ) { - /* Status updates that are NOT "IN PROGRESS" or "SUCCEEDED" map status and reason codes - * to a string plus a sub-reason code except for FailedWithVal status. FailedWithVal uses - * a numeric OTA error code and sub-reason code to cover the case where there may be too - * many description strings to reasonably include in the code. - */ - ulMsgSize = ( uint32_t ) snprintf( pcMsg, /*lint -e586 Intentionally using snprintf. */ - sizeof( pcMsg ), - pcOTA_JobStatus_StatusTemplate, - pcOTA_JobStatus_Strings[ eStatus ] ); - - if( eStatus == eJobStatus_FailedWithVal ) - { - ulMsgSize += ( uint32_t ) snprintf( &pcMsg[ ulMsgSize ], /*lint -e586 Intentionally using snprintf. */ - sizeof( pcMsg ) - ulMsgSize, - pcOTA_JobStatus_ReasonValTemplate, - lReason, - lSubReason ); - } - - /* If the status update is for "SUCCEEDED," we are identifying the version of firmware - * that has been accepted. This makes it easy to find the version associated with each - * device (aka Thing) when examining the OTA jobs on the service side via the CLI or - * possibly with some console tool. - */ - else if( eStatus == eJobStatus_Succeeded ) - { - AppVersion32_t xNewVersion; - - xNewVersion.u.lVersion32 = lSubReason; - ulMsgSize += ( uint32_t ) snprintf( &pcMsg[ ulMsgSize ], /*lint -e586 Intentionally using snprintf. */ - sizeof( pcMsg ) - ulMsgSize, - pcOTA_JobStatus_SucceededStrTemplate, - pcOTA_JobReason_Strings[ lReason ], - xNewVersion.u.x.ucMajor, - xNewVersion.u.x.ucMinor, - xNewVersion.u.x.usBuild ); - } - else - { - ulMsgSize += ( uint32_t ) snprintf( &pcMsg[ ulMsgSize ], /*lint -e586 Intentionally using snprintf. */ - sizeof( pcMsg ) - ulMsgSize, - pcOTA_JobStatus_ReasonStrTemplate, - pcOTA_JobReason_Strings[ lReason ], - lSubReason ); - } + ulMsgSize = prvBuildStatusMessageFinish( pcMsg, sizeof( pcMsg ), eStatus, lReason, lSubReason ); } else - { /* Unknown status code. Just ignore it. */ - ulMsgSize = 0UL; + { + OTA_LOG_L1( "[%s] Unknown status code: %d\r\n", OTA_METHOD_NAME, eStatus ); } } if( ulMsgSize > 0UL ) { - /* Try to build the dynamic job status topic . */ - ulTopicLen = ( uint32_t ) snprintf( pcTopicBuffer, /*lint -e586 Intentionally using snprintf. */ - sizeof( pcTopicBuffer ), - pcOTA_JobStatus_TopicTemplate, - pxAgentCtx->pcThingName, - pxAgentCtx->pcOTA_Singleton_ActiveJobName ); - - /* If the topic name was built, try to publish the status message to it. */ - if( ( ulTopicLen > 0UL ) && ( ulTopicLen < sizeof( pcTopicBuffer ) ) ) - { - OTA_LOG_L1( "[%s] Msg: %s\r\n", OTA_METHOD_NAME, pcMsg ); - eResult = prvPublishMessage( - pxAgentCtx, - pcTopicBuffer, - ( uint16_t ) ulTopicLen, - &pcMsg[ 0 ], - ulMsgSize, - eQOS ); - - if( eResult != IOT_MQTT_SUCCESS ) - { - OTA_LOG_L1( "[%s] Failed: %s\r\n", OTA_METHOD_NAME, pcTopicBuffer ); - } - else - { - OTA_LOG_L1( "[%s] '%s' to %s\r\n", OTA_METHOD_NAME, pcOTA_JobStatus_Strings[ eStatus ], pcTopicBuffer ); - } - } - else - { - OTA_LOG_L1( "[%s] Failed to build job status topic!\r\n", OTA_METHOD_NAME ); - } - } - else - { - /* Just ignore the zero length message. */ + prvPublishStatusMessage( pxAgentCtx, eStatus, pcMsg, ulMsgSize, eQOS ); } return kOTA_Err_None; @@ -745,11 +802,12 @@ OTA_Err_t prvRequestFileBlock_Mqtt( OTA_AgentContext_t * pxAgentCtx ) { DEFINE_OTA_METHOD_NAME( "prvRequestFileBlock_Mqtt" ); - uint32_t ulMsgSizeToPublish; size_t xMsgSizeFromStream; - uint32_t ulNumBlocks, ulBitmapLen, ulTopicLen; - IotMqttError_t eResult; - OTA_Err_t xErr = kOTA_Err_None; + uint32_t ulNumBlocks, ulBitmapLen; + uint32_t ulMsgSizeToPublish = 0; + uint32_t ulTopicLen = 0; + IotMqttError_t eResult = IOT_MQTT_STATUS_PENDING; + OTA_Err_t xErr = kOTA_Err_Uninitialized; char pcMsg[ OTA_REQUEST_MSG_MAX_SIZE ]; char pcTopicBuffer[ OTA_MAX_TOPIC_LEN ]; @@ -778,54 +836,7 @@ OTA_Err_t prvRequestFileBlock_Mqtt( OTA_AgentContext_t * pxAgentCtx ) ulBitmapLen, otaconfigMAX_NUM_BLOCKS_REQUEST ) ) { - ulMsgSizeToPublish = ( uint32_t ) xMsgSizeFromStream; - - /* Try to build the dynamic data REQUEST topic and subscribe to it. */ - ulTopicLen = ( uint32_t ) snprintf( pcTopicBuffer, /*lint -e586 Intentionally using snprintf. */ - sizeof( pcTopicBuffer ), - pcOTA_GetStream_TopicTemplate, - pxAgentCtx->pcThingName, - ( const char * ) C->pucStreamName ); - - if( ( ulTopicLen > 0U ) && ( ulTopicLen < sizeof( pcTopicBuffer ) ) ) - { - eResult = prvPublishMessage( - pxAgentCtx, - pcTopicBuffer, - ( uint16_t ) ulTopicLen, - &pcMsg[ 0 ], - ulMsgSizeToPublish, - IOT_MQTT_QOS_0 ); - - if( eResult != IOT_MQTT_SUCCESS ) - { - OTA_LOG_L1( "[%s] Failed: %s\r\n", OTA_METHOD_NAME, pcTopicBuffer ); - xErr = kOTA_Err_PublishFailed; - } - else - { - OTA_LOG_L1( "[%s] OK: %s\r\n", OTA_METHOD_NAME, pcTopicBuffer ); - } - - /* Restart the timer regardless if we published the Get Stream Request message - * or not. - * - * If we published the message, then the timer will be used to time - * out the OTA not continuing again. - * - * If we failed to publish the message, then - * the timer will be used to retry publishing the message again later. - * - * In both cases the max momentum, if reached, will be used to stop publishing - * the Get Stream Request message. */ - /*prvStartRequestTimer(C);*/ - } - else - { - /* 0 should never happen since we supply the format strings. It must be overflow. */ - OTA_LOG_L1( "[%s] Failed to build stream topic!\r\n", OTA_METHOD_NAME ); - xErr = kOTA_Err_TopicTooLarge; - } + xErr = kOTA_Err_None; } else { @@ -834,6 +845,51 @@ OTA_Err_t prvRequestFileBlock_Mqtt( OTA_AgentContext_t * pxAgentCtx ) } } + if( xErr == kOTA_Err_None ) + { + ulMsgSizeToPublish = ( uint32_t ) xMsgSizeFromStream; + + /* Try to build the dynamic data REQUEST topic to publish to. */ + ulTopicLen = ( uint32_t ) snprintf( pcTopicBuffer, /*lint -e586 Intentionally using snprintf. */ + sizeof( pcTopicBuffer ), + pcOTA_GetStream_TopicTemplate, + pxAgentCtx->pcThingName, + ( const char * ) C->pucStreamName ); + + if( ( ulTopicLen > 0U ) && ( ulTopicLen < sizeof( pcTopicBuffer ) ) ) + { + xErr = kOTA_Err_None; + } + else + { + /* 0 should never happen since we supply the format strings. It must be overflow. */ + OTA_LOG_L1( "[%s] Failed to build stream topic!\r\n", OTA_METHOD_NAME ); + xErr = kOTA_Err_TopicTooLarge; + } + } + + if( xErr == kOTA_Err_None ) + { + eResult = prvPublishMessage( + pxAgentCtx, + pcTopicBuffer, + ( uint16_t ) ulTopicLen, + &pcMsg[ 0 ], + ulMsgSizeToPublish, + IOT_MQTT_QOS_0 ); + + if( eResult != IOT_MQTT_SUCCESS ) + { + OTA_LOG_L1( "[%s] Failed: %s\r\n", OTA_METHOD_NAME, pcTopicBuffer ); + xErr = kOTA_Err_PublishFailed; + } + else + { + OTA_LOG_L1( "[%s] OK: %s\r\n", OTA_METHOD_NAME, pcTopicBuffer ); + xErr = kOTA_Err_None; + } + } + return xErr; } diff --git a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_mqtt.h b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_mqtt.h index 6c92993d6c..91cd09a63d 100755 --- a/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_mqtt.h +++ b/FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/aws/ota/src/mqtt/aws_iot_ota_mqtt.h @@ -1,5 +1,5 @@ /* - * FreeRTOS OTA V1.1.1 + * FreeRTOS OTA V1.2.0 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/tools/memory_estimator/config_files/aws_ota_agent_config.h b/tools/memory_estimator/config_files/aws_ota_agent_config.h index 8b1595a253..17c5921e19 100644 --- a/tools/memory_estimator/config_files/aws_ota_agent_config.h +++ b/tools/memory_estimator/config_files/aws_ota_agent_config.h @@ -104,6 +104,16 @@ */ #define otaconfigMAX_NUM_OTA_DATA_BUFFERS 2U +/** + * @brief Allow update to same or lower version. + * + * Set this to 1 to allow downgrade or same version update. This configurations parameter + * disables version check and allows update to a same or lower version.This is provided for + * testing purpose and it is recommended to always update to higher version and keep this + * configuration disabled. + */ +#define otaconfigAllowDowngrade 0U + /** * @brief The protocol selected for OTA control operations.