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; } }