From fc0bd1a015a6e1d9da162a5f8e06164d8c381781 Mon Sep 17 00:00:00 2001 From: "C.S.M" Date: Sun, 20 Sep 2026 15:05:37 +0800 Subject: [PATCH] fix(console): install IO driver before probing the terminal esp_console_new_repl_stdio() called esp_console_setup_prompt(), and thus linenoiseProbe(), before esp_stdio_install_io_driver(). On USB Serial JTAG the VFS read path reports zero available bytes until the driver is installed, so the probe never received the terminal reply, linenoise fell back to dumb mode, and the arrow keys printed escape sequences instead of recalling the command history. Install the IO driver right after the common init so that the probe has a working stdin, and uninstall it again on the error paths. Closes IDF-16181 Co-authored-by: Cursor --- components/console/esp_console_common.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/components/console/esp_console_common.c b/components/console/esp_console_common.c index cbd58f07b94..b0d2234214e 100644 --- a/components/console/esp_console_common.c +++ b/components/console/esp_console_common.c @@ -273,6 +273,7 @@ esp_err_t esp_console_new_repl_stdio(const esp_console_repl_config_t *repl_confi { esp_err_t ret = ESP_OK; esp_console_repl_universal_t *universal_repl = NULL; + bool io_driver_installed = false; if (!repl_config || !ret_repl) { ret = ESP_ERR_INVALID_ARG; goto _exit; @@ -289,9 +290,6 @@ esp_err_t esp_console_new_repl_stdio(const esp_console_repl_config_t *repl_confi fflush(stdout); fsync(fileno(stdout)); - /* the IO related initialization will be performed within the task - * created to run esp_console_repl_task */ - /* initialize console, common part */ ret = esp_console_common_init(repl_config->max_cmdline_length, repl_config->max_cmdline_args, @@ -300,6 +298,15 @@ esp_err_t esp_console_new_repl_stdio(const esp_console_repl_config_t *repl_confi goto _exit; } + /* Must happen before esp_console_setup_prompt(): the terminal probe it runs + * needs a working stdin to read the reply back. Some backends (USB Serial + * JTAG) cannot be read from at all until their driver is installed. */ + ret = esp_stdio_install_io_driver(); + if (ret != ESP_OK) { + goto _exit; + } + io_driver_installed = true; + /* setup history */ ret = esp_console_setup_history(repl_config->history_save_path, repl_config->max_history_len, &universal_repl->repl_com); if (ret != ESP_OK) { @@ -312,11 +319,6 @@ esp_err_t esp_console_new_repl_stdio(const esp_console_repl_config_t *repl_confi goto _exit; } - ret = esp_stdio_install_io_driver(); - if (ret != ESP_OK) { - goto _exit; - } - /* Fill the structure here as it will be used directly by the created task. */ universal_repl->repl_com.state = CONSOLE_REPL_STATE_INIT; universal_repl->repl_com.repl_core.del = esp_console_delete_repl_stdio; @@ -332,6 +334,9 @@ esp_err_t esp_console_new_repl_stdio(const esp_console_repl_config_t *repl_confi *ret_repl = &universal_repl->repl_com.repl_core; return ESP_OK; _exit: + if (io_driver_installed) { + esp_stdio_uninstall_io_driver(); + } if (universal_repl) { esp_console_deinit(); /* Only common_deinit() deletes state_mux, and the weak set_event_fd() never creates it. */