diff --git a/components/newlib/select.c b/components/newlib/select.c index e802a4d32..606315065 100644 --- a/components/newlib/select.c +++ b/components/newlib/select.c @@ -18,12 +18,46 @@ #ifdef CONFIG_USE_ONLY_LWIP_SELECT #include "lwip/sockets.h" -#endif + +#ifdef CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT +#define LOG_LOCAL_LEVEL ESP_LOG_NONE +#endif //CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT +#include "esp_log.h" + +static const char *TAG = "newlib_select"; + +static void log_fd_set(const char *fds_name, const fd_set *fds) +{ + if (fds_name && fds) { + ESP_LOGD(TAG, "FDs in %s =", fds_name); + for (int i = 0; i < MAX_FDS; ++i) { + if (FD_ISSET(i, fds)) { + ESP_LOGD(TAG, "%d", i); + } + } + } +} +#endif //CONFIG_USE_ONLY_LWIP_SELECT int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout) { #ifdef CONFIG_USE_ONLY_LWIP_SELECT - return lwip_select(nfds, readfds, writefds, errorfds, timeout); + ESP_LOGD(TAG, "lwip_select starts with nfds = %d", nfds); + if (timeout) { + ESP_LOGD(TAG, "timeout is %lds + %ldus", timeout->tv_sec, timeout->tv_usec); + } + log_fd_set("readfds", readfds); + log_fd_set("writefds", writefds); + log_fd_set("errorfds", errorfds); + + int ret = lwip_select(nfds, readfds, writefds, errorfds, timeout); + + ESP_LOGD(TAG, "lwip_select returns %d", ret); + log_fd_set("readfds", readfds); + log_fd_set("writefds", writefds); + log_fd_set("errorfds", errorfds); + + return ret; #else return esp_vfs_select(nfds, readfds, writefds, errorfds, timeout); #endif diff --git a/components/vfs/Kconfig b/components/vfs/Kconfig new file mode 100644 index 000000000..d3d4ae9ad --- /dev/null +++ b/components/vfs/Kconfig @@ -0,0 +1,12 @@ +menu "Virtual file system" + +config SUPPRESS_SELECT_DEBUG_OUTPUT + bool "Suppress select() related debug outputs" + default y + help + Select() related functions might produce an unconveniently lot of + debug outputs when one sets the default log level to DEBUG or higher. + It is possible to suppress these debug outputs by enabling this + option. + +endmenu diff --git a/components/vfs/include/esp_vfs.h b/components/vfs/include/esp_vfs.h index d06dd223f..61b01b76f 100644 --- a/components/vfs/include/esp_vfs.h +++ b/components/vfs/include/esp_vfs.h @@ -175,7 +175,7 @@ typedef struct int (*access)(const char *path, int amode); }; /** start_select is called for setting up synchronous I/O multiplexing of the desired file descriptors in the given VFS */ - esp_err_t (*start_select)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds); + esp_err_t (*start_select)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, SemaphoreHandle_t *signal_sem); /** socket select function for socket FDs with the functionality of POSIX select(); this should be set only for the socket VFS */ int (*socket_select)(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout); /** called by VFS to interrupt the socket_select call when select is activated from a non-socket VFS driver; set only for the socket driver */ @@ -326,8 +326,10 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds * * This function is called when the VFS driver detects a read/write/error * condition as it was requested by the previous call to start_select. + * + * @param signal_sem semaphore handle which was passed to the driver by the start_select call */ -void esp_vfs_select_triggered(); +void esp_vfs_select_triggered(SemaphoreHandle_t *signal_sem); /** * @brief Notification from a VFS driver about a read/write/error condition (ISR version) @@ -335,9 +337,10 @@ void esp_vfs_select_triggered(); * This function is called when the VFS driver detects a read/write/error * condition as it was requested by the previous call to start_select. * + * @param signal_sem semaphore handle which was passed to the driver by the start_select call * @param woken is set to pdTRUE if the function wakes up a task with higher priority */ -void esp_vfs_select_triggered_isr(BaseType_t *woken); +void esp_vfs_select_triggered_isr(SemaphoreHandle_t *signal_sem, BaseType_t *woken); #ifdef __cplusplus } // extern "C" diff --git a/components/vfs/test/test_vfs_select.c b/components/vfs/test/test_vfs_select.c index be74ebe91..f9927a54f 100644 --- a/components/vfs/test/test_vfs_select.c +++ b/components/vfs/test/test_vfs_select.c @@ -275,8 +275,12 @@ static void select_task(void *param) .tv_usec = 100000, }; - int s = select(1, NULL, NULL, NULL, &tv); - TEST_ASSERT_EQUAL(s, 0); //timeout + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(test_task_param->fd, &rfds); + + int s = select(test_task_param->fd + 1, &rfds, NULL, NULL, &tv); + TEST_ASSERT_EQUAL(0, s); //timeout if (test_task_param->sem) { xSemaphoreGive(test_task_param->sem); @@ -284,23 +288,52 @@ static void select_task(void *param) vTaskDelete(NULL); } -TEST_CASE("concurent select() fails", "[vfs]") +TEST_CASE("concurent selects work", "[vfs]") { struct timeval tv = { .tv_sec = 0, .tv_usec = 100000,//irrelevant }; - const test_task_param_t test_task_param = { + + int uart_fd, socket_fd; + const int dummy_socket_fd = open_dummy_socket(); + init(&uart_fd, &socket_fd); + + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(uart_fd, &rfds); + + test_task_param_t test_task_param = { + .fd = uart_fd, .sem = xSemaphoreCreateBinary(), }; TEST_ASSERT_NOT_NULL(test_task_param.sem); + xTaskCreate(select_task, "select_task", 4*1024, (void *) &test_task_param, 5, NULL); vTaskDelay(10 / portTICK_PERIOD_MS); //make sure the task has started and waits in select() - int s = select(1, NULL, NULL, NULL, &tv); - TEST_ASSERT_EQUAL(s, -1); //this select should fail because the other one is "waiting" - TEST_ASSERT_EQUAL(errno, EINTR); + int s = select(uart_fd + 1, &rfds, NULL, NULL, &tv); + TEST_ASSERT_EQUAL(-1, s); //this select should fail because two selects are accessing UART + //(the other one is waiting for the timeout) + TEST_ASSERT_EQUAL(EINTR, errno); - TEST_ASSERT_EQUAL(xSemaphoreTake(test_task_param.sem, 1000 / portTICK_PERIOD_MS), pdTRUE); + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(test_task_param.sem, 1000 / portTICK_PERIOD_MS)); + + FD_ZERO(&rfds); + FD_SET(socket_fd, &rfds); + + test_task_param.fd = dummy_socket_fd; + + xTaskCreate(select_task, "select_task", 4*1024, (void *) &test_task_param, 5, NULL); + vTaskDelay(10 / portTICK_PERIOD_MS); //make sure the task has started and waits in select() + + s = select(socket_fd + 1, &rfds, NULL, NULL, &tv); + TEST_ASSERT_EQUAL(0, s); //this select should timeout as well as the concurrent one because + //concurrent socket select should work + + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(test_task_param.sem, 1000 / portTICK_PERIOD_MS)); vSemaphoreDelete(test_task_param.sem); + + deinit(uart_fd, socket_fd); + close(dummy_socket_fd); } diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c index a5f3c74c7..778db912a 100644 --- a/components/vfs/vfs.c +++ b/components/vfs/vfs.c @@ -25,8 +25,15 @@ #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "esp_vfs.h" +#include "sdkconfig.h" + +#ifdef CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT +#define LOG_LOCAL_LEVEL ESP_LOG_NONE +#endif //CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT #include "esp_log.h" +static const char *TAG = "vfs"; + #define VFS_MAX_COUNT 8 /* max number of VFS entries (registered filesystems) */ #define LEN_PATH_PREFIX_IGNORED SIZE_MAX /* special length value for VFS which is never recognised by open() */ #define FD_TABLE_ENTRY_UNUSED (fd_table_t) { .permanent = false, .vfs_index = -1, .local_fd = -1 } @@ -65,15 +72,6 @@ static size_t s_vfs_count = 0; static fd_table_t s_fd_table[MAX_FDS] = { [0 ... MAX_FDS-1] = FD_TABLE_ENTRY_UNUSED }; static _lock_t s_fd_table_lock; -/* Semaphore used for waiting select events from other VFS drivers when socket - * select is not used (not registered or socket FDs are not observed by the - * given call of select) - */ -static SemaphoreHandle_t s_select_sem = NULL; - -/* Lock ensuring that select is called from only one task at the time */ -static _lock_t s_one_select_lock; - static esp_err_t esp_vfs_register_common(const char* base_path, size_t len, const esp_vfs_t* vfs, void* ctx, int *vfs_index) { if (len != LEN_PATH_PREFIX_IGNORED) { @@ -127,6 +125,7 @@ esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ct esp_err_t esp_vfs_register_fd_range(const esp_vfs_t *vfs, void *ctx, int min_fd, int max_fd) { if (min_fd < 0 || max_fd < 0 || min_fd > MAX_FDS || max_fd > MAX_FDS || min_fd > max_fd) { + ESP_LOGD(TAG, "Invalid arguments: esp_vfs_register_fd_range(0x%x, 0x%x, %d, %d)", (int) vfs, (int) ctx, min_fd, max_fd); return ESP_ERR_INVALID_ARG; } @@ -145,6 +144,7 @@ esp_err_t esp_vfs_register_fd_range(const esp_vfs_t *vfs, void *ctx, int min_fd, } } _lock_release(&s_fd_table_lock); + ESP_LOGD(TAG, "esp_vfs_register_fd_range cannot set fd %d (used by other VFS)", i); return ESP_ERR_INVALID_ARG; } s_fd_table[i].permanent = true; @@ -154,6 +154,8 @@ esp_err_t esp_vfs_register_fd_range(const esp_vfs_t *vfs, void *ctx, int min_fd, _lock_release(&s_fd_table_lock); } + ESP_LOGD(TAG, "esp_vfs_register_fd_range is successful for range <%d; %d) and VFS ID %d", min_fd, max_fd, index); + return ret; } @@ -196,6 +198,7 @@ esp_err_t esp_vfs_unregister(const char* base_path) esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int *fd) { if (vfs_id < 0 || vfs_id >= s_vfs_count || fd == NULL) { + ESP_LOGD(TAG, "Invalid arguments for esp_vfs_register_fd(%d, 0x%x)", vfs_id, (int) fd); return ESP_ERR_INVALID_ARG; } @@ -213,6 +216,8 @@ esp_err_t esp_vfs_register_fd(esp_vfs_id_t vfs_id, int *fd) } _lock_release(&s_fd_table_lock); + ESP_LOGD(TAG, "esp_vfs_register_fd(%d, 0x%x) finished with %s", vfs_id, (int) fd, esp_err_to_name(ret)); + return ret; } @@ -221,6 +226,7 @@ esp_err_t esp_vfs_unregister_fd(esp_vfs_id_t vfs_id, int fd) esp_err_t ret = ESP_ERR_INVALID_ARG; if (vfs_id < 0 || vfs_id >= s_vfs_count || fd < 0 || fd >= MAX_FDS) { + ESP_LOGD(TAG, "Invalid arguments for esp_vfs_unregister_fd(%d, %d)", vfs_id, fd); return ret; } @@ -232,6 +238,8 @@ esp_err_t esp_vfs_unregister_fd(esp_vfs_id_t vfs_id, int fd) } _lock_release(&s_fd_table_lock); + ESP_LOGD(TAG, "esp_vfs_unregister_fd(%d, %d) finished with %s", vfs_id, fd, esp_err_to_name(ret)); + return ret; } @@ -390,7 +398,7 @@ int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode) _lock_release(&s_fd_table_lock); int ret; CHECK_AND_CALL(ret, r, vfs, close, fd_within_vfs); - (void) ret; // remove "set but not used" warning + (void) ret; // remove "set but not used" warning __errno_r(r) = ENOMEM; return -1; } @@ -720,6 +728,11 @@ static void call_end_selects(int end_index, const fds_triple_t *vfs_fds_triple) } } +static inline bool esp_vfs_safe_fd_isset(int fd, const fd_set *fds) +{ + return fds && FD_ISSET(fd, fds); +} + static int set_global_fd_sets(const fds_triple_t *vfs_fds_triple, int size, fd_set *readfds, fd_set *writefds, fd_set *errorfds) { int ret = 0; @@ -729,15 +742,18 @@ static int set_global_fd_sets(const fds_triple_t *vfs_fds_triple, int size, fd_s if (item->isset) { for (int fd = 0; fd < MAX_FDS; ++fd) { const int local_fd = s_fd_table[fd].local_fd; // single read -> no locking is required - if (readfds && FD_ISSET(local_fd, &item->readfds)) { + if (readfds && esp_vfs_safe_fd_isset(local_fd, &item->readfds)) { + ESP_LOGD(TAG, "FD %d in readfds was set from VFS ID %d", fd, i); FD_SET(fd, readfds); ++ret; } - if (writefds && FD_ISSET(local_fd, &item->writefds)) { + if (writefds && esp_vfs_safe_fd_isset(local_fd, &item->writefds)) { + ESP_LOGD(TAG, "FD %d in writefds was set from VFS ID %d", fd, i); FD_SET(fd, writefds); ++ret; } - if (errorfds && FD_ISSET(local_fd, &item->errorfds)) { + if (errorfds && esp_vfs_safe_fd_isset(local_fd, &item->errorfds)) { + ESP_LOGD(TAG, "FD %d in errorfds was set from VFS ID %d", fd, i); FD_SET(fd, errorfds); ++ret; } @@ -748,25 +764,41 @@ static int set_global_fd_sets(const fds_triple_t *vfs_fds_triple, int size, fd_s return ret; } +static void esp_vfs_log_fd_set(const char *fds_name, const fd_set *fds) +{ + if (fds_name && fds) { + ESP_LOGD(TAG, "FDs in %s =", fds_name); + for (int i = 0; i < MAX_FDS; ++i) { + if (esp_vfs_safe_fd_isset(i, fds)) { + ESP_LOGD(TAG, "%d", i); + } + } + } +} + int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout) { int ret = 0; struct _reent* r = __getreent(); - if (nfds > MAX_FDS || nfds < 0) { - __errno_r(r) = EINVAL; - return -1; + ESP_LOGD(TAG, "esp_vfs_select starts with nfds = %d", nfds); + if (timeout) { + ESP_LOGD(TAG, "timeout is %lds + %ldus", timeout->tv_sec, timeout->tv_usec); } + esp_vfs_log_fd_set("readfds", readfds); + esp_vfs_log_fd_set("writefds", writefds); + esp_vfs_log_fd_set("errorfds", errorfds); - if (_lock_try_acquire(&s_one_select_lock)) { - __errno_r(r) = EINTR; + if (nfds > MAX_FDS || nfds < 0) { + ESP_LOGD(TAG, "incorrect nfds"); + __errno_r(r) = EINVAL; return -1; } fds_triple_t *vfs_fds_triple; if ((vfs_fds_triple = calloc(s_vfs_count, sizeof(fds_triple_t))) == NULL) { __errno_r(r) = ENOMEM; - _lock_release(&s_one_select_lock); + ESP_LOGD(TAG, "calloc is unsuccessful"); return -1; } @@ -785,9 +817,9 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds if (is_socket_fd) { if (!socket_select) { // no socket_select found yet so take a look - if ((readfds && FD_ISSET(fd, readfds)) || - (writefds && FD_ISSET(fd, writefds)) || - (errorfds && FD_ISSET(fd, errorfds))) { + if (esp_vfs_safe_fd_isset(fd, readfds) || + esp_vfs_safe_fd_isset(fd, writefds) || + esp_vfs_safe_fd_isset(fd, errorfds)) { const vfs_entry_t *vfs = s_vfs[vfs_index]; socket_select = vfs->vfs.socket_select; } @@ -796,20 +828,23 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds } fds_triple_t *item = &vfs_fds_triple[vfs_index]; // FD sets for VFS which belongs to fd - if (readfds && FD_ISSET(fd, readfds)) { + if (esp_vfs_safe_fd_isset(fd, readfds)) { item->isset = true; FD_SET(local_fd, &item->readfds); FD_CLR(fd, readfds); + ESP_LOGD(TAG, "removing %d from readfds and adding as local FD %d to fd_set of VFS ID %d", fd, local_fd, vfs_index); } - if (writefds && FD_ISSET(fd, writefds)) { + if (esp_vfs_safe_fd_isset(fd, writefds)) { item->isset = true; FD_SET(local_fd, &item->writefds); FD_CLR(fd, writefds); + ESP_LOGD(TAG, "removing %d from writefds and adding as local FD %d to fd_set of VFS ID %d", fd, local_fd, vfs_index); } - if (errorfds && FD_ISSET(fd, errorfds)) { + if (esp_vfs_safe_fd_isset(fd, errorfds)) { item->isset = true; FD_SET(local_fd, &item->errorfds); FD_CLR(fd, errorfds); + ESP_LOGD(TAG, "removing %d from errorfds and adding as local FD %d to fd_set of VFS ID %d", fd, local_fd, vfs_index); } } @@ -817,13 +852,19 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds // the global readfds, writefds and errorfds contain only socket FDs (if // there any) + /* Semaphore used for waiting select events from other VFS drivers when socket + * select is not used (not registered or socket FDs are not observed by the + * given call of select) + */ + SemaphoreHandle_t select_sem = NULL; + if (!socket_select) { // There is no socket VFS registered or select() wasn't called for // any socket. Therefore, we will use our own signalization. - if ((s_select_sem = xSemaphoreCreateBinary()) == NULL) { + if ((select_sem = xSemaphoreCreateBinary()) == NULL) { free(vfs_fds_triple); __errno_r(r) = ENOMEM; - _lock_release(&s_one_select_lock); + ESP_LOGD(TAG, "cannot create select_sem"); return -1; } } @@ -835,25 +876,37 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds if (vfs && vfs->vfs.start_select && item->isset) { // call start_select for all non-socket VFSs with has at least one FD set in readfds, writefds, or errorfds // note: it can point to socket VFS but item->isset will be false for that - esp_err_t err = vfs->vfs.start_select(nfds, &item->readfds, &item->writefds, &item->errorfds); + ESP_LOGD(TAG, "calling start_select for VFS ID %d with the following local FDs", i); + esp_vfs_log_fd_set("readfds", &item->readfds); + esp_vfs_log_fd_set("writefds", &item->writefds); + esp_vfs_log_fd_set("errorfds", &item->errorfds); + esp_err_t err = vfs->vfs.start_select(nfds, &item->readfds, &item->writefds, &item->errorfds, &select_sem); if (err != ESP_OK) { call_end_selects(i, vfs_fds_triple); (void) set_global_fd_sets(vfs_fds_triple, s_vfs_count, readfds, writefds, errorfds); - if (s_select_sem) { - vSemaphoreDelete(s_select_sem); - s_select_sem = NULL; + if (select_sem) { + vSemaphoreDelete(select_sem); + select_sem = NULL; } free(vfs_fds_triple); - __errno_r(r) = ENOMEM; - _lock_release(&s_one_select_lock); + __errno_r(r) = EINTR; + ESP_LOGD(TAG, "start_select failed"); return -1; } } } if (socket_select) { + ESP_LOGD(TAG, "calling socket_select with the following FDs"); + esp_vfs_log_fd_set("readfds", readfds); + esp_vfs_log_fd_set("writefds", writefds); + esp_vfs_log_fd_set("errorfds", errorfds); ret = socket_select(nfds, readfds, writefds, errorfds, timeout); + ESP_LOGD(TAG, "socket_select returned %d and the FDs are the following", ret); + esp_vfs_log_fd_set("readfds", readfds); + esp_vfs_log_fd_set("writefds", writefds); + esp_vfs_log_fd_set("errorfds", errorfds); } else { if (readfds) { FD_ZERO(readfds); @@ -869,27 +922,33 @@ int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds if (timeout) { uint32_t timeout_ms = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; ticks_to_wait = timeout_ms / portTICK_PERIOD_MS; + ESP_LOGD(TAG, "timeout is %dms", timeout_ms); } - xSemaphoreTake(s_select_sem, ticks_to_wait); + ESP_LOGD(TAG, "waiting without calling socket_select"); + xSemaphoreTake(select_sem, ticks_to_wait); } call_end_selects(s_vfs_count, vfs_fds_triple); // for VFSs for start_select was called before if (ret >= 0) { ret += set_global_fd_sets(vfs_fds_triple, s_vfs_count, readfds, writefds, errorfds); } - if (s_select_sem) { - vSemaphoreDelete(s_select_sem); - s_select_sem = NULL; + if (select_sem) { + vSemaphoreDelete(select_sem); + select_sem = NULL; } free(vfs_fds_triple); - _lock_release(&s_one_select_lock); + + ESP_LOGD(TAG, "esp_vfs_select returns %d", ret); + esp_vfs_log_fd_set("readfds", readfds); + esp_vfs_log_fd_set("writefds", writefds); + esp_vfs_log_fd_set("errorfds", errorfds); return ret; } -void esp_vfs_select_triggered() +void esp_vfs_select_triggered(SemaphoreHandle_t *signal_sem) { - if (s_select_sem) { - xSemaphoreGive(s_select_sem); + if (signal_sem && (*signal_sem)) { + xSemaphoreGive(*signal_sem); } else { // Another way would be to go through s_fd_table and find the VFS // which has a permanent FD. But in order to avoid to lock @@ -904,10 +963,10 @@ void esp_vfs_select_triggered() } } -void esp_vfs_select_triggered_isr(BaseType_t *woken) +void esp_vfs_select_triggered_isr(SemaphoreHandle_t *signal_sem, BaseType_t *woken) { - if (s_select_sem) { - xSemaphoreGiveFromISR(s_select_sem, woken); + if (signal_sem && (*signal_sem)) { + xSemaphoreGiveFromISR(*signal_sem, woken); } else { // Another way would be to go through s_fd_table and find the VFS // which has a permanent FD. But in order to avoid to lock diff --git a/components/vfs/vfs_uart.c b/components/vfs/vfs_uart.c index c1c12066c..e52b5dd74 100644 --- a/components/vfs/vfs_uart.c +++ b/components/vfs/vfs_uart.c @@ -58,6 +58,10 @@ static int s_peek_char[UART_NUM] = { NONE, NONE, NONE }; // driver is used. static bool s_non_blocking[UART_NUM]; +/* Lock ensuring that uart_select is used from only one task at the time */ +static _lock_t s_one_select_lock; + +static SemaphoreHandle_t *_signal_sem = NULL; static fd_set *_readfds = NULL; static fd_set *_writefds = NULL; static fd_set *_errorfds = NULL; @@ -85,6 +89,8 @@ static esp_line_endings_t s_rx_mode = ESP_LINE_ENDINGS_LF; #endif +static void uart_end_select(); + // Functions used to write bytes to UART. Default to "basic" functions. static tx_func_t s_uart_tx_func[UART_NUM] = { &uart_tx_char, &uart_tx_char, &uart_tx_char @@ -303,47 +309,55 @@ static void select_notif_callback(uart_port_t uart_num, uart_select_notif_t uart case UART_SELECT_READ_NOTIF: if (FD_ISSET(uart_num, _readfds_orig)) { FD_SET(uart_num, _readfds); - esp_vfs_select_triggered_isr(task_woken); + esp_vfs_select_triggered_isr(_signal_sem, task_woken); } break; case UART_SELECT_WRITE_NOTIF: if (FD_ISSET(uart_num, _writefds_orig)) { FD_SET(uart_num, _writefds); - esp_vfs_select_triggered_isr(task_woken); + esp_vfs_select_triggered_isr(_signal_sem, task_woken); } break; case UART_SELECT_ERROR_NOTIF: if (FD_ISSET(uart_num, _errorfds_orig)) { FD_SET(uart_num, _errorfds); - esp_vfs_select_triggered_isr(task_woken); + esp_vfs_select_triggered_isr(_signal_sem, task_woken); } break; } } -static esp_err_t uart_start_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds) +static esp_err_t uart_start_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, SemaphoreHandle_t *signal_sem) { + if (_lock_try_acquire(&s_one_select_lock)) { + return ESP_ERR_INVALID_STATE; + } + const int max_fds = MIN(nfds, UART_NUM); taskENTER_CRITICAL(uart_get_selectlock()); - if (_readfds || _writefds || _errorfds || _readfds_orig || _writefds_orig || _errorfds_orig) { + if (_readfds || _writefds || _errorfds || _readfds_orig || _writefds_orig || _errorfds_orig || _signal_sem) { taskEXIT_CRITICAL(uart_get_selectlock()); + uart_end_select(); return ESP_ERR_INVALID_STATE; } if ((_readfds_orig = malloc(sizeof(fd_set))) == NULL) { taskEXIT_CRITICAL(uart_get_selectlock()); + uart_end_select(); return ESP_ERR_NO_MEM; } if ((_writefds_orig = malloc(sizeof(fd_set))) == NULL) { taskEXIT_CRITICAL(uart_get_selectlock()); + uart_end_select(); return ESP_ERR_NO_MEM; } if ((_errorfds_orig = malloc(sizeof(fd_set))) == NULL) { taskEXIT_CRITICAL(uart_get_selectlock()); + uart_end_select(); return ESP_ERR_NO_MEM; } @@ -354,6 +368,8 @@ static esp_err_t uart_start_select(int nfds, fd_set *readfds, fd_set *writefds, } } + _signal_sem = signal_sem; + _readfds = readfds; _writefds = writefds; _errorfds = exceptfds; @@ -367,6 +383,8 @@ static esp_err_t uart_start_select(int nfds, fd_set *readfds, fd_set *writefds, FD_ZERO(exceptfds); taskEXIT_CRITICAL(uart_get_selectlock()); + // s_one_select_lock is not released on successfull exit - will be + // released in uart_end_select() return ESP_OK; } @@ -378,6 +396,8 @@ static void uart_end_select() uart_set_select_notif_callback(i, NULL); } + _signal_sem = NULL; + _readfds = NULL; _writefds = NULL; _errorfds = NULL; @@ -397,6 +417,7 @@ static void uart_end_select() _errorfds_orig = NULL; } taskEXIT_CRITICAL(uart_get_selectlock()); + _lock_release(&s_one_select_lock); } void esp_vfs_dev_uart_register()