Merge branch 'bugfix/vfs_select' into 'master'

VFS select: Bugfixes and debug output

See merge request idf/esp-idf!2459
This commit is contained in:
Ivan Grokhotkov 2018-05-29 20:27:10 +08:00
commit 2c65313a72
6 changed files with 224 additions and 62 deletions

View File

@ -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

12
components/vfs/Kconfig Normal file
View File

@ -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

View File

@ -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"

View File

@ -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);
}

View File

@ -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

View File

@ -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()