mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
feat(storage/stdio): Refactor stdio_vfs multiplexer
This commit is contained in:
@@ -7,7 +7,6 @@ if(non_os_build)
|
|||||||
idf_component_register()
|
idf_component_register()
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(srcs)
|
set(srcs)
|
||||||
set(includes)
|
set(includes)
|
||||||
|
|
||||||
@@ -18,10 +17,13 @@ if(${target} STREQUAL "linux")
|
|||||||
"linux/include")
|
"linux/include")
|
||||||
else()
|
else()
|
||||||
list(APPEND srcs "stdio_port.c"
|
list(APPEND srcs "stdio_port.c"
|
||||||
"stdio_vfs.c"
|
|
||||||
"stdio_simple.c"
|
"stdio_simple.c"
|
||||||
"stdio_syscalls_simple.c")
|
"stdio_syscalls_simple.c")
|
||||||
list(APPEND includes "include")
|
list(APPEND includes "include")
|
||||||
|
|
||||||
|
if(CONFIG_VFS_SUPPORT_IO)
|
||||||
|
list(APPEND srcs "stdio_vfs.c")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
idf_component_register(SRCS ${srcs}
|
idf_component_register(SRCS ${srcs}
|
||||||
|
|||||||
@@ -185,4 +185,25 @@ menu "ESP-STDIO"
|
|||||||
If enabled, esp_rom_printf and ESP_EARLY_LOG output will also be sent over USB CDC.
|
If enabled, esp_rom_printf and ESP_EARLY_LOG output will also be sent over USB CDC.
|
||||||
Disabling this option saves about 1kB or RAM.
|
Disabling this option saves about 1kB or RAM.
|
||||||
|
|
||||||
|
config ESP_STDIO_MAX_FDS
|
||||||
|
int "Max logical stdio file descriptors"
|
||||||
|
depends on VFS_SUPPORT_IO
|
||||||
|
range 0 64
|
||||||
|
default 3
|
||||||
|
help
|
||||||
|
Maximum number of logical /dev/console descriptors tracked by esp_stdio VFS.
|
||||||
|
- 0 enables basic mode with a shared internal descriptor.
|
||||||
|
- 1..2 is not allowed
|
||||||
|
- Values >3 enable descriptor-table mode.
|
||||||
|
|
||||||
|
Enabling basic mode decreases RAM usage, but disables checks for per-fd behaviour, such as read-only.
|
||||||
|
|
||||||
|
config ESP_STDIO_MAX_VFS_ENTRIES
|
||||||
|
int "Maximum number of registerable VFS sinks"
|
||||||
|
depends on VFS_SUPPORT_IO
|
||||||
|
range 1 10
|
||||||
|
default 2
|
||||||
|
help
|
||||||
|
Maximum number of filesystems that can be registered as sinks for console output.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|||||||
@@ -5,10 +5,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
#include "esp_rom_sys.h"
|
#include "esp_rom_sys.h"
|
||||||
#include "esp_stdio.h"
|
#include "esp_stdio.h"
|
||||||
|
#include "esp_vfs.h"
|
||||||
#include <sys/errno.h>
|
#include <sys/errno.h>
|
||||||
|
|
||||||
#if CONFIG_VFS_SUPPORT_IO
|
#if CONFIG_VFS_SUPPORT_IO
|
||||||
@@ -42,112 +47,277 @@
|
|||||||
* while the secondary is only used for output.
|
* while the secondary is only used for output.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if CONFIG_VFS_SUPPORT_IO
|
||||||
|
|
||||||
|
_Static_assert(CONFIG_ESP_STDIO_MAX_FDS == 0 || CONFIG_ESP_STDIO_MAX_FDS >= 3, "Invalid value for CONFIG_ESP_STDIO_MAX_FDS");
|
||||||
|
|
||||||
|
#define ESP_STDIO_IS_BASIC (CONFIG_ESP_STDIO_MAX_FDS <= 0)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int fd_primary;
|
const esp_vfs_fs_ops_t *ops;
|
||||||
int fd_secondary;
|
int vfs_flags;
|
||||||
} vfs_console_context_t;
|
void *vfs_ctx;
|
||||||
|
const char *path;
|
||||||
|
int fd;
|
||||||
|
} mux_entry_t;
|
||||||
|
|
||||||
// Secondary register part.
|
typedef struct {
|
||||||
#if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG
|
bool in_use;
|
||||||
const static esp_vfs_fs_ops_t *secondary_vfs = NULL;
|
int flags;
|
||||||
#endif // Secondary part
|
} fd_entry_t;
|
||||||
|
|
||||||
const static esp_vfs_fs_ops_t *primary_vfs = NULL;
|
typedef struct {
|
||||||
|
mux_entry_t entries[CONFIG_ESP_STDIO_MAX_VFS_ENTRIES];
|
||||||
|
size_t used;
|
||||||
|
|
||||||
static vfs_console_context_t vfs_console = {0};
|
#if !ESP_STDIO_IS_BASIC
|
||||||
|
fd_entry_t fds[CONFIG_ESP_STDIO_MAX_FDS];
|
||||||
|
#endif
|
||||||
|
|
||||||
static size_t s_open_count = 0;
|
size_t fd_count;
|
||||||
|
} context_t;
|
||||||
|
|
||||||
|
static context_t s_ctx = {0};
|
||||||
|
|
||||||
|
static const char *TAG = "esp_stdio";
|
||||||
|
|
||||||
|
static inline bool is_fd_valid(int fd)
|
||||||
|
{
|
||||||
|
#if ESP_STDIO_IS_BASIC
|
||||||
|
return (fd == 0) && (s_ctx.fd_count > 0);
|
||||||
|
#else
|
||||||
|
return (fd >= 0) && (fd < CONFIG_ESP_STDIO_MAX_FDS) && s_ctx.fds[fd].in_use;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline mux_entry_t *get_primary_entry(void)
|
||||||
|
{
|
||||||
|
assert(s_ctx.used > 0);
|
||||||
|
return &s_ctx.entries[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_VFS_SUPPORT_TERMIOS
|
||||||
|
static const mux_entry_t *get_primary_termios_entry(int fd, const esp_vfs_termios_ops_t **out_ops)
|
||||||
|
{
|
||||||
|
if (!is_fd_valid(fd)) {
|
||||||
|
errno = EBADF;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mux_entry_t *entry = get_primary_entry();
|
||||||
|
assert(entry);
|
||||||
|
|
||||||
|
const esp_vfs_termios_ops_t *termios = entry->ops->termios;
|
||||||
|
if (!termios) {
|
||||||
|
errno = ENOSYS;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_ops) {
|
||||||
|
*out_ops = termios;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
#endif // CONFIG_VFS_SUPPORT_TERMIOS
|
||||||
|
|
||||||
|
static esp_err_t esp_stdio_register_sink(const esp_vfs_fs_ops_t *ops, int vfs_flags, void *vfs_ctx, const char *path, int fd)
|
||||||
|
{
|
||||||
|
if (!ops || !path) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s_ctx.used >= CONFIG_ESP_STDIO_MAX_VFS_ENTRIES) {
|
||||||
|
ESP_EARLY_LOGE(TAG, "Too many stdio sinks registered");
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mask = ESP_VFS_FLAG_CONTEXT_PTR | ESP_VFS_FLAG_STATIC;
|
||||||
|
|
||||||
|
if ((vfs_flags & mask) != mask) {
|
||||||
|
ESP_EARLY_LOGE(TAG, "The sink FS needs to use both context pointer api and be static allocated");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ops->write_p) {
|
||||||
|
ESP_EARLY_LOGE(TAG, "Stdio sink missing mandatory write handler");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_primary = (s_ctx.used == 0);
|
||||||
|
if (is_primary && (!ops->read_p || !ops->fstat_p || !ops->fcntl_p || !ops->fsync_p)) {
|
||||||
|
ESP_EARLY_LOGE(TAG, "Primary stdio sink missing mandatory read/fstat/fcntl/fsync handlers");
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t idx = s_ctx.used++;
|
||||||
|
|
||||||
|
s_ctx.entries[idx] = (mux_entry_t) {
|
||||||
|
.ops = ops,
|
||||||
|
.vfs_flags = vfs_flags,
|
||||||
|
.vfs_ctx = vfs_ctx,
|
||||||
|
.path = path,
|
||||||
|
.fd = fd,
|
||||||
|
};
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
int console_open(__attribute__((unused)) void *ctx, const char * path, int flags, int mode)
|
int console_open(__attribute__((unused)) void *ctx, const char * path, int flags, int mode)
|
||||||
{
|
{
|
||||||
if (s_open_count > 0) {
|
#if ESP_STDIO_IS_BASIC
|
||||||
// Underlying fd is already open, so just increment the open count
|
(void) path;
|
||||||
// and return the same fd
|
s_ctx.fd_count++;
|
||||||
|
return 0;
|
||||||
s_open_count++;
|
#else
|
||||||
return 0;
|
if (!path || strcmp(path, "/") != 0) {
|
||||||
|
errno = ENOENT;
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Primary port open
|
if (s_ctx.fd_count >= CONFIG_ESP_STDIO_MAX_FDS) {
|
||||||
#if CONFIG_ESP_CONSOLE_UART
|
errno = ENFILE;
|
||||||
vfs_console.fd_primary = open("/dev/uart/"STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM), flags, mode);
|
return -1;
|
||||||
#elif CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
}
|
||||||
vfs_console.fd_primary = open("/dev/usbserjtag", flags, mode);
|
|
||||||
#elif CONFIG_ESP_CONSOLE_USB_CDC
|
int fd = -1;
|
||||||
vfs_console.fd_primary = open("/dev/cdcacm", flags, mode);
|
for (size_t i = 0; i < CONFIG_ESP_STDIO_MAX_FDS; ++i) {
|
||||||
|
if (!s_ctx.fds[i].in_use) {
|
||||||
|
fd = (int)i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
|
errno = ENFILE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_ctx.fd_count++;
|
||||||
|
|
||||||
|
s_ctx.fds[fd] = (fd_entry_t) {
|
||||||
|
.in_use = true,
|
||||||
|
.flags = flags,
|
||||||
|
};
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int console_close(__attribute__((unused)) void *ctx, int fd)
|
||||||
|
{
|
||||||
|
if (!is_fd_valid(fd)) {
|
||||||
|
errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ESP_STDIO_IS_BASIC
|
||||||
|
s_ctx.fd_count--;
|
||||||
#else
|
#else
|
||||||
vfs_console.fd_primary = open("/dev/null", flags, mode);
|
s_ctx.fds[fd].in_use = false;
|
||||||
|
s_ctx.fd_count--;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Secondary port open
|
|
||||||
#if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG
|
|
||||||
vfs_console.fd_secondary = open("/dev/secondary", flags, mode);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
s_open_count++;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t console_write(__attribute__((unused)) void *ctx, int fd, const void *data, size_t size)
|
ssize_t console_write(__attribute__((unused)) void *ctx, int fd, const void *data, size_t size)
|
||||||
{
|
{
|
||||||
write(vfs_console.fd_primary, data, size);
|
if (!is_fd_valid(fd)) {
|
||||||
#if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG
|
errno = EBADF;
|
||||||
write(vfs_console.fd_secondary, data, size);
|
return -1;
|
||||||
#endif
|
}
|
||||||
|
for (size_t i = 0; i < s_ctx.used; i++) {
|
||||||
|
const mux_entry_t *entry = s_ctx.entries + i;
|
||||||
|
if (entry->ops->write_p && entry->fd >= 0) {
|
||||||
|
entry->ops->write_p(entry->vfs_ctx, entry->fd, data, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
int console_fstat(__attribute__((unused)) void *ctx, int fd, struct stat * st)
|
int console_fstat(__attribute__((unused)) void *ctx, int fd, struct stat * st)
|
||||||
{
|
{
|
||||||
return fstat(vfs_console.fd_primary, st);
|
if (!is_fd_valid(fd)) {
|
||||||
}
|
|
||||||
|
|
||||||
int console_close(__attribute__((unused)) void *ctx, int fd)
|
|
||||||
{
|
|
||||||
if (s_open_count == 0) {
|
|
||||||
errno = EBADF;
|
errno = EBADF;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
s_open_count--;
|
const mux_entry_t *entry = get_primary_entry();
|
||||||
|
if (!entry->ops->fstat_p) {
|
||||||
// We don't actually close the underlying fd until the open count reaches 0
|
errno = ENOSYS;
|
||||||
if (s_open_count > 0) {
|
return -1;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
return entry->ops->fstat_p(entry->vfs_ctx, entry->fd, st);
|
||||||
// All function calls are to primary, except from write and close, which will be forwarded to both primary and secondary.
|
|
||||||
close(vfs_console.fd_primary);
|
|
||||||
#if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG
|
|
||||||
close(vfs_console.fd_secondary);
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t console_read(__attribute__((unused)) void *ctx, int fd, void * dst, size_t size)
|
ssize_t console_read(__attribute__((unused)) void *ctx, int fd, void * dst, size_t size)
|
||||||
{
|
{
|
||||||
return read(vfs_console.fd_primary, dst, size);
|
if (!is_fd_valid(fd)) {
|
||||||
|
errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mux_entry_t *entry = get_primary_entry();
|
||||||
|
if (!entry->ops->read_p) {
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return entry->ops->read_p(entry->vfs_ctx, entry->fd, dst, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
int console_fcntl(__attribute__((unused)) void *ctx, int fd, int cmd, int arg)
|
int console_fcntl(__attribute__((unused)) void *ctx, int fd, int cmd, int arg)
|
||||||
{
|
{
|
||||||
return fcntl(vfs_console.fd_primary, cmd, arg);
|
if (!is_fd_valid(fd)) {
|
||||||
|
errno = EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mux_entry_t *entry = get_primary_entry();
|
||||||
|
if (!entry->ops->fcntl_p) {
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return entry->ops->fcntl_p(entry->vfs_ctx, entry->fd, cmd, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
int console_fsync(__attribute__((unused)) void *ctx, int fd)
|
int console_fsync(__attribute__((unused)) void *ctx, int fd)
|
||||||
{
|
{
|
||||||
const int ret_val = fsync(vfs_console.fd_primary);
|
if (!is_fd_valid(fd)) {
|
||||||
#if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG
|
errno = EBADF;
|
||||||
(void)fsync(vfs_console.fd_secondary);
|
return -1;
|
||||||
#endif
|
}
|
||||||
|
|
||||||
|
const mux_entry_t *primary = get_primary_entry();
|
||||||
|
if (!primary->ops->fsync_p) {
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret_val = primary->ops->fsync_p(primary->vfs_ctx, primary->fd);
|
||||||
|
|
||||||
|
for (size_t i = 1; i < s_ctx.used; i++) {
|
||||||
|
const mux_entry_t *entry = s_ctx.entries + i;
|
||||||
|
if (entry->ops->fsync_p && entry->fd >= 0) {
|
||||||
|
entry->ops->fsync_p(entry->vfs_ctx, entry->fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ret_val;
|
return ret_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||||
int console_access(__attribute__((unused)) void *ctx, const char *path, int amode)
|
int console_access(__attribute__((unused)) void *ctx, const char *path, int amode)
|
||||||
{
|
{
|
||||||
// currently only UART support DIR.
|
const mux_entry_t *entry = get_primary_entry();
|
||||||
return access("/dev/uart/"STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM), amode);
|
assert(entry);
|
||||||
|
if (!entry->ops->dir || !entry->ops->dir->access_p) {
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
(void) path;
|
||||||
|
return entry->ops->dir->access_p(entry->vfs_ctx, entry->path, amode);
|
||||||
}
|
}
|
||||||
#endif // CONFIG_VFS_SUPPORT_DIR
|
#endif // CONFIG_VFS_SUPPORT_DIR
|
||||||
|
|
||||||
@@ -156,8 +326,10 @@ static esp_err_t console_start_select(int nfds, fd_set *readfds, fd_set *writefd
|
|||||||
esp_vfs_select_sem_t select_sem, void **end_select_args)
|
esp_vfs_select_sem_t select_sem, void **end_select_args)
|
||||||
{
|
{
|
||||||
// start_select is not guaranteed be implemented even though CONFIG_VFS_SUPPORT_SELECT is enabled in sdkconfig
|
// start_select is not guaranteed be implemented even though CONFIG_VFS_SUPPORT_SELECT is enabled in sdkconfig
|
||||||
if (primary_vfs->select->start_select) {
|
const mux_entry_t *entry = get_primary_entry();
|
||||||
return primary_vfs->select->start_select(nfds, readfds, writefds, exceptfds, select_sem, end_select_args);
|
assert(entry);
|
||||||
|
if (entry->fd >= 0 && entry->ops->select && entry->ops->select->start_select) {
|
||||||
|
return entry->ops->select->start_select(nfds, readfds, writefds, exceptfds, select_sem, end_select_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ESP_ERR_NOT_SUPPORTED;
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
@@ -166,8 +338,10 @@ static esp_err_t console_start_select(int nfds, fd_set *readfds, fd_set *writefd
|
|||||||
esp_err_t console_end_select(void *end_select_args)
|
esp_err_t console_end_select(void *end_select_args)
|
||||||
{
|
{
|
||||||
// end_select is not guaranteed be implemented even though CONFIG_VFS_SUPPORT_SELECT is enabled in sdkconfig
|
// end_select is not guaranteed be implemented even though CONFIG_VFS_SUPPORT_SELECT is enabled in sdkconfig
|
||||||
if (primary_vfs->select->end_select) {
|
const mux_entry_t *entry = get_primary_entry();
|
||||||
return primary_vfs->select->end_select(end_select_args);
|
assert(entry);
|
||||||
|
if (entry->fd >= 0 && entry->ops->select && entry->ops->select->end_select) {
|
||||||
|
return entry->ops->select->end_select(end_select_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ESP_ERR_NOT_SUPPORTED;
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
@@ -179,22 +353,58 @@ esp_err_t console_end_select(void *end_select_args)
|
|||||||
|
|
||||||
int console_tcsetattr(__attribute__((unused)) void *ctx, int fd, int optional_actions, const struct termios *p)
|
int console_tcsetattr(__attribute__((unused)) void *ctx, int fd, int optional_actions, const struct termios *p)
|
||||||
{
|
{
|
||||||
return tcsetattr(vfs_console.fd_primary, optional_actions, p);
|
const esp_vfs_termios_ops_t *termios = NULL;
|
||||||
|
const mux_entry_t *entry = get_primary_termios_entry(fd, &termios);
|
||||||
|
if (!entry) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!termios->tcsetattr_p) {
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return entry->ops->termios->tcsetattr_p(entry->vfs_ctx, entry->fd, optional_actions, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
int console_tcgetattr(__attribute__((unused)) void *ctx, int fd, struct termios *p)
|
int console_tcgetattr(__attribute__((unused)) void *ctx, int fd, struct termios *p)
|
||||||
{
|
{
|
||||||
return tcgetattr(vfs_console.fd_primary, p);
|
const esp_vfs_termios_ops_t *termios = NULL;
|
||||||
|
const mux_entry_t *entry = get_primary_termios_entry(fd, &termios);
|
||||||
|
if (!entry) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!termios->tcgetattr_p) {
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return entry->ops->termios->tcgetattr_p(entry->vfs_ctx, entry->fd, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
int console_tcdrain(__attribute__((unused)) void *ctx, int fd)
|
int console_tcdrain(__attribute__((unused)) void *ctx, int fd)
|
||||||
{
|
{
|
||||||
return tcdrain(vfs_console.fd_primary);
|
const esp_vfs_termios_ops_t *termios = NULL;
|
||||||
|
const mux_entry_t *entry = get_primary_termios_entry(fd, &termios);
|
||||||
|
if (!entry) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!termios->tcdrain_p) {
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return entry->ops->termios->tcdrain_p(entry->vfs_ctx, entry->fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
int console_tcflush(__attribute__((unused)) void *ctx, int fd, int select)
|
int console_tcflush(__attribute__((unused)) void *ctx, int fd, int select)
|
||||||
{
|
{
|
||||||
return tcflush(vfs_console.fd_primary, select);
|
const esp_vfs_termios_ops_t *termios = NULL;
|
||||||
|
const mux_entry_t *entry = get_primary_termios_entry(fd, &termios);
|
||||||
|
if (!entry) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!termios->tcflush_p) {
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return entry->ops->termios->tcflush_p(entry->vfs_ctx, entry->fd, select);
|
||||||
}
|
}
|
||||||
#endif // CONFIG_VFS_SUPPORT_TERMIOS
|
#endif // CONFIG_VFS_SUPPORT_TERMIOS
|
||||||
|
|
||||||
@@ -242,31 +452,82 @@ static const esp_vfs_fs_ops_t s_vfs_console = {
|
|||||||
#endif // CONFIG_VFS_SUPPORT_TERMIOS
|
#endif // CONFIG_VFS_SUPPORT_TERMIOS
|
||||||
};
|
};
|
||||||
|
|
||||||
static esp_err_t esp_vfs_dev_console_register(void)
|
|
||||||
{
|
|
||||||
return esp_vfs_register_fs(ESP_VFS_DEV_CONSOLE, &s_vfs_console, ESP_VFS_FLAG_STATIC | ESP_VFS_FLAG_CONTEXT_PTR, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
esp_err_t esp_stdio_register(void)
|
esp_err_t esp_stdio_register(void)
|
||||||
{
|
{
|
||||||
esp_err_t err = ESP_OK;
|
esp_err_t err = ESP_OK;
|
||||||
// Primary vfs part.
|
// Primary vfs part.
|
||||||
#if CONFIG_ESP_CONSOLE_UART
|
#if CONFIG_ESP_CONSOLE_UART
|
||||||
primary_vfs = esp_vfs_uart_get_vfs();
|
const esp_vfs_fs_ops_t *ops = esp_vfs_uart_get_vfs();
|
||||||
|
int fd = ops->open_p(NULL, "/" STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM), O_RDWR, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
ESP_EARLY_LOGE(TAG, "Failed to open primary UART sink, errno=%d", errno);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
err = esp_stdio_register_sink(ops,
|
||||||
|
ESP_VFS_FLAG_CONTEXT_PTR | ESP_VFS_FLAG_STATIC,
|
||||||
|
NULL,
|
||||||
|
"/" STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM),
|
||||||
|
fd);
|
||||||
#elif CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
#elif CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
||||||
primary_vfs = esp_vfs_usb_serial_jtag_get_vfs();
|
const esp_vfs_fs_ops_t *ops = esp_vfs_usb_serial_jtag_get_vfs();
|
||||||
|
int fd = ops->open_p(NULL, "/", O_RDWR, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
ESP_EARLY_LOGE(TAG, "Failed to open primary USB JTAG sink, errno=%d", errno);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
err = esp_stdio_register_sink(ops,
|
||||||
|
ESP_VFS_FLAG_CONTEXT_PTR | ESP_VFS_FLAG_STATIC,
|
||||||
|
NULL,
|
||||||
|
"/",
|
||||||
|
fd);
|
||||||
#elif CONFIG_ESP_CONSOLE_USB_CDC
|
#elif CONFIG_ESP_CONSOLE_USB_CDC
|
||||||
primary_vfs = esp_vfs_cdcacm_get_vfs();
|
const esp_vfs_fs_ops_t *ops = esp_vfs_cdcacm_get_vfs();
|
||||||
|
int fd = ops->open_p(NULL, "/", O_RDWR, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
ESP_EARLY_LOGE(TAG, "Failed to open primary USB CDC sink, errno=%d", errno);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
err = esp_stdio_register_sink(ops,
|
||||||
|
ESP_VFS_FLAG_CONTEXT_PTR | ESP_VFS_FLAG_STATIC,
|
||||||
|
NULL,
|
||||||
|
"/",
|
||||||
|
fd);
|
||||||
#else
|
#else
|
||||||
primary_vfs = esp_vfs_null_get_vfs();
|
const esp_vfs_fs_ops_t *ops = esp_vfs_null_get_vfs();
|
||||||
|
int fd = ops->open_p(NULL, "/", O_RDWR, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
ESP_EARLY_LOGE(TAG, "Failed to open primary null sink, errno=%d", errno);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
err = esp_stdio_register_sink(ops,
|
||||||
|
ESP_VFS_FLAG_CONTEXT_PTR | ESP_VFS_FLAG_STATIC,
|
||||||
|
NULL,
|
||||||
|
"/",
|
||||||
|
fd);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
// Secondary vfs part.
|
// Secondary vfs part.
|
||||||
#if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG
|
#if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG
|
||||||
secondary_vfs = esp_vfs_usb_serial_jtag_get_vfs();
|
const esp_vfs_fs_ops_t *ops_secondary = esp_vfs_usb_serial_jtag_get_vfs();
|
||||||
|
int fd_secondary = ops_secondary->open_p(NULL, "/", O_RDWR, 0);
|
||||||
|
if (fd_secondary < 0) {
|
||||||
|
ESP_EARLY_LOGE(TAG, "Failed to open secondary USB JTAG sink, errno=%d", errno);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
err = esp_stdio_register_sink(ops_secondary,
|
||||||
|
ESP_VFS_FLAG_CONTEXT_PTR | ESP_VFS_FLAG_STATIC,
|
||||||
|
NULL,
|
||||||
|
"/",
|
||||||
|
fd_secondary);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
err = esp_vfs_dev_console_register();
|
return esp_vfs_register_fs(ESP_VFS_DEV_CONSOLE, &s_vfs_console, ESP_VFS_FLAG_STATIC | ESP_VFS_FLAG_CONTEXT_PTR, NULL);
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_SYSTEM_INIT_FN(init_vfs_console, CORE, BIT(0), 119)
|
ESP_SYSTEM_INIT_FN(init_vfs_console, CORE, BIT(0), 119)
|
||||||
|
|||||||
Reference in New Issue
Block a user