void serial_handler_init(void) { has_pending_tx = false; /* init packet queues */ tx_fifo.array_len = SERIAL_QUEUE_SIZE; tx_fifo.elem_array = tx_fifo_buffer; tx_fifo.elem_size = sizeof(serial_data_t); tx_fifo.memcpy_fptr = NULL; fifo_init(&tx_fifo); rx_fifo.array_len = SERIAL_QUEUE_SIZE; rx_fifo.elem_array = rx_fifo_buffer; rx_fifo.elem_size = sizeof(serial_data_t); rx_fifo.memcpy_fptr = NULL; fifo_init(&rx_fifo); nrf_gpio_cfg_output(PIN_RDYN); nrf_gpio_pin_set(PIN_RDYN); serial_state = SERIAL_STATE_IDLE; spi_slave_config_t spi_config; spi_config.bit_order = SPIM_LSB_FIRST; spi_config.mode = SPI_MODE_0; spi_config.def_tx_character = 0; spi_config.orc_tx_character = 0; spi_config.pin_csn = PIN_CSN; spi_config.pin_miso = PIN_MISO; spi_config.pin_mosi = PIN_MOSI; spi_config.pin_sck = PIN_SCK; APP_ERROR_CHECK(spi_slave_init(&spi_config)); APP_ERROR_CHECK(spi_slave_evt_handler_register(spi_event_handler)); gpiote_init(); /* set initial buffers, dummy in tx */ //prepare_rx(); #if 1 /* notify application controller of the restart */ serial_evt_t started_event; started_event.length = 4; started_event.opcode = SERIAL_EVT_OPCODE_DEVICE_STARTED; started_event.params.device_started.operating_mode = OPERATING_MODE_STANDBY; uint32_t reset_reason; sd_power_reset_reason_get(&reset_reason); started_event.params.device_started.hw_error = !!(reset_reason & (1 << 3)); started_event.params.device_started.data_credit_available = SERIAL_QUEUE_SIZE; if (!serial_handler_event_send(&started_event)) { APP_ERROR_CHECK(NRF_ERROR_INTERNAL); } #endif }
static void serial_tx(dfu_packet_t* p_packet, uint16_t len) { #ifdef SERIAL serial_evt_t evt; if (len > 29) { APP_ERROR_CHECK(NRF_ERROR_INVALID_LENGTH); } evt.opcode = SERIAL_EVT_OPCODE_DFU; evt.length = SERIAL_PACKET_OVERHEAD + len; memcpy(&evt.params.dfu.packet, p_packet, len); serial_handler_event_send(&evt); #endif }
static void char_rx(uint8_t c) { static serial_data_t rx_buf = {0}; static uint8_t* pp = rx_buf.buffer; *(pp++) = c; uint32_t len = (uint32_t)(pp - rx_buf.buffer); if (len >= sizeof(rx_buf) || (len > 1 && len >= rx_buf.buffer[0] + 1)) /* end of command */ { if (fifo_push(&m_rx_fifo, &rx_buf) != NRF_SUCCESS) { /* respond inline, queue was full */ serial_evt_t fail_evt; fail_evt.length = 3; fail_evt.opcode = SERIAL_EVT_OPCODE_CMD_RSP; fail_evt.params.cmd_rsp.command_opcode = ((serial_cmd_t*) rx_buf.buffer)->opcode; fail_evt.params.cmd_rsp.status = ACI_STATUS_ERROR_BUSY; serial_handler_event_send(&fail_evt); } else { #ifdef BOOTLOADER NVIC_SetPendingIRQ(SWI2_IRQn); #else async_event_t async_evt; async_evt.type = EVENT_TYPE_GENERIC; async_evt.callback.generic.cb = mesh_aci_command_check_cb; async_evt.callback.generic.p_context = NULL; event_handler_push(&async_evt); #endif } if (fifo_is_full(&m_rx_fifo)) { m_serial_state = SERIAL_STATE_WAIT_FOR_QUEUE; NRF_UART0->TASKS_STOPRX = 1; } pp = rx_buf.buffer; } }
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; } }