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); }
void bootloader_packet_set_local_fields(mesh_packet_t* p_packet, uint8_t dfu_packet_len) { mesh_packet_set_local_addr(p_packet); p_packet->header.type = BLE_PACKET_TYPE_ADV_NONCONN_IND; p_packet->header.length = DFU_PACKET_OVERHEAD + dfu_packet_len; ((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 + dfu_packet_len; }
void mesh_packet_take_ownership(mesh_packet_t* p_packet) { /* some packets may come with additional advertisement fields. These must be removed. */ if (mesh_packet_has_additional_data(p_packet)) { mesh_packet_adv_data_sanitize(p_packet); } mesh_packet_set_local_addr(p_packet); }
uint32_t mesh_packet_build(mesh_packet_t* p_packet, rbc_mesh_value_handle_t handle, uint16_t version, uint8_t* data, uint8_t length) { if (p_packet == NULL) { return NRF_ERROR_NULL; } /* place mesh adv data at beginning of adv payload */ mesh_adv_data_t* p_mesh_adv_data = (mesh_adv_data_t*) &p_packet->payload[0]; if (length > RBC_MESH_VALUE_MAX_LEN) { return NRF_ERROR_INVALID_LENGTH; } mesh_packet_set_local_addr(p_packet); p_packet->header.length = MESH_PACKET_OVERHEAD + length; p_packet->header.type = BLE_PACKET_TYPE_ADV_NONCONN_IND; /* fill mesh adv data header fields */ p_mesh_adv_data->adv_data_length = MESH_PACKET_ADV_OVERHEAD + length; p_mesh_adv_data->adv_data_type = MESH_ADV_DATA_TYPE; p_mesh_adv_data->mesh_uuid = MESH_UUID; p_mesh_adv_data->handle = handle; p_mesh_adv_data->version = version; if (length > 0 && data != NULL && length <= RBC_MESH_VALUE_MAX_LEN) { memcpy(p_mesh_adv_data->data, data, length); } return NRF_SUCCESS; }
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;