diff options
author | Trygve Laugstøl <trygvis@inamo.no> | 2018-08-23 17:08:59 +0200 |
---|---|---|
committer | Trygve Laugstøl <trygvis@inamo.no> | 2018-08-23 17:12:21 +0200 |
commit | 3061ecca3d0fdfb87dabbf5f63c9e06c2a30f53a (patch) | |
tree | ab49cc16ed0b853452c5c2ed2d3042416d628986 /thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu | |
download | iot-sensors-master.tar.gz iot-sensors-master.tar.bz2 iot-sensors-master.tar.xz iot-sensors-master.zip |
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu')
9 files changed, 3061 insertions, 0 deletions
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_block.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_block.c new file mode 100644 index 0000000..8b08068 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_block.c @@ -0,0 +1,456 @@ +/** + * 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_block background_dfu_block.c + * @{ + * @ingroup background_dfu + * @brief Background DFU block handling implementation. + * + */ + +#include "background_dfu_block.h" + +#include <assert.h> + +#include "sdk_config.h" +#include "app_scheduler.h" +#include "background_dfu_operation.h" +#include "compiler_abstraction.h" +#include "nrf_dfu_handling_error.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" + +#define BITMAP_BYTE_FROM_INDEX(index) ((index) / 8) +#define BITMAP_BIT_FROM_INDEX(index) (7 - ((index) % 8)) + +static void block_buffer_store(background_dfu_block_manager_t * p_bm); + +/**@brief Convert block number to bitmap index. + * + * @param[in] block_num Block number. + * + * @return Corresponding index. + */ +static __INLINE uint16_t block_num_to_index(uint32_t block_num) +{ + return block_num % BLOCKS_PER_BUFFER; +} + +/**@brief Set a bit in a bitmap. + * + * @param[inout] p_bitmap A pointer to the bitmap. + * @param[in] index Bit index to set. + */ +static __INLINE void set_bitmap_bit(uint8_t * p_bitmap, uint16_t index) +{ + p_bitmap[BITMAP_BYTE_FROM_INDEX(index)] |= (0x01 << BITMAP_BIT_FROM_INDEX(index)); +} + +/**@brief Clear a bit in a bitmap. + * + * @param[inout] p_bitmap A pointer to the bitmap. + * @param[in] index Bit index to clear. + */ +static __INLINE void clear_bitmap_bit(uint8_t * p_bitmap, uint16_t index) +{ + p_bitmap[BITMAP_BYTE_FROM_INDEX(index)] &= ~((uint8_t)(0x01 << BITMAP_BIT_FROM_INDEX(index))); +} + +/**@brief Check if a bit in a bitmap is set. + * + * @param[inout] p_bitmap A pointer to the bitmap. + * @param[in] index Bit index to check. + * + * @return True if bit is set, false otherwise. + */ +static __INLINE bool is_block_present(const uint8_t * p_bitmap, uint16_t index) +{ + return (p_bitmap[BITMAP_BYTE_FROM_INDEX(index)] >> BITMAP_BIT_FROM_INDEX(index)) & 0x01; +} + +/** + * @brief A callback function for DFU operation. + */ +static void dfu_operation_callback(nrf_dfu_response_t * p_res, void * p_context) +{ + background_dfu_block_manager_t * p_bm = (background_dfu_block_manager_t *)p_context; + ret_code_t res_code; + + if (p_res->result != NRF_DFU_RES_CODE_SUCCESS) + { + p_bm->result_handler(BACKGROUND_DFU_BLOCK_STORE_ERROR, p_bm->p_context); + } + else + { + switch (p_res->request) + { + case NRF_DFU_OP_OBJECT_CREATE: + { + // Object created, write respective block. + uint32_t current_size = p_bm->currently_stored_block * DEFAULT_BLOCK_SIZE; + uint16_t data_offset = block_num_to_index(p_bm->currently_stored_block) * DEFAULT_BLOCK_SIZE; + uint16_t store_size = MIN(DEFAULT_BLOCK_SIZE, (p_bm->image_size - current_size)); + res_code = background_dfu_op_write(p_bm->data + data_offset, + store_size, + dfu_operation_callback, + p_bm); + + if (res_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Failed to store block (b:%d c:%d).", + p_bm->currently_stored_block, + p_bm->current_block); + p_bm->result_handler(BACKGROUND_DFU_BLOCK_STORE_ERROR, p_bm->p_context); + } + + break; + } + + case NRF_DFU_OP_OBJECT_WRITE: + if (!((p_bm->currently_stored_block + 1) % BLOCKS_PER_DFU_OBJECT) || + ((p_bm->currently_stored_block + 1) == BLOCKS_PER_SIZE(p_bm->image_size))) + { + res_code = background_dfu_op_crc(dfu_operation_callback, p_bm); + + if (res_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Failed to store block (b:%d c:%d).", + p_bm->currently_stored_block, + p_bm->current_block); + p_bm->result_handler(BACKGROUND_DFU_BLOCK_STORE_ERROR, p_bm->p_context); + } + } + else + { + p_bm->last_block_stored = p_bm->currently_stored_block; + clear_bitmap_bit(p_bm->bitmap, block_num_to_index(p_bm->currently_stored_block)); + p_bm->currently_stored_block = INVALID_BLOCK_NUMBER; + + p_bm->result_handler(BACKGROUND_DFU_BLOCK_SUCCESS, p_bm->p_context); + + block_buffer_store(p_bm); + } + + break; + + case NRF_DFU_OP_CRC_GET: + res_code = background_dfu_op_execute(dfu_operation_callback, p_bm); + + if (res_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Failed to store block (b:%d c:%d).", + p_bm->currently_stored_block, + p_bm->current_block); + p_bm->result_handler(BACKGROUND_DFU_BLOCK_STORE_ERROR, p_bm->p_context); + } + + break; + + case NRF_DFU_OP_OBJECT_EXECUTE: + p_bm->last_block_stored = p_bm->currently_stored_block; + clear_bitmap_bit(p_bm->bitmap, block_num_to_index(p_bm->currently_stored_block)); + p_bm->currently_stored_block = INVALID_BLOCK_NUMBER; + + p_bm->result_handler(BACKGROUND_DFU_BLOCK_SUCCESS, p_bm->p_context); + + block_buffer_store(p_bm); + + break; + + default: + ASSERT(false); + } + } +} + +/**@brief Store a block from the buffer in a flash. + * + * @param[inout] p_bm A pointer to the block manager. + * @param[in] p_block A block number to store. + * + * @return NRF_SUCCESS on success, an error code is returned otherwise. + */ +static ret_code_t block_store(background_dfu_block_manager_t * p_bm, uint32_t block_num) +{ + p_bm->currently_stored_block = block_num; + + ret_code_t res_code = NRF_SUCCESS; + uint32_t current_size = block_num * DEFAULT_BLOCK_SIZE; + + do + { + // Initialize DFU object if needed. + if (!(block_num % BLOCKS_PER_DFU_OBJECT)) + { + uint32_t object_size = MIN(DEFAULT_DFU_OBJECT_SIZE, (p_bm->image_size - current_size)); + + res_code = background_dfu_op_create(p_bm->image_type, + object_size, + dfu_operation_callback, + p_bm); + break; + } + + // Store block. + uint16_t data_offset = block_num_to_index(block_num) * DEFAULT_BLOCK_SIZE; + uint16_t store_size = MIN(DEFAULT_BLOCK_SIZE, (p_bm->image_size - current_size)); + res_code = background_dfu_op_write(p_bm->data + data_offset, + store_size, + dfu_operation_callback, + p_bm); + + } while (0); + return res_code; +} + +/**@brief Check if block manager is busy storing a block. + * + * @param[inout] p_bm A pointer to the block manager. + * + */ +static bool is_block_manager_busy(background_dfu_block_manager_t * p_bm) +{ + return p_bm->currently_stored_block >= 0; +} + +/**@brief Store any valid blocks from the buffer in a flash. + * + * @param[inout] p_bm A pointer to the block manager. + * + */ +static void block_buffer_store(background_dfu_block_manager_t * p_bm) +{ + ret_code_t res_code = NRF_SUCCESS; + + if (!is_block_manager_busy(p_bm)) + { + if (p_bm->last_block_stored < p_bm->current_block) + { + int32_t block = p_bm->last_block_stored + 1; + + if (is_block_present(p_bm->bitmap, block_num_to_index(block))) + { + NRF_LOG_INFO("Storing block (b:%d c:%d).", block, p_bm->current_block); + + // There is a block to store. + res_code = block_store(p_bm, block); + if (res_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Failed to store block (b:%d c:%d).", block, p_bm->current_block); + p_bm->result_handler(BACKGROUND_DFU_BLOCK_STORE_ERROR, p_bm->p_context); + } + } + else + { + NRF_LOG_WARNING("Gap encountered - quit (b:%d c:%d).", block, p_bm->current_block); + } + } + } +} + +/** + * @brief A callback function for scheduling DFU block operations. + */ +static void block_store_scheduled(void * p_evt, uint16_t event_length) +{ + UNUSED_PARAMETER(event_length); + + background_dfu_block_manager_t * p_bm = *((background_dfu_block_manager_t **)p_evt); + block_buffer_store(p_bm); +} + +/**@brief Copy block data to the buffer. + * + * @param[inout] p_bm A pointer to the block manager. + * @param[in] p_block A pointer to the block. + */ +static void block_buffer_add(background_dfu_block_manager_t * p_bm, + const background_dfu_block_t * p_block) +{ + uint16_t index = block_num_to_index(p_block->number); + + memcpy(p_bm->data + index * DEFAULT_BLOCK_SIZE, p_block->p_payload, DEFAULT_BLOCK_SIZE); + set_bitmap_bit(p_bm->bitmap, index); + + if (p_bm->current_block < (int32_t)p_block->number) + { + p_bm->current_block = (int32_t)p_block->number; + } + + // Schedule block store. + UNUSED_RETURN_VALUE(app_sched_event_put(&p_bm, sizeof(p_bm), block_store_scheduled)); +} + +/*************************************************************************************************** + * @section Public + **************************************************************************************************/ + +void block_manager_init(background_dfu_block_manager_t * p_bm, + uint32_t object_type, + uint32_t object_size, + int32_t initial_block, + block_manager_result_notify_t result_handler, + void * p_context) +{ + p_bm->image_type = object_type; + p_bm->image_size = object_size; + p_bm->last_block_stored = p_bm->current_block = initial_block - 1; + p_bm->result_handler = result_handler; + p_bm->p_context = p_context; + p_bm->currently_stored_block = INVALID_BLOCK_NUMBER; + + memset(p_bm->bitmap, 0, sizeof(p_bm->bitmap)); +} + +background_dfu_block_result_t block_manager_block_process(background_dfu_block_manager_t * p_bm, + const background_dfu_block_t * p_block) +{ + /* + * Possible scenarios: + * 1) We receive a block older than our last stored block - simply ignore it. + * 2) We receive a block that fits within current buffer range - process it. + * 3) We receive a block that exceeds current buffer range - abort DFU as we won't be able to catch-up. + */ + + if (p_block->size != DEFAULT_BLOCK_SIZE) + { + NRF_LOG_WARNING("Block with incorrect size received (s:%d n:%d).", + p_block->size, p_block->number); + return BACKGROUND_DFU_BLOCK_IGNORE; + } + + if ((int32_t)p_block->number <= p_bm->last_block_stored) + { + NRF_LOG_WARNING("Ignoring block that already was stored(o:%d n:%d).", + p_bm->last_block_stored, p_block->number); + return BACKGROUND_DFU_BLOCK_IGNORE; + } + + if ((int32_t)p_block->number > p_bm->last_block_stored + BLOCKS_PER_BUFFER) + { + NRF_LOG_WARNING("Too many blocks missed - abort DFU (o:%d n:%d).", + p_bm->last_block_stored, p_block->number); + return BACKGROUND_DFU_BLOCK_INVALID; + } + + // Block fits within current buffer - copy it into the buffer and update the current block if most + // recent block was received. + block_buffer_add(p_bm, p_block); + + return BACKGROUND_DFU_BLOCK_SUCCESS; +} + +bool block_manager_is_image_complete(const background_dfu_block_manager_t * p_bm) +{ + uint32_t image_blocks = BLOCKS_PER_SIZE(p_bm->image_size); + + NRF_LOG_DEBUG("Is image complete (o:%d n:%d).", p_bm->last_block_stored, image_blocks); + + if (p_bm->last_block_stored + 1 == image_blocks) + { + return true; + } + + return false; +} + +bool block_manager_request_bitmap_get(const background_dfu_block_manager_t * p_bm, + background_dfu_request_bitmap_t * p_req_bmp) +{ + if (p_bm->current_block > p_bm->last_block_stored) + { + memset(p_req_bmp, 0, sizeof(*p_req_bmp)); + p_req_bmp->offset = p_bm->last_block_stored + 1; + p_req_bmp->size = (p_bm->current_block - p_bm->last_block_stored + 7) / 8; + + for (uint16_t block = p_req_bmp->offset; block <= p_bm->current_block; block++) + { + if (!is_block_present(p_bm->bitmap, block_num_to_index(block))) + { + set_bitmap_bit(p_req_bmp->bitmap, block - p_req_bmp->offset); + } + } + + // Clip empty bytes at the end. + while ((p_req_bmp->size > 0) && (p_req_bmp->bitmap[p_req_bmp->size - 1] == 0)) + { + p_req_bmp->size--; + } + + if (p_req_bmp->size == 0) + { + return false; + } + + return true; + } + + return false; +} + +bool block_manager_increment_current_block(background_dfu_block_manager_t * p_bm) +{ + uint32_t image_blocks = BLOCKS_PER_SIZE(p_bm->image_size); + + if (p_bm->current_block + 1 == image_blocks) + { + // Already on last block. + return false; + } + else + { + p_bm->current_block++; + } + + return true; +} + +int32_t block_manager_get_current_block(const background_dfu_block_manager_t * p_bm) +{ + return p_bm->current_block; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_block.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_block.h new file mode 100644 index 0000000..e1e468b --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_block.h @@ -0,0 +1,197 @@ +/** + * 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_block background_dfu_block.H + * @{ + * @ingroup background_dfu + * @brief Background DFU block handling. + * + */ + +#ifndef BACKGROUND_DFU_BLOCK_H_ +#define BACKGROUND_DFU_BLOCK_H_ + +#include <stdint.h> +#include <stdbool.h> + +#include "app_util_platform.h" +#include "sdk_config.h" + +/** @brief Macro for calculating the number of blocks that fits in particular size. */ +#define BLOCKS_PER_SIZE(SIZE) ((SIZE + DEFAULT_BLOCK_SIZE - 1) / DEFAULT_BLOCK_SIZE) + +/** @brief Default block size for background DFU blocks. */ +#define DEFAULT_BLOCK_SIZE BACKGROUND_DFU_DEFAULT_BLOCK_SIZE + +/** @brief Number of blocks in superblock. */ +#define BLOCKS_PER_BUFFER BACKGROUND_DFU_BLOCKS_PER_BUFFER + +/** @brief Size of the block buffer. Shall be a multiply of @ref DEFAULT_BLOCK_SIZE. */ +#define BLOCK_BUFFER_SIZE (BLOCKS_PER_BUFFER * DEFAULT_BLOCK_SIZE) + +/** @brief Size of the bitmap reflecting the state of the blocks in a superblock. */ +#define BITMAP_SIZE ((BLOCKS_PER_BUFFER + 7) / 8) + +/** @brief Default size of DFU object. Shall be a multiply of @ref DEFAULT_BLOCK_SIZE. */ +#define DEFAULT_DFU_OBJECT_SIZE 4096 + +/** @brief Number of blocks in DFU object. */ +#define BLOCKS_PER_DFU_OBJECT (BLOCKS_PER_SIZE(DEFAULT_DFU_OBJECT_SIZE)) + +/** @brief Value of invalid block number (for example to indicate that no block is being stored). */ +#define INVALID_BLOCK_NUMBER (-1) + +/** @brief Result of a DFU block operation. */ +typedef enum +{ + BACKGROUND_DFU_BLOCK_SUCCESS, /**< Block operation completed successfully. */ + BACKGROUND_DFU_BLOCK_IGNORE, /**< Block was ignored in current DFU context (i.e. duplicated block). */ + BACKGROUND_DFU_BLOCK_INVALID, /**< Block is invalid in current context, indicates that DFU shall be aborted. */ + BACKGROUND_DFU_BLOCK_STORE_ERROR /**< Block was not stored due to internal store error. */ +} background_dfu_block_result_t; + +/**@brief A function that module can register to receive block manager error notifications. */ +typedef void (* block_manager_result_notify_t)(background_dfu_block_result_t result, + void * p_context); + +/**@brief Block information structure. */ +typedef struct +{ + uint16_t size; /**< Size of the block in bytes. */ + uint32_t number; /**< Block number. */ + uint8_t * p_payload; /**< Block payload. */ +} background_dfu_block_t; + +/**@brief Block manager structure. + * + * Block manager keeps track of received blocks, ensuring that they are written into flash in + * a correct order, and updates the missing blocks bitmap, so that they could be requested from + * the server. + */ +typedef struct +{ + uint32_t image_size; /**< Size of currently stored image. */ + uint32_t image_type; /**< Image type (init command or firmware). */ + int32_t last_block_stored; /**< Number of the last block written in the flash. */ + int32_t current_block; /**< Last received (or expected) block. */ + uint8_t data[BLOCK_BUFFER_SIZE]; /**< Block buffer. */ + uint8_t bitmap[BITMAP_SIZE]; /**< A bitmap indicating which blocks have been received. */ + block_manager_result_notify_t result_handler; /**< A callback function for error notification. */ + void * p_context; /**< A context for result notification.*/ + int32_t currently_stored_block; /**< Number of block that is currently being stored. */ +} background_dfu_block_manager_t; + +/**@brief Bitmap structure used in bitmap requests. */ +typedef struct +{ + uint16_t size; /**< Size of the bitmap, in bytes.*/ + uint16_t offset; /**< Bitmap offset, indicating which block is referenced by first bit in bitmap. */ + uint8_t bitmap[BITMAP_SIZE]; /**< Bitmap itself. One in specific bit indicates which block is missing. */ +} background_dfu_request_bitmap_t; + +/**@brief Initialize block manager. + * + * @param[inout] p_bm A pointer to the block manager. + * @param[in] object_type Type of the image to store. + * @param[in] object_size Size of the image to store. + * @param[in] initial_block Number of the first block to receive. Typically it would be 0, but + * in case DFU restarted in the middle, it may differ. + * @param[in] error_handler A callback for error notification. + * @param[in] p_context A context for error notification. + */ +void block_manager_init(background_dfu_block_manager_t * p_bm, + uint32_t object_type, + uint32_t object_size, + int32_t initial_block, + block_manager_result_notify_t result_handler, + void * p_context); + +/**@brief Process a single block. + * + * @param[inout] p_bm A pointer to the block manager. + * @param[in] p_block A pointer to the block structure containing information about the block. + * + * @retval BACKGROUND_DFU_BLOCK_SUCCESS Block stored successfully. + * @retval BACKGROUND_DFU_BLOCK_IGNORE Invalid block size or block already stored in flash. + * @retval BACKGROUND_DFU_BLOCK_INVALID Block number indicates that too many blocks were missed. + * @retval BACKGROUND_DFU_BLOCK_STORE_ERROR Block store in flash failed. + */ +background_dfu_block_result_t block_manager_block_process(background_dfu_block_manager_t * p_bm, + const background_dfu_block_t * p_block); + +/**@brief Check if an image managed by a block manager is complete. + * + * @param[in] p_bm A pointer to the block manager. + * + * @return True if image is complete, false otherwise. + */ +bool block_manager_is_image_complete(const background_dfu_block_manager_t * p_bm); + +/**@brief Get current block bitmap. + * + * @param[in] p_bm A pointer to the block manager. + * @param[out] p_req_bmp A pointer to the block bitmap structure. + * + * @return True if non-empty bitmap was generated, false otherwise. + */ +bool block_manager_request_bitmap_get(const background_dfu_block_manager_t * p_bm, + background_dfu_request_bitmap_t * p_req_bmp); + +/**@brief Increment current block, in case no blocks were received and block timeout shot. + * + * @param[in] p_bm A pointer to the block manager. + * + * @return True if block was incremented, false if block manager is already on a last block of the image. + */ +bool block_manager_increment_current_block(background_dfu_block_manager_t * p_bm); + +/**@brief Get current block number that block manager received/expects. + * + * @param[in] p_bm A pointer to the block manager. + * + * @return Current block number. + */ +int32_t block_manager_get_current_block(const background_dfu_block_manager_t * p_bm); + +#endif /* BACKGROUND_DFU_BLOCK_H_ */ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_operation.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_operation.c new file mode 100644 index 0000000..7aa6679 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_operation.c @@ -0,0 +1,141 @@ +/** + * 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_operation background_dfu_operation.c + * @{ + * @ingroup background_dfu + * @brief Background DFU operations implementation. + * + */ + +#include "background_dfu_operation.h" + +#include "sdk_config.h" +#include "nrf_dfu_req_handler.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" + +ret_code_t background_dfu_op_select(uint32_t object_type, + nrf_dfu_response_callback_t callback, + void * p_context) +{ + nrf_dfu_request_t dfu_req; + + memset(&dfu_req, 0, sizeof(dfu_req)); + + dfu_req.request = NRF_DFU_OP_OBJECT_SELECT; + dfu_req.select.object_type = object_type; + dfu_req.p_context = p_context; + dfu_req.callback.response = callback; + + return nrf_dfu_req_handler_on_req(&dfu_req); +} + +ret_code_t background_dfu_op_create(uint32_t object_type, + uint32_t object_size, + nrf_dfu_response_callback_t callback, + void * p_context) +{ + nrf_dfu_request_t dfu_req; + + memset(&dfu_req, 0, sizeof(dfu_req)); + + dfu_req.request = NRF_DFU_OP_OBJECT_CREATE; + dfu_req.create.object_size = object_size; + dfu_req.create.object_type = object_type; + dfu_req.p_context = p_context; + dfu_req.callback.response = callback; + + return nrf_dfu_req_handler_on_req(&dfu_req); +} + +ret_code_t background_dfu_op_write(const uint8_t * p_payload, + uint16_t payload_length, + nrf_dfu_response_callback_t callback, + void * p_context) +{ + nrf_dfu_request_t dfu_req; + + memset(&dfu_req, 0, sizeof(dfu_req)); + + dfu_req.request = NRF_DFU_OP_OBJECT_WRITE; + dfu_req.write.p_data = (uint8_t *)p_payload; + dfu_req.write.len = payload_length; + dfu_req.p_context = p_context; + dfu_req.callback.response = callback; + + return nrf_dfu_req_handler_on_req(&dfu_req); +} + +ret_code_t background_dfu_op_crc(nrf_dfu_response_callback_t callback, + void * p_context) +{ + nrf_dfu_request_t dfu_req; + + memset(&dfu_req, 0, sizeof(dfu_req)); + + dfu_req.request = NRF_DFU_OP_CRC_GET; + dfu_req.p_context = p_context; + dfu_req.callback.response = callback; + + return nrf_dfu_req_handler_on_req(&dfu_req); +} + +ret_code_t background_dfu_op_execute(nrf_dfu_response_callback_t callback, + void * p_context) +{ + nrf_dfu_request_t dfu_req; + + memset(&dfu_req, 0, sizeof(dfu_req)); + + dfu_req.request = NRF_DFU_OP_OBJECT_EXECUTE; + dfu_req.p_context = p_context; + dfu_req.callback.response = callback; + + return nrf_dfu_req_handler_on_req(&dfu_req); +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_operation.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_operation.h new file mode 100644 index 0000000..b01d9b2 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_operation.h @@ -0,0 +1,117 @@ +/** + * 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_operation background_dfu_operation.h + * @{ + * @ingroup background_dfu + * @brief Background DFU operations. + * + */ + +#ifndef BACKGROUND_DFU_OPERATION_H_ +#define BACKGROUND_DFU_OPERATION_H_ + +#include <stdint.h> + +#include "nrf_dfu_handling_error.h" + +/** @brief Select DFU object. + * + * @param[in] object_type Object type which should be selected. + * @param[in] callback A callback function to be executed after operation is completed. + * @param[in] p_context A pointer to the operation context. + * + * @return Operation result code. + */ +ret_code_t background_dfu_op_select(uint32_t object_type, + nrf_dfu_response_callback_t callback, + void * p_context); + +/** @brief Create DFU object. + * + * @param[in] object_type Object type which should be selected. + * @param[in] object_size Size of an object to create. + * @param[in] callback A callback function to be executed after operation is completed. + * @param[in] p_context A pointer to the operation context. + * + * @return Operation result code. + */ +ret_code_t background_dfu_op_create(uint32_t object_type, + uint32_t object_size, + nrf_dfu_response_callback_t callback, + void * p_context); + +/** @brief Write DFU object. + * + * @param[in] p_payload A pointer to data which should be written to the object. + * @param[in] payload_length Length, in bytes, of data which should be written to the object. + * @param[in] callback A callback function to be executed after operation is completed. + * @param[in] p_context A pointer to the operation context. + * + * @return Operation result code. + */ +ret_code_t background_dfu_op_write(const uint8_t * p_payload, + uint16_t payload_length, + nrf_dfu_response_callback_t callback, + void * p_context); + +/** @brief Calculate DFU object CRC. + * + * @param[in] callback A callback function to be executed after operation is completed. + * @param[in] p_context A pointer to the operation context. + * + * @return Operation result code. + */ +ret_code_t background_dfu_op_crc(nrf_dfu_response_callback_t callback, + void * p_context); + +/** @brief Execute selected DFU. + * + * @param[in] callback A callback function to be executed after operation is completed. + * @param[in] p_context A pointer to the operation context. + * + * @return Operation result code. + */ +ret_code_t background_dfu_op_execute(nrf_dfu_response_callback_t callback, + void * p_context); + +#endif /* BACKGROUND_DFU_OPERATION_H_ */ 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 <string.h> + +#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); +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_state.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_state.h new file mode 100644 index 0000000..8dd4395 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/background_dfu_state.h @@ -0,0 +1,216 @@ +/** + * 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.h + * @{ + * @ingroup background_dfu + * @brief Background DFU state management. + * + */ + +#ifndef BACKGROUND_DFU_STATE_H_ +#define BACKGROUND_DFU_STATE_H_ + +#include <stdint.h> +#include <stdbool.h> + +#include "background_dfu_block.h" +#include "nrf_dfu_req_handler.h" + +/** @brief DFU client state ID. + * + * We reuse DFU object type IDs as DFU process states IDs, + * so that current state can be used as the object type in + * function which expect one. + */ +typedef enum +{ + BACKGROUND_DFU_DOWNLOAD_INIT_CMD = NRF_DFU_OBJ_TYPE_COMMAND, + BACKGROUND_DFU_DOWNLOAD_FIRMWARE = NRF_DFU_OBJ_TYPE_DATA, + BACKGROUND_DFU_DOWNLOAD_TRIG, + BACKGROUND_DFU_WAIT_FOR_RESET, + BACKGROUND_DFU_IDLE, + BACKGROUND_DFU_ERROR, +} background_dfu_state_t; + +/** @brief DFU event definitions. */ +typedef enum +{ + BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE, + BACKGROUND_DFU_EVENT_TRANSFER_CONTINUE, + BACKGROUND_DFU_EVENT_TRANSFER_ERROR, + BACKGROUND_DFU_EVENT_PROCESSING_ERROR, +} background_dfu_event_t; + +/** @brief DFU mode definitions. */ +typedef enum +{ + BACKGROUND_DFU_MODE_UNICAST, + BACKGROUND_DFU_MODE_MULTICAST +} background_dfu_mode_t; + +/** @brief Trigger packet structure. */ +typedef PACKED_STRUCT +{ + uint8_t flags; /**< Trigger message flags. Bits 7:4 (oldest) - trigger version, bit 3 - DFU mode, bits 2:0 - reserved. */ + uint32_t init_length; + uint32_t init_crc; + uint32_t image_length; + uint32_t image_crc; +} background_dfu_trigger_t; + +/**@brief Structure with DFU diagnostic information. */ +typedef PACKED_STRUCT background_dfu_diagnostic +{ + uint32_t build_id; /**< Build identifier, based on compilation time. */ + uint8_t state; /**< Current DFU state. */ + uint8_t prev_state; /**< Previous DFU state. */ + uint16_t init_blocks_requested; /**< Number of requested missing init blocks. */ + uint16_t image_blocks_requested; /**< Number of requested missing image blocks. */ + uint16_t triggers_received; /**< Number of triggers received. */ + uint16_t total_init_blocks_received; /**< Total number of init blocks received, including retransmitted ones. */ + uint16_t total_image_blocks_received; /**< Total number of image blocks received, including retransmitted ones. */ +} background_dfu_diagnostic_t; + +/** @brief DFU client state. */ +typedef struct dfu_context +{ + background_dfu_state_t dfu_state; /**< Current DFU client state. */ + background_dfu_mode_t dfu_mode; /**< Current DFU mode. */ + bool reset_suppress; /**< If set then device won't automatically reset after + downloading firmware. */ + background_dfu_diagnostic_t dfu_diag; /**< DFU diagnostic information. */ + + uint32_t init_cmd_size; /**< Current init command size. */ + uint32_t init_cmd_crc; /**< Current init command checksum. */ + uint32_t firmware_size; /**< Current firmware command size. */ + uint32_t firmware_crc; /**< Current firmware command checksum. */ + uint32_t max_obj_size; /**< Maximum size of the DFU object. */ + uint32_t remaining_size; /**< Remaining size, in bytes, of the resource which + is being downloaded. */ + /* TODO Move the block num to the block manager. */ + uint32_t block_num; /**< Currently requested block number. */ + uint32_t * p_resource_size; /**< Downloaded resource size. */ + background_dfu_block_manager_t block_manager; /**< An entity managing block reception and storage. */ + uint8_t retry_count; /**< Number of remaining retires. */ +} background_dfu_context_t; + +/**@brief Check if payload contains valid trigger. + * + * @param[inout] p_dfu_ctx DFU context. + * @param[in] p_payload A pointer to the message payload. + * @param[in[ payload_len Payload length. + * + * @return True if trigger was valid, false otherwise. + */ +bool background_dfu_validate_trigger(background_dfu_context_t * p_dfu_ctx, + const uint8_t * p_payload, + uint32_t payload_len); + +/**@brief Process a payload with a DFU trigger. + * + * @param[inout] p_dfu_ctx DFU context. + * @param[in] p_payload A pointer to the message payload. + * @param[in[ payload_len Payload length. + * + * @return True if trigger was successfully processed, false otherwise. + */ +bool background_dfu_process_trigger(background_dfu_context_t * p_dfu_ctx, + const uint8_t * p_payload, + uint32_t payload_len); + +/**@brief Process the block and return CoAP result code corresponding to the result of operation. + * + * @param[inout] p_dfu_ctx DFU context. + * @param[in] p_block A pointer to the block structure. + * + * @return True if init command is valid, false otherwise. + * + * @retval BACKGROUND_DFU_BLOCK_SUCCESS The block was processed correctly. + * @retval BACKGROUND_DFU_BLOCK_IGNORE The block was incorrect for this node, but did not + * indicate that the DFU shall be stopped. + * @retval BACKGROUND_DFU_BLOCK_INVALID The block indicated that the node would not catch-up with the DFU + * transfer or other error occured. Node aborted DFU. + * @retval BACKGROUND_DFU_BLOCK_STORE_ERROR DFU store error. + */ +background_dfu_block_result_t background_dfu_process_block(background_dfu_context_t * p_dfu_ctx, + const background_dfu_block_t * p_block); + +/**@brief DFU state machine handler. + * + * @param[in] p_dfu_ctx DFU context. + * @param[in] event DFU event. + * + * @return NRF_SUCCESS or error code + */ +uint32_t background_dfu_handle_event(background_dfu_context_t * p_dfu_ctx, + background_dfu_event_t event); + +/**@brief Reset state machine state. + * + * @param[in] p_dfu_ctx A pointer to DFU client context. + */ +void background_dfu_reset_state(background_dfu_context_t * p_dfu_ctx); + +/**@brief Initialize state machine state. + * + * @param[in] p_dfu_ctx A pointer to DFU client context. + */ +void background_dfu_state_init(background_dfu_context_t * p_dfu_ctx); + +/**@brief Convert a DFU event enum value to a string description. + * + * @param[in] event A DFU event. + * + * @return String representing the event. + */ +const char * background_dfu_event_to_string(const background_dfu_event_t event); + +/**@brief Convert a DFU state enum value to a string description. + * + * @param[in] event A DFU state. + * + * @return String representing the state. + */ +const char * background_dfu_state_to_string(const background_dfu_state_t state); + +#endif /* BACKGROUND_DFU_STATE_H_ */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/background_dfu_transport.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/background_dfu_transport.h new file mode 100644 index 0000000..061a921 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/background_dfu_transport.h @@ -0,0 +1,91 @@ +/** + * 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_transport background_dfu_state.h + * @{ + * @ingroup background_dfu + * @brief Background DFU transport API. + * + */ + +#ifndef BACKGROUND_DFU_TRANSPORT_H_ +#define BACKGROUND_DFU_TRANSPORT_H_ + +#include "background_dfu_state.h" + +/**@brief Create and send DFU block request with missing blocks. + * + * This function is used in multicast DFU. + * + * @param[in] p_dfu_ctx A pointer to the background DFU context. + * @param[in] p_req_bmp A pointer to the bitmap structure that shall be sent. + */ +void background_dfu_transport_block_request_send(background_dfu_context_t * p_dfu_ctx, + background_dfu_request_bitmap_t * p_req_bmp); + +/**@brief Send background DFU request, based on DFU state. + * + * @param[in] p_dfu_ctx A pointer to the background DFU context. + */ +void background_dfu_transport_send_request(background_dfu_context_t * p_dfu_ctx); + +/**@brief Update background DFU transport state. + * + * @param[in] p_dfu_ctx A pointer to the background DFU context. + */ +void background_dfu_transport_state_update(background_dfu_context_t * p_dfu_ctx); + +/**@brief Get random value. + * + * @returns A random value of uint32_t type. + */ +uint32_t background_dfu_random(void); + +/** @brief Handle DFU error. + * + * Notify transport about DFU error. + */ +void background_dfu_handle_error(void); + +#endif /* BACKGROUND_DFU_COAP_H_ */ + +/** @} */ diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/tftp/tftp_dfu.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/tftp/tftp_dfu.c new file mode 100644 index 0000000..923d220 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/tftp/tftp_dfu.c @@ -0,0 +1,928 @@ +/** + * 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 + * + * @brief TFTP DFU Example - Background DFU transport implementation. + * + */ +#include "tftp_dfu.h" + +#include "background_dfu_transport.h" + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include "app_timer.h" +#include "background_dfu_block.h" +#include "background_dfu_transport.h" +#include "cJSON.h" +#include "cJSON_iot_hooks.h" +#include "crc16.h" +#include "iot_file_static.h" +#include "iot_tftp.h" +#include "nrf.h" +#include "nrf_assert.h" +#include "nrf_delay.h" +#include "nrf_dfu_req_handler.h" +#include "nrf_dfu_settings.h" +#include "nrf_dfu_utils.h" +#include "nrf_log_ctrl.h" + +#define NRF_LOG_LEVEL 4 +#define NRF_LOG_MODULE_NAME TFTP_DFU +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define MAX_LENGTH_FILENAME 32 /**< Maximum length of the filename. */ +#define MAX_CONFIG_SIZE 1024 /**< Maximum DFU of the config size. */ + +#define CONFIG_APP_KEY "app" +#define CONFIG_SD_KEY "sd" +#define CONFIG_APPSD_KEY "appsd" +#define CONFIG_BL_KEY "bl" +#define CONFIG_PATH_KEY "p" +#define CONFIG_INIT_PATH_KEY "i" +#define CONFIG_SIZE_KEY "s" +#define CONFIG_ID_KEY "id" +#define CONFIG_INIT_SIZE_KEY "is" +#define CONFIG_INIT_ID_KEY "iid" + +#define APP_TFTP_BLOCK_SIZE 512 /**< Maximum or negotiated size of data block. */ +#define APP_TFTP_RETRANSMISSION_TIME 3 /**< Number of milliseconds between retransmissions. */ + +#define BOOTLOADER_REGION_START 0x0007D000 /**< This field should correspond to start address of the bootloader, found in UICR.RESERVED, 0x10001014, register. + This value is used for sanity check, so the bootloader will fail immediately if this value differs from runtime value. + The value is used to determine max application size for updating. */ + +#define TFTP_SOFTDEVICE_UPGRADE_SUPPORT false /**< Upgrade of softdevice or bootloader is not supported with current version of Background DFU module. */ + +/** The device info offset can be modified to place the device info settings at a different location. + * If the customer reserved UICR location is used for other application specific data, the offset + * must be updated to avoid collision with that data.: + */ +/** [DFU UICR DEV offset] */ +#define UICR_CUSTOMER_DEVICE_INFO_OFFSET 0x0 /**< Device info offset inside the customer UICR reserved area. Customers may change this value to place the device information in a user-preferred location. */ +/** [DFU UICR DEV offset] */ +#define UICR_CUSTOMER_RESERVED_OFFSET 0x80 /**< Customer reserved area in the UICR. The area from UICR + 0x80 is reserved for customer usage. */ +#define DFU_DEVICE_INFO_BASE (NRF_UICR_BASE + \ + UICR_CUSTOMER_RESERVED_OFFSET + \ + UICR_CUSTOMER_DEVICE_INFO_OFFSET) /**< The device information base address inside of UICR. */ +#define DFU_DEVICE_INFO ((dfu_device_info_t *)DFU_DEVICE_INFO_BASE) /**< The memory mapped structure for device information data. */ + + +/**@brief Structure holding basic device information settings. + */ +typedef struct +{ + uint16_t device_type; /**< Device type (2 bytes), for example Heart Rate. This number must be defined by the customer before production. It can be located in UICR or FICR. */ + uint16_t device_rev; /**< Device revision (2 bytes), for example major revision 1, minor revision 0. This number must be defined by the customer before production. It can be located in UICR or FICR. */ +} dfu_device_info_t; + +/**@brief Description of single block of firmware. */ +typedef struct +{ + uint32_t size; /**< Size of firmware block. */ + uint32_t crc; /**< CRC of firmware block. Set to 0 if no checksum checking is needed. */ + uint32_t init_size; /**< Size of init file. */ + uint32_t init_crc; /**< CRC of init file. */ +} iot_dfu_firmware_block_t; + + +/**@brief Description of the new firmware that has been written into flash memory. */ +typedef struct +{ + iot_dfu_firmware_block_t application; /**< Description of Application block in firmware image. */ + iot_dfu_firmware_block_t softdevice; /**< Description of SoftDevice block in firmware image. */ + iot_dfu_firmware_block_t bootloader; /**< Description of Bootloader block in firmware image. */ +} iot_dfu_firmware_desc_t; + +/**@brief Type of image being updated by the DFU module. */ +typedef enum +{ + TFTP_DFU_IMAGE_TYPE_APPLICATION, /**< DFU updates application. */ + TFTP_DFU_IMAGE_TYPE_SOFTDEVICE, /**< DFU updates softdevice and application. */ + TFTP_DFU_IMAGE_TYPE_BOOTLOADER, /**< DFU updates bootloader. */ +} tftp_dfu_image_type_t; + +/**@brief Static data used by TFTP DFU module. */ +typedef struct +{ + iot_tftp_t tftp; /**< TFTP instance. */ + char resource_path[MAX_LENGTH_FILENAME]; /**< Path of remote resource to get. */ + uint32_t block_number; /**< Number of block that will be passed to background DFU module. */ + uint8_t block[DEFAULT_BLOCK_SIZE]; /**< Buffer for parts of blocks to pass to background DFU module. */ + uint16_t block_size; /**< Size of data in block buffer. */ + tftp_dfu_image_type_t image_type; /**< Type of image that is currently updated. */ + iot_file_t config_file; /**< Pointer to the file used for config of DFU. */ + uint8_t config_mem[MAX_CONFIG_SIZE]; /**< Static memory for configuration file. */ + cJSON * p_config_json; /**< Pointer of cJSON instance. */ + iot_dfu_firmware_desc_t firmware_desc; /**< Details from configuration file. */ +} tftp_dfu_context_t; + +static background_dfu_context_t m_dfu_ctx; /**< Background DFU context. */ +static tftp_dfu_context_t m_tftp_dfu_ctx; /**< TFTP DFU context. */ + +/*************************************************************************************************** + * @section Common operations + **************************************************************************************************/ + +/**@brief Set resource path to trigger (config) file. */ +static void trigger_path_set(void) +{ + int retval = snprintf(m_tftp_dfu_ctx.resource_path, + sizeof(m_tftp_dfu_ctx.resource_path), + "/dfu/c/%d/%d", + DFU_DEVICE_INFO->device_type, + DFU_DEVICE_INFO->device_rev); + if (retval < 0) { + NRF_LOG_ERROR("Failed to set path using snprintf, retval: %d", retval); + } +} + +/**@brief Function for reading binary path from JSON configuration. + * + * @param[out] p_dst_str Pointer to memory, where path should be stored. + * @param[in] p_key Key inside JSON configuration file, describing firmware. + * + * @return NRF_SUCCESS if path found and stored in p_dst_str, otherwise error code. + */ +static uint32_t get_path(char * p_dst_str, const char * p_key) +{ + cJSON * p_cursor; + + if ((m_tftp_dfu_ctx.p_config_json == NULL) || (p_dst_str == NULL)) + { + NRF_LOG_ERROR("Invalid parameters"); + return NRF_ERROR_INVALID_PARAM; + } + + p_cursor = cJSON_GetObjectItem(m_tftp_dfu_ctx.p_config_json, CONFIG_PATH_KEY); + if (p_cursor != NULL) + { + p_cursor = cJSON_GetObjectItem(p_cursor, (const char *)p_key); + if ((p_cursor != NULL) && (p_cursor->type == cJSON_String)) + { + memcpy(p_dst_str, p_cursor->valuestring, strlen(p_cursor->valuestring)); + } + else + { + return NRF_ERROR_INVALID_PARAM; + } + } + else + { + return NRF_ERROR_INVALID_PARAM; + } + + return NRF_SUCCESS; +} + +/**@brief Function for reading init binary path from JSON configuration. + * + * @param[out] p_dst_str Pointer to memory, where path should be stored. + * @param[in] p_key Key inside JSON configuration file, describing firmware. + * + * @return NRF_SUCCESS if path found and stored in p_dst_str, otherwise error code. + */ +static uint32_t get_init_path(char * p_dst_str, const char * p_key) +{ + cJSON * p_cursor; + + if ((m_tftp_dfu_ctx.p_config_json == NULL) || (p_dst_str == NULL)) + { + NRF_LOG_ERROR("Invalid parameters"); + return NRF_ERROR_INVALID_PARAM; + } + + p_cursor = cJSON_GetObjectItem(m_tftp_dfu_ctx.p_config_json, CONFIG_INIT_PATH_KEY); + if (p_cursor != NULL) + { + p_cursor = cJSON_GetObjectItem(p_cursor, (const char *)p_key); + if ((p_cursor != NULL) && (p_cursor->type == cJSON_String)) + { + memcpy(p_dst_str, p_cursor->valuestring, strlen(p_cursor->valuestring)); + } + else + { + return NRF_ERROR_INVALID_PARAM; + } + } + else + { + return NRF_ERROR_INVALID_PARAM; + } + + return NRF_SUCCESS; +} + +/**@brief Function for parsing JSON file to get details of an application. + * + * @param[in] p_key Key inside JSON configuration file, describing firmware. + * @param[out] p_block Pointer to structure containing description of single block of firmware. + * + * @returns NRF_SUCCESS if correctly parsed data. Otherwise an error code indicating failure reason. + */ +static uint32_t details_parse(const char * p_key, iot_dfu_firmware_block_t * p_block) +{ + cJSON * p_cursor; + cJSON * p_cursor_back; + + // Clear output parameters. + memset(p_block, 0, sizeof(*p_block)); + + if (m_tftp_dfu_ctx.p_config_json == NULL) + { + NRF_LOG_ERROR("Invalid JSON file"); + return NRF_ERROR_INVALID_DATA; + } + + p_cursor = cJSON_GetObjectItem(m_tftp_dfu_ctx.p_config_json, (const char *)p_key); + if (p_cursor != NULL) + { + p_cursor_back = p_cursor; + p_cursor = cJSON_GetObjectItem(p_cursor, CONFIG_ID_KEY); + if (p_cursor != NULL) + { + if (p_cursor->type != cJSON_Number) + { + return NRF_ERROR_INVALID_DATA; + } + + p_block->crc = p_cursor->valueint; + } + else + { + NRF_LOG_ERROR("No binary ID inside JSON."); + return NRF_ERROR_INVALID_DATA; + } + + p_cursor = p_cursor_back; + p_cursor = cJSON_GetObjectItem(p_cursor, CONFIG_INIT_SIZE_KEY); + if (p_cursor != NULL) + { + if (p_cursor->type != cJSON_Number) + { + return NRF_ERROR_INVALID_DATA; + } + + p_block->init_size = p_cursor->valueint; + } + else + { + NRF_LOG_ERROR("No init SIZE inside JSON."); + return NRF_ERROR_INVALID_DATA; + } + + p_cursor = p_cursor_back; + p_cursor = cJSON_GetObjectItem(p_cursor, CONFIG_INIT_ID_KEY); + if (p_cursor != NULL) + { + if (p_cursor->type != cJSON_Number) + { + return NRF_ERROR_INVALID_DATA; + } + + p_block->init_crc = p_cursor->valueint; + } + else + { + NRF_LOG_ERROR("No init ID inside JSON."); + return NRF_ERROR_INVALID_DATA; + } + + p_cursor = p_cursor_back; + p_cursor = cJSON_GetObjectItem(p_cursor, CONFIG_SIZE_KEY); + if (p_cursor != NULL) + { + if ((p_cursor->type != cJSON_Number) || (p_cursor->valueint == 0)) + { + return NRF_ERROR_INVALID_DATA; + } + + p_block->size = p_cursor->valueint; + } + else + { + NRF_LOG_ERROR("No binary SIZE inside JSON."); + return NRF_ERROR_INVALID_DATA; + } + } + else + { + NRF_LOG_ERROR("No binary KEY inside JSON."); + return NRF_ERROR_INVALID_DATA; + } + + return NRF_SUCCESS; +} + + +/**@brief Function for parsing and processing config file. + * + * @param[out] p_dfu_firmware_desc Details of available firmware images (size and CRC) + * @param[out] p_filename Remote path to init file that should be downloaded in DFU process + * + * @return NRF_SUCCESS If config file is valid. Otherwise an error code indicating failure reason. + */ +static uint32_t app_dfu_config_process(iot_dfu_firmware_desc_t * p_dfu_firmware_desc, + char * p_filename) +{ + uint32_t err_code = NRF_SUCCESS; + iot_dfu_firmware_block_t app; +#if TFTP_SOFTDEVICE_UPGRADE_SUPPORT + iot_dfu_firmware_block_t sd; + iot_dfu_firmware_block_t bl; +#endif // TFTP_SOFTDEVICE_UPGRADE_SUPPORT + bool app_present = false; +#if TFTP_SOFTDEVICE_UPGRADE_SUPPORT + bool sd_present = false; + bool bl_present = false; +#endif // TFTP_SOFTDEVICE_UPGRADE_SUPPORT + + // Clear global parameters before parsing a new one. + memset(p_filename, 0, MAX_LENGTH_FILENAME); + + m_tftp_dfu_ctx.p_config_json = cJSON_Parse((const char *)m_tftp_dfu_ctx.config_mem); + if (m_tftp_dfu_ctx.p_config_json == NULL) + { + NRF_LOG_ERROR("JSON parse failed."); + return NRF_ERROR_INVALID_DATA; + } + +#if TFTP_SOFTDEVICE_UPGRADE_SUPPORT + if (details_parse(CONFIG_SD_KEY, &sd) == NRF_SUCCESS) + { + sd_present = true; + } + + if (details_parse(CONFIG_BL_KEY, &bl) == NRF_SUCCESS) + { + bl_present = true; + } +#endif // TFTP_SOFTDEVICE_UPGRADE_SUPPORT + + if (details_parse(CONFIG_APP_KEY, &app) == NRF_SUCCESS) + { + app_present = true; + } + +#if TFTP_SOFTDEVICE_UPGRADE_SUPPORT + // Background DFU does not support SoftDevice or bootloader update yet. + if ((sd_present) && (app_present)) + { + NRF_LOG_INFO("Update Softdevice with Application."); + m_tftp_dfu_ctx.image_type = TFTP_DFU_IMAGE_TYPE_SOFTDEVICE; + } + else if (bl_present) + { + NRF_LOG_INFO("Update Bootloader."); + m_tftp_dfu_ctx.image_type = TFTP_DFU_IMAGE_TYPE_BOOTLOADER; + } + else +#endif // TFTP_SOFTDEVICE_UPGRADE_SUPPORT + { + if (app_present) + { + NRF_LOG_INFO("Update Application only."); + m_tftp_dfu_ctx.image_type = TFTP_DFU_IMAGE_TYPE_APPLICATION; + } + else + { + // This example application does not implement SoftDevice with Bootloader update + NRF_LOG_INFO("Device firmware up to date."); + err_code = NRF_ERROR_NOT_FOUND; + } + } + + if (err_code == NRF_SUCCESS) + { + switch (m_tftp_dfu_ctx.image_type) + { + case TFTP_DFU_IMAGE_TYPE_APPLICATION: + err_code = get_init_path(p_filename, CONFIG_APP_KEY); + if (err_code == NRF_SUCCESS) + { + p_dfu_firmware_desc->application = app; + } + + break; + +#if TFTP_SOFTDEVICE_UPGRADE_SUPPORT + case TFTP_DFU_IMAGE_TYPE_BOOTLOADER: + err_code = get_init_path(p_filename, CONFIG_BL_KEY); + if (err_code == NRF_SUCCESS) + { + p_dfu_firmware_desc->bootloader = bl; + } + + break; + + case TFTP_DFU_IMAGE_TYPE_SOFTDEVICE: + err_code = get_init_path(p_filename, CONFIG_APPSD_KEY); + if (err_code == NRF_SUCCESS) + { + p_dfu_firmware_desc->softdevice = sd; + p_dfu_firmware_desc->application = app; + } + + break; +#endif // TFTP_SOFTDEVICE_UPGRADE_SUPPORT + + default: + ASSERT(false); + } + } + + if (p_filename[0] == '\0') + { + NRF_LOG_ERROR("File name has not be found."); + err_code = NRF_ERROR_INVALID_PARAM; + } + + cJSON_Delete(m_tftp_dfu_ctx.p_config_json); + + return err_code; +} + +/** @brief Set resource path to point to firmware binary file. + * + * @param [out] p_filename Pointer to resource path. + * + * @return NRF_SUCCESS if setting firmware path succeeded or error code indicating failure reason. + */ +static uint32_t set_firmware_path(char * p_filename) +{ + uint32_t err_code = NRF_SUCCESS; + + m_tftp_dfu_ctx.p_config_json = cJSON_Parse((const char *)m_tftp_dfu_ctx.config_mem); + if (m_tftp_dfu_ctx.p_config_json == NULL) + { + NRF_LOG_ERROR("JSON parse failed."); + return NRF_ERROR_INVALID_DATA; + } + + switch (m_tftp_dfu_ctx.image_type) + { + case TFTP_DFU_IMAGE_TYPE_APPLICATION: + err_code = get_path(p_filename, CONFIG_APP_KEY); + break; + + case TFTP_DFU_IMAGE_TYPE_BOOTLOADER: + err_code = get_path(p_filename, CONFIG_BL_KEY); + break; + + case TFTP_DFU_IMAGE_TYPE_SOFTDEVICE: + err_code = get_path(p_filename, CONFIG_APPSD_KEY); + break; + + default: + ASSERT(false); + } + + if (p_filename[0] == '\0') + { + NRF_LOG_ERROR("File name has not be found."); + err_code = NRF_ERROR_INVALID_PARAM; + } + + cJSON_Delete(m_tftp_dfu_ctx.p_config_json); + + return err_code; +} + +/** @brief Send download request to the TFTP server. + * + * @param[in] p_file Pointer to file instance used to store file. May be NULL if file is not + * stored by TFTP module. + */ +static void send_request(iot_file_t * p_file) +{ + uint32_t err_code; + iot_tftp_trans_params_t trans_params; + + trans_params.block_size = APP_TFTP_BLOCK_SIZE; + trans_params.next_retr = APP_TFTP_RETRANSMISSION_TIME; + + err_code = iot_tftp_set_params(&m_tftp_dfu_ctx.tftp, &trans_params); + ASSERT(err_code == NRF_SUCCESS); + + err_code = iot_tftp_get(&m_tftp_dfu_ctx.tftp, p_file, m_tftp_dfu_ctx.resource_path); + ASSERT(err_code == NRF_SUCCESS); + + UNUSED_VARIABLE(err_code); +} + +/*************************************************************************************************** + * @section TFTP handler + **************************************************************************************************/ + +/** + * @brief Process retrieved config file + * + * @param[in] p_file Pointer to retrieved config file. + */ +static void config_file_retrieved(iot_file_t * p_file) +{ + uint32_t err_code; + background_dfu_trigger_t trigger; + iot_dfu_firmware_block_t * p_firmware_block = NULL; + + NRF_LOG_INFO("Config file successfully downloaded."); + + m_tftp_dfu_ctx.config_mem[p_file->file_size] = 0; + memset(&m_tftp_dfu_ctx.firmware_desc, 0, sizeof(m_tftp_dfu_ctx.firmware_desc)); + + err_code = app_dfu_config_process(&m_tftp_dfu_ctx.firmware_desc, m_tftp_dfu_ctx.resource_path); + + if (err_code == NRF_SUCCESS) + { + NRF_LOG_INFO("New sofware available. Starting downloading procedure."); + + switch(m_tftp_dfu_ctx.image_type) + { + case TFTP_DFU_IMAGE_TYPE_APPLICATION: + p_firmware_block = &m_tftp_dfu_ctx.firmware_desc.application; + break; + + case TFTP_DFU_IMAGE_TYPE_BOOTLOADER: + p_firmware_block = &m_tftp_dfu_ctx.firmware_desc.bootloader; + break; + + case TFTP_DFU_IMAGE_TYPE_SOFTDEVICE: + p_firmware_block = &m_tftp_dfu_ctx.firmware_desc.softdevice; + break; + } + + memset(&trigger, 0, sizeof(trigger)); + trigger.init_length = uint32_big_decode((const uint8_t *)&p_firmware_block->init_size); + trigger.init_crc = uint32_big_decode((const uint8_t *)&p_firmware_block->init_crc); + trigger.image_length = uint32_big_decode((const uint8_t *)&p_firmware_block->size); + trigger.image_crc = uint32_big_decode((const uint8_t *)&p_firmware_block->crc); + + if (background_dfu_validate_trigger(&m_dfu_ctx, (uint8_t *)&trigger, sizeof(trigger))) + { + if (!background_dfu_process_trigger(&m_dfu_ctx, (uint8_t *)&trigger, sizeof(trigger))) + { + NRF_LOG_ERROR("Error in TFTP background_dfu_process_trigger"); + } + + if (m_dfu_ctx.dfu_state == BACKGROUND_DFU_IDLE) + { + // State in DFU_IDLE, nothing to download. + err_code = iot_tftp_uninit(&m_tftp_dfu_ctx.tftp); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in TFTP uninit (%d)", err_code); + } + } + } + } + else + { + NRF_LOG_INFO("No new sofware available or incorrect JSON file."); + } +} + +/** + * @brief Pass block of retrieved data to background DFU module. + * + * @param[in] p_block Pointer to block of retrieved data. + */ +static void process_block(uint8_t * p_block) +{ + background_dfu_block_t block; + background_dfu_block_result_t result; + + block.number = m_tftp_dfu_ctx.block_number; + block.size = DEFAULT_BLOCK_SIZE; + block.p_payload = p_block; + + m_tftp_dfu_ctx.block_number++; + + result = background_dfu_process_block(&m_dfu_ctx, &block); + if (result != BACKGROUND_DFU_BLOCK_SUCCESS) + { + NRF_LOG_ERROR("Error in TFTP background_dfu_process_block (%d)", result); + } +} + +/** + * @brief Process retrieved data chunk. + * + * Split retrieved data to blocks of DEFAULT_BLOCK_SIZE bytes and pass to DFU module. + * + * @param[in] p_data_received Pointer to structure describing received data chunk. + */ +static void data_chunk_retrieved(iot_tftp_evt_data_received_t * p_data_received) +{ + uint16_t processed_bytes = 0; + + // Process part of block from previous chunk + if (m_tftp_dfu_ctx.block_size) + { + uint16_t first_block_missing_size = DEFAULT_BLOCK_SIZE - m_tftp_dfu_ctx.block_size; + + if (p_data_received->size < first_block_missing_size) + { + first_block_missing_size = p_data_received->size; + } + + memcpy(&m_tftp_dfu_ctx.block[m_tftp_dfu_ctx.block_size], + p_data_received->p_data, + first_block_missing_size); + + process_block(m_tftp_dfu_ctx.block); + + processed_bytes = first_block_missing_size; + } + + // Process received chunks + while (p_data_received->size - processed_bytes >= DEFAULT_BLOCK_SIZE) + { + process_block(&p_data_received->p_data[processed_bytes]); + processed_bytes += DEFAULT_BLOCK_SIZE; + } + + m_tftp_dfu_ctx.block_size = p_data_received->size - processed_bytes; + + // Leave not processed data + if (p_data_received->size > processed_bytes) + { + memcpy(m_tftp_dfu_ctx.block, + &p_data_received->p_data[processed_bytes], + m_tftp_dfu_ctx.block_size); + } +} + +/** + * @brief Process data left from previous chunk. + */ +static void preserved_block_process(void) +{ + process_block(m_tftp_dfu_ctx.block); + m_tftp_dfu_ctx.block_size = 0; +} + +/** + * @brief Handler of TFTP events. + */ +static void tftp_dfu_tftp_handler(iot_tftp_t * p_tftp, iot_tftp_evt_t * p_evt) +{ + switch (p_evt->id) + { + case IOT_TFTP_EVT_TRANSFER_DATA_RECEIVED: + switch (m_dfu_ctx.dfu_state) + { + case BACKGROUND_DFU_DOWNLOAD_TRIG: + break; + + case BACKGROUND_DFU_DOWNLOAD_INIT_CMD: + case BACKGROUND_DFU_DOWNLOAD_FIRMWARE: + data_chunk_retrieved(&p_evt->param.data_received); + break; + + default: + ASSERT(false); + break; + } + break; + + case IOT_TFTP_EVT_TRANSFER_GET_COMPLETE: + switch (m_dfu_ctx.dfu_state) + { + case BACKGROUND_DFU_DOWNLOAD_TRIG: + config_file_retrieved(p_evt->p_file); + break; + + case BACKGROUND_DFU_DOWNLOAD_INIT_CMD: + case BACKGROUND_DFU_DOWNLOAD_FIRMWARE: + preserved_block_process(); + break; + + default: + ASSERT(false); + break; + } + break; + + case IOT_TFTP_EVT_ERROR: + background_dfu_handle_error(); + break; + + default: + ASSERT(false); + break; + } +} + +static void dfu_observer(nrf_dfu_evt_type_t evt_type) +{ + switch (evt_type) + { + case NRF_DFU_EVT_DFU_COMPLETED: + NRF_LOG_FINAL_FLUSH(); + +#if NRF_MODULE_ENABLED(NRF_LOG_BACKEND_RTT) + // To allow the buffer to be flushed by the host. + nrf_delay_ms(100); +#endif + + NVIC_SystemReset(); + break; + + default: + break; + } +} + + +/*************************************************************************************************** + * @section Private API + **************************************************************************************************/ + +void background_dfu_transport_block_request_send(background_dfu_context_t * p_dfu_ctx, + background_dfu_request_bitmap_t * p_req_bmp) +{ + // Intentionally empty: multicast DFU not implemented. +} + +void background_dfu_transport_send_request(background_dfu_context_t * p_dfu_ctx) +{ + switch (m_dfu_ctx.dfu_state) + { + case BACKGROUND_DFU_DOWNLOAD_TRIG: + send_request(&m_tftp_dfu_ctx.config_file); + break; + + default: + // In other states download operation is triggered by state_update() notification. + break; + } +} + +void background_dfu_transport_state_update(background_dfu_context_t * p_dfu_ctx) +{ + switch (p_dfu_ctx->dfu_state) + { + case BACKGROUND_DFU_DOWNLOAD_TRIG: + trigger_path_set(); + break; + + case BACKGROUND_DFU_DOWNLOAD_INIT_CMD: + m_tftp_dfu_ctx.block_number = 0; + m_tftp_dfu_ctx.block_size = 0; + send_request(NULL); + break; + + case BACKGROUND_DFU_DOWNLOAD_FIRMWARE: + m_tftp_dfu_ctx.block_number = 0; + m_tftp_dfu_ctx.block_size = 0; + UNUSED_RETURN_VALUE(set_firmware_path(m_tftp_dfu_ctx.resource_path)); + send_request(NULL); + break; + + case BACKGROUND_DFU_WAIT_FOR_RESET: + // Do nothing. + break; + + default: + NRF_LOG_WARNING("Unhandled state in background_dfu_transport_state_update (s: %s).", + (uint32_t)background_dfu_state_to_string(p_dfu_ctx->dfu_state)); + } +} + +uint32_t background_dfu_random(void) +{ + // Intentionally empty: multicast DFU not implemented. + return 0; +} + +/*************************************************************************************************** + * @section Public API + **************************************************************************************************/ + +uint32_t tftp_dfu_init(void) +{ + uint32_t err_code = NRF_SUCCESS; + + memset(&m_tftp_dfu_ctx, 0, sizeof(m_tftp_dfu_ctx)); + + err_code = nrf_dfu_settings_init(true); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + err_code = nrf_dfu_req_handler_init(dfu_observer); + if (err_code != NRF_SUCCESS) + { + return err_code; + } + + background_dfu_state_init(&m_dfu_ctx); + + cJSON_Init(); + + // Initialize file static instance. + IOT_FILE_STATIC_INIT(&m_tftp_dfu_ctx.config_file, + m_tftp_dfu_ctx.resource_path, + m_tftp_dfu_ctx.config_mem, + MAX_CONFIG_SIZE); + + return err_code; +} + +uint32_t tftp_dfu_trigger(const ipv6_addr_t * p_host_ipv6, uint16_t src_port, uint16_t dst_port) +{ + uint32_t err_code; + iot_tftp_init_t tftp_init_params; + + NRF_LOG_INFO("Triggering DFU"); + + if (p_host_ipv6 == NULL) + { + NRF_LOG_WARNING("NULL IPv6 address"); + return NRF_ERROR_NULL; + } + + if (m_dfu_ctx.dfu_state != BACKGROUND_DFU_IDLE) + { + NRF_LOG_WARNING("Invalid state"); + return NRF_ERROR_INVALID_STATE; + } + + // Set TFTP configuration + memset(&tftp_init_params, 0, sizeof(iot_tftp_init_t)); + tftp_init_params.p_ipv6_addr = (ipv6_addr_t *)p_host_ipv6; + tftp_init_params.src_port = src_port; + tftp_init_params.dst_port = dst_port; + tftp_init_params.callback = tftp_dfu_tftp_handler; + + // Initialize instance, bind socket, check parameters. + err_code = iot_tftp_init(&m_tftp_dfu_ctx.tftp, &tftp_init_params); + if (err_code != NRF_SUCCESS) + { + NRF_LOG_ERROR("Error in TFTP init (%d)", err_code); + return err_code; + } + + trigger_path_set(); + + // Transition from DFU_IDLE to DFU_DOWNLOAD_TRIG. + return background_dfu_handle_event(&m_dfu_ctx, BACKGROUND_DFU_EVENT_TRANSFER_COMPLETE); +} + +void background_dfu_handle_error(void) +{ + UNUSED_RETURN_VALUE(iot_tftp_uninit(&m_tftp_dfu_ctx.tftp)); + tftp_dfu_handle_error(); +} + +__WEAK void tftp_dfu_handle_error(void) +{ + +} + +bool nrf_dfu_button_enter_check(void) +{ + // Dummy function for Keil compilation. This should not be called. + return false; +} diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/tftp/tftp_dfu.h b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/tftp/tftp_dfu.h new file mode 100644 index 0000000..6c8370b --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/tftp/tftp_dfu.h @@ -0,0 +1,80 @@ +/** + * 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 tftp_dfu.h + * + * @defgroup tftp_dfu TFTP transport for Background DFU + * @ingroup iot_tftp_dfu + * @{ + * @brief TFTP transport for Background DFU. + * + */ + +#ifndef TFTP_DFU_H_ +#define TFTP_DFU_H_ + +#include <stdint.h> +#include "iot_defines.h" + +/** @brief Initialize DFU client. + * + * @returns NRF_SUCCESS if DFU procedure started. Otherwise an error code indicating problem. + */ +uint32_t tftp_dfu_init(void); + +/** @brief Trigger DFU. + * + * @param[in] p_host_ipv6 IPv6 address of TFTP host. + * @param[in] src_port Source UDP port used by TFTP service. + * @param[in] dst_port Destination UDP port used by TFTP service. + * + * @returns NRF_SUCCESS if DFU procedure started. Otherwise an error code indicating problem. + */ +uint32_t tftp_dfu_trigger(const ipv6_addr_t * p_host_ipv6, uint16_t src_port, uint16_t dst_port); + +/** @brief Handle DFU error. + * + * This function can be implemented in the application to undertake application-specific action on DFU error. + */ +extern void tftp_dfu_handle_error(void); + +#endif /* TFTP_DFU_H_ */ + +/** @} */ |