fix(esp_stdio): remap console select fd bits to sink device

Map logical /dev/console fds to the primary sink fd for select.
This avoids probing UART1/UART2 for stdout/stderr and keeps UART select state consistent.
Add stdio regression checks for logical fd mapping and symmetric uart_end_select teardown.
This commit is contained in:
Tomáš Rohlínek
2026-05-27 11:37:56 +02:00
parent c17caf8d23
commit 52004409ce
4 changed files with 192 additions and 8 deletions
@@ -19,6 +19,7 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>
#if CONFIG_ESP_CONSOLE_NONE
/* Set up UART on UART_0 (console) to be able to
@@ -171,6 +172,37 @@ static void stdio_fd_init_check(void)
fprintf(stderr, "STDIO_TEST:STREAM:STDERR\n");
fflush(stderr);
}
#if CONFIG_VFS_SUPPORT_SELECT && CONFIG_ESP_CONSOLE_UART
static void stdio_select_fd_mapping_check(void)
{
bool driver_installed_here = false;
esp_err_t err = uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM, 256, 0, 0, NULL, 0);
if (err == ESP_OK) {
driver_installed_here = true;
} else {
assert(err == ESP_ERR_INVALID_STATE);
}
fd_set writefds;
FD_ZERO(&writefds);
FD_SET(fileno(stdout), &writefds);
FD_SET(fileno(stderr), &writefds);
struct timeval tv = {
.tv_sec = 0,
.tv_usec = 0,
};
errno = 0;
int ret = select(fileno(stderr) + 1, NULL, &writefds, NULL, &tv);
assert(ret >= 0);
if (driver_installed_here) {
assert(uart_driver_delete(CONFIG_ESP_CONSOLE_UART_NUM) == ESP_OK);
}
printf("STDIO_TEST:SELECT:LOGICAL_FD_MAP_OK\n");
}
#endif
#endif // CONFIG_VFS_SUPPORT_IO
void app_main(void)
@@ -188,6 +220,9 @@ void app_main(void)
close(fd);
stdio_fd_init_check();
#if CONFIG_VFS_SUPPORT_SELECT && CONFIG_ESP_CONSOLE_UART
stdio_select_fd_mapping_check();
#endif
#endif // CONFIG_VFS_SUPPORT_IO
#if CONFIG_ESP_CONSOLE_NONE
@@ -92,6 +92,7 @@ def test_esp_system_stdio_correct_open_and_close(dut: Dut) -> None:
dut.expect('STDIO_TEST:FD_INIT:IN=0,OUT=1,ERR=2')
dut.expect('STDIO_TEST:STREAM:STDOUT')
dut.expect('STDIO_TEST:STREAM:STDERR')
dut.expect('STDIO_TEST:SELECT:LOGICAL_FD_MAP_OK')
dut.expect('Opening /dev/console')
dut.expect('This should be printed to stdout')
dut.expect('Closing /dev/console')
@@ -113,6 +114,7 @@ def test_esp_system_stdio_correct_open_and_close(dut: Dut) -> None:
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')
@@ -130,6 +132,7 @@ def test_esp_stdio_non_basic_default_qemu(dut: Dut) -> None:
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')
@@ -144,6 +147,7 @@ def test_esp_stdio_non_basic_small_fd_mode_qemu(dut: Dut) -> None:
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')
@@ -160,6 +164,7 @@ def test_esp_stdio_non_basic_small_fd_mode(dut: Dut) -> None:
_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')
@@ -173,6 +178,7 @@ def test_esp_stdio_basic_mode(dut: Dut) -> None:
_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')