diff --git a/components/console/CMakeLists.txt b/components/console/CMakeLists.txt index 7f63fd4f75..b7238aa4b3 100644 --- a/components/console/CMakeLists.txt +++ b/components/console/CMakeLists.txt @@ -8,13 +8,12 @@ set(srcs "commands.c" "split_argv.c" "linenoise/linenoise.c") -set(requires vfs) +set(requires vfs esp_stdio) if(${target} STREQUAL "linux") - list(APPEND srcs "esp_console_repl_linux.c" "esp_console_repl_internal.c") + list(APPEND srcs "esp_console_repl_internal.c") else() list(APPEND srcs "esp_console_repl_chip.c") - list(APPEND requires esp_stdio) if(CONFIG_VFS_SUPPORT_SELECT) list(APPEND srcs "esp_console_repl_internal.c") endif() @@ -35,16 +34,15 @@ set(argtable_srcs argtable3/arg_cmd.c argtable3/arg_utils.c argtable3/argtable3.c) +# TODO IDF-14810: Remove io_requires dependency +if(NOT ${target} STREQUAL "linux") + set(io_requires esp_driver_uart esp_driver_usb_serial_jtag esp_usb_cdc_rom_console) +endif() -idf_component_register(SRCS ${srcs} - ${argtable_srcs} +idf_component_register(SRCS ${srcs} ${argtable_srcs} INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} PRIV_INCLUDE_DIRS private_include - REQUIRES "${requires}" - PRIV_REQUIRES esp_driver_uart - esp_driver_usb_serial_jtag - esp_usb_cdc_rom_console - ) + REQUIRES ${requires} ${io_requires}) if(CONFIG_LIBC_PICOLIBC) list(APPEND srcs_include_stdio_private ${srcs}) diff --git a/components/console/esp_console.h b/components/console/esp_console.h index 6c51ee2e1d..2a2279f6e1 100644 --- a/components/console/esp_console.h +++ b/components/console/esp_console.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2016-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2016-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,7 +14,7 @@ extern "C" { #include "esp_heap_caps.h" #include "esp_err.h" #include "freertos/FreeRTOS.h" -#include "soc/uart_pins.h" +#include "esp_stdio.h" // Forward declaration. Definition in linenoise/linenoise.h. typedef struct linenoiseCompletions linenoiseCompletions; @@ -72,65 +72,6 @@ typedef struct { .max_cmdline_length = 0, \ } -#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM -/** - * @brief Parameters for console device: UART - * - */ -typedef struct { - int channel; //!< UART channel number (count from zero) - int baud_rate; //!< Communication baud rate - int tx_gpio_num; //!< GPIO number for TX path, -1 means using default one - int rx_gpio_num; //!< GPIO number for RX path, -1 means using default one -} esp_console_dev_uart_config_t; - -#if CONFIG_ESP_CONSOLE_UART_CUSTOM -#define ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT() \ -{ \ - .channel = CONFIG_ESP_CONSOLE_UART_NUM, \ - .baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE, \ - .tx_gpio_num = (CONFIG_ESP_CONSOLE_UART_TX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_TX_GPIO : U0TXD_GPIO_NUM, \ - .rx_gpio_num = (CONFIG_ESP_CONSOLE_UART_RX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_RX_GPIO : U0RXD_GPIO_NUM, \ -} -#else -#define ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT() \ -{ \ - .channel = CONFIG_ESP_CONSOLE_UART_NUM, \ - .baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE, \ - .tx_gpio_num = -1, \ - .rx_gpio_num = -1, \ -} -#endif // CONFIG_ESP_CONSOLE_UART_CUSTOM -#endif // CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM - -#if CONFIG_ESP_CONSOLE_USB_CDC || (defined __DOXYGEN__ && SOC_USB_OTG_SUPPORTED) -/** - * @brief Parameters for console device: USB CDC - * - * @note It's an empty structure for now, reserved for future - * - */ -typedef struct { - -} esp_console_dev_usb_cdc_config_t; - -#define ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT() {} -#endif // CONFIG_ESP_CONSOLE_USB_CDC || (defined __DOXYGEN__ && SOC_USB_OTG_SUPPORTED) - -#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || (defined __DOXYGEN__ && SOC_USB_SERIAL_JTAG_SUPPORTED) -/** - * @brief Parameters for console device: USB-SERIAL-JTAG - * - * @note It's an empty structure for now, reserved for future - * - */ -typedef struct { - -} esp_console_dev_usb_serial_jtag_config_t; - -#define ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT() {} -#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || (defined __DOXYGEN__ && SOC_USB_SERIAL_JTAG_SUPPORTED) - typedef enum { ESP_CONSOLE_HELP_VERBOSE_LEVEL_0 = 0, ESP_CONSOLE_HELP_VERBOSE_LEVEL_1 = 1, @@ -377,7 +318,13 @@ struct esp_console_repl_s { esp_err_t (*del)(esp_console_repl_t *repl); }; +#if !CONFIG_IDF_TARGET_LINUX + +/* TODO IDF-14810: Remove the whole section under the #if */ #if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM +#include "driver/esp_private/uart_vfs.h" +#include "driver/uart_vfs.h" + /** * @brief Establish a console REPL environment over UART driver * @@ -399,10 +346,17 @@ struct esp_console_repl_s { * - ESP_OK on success * - ESP_FAIL Parameter error */ -esp_err_t esp_console_new_repl_uart(const esp_console_dev_uart_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl); +esp_err_t esp_console_new_repl_uart(const esp_console_dev_uart_config_t *dev_config, + const esp_console_repl_config_t *repl_config, + esp_console_repl_t **ret_repl); #endif // CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM +/* TODO IDF-14810: Remove the whole section under the #if */ #if CONFIG_ESP_CONSOLE_USB_CDC || (defined __DOXYGEN__ && SOC_USB_OTG_SUPPORTED) + +#include "esp_private/esp_vfs_cdcacm.h" +#include "esp_vfs_cdcacm.h" + /** * @brief Establish a console REPL environment over USB CDC * @@ -422,10 +376,17 @@ esp_err_t esp_console_new_repl_uart(const esp_console_dev_uart_config_t *dev_con * - ESP_OK on success * - ESP_FAIL Parameter error */ -esp_err_t esp_console_new_repl_usb_cdc(const esp_console_dev_usb_cdc_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl); +esp_err_t esp_console_new_repl_usb_cdc(const esp_console_dev_usb_cdc_config_t *dev_config, + const esp_console_repl_config_t *repl_config, + esp_console_repl_t **ret_repl); #endif // CONFIG_ESP_CONSOLE_USB_CDC || (defined __DOXYGEN__ && SOC_USB_OTG_SUPPORTED) +/* TODO IDF-14810: Remove the whole section under the #if */ #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || (defined __DOXYGEN__ && SOC_USB_SERIAL_JTAG_SUPPORTED) + +#include "driver/esp_private/usb_serial_jtag_vfs.h" +#include "driver/usb_serial_jtag_vfs.h" + /** * @brief Establish a console REPL (Read-eval-print loop) environment over USB-SERIAL-JTAG * @@ -445,9 +406,39 @@ esp_err_t esp_console_new_repl_usb_cdc(const esp_console_dev_usb_cdc_config_t *d * - ESP_OK on success * - ESP_FAIL Parameter error */ -esp_err_t esp_console_new_repl_usb_serial_jtag(const esp_console_dev_usb_serial_jtag_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl); +esp_err_t esp_console_new_repl_usb_serial_jtag(const esp_console_dev_usb_serial_jtag_config_t *dev_config, + const esp_console_repl_config_t *repl_config, + esp_console_repl_t **ret_repl); #endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || (defined __DOXYGEN__ && SOC_USB_SERIAL_JTAG_SUPPORTED) +#endif // !CONFIG_IDF_TARGET_LINUX + +/** + * @brief Create a new console REPL instance that uses the active stdio driver. + * + * This sets up a Read-Eval-Print-Loop (REPL) environment where user input and + * output are handled through the configured stdio backend. This allows the + * console shell to operate over UART, USB, or another configured console port + * without requiring backend-specific setup in user code. + * + * @param repl_config Pointer to the REPL configuration structure. + * @param ret_repl Output pointer that will be assigned the created REPL instance. + * @return ESP_OK if the REPL instance was created successfully, or an error code if creation failed. + */ +esp_err_t esp_console_new_repl_stdio(const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl); + +/** + * @brief Destroy and clean up a stdio-based console REPL instance. + * + * This releases resources associated with a REPL created using + * esp_console_new_repl_stdio. Call this when the REPL is no longer required or + * before replacing it with another console interface. + * + * @param repl Pointer to the REPL instance to delete. + * @return ESP_OK if the REPL instance was deleted successfully, or an error code on failure. + */ +esp_err_t esp_console_delete_repl_stdio(esp_console_repl_t *repl); + /** * @brief Start REPL environment * @param[in] repl REPL handle returned from esp_console_new_repl_xxx diff --git a/components/console/esp_console_common.c b/components/console/esp_console_common.c index bedcf3549f..1d8a9fa53b 100644 --- a/components/console/esp_console_common.c +++ b/components/console/esp_console_common.c @@ -1,17 +1,17 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "sdkconfig.h" -#include // __containerof #include #include #include "esp_console.h" #include "console_private.h" #include "esp_log.h" #include "linenoise/linenoise.h" +#include "esp_stdio.h" #include "esp_vfs_eventfd.h" #if CONFIG_IDF_TARGET_LINUX @@ -20,6 +20,96 @@ static const char *TAG = "console.common"; +void esp_console_repl_task(void *args) +{ + esp_console_repl_universal_t *repl_conf = (esp_console_repl_universal_t *) args; + esp_console_repl_com_t *repl_com = &repl_conf->repl_com; + + /* Waiting for task notify. This happens when `esp_console_start_repl()` + * function is called. */ + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + if (repl_com->state_mux != NULL) { + xSemaphoreTake(repl_com->state_mux, portMAX_DELAY); + } + + /* Change standard input and output of the task if the requested UART is + * NOT the default one. This block will replace stdin, stdout and stderr. + */ +#if CONFIG_LIBC_PICOLIBC + // TODO IDF-14901 + if (repl_com->_stdin) { + stdin = repl_com->_stdin; + stdout = stderr = repl_com->_stdout; + } else { + linenoise_init_with_global_stdio(); + } +#else +#if !CONFIG_IDF_TARGET_LINUX + esp_console_setup_standard_stream(repl_conf->dev_config); +#endif // !CONFIG_IDF_TARGET_LINUX +#endif // CONFIG_LIBC_PICOLIBC + + /* Disable buffering on stdin of the current task. + * If the console is ran on a different UART than the default one, + * buffering shall only be disabled for the current one. */ + setvbuf(stdin, NULL, _IONBF, 0); + + /* This message shall be printed here and not earlier as the stdout + * has just been set above. */ + printf("\r\n" + "Type 'help' to get the list of commands.\r\n" + "Use UP/DOWN arrows to navigate through command history.\r\n" + "Press TAB when typing command name to auto-complete.\r\n"); + + if (linenoiseIsDumbMode()) { + printf("\r\n" + "Your terminal application does not support escape sequences.\n\n" + "Line editing and history features are disabled.\n\n" + "On Windows, try using Windows Terminal or Putty instead.\r\n"); + } + + while (repl_com->state == CONSOLE_REPL_STATE_START) { + char *line = linenoise(repl_com->prompt); + if (line == NULL) { + ESP_LOGD(TAG, "empty line"); + /* Ignore empty lines */ + continue; + } + /* Add the command to the history */ + linenoiseHistoryAdd(line); + /* Save command history to filesystem */ + if (repl_com->history_save_path) { + linenoiseHistorySave(repl_com->history_save_path); + } + + /* Try to run the command */ + int ret; + esp_err_t err = esp_console_run(line, &ret); + if (err == ESP_ERR_NOT_FOUND) { + printf("Unrecognized command\n"); + } else if (err == ESP_ERR_INVALID_ARG) { + // command was empty + } else if (err == ESP_OK && ret != ESP_OK) { + printf("Command returned non-zero error code: 0x%x (%s)\n", ret, esp_err_to_name(ret)); + } else if (err != ESP_OK) { + printf("Internal error: %s\n", esp_err_to_name(err)); + } + /* linenoise allocates line buffer on the heap, so need to free it */ + linenoiseFree(line); + } + +#if CONFIG_LIBC_PICOLIBC + linenoise_close_stdio(); +#endif + + if (repl_com->state_mux != NULL) { + xSemaphoreGive(repl_com->state_mux); + } + ESP_LOGD(TAG, "The End"); + vTaskDelete(NULL); +} + esp_err_t esp_console_setup_prompt(const char *prompt, esp_console_repl_com_t *repl_com) { /* set command line prompt */ @@ -60,8 +150,6 @@ esp_err_t esp_console_setup_prompt(const char *prompt, esp_console_repl_com_t *r esp_err_t esp_console_setup_history(const char *history_path, uint32_t max_history_len, esp_console_repl_com_t *repl_com) { - esp_err_t ret = ESP_OK; - repl_com->history_save_path = history_path; if (history_path) { /* Load command history from filesystem */ @@ -71,12 +159,9 @@ esp_err_t esp_console_setup_history(const char *history_path, uint32_t max_histo /* Set command history size */ if (linenoiseHistorySetMaxLen(max_history_len) != 1) { ESP_LOGE(TAG, "set max history length to %"PRIu32" failed", max_history_len); - ret = ESP_FAIL; - goto _exit; + return ESP_FAIL; } return ESP_OK; -_exit: - return ret; } __attribute__((weak)) esp_err_t esp_console_internal_set_event_fd(esp_console_repl_com_t *repl_com) @@ -103,12 +188,12 @@ esp_err_t esp_console_common_init(size_t max_cmdline_length, esp_console_repl_co #endif ret = esp_console_init(&console_config); if (ret != ESP_OK) { - goto _exit; + return ret; } ret = esp_console_register_help_command(); if (ret != ESP_OK) { - goto _exit; + return ret; } /* Configure linenoise line completion library */ @@ -119,16 +204,21 @@ esp_err_t esp_console_common_init(size_t max_cmdline_length, esp_console_repl_co linenoiseSetCompletionCallback(&esp_console_get_completion); linenoiseSetHintsCallback((linenoiseHintsCallback *)&esp_console_get_hint); + /* set the maximum command line length. the function returns -1 or + * 0 so it is ok to cast it to esp_err_t here. */ + ret = linenoiseSetMaxLineLen(repl_com->max_cmdline_length); + if (ret != ESP_OK) { + return ret; + } + #if CONFIG_VFS_SUPPORT_SELECT || CONFIG_IDF_TARGET_LINUX ret = esp_console_internal_set_event_fd(repl_com); if (ret != ESP_OK) { - goto _exit; + return ret; } #endif return ESP_OK; -_exit: - return ret; } __attribute__((weak)) esp_err_t esp_console_common_deinit(esp_console_repl_com_t *repl_com) @@ -151,116 +241,109 @@ __attribute__((weak)) esp_err_t esp_console_common_deinit(esp_console_repl_com_t return ESP_OK; } -esp_err_t esp_console_start_repl(esp_console_repl_t *repl) +esp_err_t esp_console_delete_repl_stdio(esp_console_repl_t *repl) +{ + esp_console_repl_com_t *repl_com = (esp_console_repl_com_t*)repl; + esp_console_repl_universal_t *universal_repl = (esp_console_repl_universal_t*)repl_com; + + const esp_err_t ret = esp_console_common_deinit(repl_com); + if (ret != ESP_OK) { + if (ret == ESP_ERR_INVALID_STATE) { + ESP_LOGE(TAG, "already de-initialized"); + } + return ret; + } + + esp_console_deinit(); + + esp_stdio_uninstall_io_driver(); + + free(universal_repl); + + return ESP_OK; +} + +esp_err_t esp_console_new_repl_stdio(const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl) { esp_err_t ret = ESP_OK; - esp_console_repl_com_t *repl_com = __containerof(repl, esp_console_repl_com_t, repl_core); + esp_console_repl_universal_t *universal_repl = NULL; + if (!repl_config || !ret_repl) { + ret = ESP_ERR_INVALID_ARG; + goto _exit; + } + + // allocate memory for console REPL context + universal_repl = calloc(1, sizeof(esp_console_repl_universal_t)); + if (!universal_repl) { + ret = ESP_ERR_NO_MEM; + goto _exit; + } + + /* Drain stdout before reconfiguring it */ + 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, + &universal_repl->repl_com); + if (ret != ESP_OK) { + goto _exit; + } + + /* 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) { + goto _exit; + } + + /* setup prompt */ + ret = esp_console_setup_prompt(repl_config->prompt, &universal_repl->repl_com); + if (ret != ESP_OK) { + 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; + + /* Spawn a single thread to run REPL, we need to pass `universal_repl` to it as + * it also requires the uart channel. */ + if (xTaskCreatePinnedToCore(esp_console_repl_task, "console_repl", repl_config->task_stack_size, + universal_repl, repl_config->task_priority, &universal_repl->repl_com.task_hdl, repl_config->task_core_id) != pdTRUE) { + ret = ESP_FAIL; + goto _exit; + } + + *ret_repl = &universal_repl->repl_com.repl_core; + return ESP_OK; +_exit: + if (universal_repl) { + esp_console_deinit(); + free(universal_repl); + } + if (ret_repl) { + *ret_repl = NULL; + } + return ret; +} + +esp_err_t esp_console_start_repl(esp_console_repl_t *repl) +{ + esp_console_repl_com_t *repl_com = (esp_console_repl_com_t*)repl; // check if already initialized if (repl_com->state != CONSOLE_REPL_STATE_INIT) { - ret = ESP_ERR_INVALID_STATE; - goto _exit; + return ESP_ERR_INVALID_STATE; } repl_com->state = CONSOLE_REPL_STATE_START; xTaskNotifyGive(repl_com->task_hdl); return ESP_OK; -_exit: - return ret; -} - -void esp_console_repl_task(void *args) -{ - esp_console_repl_universal_t *repl_conf = (esp_console_repl_universal_t *) args; - esp_console_repl_com_t *repl_com = &repl_conf->repl_com; - - /* Waiting for task notify. This happens when `esp_console_start_repl()` - * function is called. */ - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - - if (repl_com->state_mux != NULL) { - xSemaphoreTake(repl_com->state_mux, portMAX_DELAY); - } - - /* Change standard input and output of the task if the requested UART is - * NOT the default one. This block will replace stdin, stdout and stderr. - */ -#if CONFIG_LIBC_PICOLIBC - // TODO IDF-14901 - if (repl_com->_stdin) { - stdin = repl_com->_stdin; - stdout = stderr = repl_com->_stdout; - } else { - linenoise_init_with_global_stdio(); - } -#else - const int uart_channel = repl_conf->uart_channel; - if (uart_channel != CONFIG_ESP_CONSOLE_UART_NUM) { - char path[CONSOLE_PATH_MAX_LEN] = { 0 }; - snprintf(path, CONSOLE_PATH_MAX_LEN, "/dev/uart/%d", uart_channel); - - stdin = fopen(path, "r"); - stdout = fopen(path, "w"); - stderr = stdout; - } -#endif - - /* Disable buffering on stdin of the current task. - * If the console is ran on a different UART than the default one, - * buffering shall only be disabled for the current one. */ - setvbuf(stdin, NULL, _IONBF, 0); - - /* This message shall be printed here and not earlier as the stdout - * has just been set above. */ - printf("\r\n" - "Type 'help' to get the list of commands.\r\n" - "Use UP/DOWN arrows to navigate through command history.\r\n" - "Press TAB when typing command name to auto-complete.\r\n"); - - if (linenoiseIsDumbMode()) { - printf("\r\n" - "Your terminal application does not support escape sequences.\n\n" - "Line editing and history features are disabled.\n\n" - "On Windows, try using Windows Terminal or Putty instead.\r\n"); - } - - linenoiseSetMaxLineLen(repl_com->max_cmdline_length); - while (repl_com->state == CONSOLE_REPL_STATE_START) { - char *line = linenoise(repl_com->prompt); - if (line == NULL) { - ESP_LOGD(TAG, "empty line"); - /* Ignore empty lines */ - continue; - } - /* Add the command to the history */ - linenoiseHistoryAdd(line); - /* Save command history to filesystem */ - if (repl_com->history_save_path) { - linenoiseHistorySave(repl_com->history_save_path); - } - - /* Try to run the command */ - int ret; - esp_err_t err = esp_console_run(line, &ret); - if (err == ESP_ERR_NOT_FOUND) { - printf("Unrecognized command\n"); - } else if (err == ESP_ERR_INVALID_ARG) { - // command was empty - } else if (err == ESP_OK && ret != ESP_OK) { - printf("Command returned non-zero error code: 0x%x (%s)\n", ret, esp_err_to_name(ret)); - } else if (err != ESP_OK) { - printf("Internal error: %s\n", esp_err_to_name(err)); - } - /* linenoise allocates line buffer on the heap, so need to free it */ - linenoiseFree(line); - } - -#if CONFIG_LIBC_PICOLIBC - linenoise_close_stdio(); -#endif - - if (repl_com->state_mux != NULL) { - xSemaphoreGive(repl_com->state_mux); - } - ESP_LOGD(TAG, "The End"); - vTaskDelete(NULL); } diff --git a/components/console/esp_console_repl_chip.c b/components/console/esp_console_repl_chip.c index 96132fa984..c6b173f51c 100644 --- a/components/console/esp_console_repl_chip.c +++ b/components/console/esp_console_repl_chip.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2016-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2016-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,174 +14,40 @@ #include "esp_console.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "driver/uart.h" -#include "driver/uart_vfs.h" -#include "driver/usb_serial_jtag.h" -#include "driver/usb_serial_jtag_vfs.h" -#include "esp_private/usb_console.h" -#include "esp_vfs_cdcacm.h" - +#include "esp_stdio.h" #include "console_private.h" -#if !CONFIG_ESP_CONSOLE_NONE +#if CONFIG_ESP_CONSOLE_UART +#include "driver/uart_vfs.h" + static const char *TAG = "console.repl.chip"; -#endif // !CONFIG_ESP_CONSOLE_NONE -#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM -static esp_err_t esp_console_repl_uart_delete(esp_console_repl_t *repl); -#endif // CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM -#if CONFIG_ESP_CONSOLE_USB_CDC -static esp_err_t esp_console_repl_usb_cdc_delete(esp_console_repl_t *repl); -#endif // CONFIG_ESP_CONSOLE_USB_CDC -#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG -static esp_err_t esp_console_repl_usb_serial_jtag_delete(esp_console_repl_t *repl); -#endif //CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG - -#if CONFIG_ESP_CONSOLE_USB_CDC -esp_err_t esp_console_new_repl_usb_cdc(const esp_console_dev_usb_cdc_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl) +static esp_err_t esp_console_repl_uart_delete_legacy(esp_console_repl_t *repl) { - esp_err_t ret = ESP_OK; - esp_console_repl_universal_t *cdc_repl = NULL; - if (!repl_config || !dev_config || !ret_repl) { - ret = ESP_ERR_INVALID_ARG; - goto _exit; - } - // allocate memory for console REPL context - cdc_repl = calloc(1, sizeof(esp_console_repl_universal_t)); - if (!cdc_repl) { - ret = ESP_ERR_NO_MEM; - goto _exit; + esp_console_repl_com_t *repl_com = (esp_console_repl_com_t*)repl; + esp_console_repl_universal_t *uart_repl = (esp_console_repl_universal_t*)repl_com; + + // check if already de-initialized + if (repl_com->state == CONSOLE_REPL_STATE_DEINIT) { + ESP_LOGE(TAG, "already de-initialized"); + return ESP_ERR_INVALID_STATE; } - /* Minicom, screen, idf_monitor send CR when ENTER key is pressed */ - esp_vfs_dev_cdcacm_set_rx_line_endings(ESP_LINE_ENDINGS_CR); - /* Move the caret to the beginning of the next line on '\n' */ - esp_vfs_dev_cdcacm_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); - - /* Enable blocking mode on stdin and stdout */ - fcntl(fileno(stdout), F_SETFL, 0); - fcntl(fileno(stdin), F_SETFL, 0); - - // initialize console, common part - ret = esp_console_common_init(repl_config->max_cmdline_length, &cdc_repl->repl_com); + const esp_err_t ret = esp_console_common_deinit(repl_com); if (ret != ESP_OK) { - goto _exit; + return ret; } - // setup history - ret = esp_console_setup_history(repl_config->history_save_path, repl_config->max_history_len, &cdc_repl->repl_com); - if (ret != ESP_OK) { - goto _exit; - } + esp_console_deinit(); + uart_vfs_dev_port_deinit(uart_repl->dev_config); + /* free dev_config since it was malloc in esp_console_new_repl_uart_legacy */ + free(uart_repl->dev_config); + free(uart_repl); - // setup prompt - esp_console_setup_prompt(repl_config->prompt, &cdc_repl->repl_com); - - /* Fill the structure here as it will be used directly by the created task. */ - cdc_repl->uart_channel = CONFIG_ESP_CONSOLE_UART_NUM; - cdc_repl->repl_com.state = CONSOLE_REPL_STATE_INIT; - cdc_repl->repl_com.repl_core.del = esp_console_repl_usb_cdc_delete; - - /* spawn a single thread to run REPL */ - if (xTaskCreatePinnedToCore(esp_console_repl_task, "console_repl", repl_config->task_stack_size, - cdc_repl, repl_config->task_priority, &cdc_repl->repl_com.task_hdl, repl_config->task_core_id) != pdTRUE) { - ret = ESP_FAIL; - goto _exit; - } - - *ret_repl = &cdc_repl->repl_com.repl_core; return ESP_OK; -_exit: - if (cdc_repl) { - esp_console_deinit(); - free(cdc_repl); - } - if (ret_repl) { - *ret_repl = NULL; - } - return ret; } -#endif // CONFIG_ESP_CONSOLE_USB_CDC -#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG -esp_err_t esp_console_new_repl_usb_serial_jtag(const esp_console_dev_usb_serial_jtag_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl) -{ - esp_console_repl_universal_t *usb_serial_jtag_repl = NULL; - if (!repl_config || !dev_config || !ret_repl) { - return ESP_ERR_INVALID_ARG; - } - - esp_err_t ret = ESP_OK; - // allocate memory for console REPL context - usb_serial_jtag_repl = calloc(1, sizeof(esp_console_repl_universal_t)); - if (!usb_serial_jtag_repl) { - ret = ESP_ERR_NO_MEM; - goto _exit; - } - - /* Minicom, screen, idf_monitor send CR when ENTER key is pressed */ - usb_serial_jtag_vfs_set_rx_line_endings(ESP_LINE_ENDINGS_CR); - /* Move the caret to the beginning of the next line on '\n' */ - usb_serial_jtag_vfs_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF); - - /* Enable blocking mode on stdin and stdout */ - fcntl(fileno(stdout), F_SETFL, 0); - fcntl(fileno(stdin), F_SETFL, 0); - - usb_serial_jtag_driver_config_t usb_serial_jtag_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(); - - /* Install USB-SERIAL-JTAG driver for interrupt-driven reads and writes */ - ret = usb_serial_jtag_driver_install(&usb_serial_jtag_config); - if (ret != ESP_OK) { - goto _exit; - } - - // initialize console, common part - ret = esp_console_common_init(repl_config->max_cmdline_length, &usb_serial_jtag_repl->repl_com); - if (ret != ESP_OK) { - goto _exit; - } - - /* Tell vfs to use usb-serial-jtag driver */ - usb_serial_jtag_vfs_use_driver(); - - // setup history - ret = esp_console_setup_history(repl_config->history_save_path, repl_config->max_history_len, &usb_serial_jtag_repl->repl_com); - if (ret != ESP_OK) { - goto _exit; - } - - // setup prompt - esp_console_setup_prompt(repl_config->prompt, &usb_serial_jtag_repl->repl_com); - - /* Fill the structure here as it will be used directly by the created task. */ - usb_serial_jtag_repl->uart_channel = CONFIG_ESP_CONSOLE_UART_NUM; - usb_serial_jtag_repl->repl_com.state = CONSOLE_REPL_STATE_INIT; - usb_serial_jtag_repl->repl_com.repl_core.del = esp_console_repl_usb_serial_jtag_delete; - - /* spawn a single thread to run REPL */ - if (xTaskCreatePinnedToCore(esp_console_repl_task, "console_repl", repl_config->task_stack_size, - usb_serial_jtag_repl, repl_config->task_priority, &usb_serial_jtag_repl->repl_com.task_hdl, repl_config->task_core_id) != pdTRUE) { - ret = ESP_FAIL; - goto _exit; - } - - *ret_repl = &usb_serial_jtag_repl->repl_com.repl_core; - return ESP_OK; -_exit: - if (usb_serial_jtag_repl) { - esp_console_deinit(); - free(usb_serial_jtag_repl); - } - if (ret_repl) { - *ret_repl = NULL; - } - return ret; -} -#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG - -#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM -esp_err_t esp_console_new_repl_uart(const esp_console_dev_uart_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl) +static esp_err_t esp_console_new_repl_uart_legacy(const esp_console_dev_uart_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl) { esp_err_t ret = ESP_OK; esp_console_repl_universal_t *uart_repl = NULL; @@ -200,46 +66,11 @@ esp_err_t esp_console_new_repl_uart(const esp_console_dev_uart_config_t *dev_con fflush(stdout); fsync(fileno(stdout)); - /* Minicom, screen, idf_monitor send CR when ENTER key is pressed */ - uart_vfs_dev_port_set_rx_line_endings(dev_config->channel, ESP_LINE_ENDINGS_CR); - /* Move the caret to the beginning of the next line on '\n' */ - uart_vfs_dev_port_set_tx_line_endings(dev_config->channel, ESP_LINE_ENDINGS_CRLF); - - /* Configure UART. Note that REF_TICK/XTAL is used so that the baud rate remains - * correct while APB frequency is changing in light sleep mode. - */ -#if SOC_UART_SUPPORT_REF_TICK - uart_sclk_t clk_source = UART_SCLK_REF_TICK; - // REF_TICK clock can't provide a high baudrate - if (dev_config->baud_rate > 1 * 1000 * 1000) { - clk_source = UART_SCLK_DEFAULT; - ESP_LOGW(TAG, "light sleep UART wakeup might not work at the configured baud rate"); - } -#elif SOC_UART_SUPPORT_XTAL_CLK - uart_sclk_t clk_source = UART_SCLK_XTAL; -#else -#error "No UART clock source is aware of DFS" -#endif // SOC_UART_SUPPORT_xxx - const uart_config_t uart_config = { - .baud_rate = dev_config->baud_rate, - .data_bits = UART_DATA_8_BITS, - .parity = UART_PARITY_DISABLE, - .stop_bits = UART_STOP_BITS_1, - .source_clk = clk_source, - }; - - uart_param_config(dev_config->channel, &uart_config); - uart_set_pin(dev_config->channel, dev_config->tx_gpio_num, dev_config->rx_gpio_num, -1, -1); - - /* Install UART driver for interrupt-driven reads and writes */ - ret = uart_driver_install(dev_config->channel, 256, 0, 0, NULL, 0); + ret = uart_vfs_dev_port_init(dev_config, ESP_LINE_ENDINGS_CR, ESP_LINE_ENDINGS_CRLF); if (ret != ESP_OK) { goto _exit; } - /* Tell VFS to use UART driver */ - uart_vfs_dev_use_driver(dev_config->channel); - // initialize console, common part ret = esp_console_common_init(repl_config->max_cmdline_length, &uart_repl->repl_com); if (ret != ESP_OK) { @@ -278,7 +109,10 @@ esp_err_t esp_console_new_repl_uart(const esp_console_dev_uart_config_t *dev_con #endif // setup prompt - esp_console_setup_prompt(repl_config->prompt, &uart_repl->repl_com); + ret = esp_console_setup_prompt(repl_config->prompt, &uart_repl->repl_com); + if (ret != ESP_OK) { + goto _exit; + } #if CONFIG_LIBC_PICOLIBC // TODO IDF-14901 if (uart_repl->repl_com._stdin) { @@ -288,10 +122,20 @@ esp_err_t esp_console_new_repl_uart(const esp_console_dev_uart_config_t *dev_con } #endif + /* we have to make a copy of the dev_config because it will be used to init the + * uart driver in the repl task. We have no guarantee that dev_config will still + * be valid then */ + esp_console_dev_uart_config_t *copy_config = malloc(sizeof(esp_console_dev_uart_config_t)); + if (copy_config == NULL) { + ret = ESP_ERR_NO_MEM; + goto _exit; + } + memcpy(copy_config, dev_config, sizeof(esp_console_dev_uart_config_t)); + /* Fill the structure here as it will be used directly by the created task. */ - uart_repl->uart_channel = dev_config->channel; + uart_repl->dev_config = copy_config; uart_repl->repl_com.state = CONSOLE_REPL_STATE_INIT; - uart_repl->repl_com.repl_core.del = esp_console_repl_uart_delete; + uart_repl->repl_com.repl_core.del = esp_console_repl_uart_delete_legacy; /* Spawn a single thread to run REPL, we need to pass `uart_repl` to it as * it also requires the uart channel. */ @@ -306,7 +150,6 @@ esp_err_t esp_console_new_repl_uart(const esp_console_dev_uart_config_t *dev_con _exit: if (uart_repl) { esp_console_deinit(); - uart_driver_delete(dev_config->channel); free(uart_repl); } if (ret_repl) { @@ -314,83 +157,57 @@ _exit: } return ret; } -#endif // CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM -#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM -static esp_err_t esp_console_repl_uart_delete(esp_console_repl_t *repl) +esp_err_t esp_console_new_repl_uart(const esp_console_dev_uart_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl) { - esp_err_t ret = ESP_OK; - esp_console_repl_com_t *repl_com = __containerof(repl, esp_console_repl_com_t, repl_core); - esp_console_repl_universal_t *uart_repl = __containerof(repl_com, esp_console_repl_universal_t, repl_com); - // check if already de-initialized - if (repl_com->state == CONSOLE_REPL_STATE_DEINIT) { - ESP_LOGE(TAG, "already de-initialized"); - ret = ESP_ERR_INVALID_STATE; - goto _exit; - } - - ret = esp_console_common_deinit(&uart_repl->repl_com); - if (ret != ESP_OK) { - goto _exit; - } - - esp_console_deinit(); - uart_vfs_dev_use_nonblocking(uart_repl->uart_channel); - uart_driver_delete(uart_repl->uart_channel); - free(uart_repl); -_exit: - return ret; + return esp_console_new_repl_uart_legacy(dev_config, repl_config, ret_repl); } -#endif // CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM + +void esp_console_repl_uart_setup_std_stream(const esp_console_dev_uart_config_t *dev_config) +{ + const int uart_channel = dev_config->channel; + if (uart_channel != CONFIG_ESP_CONSOLE_UART_NUM) { + char path[CONSOLE_PATH_MAX_LEN] = { 0 }; + snprintf(path, CONSOLE_PATH_MAX_LEN, "/dev/uart/%d", uart_channel); + + FILE *temp_stdin = fopen(path, "r"); + FILE *temp_stdout = fopen(path, "w"); + if (temp_stdin == NULL || temp_stdout == NULL) { + ESP_LOGE(TAG, "Failed to open UART device file for channel %d", uart_channel); + if (temp_stdin) { + fclose(temp_stdin); + } + if (temp_stdout) { + fclose(temp_stdout); + } + } else { + stdin = temp_stdin; + stdout = temp_stdout; + stderr = stdout; + } + } +} +#endif // CONFIG_ESP_CONSOLE_UART + +#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG +esp_err_t esp_console_new_repl_usb_serial_jtag(const esp_console_dev_usb_serial_jtag_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl) +{ + (void)dev_config; + return esp_console_new_repl_stdio(repl_config, ret_repl); +} +#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG #if CONFIG_ESP_CONSOLE_USB_CDC -static esp_err_t esp_console_repl_usb_cdc_delete(esp_console_repl_t *repl) +esp_err_t esp_console_new_repl_usb_cdc(const esp_console_dev_usb_cdc_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl) { - esp_err_t ret = ESP_OK; - esp_console_repl_com_t *repl_com = __containerof(repl, esp_console_repl_com_t, repl_core); - esp_console_repl_universal_t *cdc_repl = __containerof(repl_com, esp_console_repl_universal_t, repl_com); - // check if already de-initialized - if (repl_com->state == CONSOLE_REPL_STATE_DEINIT) { - ESP_LOGE(TAG, "already de-initialized"); - ret = ESP_ERR_INVALID_STATE; - goto _exit; - } - - ret = esp_console_common_deinit(&cdc_repl->repl_com); - if (ret != ESP_OK) { - goto _exit; - } - - esp_console_deinit(); - free(cdc_repl); -_exit: - return ret; + (void)dev_config; + return esp_console_new_repl_stdio(repl_config, ret_repl); } #endif // CONFIG_ESP_CONSOLE_USB_CDC -#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG -static esp_err_t esp_console_repl_usb_serial_jtag_delete(esp_console_repl_t *repl) +void esp_console_setup_standard_stream(void *dev_config) { - esp_err_t ret = ESP_OK; - esp_console_repl_com_t *repl_com = __containerof(repl, esp_console_repl_com_t, repl_core); - esp_console_repl_universal_t *usb_serial_jtag_repl = __containerof(repl_com, esp_console_repl_universal_t, repl_com); - // check if already de-initialized - if (repl_com->state == CONSOLE_REPL_STATE_DEINIT) { - ESP_LOGE(TAG, "already de-initialized"); - ret = ESP_ERR_INVALID_STATE; - goto _exit; - } - - ret = esp_console_common_deinit(&usb_serial_jtag_repl->repl_com); - if (ret != ESP_OK) { - goto _exit; - } - - esp_console_deinit(); - usb_serial_jtag_vfs_use_nonblocking(); - usb_serial_jtag_driver_uninstall(); - free(usb_serial_jtag_repl); -_exit: - return ret; +#if CONFIG_ESP_CONSOLE_UART + esp_console_repl_uart_setup_std_stream((esp_console_dev_uart_config_t *)dev_config); +#endif // CONFIG_ESP_CONSOLE_UART } -#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG diff --git a/components/console/esp_console_repl_internal.c b/components/console/esp_console_repl_internal.c index fbfd29b4dc..b2c291d467 100644 --- a/components/console/esp_console_repl_internal.c +++ b/components/console/esp_console_repl_internal.c @@ -6,10 +6,8 @@ #include "sdkconfig.h" #include -#include // __containerof #include #include -#include "esp_console.h" #include "console_private.h" #include "esp_log.h" #include "linenoise/linenoise.h" diff --git a/components/console/esp_console_repl_linux.c b/components/console/esp_console_repl_linux.c deleted file mode 100644 index abc3fbbbdd..0000000000 --- a/components/console/esp_console_repl_linux.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sdkconfig.h" -#include "esp_log.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" - -#include "linenoise/linenoise.h" -#include "esp_console.h" -#include "console_private.h" - -static const char *TAG = "console.repl"; - -static struct termios s_orig_termios; - -/** - * This function restores the original terminal settings. - */ -static void disable_raw_mode(void) -{ - assert(tcsetattr(STDIN_FILENO, TCSAFLUSH, &s_orig_termios) == 0); -} - -/** - * Depending on if the input is a terminal or a file or pipe, we need to apply different - * settings to avoid additional processing or buffering getting into our way. - */ -static void prepare_input_stream(void) -{ - // Set stdin to unbuffered - setvbuf(stdin, NULL, _IONBF, 0); - - const int stdin_fileno = fileno(stdin); - - if (isatty(stdin_fileno)) { - // Use Termios driver to activate CR-NL translation and deactivate echo and canonical mode - assert(tcgetattr(stdin_fileno, &s_orig_termios) == 0); - struct termios raw = s_orig_termios; - raw.c_iflag |= ICRNL; // we translate to NL because linenoise expects NL - raw.c_lflag &= ~(ECHO | ICANON); // turn off echo and canonical mode - assert(tcsetattr(stdin_fileno, TCSAFLUSH, &raw) == 0); - - // Make sure user does not end up with a broken terminal - assert(atexit(disable_raw_mode) == 0); - } else { - // Flush input - assert(fflush(stdin) == 0); - } -} - -static esp_err_t esp_console_repl_linux_delete(esp_console_repl_t *repl) -{ - esp_err_t ret = ESP_OK; - esp_console_repl_com_t *repl_com = __containerof(repl, esp_console_repl_com_t, repl_core); - esp_console_repl_universal_t *linux_repl = __containerof(repl_com, esp_console_repl_universal_t, repl_com); - // check if already de-initialized - if (repl_com->state == CONSOLE_REPL_STATE_DEINIT) { - ESP_LOGE(TAG, "already de-initialized"); - ret = ESP_ERR_INVALID_STATE; - goto _exit; - } - repl_com->state = CONSOLE_REPL_STATE_DEINIT; - - ret = esp_console_common_deinit(&linux_repl->repl_com); - if (ret != ESP_OK) { - goto _exit; - } - - esp_console_deinit(); - - free(linux_repl); -_exit: - return ret; -} - -static esp_err_t esp_console_new_repl_linux(const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl) -{ - esp_console_repl_universal_t *linux_repl = NULL; - if (!repl_config || !ret_repl) { - return ESP_ERR_INVALID_ARG; - } - - esp_err_t ret = ESP_OK; - // allocate memory for console REPL context - linux_repl = calloc(1, sizeof(esp_console_repl_universal_t)); - if (!linux_repl) { - ret = ESP_ERR_NO_MEM; - goto _exit; - } - - /* Enable blocking mode on stdin and stdout */ - fcntl(fileno(stdout), F_SETFL, 0); - fcntl(fileno(stdin), F_SETFL, 0); - - // initialize console , common part - ret = esp_console_common_init(repl_config->max_cmdline_length, &linux_repl->repl_com); - if (ret != ESP_OK) { - goto _exit; - } - - // setup history - ret = esp_console_setup_history(repl_config->history_save_path, repl_config->max_history_len, &linux_repl->repl_com); - if (ret != ESP_OK) { - goto _exit; - } - - // Make sure the setup works on Linux without buffering or additional processing - prepare_input_stream(); - - // setup prompt - esp_console_setup_prompt(repl_config->prompt, &linux_repl->repl_com); - - /* Fill the structure here as it will be used directly by the created task. */ - linux_repl->uart_channel = CONFIG_ESP_CONSOLE_UART_NUM; - linux_repl->repl_com.state = CONSOLE_REPL_STATE_INIT; - linux_repl->repl_com.repl_core.del = esp_console_repl_linux_delete; - - /* spawn a single thread to run REPL */ - if (xTaskCreate(esp_console_repl_task, "console_repl", repl_config->task_stack_size, - linux_repl, repl_config->task_priority, &linux_repl->repl_com.task_hdl) != pdTRUE) { - ret = ESP_FAIL; - goto _exit; - } - - *ret_repl = &linux_repl->repl_com.repl_core; - return ESP_OK; -_exit: - if (linux_repl) { - esp_console_deinit(); - free(linux_repl); - } - if (ret_repl) { - *ret_repl = NULL; - } - return ret; -} - -#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM -esp_err_t esp_console_new_repl_uart(const esp_console_dev_uart_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl) -{ - if (!dev_config) { - return ESP_ERR_INVALID_ARG; - } - - return esp_console_new_repl_linux(repl_config, ret_repl); -} -#endif // CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM - -#if CONFIG_ESP_CONSOLE_USB_CDC -esp_err_t esp_console_new_repl_usb_cdc(const esp_console_dev_usb_cdc_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl) -{ - if (!dev_config) { - return ESP_ERR_INVALID_ARG; - } - - return esp_console_new_repl_linux(repl_config, ret_repl); -} -#endif // CONFIG_ESP_CONSOLE_USB_CDC - -#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG -esp_err_t esp_console_new_repl_usb_serial_jtag(const esp_console_dev_usb_serial_jtag_config_t *dev_config, const esp_console_repl_config_t *repl_config, esp_console_repl_t **ret_repl) -{ - if (!dev_config) { - return ESP_ERR_INVALID_ARG; - } - - return esp_console_new_repl_linux(repl_config, ret_repl); -} -#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG diff --git a/components/console/private_include/console_private.h b/components/console/private_include/console_private.h index 2e326fcb93..cf60d233c9 100644 --- a/components/console/private_include/console_private.h +++ b/components/console/private_include/console_private.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -45,9 +45,15 @@ typedef struct { typedef struct { esp_console_repl_com_t repl_com; // base class - int uart_channel; // uart channel number + void *dev_config; } esp_console_repl_universal_t; +_Static_assert(offsetof(esp_console_repl_com_t, repl_core) == 0, + "repl_core must be the first member of esp_console_repl_com_t"); + +_Static_assert(offsetof(esp_console_repl_universal_t, repl_com) == 0, + "repl_com must be the first member of esp_console_repl_universal_t"); + void esp_console_repl_task(void *args); esp_err_t esp_console_common_init(size_t max_cmdline_length, esp_console_repl_com_t *repl_com); @@ -57,3 +63,5 @@ esp_err_t esp_console_setup_prompt(const char *prompt, esp_console_repl_com_t *r esp_err_t esp_console_setup_history(const char *history_path, uint32_t max_history_len, esp_console_repl_com_t *repl_com); + +void esp_console_setup_standard_stream(void *dev_config); diff --git a/components/console/test_apps/console/main/test_console.c b/components/console/test_apps/console/main/test_console.c index 5aaf766ed9..e6d7103559 100644 --- a/components/console/test_apps/console/main/test_console.c +++ b/components/console/test_apps/console/main/test_console.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -89,10 +89,8 @@ TEST_CASE("esp console register with normal and context aware function set to NU TEST_CASE("esp console init function NULL param fails", "[console]") { esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); - esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_console_new_repl_uart(NULL, &repl_config, &s_repl)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_console_new_repl_uart(&uart_config, NULL, &s_repl)); - TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_console_new_repl_uart(&uart_config, &repl_config, NULL)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_console_new_repl_stdio(NULL, &s_repl)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, esp_console_new_repl_stdio(&repl_config, NULL)); } TEST_CASE("esp console init/deinit test", "[console]") @@ -161,8 +159,7 @@ TEST_CASE("esp console repl test", "[console][ignore]") TEST_ASSERT_NOT_NULL(s_test_console_mutex); esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); - esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); - TEST_ESP_OK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl)); + TEST_ESP_OK(esp_console_new_repl_stdio(&repl_config, &s_repl)); TEST_ESP_OK(esp_console_cmd_register(&s_quit_cmd)); @@ -195,8 +192,7 @@ TEST_CASE("esp console repl deinit", "[console][ignore]") set_leak_threshold(248); esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); - esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); - TEST_ESP_OK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl)); + TEST_ESP_OK(esp_console_new_repl_stdio(&repl_config, &s_repl)); /* start the repl task */ TEST_ESP_OK(esp_console_start_repl(s_repl)); @@ -227,11 +223,28 @@ static const esp_console_cmd_t cmd_z = { .func = do_hello_cmd, }; -TEST_CASE("esp console help command - sorted registration", "[console][ignore]") +/* To keep testing the old API esp_console_new_repl_uart, the following + * 2 test cases will initialize repl using this API when run on other target + * than linux and will use the new API on linux. */ +#if !CONFIG_IDF_TARGET_LINUX +static void test_console_repl_init(void) { esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); + /* use the old API to register the IO so we keep some coverage for it in the tests */ esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); TEST_ESP_OK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl)); +} +#else +static void test_console_repl_init(void) +{ + esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); + TEST_ESP_OK(esp_console_new_repl_stdio(&repl_config, &s_repl)); +} +#endif // !CONFIG_IDF_TARGET_LINUX + +TEST_CASE("esp console help command - sorted registration", "[console][ignore]") +{ + test_console_repl_init(); TEST_ESP_OK(esp_console_cmd_register(&cmd_a)); TEST_ESP_OK(esp_console_register_help_command()); @@ -248,9 +261,7 @@ TEST_CASE("esp console help command - sorted registration", "[console][ignore]") */ TEST_CASE("esp console help command - reverse registration", "[console][ignore]") { - esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); - esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); - TEST_ESP_OK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl)); + test_console_repl_init(); TEST_ESP_OK(esp_console_cmd_register(&cmd_z)); TEST_ESP_OK(esp_console_cmd_register(&s_quit_cmd)); @@ -336,8 +347,7 @@ TEST_CASE("esp console test with context", "[console]") TEST_CASE("esp console help command - set verbose level = 0", "[console][ignore]") { esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); - esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); - TEST_ESP_OK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl)); + TEST_ESP_OK(esp_console_new_repl_stdio(&repl_config, &s_repl)); TEST_ESP_OK(esp_console_register_help_command()); TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_console_set_help_verbose_level(ESP_CONSOLE_HELP_VERBOSE_LEVEL_MAX_NUM)); TEST_ESP_OK(esp_console_set_help_verbose_level(ESP_CONSOLE_HELP_VERBOSE_LEVEL_0)); @@ -348,8 +358,7 @@ TEST_CASE("esp console help command - set verbose level = 0", "[console][ignore] TEST_CASE("esp console help command - set verbose level = 1", "[console][ignore]") { esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); - esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); - TEST_ESP_OK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl)); + TEST_ESP_OK(esp_console_new_repl_stdio(&repl_config, &s_repl)); TEST_ESP_OK(esp_console_register_help_command()); TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_console_set_help_verbose_level(ESP_CONSOLE_HELP_VERBOSE_LEVEL_MAX_NUM)); TEST_ESP_OK(esp_console_set_help_verbose_level(ESP_CONSOLE_HELP_VERBOSE_LEVEL_1)); @@ -360,8 +369,7 @@ TEST_CASE("esp console help command - set verbose level = 1", "[console][ignore] TEST_CASE("esp console help command - --verbose sub command", "[console][ignore]") { esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); - esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); - TEST_ESP_OK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl)); + TEST_ESP_OK(esp_console_new_repl_stdio(&repl_config, &s_repl)); TEST_ESP_OK(esp_console_register_help_command()); TEST_ESP_OK(esp_console_start_repl(s_repl)); vTaskDelay(pdMS_TO_TICKS(5000)); @@ -370,8 +378,7 @@ TEST_CASE("esp console help command - --verbose sub command", "[console][ignore] TEST_CASE("esp console deregister commands", "[console][ignore]") { esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); - esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); - TEST_ESP_OK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl)); + TEST_ESP_OK(esp_console_new_repl_stdio(&repl_config, &s_repl)); TEST_ESP_OK(esp_console_cmd_register(&cmd_a)); TEST_ESP_OK(esp_console_cmd_register(&s_quit_cmd)); @@ -388,8 +395,7 @@ TEST_CASE("esp console deregister commands", "[console][ignore]") TEST_CASE("esp console re-register commands", "[console][ignore]") { esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); - esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); - TEST_ESP_OK(esp_console_new_repl_uart(&uart_config, &repl_config, &s_repl)); + TEST_ESP_OK(esp_console_new_repl_stdio(&repl_config, &s_repl)); TEST_ESP_OK(esp_console_cmd_register(&cmd_a)); TEST_ESP_OK(esp_console_cmd_register(&s_quit_cmd)); @@ -409,7 +415,6 @@ TEST_CASE("esp console re-register commands", "[console][ignore]") } #if !CONFIG_IDF_TARGET_LINUX - TEST_CASE("esp console repl custom_uart test", "[console][ignore]") { set_leak_threshold(248); diff --git a/components/esp_driver_uart/include/driver/esp_private/uart_vfs.h b/components/esp_driver_uart/include/driver/esp_private/uart_vfs.h index ced7196835..8898163285 100644 --- a/components/esp_driver_uart/include/driver/esp_private/uart_vfs.h +++ b/components/esp_driver_uart/include/driver/esp_private/uart_vfs.h @@ -1,12 +1,16 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #pragma once +#include "esp_vfs_common.h" #include "esp_vfs.h" +#include "esp_vfs_ops.h" +#include "esp_err.h" +#include "driver/uart_vfs.h" #ifdef __cplusplus extern "C" { @@ -22,6 +26,33 @@ extern "C" { */ const esp_vfs_fs_ops_t *esp_vfs_uart_get_vfs(void); +/** + * @brief Initialize the UART VFS console port. + * + * Registers a UART as a console device in the ESP-IDF virtual filesystem layer. + * Once initialized, system input and output streams such as `printf` and shell + * consoles will communicate through the configured UART channel. + * + * @param config Pointer to the UART VFS device configuration. + * @param rx_mode Line ending mode to apply when receiving data. + * @param tx_mode Line ending mode to apply when transmitting data. + * @return ESP_OK if initialization completes successfully, or an error code if it fails. + */ +esp_err_t uart_vfs_dev_port_init(const esp_console_dev_uart_config_t *config, + esp_line_endings_t rx_mode, + esp_line_endings_t tx_mode); + +/** + * @brief Deinitialize the UART VFS console port. + * + * Unregisters the UART from the VFS layer and restores driver state. Use this + * when the UART console is no longer required or when switching to another + * console backend. + * + * @param config Pointer to the UART VFS device configuration. + */ +void uart_vfs_dev_port_deinit(const esp_console_dev_uart_config_t *config); + #ifdef __cplusplus } #endif diff --git a/components/esp_driver_uart/include/driver/uart_vfs.h b/components/esp_driver_uart/include/driver/uart_vfs.h index 5510309aa6..710b0f24ea 100644 --- a/components/esp_driver_uart/include/driver/uart_vfs.h +++ b/components/esp_driver_uart/include/driver/uart_vfs.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,11 +7,26 @@ #pragma once #include "esp_vfs_common.h" +#include "esp_err.h" #ifdef __cplusplus extern "C" { #endif +/* TODO IDF-14810: Rename so esp_console does not appear in the name. */ +/** + * @brief UART VFS configuration structure. + * + * Defines the configuration parameters required to initialize and register + * a UART VFS driver instance. + */ +typedef struct esp_console_dev_uart_config { + int channel; //!< UART channel number (count from zero) + int baud_rate; //!< Communication baud rate + int tx_gpio_num; //!< GPIO number for TX path, -1 means using default one + int rx_gpio_num; //!< GPIO number for RX path, -1 means using default one +} esp_console_dev_uart_config_t; + /** * @brief Add /dev/uart virtual filesystem driver * diff --git a/components/esp_driver_uart/src/uart_vfs.c b/components/esp_driver_uart/src/uart_vfs.c index f733ee88c2..fa9221f2a5 100644 --- a/components/esp_driver_uart/src/uart_vfs.c +++ b/components/esp_driver_uart/src/uart_vfs.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,6 +14,7 @@ #include "sdkconfig.h" #include "esp_attr.h" #include "driver/uart_vfs.h" +#include "driver/esp_private/uart_vfs.h" #include "driver/uart.h" #include "driver/uart_select.h" #include "esp_rom_serial_output.h" @@ -1163,12 +1164,73 @@ void uart_vfs_dev_use_driver(int uart_num) } #if CONFIG_ESP_CONSOLE_UART +esp_err_t uart_vfs_dev_port_init(const esp_console_dev_uart_config_t *config, + esp_line_endings_t rx_mode, + esp_line_endings_t tx_mode) +{ + if (config == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (uart_vfs_dev_port_set_rx_line_endings(config->channel, rx_mode) == -1) { + return ESP_FAIL; + } + + if (uart_vfs_dev_port_set_tx_line_endings(config->channel, tx_mode) == -1) { + return ESP_FAIL; + } + + /* Configure UART. Note that REF_TICK/XTAL is used so that the baud rate remains + * correct while APB frequency is changing in light sleep mode. + */ +#if SOC_UART_SUPPORT_REF_TICK + uart_sclk_t clk_source = UART_SCLK_REF_TICK; + // REF_TICK clock can't provide a high baudrate + if (config->baud_rate > 1 * 1000 * 1000) { + clk_source = UART_SCLK_DEFAULT; + ESP_LOGW("uart_vfs", "light sleep UART wakeup might not work at the configured baud rate"); + } +#elif SOC_UART_SUPPORT_XTAL_CLK + uart_sclk_t clk_source = UART_SCLK_XTAL; +#else +#error "No UART clock source is aware of DFS" +#endif // SOC_UART_SUPPORT_xxx + const uart_config_t uart_driver_config = { + .baud_rate = config->baud_rate, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .source_clk = clk_source, + }; + + uart_param_config(config->channel, &uart_driver_config); + uart_set_pin(config->channel, config->tx_gpio_num, config->rx_gpio_num, -1, -1); + + /* Install UART driver for interrupt-driven reads and writes */ + const esp_err_t ret = uart_driver_install(config->channel, 256, 0, 0, NULL, 0); + if (ret != ESP_OK) { + uart_driver_delete(config->channel); + return ret; + } + + /* Tell VFS to use UART driver */ + uart_vfs_dev_use_driver(config->channel); + + return ESP_OK; +} + +void uart_vfs_dev_port_deinit(const esp_console_dev_uart_config_t *config) +{ + uart_vfs_dev_use_nonblocking(config->channel); + uart_driver_delete(config->channel); +} + ESP_SYSTEM_INIT_FN(init_vfs_uart, CORE, BIT(0), 110) { uart_vfs_dev_register(); return ESP_OK; } -#endif +#endif // CONFIG_ESP_CONSOLE_UART void uart_vfs_include_dev_init(void) { diff --git a/components/esp_driver_usb_serial_jtag/include/driver/esp_private/usb_serial_jtag_vfs.h b/components/esp_driver_usb_serial_jtag/include/driver/esp_private/usb_serial_jtag_vfs.h index 3c4c0f9ecd..3588c15860 100644 --- a/components/esp_driver_usb_serial_jtag/include/driver/esp_private/usb_serial_jtag_vfs.h +++ b/components/esp_driver_usb_serial_jtag/include/driver/esp_private/usb_serial_jtag_vfs.h @@ -1,12 +1,16 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #pragma once +#include "esp_err.h" #include "esp_vfs.h" +#include "esp_vfs_common.h" +#include "esp_vfs_ops.h" +#include "driver/usb_serial_jtag_vfs.h" #ifdef __cplusplus extern "C" { @@ -22,6 +26,34 @@ extern "C" { */ const esp_vfs_fs_ops_t *esp_vfs_usb_serial_jtag_get_vfs(void); +/** + * @brief Initialize the USB Serial JTAG VFS console port. + * + * Registers the USB Serial JTAG interface as a standard console I/O device + * through the ESP-IDF virtual filesystem. Once initialized, standard input + * and output streams (such as `printf` and interactive console REPL) will be + * routed through the USB Serial JTAG peripheral. + * + * @param config Pointer to the USB Serial JTAG VFS device configuration. + * @param rx_mode Line ending mode to apply to received data. + * @param tx_mode Line ending mode to apply to transmitted data. + * @return ESP_OK on successful initialization, or an error code if setup fails. + */ +esp_err_t usb_serial_jtag_vfs_dev_port_init(const esp_console_dev_usb_serial_jtag_config_t *config, + esp_line_endings_t rx_mode, + esp_line_endings_t tx_mode); + +/** + * @brief Deinitialize the USB Serial JTAG VFS console port. + * + * Unregisters the USB Serial JTAG VFS device and restores driver state. Call + * this when the console is no longer needed or when switching to a different + * console backend. + * + * @param config Pointer to the USB Serial JTAG VFS device configuration. + */ +void usb_serial_jtag_vfs_dev_port_deinit(const esp_console_dev_usb_serial_jtag_config_t *config); + #ifdef __cplusplus } #endif diff --git a/components/esp_driver_usb_serial_jtag/include/driver/usb_serial_jtag_vfs.h b/components/esp_driver_usb_serial_jtag/include/driver/usb_serial_jtag_vfs.h index c5a790ee4b..9173e323af 100644 --- a/components/esp_driver_usb_serial_jtag/include/driver/usb_serial_jtag_vfs.h +++ b/components/esp_driver_usb_serial_jtag/include/driver/usb_serial_jtag_vfs.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,6 +14,17 @@ extern "C" { #endif +/* TODO IDF-14810: Rename so esp_console does not appear in the name. */ +/** + * @brief Parameters for console device: USB-SERIAL-JTAG + * + * @note It's an empty structure for now, reserved for future + * + */ +typedef struct esp_console_dev_usb_serial_jtag_config { + +} esp_console_dev_usb_serial_jtag_config_t; + /** * @brief add /dev/usbserjtag virtual filesystem driver * diff --git a/components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_vfs.c b/components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_vfs.c index 3418b7a983..b53d9c86a2 100644 --- a/components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_vfs.c +++ b/components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_vfs.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -25,6 +25,7 @@ #include "driver/usb_serial_jtag_select.h" #include "driver/usb_serial_jtag_vfs.h" #include "driver/usb_serial_jtag.h" +#include "driver/esp_private/usb_serial_jtag_vfs.h" #include "esp_private/startup_internal.h" #include "esp_heap_caps.h" @@ -680,6 +681,36 @@ ESP_SYSTEM_INIT_FN(init_vfs_usj, CORE, BIT(0), 111) usb_serial_jtag_vfs_register(); return ESP_OK; } + +esp_err_t usb_serial_jtag_vfs_dev_port_init(const esp_console_dev_usb_serial_jtag_config_t *config, + esp_line_endings_t rx_mode, + esp_line_endings_t tx_mode) +{ + (void)config; + + usb_serial_jtag_vfs_set_rx_line_endings(rx_mode); + usb_serial_jtag_vfs_set_tx_line_endings(tx_mode); + + /* Install USB-SERIAL-JTAG driver for interrupt-driven reads and writes */ + usb_serial_jtag_driver_config_t usb_serial_jtag_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT(); + esp_err_t ret = usb_serial_jtag_driver_install(&usb_serial_jtag_config); + if (ret != ESP_OK) { + return ret; + } + + /* Tell vfs to use usb-serial-jtag driver */ + usb_serial_jtag_vfs_use_driver(); + + return ESP_OK; +} + +void usb_serial_jtag_vfs_dev_port_deinit(const esp_console_dev_usb_serial_jtag_config_t *config) +{ + (void)config; + usb_serial_jtag_vfs_use_nonblocking(); + usb_serial_jtag_driver_uninstall(); +} + #endif #if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG diff --git a/components/esp_stdio/CMakeLists.txt b/components/esp_stdio/CMakeLists.txt index 6f2b3f18e1..b929cc4e4a 100644 --- a/components/esp_stdio/CMakeLists.txt +++ b/components/esp_stdio/CMakeLists.txt @@ -3,17 +3,29 @@ idf_build_get_property(target IDF_TARGET) # Bootloader builds only needs it for config, not for anything else idf_build_get_property(non_os_build NON_OS_BUILD) -if(non_os_build OR ${target} STREQUAL "linux") +if(non_os_build) idf_component_register() return() endif() -set(srcs "stdio_vfs.c" - "stdio_simple.c" - "stdio_syscalls_simple.c") +set(srcs) +set(includes) + +if(${target} STREQUAL "linux") + list(APPEND srcs "stdio_port.c" + "linux/esp_stdio_linux.c") + list(APPEND includes "include" + "linux/include") +else() + list(APPEND srcs "stdio_port.c" + "stdio_vfs.c" + "stdio_simple.c" + "stdio_syscalls_simple.c") + list(APPEND includes "include") +endif() idf_component_register(SRCS ${srcs} - INCLUDE_DIRS include) + INCLUDE_DIRS ${includes}) if(CONFIG_VFS_SUPPORT_IO) if(IDF_BUILD_V2) diff --git a/components/esp_stdio/include/esp_stdio.h b/components/esp_stdio/include/esp_stdio.h index 935b16e021..c980fee707 100644 --- a/components/esp_stdio/include/esp_stdio.h +++ b/components/esp_stdio/include/esp_stdio.h @@ -1,12 +1,12 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ - #pragma once #include "esp_err.h" +#include "esp_stdio_cli_config.h" #ifdef __cplusplus extern "C" { @@ -21,6 +21,30 @@ extern "C" { */ esp_err_t esp_stdio_register(void); +/** + * @brief Install and enable the default stdio driver. + * + * Initializes the selected console backend and registers it as the active + * input and output stream source. After calling this function, standard I/O + * functions such as `printf` and terminal input will use the configured backend. + * + * Line endings are automatically configured for typical terminal use: + * - RX: CR (terminals send CR when Enter is pressed) + * - TX: CRLF (move cursor to beginning of next line on newline) + * + * @return ESP_OK if the driver is successfully installed, or an appropriate error code otherwise. + */ +esp_err_t esp_stdio_install_io_driver(void); + +/** + * @brief Uninstall the active stdio driver. + * + * Restores driver state and detaches the console backend from the standard I/O + * streams. Call this when shutting down or when replacing the current console + * driver with a different one. + */ +void esp_stdio_uninstall_io_driver(void); + #ifdef __cplusplus } #endif diff --git a/components/esp_stdio/include/esp_stdio_cli_config.h b/components/esp_stdio/include/esp_stdio_cli_config.h new file mode 100644 index 0000000000..224f82c4de --- /dev/null +++ b/components/esp_stdio/include/esp_stdio_cli_config.h @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sdkconfig.h" + +#if CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM +#include "soc/uart_pins.h" +#if CONFIG_ESP_CONSOLE_UART_CUSTOM +#define ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT() \ +{ \ + .channel = CONFIG_ESP_CONSOLE_UART_NUM, \ + .baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE, \ + .tx_gpio_num = (CONFIG_ESP_CONSOLE_UART_TX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_TX_GPIO : U0TXD_GPIO_NUM, \ + .rx_gpio_num = (CONFIG_ESP_CONSOLE_UART_RX_GPIO >= 0) ? CONFIG_ESP_CONSOLE_UART_RX_GPIO : U0RXD_GPIO_NUM, \ +} +#else +#define ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT() \ +{ \ + .channel = CONFIG_ESP_CONSOLE_UART_NUM, \ + .baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE, \ + .tx_gpio_num = -1, \ + .rx_gpio_num = -1, \ +} +#endif // CONFIG_ESP_CONSOLE_UART_CUSTOM +#endif // CONFIG_ESP_CONSOLE_UART_DEFAULT || CONFIG_ESP_CONSOLE_UART_CUSTOM + +#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || (defined __DOXYGEN__ && SOC_USB_SERIAL_JTAG_SUPPORTED) +#define ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT() {} +#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG || (defined __DOXYGEN__ && SOC_USB_SERIAL_JTAG_SUPPORTED) + +#if CONFIG_ESP_CONSOLE_USB_CDC || (defined __DOXYGEN__ && SOC_USB_OTG_SUPPORTED) +#define ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT() {} +#endif // CONFIG_ESP_CONSOLE_USB_CDC || (defined __DOXYGEN__ && SOC_USB_OTG_SUPPORTED) + +#if CONFIG_IDF_TARGET_LINUX +#define ESP_CONSOLE_DEV_LINUX_CONFIG_DEFAULT() {} +#endif // CONFIG_IDF_TARGET_LINUX + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_stdio/linux/esp_stdio_linux.c b/components/esp_stdio/linux/esp_stdio_linux.c new file mode 100644 index 0000000000..37993a8139 --- /dev/null +++ b/components/esp_stdio/linux/esp_stdio_linux.c @@ -0,0 +1,54 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "esp_stdio_linux.h" + +static struct termios s_orig_termios; + +/** + * This function restores the original terminal settings. + */ +static void disable_raw_mode(void) +{ + assert(tcsetattr(STDIN_FILENO, TCSAFLUSH, &s_orig_termios) == 0); +} + +void linux_vfs_dev_port_deinit(linux_port_config_t *config) +{ + (void)config; +} + +esp_err_t linux_vfs_dev_port_init(linux_port_config_t *config) +{ + (void)config; + + // Set stdin to unbuffered + setvbuf(stdin, NULL, _IONBF, 0); + + const int stdin_fileno = fileno(stdin); + + if (isatty(stdin_fileno)) { + // Use Termios driver to activate CR-NL translation and deactivate echo and canonical mode + assert(tcgetattr(stdin_fileno, &s_orig_termios) == 0); + struct termios raw = s_orig_termios; + raw.c_iflag |= ICRNL; // we translate to NL because linenoise expects NL + raw.c_lflag &= ~(ECHO | ICANON); // turn off echo and canonical mode + assert(tcsetattr(stdin_fileno, TCSAFLUSH, &raw) == 0); + + // Make sure user does not end up with a broken terminal + assert(atexit(disable_raw_mode) == 0); + } else { + // Flush input + assert(fflush(stdin) == 0); + } + + return ESP_OK; +} diff --git a/components/esp_stdio/linux/include/esp_stdio_linux.h b/components/esp_stdio/linux/include/esp_stdio_linux.h new file mode 100644 index 0000000000..356663f675 --- /dev/null +++ b/components/esp_stdio/linux/include/esp_stdio_linux.h @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Configuration for a Linux host-side console port. + * + * Represents the configuration used when running the console interface on a + * Linux host environment. This is typically used in host-based testing, + * emulation, or development workflows where the console backend is not a + * hardware UART or USB device. + */ +typedef struct linux_port_config { + +} linux_port_config_t; + +/** + * @brief Install the Linux console driver. + * + * Sets up the Linux console port as the active input/output backend for console + * operations. After installation, standard I/O (such as printf or terminal input) + * will pass through this Linux-based interface. + * + * @param config Pointer to the Linux console port configuration. + * @return ESP_OK if the driver was successfully installed, or an error otherwise. + */ +esp_err_t linux_vfs_dev_port_init(linux_port_config_t *config); + +/** + * @brief Uninstall the Linux console driver. + * + * Releases resources allocated during driver installation and restores state + * associated with the Linux console backend. Call this when shutting down or + * switching to another console interface. + * + * @param config Pointer to the Linux console port configuration. + */ +void linux_vfs_dev_port_deinit(linux_port_config_t *config); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_stdio/stdio_port.c b/components/esp_stdio/stdio_port.c new file mode 100644 index 0000000000..4b22f429b3 --- /dev/null +++ b/components/esp_stdio/stdio_port.c @@ -0,0 +1,79 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "sdkconfig.h" +#include "esp_stdio.h" + +#if CONFIG_IDF_TARGET_LINUX +#include "esp_stdio_linux.h" +#elif CONFIG_VFS_SUPPORT_IO // VFS required for all device drivers below + +#include "esp_vfs_common.h" + +#if CONFIG_ESP_CONSOLE_USB_CDC +#include "esp_vfs_cdcacm.h" +#include "esp_private/esp_vfs_cdcacm.h" +#endif // CONFIG_ESP_CONSOLE_USB_CDC + +#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED +#include "driver/esp_private/usb_serial_jtag_vfs.h" +#include "driver/usb_serial_jtag_vfs.h" +#endif // #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED + +#if CONFIG_ESP_CONSOLE_UART +#include "driver/esp_private/uart_vfs.h" +#include "driver/uart_vfs.h" +#endif // CONFIG_ESP_CONSOLE_UART + +#endif // CONFIG_VFS_SUPPORT_IO + +esp_err_t esp_stdio_install_io_driver(void) +{ + esp_err_t ret = ESP_FAIL; + +#if CONFIG_IDF_TARGET_LINUX + linux_port_config_t config = ESP_CONSOLE_DEV_LINUX_CONFIG_DEFAULT(); + ret = linux_vfs_dev_port_init(&config); +#elif CONFIG_VFS_SUPPORT_IO + + /* - set rx_mode to ESP_LINE_ENDINGS_CRLF as minicom, screen, idf_monitor + * send CR when ENTER key is pressed. + * - set tx_mode to move the caret to the beginning of the next line on '\n' */ + +#if CONFIG_ESP_CONSOLE_UART + esp_console_dev_uart_config_t config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); + ret = uart_vfs_dev_port_init(&config, ESP_LINE_ENDINGS_CR, ESP_LINE_ENDINGS_CRLF); +#elif CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG + esp_console_dev_usb_serial_jtag_config_t config = ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT(); + ret = usb_serial_jtag_vfs_dev_port_init(&config, ESP_LINE_ENDINGS_CR, ESP_LINE_ENDINGS_CRLF); +#elif CONFIG_ESP_CONSOLE_USB_CDC + esp_console_dev_usb_cdc_config_t config = ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT(); + ret = cdcacm_vfs_dev_port_init(&config, ESP_LINE_ENDINGS_CR, ESP_LINE_ENDINGS_CRLF); +#endif +#else + ret = ESP_ERR_NOT_SUPPORTED; +#endif + return ret; +} + +void esp_stdio_uninstall_io_driver(void) +{ +#if CONFIG_IDF_TARGET_LINUX + linux_port_config_t config = ESP_CONSOLE_DEV_LINUX_CONFIG_DEFAULT(); + linux_vfs_dev_port_deinit(&config); +#elif CONFIG_VFS_SUPPORT_IO +#if CONFIG_ESP_CONSOLE_UART + esp_console_dev_uart_config_t config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); + uart_vfs_dev_port_deinit(&config); +#elif CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG + esp_console_dev_usb_serial_jtag_config_t config = ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT(); + usb_serial_jtag_vfs_dev_port_deinit(&config); +#elif CONFIG_ESP_CONSOLE_USB_CDC + esp_console_dev_usb_cdc_config_t config = ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT(); + cdcacm_vfs_dev_port_deinit(&config); +#endif +#endif +} diff --git a/components/esp_stdio/stdio_vfs.c b/components/esp_stdio/stdio_vfs.c index 806c4f1d7b..28fd648758 100644 --- a/components/esp_stdio/stdio_vfs.c +++ b/components/esp_stdio/stdio_vfs.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,26 +10,26 @@ #include "esp_rom_sys.h" #include "esp_stdio.h" #include + #if CONFIG_VFS_SUPPORT_IO #if CONFIG_ESP_CONSOLE_USB_CDC #include "esp_vfs_cdcacm.h" -#include "esp_private/usb_console.h" -#endif -#if CONFIG_ESP_CONSOLE_USB_CDC #include "esp_private/esp_vfs_cdcacm.h" -#endif +#endif // CONFIG_ESP_CONSOLE_USB_CDC #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED #include "driver/esp_private/usb_serial_jtag_vfs.h" -#endif +#include "driver/usb_serial_jtag_vfs.h" +#endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED + #if CONFIG_ESP_CONSOLE_UART #include "driver/esp_private/uart_vfs.h" -#endif +#include "driver/uart_vfs.h" +#endif // CONFIG_ESP_CONSOLE_UART #include "esp_private/startup_internal.h" #include "esp_private/nullfs.h" -#endif #define STRINGIFY(s) STRINGIFY2(s) #define STRINGIFY2(s) #s @@ -47,8 +47,6 @@ typedef struct { int fd_secondary; } vfs_console_context_t; -#if CONFIG_VFS_SUPPORT_IO - // Secondary register part. #if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG const static esp_vfs_fs_ops_t *secondary_vfs = NULL; diff --git a/components/esp_usb_cdc_rom_console/include/esp_private/esp_vfs_cdcacm.h b/components/esp_usb_cdc_rom_console/include/esp_private/esp_vfs_cdcacm.h index 8c1f911a50..5435be1530 100644 --- a/components/esp_usb_cdc_rom_console/include/esp_private/esp_vfs_cdcacm.h +++ b/components/esp_usb_cdc_rom_console/include/esp_private/esp_vfs_cdcacm.h @@ -1,12 +1,15 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #pragma once -#include "esp_vfs.h" +#include "esp_err.h" +#include "esp_vfs_common.h" +#include "../esp_vfs_cdcacm.h" /* use relative path to make sure we include the correct header */ +#include "esp_vfs_ops.h" #ifdef __cplusplus extern "C" { @@ -22,6 +25,33 @@ extern "C" { */ const esp_vfs_fs_ops_t *esp_vfs_cdcacm_get_vfs(void); +/** + * @brief Initialize the USB CDC-ACM VFS console port. + * + * Registers the USB CDC-ACM interface as a console device in the ESP-IDF + * virtual filesystem layer. Once initialized, standard I/O functions such as + * `printf` and interactive console input will use the USB CDC-ACM channel. + * + * @param config Pointer to the USB CDC-ACM VFS device configuration. + * @param rx_mode Line ending mode to apply when receiving data. + * @param tx_mode Line ending mode to apply when transmitting data. + * @return ESP_OK if initialization completed successfully, or a related error code. + */ +esp_err_t cdcacm_vfs_dev_port_init(const esp_console_dev_usb_cdc_config_t *config, + esp_line_endings_t rx_mode, + esp_line_endings_t tx_mode); + +/** + * @brief Deinitialize the USB CDC-ACM VFS console port. + * + * Unregisters the USB CDC-ACM VFS device and releases resources allocated during + * initialization. Call this when the console is being shut down or replaced by + * another console backend. + * + * @param config Pointer to the USB CDC-ACM VFS device configuration. + */ +void cdcacm_vfs_dev_port_deinit(const esp_console_dev_usb_cdc_config_t *config); + #ifdef __cplusplus } #endif diff --git a/components/esp_usb_cdc_rom_console/include/esp_vfs_cdcacm.h b/components/esp_usb_cdc_rom_console/include/esp_vfs_cdcacm.h index 0601c45e52..0393829169 100644 --- a/components/esp_usb_cdc_rom_console/include/esp_vfs_cdcacm.h +++ b/components/esp_usb_cdc_rom_console/include/esp_vfs_cdcacm.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,6 +13,17 @@ extern "C" { #endif +/* TODO IDF-14810: Rename so esp_console does not appear in the name. */ +/** + * @brief Parameters for console device: USB CDC + * + * @note It's an empty structure for now, reserved for future + * + */ +typedef struct esp_console_dev_usb_cdc_config { + +} esp_console_dev_usb_cdc_config_t; + /** * @brief add /dev/cdcacm virtual filesystem driver * diff --git a/components/esp_usb_cdc_rom_console/vfs_cdcacm.c b/components/esp_usb_cdc_rom_console/vfs_cdcacm.c index c205b6f21f..eb6f55fc97 100644 --- a/components/esp_usb_cdc_rom_console/vfs_cdcacm.c +++ b/components/esp_usb_cdc_rom_console/vfs_cdcacm.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,6 +16,7 @@ #include "esp_attr.h" #include "sdkconfig.h" #include "esp_heap_caps.h" +#include "esp_private/esp_vfs_cdcacm.h" #include "esp_private/esp_vfs_cdcacm_select.h" #include "esp_private/usb_console.h" #include "esp_private/startup_internal.h" @@ -533,6 +534,24 @@ ESP_SYSTEM_INIT_FN(init_vfs_usb_cdc_rom_console, CORE, BIT(0), 113) return esp_vfs_dev_cdcacm_register(); } + +esp_err_t cdcacm_vfs_dev_port_init(const esp_console_dev_usb_cdc_config_t *config, + esp_line_endings_t rx_mode, + esp_line_endings_t tx_mode) +{ + (void)config; + + esp_vfs_dev_cdcacm_set_rx_line_endings(rx_mode); + esp_vfs_dev_cdcacm_set_tx_line_endings(tx_mode); + + return ESP_OK; +} + +void cdcacm_vfs_dev_port_deinit(const esp_console_dev_usb_cdc_config_t *config) +{ + (void)config; +} + #endif void esp_vfs_dev_cdcacm_include_dev_init(void) diff --git a/docs/en/api-reference/system/console.rst b/docs/en/api-reference/system/console.rst index fc191f91d2..52ea9cbc1e 100644 --- a/docs/en/api-reference/system/console.rst +++ b/docs/en/api-reference/system/console.rst @@ -185,13 +185,10 @@ Initialize Console REPL Environment To establish a basic REPL environment, ``console`` component provides several useful APIs, combining those functions described above. -In a typical application, you only need to call :cpp:func:`esp_console_new_repl_uart` to initialize the REPL environment based on UART device, including driver install, basic console configuration, spawning a thread to do REPL task and register several useful commands (e.g., `help`). +In a typical application, you only need to call :cpp:func:`esp_console_new_repl_stdio` to initialize the REPL environment, including driver install, basic console configuration, spawning a thread to do REPL task and register several useful commands (e.g., `help`). After that, you can register your own commands with :cpp:func:`esp_console_cmd_register`. The REPL environment keeps in init state until you call :cpp:func:`esp_console_start_repl`. -.. only:: SOC_USB_SERIAL_JTAG_SUPPORTED - - Likewise, if your REPL environment is based on USB_SERIAL_JTAG device, you only need to call :cpp:func:`esp_console_new_repl_usb_serial_jtag` at first step. Then call other functions as usual. Application Examples -------------------- diff --git a/examples/system/console/basic/main/console_example_main.c b/examples/system/console/basic/main/console_example_main.c index 3ab666bff5..6e5bc57d7d 100644 --- a/examples/system/console/basic/main/console_example_main.c +++ b/examples/system/console/basic/main/console_example_main.c @@ -102,21 +102,6 @@ void app_main(void) #endif register_nvs(); -#if defined(CONFIG_ESP_CONSOLE_UART_DEFAULT) || defined(CONFIG_ESP_CONSOLE_UART_CUSTOM) - esp_console_dev_uart_config_t hw_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl)); - -#elif defined(CONFIG_ESP_CONSOLE_USB_CDC) - esp_console_dev_usb_cdc_config_t hw_config = ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_console_new_repl_usb_cdc(&hw_config, &repl_config, &repl)); - -#elif defined(CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG) - esp_console_dev_usb_serial_jtag_config_t hw_config = ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_console_new_repl_usb_serial_jtag(&hw_config, &repl_config, &repl)); - -#else -#error Unsupported console type -#endif - + ESP_ERROR_CHECK(esp_console_new_repl_stdio(&repl_config, &repl)); ESP_ERROR_CHECK(esp_console_start_repl(repl)); }