static void start_target(void)
{
    set_timeout(STATE_TIMEOUT_TARGET);
    m_state = BL_STATE_DFU_TARGET;

    uint32_t segment_size = 0;
    bl_info_entry_t flags_entry;
    memset(&flags_entry, 0xFF, (BL_INFO_LEN_FLAGS + 3) & ~0x03UL);

    switch (m_transaction.type)
    {
        case DFU_TYPE_SD:
            segment_size = m_bl_info_pointers.p_segment_sd->length;
            flags_entry.flags.sd_intact = false;
            break;
        case DFU_TYPE_APP:
            segment_size = m_bl_info_pointers.p_segment_app->length;
            flags_entry.flags.app_intact = false;
            break;
        case DFU_TYPE_BOOTLOADER:
            segment_size = m_bl_info_pointers.p_segment_bl->length;
            flags_entry.flags.bl_intact = false;
            break;
        default:
            segment_size = 0;
    }
    
    /* Tag the transfer as incomplete in device page if we're about to overwrite it. */
    if (m_transaction.p_start_addr == m_transaction.p_bank_addr)
    {
        if (m_bl_info_pointers.p_flags == NULL)
        {
            m_bl_info_pointers.p_flags = &bootloader_info_entry_put(BL_INFO_TYPE_FLAGS, &flags_entry, BL_INFO_LEN_FLAGS)->flags;
        }
        else
        {
            /* update inline */
            nrf_flash_store((uint32_t*) m_bl_info_pointers.p_flags, (uint8_t*) &flags_entry, (BL_INFO_LEN_FLAGS + 3) & ~0x03UL, 0);
        }
    }

    if (dfu_start(
            m_transaction.p_start_addr,
            m_transaction.p_bank_addr,
            m_transaction.length,
            segment_size,
            m_transaction.segment_is_valid_after_transfer) != NRF_SUCCESS)
    {
        start_req(m_transaction.type, true);
    }
    else
    {
        transport_tx_abort(mp_beacon); /* stop beaconing */
    }
}
static void handle_data_packet(dfu_packet_t* p_packet, uint16_t length)
{
    mesh_packet_t* p_cache_packet = packet_cache_entry_get(p_packet);
    if (p_cache_packet)
    {
        transport_tx_skip(p_cache_packet);
    }
    
    bool do_relay = false;
    if (p_packet->payload.data.transaction_id == m_transaction.transaction_id)
    {
        /* check and add to cache */
        if (data_packet_in_cache(p_packet))
        {
            return;
        }
        m_data_cache[(m_data_index++) & (DATA_CACHE_SIZE - 1)] = p_packet->payload.data.segment;
        
        
        if (m_state == BL_STATE_DFU_READY)
        {
            if (p_packet->payload.start.segment == 0)
            {
                bl_info_segment_t* p_segment = NULL;
                switch (m_transaction.type)
                {
                    case DFU_TYPE_APP:
                        p_segment = m_bl_info_pointers.p_segment_app;
                        break;
                    case DFU_TYPE_SD:
                        p_segment = m_bl_info_pointers.p_segment_sd;
                        break;
                    case DFU_TYPE_BOOTLOADER:
                        p_segment = m_bl_info_pointers.p_segment_bl;
                        break;
                    default:
                        APP_ERROR_CHECK(NRF_ERROR_NOT_SUPPORTED);
                }
                
                m_transaction.p_indicated_start_addr = (uint32_t*) p_packet->payload.start.start_address;
                uint32_t start_address = p_packet->payload.start.start_address;
                /* if the host doesn't know the start address, we use start of segment: */
                if (start_address == START_ADDRESS_UNKNOWN)
                {
                    start_address = p_segment->start;
                }

                uint32_t segment_count = ((p_packet->payload.start.length * 4) + (start_address & 0x0F) - 1) / 16 + 1;
                
                if (p_packet->payload.start.signature_length != 0)
                {
                    segment_count += p_packet->payload.start.signature_length / SEGMENT_LENGTH;
                }
                if (segment_count > 0xFFFF)
                {
                    /* can't have more than 65536 segments in a transmission */
                    segment_count = 0xFFFF;
                }

                m_transaction.segments_remaining                = segment_count;
                m_transaction.segment_count                     = segment_count;
                m_transaction.p_start_addr                      = (uint32_t*) start_address;
                m_transaction.length                            = p_packet->payload.start.length * 4;
                m_transaction.signature_length                  = p_packet->payload.start.signature_length;
                m_transaction.segment_is_valid_after_transfer   = p_packet->payload.start.last;
                m_transaction.p_last_requested_entry            = NULL;
                m_transaction.signature_bitmap                  = 0;
                

                if (m_transaction.type == DFU_TYPE_BOOTLOADER)
                {
                    m_transaction.p_bank_addr = (uint32_t*) (
                        (m_bl_info_pointers.p_segment_app->start) +
                        (m_bl_info_pointers.p_segment_app->length) -
                        (m_transaction.length & ((uint32_t) ~(PAGE_SIZE - 1))) -
                        (PAGE_SIZE)
                    );
                }
                else
                {
                    m_transaction.p_bank_addr = m_transaction.p_start_addr;
                }

                if ((uint32_t) m_transaction.p_start_addr >= p_segment->start &&
                    (uint32_t) m_transaction.p_start_addr + m_transaction.length <= p_segment->start + p_segment->length)
                {
                    start_target();
                    do_relay = true;
                }
            }
            else
            {
                m_tid_cache[(m_tid_index++) & (TRANSACTION_ID_CACHE_SIZE - 1)] = m_transaction.transaction_id;
                start_req(m_transaction.type, true); /* go back to req, we've missed packet 0 */
            }
        }
        else if (m_state == BL_STATE_DFU_TARGET)
        {
            if (p_packet->payload.data.segment > 0 &&
                p_packet->payload.data.segment <= m_transaction.segment_count)
            {
                uint32_t* p_addr = NULL; 
                uint32_t error_code = NRF_ERROR_NULL;
                
                if (p_packet->payload.data.segment <= 
                    m_transaction.segment_count - m_transaction.signature_length / SEGMENT_LENGTH)
                {
                    p_addr = addr_from_seg(p_packet->payload.data.segment);
                    error_code  = dfu_data((uint32_t) p_addr,
                                               p_packet->payload.data.data,
                                               length - (DFU_PACKET_LEN_DATA - SEGMENT_LENGTH));
                }
                else /* treat signature packets at the end */
                {
                    uint32_t index = p_packet->payload.data.segment - (m_transaction.segment_count - m_transaction.signature_length / SEGMENT_LENGTH) - 1;
                    if (index >= m_transaction.signature_length / SEGMENT_LENGTH || 
                        m_transaction.signature_bitmap & (1 << index))
                    {
                        error_code = NRF_ERROR_INVALID_STATE;
                    }
                    else
                    {
                        memcpy(&m_transaction.signature[index * SEGMENT_LENGTH], 
                               p_packet->payload.data.data, 
                               length - (DFU_PACKET_LEN_DATA - SEGMENT_LENGTH));
                        
                        m_transaction.signature_bitmap |= (1 << index);
                        error_code = NRF_SUCCESS;
                    }
                }
                
                if (error_code == NRF_SUCCESS)
                {
                    set_timeout(STATE_TIMEOUT_TARGET);
                    m_transaction.segments_remaining--;
                    do_relay = true;
                    /* check whether we've lost any entries, and request them */
                    uint32_t* p_req_entry = NULL;
                    uint32_t req_entry_len = 0;
                    mesh_packet_t* p_req_packet;
                    
                    if (dfu_get_oldest_missing_entry(
                            m_transaction.p_last_requested_entry,
                            &p_req_entry,
                            &req_entry_len) &&
                        (
                         /* don't request the previous packet yet */
                         ADDR_SEGMENT(p_req_entry, m_transaction.p_start_addr) < p_packet->payload.data.segment - 1 ||
                         m_transaction.segment_count == p_packet->payload.data.segment
                        ) 
                       )
                    {
                        if(!mesh_packet_acquire(&p_req_packet))
                        {
                            return;
                        }
                        if (mesh_packet_build(p_req_packet,
                                DFU_PACKET_TYPE_DATA_REQ,
                                ADDR_SEGMENT(p_req_entry, m_transaction.p_start_addr),
                                (uint8_t*) &m_transaction.transaction_id,
                                4) == NRF_SUCCESS &&
                            transport_tx(p_req_packet, TX_REPEATS_REQ, TX_INTERVAL_TYPE_REQ, NULL))
                        {
                            m_transaction.p_last_requested_entry = (uint32_t*) p_req_entry;
                        }
                        mesh_packet_ref_count_dec(p_req_packet);
                    }
                }
            }

            /* ending the DFU */
            if (m_transaction.segments_remaining == 0)
            {
                dfu_end();
                start_rampdown();
            }
        }
        else if (m_state == BL_STATE_RELAY_CANDIDATE || 
                 m_state == BL_STATE_RELAY)
        {
            m_state = BL_STATE_RELAY;
            transport_tx_abort(mp_beacon);
            set_timeout(STATE_TIMEOUT_RELAY);
            do_relay = true;
        }
    }

    if (do_relay)
    {
        relay_packet(p_packet, length);
    }
}
static dfu_packet_t* beacon_set(beacon_type_t type)
{
    if (mp_beacon)
    {
        transport_tx_abort(mp_beacon);
        if (!mesh_packet_ref_count_dec(mp_beacon))
        {
#ifdef DEBUG_LEDS            
            NRF_GPIO->OUTCLR = (1 << 24);
#endif
        }
    }
    if (!mesh_packet_acquire(&mp_beacon))
    {
        bootloader_abort(BL_END_ERROR_NO_MEM);
    }
    dfu_packet_t* p_dfu = (dfu_packet_t*) &(((ble_ad_t*) mp_beacon->payload)->data[2]);
    memset(p_dfu, 0, sizeof(dfu_packet_t));
    
    uint8_t repeats = TX_REPEATS_FWID;
    tx_interval_type_t interval_type = TX_INTERVAL_TYPE_FWID;
    
    if (type >= BEACON_TYPE_READY_APP &&
        type <= BEACON_TYPE_READY_BL)
    {
        p_dfu->packet_type = DFU_PACKET_TYPE_STATE;
        p_dfu->payload.state.authority = m_transaction.authority;
        p_dfu->payload.state.flood = m_transaction.flood;
        p_dfu->payload.state.transaction_id = m_transaction.transaction_id;
        p_dfu->payload.state.relay_node = (m_state == BL_STATE_RELAY_CANDIDATE);
        p_dfu->payload.state.fwid = m_transaction.target_fwid_union;
        repeats = TX_REPEATS_READY;
        interval_type = TX_INTERVAL_TYPE_READY;
    }

    switch (type)
    {
        case BEACON_TYPE_FWID:
            bootloader_packet_set_local_fields(mp_beacon, DFU_PACKET_LEN_FWID);
            p_dfu->packet_type = DFU_PACKET_TYPE_FWID;
            p_dfu->payload.fwid = *m_bl_info_pointers.p_fwid;
            break;

        case BEACON_TYPE_READY_APP:
            bootloader_packet_set_local_fields(mp_beacon, DFU_PACKET_LEN_STATE_APP);
            p_dfu->payload.state.dfu_type = DFU_TYPE_APP;
            break;

        case BEACON_TYPE_READY_SD:
            bootloader_packet_set_local_fields(mp_beacon, DFU_PACKET_LEN_STATE_SD);
            p_dfu->payload.state.dfu_type = DFU_TYPE_SD;
            break;

        case BEACON_TYPE_READY_BL:
            bootloader_packet_set_local_fields(mp_beacon, DFU_PACKET_LEN_STATE_BL);
            p_dfu->payload.state.dfu_type = DFU_TYPE_BOOTLOADER;
            break;
    }
    transport_tx(mp_beacon, repeats, interval_type, NULL);

    return p_dfu;
}
static uint32_t bl_evt_handler(bl_evt_t* p_evt)
{
    static bl_cmd_t rsp_cmd;
    bool respond = false;
    switch (p_evt->type)
    {
        case BL_EVT_TYPE_DFU_ABORT:
            bootloader_abort(p_evt->params.dfu.abort.reason);

            /* If bootloader abort returned, it means that the application
             * doesn't work, and we should return to the dfu operation. */
            dfu_mesh_start();
            break;
        case BL_EVT_TYPE_TX_RADIO:
        {
            mesh_packet_t* p_packet = NULL;
            if (!mesh_packet_acquire(&p_packet))
            {
                return NRF_ERROR_NO_MEM;
            }

            mesh_packet_set_local_addr(p_packet);
            p_packet->header.type = BLE_PACKET_TYPE_ADV_NONCONN_IND;
            p_packet->header.length = DFU_PACKET_OVERHEAD + p_evt->params.tx.radio.length;
            ((ble_ad_t*) p_packet->payload)->adv_data_type = MESH_ADV_DATA_TYPE;
            ((ble_ad_t*) p_packet->payload)->data[0] = (MESH_UUID & 0xFF);
            ((ble_ad_t*) p_packet->payload)->data[1] = (MESH_UUID >> 8) & 0xFF;
            ((ble_ad_t*) p_packet->payload)->adv_data_length = DFU_PACKET_ADV_OVERHEAD + p_evt->params.tx.radio.length;
            memcpy(&p_packet->payload[4], p_evt->params.tx.radio.p_dfu_packet, p_evt->params.tx.radio.length);

            bool success = transport_tx(p_packet,
                                        p_evt->params.tx.radio.tx_slot,
                                        p_evt->params.tx.radio.tx_count,
                                        (tx_interval_type_t) p_evt->params.tx.radio.interval_type);
            mesh_packet_ref_count_dec(p_packet);

            if (!success)
            {
                return NRF_ERROR_INTERNAL;
            }
            break;
        }
        case BL_EVT_TYPE_TX_ABORT:
            transport_tx_abort(p_evt->params.tx.abort.tx_slot);
            break;
        case BL_EVT_TYPE_TX_SERIAL:
        {
#ifdef RBC_MESH_SERIAL
            serial_evt_t serial_evt;
            serial_evt.opcode = SERIAL_EVT_OPCODE_DFU;
            memcpy(&serial_evt.params.dfu.packet,
                    p_evt->params.tx.serial.p_dfu_packet,
                    p_evt->params.tx.serial.length);
            serial_evt.length = SERIAL_PACKET_OVERHEAD + p_evt->params.tx.serial.length;
            if (!serial_handler_event_send(&serial_evt))
            {
                return NRF_ERROR_INTERNAL;
            }
            break;
#endif
        }
        case BL_EVT_TYPE_TIMER_SET:
            set_timeout(US_TO_RTC_TICKS(p_evt->params.timer.set.delay_us), TIMEOUT_ACTION_DFU_TIMEOUT);
            break;

        case BL_EVT_TYPE_DFU_NEW_FW:
            {
                stop_timeout();
                __LOG("New FW event\n");
                switch (p_evt->params.dfu.new_fw.fw_type)
                {
                    case DFU_TYPE_APP:
                        __LOG("\tAPP: %08x.%04x:%08x\n",
                                (uint32_t) p_evt->params.dfu.new_fw.fwid.app.company_id,
                                (uint32_t) p_evt->params.dfu.new_fw.fwid.app.app_id,
                                (uint32_t) p_evt->params.dfu.new_fw.fwid.app.app_version);
                        break;
                    case DFU_TYPE_SD:
                        __LOG("\tSD: %04x\n",
                                (uint32_t) p_evt->params.dfu.new_fw.fwid.sd);
                        break;
                    case DFU_TYPE_BOOTLOADER:
                        __LOG("\tBL: %02x:%02x\n",
                                (uint32_t) p_evt->params.dfu.new_fw.fwid.bootloader.id,
                                (uint32_t) p_evt->params.dfu.new_fw.fwid.bootloader.ver);
                        break;
                    default: break;
                }
                /* accept all new firmware, as the bootloader wouldn't run
                   unless there's an actual reason for it. */
                rsp_cmd.type = BL_CMD_TYPE_DFU_START_TARGET;
                rsp_cmd.params.dfu.start.target.p_bank_start = (uint32_t*) 0xFFFFFFFF; /* no banking */
                rsp_cmd.params.dfu.start.target.type = p_evt->params.dfu.new_fw.fw_type;
                rsp_cmd.params.dfu.start.target.fwid = p_evt->params.dfu.new_fw.fwid;
                respond = true;
            }
            break;

        case BL_EVT_TYPE_BANK_AVAILABLE:
            __LOG("Bank:\n");
            switch (p_evt->params.bank_available.bank_dfu_type)
            {
                case DFU_TYPE_APP:
                    __LOG("\tAPP: %08x.%04x:%08x\n",
                            (uint32_t) p_evt->params.bank_available.bank_fwid.app.company_id,
                            (uint32_t) p_evt->params.bank_available.bank_fwid.app.app_id,
                            (uint32_t) p_evt->params.bank_available.bank_fwid.app.app_version);
                    break;
                case DFU_TYPE_SD:
                    __LOG("\tSD: %04x\n",
                            (uint32_t) p_evt->params.bank_available.bank_fwid.sd);
                    break;
                case DFU_TYPE_BOOTLOADER:
                    __LOG("\tBL: %02x:%02x\n",
                            (uint32_t) p_evt->params.bank_available.bank_fwid.bootloader.id,
                            (uint32_t) p_evt->params.bank_available.bank_fwid.bootloader.ver);
                    break;
                default: break;
            }
            __LOG("\tLocation: 0x%x\n", p_evt->params.bank_available.p_bank_addr);
            __LOG("\tLength: 0x%x\n", p_evt->params.bank_available.bank_length);
            if (p_evt->params.bank_available.bank_dfu_type == DFU_TYPE_BOOTLOADER)
            {
                if (!dfu_mesh_app_is_valid())
                {
                    dfu_bank_flash(DFU_TYPE_BOOTLOADER);
                }
            }
            break;

        case BL_EVT_TYPE_DFU_REQ:
            {
                /* Always attempt to relay incoming transfers in BL mode. Will
                   not abort ongoing transfers. */
                if (p_evt->params.dfu.req.role == DFU_ROLE_RELAY)
                {
                    stop_timeout();
                    bl_cmd_t relay_cmd;
                    relay_cmd.type = BL_CMD_TYPE_DFU_START_RELAY;
                    relay_cmd.params.dfu.start.relay.fwid = p_evt->params.dfu.req.fwid;
                    relay_cmd.params.dfu.start.relay.type = p_evt->params.dfu.req.dfu_type;
                    relay_cmd.params.dfu.start.relay.transaction_id = p_evt->params.dfu.req.transaction_id;
                    bootloader_cmd_send(&relay_cmd);
                }
            }
            break;

        case BL_EVT_TYPE_DFU_START:
            set_timeout(TIMER_START_TIMEOUT, TIMEOUT_ACTION_DFU_ABORT);
            break;
        case BL_EVT_TYPE_DFU_DATA_SEGMENT_RX:
            __LOG("RX %u/%u\n",
                    p_evt->params.dfu.data_segment.received_segment,
                    p_evt->params.dfu.data_segment.total_segments);
            set_timeout(TIMER_DATA_TIMEOUT, TIMEOUT_ACTION_DFU_ABORT);
            break;

        case BL_EVT_TYPE_DFU_END:
            if (p_evt->params.dfu.end.dfu_type == DFU_TYPE_APP ||
                p_evt->params.dfu.end.dfu_type == DFU_TYPE_SD)
            {
                /* attempt to reboot to app */
                bootloader_abort(DFU_END_SUCCESS);
            }
            break;

        /* Defer the flash operations to an asynchronous handler. Doing it
           inline causes stack overflow, as the bootloader continues in the
           response callback. */
        case BL_EVT_TYPE_FLASH_WRITE:
            {
                if (!IS_WORD_ALIGNED(p_evt->params.flash.write.start_addr) ||
                    !IS_WORD_ALIGNED(p_evt->params.flash.write.p_data))
                {
                    return NRF_ERROR_INVALID_ADDR;
                }
                if (!IS_WORD_ALIGNED(p_evt->params.flash.write.length))
                {
                    return NRF_ERROR_INVALID_LENGTH;
                }
                if ((p_evt->params.flash.write.start_addr + p_evt->params.flash.write.length) > NRF_UICR->BOOTLOADERADDR &&
                    p_evt->params.flash.write.start_addr < 0x3f800)
                {
                    APP_ERROR_CHECK(NRF_ERROR_INVALID_ADDR);
                }
                flash_queue_entry_t queue_entry;
                queue_entry.type = FLASH_OP_TYPE_WRITE;
                memcpy(&queue_entry.op, &p_evt->params.flash, sizeof(flash_op_t));
                if (fifo_push(&m_flash_fifo, &queue_entry) != NRF_SUCCESS)
                {
                    __LOG(RTT_CTRL_TEXT_RED "FLASH FIFO FULL :( Increase the fifo size.\n");
                    return NRF_ERROR_NO_MEM;
                }
                NVIC_SetPendingIRQ(FLASH_HANDLER_IRQn);
            }
            break;
        case BL_EVT_TYPE_FLASH_ERASE:
            {
                flash_queue_entry_t queue_entry;
                queue_entry.type = FLASH_OP_TYPE_ERASE;
                memcpy(&queue_entry.op, &p_evt->params.flash, sizeof(flash_op_t));
                if (fifo_push(&m_flash_fifo, &queue_entry) != NRF_SUCCESS)
                {
                    __LOG(RTT_CTRL_TEXT_RED "FLASH FIFO FULL :( Increase the fifo size.\n");
                    return NRF_ERROR_NO_MEM;
                }
                NVIC_SetPendingIRQ(FLASH_HANDLER_IRQn);
            }
            break;
        default:
            return NRF_ERROR_NOT_SUPPORTED;
    }
    if (respond)
    {
        /* tail recursion */
        return bl_cmd_handler(&rsp_cmd);
    }
    else
    {
        return NRF_SUCCESS;
    }
}