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 <cursoragent@cursor.com>
This commit is contained in:
C.S.M
2026-09-20 15:05:37 +08:00
co-authored by Cursor
parent a9e154dd9d
commit fc0bd1a015
+13 -8
View File
@@ -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. */