static void relay_packet(dfu_packet_t* p_packet, uint16_t length)
{
    mesh_packet_t* p_mesh_packet = mesh_packet_get_aligned(p_packet);
    if (!p_mesh_packet)
    {
        if (!mesh_packet_acquire(&p_mesh_packet))
        {
            APP_ERROR_CHECK(NRF_ERROR_NO_MEM);
        }
        mesh_packet_build(
            p_mesh_packet,
            p_packet->packet_type,
            p_packet->payload.data.segment,
            (uint8_t*) &p_packet->payload.data.transaction_id,
            length - 4);
    }
    else
    {
        mesh_packet_ref_count_inc(p_mesh_packet);
    }

    mesh_packet_set_local_addr(p_mesh_packet);

    if (transport_tx(p_mesh_packet, TX_REPEATS_DATA, TX_INTERVAL_TYPE_DATA, packet_release_callback))
    {
        mp_sent_packets[(m_sent_packet_index++) & (SENT_PACKET_COUNT - 1)] = p_mesh_packet;
    }
    mesh_packet_ref_count_dec(p_mesh_packet);
}
Exemple #2
0
/* radio callback, executed in STACK_LOW */
static void tx_cb(uint8_t* data)
{
    rbc_mesh_event_t tx_event;
    mesh_adv_data_t* p_adv_data = mesh_packet_adv_data_get((mesh_packet_t*) data);
    bool doing_tx_event = false;
    if (p_adv_data != NULL && 
        vh_tx_event_flag_get(p_adv_data->handle, &doing_tx_event) == NRF_SUCCESS 
        && doing_tx_event
    )
    {
        tx_event.event_type = RBC_MESH_EVENT_TYPE_TX;
        tx_event.value_handle = p_adv_data->handle;
        tx_event.data = p_adv_data->data;
        tx_event.data_len = p_adv_data->adv_data_length - MESH_PACKET_ADV_OVERHEAD;
        tx_event.version_delta = 0;

        APP_ERROR_CHECK(rbc_mesh_event_push(&tx_event));
#if RBC_MESH_SERIAL
        mesh_aci_rbc_event_handler(&tx_event);
#endif
    }

    mesh_packet_ref_count_dec((mesh_packet_t*) data); /* radio ref removed (pushed in tc_tx) */
    vh_order_update(timer_get_timestamp()); /* tell the vh, so that it can push more updates */
}
Exemple #3
0
/* packet processing, executed in APP_LOW */
void tc_packet_handler(uint8_t* data, uint32_t crc, uint64_t timestamp)
{
//	LOGi("_6");
    SET_PIN(PIN_RX);
    mesh_packet_t* p_packet = (mesh_packet_t*) data;
//	printArray(p_packet, sizeof(mesh_packet_t));

    if (p_packet->header.length > MESH_PACKET_OVERHEAD + RBC_MESH_VALUE_MAX_LEN)
    {
//    	LOGi("_2");
        /* invalid packet, ignore */
        CLEAR_PIN(PIN_RX);
        mesh_packet_ref_count_dec(p_packet); /* from rx_cb */
        return;
    }
    
    ble_gap_addr_t addr;
    memcpy(addr.addr, p_packet->addr, BLE_GAP_ADDR_LEN);
    addr.addr_type = p_packet->header.addr_type;
    
    mesh_adv_data_t* p_mesh_adv_data = mesh_packet_adv_data_get(p_packet);
    
    
    if (p_mesh_adv_data != NULL)
    {
//    	LOGi("_3");
        /* filter mesh packets on handle range */
        if (p_mesh_adv_data->handle <= RBC_MESH_APP_MAX_HANDLE)
        {
//        	LOGi("_4");
            mesh_app_packet_handle(p_mesh_adv_data, timestamp);
        }
    }
    
    /* this packet is no longer needed in this context */
    mesh_packet_ref_count_dec(p_packet); /* from rx_cb */

    if (g_state.queue_saturation)
    {
//    	LOGi("_5");
        order_search();
        g_state.queue_saturation = false;
    }
    
    CLEAR_PIN(PIN_RX);
}
Exemple #4
0
/* radio callback, executed in STACK_LOW */
static void rx_cb(uint8_t* data, bool success, uint32_t crc)
{
    if (success)
    {
        async_event_t evt;
        evt.type = EVENT_TYPE_PACKET;
        evt.callback.packet.payload = data;
        evt.callback.packet.crc = crc;
        evt.callback.packet.timestamp = timer_get_timestamp();
        mesh_packet_ref_count_inc((mesh_packet_t*) data); /* event handler has a ref */
        if (event_handler_push(&evt) != NRF_SUCCESS)
        {
            g_state.queue_saturation = true;
            mesh_packet_ref_count_dec((mesh_packet_t*) data); /* event handler lost its ref */
        }
    }

    /* no longer needed in this context */
    mesh_packet_ref_count_dec((mesh_packet_t*) data);
}
static void data_entry_free(data_entry_t* p_data_entry)
{
    if (p_data_entry == NULL)
        return;

    if (p_data_entry->p_packet != NULL)
    {
        mesh_packet_ref_count_dec(p_data_entry->p_packet); /* data cache ref remove */
        p_data_entry->p_packet = NULL;
    }
    /* reset trickle params */
    trickle_enable(&p_data_entry->trickle);
}
uint32_t handle_storage_info_set(uint16_t handle, handle_info_t* p_info)
{
    if (p_info == NULL)
    {
        return NRF_ERROR_NULL;
    }
    if (handle == RBC_MESH_INVALID_HANDLE)
    {
        return NRF_ERROR_INVALID_ADDR;
    }

    uint16_t handle_index = handle_entry_get(handle, true);
    if (handle_index == HANDLE_CACHE_ENTRY_INVALID)
    {
        /* couldn't find an existing entry, allocate one */
        handle_index = handle_entry_to_head(handle);
        if (handle_index == HANDLE_CACHE_ENTRY_INVALID)
        {
            return NRF_ERROR_NO_MEM;
        }
    }

    uint16_t data_index = m_handle_cache[handle_index].data_entry;

    if (data_index == DATA_CACHE_ENTRY_INVALID)
    {
        data_index = data_entry_allocate();
        if (data_index == DATA_CACHE_ENTRY_INVALID)
        {
            return NRF_ERROR_NO_MEM;
        }
        m_handle_cache[handle_index].data_entry = data_index;
    }
    trickle_timer_reset(&m_data_cache[data_index].trickle, timer_now());

    m_handle_cache[handle_index].version = p_info->version;
    if (m_data_cache[data_index].p_packet != NULL)
    {
        mesh_packet_ref_count_dec(m_data_cache[data_index].p_packet);
    }

    /* reference for the cache */
    mesh_packet_ref_count_inc(p_info->p_packet);
    m_data_cache[m_handle_cache[handle_index].data_entry].p_packet = p_info->p_packet;
    return NRF_SUCCESS;
}
Exemple #7
0
static void order_search(void)
{
    radio_event_t evt;
    
    evt.event_type = RADIO_EVENT_TYPE_RX_PREEMPTABLE;
    evt.start_time = 0;
    evt.access_address = 1;
    evt.channel = g_state.channel;
    evt.callback.rx = rx_cb;
    
    if (!mesh_packet_acquire((mesh_packet_t**) &evt.packet_ptr))
    {
        return; /* something is hogging all the packets */
    }
    if (!radio_order(&evt))
    {
        /* couldn't queue the packet for reception, immediately free its only ref */
        mesh_packet_ref_count_dec((mesh_packet_t*) evt.packet_ptr);
    }
}
Exemple #8
0
uint32_t tc_tx(mesh_packet_t* p_packet)
{
    TICK_PIN(PIN_MESH_TX);
    
    /* queue the packet for transmission */
    radio_event_t event;
    memset(&event, 0, sizeof(radio_event_t));
    event.start_time = 0;
    mesh_packet_ref_count_inc(p_packet); /* queue will have a reference until tx_cb */
    event.packet_ptr = (uint8_t*) p_packet;
    event.access_address = 0;
    event.channel = g_state.channel;
    event.callback.tx = tx_cb;
    event.event_type = RADIO_EVENT_TYPE_TX;
    if (!radio_order(&event))
    {
        mesh_packet_ref_count_dec(p_packet); /* queue couldn't hold the ref */
        return NRF_ERROR_NO_MEM;
    }
    
    return NRF_SUCCESS;
}
static void tx_timeout(uint32_t timestamp, void* p_context)
{
    uint32_t next_timeout = timestamp + (UINT32_MAX / 2);
    for (uint32_t i = 0; i < DFU_TX_SLOTS; ++i)
    {
        if (m_tx_slots[i].p_packet)
        {
            uint32_t timeout = next_tx_timeout(&m_tx_slots[i]);
            if (TIMER_OLDER_THAN(timeout, (timestamp + DFU_TX_TIMER_MARGIN_US)))
            {
                if (tc_tx(m_tx_slots[i].p_packet, &m_tx_config) == NRF_SUCCESS)
                {
                    m_tx_slots[i].tx_count++;

                    if (m_tx_slots[i].tx_count == TX_REPEATS_INF &&
                        m_tx_slots[i].repeats  == TX_REPEATS_INF)
                    {
                        m_tx_slots[i].order_time = timeout;
                        m_tx_slots[i].tx_count = 0;
                    }
                    else if (m_tx_slots[i].tx_count >= m_tx_slots[i].repeats)
                    {
                        mesh_packet_ref_count_dec(m_tx_slots[i].p_packet);
                        memset(&m_tx_slots[i], 0, sizeof(dfu_tx_t));
                    }
                    timeout = next_tx_timeout(&m_tx_slots[i]);
                }
            }
            if (TIMER_DIFF(timeout, timestamp) < TIMER_DIFF(next_timeout, timestamp))
            {
                next_timeout = timeout;
            }
        }
    }
    m_tx_timer_evt.timestamp = next_timeout;
    APP_ERROR_CHECK(timer_sch_reschedule(&m_tx_timer_evt, next_timeout));
    m_tx_scheduled = true;
}
void local_packet_push(void* p_context)
{
    mesh_packet_t* p_packet = (mesh_packet_t*) p_context;
    mesh_adv_data_t* p_adv = mesh_packet_adv_data_get(p_packet);
    if (p_adv != NULL)
    {
        handle_info_t info =
        {
            .version = p_adv->version,
            .p_packet = p_packet
        };
        uint16_t handle_index = handle_entry_get(p_adv->handle, true);
        if (handle_index != HANDLE_CACHE_ENTRY_INVALID)
        {
            info.version = m_handle_cache[handle_index].version;
            version_increment(&info.version);
        }
        p_adv->version = info.version;

        handle_storage_info_set(p_adv->handle, &info);
    }
    mesh_packet_ref_count_dec(p_packet); /* for the event queue */
}
uint32_t handle_storage_flag_set(uint16_t handle, handle_flag_t flag, bool value)
{
    if (flag >= HANDLE_FLAG__MAX)
    {
        return NRF_ERROR_INVALID_PARAM;
    }
    if (handle == RBC_MESH_INVALID_HANDLE)
    {
        return NRF_ERROR_INVALID_ADDR;
    }

    uint16_t handle_index = handle_entry_get(handle, true);

    switch (flag)
    {
        case HANDLE_FLAG_PERSISTENT:
            if (handle_index == HANDLE_CACHE_ENTRY_INVALID)
            {
                handle_index = handle_entry_to_head(handle);

                if (handle_index == HANDLE_CACHE_ENTRY_INVALID)
                {
                    return NRF_ERROR_NO_MEM;
                }
            }
            m_handle_cache[handle_index].persistent = value;
            break;

        case HANDLE_FLAG_TX_EVENT:
            if (handle_index == HANDLE_CACHE_ENTRY_INVALID)
            {
                handle_index = handle_entry_to_head(handle);

                if (handle_index == HANDLE_CACHE_ENTRY_INVALID)
                {
                    return NRF_ERROR_NO_MEM;
                }
            }
            m_handle_cache[handle_index].tx_event = value;
            break;

        case HANDLE_FLAG_DISABLED:
            if (value)
            {
                if (handle_index == HANDLE_CACHE_ENTRY_INVALID)
                {
                    return NRF_SUCCESS; /* the value is already disabled */
                }
                if (m_handle_cache[handle_index].data_entry == DATA_CACHE_ENTRY_INVALID)
                {
                    return NRF_SUCCESS; /* the value is already disabled */
                }
                trickle_disable(&m_data_cache[m_handle_cache[handle_index].data_entry].trickle);
            }
            else
            {
                uint32_t error_code;
                mesh_packet_t* p_packet = NULL;
                if (!mesh_packet_acquire(&p_packet))
                {
                    return NRF_ERROR_NO_MEM;
                }

                error_code = mesh_packet_build(p_packet,
                        handle,
                        0,
                        NULL,
                        0);
                if (error_code != NRF_SUCCESS)
                {
                    mesh_packet_ref_count_dec(p_packet);
                    return error_code;
                }

                if (handle_index == HANDLE_CACHE_ENTRY_INVALID)
                {
                    /* may safely run this function inline, as we're already in event handler */
                    local_packet_push(p_packet);
                    return NRF_SUCCESS;
                }
                else
                {
                    if (m_handle_cache[handle_index].data_entry == DATA_CACHE_ENTRY_INVALID)
                    {
                        m_handle_cache[handle_index].data_entry = data_entry_allocate();
                        if (m_handle_cache[handle_index].data_entry == DATA_CACHE_ENTRY_INVALID)
                        {
                            return NRF_ERROR_NO_MEM;
                        }
                    }
                    if (m_data_cache[m_handle_cache[handle_index].data_entry].p_packet != NULL)
                    {
                        /* someone set the packet already, let's not overwrite it. */
                        mesh_packet_ref_count_dec(p_packet);
                    }
                    else
                    {
                        m_data_cache[m_handle_cache[handle_index].data_entry].p_packet = p_packet;
                    }
                    trickle_enable(&m_data_cache[m_handle_cache[handle_index].data_entry].trickle);
                }
            }
            break;
        default:
            return NRF_ERROR_INVALID_PARAM;
    }

    return NRF_SUCCESS;
}
static void handle_data_req_packet(dfu_packet_t* p_packet)
{
    if (p_packet->payload.data.transaction_id == m_transaction.transaction_id)
    {
        if (m_state == BL_STATE_RELAY)
        {
            /* only relay new packets, look for it in cache */
            if (packet_cache_entry_get(p_packet) == NULL)
            {
                set_timeout(STATE_TIMEOUT_RELAY);
                relay_packet(p_packet, 8);
            }
        }
        else
        {   
            req_cache_entry_t* p_req_entry = NULL;
            /* check that we haven't served this request recently. */
            for (uint32_t i = 0; i < REQ_CACHE_SIZE; ++i)
            {
                if (m_req_cache[i].segment == p_packet->payload.req_data.segment)
                {
                    if (m_req_cache[i].rx_count++ < REQ_RX_COUNT_RETRY)
                    {
                        return;
                    }
                    p_req_entry = &m_req_cache[i];
                    break;
                }
            }
            mesh_packet_t* p_rsp;
            if (mesh_packet_acquire(&p_rsp))
            {
                dfu_packet_t* p_dfu_rsp = (dfu_packet_t*) &((ble_ad_t*) p_rsp->payload)->data[2];
                /* serve request */
                if (
                    dfu_has_entry(
                        (uint32_t*) SEGMENT_ADDR(p_packet->payload.req_data.segment, m_transaction.p_start_addr),
                        p_dfu_rsp->payload.rsp_data.data, SEGMENT_LENGTH)
                   )
                {
                    p_dfu_rsp->packet_type = DFU_PACKET_TYPE_DATA_RSP;
                    p_dfu_rsp->payload.rsp_data.segment = p_packet->payload.req_data.segment;
                    p_dfu_rsp->payload.rsp_data.transaction_id = p_packet->payload.req_data.transaction_id;
                    
                    bootloader_packet_set_local_fields(p_rsp, DFU_PACKET_LEN_DATA_RSP);
                    transport_tx(p_rsp, TX_REPEATS_RSP, TX_INTERVAL_TYPE_RSP, NULL);
                    serial_tx(p_dfu_rsp, DFU_PACKET_LEN_DATA_RSP);
                }
                mesh_packet_ref_count_dec(p_rsp);

                /* log our attempt at responding */
                if (!p_req_entry)
                {
                    p_req_entry = &m_req_cache[(m_req_index++) & (REQ_CACHE_SIZE - 1)];
                    p_req_entry->segment = p_packet->payload.req_data.segment;
                }
                p_req_entry->rx_count = 0;
            }
        }
    }
}
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;
    }
}
uint32_t dfu_evt_handler(bl_evt_t* p_evt)
{
    __LOG("BL EVT (0x%x)\n", p_evt->type);
    switch (p_evt->type)
    {
        case BL_EVT_TYPE_ECHO:
            __LOG("\tEcho: %s\n", p_evt->params.echo.str);
            break;
        case BL_EVT_TYPE_DFU_ABORT:
            {
                __LOG("\tAbort event. Reason: 0x%x\n", p_evt->params.dfu.abort.reason);
                rbc_mesh_event_t evt;
                evt.type = RBC_MESH_EVENT_TYPE_DFU_END;
                evt.params.dfu.end.dfu_type = m_transfer_state.type;
                evt.params.dfu.end.role = m_transfer_state.role;
                evt.params.dfu.end.fwid = m_transfer_state.fwid;
                evt.params.dfu.end.end_reason = p_evt->params.dfu.abort.reason;
                memset(&m_transfer_state, 0, sizeof(dfu_transfer_state_t));
                rbc_mesh_event_push(&evt);
            }
            break;

        case BL_EVT_TYPE_DFU_NEW_FW:
            {
                __LOG("\tNew firmware!\n");
                rbc_mesh_event_t evt;
                evt.type = RBC_MESH_EVENT_TYPE_DFU_NEW_FW_AVAILABLE;
                evt.params.dfu.new_fw.dfu_type = p_evt->params.dfu.new_fw.fw_type;
                evt.params.dfu.new_fw.new_fwid = p_evt->params.dfu.new_fw.fwid;
                if (get_curr_fwid(
                            p_evt->params.dfu.new_fw.fw_type,
                            &evt.params.dfu.new_fw.current_fwid) == NRF_SUCCESS)
                {
                    rbc_mesh_event_push(&evt);
                }
            }
            break;

        case BL_EVT_TYPE_DFU_REQ:
            {
                __LOG("\tSource/relay request!\n");
                /* Forward to application */
                rbc_mesh_event_t evt;
                switch (p_evt->params.dfu.req.role)
                {
                    case DFU_ROLE_RELAY:
                        evt.type = RBC_MESH_EVENT_TYPE_DFU_RELAY_REQ;
                        evt.params.dfu.relay_req.dfu_type = p_evt->params.dfu.req.dfu_type;
                        evt.params.dfu.relay_req.fwid = p_evt->params.dfu.req.fwid;
                        evt.params.dfu.relay_req.authority = p_evt->params.dfu.req.dfu_type;
                        break;
                    case DFU_ROLE_SOURCE:
                        evt.type = RBC_MESH_EVENT_TYPE_DFU_SOURCE_REQ;
                        evt.params.dfu.source_req.dfu_type = p_evt->params.dfu.req.dfu_type;
                        break;
                    default:
                        return NRF_ERROR_NOT_SUPPORTED;
                }
                rbc_mesh_event_push(&evt);
            }
            break;

        case BL_EVT_TYPE_DFU_START:
            {
                __LOG("\tDFU start\n");
                if (p_evt->params.dfu.start.role == DFU_ROLE_TARGET)
                {
                    m_transfer_state.state = DFU_STATE_TARGET;
                }
                else if (p_evt->params.dfu.start.role == DFU_ROLE_RELAY)
                {
                    m_transfer_state.state = DFU_STATE_RELAY;
                }
                rbc_mesh_event_t evt;
                evt.type = RBC_MESH_EVENT_TYPE_DFU_START;
                evt.params.dfu.start.dfu_type = p_evt->params.dfu.start.dfu_type;
                evt.params.dfu.start.fwid = p_evt->params.dfu.start.fwid;
                evt.params.dfu.start.role = p_evt->params.dfu.start.role;
                rbc_mesh_event_push(&evt);
                m_timer_evt.cb = abort_timeout;
                return timer_sch_reschedule(&m_timer_evt, timer_now() + TIMER_START_TIMEOUT);
            }


        case BL_EVT_TYPE_DFU_DATA_SEGMENT_RX:
            m_transfer_state.data_progress = (uint8_t) (
                    ((uint32_t) p_evt->params.dfu.data_segment.received_segment * 100) /
                    ((uint32_t) p_evt->params.dfu.data_segment.total_segments));
            m_timer_evt.cb = abort_timeout;
            return timer_sch_reschedule(&m_timer_evt, timer_now() + TIMER_DATA_TIMEOUT);

        case BL_EVT_TYPE_DFU_END:
            {
                __LOG("\tDFU END!\n");
                rbc_mesh_event_t evt;
                evt.type = RBC_MESH_EVENT_TYPE_DFU_END;
                evt.params.dfu.end.dfu_type = p_evt->params.dfu.end.dfu_type;
                evt.params.dfu.end.fwid = p_evt->params.dfu.end.fwid;
                evt.params.dfu.end.end_reason = DFU_END_SUCCESS;
                evt.params.dfu.end.role = p_evt->params.dfu.end.role;
                rbc_mesh_event_push(&evt);
                timer_sch_abort(&m_timer_evt);
            }
            break;

        case BL_EVT_TYPE_BANK_AVAILABLE:
            {
                __LOG("\tDFU BANK AVAILABLE\n");
                rbc_mesh_event_t evt;
                evt.type = RBC_MESH_EVENT_TYPE_DFU_BANK_AVAILABLE;
                evt.params.dfu.bank.dfu_type     = p_evt->params.bank_available.bank_dfu_type;
                evt.params.dfu.bank.fwid         = p_evt->params.bank_available.bank_fwid;
                evt.params.dfu.bank.is_signed    = p_evt->params.bank_available.is_signed;
                evt.params.dfu.bank.p_start_addr = p_evt->params.bank_available.p_bank_addr;
                rbc_mesh_event_push(&evt);
            }
            break;

        case BL_EVT_TYPE_FLASH_ERASE:
            {

                if (p_evt->params.flash.erase.start_addr & (NRF_FICR->CODEPAGESIZE - 1))
                {
                    return NRF_ERROR_INVALID_ADDR;
                }
                if (p_evt->params.flash.erase.length & (NRF_FICR->CODEPAGESIZE - 1))
                {
                    return NRF_ERROR_INVALID_LENGTH;
                }

                uint32_t error_code = mesh_flash_op_push(FLASH_OP_TYPE_ERASE, &p_evt->params.flash);
                if (error_code == NRF_SUCCESS)
                {
                    __LOG("\tErase flash at: 0x%x (length %d)\n", p_evt->params.flash.erase.start_addr, p_evt->params.flash.erase.length);
                }
                return error_code;
            }

        case BL_EVT_TYPE_FLASH_WRITE:
            {
                if (!IS_WORD_ALIGNED(p_evt->params.flash.write.start_addr))
                {
                    return NRF_ERROR_INVALID_ADDR;
                }
                if (!IS_WORD_ALIGNED(p_evt->params.flash.write.length))
                {
                    return NRF_ERROR_INVALID_LENGTH;
                }
                uint32_t error_code = mesh_flash_op_push(FLASH_OP_TYPE_WRITE, &p_evt->params.flash);
                if (error_code == NRF_SUCCESS)
                {
                    __LOG("\tWrite flash at: 0x%x (length %d)\n", p_evt->params.flash.write.start_addr, p_evt->params.flash.write.length);
                }
                return error_code;
            }

        case BL_EVT_TYPE_TX_RADIO:
            __LOG("\tRADIO TX! SLOT %d, count %d, interval: %s, handle: %x\n",
                p_evt->params.tx.radio.tx_slot,
                p_evt->params.tx.radio.tx_count,
                p_evt->params.tx.radio.interval_type == BL_RADIO_INTERVAL_TYPE_EXPONENTIAL ? "exponential" : "periodic",
                p_evt->params.tx.radio.p_dfu_packet->packet_type
            );

            if (m_tx_slots[p_evt->params.tx.radio.tx_slot].p_packet)
            {
                mesh_packet_ref_count_dec(m_tx_slots[p_evt->params.tx.radio.tx_slot].p_packet);
                m_tx_slots[p_evt->params.tx.radio.tx_slot].p_packet = NULL;
            }
            if (mesh_packet_acquire(&m_tx_slots[p_evt->params.tx.radio.tx_slot].p_packet))
            {
                uint32_t time_now = timer_now();
                /* build packet */
                mesh_packet_set_local_addr(m_tx_slots[p_evt->params.tx.radio.tx_slot].p_packet);
                m_tx_slots[p_evt->params.tx.radio.tx_slot].p_packet->header.type = BLE_PACKET_TYPE_ADV_NONCONN_IND;
                m_tx_slots[p_evt->params.tx.radio.tx_slot].p_packet->header.length = DFU_PACKET_OVERHEAD + p_evt->params.tx.radio.length;
                ((ble_ad_t*) m_tx_slots[p_evt->params.tx.radio.tx_slot].p_packet->payload)->adv_data_type = MESH_ADV_DATA_TYPE;
                ((ble_ad_t*) m_tx_slots[p_evt->params.tx.radio.tx_slot].p_packet->payload)->data[0] = (MESH_UUID & 0xFF);
                ((ble_ad_t*) m_tx_slots[p_evt->params.tx.radio.tx_slot].p_packet->payload)->data[1] = (MESH_UUID >> 8) & 0xFF;
                ((ble_ad_t*) m_tx_slots[p_evt->params.tx.radio.tx_slot].p_packet->payload)->adv_data_length = DFU_PACKET_ADV_OVERHEAD + p_evt->params.tx.radio.length;
                memcpy(&m_tx_slots[p_evt->params.tx.radio.tx_slot].p_packet->payload[4], p_evt->params.tx.radio.p_dfu_packet, p_evt->params.tx.radio.length);

                /* fill other fields in the TX slot. */
                m_tx_slots[p_evt->params.tx.radio.tx_slot].interval_type = p_evt->params.tx.radio.interval_type;
                m_tx_slots[p_evt->params.tx.radio.tx_slot].repeats = p_evt->params.tx.radio.tx_count;
                m_tx_slots[p_evt->params.tx.radio.tx_slot].tx_count = 0;
                m_tx_slots[p_evt->params.tx.radio.tx_slot].order_time = time_now + DFU_TX_TIMER_MARGIN_US + (rand_prng_get(&m_prng) & (DFU_TX_START_DELAY_MASK_US));

                /* Fire away */
                if (!m_tx_scheduled || TIMER_DIFF(m_tx_slots[p_evt->params.tx.radio.tx_slot].order_time, time_now) < TIMER_DIFF(m_tx_timer_evt.timestamp, time_now))
                {
                    m_tx_scheduled = true;
                    timer_sch_reschedule(&m_tx_timer_evt,
                            m_tx_slots[p_evt->params.tx.radio.tx_slot].order_time);
                }
            }
            else
            {
                return NRF_ERROR_NO_MEM;