void bootloader_reset(void) { #ifdef BOOTLOADER_BUILD uart_tx_flush(0); /* Ensure any buffered log output is displayed */ uart_tx_flush(1); ets_delay_us(1000); /* Allow last byte to leave FIFO */ REG_WRITE(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_SYS_RST); while (1) { } /* This line will never be reached, used to keep gcc happy */ #else abort(); /* This function should really not be called from application code */ #endif }
void stat_help(void) { uart_printf( "Stat command (s) output consists of heading line followed by %u channel stat lines" UART_EOL "Heading line: group current_timestamp" UART_EOL " group - the number identifying radio frequency being used for communications:" UART_EOL " 0 - 24%02uMHz, 1 - 24%02uMHz" UART_EOL " current_timestamp - the current receiver timestamp" UART_EOL UART_EOL "Stat line: epoch rep_total rep_received first_ts last_ts sn ts bt_pressed pressed_ts released_ts vcc" UART_EOL " epoch - the number incremented every time the transmitting module restart" UART_EOL " is detected, zero epoch means the corresponding module was not ever run" UART_EOL " rep_total rep_received - the number of report messages in current epoch" UART_EOL " first_ts last_ts - timestamps of first and last report received in current epoch" UART_EOL " sn - the last report sequence number" UART_EOL " ts - the last report timestamp as reported by sender" UART_EOL " bt_pressed - the button status: 0 - released, 1 - pressed" UART_EOL " pressed_ts released_ts - the timestamps of the button first press / last release" UART_EOL " vcc - the transmitter power voltage measured in millivolts" UART_EOL UART_EOL "All timestamps are measured in 1/1024 sec units." UART_EOL "The ts, pressed_ts, released_ts are measured by the transmitter clock." UART_EOL, MAX_GR_ROLES, GR0_CH, GR1_CH ); uart_tx_flush(); }
// Print stat to UART void stat_dump(void) { unsigned i, ts = rtc_current(); uart_printf("%u %u" UART_EOL, g_stat_group, ts); for (i = 0; i < MAX_GR_ROLES; ++i) { struct stat_buffer const* b = &g_stat_buff[i]; struct stat_entry const* s = &b->e[b->ei]; uart_printf("%u %u %u %u %u ", s->epoch, s->reports_total, s->reports_received, s->first_report_ts, s->last_report_ts ); struct report_packet const* r = &s->last_report; uart_printf("%u %u %u %u %u %u" UART_EOL, r->sn, r->ts, r->bt_pressed, r->bt_pressed_ts, r->bt_released_ts, r->vcc_mv ); } uart_tx_flush(); }
void uart_rx_process(void) { switch (g_uart_rx_buff[0]) { case 'r': get_stat(); break; case 'u': get_transmitter_uptime(); break; case 's': x_start(); break; case 'q': if (g_uart_rx_buff[1] == 'd') { x_get_page(); } else { x_get_status(); } break; case '?': get_help(); break; default: uart_printf("invalid command, send ? to get help" UART_EOL); uart_tx_flush(); } }
static void x_start() { x_upd_tout(); x_set_status(x_starting); uart_printf(UART_EOL); uart_tx_flush(); }
static void get_transmitter_uptime(void) { if (!g_report_packets) { uart_printf(UART_EOL); } else { uart_printf("%u" UART_EOL, last_report_age() + g_last_report.sn * MEASURING_PERIOD); } uart_tx_flush(); }
static inline void get_help(void) { uart_printf(" r - print last report and reception stat" UART_EOL); uart_printf(" u - get transmitter uptime in seconds" UART_EOL); uart_printf(" s - start data transfer" UART_EOL); uart_printf(" q - query data transfer status" UART_EOL); uart_printf(" qd - query data transfer status and data page if available" UART_EOL); uart_printf(" ? - this help" UART_EOL); uart_tx_flush(); }
void panic_puts(const char *outstr) { /* Flush the output buffer */ uart_flush_output(); /* Put all characters in the output buffer */ while (*outstr) panic_txchar(NULL, *outstr++); /* Flush the transmit FIFO */ uart_tx_flush(); }
void uart_rx_process(void) { switch (g_uart_rx_buff[0]) { case 's': g_stat_request = 1; break; case '?': stat_help(); break; default: uart_printf("valid commands are: s, ?" UART_EOL); uart_tx_flush(); } }
void panic_printf(const char *format, ...) { va_list args; /* Flush the output buffer */ uart_flush_output(); va_start(args, format); vfnprintf(panic_txchar, NULL, format, args); va_end(args); /* Flush the transmit FIFO */ uart_tx_flush(); }
static void _console_tx_flush(void) { #if CONFIG_CONSOLE_SERIAL8250MEM uart8250_mem_tx_flush(CONFIG_OXFORD_OXPCIE_BASE_ADDRESS + 0x1000); #endif #if CONFIG_CONSOLE_SERIAL8250 uart8250_tx_flush(CONFIG_TTYS0_BASE); #endif #if CONFIG_CONSOLE_SERIAL_UART uart_tx_flush(); #endif #if CONFIG_USBDEBUG usbdebug_tx_flush(0); #endif }
static void get_stat(void) { if (!g_report_packets) { uart_printf("no valid reports received" UART_EOL); } else { int i, pg_cnt = 0; for (i = 0; i < DATA_PAGES; ++i) { if (bmap_get_bit(g_last_report.page_bitmap, i)) ++pg_cnt; } uart_printf("status = %#x" UART_EOL, g_last_report.hdr.status); uart_printf("PW = %.1f" UART_EOL, PW_SCALE * g_last_report.power); uart_printf("Vbatt = %.4f" UART_EOL, VCC_SCALE * g_last_report.vbatt); uart_printf("SN = %u" UART_EOL, g_last_report.sn); uart_printf("%u pages used" UART_EOL, pg_cnt); uart_printf("%u report packets received" UART_EOL, g_report_packets); uart_printf("last packet was received %u sec ago" UART_EOL, last_report_age()); } if (g_total_packets) { uart_printf("total packets received: %u (%u%% good)" UART_EOL, g_total_packets, 100 * g_good_packets / g_total_packets); } uart_tx_flush(); }
void cmd_loop() { while(1) { /* Wait for a command */ while(ub.command == NULL) { } esp_command_req_t *command = ub.command; ub.command = NULL; /* provide easy access for 32-bit data words */ uint32_t *data_words = (uint32_t *)command->data_buf; /* Send command response header */ esp_command_response_t resp = { .resp = 1, .op_ret = command->op, .len_ret = 0, /* esptool.py ignores this value */ .value = 0, }; /* ESP_READ_REG is the only command that needs to write into the 'resp' structure before we send it back. */ if (command->op == ESP_READ_REG && command->data_len == 4) { resp.value = REG_READ(data_words[0]); } /* Send the command response. */ SLIP_send_frame_delimiter(); SLIP_send_frame_data_buf(&resp, sizeof(esp_command_response_t)); if(command->data_len > MAX_WRITE_BLOCK+16) { SLIP_send_frame_data(ESP_BAD_DATA_LEN); SLIP_send_frame_data(0xEE); SLIP_send_frame_delimiter(); continue; } /* ... some commands will insert in-frame response data between here and when we send the end of the frame */ esp_command_error error = ESP_CMD_NOT_IMPLEMENTED; int status = 0; /* First stage of command processing - before sending error/status */ switch (command->op) { case ESP_ERASE_FLASH: error = verify_data_len(command, 0) || SPIEraseChip(); break; case ESP_ERASE_REGION: /* Params for ERASE_REGION are addr, len */ error = verify_data_len(command, 8) || handle_flash_erase(data_words[0], data_words[1]); break; case ESP_SET_BAUD: /* ESP_SET_BAUD sends two args, we ignore the second one */ error = verify_data_len(command, 8); /* actual baud setting happens after we send the reply */ break; case ESP_READ_FLASH: error = verify_data_len(command, 16); /* actual data is sent after we send the reply */ break; case ESP_FLASH_VERIFY_MD5: /* unsure why the MD5 command has 4 params but we only pass 2 of them, but this is in ESP32 ROM so we can't mess with it. */ error = verify_data_len(command, 16) || handle_flash_get_md5sum(data_words[0], data_words[1]); break; case ESP_FLASH_BEGIN: /* parameters (interpreted differently to ROM flasher): 0 - erase_size (used as total size to write) 1 - num_blocks (ignored) 2 - block_size (should be MAX_WRITE_BLOCK, relies on num_blocks * block_size >= erase_size) 3 - offset (used as-is) */ if (command->data_len == 16 && data_words[2] != MAX_WRITE_BLOCK) { error = ESP_BAD_BLOCKSIZE; } else { error = verify_data_len(command, 16) || handle_flash_begin(data_words[0], data_words[3]); } break; case ESP_FLASH_DEFLATED_BEGIN: /* parameters: 0 - uncompressed size 1 - num_blocks (based on compressed size) 2 - block_size (should be MAX_WRITE_BLOCK, total bytes over serial = num_blocks * block_size) 3 - offset (used as-is) */ if (command->data_len == 16 && data_words[2] != MAX_WRITE_BLOCK) { error = ESP_BAD_BLOCKSIZE; } else { error = verify_data_len(command, 16) || handle_flash_deflated_begin(data_words[0], data_words[1] * data_words[2], data_words[3]); } break; case ESP_FLASH_DATA: case ESP_FLASH_DEFLATED_DATA: /* ACK DATA commands immediately, then process them a few lines down, allowing next command to buffer */ if(is_in_flash_mode()) { error = get_flash_error(); int payload_len = command->data_len - 16; if (data_words[0] != payload_len) { /* First byte of data payload header is length (repeated) as a word */ error = ESP_BAD_DATA_LEN; } uint8_t data_checksum = calculate_checksum(command->data_buf + 16, payload_len); if (data_checksum != command->checksum) { error = ESP_BAD_DATA_CHECKSUM; } } else { error = ESP_NOT_IN_FLASH_MODE; } break; case ESP_FLASH_END: case ESP_FLASH_DEFLATED_END: error = handle_flash_end(); break; case ESP_SPI_SET_PARAMS: /* data params: fl_id, total_size, block_size, sector_Size, page_size, status_mask */ error = verify_data_len(command, 24) || handle_spi_set_params(data_words, &status); break; case ESP_SPI_ATTACH: /* parameter is 'hspi mode' (0, 1 or a pin mask for ESP32. Ignored on ESP8266.) */ error = verify_data_len(command, 4) || handle_spi_attach(data_words[0]); break; case ESP_WRITE_REG: /* params are addr, value, mask (ignored), delay_us (ignored) */ error = verify_data_len(command, 16); if (error == ESP_OK) { REG_WRITE(data_words[0], data_words[1]); } break; case ESP_READ_REG: /* actual READ_REG operation happens higher up */ error = verify_data_len(command, 4); break; case ESP_MEM_BEGIN: error = verify_data_len(command, 16) || handle_mem_begin(data_words[0], data_words[3]); break; case ESP_MEM_DATA: error = handle_mem_data(command->data_buf + 16, command->data_len - 16); break; case ESP_MEM_END: error = verify_data_len(command, 8) || handle_mem_finish(); break; case ESP_RUN_USER_CODE: /* Returning from here will run user code, ie standard boot process This command does not send a response. */ return; } SLIP_send_frame_data(error); SLIP_send_frame_data(status); SLIP_send_frame_delimiter(); /* Some commands need to do things after after sending this response */ if (error == ESP_OK) { switch(command->op) { case ESP_SET_BAUD: ets_delay_us(10000); uart_div_modify(0, baud_rate_to_divider(data_words[0])); ets_delay_us(1000); break; case ESP_READ_FLASH: /* args are: offset, length, block_size, max_in_flight */ handle_flash_read(data_words[0], data_words[1], data_words[2], data_words[3]); break; case ESP_FLASH_DATA: /* drop into flashing mode, discard 16 byte payload header */ handle_flash_data(command->data_buf + 16, command->data_len - 16); break; case ESP_FLASH_DEFLATED_DATA: handle_flash_deflated_data(command->data_buf + 16, command->data_len - 16); break; case ESP_FLASH_DEFLATED_END: case ESP_FLASH_END: /* passing 0 as parameter for ESP_FLASH_END means reboot now */ if (data_words[0] == 0) { /* Flush the FLASH_END response before rebooting */ #ifdef ESP32 uart_tx_flush(0); #endif ets_delay_us(10000); software_reset(); } break; case ESP_MEM_END: if (data_words[1] != 0) { void (*entrypoint_fn)(void) = (void (*))data_words[1]; /* this is a little different from the ROM loader, which exits the loader routine and _then_ calls this function. But for our purposes so far, having a bit of extra stuff on the stack doesn't really matter. */ entrypoint_fn(); } break; } } } } extern uint32_t _bss_start; extern uint32_t _bss_end; void __attribute__((used)) stub_main(); #ifdef ESP8266 __asm__ ( ".global stub_main_8266\n" ".literal_position\n" ".align 4\n" "stub_main_8266:\n" /* ESP8266 wrapper for "stub_main()" manipulates the return address in * a0, so 'return' from here runs user code. * * After setting a0, we jump directly to stub_main_inner() which is a * normal C function * * Adapted from similar approach used by Cesanta Software for ESP8266 * flasher stub. * */ "movi a0, 0x400010a8;" "j stub_main;"); #endif /* This function is called from stub_main, with return address reset to point to user code. */ void stub_main() { const uint32_t greeting = 0x4941484f; /* OHAI */ /* this points to stub_main now, clear for next boot */ ets_set_user_start(0); /* zero bss */ for(uint32_t *p = &_bss_start; p < &_bss_end; p++) { *p = 0; } SLIP_send(&greeting, 4); /* All UART reads come via uart_isr */ ub.reading_buf = ub.buf_a; ets_isr_attach(ETS_UART0_INUM, uart_isr, NULL); SET_PERI_REG_MASK(UART_INT_ENA(0), UART_RX_INTS); ets_isr_unmask(1 << ETS_UART0_INUM); /* Configure default SPI flash functionality. Can be overriden later by esptool.py. */ #ifdef ESP8266 SelectSpiFunction(); #else uint32_t spiconfig = ets_efuse_get_spiconfig(); uint32_t strapping = REG_READ(GPIO_STRAP_REG); /* If GPIO1 (U0TXD) is pulled low and no other boot mode is set in efuse, assume HSPI flash mode (same as normal boot) */ if (spiconfig == 0 && (strapping & 0x1c) == 0x08) { spiconfig = 1; /* HSPI flash mode */ } spi_flash_attach(spiconfig, 0); #endif SPIParamCfg(0, 16*1024*1024, FLASH_BLOCK_SIZE, FLASH_SECTOR_SIZE, FLASH_PAGE_SIZE, FLASH_STATUS_MASK); cmd_loop(); /* if cmd_loop returns, it's due to ESP_RUN_USER_CODE command. */ return; }