This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Porting FreeRTOS into Thread Secure DFU example

Hi All, 

We've build multiple applications based on Thread. Now, with time our applications are getting bigger and complex, so we decided to integrate FreeRTOS into our applications. 

We have successfully integrated FreeRTOS into our applications except dfu part. 

I understand - this question has already been asked https://devzone.nordicsemi.com/f/nordic-q-a/67022/unable-to-add-freertos-to-thread-secure-dfu-example/274908#274908 

but that didn't conclude into any solution. 

What I understand from above Thread is that app_scheduler should not be used in an FreeRTOS environment. 

Then, there are multiple files in Thread Secure DFU example which makes a call to app_sched_event_put , I think the solution could be to replace app_sched_event_put in these 

files with FreeRTOS tasks/interrupts.  

Can someone suggest me some direction/code - how can I replace app_sched_event_put with FreeRTOS tasks ?  

Thanks. 

Parents
  • Then, there are multiple files in Thread Secure DFU example which makes a call to app_sched_event_put , I think the solution could be to replace app_sched_event_put in these 

    files with FreeRTOS tasks/interrupts.  

     Hi Ashish,

    If the handler inside the app_sched_event_put is static for a given call then it is quite easy to replace this handler with a FreeRTOS task and instead of calling app_sched_event_put you can replace it with a task queues. or any other task communication mechanism.

    The porting becomes hard only when app_sched_event_put is used with a dynamic handler pointer, which I do not think is the case in the DFU. I believe that it should be straight to do that, but havent looked into this in details, so I cannot be sure of how much efforts it would take.

  • Hi Susheel,  

    Just to ensure - I'm proceeding in the right direction, I'm attaching two files - background_dfu_block.c and nrf_dfu_req_handler.c,  that make a call to app_sched_event_put. 

    In my understanding - app_sched_event_put is being called with static handlers. 

    Please have a look into both files and confirm if I'm right.  

      
    
    
    
    /**
     * Copyright (c) 2016 - 2019, 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.
     *
     */
    #include <stdint.h>
    #include <stdbool.h>
    #include "sdk_config.h"
    #include "nrf_dfu.h"
    #include "nrf_dfu_types.h"
    #include "nrf_dfu_req_handler.h"
    #include "nrf_dfu_handling_error.h"
    #include "nrf_dfu_settings.h"
    #include "nrf_dfu_utils.h"
    #include "nrf_dfu_flash.h"
    #include "nrf_fstorage.h"
    #include "nrf_bootloader_info.h"
    #include "app_util.h"
    #include "pb.h"
    #include "pb_common.h"
    #include "pb_decode.h"
    #include "dfu-cc.pb.h"
    #include "crc32.h"
    #include "app_scheduler.h"
    #include "sdk_macros.h"
    #include "nrf_crypto.h"
    #include "nrf_assert.h"
    #include "nrf_dfu_validation.h"
    
    #define NRF_LOG_MODULE_NAME nrf_dfu_req_handler
    #include "nrf_log.h"
    NRF_LOG_MODULE_REGISTER();
    
    #define NRF_DFU_PROTOCOL_VERSION    (0x01)
    
    #ifndef NRF_DFU_PROTOCOL_REDUCED
    #define NRF_DFU_PROTOCOL_REDUCED 0
    #endif
    
    STATIC_ASSERT(DFU_SIGNED_COMMAND_SIZE <= INIT_COMMAND_MAX_SIZE);
    
    static uint32_t m_firmware_start_addr;          /**< Start address of the current firmware image. */
    static uint32_t m_firmware_size_req;            /**< The size of the entire firmware image. Defined by the init command. */
    
    static nrf_dfu_observer_t m_observer;
    
    
    static void on_dfu_complete(nrf_fstorage_evt_t * p_evt)
    {
        UNUSED_PARAMETER(p_evt);
    
        NRF_LOG_DEBUG("All flash operations have completed. DFU completed.");
    
        m_observer(NRF_DFU_EVT_DFU_COMPLETED);
    }
    
    
    static nrf_dfu_result_t ext_err_code_handle(nrf_dfu_result_t ret_val)
    {
        if (ret_val < NRF_DFU_RES_CODE_EXT_ERROR)
        {
            return ret_val;
        }
        else
        {
            nrf_dfu_ext_error_code_t ext_err =
                    (nrf_dfu_ext_error_code_t)((uint8_t)ret_val - (uint8_t)NRF_DFU_RES_CODE_EXT_ERROR);
            return ext_error_set(ext_err);
        }
    }
    
    
    #if !NRF_DFU_PROTOCOL_REDUCED
    static void on_protocol_version_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
    {
        UNUSED_PARAMETER(p_req);
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_PROTOCOL_VERSION");
    
        if (NRF_DFU_PROTOCOL_VERSION_MSG)
        {
            p_res->protocol.version = NRF_DFU_PROTOCOL_VERSION;
        }
        else
        {
            NRF_LOG_DEBUG("NRF_DFU_OP_PROTOCOL_VERSION disabled.");
            p_res->result = NRF_DFU_RES_CODE_OP_CODE_NOT_SUPPORTED;
        }
    }
    
    
    static void on_hw_version_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_HARDWARE_VERSION");
    
        p_res->hardware.part    = NRF_FICR->INFO.PART;
        p_res->hardware.variant = NRF_FICR->INFO.VARIANT;
    
        /* FICR values are in Kilobytes, we report them in bytes. */
        p_res->hardware.memory.ram_size      = NRF_FICR->INFO.RAM   * 1024;
        p_res->hardware.memory.rom_size      = NRF_FICR->INFO.FLASH * 1024;
        p_res->hardware.memory.rom_page_size = NRF_FICR->CODEPAGESIZE;
    }
    
    
    static void on_fw_version_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_FIRMWARE_VERSION");
        NRF_LOG_DEBUG("Firmware image requested: %d", p_req->firmware.image_number);
    
        if (NRF_DFU_PROTOCOL_FW_VERSION_MSG)
        {
            uint8_t fw_count = 1;
    
            if (SD_PRESENT)
            {
                fw_count++;
            }
    
            if (s_dfu_settings.bank_0.bank_code == NRF_DFU_BANK_VALID_APP)
            {
                fw_count++;
            }
    
            p_res->result = NRF_DFU_RES_CODE_SUCCESS;
    
            if (p_req->firmware.image_number == 0)
            {
                /* Bootloader is always present and it is always image zero. */
                p_res->firmware.type    = NRF_DFU_FIRMWARE_TYPE_BOOTLOADER;
                p_res->firmware.version = s_dfu_settings.bootloader_version;
                p_res->firmware.addr    = BOOTLOADER_START_ADDR;
                p_res->firmware.len     = BOOTLOADER_SIZE;
            }
            else if ((p_req->firmware.image_number == 1) && SD_PRESENT)
            {
                /* If a SoftDevice is present, it will be firmware image one. */
                p_res->firmware.type    = NRF_DFU_FIRMWARE_TYPE_SOFTDEVICE;
                p_res->firmware.version = SD_VERSION_GET(MBR_SIZE);
                p_res->firmware.addr    = MBR_SIZE;
                p_res->firmware.len     = SD_SIZE_GET(MBR_SIZE);
            }
            else if ((p_req->firmware.image_number < fw_count))
            {
                /* Either there is no SoftDevice and the firmware image requested is one,
                 * or there is a SoftDevice and the firmware image requested is two.
                 */
                p_res->firmware.type    = NRF_DFU_FIRMWARE_TYPE_APPLICATION;
                p_res->firmware.version = s_dfu_settings.app_version;
                p_res->firmware.addr    = nrf_dfu_app_start_address();
                p_res->firmware.len     = s_dfu_settings.bank_0.image_size;
            }
            else
            {
                NRF_LOG_DEBUG("No such firmware image");
                p_res->firmware.type    = NRF_DFU_FIRMWARE_TYPE_UNKNOWN;
                p_res->firmware.version = 0x00;
                p_res->firmware.addr    = 0x00;
                p_res->firmware.len     = 0x00;
            }
        }
        else
        {
            NRF_LOG_DEBUG("NRF_DFU_OP_FIRMWARE_VERSION disabled.");
            p_res->result        = NRF_DFU_RES_CODE_OP_CODE_NOT_SUPPORTED;
            p_res->firmware.type = NRF_DFU_FIRMWARE_TYPE_UNKNOWN;
        }
    }
    
    
    static void on_ping_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_PING");
        p_res->ping.id = p_req->ping.id;
    }
    
    
    static void on_mtu_get_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_MTU_GET");
        p_res->mtu.size = p_req->mtu.size;
    }
    #endif // !NRF_DFU_PROTOCOL_REDUCED
    
    
    static void on_prn_set_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        UNUSED_PARAMETER(p_req);
        UNUSED_PARAMETER(p_res);
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_RECEIPT_NOTIF_SET");
    }
    
    
    static void on_abort_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        UNUSED_PARAMETER(p_req);
        UNUSED_PARAMETER(p_res);
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_ABORT");
    
        m_observer(NRF_DFU_EVT_DFU_ABORTED);
    }
    
    
    /* Set offset and CRC fields in the response for a 'command' message. */
    static void cmd_response_offset_and_crc_set(nrf_dfu_response_t * const p_res)
    {
        ASSERT(p_res);
    
        /* Copy the CRC and offset of the init packet. */
        p_res->crc.offset = s_dfu_settings.progress.command_offset;
        p_res->crc.crc    = s_dfu_settings.progress.command_crc;
    }
    
    
    static void on_cmd_obj_select_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
    {
        UNUSED_PARAMETER(p_req);
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_SELECT (command)");
    
        p_res->select.max_size = INIT_COMMAND_MAX_SIZE;
        cmd_response_offset_and_crc_set(p_res);
    }
    
    
    static void on_cmd_obj_create_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        ASSERT(p_req);
        ASSERT(p_res);
    
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_CREATE (command)");
    
        m_observer(NRF_DFU_EVT_DFU_STARTED);
    
        nrf_dfu_result_t ret_val = nrf_dfu_validation_init_cmd_create(p_req->create.object_size);
        p_res->result = ext_err_code_handle(ret_val);
    }
    
    
    static void on_cmd_obj_write_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        ASSERT(p_req);
        ASSERT(p_req->write.p_data);
        ASSERT(p_req->write.len);
        ASSERT(p_res);
    
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_WRITE (command)");
    
        nrf_dfu_result_t ret_val;
    
        ret_val = nrf_dfu_validation_init_cmd_append(p_req->write.p_data, p_req->write.len);
        p_res->result = ext_err_code_handle(ret_val);
    
        /* Update response. This is only used when the PRN is triggered and the 'write' message
         * is answered with a CRC message and these field are copied into the response. */
        cmd_response_offset_and_crc_set(p_res);
    
        /* If a callback to free the request payload buffer was provided, invoke it now. */
        if (p_req->callback.write)
        {
            p_req->callback.write((void*)p_req->write.p_data);
        }
    }
    
    
    static void on_cmd_obj_execute_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
    {
        ASSERT(p_req);
        ASSERT(p_res);
    
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_EXECUTE (command)");
    
        nrf_dfu_result_t ret_val;
        ret_val = nrf_dfu_validation_init_cmd_execute(&m_firmware_start_addr, &m_firmware_size_req);
        p_res->result = ext_err_code_handle(ret_val);
    
        if (p_res->result == NRF_DFU_RES_CODE_SUCCESS)
        {
            if (nrf_dfu_settings_write_and_backup(NULL) == NRF_SUCCESS)
            {
                /* Setting DFU to initialized */
                NRF_LOG_DEBUG("Writing valid init command to flash.");
            }
            else
            {
                p_res->result = NRF_DFU_RES_CODE_OPERATION_FAILED;
            }
        }
    }
    
    
    static void on_cmd_obj_crc_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
    {
        UNUSED_PARAMETER(p_req);
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_CRC_GET (command)");
    
        cmd_response_offset_and_crc_set(p_res);
    }
    
    
    /** @brief Function handling command requests from the transport layer.
     *
     * @param   p_req[in]       Pointer to the structure holding the DFU request.
     * @param   p_res[out]      Pointer to the structure holding the DFU response.
     *
     * @retval NRF_SUCCESS      If the command request was executed successfully.
     *                          Any other error code indicates that the data request
     *                          could not be handled.
     */
    static void nrf_dfu_command_req(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        ASSERT(p_req);
        ASSERT(p_res);
    
        switch (p_req->request)
        {
            case NRF_DFU_OP_OBJECT_CREATE:
            {
                on_cmd_obj_create_request(p_req, p_res);
            } break;
    
            case NRF_DFU_OP_CRC_GET:
            {
                on_cmd_obj_crc_request(p_req, p_res);
            } break;
    
            case NRF_DFU_OP_OBJECT_WRITE:
            {
                on_cmd_obj_write_request(p_req, p_res);
            } break;
    
            case NRF_DFU_OP_OBJECT_EXECUTE:
            {
                on_cmd_obj_execute_request(p_req, p_res);
            } break;
    
            case NRF_DFU_OP_OBJECT_SELECT:
            {
                on_cmd_obj_select_request(p_req, p_res);
            } break;
    
            default:
            {
                ASSERT(false);
            } break;
        }
    }
    
    
    static void on_data_obj_select_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_SELECT (data)");
    
        p_res->select.crc    = s_dfu_settings.progress.firmware_image_crc;
        p_res->select.offset = s_dfu_settings.progress.firmware_image_offset;
    
        p_res->select.max_size = DATA_OBJECT_MAX_SIZE;
    
        NRF_LOG_DEBUG("crc = 0x%x, offset = 0x%x, max_size = 0x%x",
                      p_res->select.crc,
                      p_res->select.offset,
                      p_res->select.max_size);
    }
    
    
    static void on_data_obj_create_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_CREATE (data)");
    
        if (!nrf_dfu_validation_init_cmd_present())
        {
            /* Can't accept data because DFU isn't initialized by init command. */
            NRF_LOG_ERROR("Cannot create data object without valid init command");
            p_res->result = NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED;
            return;
        }
    
        if (p_req->create.object_size == 0)
        {
            NRF_LOG_ERROR("Object size cannot be 0.")
            p_res->result = NRF_DFU_RES_CODE_INVALID_PARAMETER;
            return;
        }
    
        if (  ((p_req->create.object_size & (CODE_PAGE_SIZE - 1)) != 0)
            && (s_dfu_settings.progress.firmware_image_offset_last + p_req->create.object_size != m_firmware_size_req))
        {
            NRF_LOG_ERROR("Object size must be page aligned");
            p_res->result = NRF_DFU_RES_CODE_INVALID_PARAMETER;
            return;
        }
    
        if (p_req->create.object_size > DATA_OBJECT_MAX_SIZE)
        {
            /* It is impossible to handle the command because the size is too large */
            NRF_LOG_ERROR("Invalid size for object (too large)");
            p_res->result = NRF_DFU_RES_CODE_INSUFFICIENT_RESOURCES;
            return;
        }
    
        if ((s_dfu_settings.progress.firmware_image_offset_last + p_req->create.object_size) >
            m_firmware_size_req)
        {
            NRF_LOG_ERROR("Creating the object with size 0x%08x would overflow firmware size. "
                          "Offset is 0x%08x and firmware size is 0x%08x.",
                          p_req->create.object_size,
                          s_dfu_settings.progress.firmware_image_offset_last,
                          m_firmware_size_req);
    
            p_res->result = NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED;
            return;
        }
    
        s_dfu_settings.progress.data_object_size      = p_req->create.object_size;
        s_dfu_settings.progress.firmware_image_crc    = s_dfu_settings.progress.firmware_image_crc_last;
        s_dfu_settings.progress.firmware_image_offset = s_dfu_settings.progress.firmware_image_offset_last;
        s_dfu_settings.write_offset                   = s_dfu_settings.progress.firmware_image_offset_last;
    
        /* Erase the page we're at. */
        if (nrf_dfu_flash_erase((m_firmware_start_addr + s_dfu_settings.progress.firmware_image_offset),
                                CEIL_DIV(p_req->create.object_size, CODE_PAGE_SIZE), NULL) != NRF_SUCCESS)
        {
            NRF_LOG_ERROR("Erase operation failed");
            p_res->result = NRF_DFU_RES_CODE_INVALID_OBJECT;
            return;
        }
    
        NRF_LOG_DEBUG("Creating object with size: %d. Offset: 0x%08x, CRC: 0x%08x",
                     s_dfu_settings.progress.data_object_size,
                     s_dfu_settings.progress.firmware_image_offset,
                     s_dfu_settings.progress.firmware_image_crc);
    }
    
    
    static void on_data_obj_write_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_WRITE (data)");
    
        if (!nrf_dfu_validation_init_cmd_present())
        {
            /* Can't accept data because DFU isn't initialized by init command. */
            p_res->result = NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED;
            return;
        }
    
        uint32_t const data_object_offset = s_dfu_settings.progress.firmware_image_offset -
                                            s_dfu_settings.progress.firmware_image_offset_last;
    
        if ((p_req->write.len + data_object_offset) > s_dfu_settings.progress.data_object_size)
        {
            /* Can't accept data because too much data has been received. */
            NRF_LOG_ERROR("Write request too long");
            p_res->result = NRF_DFU_RES_CODE_INVALID_PARAMETER;
            return;
        }
    
        uint32_t const write_addr = m_firmware_start_addr + s_dfu_settings.write_offset;
        /* CRC must be calculated before handing off the data to fstorage because the data is
         * freed on write completion.
         */
        uint32_t const next_crc =
            crc32_compute(p_req->write.p_data, p_req->write.len, &s_dfu_settings.progress.firmware_image_crc);
    
        ASSERT(p_req->callback.write);
    
        ret_code_t ret =
            nrf_dfu_flash_store(write_addr, p_req->write.p_data, p_req->write.len, p_req->callback.write);
    
        if (ret != NRF_SUCCESS)
        {
            /* When nrf_dfu_flash_store() fails because there is no space in the queue,
             * stop processing the request so that the peer can detect a CRC error
             * and retransmit this object. Remember to manually free the buffer !
             */
            p_req->callback.write((void*)p_req->write.p_data);
            return;
        }
    
        /* Update the CRC of the firmware image. */
        s_dfu_settings.write_offset                   += p_req->write.len;
        s_dfu_settings.progress.firmware_image_offset += p_req->write.len;
        s_dfu_settings.progress.firmware_image_crc     = next_crc;
    
        /* This is only used when the PRN is triggered and the 'write' message
         * is answered with a CRC message and these field are copied into the response.
         */
        p_res->write.crc    = s_dfu_settings.progress.firmware_image_crc;
        p_res->write.offset = s_dfu_settings.progress.firmware_image_offset;
    }
    
    
    static void on_data_obj_crc_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_CRC_GET (data)");
        NRF_LOG_DEBUG("Offset:%d, CRC:0x%08x",
                     s_dfu_settings.progress.firmware_image_offset,
                     s_dfu_settings.progress.firmware_image_crc);
    
        p_res->crc.crc    = s_dfu_settings.progress.firmware_image_crc;
        p_res->crc.offset = s_dfu_settings.progress.firmware_image_offset;
    }
    
    
    static void on_data_obj_execute_request_sched(void * p_evt, uint16_t event_length)
    {
        UNUSED_PARAMETER(event_length);
    
        ret_code_t          ret;
        nrf_dfu_request_t * p_req = (nrf_dfu_request_t *)(p_evt);
    
        /* Wait for all buffers to be written in flash. */
        if (nrf_fstorage_is_busy(NULL))
        {
            ret = app_sched_event_put(p_req, sizeof(nrf_dfu_request_t), on_data_obj_execute_request_sched);
            if (ret != NRF_SUCCESS)
            {
                NRF_LOG_ERROR("Failed to schedule object execute: 0x%x.", ret);
            }
            return;
        }
    
        nrf_dfu_response_t res =
        {
            .request = NRF_DFU_OP_OBJECT_EXECUTE,
        };
    
        if (s_dfu_settings.progress.firmware_image_offset == m_firmware_size_req)
        {
            NRF_LOG_DEBUG("Whole firmware image received. Postvalidating.");
    
            #if NRF_DFU_IN_APP
            res.result = nrf_dfu_validation_post_data_execute(m_firmware_start_addr, m_firmware_size_req);
            #else
            res.result = nrf_dfu_validation_activation_prepare(m_firmware_start_addr, m_firmware_size_req);
            #endif
    
            res.result = ext_err_code_handle(res.result);
    
            /* Provide response to transport */
            p_req->callback.response(&res, p_req->p_context);
    
            ret = nrf_dfu_settings_write_and_backup((nrf_dfu_flash_callback_t)on_dfu_complete);
            UNUSED_RETURN_VALUE(ret);
        }
        else
        {
            res.result = NRF_DFU_RES_CODE_SUCCESS;
    
            /* Provide response to transport */
            p_req->callback.response(&res, p_req->p_context);
    
            if (NRF_DFU_SAVE_PROGRESS_IN_FLASH)
            {
                /* Allowing skipping settings backup to save time and flash wear. */
                ret = nrf_dfu_settings_write_and_backup(NULL);
                UNUSED_RETURN_VALUE(ret);
            }
        }
    
        NRF_LOG_DEBUG("Request handling complete. Result: 0x%x", res.result);
    }
    
    
    static bool on_data_obj_execute_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_EXECUTE (data)");
    
        uint32_t const data_object_size = s_dfu_settings.progress.firmware_image_offset -
                                          s_dfu_settings.progress.firmware_image_offset_last;
    
        if (s_dfu_settings.progress.data_object_size != data_object_size)
        {
            /* The size of the written object was not as expected. */
            NRF_LOG_ERROR("Invalid data. expected: %d, got: %d",
                          s_dfu_settings.progress.data_object_size,
                          data_object_size);
    
            p_res->result = NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED;
            return true;
        }
    
        /* Update the offset and crc values for the last object written. */
        s_dfu_settings.progress.data_object_size           = 0;
        s_dfu_settings.progress.firmware_image_crc_last    = s_dfu_settings.progress.firmware_image_crc;
        s_dfu_settings.progress.firmware_image_offset_last = s_dfu_settings.progress.firmware_image_offset;
    
        on_data_obj_execute_request_sched(p_req, 0);
    
        m_observer(NRF_DFU_EVT_OBJECT_RECEIVED);
    
        return false;
    }
    
    
    static bool nrf_dfu_data_req(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        ASSERT(p_req);
        ASSERT(p_res);
    
        bool response_ready = true;
    
        switch (p_req->request)
        {
            case NRF_DFU_OP_OBJECT_CREATE:
            {
                on_data_obj_create_request(p_req, p_res);
            } break;
    
            case NRF_DFU_OP_OBJECT_WRITE:
            {
                on_data_obj_write_request(p_req, p_res);
            } break;
    
            case NRF_DFU_OP_CRC_GET:
            {
                on_data_obj_crc_request(p_req, p_res);
            } break;
    
            case NRF_DFU_OP_OBJECT_EXECUTE:
            {
                response_ready = on_data_obj_execute_request(p_req, p_res);
            } break;
    
            case NRF_DFU_OP_OBJECT_SELECT:
            {
                on_data_obj_select_request(p_req, p_res);
            } break;
    
            default:
            {
                ASSERT(false);
            } break;
        }
    
        return response_ready;
    }
    
    
    /**@brief Function for handling requests to manipulate data or command objects.
     *
     * @param[in]  p_req    Request.
     * @param[out] p_res    Response.
     *
     * @return  Whether response is ready to be sent.
     */
    static bool nrf_dfu_obj_op(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        /* Keep track of the current object type since write and execute requests don't contain it. */
        static nrf_dfu_obj_type_t current_object = NRF_DFU_OBJ_TYPE_COMMAND;
    
        if (    (p_req->request == NRF_DFU_OP_OBJECT_SELECT)
            ||  (p_req->request == NRF_DFU_OP_OBJECT_CREATE))
        {
            STATIC_ASSERT(offsetof(nrf_dfu_request_select_t, object_type) ==
                          offsetof(nrf_dfu_request_create_t, object_type),
                          "Wrong object_type offset!");
    
            current_object = (nrf_dfu_obj_type_t)(p_req->select.object_type);
        }
    
        bool response_ready = true;
    
        switch (current_object)
        {
            case NRF_DFU_OBJ_TYPE_COMMAND:
                nrf_dfu_command_req(p_req, p_res);
                break;
    
            case NRF_DFU_OBJ_TYPE_DATA:
                response_ready = nrf_dfu_data_req(p_req, p_res);
                break;
    
            default:
                /* The select request had an invalid object type. */
                NRF_LOG_ERROR("Invalid object type in request.");
                current_object = NRF_DFU_OBJ_TYPE_INVALID;
                p_res->result  = NRF_DFU_RES_CODE_INVALID_OBJECT;
                break;
        }
    
        return response_ready;
    }
    
    
    static void nrf_dfu_req_handler_req_process(nrf_dfu_request_t * p_req)
    {
        ASSERT(p_req->callback.response);
    
        bool response_ready = true;
    
        /* The request handlers assume these values to be set. */
        nrf_dfu_response_t response =
        {
            .request = p_req->request,
            .result  = NRF_DFU_RES_CODE_SUCCESS,
        };
    
    
        switch (p_req->request)
        {
    #if !NRF_DFU_PROTOCOL_REDUCED
            case NRF_DFU_OP_PROTOCOL_VERSION:
            {
                on_protocol_version_request(p_req, &response);
            } break;
    
            case NRF_DFU_OP_HARDWARE_VERSION:
            {
                on_hw_version_request(p_req, &response);
            } break;
    
            case NRF_DFU_OP_FIRMWARE_VERSION:
            {
                on_fw_version_request(p_req, &response);
            } break;
    
            case NRF_DFU_OP_PING:
            {
                on_ping_request(p_req, &response);
            } break;
    
            case NRF_DFU_OP_MTU_GET:
            {
                on_mtu_get_request(p_req, &response);
            } break;
    #endif
            case NRF_DFU_OP_RECEIPT_NOTIF_SET:
            {
                on_prn_set_request(p_req, &response);
            } break;
    
            case NRF_DFU_OP_ABORT:
            {
                on_abort_request(p_req, &response);
            } break;
    
            case NRF_DFU_OP_OBJECT_CREATE:
                /* Restart the inactivity timer on CREATE messages. */
                /* Fallthrough. */
            case NRF_DFU_OP_OBJECT_SELECT:
            case NRF_DFU_OP_OBJECT_WRITE:
            case NRF_DFU_OP_OBJECT_EXECUTE:
            case NRF_DFU_OP_CRC_GET:
            {
                response_ready = nrf_dfu_obj_op(p_req, &response);
            } break;
    
            default:
                NRF_LOG_INFO("Invalid opcode received: 0x%x.", p_req->request);
                response.result = NRF_DFU_RES_CODE_OP_CODE_NOT_SUPPORTED;
                break;
        }
    
        if (response_ready)
        {
            NRF_LOG_DEBUG("Request handling complete. Result: 0x%x", response.result);
    
            p_req->callback.response(&response, p_req->p_context);
    
            if (response.result != NRF_DFU_RES_CODE_SUCCESS)
            {
                m_observer(NRF_DFU_EVT_DFU_FAILED);
            }
        }
    }
    
    
    static void nrf_dfu_req_handler_req(void * p_evt, uint16_t event_length)
    {
        nrf_dfu_request_t * p_req = (nrf_dfu_request_t *)(p_evt);
        nrf_dfu_req_handler_req_process(p_req);
    }
    
    
    ret_code_t nrf_dfu_req_handler_on_req(nrf_dfu_request_t * p_req)
    {
        ret_code_t ret;
    
        if (p_req->callback.response == NULL)
        {
            return NRF_ERROR_INVALID_PARAM;
        }
    
        ret = app_sched_event_put(p_req, sizeof(nrf_dfu_request_t), nrf_dfu_req_handler_req);
        if (ret != NRF_SUCCESS)
        {
            NRF_LOG_WARNING("Scheduler ran out of space!");
        }
    
        return ret;
    }
    
    
    ret_code_t nrf_dfu_req_handler_init(nrf_dfu_observer_t observer)
    {
        ret_code_t       ret_val;
        nrf_dfu_result_t result;
    
        if (observer == NULL)
        {
            return NRF_ERROR_INVALID_PARAM;
        }
    
    #if defined(BLE_STACK_SUPPORT_REQD) || defined(ANT_STACK_SUPPORT_REQD)
        ret_val  = nrf_dfu_flash_init(true);
    #else
        ret_val = nrf_dfu_flash_init(false);
    #endif
        if (ret_val != NRF_SUCCESS)
        {
            return ret_val;
        }
    
        nrf_dfu_validation_init();
        if (nrf_dfu_validation_init_cmd_present())
        {
            /* Execute a previously received init packed. Subsequent executes will have no effect. */
            result = nrf_dfu_validation_init_cmd_execute(&m_firmware_start_addr, &m_firmware_size_req);
            if (result != NRF_DFU_RES_CODE_SUCCESS)
            {
                /* Init packet in flash is not valid! */
                return NRF_ERROR_INTERNAL;
            }
        }
    
        m_observer = observer;
    
        /* Initialize extended error handling with "No error" as the most recent error. */
        result = ext_error_set(NRF_DFU_EXT_ERROR_NO_ERROR);
        UNUSED_RETURN_VALUE(result);
    
        return NRF_SUCCESS;
    }
    
    /**
     * Copyright (c) 2017 - 2019, 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))
    
    /**@brief A maximum number of concurrent delayed context structures. */
    #ifndef SOFTDEVICE_PRESENT
    #define DFU_FLASH_OPERATION_OP_ENTRIES 2
    #else
    #define DFU_FLASH_OPERATION_OP_ENTRIES NRF_FSTORAGE_SD_QUEUE_SIZE
    #endif
    
    /**@brief An enumeration describing delayed flash operation status. */
    typedef enum
    {
        STATUS_NONE,
        STATUS_REGISTERED,
        STATUS_FLASH_DONE,
        STATUS_PROCESSING_DONE,
        STATUS_CALLBACK_CALLED
    } dfu_flash_operation_status_t;
    
    /**@brief A structure representing delayed DFU context. */
    typedef struct
    {
        nrf_dfu_response_t             response;
        void                         * p_context;
        void                         * p_buf;
        dfu_flash_operation_status_t   status;
    } dfu_flash_operation_ctx_t;
    
    
    /**@brief An array storing delayed DFU context structures.
     */
    static dfu_flash_operation_ctx_t m_flash_operations[DFU_FLASH_OPERATION_OP_ENTRIES];
    
    
    static void block_buffer_store(background_dfu_block_manager_t * p_bm);
    static void dfu_operation_callback(nrf_dfu_response_t * p_res, void * p_context);
    
    
    /**@brief A function to initialize delayed DFU context module.
     */
    static void dfu_flash_operation_init(void)
    {
        uint32_t i;
    
        for (i = 0; i < DFU_FLASH_OPERATION_OP_ENTRIES; i++)
        {
            m_flash_operations[i].status = STATUS_NONE;
        }
    }
    
    /**@brief A function to register a new context for delayed DFU operations.
     *
     * @param[in] p_buf      Pointer to the memory buffer, that will be used for flash operation.
     * @param[in] p_context  Pointer to the DFU request context.
     *
     * @return NRF_SUCCESS if a new context entry was registered, NRF_ERROR_NO_MEM otherwise.
     */
    static ret_code_t dfu_flash_operation_register(void * p_buf, void * p_context)
    {
        uint32_t i;
    
        for (i = 0; i < DFU_FLASH_OPERATION_OP_ENTRIES; i++)
        {
            if (m_flash_operations[i].status == STATUS_NONE)
            {
                m_flash_operations[i].status    = STATUS_REGISTERED;
                m_flash_operations[i].p_buf     = p_buf;
                m_flash_operations[i].p_context = p_context;
                memset(&m_flash_operations[i].response, 0, sizeof(nrf_dfu_response_t));
    
                return NRF_SUCCESS;
            }
        }
    
        return NRF_ERROR_NO_MEM;
    }
    
    /**@brief A function to get a context structure based on DFU block context.
     *
     * @param[in] p_context  Pointer to the DFU request context.
     *
     * @return Pointer to the found delayed DFU context structure, NULL otherwise.
     */
    static dfu_flash_operation_ctx_t * dfu_flash_operation_find_by_context(void * p_context)
    {
        uint32_t i;
    
        for (i = 0; i < DFU_FLASH_OPERATION_OP_ENTRIES; i++)
        {
            if ((m_flash_operations[i].status != STATUS_NONE) && (m_flash_operations[i].p_context == p_context))
            {
                return &m_flash_operations[i];
            }
        }
    
        return NULL;
    }
    
    /**@brief A function to get a context structure based on flash operation buffer address.
     *
     * @param[in] p_buf      Pointer to the memory buffer, that will be used for flash operation.
     *
     * @return Pointer to the found delayed DFU context structure, NULL otherwise.
     */
    static dfu_flash_operation_ctx_t * dfu_flash_operation_find_by_buffer(void * p_buf)
    {
        uint32_t i;
    
        for (i = 0; i < DFU_FLASH_OPERATION_OP_ENTRIES; i++)
        {
            if ((m_flash_operations[i].status != STATUS_NONE) && (m_flash_operations[i].p_buf == p_buf))
            {
                return &m_flash_operations[i];
            }
        }
    
        return NULL;
    }
    
    /**@brief A callback function for processing events, implementing delayed DFU operation.
     *
     * @param[in] p_res      Pointer to the DFU response structure.
     * @param[in] p_context  Pointer to the DFU request context.
     */
    static void dfu_operation_callback_delayed(nrf_dfu_response_t * p_res, void * p_context)
    {
        dfu_flash_operation_ctx_t * p_flash_ctx = dfu_flash_operation_find_by_context(p_context);
        bool                        call        = false;
    
        if (p_res->result != NRF_DFU_RES_CODE_SUCCESS)
        {
            dfu_operation_callback(p_res, p_context);
            return;
        }
    
        if (p_flash_ctx == NULL)
        {
            return;
        }
    
        CRITICAL_REGION_ENTER();
        switch(p_flash_ctx->status)
        {
            case STATUS_REGISTERED:
                // Store operation result for a delayed call.
                p_flash_ctx->response = *p_res;
                p_flash_ctx->status   = STATUS_PROCESSING_DONE;
                break;
    
            case STATUS_FLASH_DONE:
                p_flash_ctx->status = STATUS_CALLBACK_CALLED;
                call = true;
                break;
    
            case STATUS_NONE:
            case STATUS_PROCESSING_DONE:
            case STATUS_CALLBACK_CALLED:
            default:
                break;
        }
        CRITICAL_REGION_EXIT();
    
        if (call)
        {
            // If both: flash and processing callbacks received - call main state machine.
            dfu_operation_callback(p_res, p_flash_ctx->p_context);
            p_flash_ctx->status = STATUS_NONE;
        }
    }
    
    /**@brief A callback function for flash events, implementing delayed DFU operation.
     *
     * @param[in] p_buf  Pointer to the memory buffer, related to the finished flash operation.
     */
    static void dfu_operation_flash_callback(void * p_buf)
    {
        dfu_flash_operation_ctx_t * p_flash_ctx = dfu_flash_operation_find_by_buffer(p_buf);
        bool                        call        = false;
    
        if (p_flash_ctx == NULL)
        {
            return;
        }
    
        CRITICAL_REGION_ENTER();
        switch(p_flash_ctx->status)
        {
            case STATUS_REGISTERED:
                p_flash_ctx->status = STATUS_FLASH_DONE;
                break;
    
            case STATUS_PROCESSING_DONE:
                p_flash_ctx->status = STATUS_CALLBACK_CALLED;
                call = true;
                break;
    
            case STATUS_NONE:
            case STATUS_FLASH_DONE:
            case STATUS_CALLBACK_CALLED:
            default:
                break;
        }
        CRITICAL_REGION_EXIT();
    
        if (call)
        {
            // If both: flash and processing callbacks received - call main state machine.
            dfu_operation_callback(&p_flash_ctx->response, p_flash_ctx->p_context);
            p_flash_ctx->status = STATUS_NONE;
        }
    }
    
    /**@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 ret_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));
    
                    ret_code = dfu_flash_operation_register(p_bm->data + data_offset, p_bm);
                    if (ret_code == NRF_SUCCESS)
                    {
                        ret_code = background_dfu_op_write(p_bm->data + data_offset,
                                                           store_size,
                                                           dfu_operation_callback_delayed,
                                                           dfu_operation_flash_callback,
                                                           p_bm);
                    }
                    else
                    {
                        ret_code = background_dfu_op_write(p_bm->data + data_offset,
                                                           store_size,
                                                           dfu_operation_callback,
                                                           dfu_operation_flash_callback,
                                                           p_bm);
                    }
    
                    if (ret_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) == (int32_t)BLOCKS_PER_SIZE(p_bm->image_size)))
                    {
                        ret_code = background_dfu_op_crc(dfu_operation_callback, p_bm);
    
                        if (ret_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:
                    ret_code = background_dfu_op_execute(dfu_operation_callback, p_bm);
    
                    if (ret_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 ret_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));
    
                ret_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));
    
            ret_code = dfu_flash_operation_register(p_bm->data + data_offset, p_bm);
            if (ret_code == NRF_SUCCESS)
            {
                ret_code = background_dfu_op_write(p_bm->data + data_offset,
                                                   store_size,
                                                   dfu_operation_callback_delayed,
                                                   dfu_operation_flash_callback,
                                                   p_bm);
            }
            else
            {
                ret_code = background_dfu_op_write(p_bm->data + data_offset,
                                                   store_size,
                                                   dfu_operation_callback,
                                                   dfu_operation_flash_callback,
                                                   p_bm);
            }
    
        } while (0);
        return ret_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 ret_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.
                    ret_code = block_store(p_bm, block);
                    if (ret_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));
        dfu_flash_operation_init();
    }
    
    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)
    {
        int32_t image_blocks = (int32_t)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)
    {
        int32_t image_blocks = (int32_t)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;
    }
    
Reply
  • Hi Susheel,  

    Just to ensure - I'm proceeding in the right direction, I'm attaching two files - background_dfu_block.c and nrf_dfu_req_handler.c,  that make a call to app_sched_event_put. 

    In my understanding - app_sched_event_put is being called with static handlers. 

    Please have a look into both files and confirm if I'm right.  

      
    
    
    
    /**
     * Copyright (c) 2016 - 2019, 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.
     *
     */
    #include <stdint.h>
    #include <stdbool.h>
    #include "sdk_config.h"
    #include "nrf_dfu.h"
    #include "nrf_dfu_types.h"
    #include "nrf_dfu_req_handler.h"
    #include "nrf_dfu_handling_error.h"
    #include "nrf_dfu_settings.h"
    #include "nrf_dfu_utils.h"
    #include "nrf_dfu_flash.h"
    #include "nrf_fstorage.h"
    #include "nrf_bootloader_info.h"
    #include "app_util.h"
    #include "pb.h"
    #include "pb_common.h"
    #include "pb_decode.h"
    #include "dfu-cc.pb.h"
    #include "crc32.h"
    #include "app_scheduler.h"
    #include "sdk_macros.h"
    #include "nrf_crypto.h"
    #include "nrf_assert.h"
    #include "nrf_dfu_validation.h"
    
    #define NRF_LOG_MODULE_NAME nrf_dfu_req_handler
    #include "nrf_log.h"
    NRF_LOG_MODULE_REGISTER();
    
    #define NRF_DFU_PROTOCOL_VERSION    (0x01)
    
    #ifndef NRF_DFU_PROTOCOL_REDUCED
    #define NRF_DFU_PROTOCOL_REDUCED 0
    #endif
    
    STATIC_ASSERT(DFU_SIGNED_COMMAND_SIZE <= INIT_COMMAND_MAX_SIZE);
    
    static uint32_t m_firmware_start_addr;          /**< Start address of the current firmware image. */
    static uint32_t m_firmware_size_req;            /**< The size of the entire firmware image. Defined by the init command. */
    
    static nrf_dfu_observer_t m_observer;
    
    
    static void on_dfu_complete(nrf_fstorage_evt_t * p_evt)
    {
        UNUSED_PARAMETER(p_evt);
    
        NRF_LOG_DEBUG("All flash operations have completed. DFU completed.");
    
        m_observer(NRF_DFU_EVT_DFU_COMPLETED);
    }
    
    
    static nrf_dfu_result_t ext_err_code_handle(nrf_dfu_result_t ret_val)
    {
        if (ret_val < NRF_DFU_RES_CODE_EXT_ERROR)
        {
            return ret_val;
        }
        else
        {
            nrf_dfu_ext_error_code_t ext_err =
                    (nrf_dfu_ext_error_code_t)((uint8_t)ret_val - (uint8_t)NRF_DFU_RES_CODE_EXT_ERROR);
            return ext_error_set(ext_err);
        }
    }
    
    
    #if !NRF_DFU_PROTOCOL_REDUCED
    static void on_protocol_version_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
    {
        UNUSED_PARAMETER(p_req);
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_PROTOCOL_VERSION");
    
        if (NRF_DFU_PROTOCOL_VERSION_MSG)
        {
            p_res->protocol.version = NRF_DFU_PROTOCOL_VERSION;
        }
        else
        {
            NRF_LOG_DEBUG("NRF_DFU_OP_PROTOCOL_VERSION disabled.");
            p_res->result = NRF_DFU_RES_CODE_OP_CODE_NOT_SUPPORTED;
        }
    }
    
    
    static void on_hw_version_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_HARDWARE_VERSION");
    
        p_res->hardware.part    = NRF_FICR->INFO.PART;
        p_res->hardware.variant = NRF_FICR->INFO.VARIANT;
    
        /* FICR values are in Kilobytes, we report them in bytes. */
        p_res->hardware.memory.ram_size      = NRF_FICR->INFO.RAM   * 1024;
        p_res->hardware.memory.rom_size      = NRF_FICR->INFO.FLASH * 1024;
        p_res->hardware.memory.rom_page_size = NRF_FICR->CODEPAGESIZE;
    }
    
    
    static void on_fw_version_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_FIRMWARE_VERSION");
        NRF_LOG_DEBUG("Firmware image requested: %d", p_req->firmware.image_number);
    
        if (NRF_DFU_PROTOCOL_FW_VERSION_MSG)
        {
            uint8_t fw_count = 1;
    
            if (SD_PRESENT)
            {
                fw_count++;
            }
    
            if (s_dfu_settings.bank_0.bank_code == NRF_DFU_BANK_VALID_APP)
            {
                fw_count++;
            }
    
            p_res->result = NRF_DFU_RES_CODE_SUCCESS;
    
            if (p_req->firmware.image_number == 0)
            {
                /* Bootloader is always present and it is always image zero. */
                p_res->firmware.type    = NRF_DFU_FIRMWARE_TYPE_BOOTLOADER;
                p_res->firmware.version = s_dfu_settings.bootloader_version;
                p_res->firmware.addr    = BOOTLOADER_START_ADDR;
                p_res->firmware.len     = BOOTLOADER_SIZE;
            }
            else if ((p_req->firmware.image_number == 1) && SD_PRESENT)
            {
                /* If a SoftDevice is present, it will be firmware image one. */
                p_res->firmware.type    = NRF_DFU_FIRMWARE_TYPE_SOFTDEVICE;
                p_res->firmware.version = SD_VERSION_GET(MBR_SIZE);
                p_res->firmware.addr    = MBR_SIZE;
                p_res->firmware.len     = SD_SIZE_GET(MBR_SIZE);
            }
            else if ((p_req->firmware.image_number < fw_count))
            {
                /* Either there is no SoftDevice and the firmware image requested is one,
                 * or there is a SoftDevice and the firmware image requested is two.
                 */
                p_res->firmware.type    = NRF_DFU_FIRMWARE_TYPE_APPLICATION;
                p_res->firmware.version = s_dfu_settings.app_version;
                p_res->firmware.addr    = nrf_dfu_app_start_address();
                p_res->firmware.len     = s_dfu_settings.bank_0.image_size;
            }
            else
            {
                NRF_LOG_DEBUG("No such firmware image");
                p_res->firmware.type    = NRF_DFU_FIRMWARE_TYPE_UNKNOWN;
                p_res->firmware.version = 0x00;
                p_res->firmware.addr    = 0x00;
                p_res->firmware.len     = 0x00;
            }
        }
        else
        {
            NRF_LOG_DEBUG("NRF_DFU_OP_FIRMWARE_VERSION disabled.");
            p_res->result        = NRF_DFU_RES_CODE_OP_CODE_NOT_SUPPORTED;
            p_res->firmware.type = NRF_DFU_FIRMWARE_TYPE_UNKNOWN;
        }
    }
    
    
    static void on_ping_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_PING");
        p_res->ping.id = p_req->ping.id;
    }
    
    
    static void on_mtu_get_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_MTU_GET");
        p_res->mtu.size = p_req->mtu.size;
    }
    #endif // !NRF_DFU_PROTOCOL_REDUCED
    
    
    static void on_prn_set_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        UNUSED_PARAMETER(p_req);
        UNUSED_PARAMETER(p_res);
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_RECEIPT_NOTIF_SET");
    }
    
    
    static void on_abort_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        UNUSED_PARAMETER(p_req);
        UNUSED_PARAMETER(p_res);
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_ABORT");
    
        m_observer(NRF_DFU_EVT_DFU_ABORTED);
    }
    
    
    /* Set offset and CRC fields in the response for a 'command' message. */
    static void cmd_response_offset_and_crc_set(nrf_dfu_response_t * const p_res)
    {
        ASSERT(p_res);
    
        /* Copy the CRC and offset of the init packet. */
        p_res->crc.offset = s_dfu_settings.progress.command_offset;
        p_res->crc.crc    = s_dfu_settings.progress.command_crc;
    }
    
    
    static void on_cmd_obj_select_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
    {
        UNUSED_PARAMETER(p_req);
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_SELECT (command)");
    
        p_res->select.max_size = INIT_COMMAND_MAX_SIZE;
        cmd_response_offset_and_crc_set(p_res);
    }
    
    
    static void on_cmd_obj_create_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        ASSERT(p_req);
        ASSERT(p_res);
    
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_CREATE (command)");
    
        m_observer(NRF_DFU_EVT_DFU_STARTED);
    
        nrf_dfu_result_t ret_val = nrf_dfu_validation_init_cmd_create(p_req->create.object_size);
        p_res->result = ext_err_code_handle(ret_val);
    }
    
    
    static void on_cmd_obj_write_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        ASSERT(p_req);
        ASSERT(p_req->write.p_data);
        ASSERT(p_req->write.len);
        ASSERT(p_res);
    
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_WRITE (command)");
    
        nrf_dfu_result_t ret_val;
    
        ret_val = nrf_dfu_validation_init_cmd_append(p_req->write.p_data, p_req->write.len);
        p_res->result = ext_err_code_handle(ret_val);
    
        /* Update response. This is only used when the PRN is triggered and the 'write' message
         * is answered with a CRC message and these field are copied into the response. */
        cmd_response_offset_and_crc_set(p_res);
    
        /* If a callback to free the request payload buffer was provided, invoke it now. */
        if (p_req->callback.write)
        {
            p_req->callback.write((void*)p_req->write.p_data);
        }
    }
    
    
    static void on_cmd_obj_execute_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
    {
        ASSERT(p_req);
        ASSERT(p_res);
    
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_EXECUTE (command)");
    
        nrf_dfu_result_t ret_val;
        ret_val = nrf_dfu_validation_init_cmd_execute(&m_firmware_start_addr, &m_firmware_size_req);
        p_res->result = ext_err_code_handle(ret_val);
    
        if (p_res->result == NRF_DFU_RES_CODE_SUCCESS)
        {
            if (nrf_dfu_settings_write_and_backup(NULL) == NRF_SUCCESS)
            {
                /* Setting DFU to initialized */
                NRF_LOG_DEBUG("Writing valid init command to flash.");
            }
            else
            {
                p_res->result = NRF_DFU_RES_CODE_OPERATION_FAILED;
            }
        }
    }
    
    
    static void on_cmd_obj_crc_request(nrf_dfu_request_t const * p_req, nrf_dfu_response_t * p_res)
    {
        UNUSED_PARAMETER(p_req);
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_CRC_GET (command)");
    
        cmd_response_offset_and_crc_set(p_res);
    }
    
    
    /** @brief Function handling command requests from the transport layer.
     *
     * @param   p_req[in]       Pointer to the structure holding the DFU request.
     * @param   p_res[out]      Pointer to the structure holding the DFU response.
     *
     * @retval NRF_SUCCESS      If the command request was executed successfully.
     *                          Any other error code indicates that the data request
     *                          could not be handled.
     */
    static void nrf_dfu_command_req(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        ASSERT(p_req);
        ASSERT(p_res);
    
        switch (p_req->request)
        {
            case NRF_DFU_OP_OBJECT_CREATE:
            {
                on_cmd_obj_create_request(p_req, p_res);
            } break;
    
            case NRF_DFU_OP_CRC_GET:
            {
                on_cmd_obj_crc_request(p_req, p_res);
            } break;
    
            case NRF_DFU_OP_OBJECT_WRITE:
            {
                on_cmd_obj_write_request(p_req, p_res);
            } break;
    
            case NRF_DFU_OP_OBJECT_EXECUTE:
            {
                on_cmd_obj_execute_request(p_req, p_res);
            } break;
    
            case NRF_DFU_OP_OBJECT_SELECT:
            {
                on_cmd_obj_select_request(p_req, p_res);
            } break;
    
            default:
            {
                ASSERT(false);
            } break;
        }
    }
    
    
    static void on_data_obj_select_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_SELECT (data)");
    
        p_res->select.crc    = s_dfu_settings.progress.firmware_image_crc;
        p_res->select.offset = s_dfu_settings.progress.firmware_image_offset;
    
        p_res->select.max_size = DATA_OBJECT_MAX_SIZE;
    
        NRF_LOG_DEBUG("crc = 0x%x, offset = 0x%x, max_size = 0x%x",
                      p_res->select.crc,
                      p_res->select.offset,
                      p_res->select.max_size);
    }
    
    
    static void on_data_obj_create_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_CREATE (data)");
    
        if (!nrf_dfu_validation_init_cmd_present())
        {
            /* Can't accept data because DFU isn't initialized by init command. */
            NRF_LOG_ERROR("Cannot create data object without valid init command");
            p_res->result = NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED;
            return;
        }
    
        if (p_req->create.object_size == 0)
        {
            NRF_LOG_ERROR("Object size cannot be 0.")
            p_res->result = NRF_DFU_RES_CODE_INVALID_PARAMETER;
            return;
        }
    
        if (  ((p_req->create.object_size & (CODE_PAGE_SIZE - 1)) != 0)
            && (s_dfu_settings.progress.firmware_image_offset_last + p_req->create.object_size != m_firmware_size_req))
        {
            NRF_LOG_ERROR("Object size must be page aligned");
            p_res->result = NRF_DFU_RES_CODE_INVALID_PARAMETER;
            return;
        }
    
        if (p_req->create.object_size > DATA_OBJECT_MAX_SIZE)
        {
            /* It is impossible to handle the command because the size is too large */
            NRF_LOG_ERROR("Invalid size for object (too large)");
            p_res->result = NRF_DFU_RES_CODE_INSUFFICIENT_RESOURCES;
            return;
        }
    
        if ((s_dfu_settings.progress.firmware_image_offset_last + p_req->create.object_size) >
            m_firmware_size_req)
        {
            NRF_LOG_ERROR("Creating the object with size 0x%08x would overflow firmware size. "
                          "Offset is 0x%08x and firmware size is 0x%08x.",
                          p_req->create.object_size,
                          s_dfu_settings.progress.firmware_image_offset_last,
                          m_firmware_size_req);
    
            p_res->result = NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED;
            return;
        }
    
        s_dfu_settings.progress.data_object_size      = p_req->create.object_size;
        s_dfu_settings.progress.firmware_image_crc    = s_dfu_settings.progress.firmware_image_crc_last;
        s_dfu_settings.progress.firmware_image_offset = s_dfu_settings.progress.firmware_image_offset_last;
        s_dfu_settings.write_offset                   = s_dfu_settings.progress.firmware_image_offset_last;
    
        /* Erase the page we're at. */
        if (nrf_dfu_flash_erase((m_firmware_start_addr + s_dfu_settings.progress.firmware_image_offset),
                                CEIL_DIV(p_req->create.object_size, CODE_PAGE_SIZE), NULL) != NRF_SUCCESS)
        {
            NRF_LOG_ERROR("Erase operation failed");
            p_res->result = NRF_DFU_RES_CODE_INVALID_OBJECT;
            return;
        }
    
        NRF_LOG_DEBUG("Creating object with size: %d. Offset: 0x%08x, CRC: 0x%08x",
                     s_dfu_settings.progress.data_object_size,
                     s_dfu_settings.progress.firmware_image_offset,
                     s_dfu_settings.progress.firmware_image_crc);
    }
    
    
    static void on_data_obj_write_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_WRITE (data)");
    
        if (!nrf_dfu_validation_init_cmd_present())
        {
            /* Can't accept data because DFU isn't initialized by init command. */
            p_res->result = NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED;
            return;
        }
    
        uint32_t const data_object_offset = s_dfu_settings.progress.firmware_image_offset -
                                            s_dfu_settings.progress.firmware_image_offset_last;
    
        if ((p_req->write.len + data_object_offset) > s_dfu_settings.progress.data_object_size)
        {
            /* Can't accept data because too much data has been received. */
            NRF_LOG_ERROR("Write request too long");
            p_res->result = NRF_DFU_RES_CODE_INVALID_PARAMETER;
            return;
        }
    
        uint32_t const write_addr = m_firmware_start_addr + s_dfu_settings.write_offset;
        /* CRC must be calculated before handing off the data to fstorage because the data is
         * freed on write completion.
         */
        uint32_t const next_crc =
            crc32_compute(p_req->write.p_data, p_req->write.len, &s_dfu_settings.progress.firmware_image_crc);
    
        ASSERT(p_req->callback.write);
    
        ret_code_t ret =
            nrf_dfu_flash_store(write_addr, p_req->write.p_data, p_req->write.len, p_req->callback.write);
    
        if (ret != NRF_SUCCESS)
        {
            /* When nrf_dfu_flash_store() fails because there is no space in the queue,
             * stop processing the request so that the peer can detect a CRC error
             * and retransmit this object. Remember to manually free the buffer !
             */
            p_req->callback.write((void*)p_req->write.p_data);
            return;
        }
    
        /* Update the CRC of the firmware image. */
        s_dfu_settings.write_offset                   += p_req->write.len;
        s_dfu_settings.progress.firmware_image_offset += p_req->write.len;
        s_dfu_settings.progress.firmware_image_crc     = next_crc;
    
        /* This is only used when the PRN is triggered and the 'write' message
         * is answered with a CRC message and these field are copied into the response.
         */
        p_res->write.crc    = s_dfu_settings.progress.firmware_image_crc;
        p_res->write.offset = s_dfu_settings.progress.firmware_image_offset;
    }
    
    
    static void on_data_obj_crc_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_CRC_GET (data)");
        NRF_LOG_DEBUG("Offset:%d, CRC:0x%08x",
                     s_dfu_settings.progress.firmware_image_offset,
                     s_dfu_settings.progress.firmware_image_crc);
    
        p_res->crc.crc    = s_dfu_settings.progress.firmware_image_crc;
        p_res->crc.offset = s_dfu_settings.progress.firmware_image_offset;
    }
    
    
    static void on_data_obj_execute_request_sched(void * p_evt, uint16_t event_length)
    {
        UNUSED_PARAMETER(event_length);
    
        ret_code_t          ret;
        nrf_dfu_request_t * p_req = (nrf_dfu_request_t *)(p_evt);
    
        /* Wait for all buffers to be written in flash. */
        if (nrf_fstorage_is_busy(NULL))
        {
            ret = app_sched_event_put(p_req, sizeof(nrf_dfu_request_t), on_data_obj_execute_request_sched);
            if (ret != NRF_SUCCESS)
            {
                NRF_LOG_ERROR("Failed to schedule object execute: 0x%x.", ret);
            }
            return;
        }
    
        nrf_dfu_response_t res =
        {
            .request = NRF_DFU_OP_OBJECT_EXECUTE,
        };
    
        if (s_dfu_settings.progress.firmware_image_offset == m_firmware_size_req)
        {
            NRF_LOG_DEBUG("Whole firmware image received. Postvalidating.");
    
            #if NRF_DFU_IN_APP
            res.result = nrf_dfu_validation_post_data_execute(m_firmware_start_addr, m_firmware_size_req);
            #else
            res.result = nrf_dfu_validation_activation_prepare(m_firmware_start_addr, m_firmware_size_req);
            #endif
    
            res.result = ext_err_code_handle(res.result);
    
            /* Provide response to transport */
            p_req->callback.response(&res, p_req->p_context);
    
            ret = nrf_dfu_settings_write_and_backup((nrf_dfu_flash_callback_t)on_dfu_complete);
            UNUSED_RETURN_VALUE(ret);
        }
        else
        {
            res.result = NRF_DFU_RES_CODE_SUCCESS;
    
            /* Provide response to transport */
            p_req->callback.response(&res, p_req->p_context);
    
            if (NRF_DFU_SAVE_PROGRESS_IN_FLASH)
            {
                /* Allowing skipping settings backup to save time and flash wear. */
                ret = nrf_dfu_settings_write_and_backup(NULL);
                UNUSED_RETURN_VALUE(ret);
            }
        }
    
        NRF_LOG_DEBUG("Request handling complete. Result: 0x%x", res.result);
    }
    
    
    static bool on_data_obj_execute_request(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        NRF_LOG_DEBUG("Handle NRF_DFU_OP_OBJECT_EXECUTE (data)");
    
        uint32_t const data_object_size = s_dfu_settings.progress.firmware_image_offset -
                                          s_dfu_settings.progress.firmware_image_offset_last;
    
        if (s_dfu_settings.progress.data_object_size != data_object_size)
        {
            /* The size of the written object was not as expected. */
            NRF_LOG_ERROR("Invalid data. expected: %d, got: %d",
                          s_dfu_settings.progress.data_object_size,
                          data_object_size);
    
            p_res->result = NRF_DFU_RES_CODE_OPERATION_NOT_PERMITTED;
            return true;
        }
    
        /* Update the offset and crc values for the last object written. */
        s_dfu_settings.progress.data_object_size           = 0;
        s_dfu_settings.progress.firmware_image_crc_last    = s_dfu_settings.progress.firmware_image_crc;
        s_dfu_settings.progress.firmware_image_offset_last = s_dfu_settings.progress.firmware_image_offset;
    
        on_data_obj_execute_request_sched(p_req, 0);
    
        m_observer(NRF_DFU_EVT_OBJECT_RECEIVED);
    
        return false;
    }
    
    
    static bool nrf_dfu_data_req(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        ASSERT(p_req);
        ASSERT(p_res);
    
        bool response_ready = true;
    
        switch (p_req->request)
        {
            case NRF_DFU_OP_OBJECT_CREATE:
            {
                on_data_obj_create_request(p_req, p_res);
            } break;
    
            case NRF_DFU_OP_OBJECT_WRITE:
            {
                on_data_obj_write_request(p_req, p_res);
            } break;
    
            case NRF_DFU_OP_CRC_GET:
            {
                on_data_obj_crc_request(p_req, p_res);
            } break;
    
            case NRF_DFU_OP_OBJECT_EXECUTE:
            {
                response_ready = on_data_obj_execute_request(p_req, p_res);
            } break;
    
            case NRF_DFU_OP_OBJECT_SELECT:
            {
                on_data_obj_select_request(p_req, p_res);
            } break;
    
            default:
            {
                ASSERT(false);
            } break;
        }
    
        return response_ready;
    }
    
    
    /**@brief Function for handling requests to manipulate data or command objects.
     *
     * @param[in]  p_req    Request.
     * @param[out] p_res    Response.
     *
     * @return  Whether response is ready to be sent.
     */
    static bool nrf_dfu_obj_op(nrf_dfu_request_t * p_req, nrf_dfu_response_t * p_res)
    {
        /* Keep track of the current object type since write and execute requests don't contain it. */
        static nrf_dfu_obj_type_t current_object = NRF_DFU_OBJ_TYPE_COMMAND;
    
        if (    (p_req->request == NRF_DFU_OP_OBJECT_SELECT)
            ||  (p_req->request == NRF_DFU_OP_OBJECT_CREATE))
        {
            STATIC_ASSERT(offsetof(nrf_dfu_request_select_t, object_type) ==
                          offsetof(nrf_dfu_request_create_t, object_type),
                          "Wrong object_type offset!");
    
            current_object = (nrf_dfu_obj_type_t)(p_req->select.object_type);
        }
    
        bool response_ready = true;
    
        switch (current_object)
        {
            case NRF_DFU_OBJ_TYPE_COMMAND:
                nrf_dfu_command_req(p_req, p_res);
                break;
    
            case NRF_DFU_OBJ_TYPE_DATA:
                response_ready = nrf_dfu_data_req(p_req, p_res);
                break;
    
            default:
                /* The select request had an invalid object type. */
                NRF_LOG_ERROR("Invalid object type in request.");
                current_object = NRF_DFU_OBJ_TYPE_INVALID;
                p_res->result  = NRF_DFU_RES_CODE_INVALID_OBJECT;
                break;
        }
    
        return response_ready;
    }
    
    
    static void nrf_dfu_req_handler_req_process(nrf_dfu_request_t * p_req)
    {
        ASSERT(p_req->callback.response);
    
        bool response_ready = true;
    
        /* The request handlers assume these values to be set. */
        nrf_dfu_response_t response =
        {
            .request = p_req->request,
            .result  = NRF_DFU_RES_CODE_SUCCESS,
        };
    
    
        switch (p_req->request)
        {
    #if !NRF_DFU_PROTOCOL_REDUCED
            case NRF_DFU_OP_PROTOCOL_VERSION:
            {
                on_protocol_version_request(p_req, &response);
            } break;
    
            case NRF_DFU_OP_HARDWARE_VERSION:
            {
                on_hw_version_request(p_req, &response);
            } break;
    
            case NRF_DFU_OP_FIRMWARE_VERSION:
            {
                on_fw_version_request(p_req, &response);
            } break;
    
            case NRF_DFU_OP_PING:
            {
                on_ping_request(p_req, &response);
            } break;
    
            case NRF_DFU_OP_MTU_GET:
            {
                on_mtu_get_request(p_req, &response);
            } break;
    #endif
            case NRF_DFU_OP_RECEIPT_NOTIF_SET:
            {
                on_prn_set_request(p_req, &response);
            } break;
    
            case NRF_DFU_OP_ABORT:
            {
                on_abort_request(p_req, &response);
            } break;
    
            case NRF_DFU_OP_OBJECT_CREATE:
                /* Restart the inactivity timer on CREATE messages. */
                /* Fallthrough. */
            case NRF_DFU_OP_OBJECT_SELECT:
            case NRF_DFU_OP_OBJECT_WRITE:
            case NRF_DFU_OP_OBJECT_EXECUTE:
            case NRF_DFU_OP_CRC_GET:
            {
                response_ready = nrf_dfu_obj_op(p_req, &response);
            } break;
    
            default:
                NRF_LOG_INFO("Invalid opcode received: 0x%x.", p_req->request);
                response.result = NRF_DFU_RES_CODE_OP_CODE_NOT_SUPPORTED;
                break;
        }
    
        if (response_ready)
        {
            NRF_LOG_DEBUG("Request handling complete. Result: 0x%x", response.result);
    
            p_req->callback.response(&response, p_req->p_context);
    
            if (response.result != NRF_DFU_RES_CODE_SUCCESS)
            {
                m_observer(NRF_DFU_EVT_DFU_FAILED);
            }
        }
    }
    
    
    static void nrf_dfu_req_handler_req(void * p_evt, uint16_t event_length)
    {
        nrf_dfu_request_t * p_req = (nrf_dfu_request_t *)(p_evt);
        nrf_dfu_req_handler_req_process(p_req);
    }
    
    
    ret_code_t nrf_dfu_req_handler_on_req(nrf_dfu_request_t * p_req)
    {
        ret_code_t ret;
    
        if (p_req->callback.response == NULL)
        {
            return NRF_ERROR_INVALID_PARAM;
        }
    
        ret = app_sched_event_put(p_req, sizeof(nrf_dfu_request_t), nrf_dfu_req_handler_req);
        if (ret != NRF_SUCCESS)
        {
            NRF_LOG_WARNING("Scheduler ran out of space!");
        }
    
        return ret;
    }
    
    
    ret_code_t nrf_dfu_req_handler_init(nrf_dfu_observer_t observer)
    {
        ret_code_t       ret_val;
        nrf_dfu_result_t result;
    
        if (observer == NULL)
        {
            return NRF_ERROR_INVALID_PARAM;
        }
    
    #if defined(BLE_STACK_SUPPORT_REQD) || defined(ANT_STACK_SUPPORT_REQD)
        ret_val  = nrf_dfu_flash_init(true);
    #else
        ret_val = nrf_dfu_flash_init(false);
    #endif
        if (ret_val != NRF_SUCCESS)
        {
            return ret_val;
        }
    
        nrf_dfu_validation_init();
        if (nrf_dfu_validation_init_cmd_present())
        {
            /* Execute a previously received init packed. Subsequent executes will have no effect. */
            result = nrf_dfu_validation_init_cmd_execute(&m_firmware_start_addr, &m_firmware_size_req);
            if (result != NRF_DFU_RES_CODE_SUCCESS)
            {
                /* Init packet in flash is not valid! */
                return NRF_ERROR_INTERNAL;
            }
        }
    
        m_observer = observer;
    
        /* Initialize extended error handling with "No error" as the most recent error. */
        result = ext_error_set(NRF_DFU_EXT_ERROR_NO_ERROR);
        UNUSED_RETURN_VALUE(result);
    
        return NRF_SUCCESS;
    }
    
    /**
     * Copyright (c) 2017 - 2019, 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))
    
    /**@brief A maximum number of concurrent delayed context structures. */
    #ifndef SOFTDEVICE_PRESENT
    #define DFU_FLASH_OPERATION_OP_ENTRIES 2
    #else
    #define DFU_FLASH_OPERATION_OP_ENTRIES NRF_FSTORAGE_SD_QUEUE_SIZE
    #endif
    
    /**@brief An enumeration describing delayed flash operation status. */
    typedef enum
    {
        STATUS_NONE,
        STATUS_REGISTERED,
        STATUS_FLASH_DONE,
        STATUS_PROCESSING_DONE,
        STATUS_CALLBACK_CALLED
    } dfu_flash_operation_status_t;
    
    /**@brief A structure representing delayed DFU context. */
    typedef struct
    {
        nrf_dfu_response_t             response;
        void                         * p_context;
        void                         * p_buf;
        dfu_flash_operation_status_t   status;
    } dfu_flash_operation_ctx_t;
    
    
    /**@brief An array storing delayed DFU context structures.
     */
    static dfu_flash_operation_ctx_t m_flash_operations[DFU_FLASH_OPERATION_OP_ENTRIES];
    
    
    static void block_buffer_store(background_dfu_block_manager_t * p_bm);
    static void dfu_operation_callback(nrf_dfu_response_t * p_res, void * p_context);
    
    
    /**@brief A function to initialize delayed DFU context module.
     */
    static void dfu_flash_operation_init(void)
    {
        uint32_t i;
    
        for (i = 0; i < DFU_FLASH_OPERATION_OP_ENTRIES; i++)
        {
            m_flash_operations[i].status = STATUS_NONE;
        }
    }
    
    /**@brief A function to register a new context for delayed DFU operations.
     *
     * @param[in] p_buf      Pointer to the memory buffer, that will be used for flash operation.
     * @param[in] p_context  Pointer to the DFU request context.
     *
     * @return NRF_SUCCESS if a new context entry was registered, NRF_ERROR_NO_MEM otherwise.
     */
    static ret_code_t dfu_flash_operation_register(void * p_buf, void * p_context)
    {
        uint32_t i;
    
        for (i = 0; i < DFU_FLASH_OPERATION_OP_ENTRIES; i++)
        {
            if (m_flash_operations[i].status == STATUS_NONE)
            {
                m_flash_operations[i].status    = STATUS_REGISTERED;
                m_flash_operations[i].p_buf     = p_buf;
                m_flash_operations[i].p_context = p_context;
                memset(&m_flash_operations[i].response, 0, sizeof(nrf_dfu_response_t));
    
                return NRF_SUCCESS;
            }
        }
    
        return NRF_ERROR_NO_MEM;
    }
    
    /**@brief A function to get a context structure based on DFU block context.
     *
     * @param[in] p_context  Pointer to the DFU request context.
     *
     * @return Pointer to the found delayed DFU context structure, NULL otherwise.
     */
    static dfu_flash_operation_ctx_t * dfu_flash_operation_find_by_context(void * p_context)
    {
        uint32_t i;
    
        for (i = 0; i < DFU_FLASH_OPERATION_OP_ENTRIES; i++)
        {
            if ((m_flash_operations[i].status != STATUS_NONE) && (m_flash_operations[i].p_context == p_context))
            {
                return &m_flash_operations[i];
            }
        }
    
        return NULL;
    }
    
    /**@brief A function to get a context structure based on flash operation buffer address.
     *
     * @param[in] p_buf      Pointer to the memory buffer, that will be used for flash operation.
     *
     * @return Pointer to the found delayed DFU context structure, NULL otherwise.
     */
    static dfu_flash_operation_ctx_t * dfu_flash_operation_find_by_buffer(void * p_buf)
    {
        uint32_t i;
    
        for (i = 0; i < DFU_FLASH_OPERATION_OP_ENTRIES; i++)
        {
            if ((m_flash_operations[i].status != STATUS_NONE) && (m_flash_operations[i].p_buf == p_buf))
            {
                return &m_flash_operations[i];
            }
        }
    
        return NULL;
    }
    
    /**@brief A callback function for processing events, implementing delayed DFU operation.
     *
     * @param[in] p_res      Pointer to the DFU response structure.
     * @param[in] p_context  Pointer to the DFU request context.
     */
    static void dfu_operation_callback_delayed(nrf_dfu_response_t * p_res, void * p_context)
    {
        dfu_flash_operation_ctx_t * p_flash_ctx = dfu_flash_operation_find_by_context(p_context);
        bool                        call        = false;
    
        if (p_res->result != NRF_DFU_RES_CODE_SUCCESS)
        {
            dfu_operation_callback(p_res, p_context);
            return;
        }
    
        if (p_flash_ctx == NULL)
        {
            return;
        }
    
        CRITICAL_REGION_ENTER();
        switch(p_flash_ctx->status)
        {
            case STATUS_REGISTERED:
                // Store operation result for a delayed call.
                p_flash_ctx->response = *p_res;
                p_flash_ctx->status   = STATUS_PROCESSING_DONE;
                break;
    
            case STATUS_FLASH_DONE:
                p_flash_ctx->status = STATUS_CALLBACK_CALLED;
                call = true;
                break;
    
            case STATUS_NONE:
            case STATUS_PROCESSING_DONE:
            case STATUS_CALLBACK_CALLED:
            default:
                break;
        }
        CRITICAL_REGION_EXIT();
    
        if (call)
        {
            // If both: flash and processing callbacks received - call main state machine.
            dfu_operation_callback(p_res, p_flash_ctx->p_context);
            p_flash_ctx->status = STATUS_NONE;
        }
    }
    
    /**@brief A callback function for flash events, implementing delayed DFU operation.
     *
     * @param[in] p_buf  Pointer to the memory buffer, related to the finished flash operation.
     */
    static void dfu_operation_flash_callback(void * p_buf)
    {
        dfu_flash_operation_ctx_t * p_flash_ctx = dfu_flash_operation_find_by_buffer(p_buf);
        bool                        call        = false;
    
        if (p_flash_ctx == NULL)
        {
            return;
        }
    
        CRITICAL_REGION_ENTER();
        switch(p_flash_ctx->status)
        {
            case STATUS_REGISTERED:
                p_flash_ctx->status = STATUS_FLASH_DONE;
                break;
    
            case STATUS_PROCESSING_DONE:
                p_flash_ctx->status = STATUS_CALLBACK_CALLED;
                call = true;
                break;
    
            case STATUS_NONE:
            case STATUS_FLASH_DONE:
            case STATUS_CALLBACK_CALLED:
            default:
                break;
        }
        CRITICAL_REGION_EXIT();
    
        if (call)
        {
            // If both: flash and processing callbacks received - call main state machine.
            dfu_operation_callback(&p_flash_ctx->response, p_flash_ctx->p_context);
            p_flash_ctx->status = STATUS_NONE;
        }
    }
    
    /**@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 ret_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));
    
                    ret_code = dfu_flash_operation_register(p_bm->data + data_offset, p_bm);
                    if (ret_code == NRF_SUCCESS)
                    {
                        ret_code = background_dfu_op_write(p_bm->data + data_offset,
                                                           store_size,
                                                           dfu_operation_callback_delayed,
                                                           dfu_operation_flash_callback,
                                                           p_bm);
                    }
                    else
                    {
                        ret_code = background_dfu_op_write(p_bm->data + data_offset,
                                                           store_size,
                                                           dfu_operation_callback,
                                                           dfu_operation_flash_callback,
                                                           p_bm);
                    }
    
                    if (ret_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) == (int32_t)BLOCKS_PER_SIZE(p_bm->image_size)))
                    {
                        ret_code = background_dfu_op_crc(dfu_operation_callback, p_bm);
    
                        if (ret_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:
                    ret_code = background_dfu_op_execute(dfu_operation_callback, p_bm);
    
                    if (ret_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 ret_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));
    
                ret_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));
    
            ret_code = dfu_flash_operation_register(p_bm->data + data_offset, p_bm);
            if (ret_code == NRF_SUCCESS)
            {
                ret_code = background_dfu_op_write(p_bm->data + data_offset,
                                                   store_size,
                                                   dfu_operation_callback_delayed,
                                                   dfu_operation_flash_callback,
                                                   p_bm);
            }
            else
            {
                ret_code = background_dfu_op_write(p_bm->data + data_offset,
                                                   store_size,
                                                   dfu_operation_callback,
                                                   dfu_operation_flash_callback,
                                                   p_bm);
            }
    
        } while (0);
        return ret_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 ret_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.
                    ret_code = block_store(p_bm, block);
                    if (ret_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));
        dfu_flash_operation_init();
    }
    
    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)
    {
        int32_t image_blocks = (int32_t)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)
    {
        int32_t image_blocks = (int32_t)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;
    }
    
Children
Related