mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-18 11:33:17 +03:00
158 lines
4.3 KiB
C
158 lines
4.3 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/*
|
|
* Public mux API: register_driver / unregister_driver / set_primary /
|
|
* has_user_primary.
|
|
*
|
|
* This file is intentionally separate from stdio_vfs.c so that it is only
|
|
* linked into a binary when the application actually calls one of these
|
|
* functions. stdio_vfs.c is always pulled in (via the
|
|
* -u esp_vfs_include_console_register linker hook) but this file is not, so
|
|
* apps that never use the custom-IO API do not pay for the code here.
|
|
*/
|
|
|
|
#include "sdkconfig.h"
|
|
|
|
#if CONFIG_VFS_SUPPORT_IO
|
|
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include "esp_err.h"
|
|
#include "esp_log.h"
|
|
#include "esp_stdio.h"
|
|
#include "esp_stdio_private.h"
|
|
|
|
static const char *TAG = "esp_stdio";
|
|
|
|
bool esp_stdio_has_user_primary(void)
|
|
{
|
|
/* A user primary is active exactly when it differs from the Kconfig
|
|
* default that esp_stdio_register() installed at boot. */
|
|
return s_active_primary != s_default_primary;
|
|
}
|
|
|
|
/* Allocate a free pool slot for a new driver. Caller must hold s_lock.
|
|
* The entry is created with no open fd; opening is left to the caller. */
|
|
static esp_err_t alloc_entry(const esp_stdio_driver_config_t *config, esp_stdio_entry_t **out)
|
|
{
|
|
for (int i = 0; i < STDIO_MAX_ENTRIES; i++) {
|
|
if (!s_entry_pool[i].in_use) {
|
|
s_entry_pool[i] = (esp_stdio_entry_t) {
|
|
.ops = config->vfs_ops,
|
|
.vfs_ctx = config->vfs_ctx,
|
|
.path = config->path,
|
|
.fd = -1,
|
|
.in_use = true,
|
|
};
|
|
*out = &s_entry_pool[i];
|
|
return ESP_OK;
|
|
}
|
|
}
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
|
|
esp_err_t esp_stdio_register_driver(const esp_stdio_driver_config_t *config,
|
|
esp_stdio_handle_t *out_handle)
|
|
{
|
|
if (!config || !config->vfs_ops || !config->path || !out_handle) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
_lock_acquire(&s_lock);
|
|
|
|
esp_stdio_entry_t *e = NULL;
|
|
esp_err_t err = alloc_entry(config, &e);
|
|
if (err != ESP_OK) {
|
|
_lock_release(&s_lock);
|
|
ESP_EARLY_LOGE(TAG, "No space left for stdio registration");
|
|
return err;
|
|
}
|
|
|
|
/* A freshly registered driver is an auxiliary. If /dev/console is already
|
|
* open, open it now (write-only) so it starts receiving the write/fsync
|
|
* fan-out. */
|
|
if (s_open_count > 0) {
|
|
entry_open(e, O_WRONLY);
|
|
}
|
|
|
|
_lock_release(&s_lock);
|
|
*out_handle = e;
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t esp_stdio_set_primary_driver(esp_stdio_handle_t handle,
|
|
esp_stdio_handle_t *out_old_primary)
|
|
{
|
|
if (!handle) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
_lock_acquire(&s_lock);
|
|
|
|
if (!handle->in_use) {
|
|
_lock_release(&s_lock);
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
esp_stdio_entry_t *old = s_active_primary;
|
|
|
|
/* Promote: if the console is open, ensure the new primary has a read+write
|
|
* fd. An entry that was serving as an auxiliary only has an O_WRONLY fd,
|
|
* which would make reads fail, so it is re-opened O_RDWR. The old primary
|
|
* stays registered and drops back to a write-only auxiliary sink. */
|
|
if (handle != old && s_open_count > 0) {
|
|
if (handle->fd >= 0) {
|
|
entry_close(handle);
|
|
}
|
|
entry_open(handle, O_RDWR);
|
|
}
|
|
s_active_primary = handle;
|
|
|
|
_lock_release(&s_lock);
|
|
|
|
if (out_old_primary) {
|
|
*out_old_primary = old;
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t esp_stdio_unregister_driver(esp_stdio_handle_t handle)
|
|
{
|
|
if (!handle) {
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
_lock_acquire(&s_lock);
|
|
|
|
if (!handle->in_use) {
|
|
_lock_release(&s_lock);
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
/* Only auxiliaries can be removed; the active primary must first be
|
|
* replaced with esp_stdio_set_primary_driver() before it can be
|
|
* unregistered. */
|
|
if (handle == s_active_primary) {
|
|
_lock_release(&s_lock);
|
|
return ESP_ERR_INVALID_STATE;
|
|
}
|
|
|
|
entry_close(handle);
|
|
handle->in_use = false;
|
|
handle->ops = NULL;
|
|
|
|
/* Keep the has-user-primary anchor valid if the boot console is removed. */
|
|
if (handle == s_default_primary) {
|
|
s_default_primary = NULL;
|
|
}
|
|
|
|
_lock_release(&s_lock);
|
|
return ESP_OK;
|
|
}
|
|
|
|
#endif // CONFIG_VFS_SUPPORT_IO
|