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:
commit
2c65313a72
6 changed files with 224 additions and 62 deletions
|
@ -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
12
components/vfs/Kconfig
Normal 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
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue