mirror of
https://github.com/espressif/esp-idf.git
synced 2026-08-18 06:35:35 +03:00
Merge branch 'fix/stdio_remove_fd_table' into 'master'
refactor(storage/stdio): remove fd table, forward opens to primary backend See merge request espressif/esp-idf!49019
This commit is contained in:
@@ -185,28 +185,6 @@ menu "ESP-STDIO"
|
||||
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.
|
||||
|
||||
config ESP_STDIO_BASIC_MODE
|
||||
bool "Use basic /dev/console descriptor handling"
|
||||
depends on VFS_SUPPORT_IO
|
||||
default n
|
||||
help
|
||||
Enable the low-overhead legacy /dev/console descriptor handling.
|
||||
|
||||
In basic mode, all open() calls share a single internal descriptor. This keeps RAM usage
|
||||
lower, but disables checks for per-fd behaviour, such as read-only versus write-only access.
|
||||
|
||||
config ESP_STDIO_MAX_FDS
|
||||
int "Max logical stdio file descriptors"
|
||||
depends on VFS_SUPPORT_IO && !ESP_STDIO_BASIC_MODE
|
||||
range 3 64
|
||||
default 16
|
||||
help
|
||||
Maximum number of logical /dev/console descriptors tracked by esp_stdio VFS.
|
||||
|
||||
stdin, stdout, and stderr each occupy one slot. The default leaves room for additional
|
||||
opens of /dev/console (e.g. dup2, reopen). If set to exactly 3, only the three standard
|
||||
streams fit and further opens fail with ENFILE unless one is closed first.
|
||||
|
||||
config ESP_STDIO_MAX_VFS_ENTRIES
|
||||
int "Maximum number of registerable VFS sinks"
|
||||
depends on VFS_SUPPORT_IO
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
|
||||
#include "esp_private/startup_internal.h"
|
||||
#include "esp_private/nullfs.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include <sys/lock.h>
|
||||
#endif
|
||||
|
||||
@@ -52,96 +51,28 @@
|
||||
|
||||
#if CONFIG_VFS_SUPPORT_IO
|
||||
|
||||
#if !CONFIG_ESP_STDIO_BASIC_MODE
|
||||
_Static_assert(CONFIG_ESP_STDIO_MAX_FDS >= 3, "Invalid value for CONFIG_ESP_STDIO_MAX_FDS");
|
||||
#endif
|
||||
|
||||
#define ESP_STDIO_IS_BASIC (CONFIG_ESP_STDIO_BASIC_MODE)
|
||||
|
||||
typedef struct {
|
||||
const esp_vfs_fs_ops_t *ops;
|
||||
int vfs_flags;
|
||||
const esp_vfs_fs_ops_t *ops; /* NULL = slot is free */
|
||||
void *vfs_ctx;
|
||||
const char *path;
|
||||
int fd;
|
||||
int fd; /* -1 if not opened (used by auxiliaries) */
|
||||
} mux_entry_t;
|
||||
|
||||
typedef struct {
|
||||
bool in_use;
|
||||
int flags;
|
||||
} fd_entry_t;
|
||||
/* Primary backend (all ops forwarded except open + write + fsync) */
|
||||
static mux_entry_t s_primary = { .fd = -1 };
|
||||
|
||||
typedef struct {
|
||||
mux_entry_t entries[CONFIG_ESP_STDIO_MAX_VFS_ENTRIES];
|
||||
size_t used;
|
||||
|
||||
#if !ESP_STDIO_IS_BASIC
|
||||
fd_entry_t fds[CONFIG_ESP_STDIO_MAX_FDS];
|
||||
#endif
|
||||
|
||||
size_t fd_count;
|
||||
} context_t;
|
||||
|
||||
static context_t s_ctx = {0};
|
||||
static _lock_t s_ctx_lock;
|
||||
/* Auxiliary sinks (write-only fan-out) */
|
||||
#define STDIO_MAX_AUXILIARY (CONFIG_ESP_STDIO_MAX_VFS_ENTRIES - 1)
|
||||
static mux_entry_t s_auxiliary[STDIO_MAX_AUXILIARY] = { [0 ... STDIO_MAX_AUXILIARY - 1] = { .fd = -1 } };
|
||||
static int s_open_count = 0;
|
||||
static _lock_t s_lock;
|
||||
|
||||
static const char *TAG = "esp_stdio";
|
||||
|
||||
static inline bool is_fd_valid_nolock(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 bool is_fd_valid(int fd)
|
||||
{
|
||||
bool valid;
|
||||
|
||||
_lock_acquire(&s_ctx_lock);
|
||||
valid = is_fd_valid_nolock(fd);
|
||||
_lock_release(&s_ctx_lock);
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
#if !ESP_STDIO_IS_BASIC
|
||||
static inline int get_fd_flags_nolock(int fd)
|
||||
{
|
||||
assert((fd >= 0) && (fd < CONFIG_ESP_STDIO_MAX_FDS));
|
||||
return s_ctx.fds[fd].flags;
|
||||
}
|
||||
|
||||
static inline bool fd_allows_read_nolock(int fd)
|
||||
{
|
||||
const int flags = get_fd_flags_nolock(fd);
|
||||
return (flags & O_ACCMODE) != O_WRONLY;
|
||||
}
|
||||
|
||||
static inline bool fd_allows_write_nolock(int fd)
|
||||
{
|
||||
const int flags = get_fd_flags_nolock(fd);
|
||||
return (flags & O_ACCMODE) != O_RDONLY;
|
||||
}
|
||||
#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();
|
||||
const mux_entry_t *entry = &s_primary;
|
||||
assert(entry);
|
||||
|
||||
const esp_vfs_termios_ops_t *termios = entry->ops->termios;
|
||||
@@ -158,238 +89,156 @@ static const mux_entry_t *get_primary_termios_entry(int fd, const esp_vfs_termio
|
||||
}
|
||||
#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)
|
||||
static esp_err_t __attribute__((unused)) register_auxiliary(const esp_vfs_fs_ops_t *ops, void *ctx, const char *path)
|
||||
{
|
||||
if (!ops || !path) {
|
||||
if (!ops || !path || !ops->write_p) {
|
||||
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;
|
||||
_lock_acquire(&s_lock);
|
||||
for (size_t i = 0; i < STDIO_MAX_AUXILIARY; i++) {
|
||||
if (s_auxiliary[i].ops == NULL) {
|
||||
s_auxiliary[i] = (mux_entry_t) {
|
||||
.ops = ops,
|
||||
.vfs_ctx = ctx,
|
||||
.path = path,
|
||||
.fd = -1,
|
||||
};
|
||||
_lock_release(&s_lock);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
_lock_release(&s_lock);
|
||||
ESP_EARLY_LOGE(TAG, "Too many auxiliary sinks");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
int console_open(__attribute__((unused)) void *ctx, const char * path, int flags, int mode)
|
||||
{
|
||||
#if ESP_STDIO_IS_BASIC
|
||||
(void) path;
|
||||
(void) flags;
|
||||
(void) mode;
|
||||
|
||||
_lock_acquire(&s_ctx_lock);
|
||||
s_ctx.fd_count++;
|
||||
_lock_release(&s_ctx_lock);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
(void) mode;
|
||||
|
||||
if (!path || strcmp(path, "/") != 0) {
|
||||
errno = ENOENT;
|
||||
(void)path;
|
||||
const mux_entry_t *entry = &s_primary;
|
||||
if (!entry->ops || !entry->ops->open_p) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_lock_acquire(&s_ctx_lock);
|
||||
|
||||
if (s_ctx.fd_count >= CONFIG_ESP_STDIO_MAX_FDS) {
|
||||
_lock_release(&s_ctx_lock);
|
||||
errno = ENFILE;
|
||||
int local_fd = entry->ops->open_p(entry->vfs_ctx, entry->path, flags, mode);
|
||||
if (local_fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fd = -1;
|
||||
for (size_t i = 0; i < CONFIG_ESP_STDIO_MAX_FDS; ++i) {
|
||||
if (!s_ctx.fds[i].in_use) {
|
||||
fd = (int)i;
|
||||
break;
|
||||
/* Lazily open auxiliaries on first console open */
|
||||
_lock_acquire(&s_lock);
|
||||
if (s_open_count == 0) {
|
||||
for (size_t i = 0; i < STDIO_MAX_AUXILIARY; i++) {
|
||||
mux_entry_t *aux = &s_auxiliary[i];
|
||||
if (aux->ops && aux->ops->open_p && aux->fd < 0) {
|
||||
aux->fd = aux->ops->open_p(aux->vfs_ctx, aux->path, O_WRONLY, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
s_open_count++;
|
||||
_lock_release(&s_lock);
|
||||
|
||||
if (fd < 0) {
|
||||
_lock_release(&s_ctx_lock);
|
||||
errno = ENFILE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
s_ctx.fd_count++;
|
||||
s_ctx.fds[fd] = (fd_entry_t) {
|
||||
.in_use = true,
|
||||
.flags = flags,
|
||||
};
|
||||
|
||||
_lock_release(&s_ctx_lock);
|
||||
return fd;
|
||||
#endif
|
||||
return local_fd;
|
||||
}
|
||||
|
||||
int console_close(__attribute__((unused)) void *ctx, int fd)
|
||||
{
|
||||
_lock_acquire(&s_ctx_lock);
|
||||
|
||||
if (!is_fd_valid_nolock(fd)) {
|
||||
_lock_release(&s_ctx_lock);
|
||||
errno = EBADF;
|
||||
const mux_entry_t *entry = &s_primary;
|
||||
if (!entry->ops || !entry->ops->close_p) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if ESP_STDIO_IS_BASIC
|
||||
s_ctx.fd_count--;
|
||||
#else
|
||||
s_ctx.fds[fd].in_use = false;
|
||||
s_ctx.fd_count--;
|
||||
#endif
|
||||
int ret = entry->ops->close_p(entry->vfs_ctx, fd);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Close auxiliaries when last console fd is closed */
|
||||
_lock_acquire(&s_lock);
|
||||
if (s_open_count > 0) {
|
||||
s_open_count--;
|
||||
}
|
||||
if (s_open_count == 0) {
|
||||
for (size_t i = 0; i < STDIO_MAX_AUXILIARY; i++) {
|
||||
mux_entry_t *aux = &s_auxiliary[i];
|
||||
if (aux->ops && aux->ops->close_p && aux->fd >= 0) {
|
||||
aux->ops->close_p(aux->vfs_ctx, aux->fd);
|
||||
aux->fd = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
_lock_release(&s_lock);
|
||||
|
||||
_lock_release(&s_ctx_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t console_write(__attribute__((unused)) void *ctx, int fd, const void *data, size_t size)
|
||||
{
|
||||
#if ESP_STDIO_IS_BASIC
|
||||
if (!is_fd_valid(fd)) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
_lock_acquire(&s_ctx_lock);
|
||||
if (!is_fd_valid_nolock(fd)) {
|
||||
_lock_release(&s_ctx_lock);
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
if (!fd_allows_write_nolock(fd)) {
|
||||
_lock_release(&s_ctx_lock);
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
_lock_release(&s_ctx_lock);
|
||||
#endif
|
||||
const mux_entry_t *primary = &s_primary;
|
||||
ssize_t ret_val = primary->ops->write_p(primary->vfs_ctx, fd, data, size);
|
||||
|
||||
const mux_entry_t *primary = get_primary_entry();
|
||||
ssize_t ret_val = primary->ops->write_p(primary->vfs_ctx, primary->fd, data, size);
|
||||
|
||||
for (size_t i = 1; i < s_ctx.used; i++) {
|
||||
const mux_entry_t *entry = s_ctx.entries + i;
|
||||
if (entry->ops->write_p && entry->fd >= 0) {
|
||||
_lock_acquire(&s_lock);
|
||||
for (size_t i = 0; i < STDIO_MAX_AUXILIARY; i++) {
|
||||
const mux_entry_t *entry = &s_auxiliary[i];
|
||||
if (entry->ops && entry->ops->write_p && entry->fd >= 0) {
|
||||
(void) entry->ops->write_p(entry->vfs_ctx, entry->fd, data, size);
|
||||
}
|
||||
}
|
||||
_lock_release(&s_lock);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
int console_fstat(__attribute__((unused)) void *ctx, int fd, struct stat * st)
|
||||
{
|
||||
if (!is_fd_valid(fd)) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const mux_entry_t *entry = get_primary_entry();
|
||||
const mux_entry_t *entry = &s_primary;
|
||||
if (!entry->ops->fstat_p) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
return entry->ops->fstat_p(entry->vfs_ctx, entry->fd, st);
|
||||
return entry->ops->fstat_p(entry->vfs_ctx, fd, st);
|
||||
}
|
||||
|
||||
ssize_t console_read(__attribute__((unused)) void *ctx, int fd, void * dst, size_t size)
|
||||
{
|
||||
#if ESP_STDIO_IS_BASIC
|
||||
if (!is_fd_valid(fd)) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
_lock_acquire(&s_ctx_lock);
|
||||
if (!is_fd_valid_nolock(fd)) {
|
||||
_lock_release(&s_ctx_lock);
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
if (!fd_allows_read_nolock(fd)) {
|
||||
_lock_release(&s_ctx_lock);
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
_lock_release(&s_ctx_lock);
|
||||
#endif
|
||||
|
||||
const mux_entry_t *entry = get_primary_entry();
|
||||
const mux_entry_t *entry = &s_primary;
|
||||
if (!entry->ops->read_p) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
return entry->ops->read_p(entry->vfs_ctx, entry->fd, dst, size);
|
||||
return entry->ops->read_p(entry->vfs_ctx, fd, dst, size);
|
||||
}
|
||||
|
||||
int console_fcntl(__attribute__((unused)) void *ctx, int fd, int cmd, int arg)
|
||||
{
|
||||
if (!is_fd_valid(fd)) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const mux_entry_t *entry = get_primary_entry();
|
||||
const mux_entry_t *entry = &s_primary;
|
||||
if (!entry->ops->fcntl_p) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
return entry->ops->fcntl_p(entry->vfs_ctx, entry->fd, cmd, arg);
|
||||
return entry->ops->fcntl_p(entry->vfs_ctx, fd, cmd, arg);
|
||||
}
|
||||
|
||||
int console_fsync(__attribute__((unused)) void *ctx, int fd)
|
||||
{
|
||||
if (!is_fd_valid(fd)) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const mux_entry_t *primary = get_primary_entry();
|
||||
const mux_entry_t *primary = &s_primary;
|
||||
if (!primary->ops->fsync_p) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret_val = primary->ops->fsync_p(primary->vfs_ctx, primary->fd);
|
||||
int ret_val = primary->ops->fsync_p(primary->vfs_ctx, 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) {
|
||||
_lock_acquire(&s_lock);
|
||||
for (size_t i = 0; i < STDIO_MAX_AUXILIARY; i++) {
|
||||
const mux_entry_t *entry = &s_auxiliary[i];
|
||||
if (entry->ops && entry->ops->fsync_p && entry->fd >= 0) {
|
||||
(void) entry->ops->fsync_p(entry->vfs_ctx, entry->fd);
|
||||
}
|
||||
}
|
||||
_lock_release(&s_lock);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
@@ -397,7 +246,7 @@ int console_fsync(__attribute__((unused)) void *ctx, int fd)
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
int console_access(__attribute__((unused)) void *ctx, const char *path, int amode)
|
||||
{
|
||||
const mux_entry_t *entry = get_primary_entry();
|
||||
const mux_entry_t *entry = &s_primary;
|
||||
assert(entry);
|
||||
if (!entry->ops->dir || !entry->ops->dir->access_p) {
|
||||
errno = ENOSYS;
|
||||
@@ -410,165 +259,23 @@ int console_access(__attribute__((unused)) void *ctx, const char *path, int amod
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
|
||||
typedef struct {
|
||||
void *sink_select_args;
|
||||
fd_set *readfds;
|
||||
fd_set *writefds;
|
||||
fd_set *exceptfds;
|
||||
uint64_t interested_read;
|
||||
uint64_t interested_write;
|
||||
uint64_t interested_except;
|
||||
} console_select_ctx_t;
|
||||
|
||||
/* Kconfig allows up to 64 logical fds; uint64_t masks and (1ULL << i) require i < 64. */
|
||||
enum { CONSOLE_SELECT_MAX_SCAN = 64 };
|
||||
|
||||
static esp_err_t console_start_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
|
||||
esp_vfs_select_sem_t select_sem, void **end_select_args)
|
||||
{
|
||||
const mux_entry_t *entry = get_primary_entry();
|
||||
assert(entry);
|
||||
*end_select_args = NULL;
|
||||
|
||||
if (entry->fd < 0 || entry->ops->select == NULL || entry->ops->select->start_select == NULL) {
|
||||
const mux_entry_t *entry = &s_primary;
|
||||
if (!entry->ops || !entry->ops->select || !entry->ops->select->start_select) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
return entry->ops->select->start_select(nfds, readfds, writefds, exceptfds, select_sem, end_select_args);
|
||||
}
|
||||
|
||||
uint64_t interested_read = 0;
|
||||
uint64_t interested_write = 0;
|
||||
uint64_t interested_except = 0;
|
||||
|
||||
const int scan = nfds < CONSOLE_SELECT_MAX_SCAN ? nfds : CONSOLE_SELECT_MAX_SCAN;
|
||||
for (int i = 0; i < scan; ++i) {
|
||||
if (!is_fd_valid(i)) {
|
||||
continue;
|
||||
}
|
||||
if (readfds && FD_ISSET(i, readfds)) {
|
||||
interested_read |= (1ULL << i);
|
||||
}
|
||||
if (writefds && FD_ISSET(i, writefds)) {
|
||||
interested_write |= (1ULL << i);
|
||||
}
|
||||
if (exceptfds && FD_ISSET(i, exceptfds)) {
|
||||
interested_except |= (1ULL << i);
|
||||
}
|
||||
}
|
||||
|
||||
if (interested_read == 0 && interested_write == 0 && interested_except == 0) {
|
||||
static esp_err_t console_end_select(void *end_select_args)
|
||||
{
|
||||
const mux_entry_t *entry = &s_primary;
|
||||
if (!entry->ops || !entry->ops->select || !entry->ops->select->end_select) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
const int hw = entry->fd;
|
||||
|
||||
if (readfds) {
|
||||
FD_ZERO(readfds);
|
||||
if (interested_read) {
|
||||
FD_SET(hw, readfds);
|
||||
}
|
||||
}
|
||||
if (writefds) {
|
||||
FD_ZERO(writefds);
|
||||
if (interested_write) {
|
||||
FD_SET(hw, writefds);
|
||||
}
|
||||
}
|
||||
if (exceptfds) {
|
||||
FD_ZERO(exceptfds);
|
||||
if (interested_except) {
|
||||
FD_SET(hw, exceptfds);
|
||||
}
|
||||
}
|
||||
|
||||
const int forward_nfds = nfds > hw + 1 ? nfds : hw + 1;
|
||||
|
||||
console_select_ctx_t *ctx = heap_caps_malloc(sizeof(console_select_ctx_t), MALLOC_CAP_INTERNAL);
|
||||
if (ctx == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
ctx->sink_select_args = NULL;
|
||||
ctx->readfds = readfds;
|
||||
ctx->writefds = writefds;
|
||||
ctx->exceptfds = exceptfds;
|
||||
ctx->interested_read = interested_read;
|
||||
ctx->interested_write = interested_write;
|
||||
ctx->interested_except = interested_except;
|
||||
|
||||
esp_err_t err = entry->ops->select->start_select(forward_nfds, readfds, writefds, exceptfds, select_sem,
|
||||
&ctx->sink_select_args);
|
||||
if (err != ESP_OK) {
|
||||
if (readfds) {
|
||||
FD_ZERO(readfds);
|
||||
for (int i = 0; i < scan; ++i) {
|
||||
if (interested_read & (1ULL << i)) {
|
||||
FD_SET(i, readfds);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (writefds) {
|
||||
FD_ZERO(writefds);
|
||||
for (int i = 0; i < scan; ++i) {
|
||||
if (interested_write & (1ULL << i)) {
|
||||
FD_SET(i, writefds);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (exceptfds) {
|
||||
FD_ZERO(exceptfds);
|
||||
for (int i = 0; i < scan; ++i) {
|
||||
if (interested_except & (1ULL << i)) {
|
||||
FD_SET(i, exceptfds);
|
||||
}
|
||||
}
|
||||
}
|
||||
heap_caps_free(ctx);
|
||||
return err;
|
||||
}
|
||||
|
||||
*end_select_args = ctx;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void console_expand_fdset(fd_set *fds, uint64_t interested, int hw)
|
||||
{
|
||||
if (fds == NULL) {
|
||||
return;
|
||||
}
|
||||
const bool ready = FD_ISSET(hw, fds);
|
||||
FD_ZERO(fds);
|
||||
if (!ready || interested == 0) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < CONSOLE_SELECT_MAX_SCAN; ++i) {
|
||||
if (interested & (1ULL << i)) {
|
||||
FD_SET(i, fds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t console_end_select(void *end_select_args)
|
||||
{
|
||||
if (end_select_args == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
console_select_ctx_t *ctx = (console_select_ctx_t *)end_select_args;
|
||||
const mux_entry_t *entry = get_primary_entry();
|
||||
assert(entry);
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (entry->fd >= 0 && entry->ops->select && entry->ops->select->end_select) {
|
||||
ret = entry->ops->select->end_select(ctx->sink_select_args);
|
||||
}
|
||||
|
||||
const int hw = entry->fd;
|
||||
if (hw >= 0) {
|
||||
console_expand_fdset(ctx->readfds, ctx->interested_read, hw);
|
||||
console_expand_fdset(ctx->writefds, ctx->interested_write, hw);
|
||||
console_expand_fdset(ctx->exceptfds, ctx->interested_except, hw);
|
||||
}
|
||||
|
||||
heap_caps_free(ctx);
|
||||
return ret;
|
||||
return entry->ops->select->end_select(end_select_args);
|
||||
}
|
||||
|
||||
#endif // CONFIG_VFS_SUPPORT_SELECT
|
||||
@@ -586,7 +293,7 @@ int console_tcsetattr(__attribute__((unused)) void *ctx, int fd, int optional_ac
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
return entry->ops->termios->tcsetattr_p(entry->vfs_ctx, entry->fd, optional_actions, p);
|
||||
return entry->ops->termios->tcsetattr_p(entry->vfs_ctx, fd, optional_actions, p);
|
||||
}
|
||||
|
||||
int console_tcgetattr(__attribute__((unused)) void *ctx, int fd, struct termios *p)
|
||||
@@ -600,7 +307,7 @@ int console_tcgetattr(__attribute__((unused)) void *ctx, int fd, struct termios
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
return entry->ops->termios->tcgetattr_p(entry->vfs_ctx, entry->fd, p);
|
||||
return entry->ops->termios->tcgetattr_p(entry->vfs_ctx, fd, p);
|
||||
}
|
||||
|
||||
int console_tcdrain(__attribute__((unused)) void *ctx, int fd)
|
||||
@@ -614,7 +321,7 @@ int console_tcdrain(__attribute__((unused)) void *ctx, int fd)
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
return entry->ops->termios->tcdrain_p(entry->vfs_ctx, entry->fd);
|
||||
return entry->ops->termios->tcdrain_p(entry->vfs_ctx, fd);
|
||||
}
|
||||
|
||||
int console_tcflush(__attribute__((unused)) void *ctx, int fd, int select)
|
||||
@@ -628,7 +335,7 @@ int console_tcflush(__attribute__((unused)) void *ctx, int fd, int select)
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
return entry->ops->termios->tcflush_p(entry->vfs_ctx, entry->fd, select);
|
||||
return entry->ops->termios->tcflush_p(entry->vfs_ctx, fd, select);
|
||||
}
|
||||
#endif // CONFIG_VFS_SUPPORT_TERMIOS
|
||||
|
||||
@@ -678,80 +385,52 @@ static const esp_vfs_fs_ops_t s_vfs_console = {
|
||||
|
||||
esp_err_t esp_stdio_register(void)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
_lock_init(&s_ctx_lock);
|
||||
_lock_init(&s_lock);
|
||||
|
||||
// Primary vfs part.
|
||||
#if CONFIG_ESP_CONSOLE_UART
|
||||
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);
|
||||
s_primary = (mux_entry_t) {
|
||||
.ops = esp_vfs_uart_get_vfs(),
|
||||
.vfs_ctx = NULL,
|
||||
.path = "/" STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM),
|
||||
.fd = -1,
|
||||
};
|
||||
#elif CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
||||
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);
|
||||
s_primary = (mux_entry_t) {
|
||||
.ops = esp_vfs_usb_serial_jtag_get_vfs(),
|
||||
.vfs_ctx = NULL,
|
||||
.path = "/",
|
||||
.fd = -1,
|
||||
};
|
||||
#elif CONFIG_ESP_CONSOLE_USB_CDC
|
||||
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);
|
||||
s_primary = (mux_entry_t) {
|
||||
.ops = esp_vfs_cdcacm_get_vfs(),
|
||||
.vfs_ctx = NULL,
|
||||
.path = "/",
|
||||
.fd = -1,
|
||||
};
|
||||
#else
|
||||
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);
|
||||
s_primary = (mux_entry_t) {
|
||||
.ops = esp_vfs_null_get_vfs(),
|
||||
.vfs_ctx = NULL,
|
||||
.path = "/",
|
||||
.fd = -1,
|
||||
};
|
||||
#endif
|
||||
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
if (!s_primary.ops) {
|
||||
ESP_EARLY_LOGE(TAG, "No primary console backend available");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// Secondary vfs part.
|
||||
// Auxiliary sinks (write-only fan-out).
|
||||
#if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG
|
||||
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);
|
||||
esp_err_t err = register_auxiliary(esp_vfs_usb_serial_jtag_get_vfs(), NULL, "/");
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
return esp_vfs_register_fs(ESP_VFS_DEV_CONSOLE, &s_vfs_console, ESP_VFS_FLAG_STATIC | ESP_VFS_FLAG_CONTEXT_PTR, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,13 +52,7 @@ static void console_none_print(void)
|
||||
#endif
|
||||
|
||||
#if CONFIG_VFS_SUPPORT_IO
|
||||
#if CONFIG_ESP_STDIO_BASIC_MODE
|
||||
#define ESP_STDIO_RUN_OPEN_CLOSE_CHECK 1
|
||||
#elif CONFIG_ESP_STDIO_MAX_FDS > 3
|
||||
#define ESP_STDIO_RUN_OPEN_CLOSE_CHECK 1
|
||||
#else
|
||||
#define ESP_STDIO_RUN_OPEN_CLOSE_CHECK 0
|
||||
#endif
|
||||
|
||||
#if ESP_STDIO_RUN_OPEN_CLOSE_CHECK
|
||||
static void console_open_close_check(void)
|
||||
@@ -80,122 +74,26 @@ static void console_open_close_check(void)
|
||||
|
||||
static void stdio_fd_mode_behavior_check(void)
|
||||
{
|
||||
#if CONFIG_ESP_STDIO_BASIC_MODE
|
||||
printf("STDIO_TEST:MODE=BASIC\n");
|
||||
printf("STDIO_TEST:MODE=FORWARDING\n");
|
||||
|
||||
int fd0 = open("/dev/console", O_RDWR);
|
||||
int fd1 = open("/dev/console", O_RDWR);
|
||||
int fd2 = open("/dev/console", O_RDWR);
|
||||
assert(fd0 >= 0);
|
||||
assert(fd1 >= 0);
|
||||
assert(fd2 >= 0);
|
||||
printf("STDIO_TEST:BASIC:OPEN_OK\n");
|
||||
|
||||
const char *msg = "STDIO_TEST:BASIC:WRITE_OK\n";
|
||||
int fd1 = open("/dev/console", O_RDWR);
|
||||
assert(fd1 >= 0);
|
||||
assert(fd0 != fd1);
|
||||
printf("STDIO_TEST:UNIQUE_FDS_OK\n");
|
||||
|
||||
const char *msg = "STDIO_TEST:WRITE_OK\n";
|
||||
ssize_t wr = write(fd0, msg, strlen(msg));
|
||||
assert(wr == (ssize_t) strlen(msg));
|
||||
|
||||
/* Verify write return value matches actual bytes (primary sink propagation) */
|
||||
const char *short_msg = "ab";
|
||||
wr = write(fd1, short_msg, 2);
|
||||
assert(wr == 2);
|
||||
assert(fsync(fd0) == 0);
|
||||
printf("STDIO_TEST:FSYNC_OK\n");
|
||||
|
||||
assert(close(fd0) == 0);
|
||||
assert(close(fd1) == 0);
|
||||
assert(close(fd2) == 0);
|
||||
printf("STDIO_TEST:BASIC:CLOSE_OK\n");
|
||||
|
||||
errno = 0;
|
||||
assert(close(-1) < 0);
|
||||
assert(errno == EBADF);
|
||||
|
||||
errno = 0;
|
||||
assert(write(fd0, msg, strlen(msg)) < 0);
|
||||
assert(errno == EBADF);
|
||||
printf("STDIO_TEST:BASIC:EBADF_OK\n");
|
||||
#else
|
||||
printf("STDIO_TEST:MODE=NON_BASIC\n");
|
||||
|
||||
errno = 0;
|
||||
int fd0 = open("/dev/console", O_RDWR);
|
||||
if (fd0 < 0) {
|
||||
/* Small fd limits can be fully consumed by stdin/stdout/stderr. */
|
||||
assert(errno == ENFILE);
|
||||
printf("STDIO_TEST:NON_BASIC:SATURATED_OK\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int fd1 = open("/dev/console", O_RDWR);
|
||||
int fd2 = open("/dev/console", O_RDWR);
|
||||
assert(fd0 >= 0);
|
||||
assert(fd1 >= 0);
|
||||
assert(fd2 >= 0);
|
||||
assert(fd0 != fd1 && fd1 != fd2 && fd0 != fd2);
|
||||
printf("STDIO_TEST:NON_BASIC:UNIQUE_OK\n");
|
||||
|
||||
assert(close(fd1) == 0);
|
||||
int fd_reused = open("/dev/console", O_RDWR);
|
||||
assert(fd_reused == fd1);
|
||||
printf("STDIO_TEST:NON_BASIC:REUSE_OK\n");
|
||||
assert(close(fd0) == 0);
|
||||
assert(close(fd2) == 0);
|
||||
assert(close(fd_reused) == 0);
|
||||
|
||||
int fd_ro = open("/dev/console", O_RDONLY);
|
||||
assert(fd_ro >= 0);
|
||||
errno = 0;
|
||||
assert(write(fd_ro, "x", 1) < 0);
|
||||
assert(errno == EBADF);
|
||||
assert(close(fd_ro) == 0);
|
||||
|
||||
int fd_wo = open("/dev/console", O_WRONLY);
|
||||
assert(fd_wo >= 0);
|
||||
errno = 0;
|
||||
char tmp = 0;
|
||||
assert(read(fd_wo, &tmp, 1) < 0);
|
||||
assert(errno == EBADF);
|
||||
assert(close(fd_wo) == 0);
|
||||
|
||||
int fd_rw = open("/dev/console", O_RDWR);
|
||||
assert(fd_rw >= 0);
|
||||
const char *wmsg = "z";
|
||||
ssize_t wret = write(fd_rw, wmsg, 1);
|
||||
assert(wret == 1);
|
||||
assert(close(fd_rw) == 0);
|
||||
printf("STDIO_TEST:NON_BASIC:FLAGS_OK\n");
|
||||
|
||||
int fd_fsync = open("/dev/console", O_RDWR);
|
||||
assert(fd_fsync >= 0);
|
||||
assert(fsync(fd_fsync) == 0);
|
||||
assert(close(fd_fsync) == 0);
|
||||
printf("STDIO_TEST:NON_BASIC:FSYNC_OK\n");
|
||||
|
||||
int fds[CONFIG_ESP_STDIO_MAX_FDS + 2];
|
||||
int opened = 0;
|
||||
while (opened < (int)(sizeof(fds) / sizeof(fds[0]))) {
|
||||
int fd = open("/dev/console", O_RDWR);
|
||||
if (fd < 0) {
|
||||
break;
|
||||
}
|
||||
fds[opened++] = fd;
|
||||
}
|
||||
assert(opened >= 0);
|
||||
assert(opened <= CONFIG_ESP_STDIO_MAX_FDS);
|
||||
errno = 0;
|
||||
assert(open("/dev/console", O_RDWR) < 0);
|
||||
assert(errno == ENFILE);
|
||||
printf("STDIO_TEST:NON_BASIC:LIMIT_OK\n");
|
||||
|
||||
for (int i = 0; i < opened; ++i) {
|
||||
assert(close(fds[i]) == 0);
|
||||
}
|
||||
|
||||
const char *msg = "x";
|
||||
errno = 0;
|
||||
assert(write(-1, msg, 1) < 0);
|
||||
assert(errno == EBADF);
|
||||
printf("STDIO_TEST:NON_BASIC:EBADF_OK\n");
|
||||
#endif
|
||||
printf("STDIO_TEST:CLOSE_OK\n");
|
||||
}
|
||||
|
||||
static void stdio_fd_init_check(void)
|
||||
|
||||
@@ -97,13 +97,11 @@ def test_esp_system_stdio_correct_open_and_close(dut: Dut) -> None:
|
||||
dut.expect('This should be printed to stdout')
|
||||
dut.expect('Closing /dev/console')
|
||||
dut.expect('This should be printed to stdout')
|
||||
dut.expect('STDIO_TEST:MODE=NON_BASIC')
|
||||
dut.expect('STDIO_TEST:NON_BASIC:UNIQUE_OK')
|
||||
dut.expect('STDIO_TEST:NON_BASIC:REUSE_OK')
|
||||
dut.expect('STDIO_TEST:NON_BASIC:FLAGS_OK')
|
||||
dut.expect('STDIO_TEST:NON_BASIC:FSYNC_OK')
|
||||
dut.expect('STDIO_TEST:NON_BASIC:LIMIT_OK')
|
||||
dut.expect('STDIO_TEST:NON_BASIC:EBADF_OK')
|
||||
dut.expect('STDIO_TEST:MODE=FORWARDING')
|
||||
dut.expect('STDIO_TEST:UNIQUE_FDS_OK')
|
||||
dut.expect('STDIO_TEST:WRITE_OK')
|
||||
dut.expect('STDIO_TEST:FSYNC_OK')
|
||||
dut.expect('STDIO_TEST:CLOSE_OK')
|
||||
|
||||
|
||||
@pytest.mark.host_test
|
||||
@@ -117,74 +115,8 @@ def test_esp_stdio_non_basic_default_qemu(dut: Dut) -> None:
|
||||
dut.expect('Hello World')
|
||||
dut.expect('STDIO_TEST:FD_INIT:IN=0,OUT=1,ERR=2')
|
||||
dut.expect('STDIO_TEST:SELECT:LOGICAL_FD_MAP_OK')
|
||||
dut.expect('STDIO_TEST:MODE=NON_BASIC')
|
||||
dut.expect('STDIO_TEST:NON_BASIC:UNIQUE_OK')
|
||||
dut.expect('STDIO_TEST:NON_BASIC:REUSE_OK')
|
||||
dut.expect('STDIO_TEST:NON_BASIC:FLAGS_OK')
|
||||
dut.expect('STDIO_TEST:NON_BASIC:FSYNC_OK')
|
||||
dut.expect('STDIO_TEST:NON_BASIC:LIMIT_OK')
|
||||
dut.expect('STDIO_TEST:NON_BASIC:EBADF_OK')
|
||||
|
||||
|
||||
@pytest.mark.host_test
|
||||
@pytest.mark.qemu
|
||||
@pytest.mark.parametrize(
|
||||
'config, embedded_services',
|
||||
[('stdio_non_basic_small_fd', 'idf,qemu')],
|
||||
)
|
||||
@idf_parametrize('target', ['esp32'], indirect=['target'])
|
||||
def test_esp_stdio_non_basic_small_fd_mode_qemu(dut: Dut) -> None:
|
||||
dut.expect('Hello World')
|
||||
dut.expect('STDIO_TEST:FD_INIT:IN=0,OUT=1,ERR=2')
|
||||
dut.expect('STDIO_TEST:SELECT:LOGICAL_FD_MAP_OK')
|
||||
dut.expect('STDIO_TEST:MODE=NON_BASIC')
|
||||
dut.expect('STDIO_TEST:NON_BASIC:SATURATED_OK')
|
||||
|
||||
|
||||
@pytest.mark.host_test
|
||||
@pytest.mark.qemu
|
||||
@pytest.mark.parametrize(
|
||||
'config, embedded_services',
|
||||
[('stdio_basic_mode', 'idf,qemu')],
|
||||
)
|
||||
@idf_parametrize('target', ['esp32'], indirect=['target'])
|
||||
def test_esp_stdio_basic_mode_qemu(dut: Dut) -> None:
|
||||
dut.expect('Hello World')
|
||||
dut.expect('STDIO_TEST:FD_INIT:IN=0,OUT=1,ERR=2')
|
||||
dut.expect('STDIO_TEST:SELECT:LOGICAL_FD_MAP_OK')
|
||||
dut.expect('STDIO_TEST:MODE=BASIC')
|
||||
dut.expect('STDIO_TEST:BASIC:OPEN_OK')
|
||||
dut.expect('STDIO_TEST:BASIC:WRITE_OK')
|
||||
dut.expect('STDIO_TEST:BASIC:CLOSE_OK')
|
||||
dut.expect('STDIO_TEST:BASIC:EBADF_OK')
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@idf_parametrize('config', ['stdio_non_basic_small_fd'], indirect=['config'])
|
||||
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
|
||||
@pytest.mark.temp_skip_ci(targets=['esp32p4'], reason='p4 rev3 migration')
|
||||
def test_esp_stdio_non_basic_small_fd_mode(dut: Dut) -> None:
|
||||
dut.expect('2nd stage bootloader')
|
||||
_expect_app_main_banner(dut)
|
||||
dut.expect('Hello World')
|
||||
dut.expect('STDIO_TEST:FD_INIT:IN=0,OUT=1,ERR=2')
|
||||
dut.expect('STDIO_TEST:SELECT:LOGICAL_FD_MAP_OK')
|
||||
dut.expect('STDIO_TEST:MODE=NON_BASIC')
|
||||
dut.expect('STDIO_TEST:NON_BASIC:SATURATED_OK')
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@idf_parametrize('config', ['stdio_basic_mode'], indirect=['config'])
|
||||
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
|
||||
@pytest.mark.temp_skip_ci(targets=['esp32p4'], reason='p4 rev3 migration')
|
||||
def test_esp_stdio_basic_mode(dut: Dut) -> None:
|
||||
dut.expect('2nd stage bootloader')
|
||||
_expect_app_main_banner(dut)
|
||||
dut.expect('Hello World')
|
||||
dut.expect('STDIO_TEST:FD_INIT:IN=0,OUT=1,ERR=2')
|
||||
dut.expect('STDIO_TEST:SELECT:LOGICAL_FD_MAP_OK')
|
||||
dut.expect('STDIO_TEST:MODE=BASIC')
|
||||
dut.expect('STDIO_TEST:BASIC:OPEN_OK')
|
||||
dut.expect('STDIO_TEST:BASIC:WRITE_OK')
|
||||
dut.expect('STDIO_TEST:BASIC:CLOSE_OK')
|
||||
dut.expect('STDIO_TEST:BASIC:EBADF_OK')
|
||||
dut.expect('STDIO_TEST:MODE=FORWARDING')
|
||||
dut.expect('STDIO_TEST:UNIQUE_FDS_OK')
|
||||
dut.expect('STDIO_TEST:WRITE_OK')
|
||||
dut.expect('STDIO_TEST:FSYNC_OK')
|
||||
dut.expect('STDIO_TEST:CLOSE_OK')
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
CONFIG_ESP_STDIO_BASIC_MODE=y
|
||||
@@ -1,2 +0,0 @@
|
||||
CONFIG_ESP_STDIO_BASIC_MODE=n
|
||||
CONFIG_ESP_STDIO_MAX_FDS=3
|
||||
Reference in New Issue
Block a user