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); }
/* 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 */ }
/* 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); }
/* 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; }
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); } }
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;