aboutsummaryrefslogtreecommitdiff
path: root/thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/tftp/tftp_dfu.c
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/tftp/tftp_dfu.c')
-rw-r--r--thirdparty/nRF5_SDK_15.0.0_a53641a/components/iot/background_dfu/transport/tftp/tftp_dfu.c928
1 files changed, 928 insertions, 0 deletions
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;
+}