feat(esp_stdio): add custom IO registration

This commit is contained in:
Guillaume Souchere
2026-09-09 09:00:45 +02:00
parent 4cf9d611e6
commit 654db440cb
30 changed files with 1093 additions and 220 deletions
+104 -4
View File
@@ -7,6 +7,7 @@
#include "esp_err.h"
#include "esp_stdio_cli_config.h"
#include "esp_vfs_common.h"
#ifdef __cplusplus
extern "C" {
@@ -14,15 +15,107 @@ extern "C" {
#define ESP_VFS_DEV_CONSOLE "/dev/console"
#if CONFIG_VFS_SUPPORT_IO
#include "esp_vfs_common.h"
#include "esp_vfs_ops.h"
/**
* @brief add uart/usb_serial_jtag/usb_otg_acmcdc virtual filesystem driver
* @brief Configuration for registering a console I/O backend in the mux.
*
* This function is called from startup code to enable serial output
* The caller must keep the ops table, context, and path string valid until
* esp_stdio_unregister_io() is called.
*/
typedef struct {
const esp_vfs_fs_ops_t *vfs_ops; /*!< VFS operations table (must not be NULL) */
void *vfs_ctx; /*!< Context pointer forwarded to every VFS callback */
const char *path; /*!< Path used for open() inside the driver (e.g. "/0", "/") */
} esp_stdio_io_config_t;
/** @brief Opaque handle representing a registered console I/O backend. */
typedef struct esp_stdio_entry *esp_stdio_handle_t;
/**
* @brief Register the default console VFS backends from Kconfig.
*
* Called from startup code. Sets up the primary and any Kconfig-selected
* auxiliary sinks, and mounts them under /dev/console.
*/
esp_err_t esp_stdio_register(void);
/**
* @brief Install and enable the default stdio driver.
* @brief Register a VFS backend as a write-only auxiliary sink in the console mux.
*
* The backend starts receiving fan-out writes immediately (once /dev/console
* is open). To make it the active read+write primary, pass the returned
* handle to esp_stdio_push_primary().
*
* The caller is responsible for initialising the driver before calling this
* function. The ops table, context, and path string must remain valid until
* esp_stdio_unregister_io() is called.
*
* @param config Pointer to the I/O configuration (must not be NULL; vfs_ops
* and path must not be NULL).
* @param out_handle Receives the opaque handle on success (must not be NULL).
* @return ESP_OK on success
* ESP_ERR_INVALID_ARG if any required pointer is NULL
* ESP_ERR_NO_MEM if the entry pool is exhausted
*/
esp_err_t esp_stdio_register_io(const esp_stdio_io_config_t *config,
esp_stdio_handle_t *out_handle);
/**
* @brief Push an existing handle onto the primary stack.
*
* Removes the entry from the auxiliary list and makes it the active
* read+write backend. The previous primary is suspended on a stack and
* resumes when esp_stdio_pop_primary() is called.
*
* @param handle A valid handle returned by esp_stdio_register_io().
* @return ESP_OK on success
* ESP_ERR_INVALID_ARG if handle is NULL or not currently registered as auxiliary
* ESP_ERR_NO_MEM if the primary stack is full
*/
esp_err_t esp_stdio_push_primary(esp_stdio_handle_t handle);
/**
* @brief Remove a primary from the stack.
*
* The removed entry is returned to the auxiliary list. When it was the active
* (top) primary, the next-most-recent primary becomes active; the
* system-registered primary at the base of the stack always remains as the
* ultimate fallback and can never be removed.
*
* @param handle The primary to remove. Pass NULL to remove the current active
* (top) primary. Pass a specific handle to remove it from
* wherever it sits in the stack (owner-keyed removal, which lets
* independent owners release their own primary without disturbing
* a more-recently-pushed one).
* @return ESP_OK on success
* ESP_ERR_INVALID_STATE if there is no user-pushed primary to remove
* (NULL), or if @p handle is not currently on the
* primary stack
*/
esp_err_t esp_stdio_pop_primary(esp_stdio_handle_t handle);
/**
* @brief Unregister a backend from the console mux entirely.
*
* The handle's backend fd is closed if it was open. If the handle is
* currently the active primary it is implicitly popped first (the previous
* primary is restored). If it is buried in the primary stack it is removed
* from wherever it sits. After this call the handle is invalid.
*
* @param handle A valid handle returned by esp_stdio_register_io().
* @return ESP_OK on success
* ESP_ERR_INVALID_ARG if handle is NULL
*/
esp_err_t esp_stdio_unregister_io(esp_stdio_handle_t handle);
#endif // CONFIG_VFS_SUPPORT_IO
/**
* @brief Install and enable the 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
@@ -32,6 +125,10 @@ esp_err_t esp_stdio_register(void);
* - RX: CR (terminals send CR when Enter is pressed)
* - TX: CRLF (move cursor to beginning of next line on newline)
*
* If a user has taken over the primary console with esp_stdio_push_primary(),
* this function is a no-op (the user backend is assumed to be already
* initialised by the caller).
*
* @return ESP_OK if the driver is successfully installed, or an appropriate error code otherwise.
*/
esp_err_t esp_stdio_install_io_driver(void);
@@ -42,8 +139,11 @@ esp_err_t esp_stdio_install_io_driver(void);
* 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.
*
* If a user has taken over the primary console with esp_stdio_push_primary(),
* this function is a no-op.
*/
void esp_stdio_uninstall_io_driver(void);
esp_err_t esp_stdio_uninstall_io_driver(void);
#ifdef __cplusplus
}