mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
feat(esp_stdio): Revert breaking change
- also revert the modification in the singature of uninstall function in all the concerned esp_driver* components and esp_stdio. - added proper documentation of the feature in the programming guide
This commit is contained in:
@@ -50,9 +50,8 @@ esp_err_t uart_vfs_dev_port_init(const esp_console_dev_uart_config_t *config,
|
||||
* console backend.
|
||||
*
|
||||
* @param config Pointer to the UART VFS device configuration.
|
||||
* @return ESP_OK if deinitialization completes successfully, or an error code if it fails.
|
||||
*/
|
||||
esp_err_t uart_vfs_dev_port_deinit(const esp_console_dev_uart_config_t *config);
|
||||
void uart_vfs_dev_port_deinit(const esp_console_dev_uart_config_t *config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1163,7 +1163,6 @@ 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)
|
||||
@@ -1219,11 +1218,10 @@ esp_err_t uart_vfs_dev_port_init(const esp_console_dev_uart_config_t *config,
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t uart_vfs_dev_port_deinit(const esp_console_dev_uart_config_t *config)
|
||||
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);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
ESP_SYSTEM_INIT_FN(init_vfs_uart, CORE, BIT(0), 110)
|
||||
|
||||
+1
-2
@@ -51,9 +51,8 @@ esp_err_t usb_serial_jtag_vfs_dev_port_init(const esp_console_dev_usb_serial_jta
|
||||
* console backend.
|
||||
*
|
||||
* @param config Pointer to the USB Serial JTAG VFS device configuration.
|
||||
* @return ESP_OK if the driver was successfully uninstalled, or an error otherwise.
|
||||
*/
|
||||
esp_err_t usb_serial_jtag_vfs_dev_port_deinit(const esp_console_dev_usb_serial_jtag_config_t *config);
|
||||
void usb_serial_jtag_vfs_dev_port_deinit(const esp_console_dev_usb_serial_jtag_config_t *config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -704,12 +704,11 @@ esp_err_t usb_serial_jtag_vfs_dev_port_init(const esp_console_dev_usb_serial_jta
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t usb_serial_jtag_vfs_dev_port_deinit(const esp_console_dev_usb_serial_jtag_config_t *config)
|
||||
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();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,6 +14,18 @@ extern "C" {
|
||||
|
||||
#define ESP_VFS_DEV_CONSOLE "/dev/console"
|
||||
|
||||
/**
|
||||
* @brief Register the default console VFS backend(s) selected in Kconfig.
|
||||
*
|
||||
* Sets up the primary (and any Kconfig-selected secondary) console sink and
|
||||
* mounts them under /dev/console. This function is automatically called from
|
||||
* startup code to enable serial output; applications normally do not need to
|
||||
* call it themselves.
|
||||
*
|
||||
* @return ESP_OK on success, or an error code from the underlying VFS registration.
|
||||
*/
|
||||
esp_err_t esp_stdio_register(void);
|
||||
|
||||
#if CONFIG_VFS_SUPPORT_IO
|
||||
|
||||
#include "esp_vfs_common.h"
|
||||
@@ -134,7 +146,7 @@ esp_err_t esp_stdio_install_io_driver(void);
|
||||
* If a user has taken over the primary console with esp_stdio_push_primary(),
|
||||
* this function is a no-op.
|
||||
*/
|
||||
esp_err_t esp_stdio_uninstall_io_driver(void);
|
||||
void esp_stdio_uninstall_io_driver(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -21,10 +21,9 @@ static void disable_raw_mode(void)
|
||||
assert(tcsetattr(STDIN_FILENO, TCSAFLUSH, &s_orig_termios) == 0);
|
||||
}
|
||||
|
||||
esp_err_t linux_vfs_dev_port_deinit(linux_port_config_t *config)
|
||||
void linux_vfs_dev_port_deinit(linux_port_config_t *config)
|
||||
{
|
||||
(void)config;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t linux_vfs_dev_port_init(linux_port_config_t *config)
|
||||
|
||||
@@ -43,9 +43,8 @@ esp_err_t linux_vfs_dev_port_init(linux_port_config_t *config);
|
||||
* switching to another console interface.
|
||||
*
|
||||
* @param config Pointer to the Linux console port configuration.
|
||||
* @return ESP_OK if the driver was successfully uninstalled, or an error otherwise.
|
||||
*/
|
||||
esp_err_t linux_vfs_dev_port_deinit(linux_port_config_t *config);
|
||||
void linux_vfs_dev_port_deinit(linux_port_config_t *config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -73,30 +73,28 @@ esp_err_t esp_stdio_install_io_driver(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_stdio_uninstall_io_driver(void)
|
||||
void esp_stdio_uninstall_io_driver(void)
|
||||
{
|
||||
#if CONFIG_VFS_SUPPORT_IO
|
||||
/* If a user primary is registered, deinit is the caller's responsibility. */
|
||||
if (esp_stdio_has_user_primary()) {
|
||||
return ESP_OK;
|
||||
return;
|
||||
}
|
||||
#endif // CONFIG_VFS_SUPPORT_IO
|
||||
|
||||
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_deinit(&config);
|
||||
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();
|
||||
ret = uart_vfs_dev_port_deinit(&config);
|
||||
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();
|
||||
ret = usb_serial_jtag_vfs_dev_port_deinit(&config);
|
||||
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();
|
||||
ret = cdcacm_vfs_dev_port_deinit(&config);
|
||||
cdcacm_vfs_dev_port_deinit(&config);
|
||||
#endif
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -358,7 +358,7 @@ static const esp_vfs_fs_ops_t s_vfs_console = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static esp_err_t esp_stdio_register(void)
|
||||
esp_err_t esp_stdio_register(void)
|
||||
{
|
||||
_lock_init(&s_lock);
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ TEST_CASE("install/uninstall are no-ops with user primary", "[esp_stdio]")
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_stdio_push_primary(h));
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_stdio_install_io_driver());
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_stdio_uninstall_io_driver());
|
||||
esp_stdio_uninstall_io_driver();
|
||||
TEST_ASSERT_EQUAL(0, mock_a.open_count); /* driver not initialised by install */
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_stdio_pop_primary(NULL));
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ from pytest_embedded_idf.utils import soc_filtered_targets
|
||||
)
|
||||
def test_esp_system(dut: Dut) -> None:
|
||||
# esp32p4 32MB PSRAM initialize in startup takes more than 30 sec
|
||||
dut.run_all_single_board_cases(timeout=120)
|
||||
dut.run_all_single_board_cases(timeout=60)
|
||||
|
||||
|
||||
def esp_reset_and_wait_ready(dut: Dut) -> None:
|
||||
|
||||
@@ -49,9 +49,8 @@ esp_err_t cdcacm_vfs_dev_port_init(const esp_console_dev_usb_cdc_config_t *confi
|
||||
* another console backend.
|
||||
*
|
||||
* @param config Pointer to the USB CDC-ACM VFS device configuration.
|
||||
* @return ESP_OK if deinitialization completes successfully, or an error code if it fails.
|
||||
*/
|
||||
esp_err_t cdcacm_vfs_dev_port_deinit(const esp_console_dev_usb_cdc_config_t *config);
|
||||
void cdcacm_vfs_dev_port_deinit(const esp_console_dev_usb_cdc_config_t *config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -547,10 +547,9 @@ esp_err_t cdcacm_vfs_dev_port_init(const esp_console_dev_usb_cdc_config_t *confi
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t cdcacm_vfs_dev_port_deinit(const esp_console_dev_usb_cdc_config_t *config)
|
||||
void cdcacm_vfs_dev_port_deinit(const esp_console_dev_usb_cdc_config_t *config)
|
||||
{
|
||||
(void)config;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
CONFIG_IDF_TARGET="linux"
|
||||
CONFIG_VFS_SUPPORT_IO=y
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=n
|
||||
CONFIG_WL_SECTOR_SIZE=4096
|
||||
|
||||
@@ -170,3 +170,72 @@ Once you have created a custom VFS driver, use :cpp:func:`esp_vfs_register_fs()`
|
||||
stderr = f;
|
||||
|
||||
Note that logging functions (``ESP_LOGE()``, etc.) write their output to ``stdout``. Keep this in mind when using logging within the implementation of your custom VFS (or any components which it calls). For example, if the custom VFS driver's ``write()`` operation fails and uses ``ESP_LOGE()`` to log the error, this will cause the output to be sent to ``stdout``, which would again call the custom VFS driver's ``write()`` operation. This would result in an infinite loop. It is recommended to keep track of this re-entry condition in the VFS driver's ``write()`` implementation, and return immediately if the write operation is still in progress.
|
||||
|
||||
Console I/O multiplexer
|
||||
-----------------------
|
||||
|
||||
In addition to redirecting ``stdout`` and ``stderr`` manually (as shown above), ESP-IDF provides a console I/O multiplexer that lets an application register one or more custom VFS backends and switch between them at runtime, without reassigning the ``stdin``/``stdout``/``stderr`` streams itself.
|
||||
|
||||
The multiplexer is exposed on ``/dev/console`` and manages backends in two roles:
|
||||
|
||||
- **Primary** — the active read and write backend. Both application input (``stdin``) and output (``stdout``/``stderr``) go through it. Primaries are kept on a stack: pushing a new primary suspends the previous one, and popping restores it. The default console selected in Kconfig sits at the base of the stack and is always kept as the ultimate fallback.
|
||||
- **Auxiliary** — a write-only sink. Every byte written to ``stdout`` and ``stderr`` is fanned out to all registered auxiliaries in addition to the primary. This is useful, for example, to mirror console output to a log file or a network connection while keeping the interactive console on the physical interface.
|
||||
|
||||
The maximum number of backends that can be registered at once (primaries plus auxiliaries, including the Kconfig default) is set by :ref:`CONFIG_ESP_STDIO_MAX_VFS_ENTRIES`.
|
||||
|
||||
API overview
|
||||
^^^^^^^^^^^^
|
||||
|
||||
The multiplexer API is declared in ``esp_stdio.h`` and is available when ``CONFIG_VFS_SUPPORT_IO`` is enabled:
|
||||
|
||||
- ``esp_stdio_register_io()`` — register a VFS backend as a write-only auxiliary and return an opaque handle. The backend starts receiving the write fan-out immediately.
|
||||
- ``esp_stdio_push_primary()`` — promote a previously registered handle to the active read and write primary. The current primary is suspended on the stack.
|
||||
- ``esp_stdio_pop_primary()`` — remove a primary from the stack, returning it to auxiliary status. Passing ``NULL`` removes the current (top) primary; passing a specific handle removes it from wherever it sits in the stack.
|
||||
- ``esp_stdio_unregister_io()`` — remove a backend from the multiplexer entirely. If it is currently the active primary, it is popped first.
|
||||
|
||||
The backend is described by an ``esp_stdio_io_config_t`` structure:
|
||||
|
||||
.. list::
|
||||
|
||||
- ``vfs_ops`` — pointer to the VFS operations table (must not be ``NULL``).
|
||||
- ``vfs_ctx`` — context pointer forwarded to every VFS callback.
|
||||
- ``path`` — path passed to ``open()`` inside the driver, for example ``"/"``.
|
||||
|
||||
.. note::
|
||||
|
||||
The ops table, context, and path string passed in the configuration must remain valid until the backend is removed with ``esp_stdio_unregister_io()``.
|
||||
|
||||
Example
|
||||
^^^^^^^
|
||||
|
||||
The following example registers a custom backend, makes it the active console, and later restores the previous one:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include "esp_stdio.h"
|
||||
|
||||
// A VFS operations table implemented by the application.
|
||||
extern const esp_vfs_fs_ops_t my_console_vfs;
|
||||
|
||||
esp_stdio_handle_t handle;
|
||||
esp_stdio_io_config_t config = {
|
||||
.vfs_ops = &my_console_vfs,
|
||||
.vfs_ctx = NULL,
|
||||
.path = "/",
|
||||
};
|
||||
|
||||
// Register as a write-only auxiliary: output is now mirrored to it.
|
||||
ESP_ERROR_CHECK(esp_stdio_register_io(&config, &handle));
|
||||
|
||||
// Promote it to the active read + write primary.
|
||||
ESP_ERROR_CHECK(esp_stdio_push_primary(handle));
|
||||
|
||||
// ... application uses the custom console for stdin/stdout/stderr ...
|
||||
|
||||
// Restore the previous primary (the custom backend reverts to auxiliary).
|
||||
ESP_ERROR_CHECK(esp_stdio_pop_primary(handle));
|
||||
|
||||
// Remove the backend from the multiplexer entirely.
|
||||
ESP_ERROR_CHECK(esp_stdio_unregister_io(handle));
|
||||
|
||||
The same re-entrancy caveat described above for logging applies here: the backend's ``write()`` implementation must not trigger logging that would recursively write to the console.
|
||||
|
||||
Reference in New Issue
Block a user