From 3061ecca3d0fdfb87dabbf5f63c9e06c2a30f53a Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Thu, 23 Aug 2018 17:08:59 +0200 Subject: o Initial import. --- .../iot/background_dfu/background_dfu_state.c | 835 +++++++++++++++++++++ 1 file changed, 835 insertions(+) create mode 100644 thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_state.c (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_state.c') diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_state.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_state.c new file mode 100644 index 0000000..5004558 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_state.c @@ -0,0 +1,835 @@ +/** + * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/** @file + * + * @defgroup background_dfu_state background_dfu_state.c + * @{ + * @ingroup background_dfu + * @brief Background DFU state management. + * + */ + +#include "background_dfu_state.h" + +#include + +#include "sdk_config.h" +#include "app_timer.h" +#include "compiler_abstraction.h" +#include "nrf_dfu_types.h" +#include "nrf_dfu_settings.h" +#include "sha256.h" +#include "background_dfu_transport.h" +#include "background_dfu_operation.h" + +#define NRF_LOG_MODULE_NAME background_dfu + +#define NRF_LOG_LEVEL BACKGROUND_DFU_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR BACKGROUND_DFU_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR BACKGROUND_DFU_CONFIG_DEBUG_COLOR + +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define BLOCK_REQUEST_JITTER_MIN 200 /**< Minimum jitter value when sending bitmap with requested blocks in multicast DFU. */ +#define BLOCK_REQUEST_JITTER_MAX 2000 /**< Maximum jitter value when sending bitmap with requested blocks in multicast DFU. */ +#define BLOCK_RECEIVE_TIMEOUT 2000 /**< Timeout value after which block is considered missing in multicast DFU. */ + +#define DFU_DATE_TIME (__DATE__ " " __TIME__) + +/**@brief DFU trigger packet version. */ +#define TRIGGER_VERSION 1 + +/** + * @defgroup background_dfu_trigger_flags Trigger flags and offsets. + * @{ + */ +#define TRIGGER_FLAGS_VERSION_OFFSET 4 +#define TRIGGER_FLAGS_VERSION_MASK 0xF0 +#define TRIGGER_FLAGS_MODE_OFFSET 3 +#define TRIGGER_FLAGS_MODE_MASK 0x08 +#define TRIGGER_FLAGS_RESET_OFFSET 2 +#define TRIGGER_FLAGS_RESET_MASK 0x04 +/** @} */ + +APP_TIMER_DEF(m_missing_block_timer); +APP_TIMER_DEF(m_block_timeout_timer); + +/**@brief Defines how many retries are performed in case no response is received. */ +#define DEFAULT_RETRIES 3 + +/**@brief DFU error handler. + * + * @param[inout] p_dfu_ctx DFU context. + */ +static __INLINE void dfu_handle_error(background_dfu_context_t * p_dfu_ctx) +{ + p_dfu_ctx->dfu_state = BACKGROUND_DFU_ERROR; + + background_dfu_handle_error(); +} + +/**@brief Get randomized jitter value. + * + * @return Randomized jitter value between BLOCK_REQUEST_JITTER_MIN and BLOCK_REQUEST_JITTER_MAX. + */ +static __INLINE uint32_t block_request_jitter_get(void) +{ + return BLOCK_REQUEST_JITTER_MIN + (background_dfu_random() % + (BLOCK_REQUEST_JITTER_MAX - BLOCK_REQUEST_JITTER_MIN)); +} + +/**@brief Starts block timeout timer. + * + * @param[inout] p_dfu_ctx DFU context. + */ +static __INLINE void start_block_timeout_timer(background_dfu_context_t * p_dfu_ctx) +{ + uint32_t err_code = app_timer_start(m_block_timeout_timer, + APP_TIMER_TICKS(BLOCK_RECEIVE_TIMEOUT), + p_dfu_ctx); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in app_timer_start (%d)", err_code); + } +} + +/**@brief Stops block timeout timer. + * + * @param[inout] p_dfu_ctx DFU context. + */ +static __INLINE void stop_block_timeout_timer(background_dfu_context_t * p_dfu_ctx) +{ + UNUSED_PARAMETER(p_dfu_ctx); + uint32_t err_code = app_timer_stop(m_block_timeout_timer); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in app_timer_stop (%d)", err_code); + } +} + +/**@brief Restarts block timeout timer. + * + * @param[inout] p_dfu_ctx DFU context. + */ +static __INLINE void restart_block_timeout_timer(background_dfu_context_t * p_dfu_ctx) +{ + stop_block_timeout_timer(p_dfu_ctx); + start_block_timeout_timer(p_dfu_ctx); +} + +/*************************************************************************************************** + * @section Handle DFU Trigger + **************************************************************************************************/ + +/**@brief Parses trigger data and updates DFU client context accordingly. + * + * @param[inout] p_dfu_ctx A pointer to DFU Client context. + * @param[in] p_trigger A pointer to trigger data. + * + * @return True if parsing was successful, false otherwise. + */ +static bool parse_trigger(background_dfu_context_t * p_dfu_ctx, + const background_dfu_trigger_t * p_trigger) +{ + uint8_t trigger_version = (p_trigger->flags & TRIGGER_FLAGS_VERSION_MASK) + >> TRIGGER_FLAGS_VERSION_OFFSET; + + if (trigger_version <= TRIGGER_VERSION) + { + // Base fields available from version 0. + p_dfu_ctx->init_cmd_size = uint32_big_decode((const uint8_t *)&p_trigger->init_length); + p_dfu_ctx->init_cmd_crc = uint32_big_decode((const uint8_t *)&p_trigger->init_crc); + p_dfu_ctx->firmware_size = uint32_big_decode((const uint8_t *)&p_trigger->image_length); + p_dfu_ctx->firmware_crc = uint32_big_decode((const uint8_t *)&p_trigger->image_crc); + + // Mode flag was added in DFU Trigger version 1. + if (trigger_version >= 1) + { + p_dfu_ctx->dfu_mode = (background_dfu_mode_t)((p_trigger->flags + & TRIGGER_FLAGS_MODE_MASK) >> TRIGGER_FLAGS_MODE_OFFSET); + p_dfu_ctx->reset_suppress = (p_trigger->flags & TRIGGER_FLAGS_RESET_MASK) >> + TRIGGER_FLAGS_RESET_OFFSET; + + } + else + { + p_dfu_ctx->dfu_mode = BACKGROUND_DFU_MODE_UNICAST; + } + + NRF_LOG_INFO("DFU trigger: init (sz=%d, crc=%0X) image (sz=%d, crc=%0X)", + p_dfu_ctx->init_cmd_size, + p_dfu_ctx->init_cmd_crc, + p_dfu_ctx->firmware_size, + p_dfu_ctx->firmware_crc); + + return true; + } + + return false; +} + +bool background_dfu_validate_trigger(background_dfu_context_t * p_dfu_ctx, + const uint8_t * p_payload, + uint32_t payload_len) +{ + if (payload_len != sizeof(background_dfu_trigger_t)) + { + NRF_LOG_ERROR("Validate trigger: size mismatch"); + return false; + } + + if ((p_dfu_ctx->dfu_state != BACKGROUND_DFU_IDLE) && + (p_dfu_ctx->dfu_state != BACKGROUND_DFU_DOWNLOAD_TRIG)) + { + NRF_LOG_ERROR("Validate trigger: DFU already in progress (s:%s).", + (uint32_t)background_dfu_state_to_string(p_dfu_ctx->dfu_state)); + return false; + } + + uint8_t trigger_version = (((background_dfu_trigger_t *)p_payload)->flags + & TRIGGER_FLAGS_VERSION_MASK) >> TRIGGER_FLAGS_VERSION_OFFSET; + if (trigger_version > TRIGGER_VERSION) + { + NRF_LOG_ERROR("Validate trigger: invalid trigger version."); + return false; + } + + return true; +} + +bool background_dfu_process_trigger(background_dfu_context_t * p_dfu_ctx, + const uint8_t * p_payload, + uint32_t payload_len) +{ + bool result = false; + + do + { + if (!parse_trigger(p_dfu_ctx, (background_dfu_trigger_t *)p_payload)) + { + NRF_LOG_ERROR("Process trigger: failed to parse payload"); + break; + } + + p_dfu_ctx->dfu_state = BACKGROUND_DFU_DOWNLOAD_TRIG; + + uint32_t err_code = background_dfu_handle_event(p_dfu_ctx, + BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in background_dfu_handle_event (%d)", err_code); + } + + result = true; + } while(0); + + return result; +} + +/*************************************************************************************************** + * @section DFU checks + **************************************************************************************************/ + +background_dfu_block_result_t background_dfu_process_block(background_dfu_context_t * p_dfu_ctx, + const background_dfu_block_t * p_block) +{ + background_dfu_block_result_t result = block_manager_block_process(&p_dfu_ctx->block_manager, + p_block); + uint32_t err_code = NRF_SUCCESS; + + switch (result) + { + case BACKGROUND_DFU_BLOCK_IGNORE: + // Ignore. + if (p_dfu_ctx->dfu_mode == BACKGROUND_DFU_MODE_MULTICAST) + { + restart_block_timeout_timer(p_dfu_ctx); + } + + break; + + case BACKGROUND_DFU_BLOCK_SUCCESS: + // Intentionally empty. + break; + + default: + err_code = background_dfu_handle_event(p_dfu_ctx, BACKGROUND_DFU_EVENT_PROCESSING_ERROR); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in background_dfu_handle_event (%d)", err_code); + } + + break; + } + + return result; +} + +/**@brief Check if installed image is different from the incoming one. + * + * @param[in] p_dfu_ctx A pointer to DFU client context. + * + * @return True if image different, false otherwise. + * + */ +static bool is_image_different(const background_dfu_context_t * p_dfu_ctx) +{ + if (s_dfu_settings.bank_0.bank_code == NRF_DFU_BANK_INVALID) + { + NRF_LOG_WARNING("No image in bank 0"); + return true; + } + + if (s_dfu_settings.bank_0.image_crc != p_dfu_ctx->firmware_crc) + { + NRF_LOG_WARNING("Installed image CRC is different"); + return true; + } + + return false; +} + +/** + * @brief A callback function for block manager. + */ +static void dfu_block_manager_result_handler(background_dfu_block_result_t result, void * p_context) +{ + background_dfu_context_t * p_dfu_ctx = p_context; + uint32_t err_code; + + if (result == BACKGROUND_DFU_BLOCK_SUCCESS) + { + if (p_dfu_ctx->dfu_mode == BACKGROUND_DFU_MODE_MULTICAST) + { + restart_block_timeout_timer(p_dfu_ctx); + } + + if (block_manager_is_image_complete(&p_dfu_ctx->block_manager)) + { + err_code = background_dfu_handle_event(p_dfu_ctx, + BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in background_dfu_handle_event (%d)", err_code); + } + } + else + { + // FIXME I don't like it here. + p_dfu_ctx->block_num++; + + err_code = background_dfu_handle_event(p_dfu_ctx, + BACKGROUND_DFU_EVENT_TRANSFER_CONTINUE); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in background_dfu_handle_event (%d)", err_code); + } + } + } + else + { + err_code = background_dfu_handle_event(p_dfu_ctx, BACKGROUND_DFU_EVENT_PROCESSING_ERROR); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in background_dfu_handle_event (%d)", err_code); + } + } +} + +/** + * @brief Prepare state machine to download init command. + */ +static void setup_download_init_command(background_dfu_context_t * p_dfu_ctx) +{ + p_dfu_ctx->p_resource_size = &p_dfu_ctx->init_cmd_size; + p_dfu_ctx->retry_count = DEFAULT_RETRIES; + p_dfu_ctx->block_num = 0; + + background_dfu_transport_state_update(p_dfu_ctx); + + block_manager_init(&p_dfu_ctx->block_manager, + p_dfu_ctx->dfu_state, + *p_dfu_ctx->p_resource_size, + p_dfu_ctx->block_num, + dfu_block_manager_result_handler, + p_dfu_ctx); + + if (p_dfu_ctx->dfu_mode == BACKGROUND_DFU_MODE_MULTICAST) + { + NRF_LOG_INFO("Init complete. Multicast Mode."); + uint32_t jitter = block_request_jitter_get(); + uint32_t err_code = app_timer_start(m_missing_block_timer, + APP_TIMER_TICKS(jitter), + p_dfu_ctx); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in app_timer_start (%d)", err_code); + } + } + else + { + NRF_LOG_INFO("Init complete. Unicast Mode."); + } +} + +/** + * @brief A callback function for DFU command operations. + */ +static void dfu_init_check_callback(nrf_dfu_response_t * p_res, void * p_context) +{ + background_dfu_context_t * p_dfu_ctx = (background_dfu_context_t *)p_context; + + switch (p_res->request) + { + case NRF_DFU_OP_OBJECT_SELECT: + if (p_res->result != NRF_DFU_RES_CODE_SUCCESS) + { + NRF_LOG_ERROR("No valid init command - select failed"); + setup_download_init_command((background_dfu_context_t *)p_context); + + UNUSED_RETURN_VALUE(background_dfu_handle_event(p_dfu_ctx, BACKGROUND_DFU_EVENT_TRANSFER_CONTINUE)); + } + + p_dfu_ctx->max_obj_size = p_res->select.max_size; + p_dfu_ctx->block_num = p_res->select.offset / DEFAULT_BLOCK_SIZE; + + if (background_dfu_op_execute(dfu_init_check_callback, p_context) != NRF_SUCCESS) + { + NRF_LOG_ERROR("No valid init command - execute error"); + setup_download_init_command((background_dfu_context_t *)p_context); + + UNUSED_RETURN_VALUE(background_dfu_handle_event(p_dfu_ctx, BACKGROUND_DFU_EVENT_TRANSFER_CONTINUE)); + } + + break; + + case NRF_DFU_OP_OBJECT_EXECUTE: + if ((p_res->result != NRF_DFU_RES_CODE_SUCCESS) || + (s_dfu_settings.progress.command_crc != p_dfu_ctx->init_cmd_crc)) + { + NRF_LOG_ERROR("Init commad has changed"); + p_dfu_ctx->remaining_size = 0; + setup_download_init_command((background_dfu_context_t *)p_context); + + UNUSED_RETURN_VALUE(background_dfu_handle_event(p_dfu_ctx, BACKGROUND_DFU_EVENT_TRANSFER_CONTINUE)); + } + else + { + // Valid init command stored, download firmware. + p_dfu_ctx->dfu_diag.state = BACKGROUND_DFU_DOWNLOAD_INIT_CMD; + + UNUSED_RETURN_VALUE(background_dfu_handle_event(p_dfu_ctx, BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE)); + } + + break; + + default: + ASSERT(false); + } +} + +/** + * @brief A callback function for DFU data operation. + */ +static void dfu_data_select_callback(nrf_dfu_response_t * p_res, void * p_context) +{ + ASSERT(p_res->request == NRF_DFU_OP_OBJECT_SELECT); + + background_dfu_context_t * p_dfu_ctx = (background_dfu_context_t *)p_context; + if (p_res->result != NRF_DFU_RES_CODE_SUCCESS) + { + NRF_LOG_ERROR("Select failed"); + dfu_handle_error(p_dfu_ctx); + return; + } + + p_dfu_ctx->dfu_state = BACKGROUND_DFU_DOWNLOAD_FIRMWARE; + p_dfu_ctx->p_resource_size = &p_dfu_ctx->firmware_size; + p_dfu_ctx->retry_count = DEFAULT_RETRIES; + p_dfu_ctx->block_num = (p_res->select.offset / DEFAULT_BLOCK_SIZE); + p_dfu_ctx->max_obj_size = p_res->select.max_size; + + background_dfu_transport_state_update(p_dfu_ctx); + + block_manager_init(&p_dfu_ctx->block_manager, + p_dfu_ctx->dfu_state, + *p_dfu_ctx->p_resource_size, + p_dfu_ctx->block_num, + dfu_block_manager_result_handler, + p_dfu_ctx); + + UNUSED_RETURN_VALUE(background_dfu_handle_event(p_dfu_ctx, BACKGROUND_DFU_EVENT_TRANSFER_CONTINUE)); +} + +/*************************************************************************************************** + * @section Timer handlers + **************************************************************************************************/ + +/**@brief Handler function for block request timer. + * + * @param[inout] p_context DFU context. + */ +static void block_request_handler(void * p_context) +{ + background_dfu_context_t * p_dfu_ctx = (background_dfu_context_t *)p_context; + + if ((p_dfu_ctx->dfu_state != BACKGROUND_DFU_DOWNLOAD_FIRMWARE) && + (p_dfu_ctx->dfu_state != BACKGROUND_DFU_DOWNLOAD_INIT_CMD)) + { + return; + } + + background_dfu_request_bitmap_t req_bmp; + if (block_manager_request_bitmap_get(&p_dfu_ctx->block_manager, &req_bmp) && + (req_bmp.size > 0)) + { + background_dfu_transport_block_request_send(p_dfu_ctx, &req_bmp); + } + + // Reschedule the timer. + uint32_t jitter = block_request_jitter_get(); + uint32_t err_code = app_timer_start(m_missing_block_timer, APP_TIMER_TICKS(jitter), p_dfu_ctx); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in app_timer_start (%d)", err_code); + } +} + +/**@brief Handler function for block timeout timer. + * + * @param[inout] p_context DFU context. + */ +static void block_timeout_handler(void * p_context) +{ + background_dfu_context_t * p_dfu_ctx = (background_dfu_context_t *)p_context; + + NRF_LOG_INFO("Block timeout! (b: %d)", + block_manager_get_current_block(&p_dfu_ctx->block_manager)); + + if ((p_dfu_ctx->dfu_state != BACKGROUND_DFU_DOWNLOAD_FIRMWARE) && + (p_dfu_ctx->dfu_state != BACKGROUND_DFU_DOWNLOAD_INIT_CMD)) + { + return; + } + + if (block_manager_increment_current_block(&p_dfu_ctx->block_manager)) + { + start_block_timeout_timer(p_dfu_ctx); + } +} + +/*************************************************************************************************** + * @section API functions + **************************************************************************************************/ + +/** @brief Helper function converting DFU state to string. + * + * @param[in] state DFU client state. + * + * @return A pointer to null terminated string with state name. + */ +const char * background_dfu_state_to_string(const background_dfu_state_t state) +{ + static const char * const names[] = + { + "DFU_DOWNLOAD_INIT_CMD", + "DFU_DOWNLOAD_FIRMWARE", + "DFU_DOWNLOAD_TRIG", + "DFU_WAIT_FOR_RESET", + "DFU_IDLE", + "DFU_ERROR", + }; + + return names[(uint32_t)state - BACKGROUND_DFU_DOWNLOAD_INIT_CMD]; +} + +/** @brief Helper function convering DFU event name to string. + * + * @param[in] state DFU client event. + * + * @return A pointer to null terminated string with event name. + */ +const char * background_dfu_event_to_string(const background_dfu_event_t event) +{ + static const char * const names[] = { + "DFU_EVENT_TRANSFER_COMPLETE", + "DFU_EVENT_TRANSFER_CONTINUE", + "DFU_EVENT_TRANSFER_ERROR", + "DFU_EVENT_PROCESSING_ERROR", + }; + + return names[event]; +} + +uint32_t background_dfu_handle_event(background_dfu_context_t * p_dfu_ctx, + background_dfu_event_t event) +{ + uint32_t err_code = NRF_SUCCESS; + + NRF_LOG_INFO("state=%s event=%s", + (uint32_t)background_dfu_state_to_string(p_dfu_ctx->dfu_state), + (uint32_t)background_dfu_event_to_string(event)); + + switch (p_dfu_ctx->dfu_state) + { + case BACKGROUND_DFU_IDLE: + { + if (event == BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE) + { + p_dfu_ctx->dfu_diag.prev_state = BACKGROUND_DFU_IDLE; + + p_dfu_ctx->dfu_state = BACKGROUND_DFU_DOWNLOAD_TRIG; + p_dfu_ctx->block_num = 0; + p_dfu_ctx->retry_count = DEFAULT_RETRIES; + + background_dfu_transport_state_update(p_dfu_ctx); + } + + break; + } + + case BACKGROUND_DFU_DOWNLOAD_TRIG: + { + if (event == BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE) + { + if (!is_image_different(p_dfu_ctx)) + { + NRF_LOG_INFO("Image is already installed"); + background_dfu_reset_state(p_dfu_ctx); + break; + } + + p_dfu_ctx->dfu_diag.prev_state = BACKGROUND_DFU_DOWNLOAD_TRIG; + + p_dfu_ctx->dfu_state = BACKGROUND_DFU_DOWNLOAD_INIT_CMD; + + // Initiate init command check procedure. + if (background_dfu_op_select(NRF_DFU_OBJ_TYPE_COMMAND, + dfu_init_check_callback, + p_dfu_ctx) != NRF_SUCCESS) + { + NRF_LOG_ERROR("No valid init command - select error"); + setup_download_init_command(p_dfu_ctx); + } + else + { + // We wait for dfu request to finish - do not send anything. + return NRF_SUCCESS; + } + } + + break; + } + + case BACKGROUND_DFU_DOWNLOAD_INIT_CMD: + { + if (event == BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE) + { + p_dfu_ctx->dfu_diag.prev_state = BACKGROUND_DFU_DOWNLOAD_INIT_CMD; + + if (p_dfu_ctx->dfu_mode == BACKGROUND_DFU_MODE_MULTICAST) + { + stop_block_timeout_timer(p_dfu_ctx); + } + + if (background_dfu_op_select(NRF_DFU_OBJ_TYPE_DATA, + dfu_data_select_callback, + p_dfu_ctx) != NRF_SUCCESS) + { + NRF_LOG_ERROR("Select failed"); + dfu_handle_error(p_dfu_ctx); + err_code = NRF_ERROR_INTERNAL; + } + else + { + return NRF_SUCCESS; + } + } + else if (event == BACKGROUND_DFU_EVENT_PROCESSING_ERROR) + { + p_dfu_ctx->dfu_diag.prev_state = BACKGROUND_DFU_DOWNLOAD_INIT_CMD; + + if (p_dfu_ctx->dfu_mode == BACKGROUND_DFU_MODE_MULTICAST) + { + stop_block_timeout_timer(p_dfu_ctx); + } + + NRF_LOG_ERROR("Processing error while downloading init command."); + dfu_handle_error(p_dfu_ctx); + } + break; + } + + case BACKGROUND_DFU_DOWNLOAD_FIRMWARE: + { + if (event == BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE) + { + p_dfu_ctx->dfu_diag.prev_state = BACKGROUND_DFU_DOWNLOAD_FIRMWARE; + + p_dfu_ctx->dfu_state = BACKGROUND_DFU_WAIT_FOR_RESET; + + if (p_dfu_ctx->dfu_mode == BACKGROUND_DFU_MODE_MULTICAST) + { + stop_block_timeout_timer(p_dfu_ctx); + } + + background_dfu_transport_state_update(p_dfu_ctx); + } + else if (event == BACKGROUND_DFU_EVENT_PROCESSING_ERROR) + { + p_dfu_ctx->dfu_diag.prev_state = BACKGROUND_DFU_DOWNLOAD_FIRMWARE; + + if (p_dfu_ctx->dfu_mode == BACKGROUND_DFU_MODE_MULTICAST) + { + stop_block_timeout_timer(p_dfu_ctx); + } + + NRF_LOG_ERROR("Processing error while downloading firmware."); + dfu_handle_error(p_dfu_ctx); + } + break; + } + + case BACKGROUND_DFU_WAIT_FOR_RESET: + NRF_LOG_WARNING("An event received in wait for reset state. This should not happen."); + break; + + default: + NRF_LOG_ERROR("Unhandled state"); + break; + } + + if ((p_dfu_ctx->dfu_state != BACKGROUND_DFU_IDLE) && + (p_dfu_ctx->dfu_state != BACKGROUND_DFU_ERROR) && + (p_dfu_ctx->dfu_state != BACKGROUND_DFU_WAIT_FOR_RESET)) + { + if (((p_dfu_ctx->dfu_state == BACKGROUND_DFU_DOWNLOAD_FIRMWARE) || + (p_dfu_ctx->dfu_state == BACKGROUND_DFU_DOWNLOAD_INIT_CMD)) && + (p_dfu_ctx->dfu_mode == BACKGROUND_DFU_MODE_MULTICAST)) + { + // In multicast DFU firmware download, client doesn't initiate block requests. + } + else + { + if ((event == BACKGROUND_DFU_EVENT_TRANSFER_ERROR) && (p_dfu_ctx->retry_count > 0)) + { + p_dfu_ctx->retry_count -= 1; + } + + if (p_dfu_ctx->retry_count > 0) + { + background_dfu_transport_send_request(p_dfu_ctx); + } + else + { + NRF_LOG_ERROR("No more retries"); + dfu_handle_error(p_dfu_ctx); + } + } + } + + return err_code; +} + +void background_dfu_reset_state(background_dfu_context_t * p_dfu_ctx) +{ + sha256_context_t sha256_ctx; + + uint8_t hash[32]; + uint32_t err_code = NRF_SUCCESS; + + p_dfu_ctx->dfu_state = BACKGROUND_DFU_IDLE; + p_dfu_ctx->dfu_mode = BACKGROUND_DFU_MODE_UNICAST; + p_dfu_ctx->init_cmd_size = 0; + p_dfu_ctx->firmware_size = 0; + p_dfu_ctx->remaining_size = 0; + + memset(&p_dfu_ctx->dfu_diag, 0, sizeof(p_dfu_ctx->dfu_diag)); + + err_code = sha256_init(&sha256_ctx); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in sha256_init (%d)", err_code); + } + + err_code = sha256_update(&sha256_ctx, (const uint8_t *)DFU_DATE_TIME, strlen(DFU_DATE_TIME)); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in sha256_update (%d)", err_code); + } + + err_code = sha256_final(&sha256_ctx, (uint8_t *)hash, false); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in sha256_final (%d)", err_code); + } + + p_dfu_ctx->dfu_diag.build_id = uint32_big_decode(hash); + p_dfu_ctx->dfu_diag.state = BACKGROUND_DFU_IDLE; + p_dfu_ctx->dfu_diag.prev_state = BACKGROUND_DFU_IDLE; + + NRF_LOG_INFO("Current DFU Diag version: %s, 0x%08x", + (uint32_t)DFU_DATE_TIME, p_dfu_ctx->dfu_diag.build_id); +} + +void background_dfu_state_init(background_dfu_context_t * p_dfu_ctx) +{ + uint32_t err_code; + + err_code = app_timer_create(&m_missing_block_timer, + APP_TIMER_MODE_SINGLE_SHOT, + block_request_handler); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in app_timer_create (%d)", err_code); + } + + err_code = app_timer_create(&m_block_timeout_timer, + APP_TIMER_MODE_SINGLE_SHOT, + block_timeout_handler); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in app_timer_create (%d)", err_code); + } + + background_dfu_reset_state(p_dfu_ctx); +} -- cgit v1.2.3