mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
VFS: Implement poll() based on select()
Closes https://github.com/espressif/esp-idf/issues/2945
This commit is contained in:
parent
e9a764d9a5
commit
1d4eeeb464
@ -3,6 +3,7 @@ set(COMPONENT_SRCS "locks.c"
|
||||
"random.c"
|
||||
"reent_init.c"
|
||||
"select.c"
|
||||
"poll.c"
|
||||
"syscall_table.c"
|
||||
"syscalls.c"
|
||||
"termios.c"
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -11,22 +11,38 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef _ESP_PLATFORM_SYS_POLL_H_
|
||||
#define _ESP_PLATFORM_SYS_POLL_H_
|
||||
|
||||
#define POLLIN 0x0001 /* any readable data available */
|
||||
#define POLLOUT 0x0004 /* file descriptor is writeable */
|
||||
#define POLLPRI 0x0002 /* OOB/Urgent readable data */
|
||||
#define POLLERR 0x0008 /* some poll error occurred */
|
||||
#define POLLHUP 0x0010 /* file descriptor was "hung up" */
|
||||
#define POLLIN (1u << 0) /* data other than high-priority may be read without blocking */
|
||||
#define POLLRDNORM (1u << 1) /* normal data may be read without blocking */
|
||||
#define POLLRDBAND (1u << 2) /* priority data may be read without blocking */
|
||||
#define POLLPRI (POLLRDBAND) /* high-priority data may be read without blocking */
|
||||
// Note: POLLPRI is made equivalent to POLLRDBAND in order to fit all these events into one byte
|
||||
#define POLLOUT (1u << 3) /* normal data may be written without blocking */
|
||||
#define POLLWRNORM (POLLOUT) /* equivalent to POLLOUT */
|
||||
#define POLLWRBAND (1u << 4) /* priority data my be written */
|
||||
#define POLLERR (1u << 5) /* some poll error occurred */
|
||||
#define POLLHUP (1u << 6) /* file descriptor was "hung up" */
|
||||
#define POLLNVAL (1u << 7) /* the specified file descriptor is invalid */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct pollfd {
|
||||
int fd; /* The descriptor. */
|
||||
short events; /* The event(s) is/are specified here. */
|
||||
short revents; /* Events found are returned here. */
|
||||
int fd; /* The descriptor. */
|
||||
short events; /* The event(s) is/are specified here. */
|
||||
short revents; /* Events found are returned here. */
|
||||
};
|
||||
|
||||
typedef unsigned int nfds_t;
|
||||
|
||||
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // _ESP_PLATFORM_SYS_POLL_H_
|
||||
|
21
components/newlib/poll.c
Normal file
21
components/newlib/poll.c
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <sys/poll.h>
|
||||
#include "esp_vfs.h"
|
||||
|
||||
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
|
||||
{
|
||||
return esp_vfs_poll(fds, nfds, timeout);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -27,6 +27,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/termios.h>
|
||||
#include <sys/poll.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
@ -379,6 +380,22 @@ void esp_vfs_select_triggered(SemaphoreHandle_t *signal_sem);
|
||||
*/
|
||||
void esp_vfs_select_triggered_isr(SemaphoreHandle_t *signal_sem, BaseType_t *woken);
|
||||
|
||||
/**
|
||||
* @brief Implements the VFS layer for synchronous I/O multiplexing by poll()
|
||||
*
|
||||
* The implementation is based on esp_vfs_select. The parameters and return values are compatible with POSIX poll().
|
||||
*
|
||||
* @param fds Pointer to the array containing file descriptors and events poll() should consider.
|
||||
* @param nfds Number of items in the array fds.
|
||||
* @param timeout Poll() should wait at least timeout milliseconds. If the value is 0 then it should return
|
||||
* immediately. If the value is -1 then it should wait (block) until the event occurs.
|
||||
*
|
||||
* @return A positive return value indicates the number of file descriptors that have been selected. The 0
|
||||
* return value indicates a timed-out poll. -1 is return on failure and errno is set accordingly.
|
||||
*
|
||||
*/
|
||||
int esp_vfs_poll(struct pollfd *fds, nfds_t nfds, int timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -195,6 +195,67 @@ TEST_CASE("UART can do select()", "[vfs]")
|
||||
deinit(uart_fd, socket_fd);
|
||||
}
|
||||
|
||||
TEST_CASE("UART can do poll()", "[vfs]")
|
||||
{
|
||||
int uart_fd;
|
||||
int socket_fd;
|
||||
char recv_message[sizeof(message)];
|
||||
|
||||
init(&uart_fd, &socket_fd);
|
||||
|
||||
struct pollfd poll_fds[] = {
|
||||
{
|
||||
.fd = uart_fd,
|
||||
.events = POLLIN,
|
||||
},
|
||||
{
|
||||
.fd = -1, // should be ignored according to the documentation of poll()
|
||||
},
|
||||
};
|
||||
|
||||
const test_task_param_t test_task_param = {
|
||||
.fd = uart_fd,
|
||||
.delay_ms = 50,
|
||||
.sem = xSemaphoreCreateBinary(),
|
||||
};
|
||||
TEST_ASSERT_NOT_NULL(test_task_param.sem);
|
||||
start_task(&test_task_param);
|
||||
|
||||
int s = poll(poll_fds, sizeof(poll_fds)/sizeof(poll_fds[0]), 100);
|
||||
TEST_ASSERT_EQUAL(s, 1);
|
||||
TEST_ASSERT_EQUAL(uart_fd, poll_fds[0].fd);
|
||||
TEST_ASSERT_EQUAL(POLLIN, poll_fds[0].revents);
|
||||
TEST_ASSERT_EQUAL(-1, poll_fds[1].fd);
|
||||
TEST_ASSERT_EQUAL(0, poll_fds[1].revents);
|
||||
|
||||
int read_bytes = read(uart_fd, recv_message, sizeof(message));
|
||||
TEST_ASSERT_EQUAL(read_bytes, sizeof(message));
|
||||
TEST_ASSERT_EQUAL_MEMORY(message, recv_message, sizeof(message));
|
||||
|
||||
TEST_ASSERT_EQUAL(xSemaphoreTake(test_task_param.sem, 1000 / portTICK_PERIOD_MS), pdTRUE);
|
||||
|
||||
poll_fds[1].fd = socket_fd;
|
||||
poll_fds[1].events = POLLIN;
|
||||
|
||||
start_task(&test_task_param);
|
||||
|
||||
s = poll(poll_fds, sizeof(poll_fds)/sizeof(poll_fds[0]), 100);
|
||||
TEST_ASSERT_EQUAL(s, 1);
|
||||
TEST_ASSERT_EQUAL(uart_fd, poll_fds[0].fd);
|
||||
TEST_ASSERT_EQUAL(POLLIN, poll_fds[0].revents);
|
||||
TEST_ASSERT_EQUAL(socket_fd, poll_fds[1].fd);
|
||||
TEST_ASSERT_EQUAL(0, poll_fds[1].revents);
|
||||
|
||||
read_bytes = read(uart_fd, recv_message, sizeof(message));
|
||||
TEST_ASSERT_EQUAL(read_bytes, sizeof(message));
|
||||
TEST_ASSERT_EQUAL_MEMORY(message, recv_message, sizeof(message));
|
||||
|
||||
TEST_ASSERT_EQUAL(xSemaphoreTake(test_task_param.sem, 1000 / portTICK_PERIOD_MS), pdTRUE);
|
||||
vSemaphoreDelete(test_task_param.sem);
|
||||
|
||||
deinit(uart_fd, socket_fd);
|
||||
}
|
||||
|
||||
TEST_CASE("socket can do select()", "[vfs]")
|
||||
{
|
||||
int uart_fd;
|
||||
@ -239,6 +300,58 @@ TEST_CASE("socket can do select()", "[vfs]")
|
||||
close(dummy_socket_fd);
|
||||
}
|
||||
|
||||
TEST_CASE("socket can do poll()", "[vfs]")
|
||||
{
|
||||
int uart_fd;
|
||||
int socket_fd;
|
||||
char recv_message[sizeof(message)];
|
||||
|
||||
init(&uart_fd, &socket_fd);
|
||||
const int dummy_socket_fd = open_dummy_socket();
|
||||
|
||||
struct pollfd poll_fds[] = {
|
||||
{
|
||||
.fd = uart_fd,
|
||||
.events = POLLIN,
|
||||
},
|
||||
{
|
||||
.fd = socket_fd,
|
||||
.events = POLLIN,
|
||||
},
|
||||
{
|
||||
.fd = dummy_socket_fd,
|
||||
.events = POLLIN,
|
||||
},
|
||||
};
|
||||
|
||||
const test_task_param_t test_task_param = {
|
||||
.fd = socket_fd,
|
||||
.delay_ms = 50,
|
||||
.sem = xSemaphoreCreateBinary(),
|
||||
};
|
||||
TEST_ASSERT_NOT_NULL(test_task_param.sem);
|
||||
start_task(&test_task_param);
|
||||
|
||||
int s = poll(poll_fds, sizeof(poll_fds)/sizeof(poll_fds[0]), 100);
|
||||
TEST_ASSERT_EQUAL(s, 1);
|
||||
TEST_ASSERT_EQUAL(uart_fd, poll_fds[0].fd);
|
||||
TEST_ASSERT_EQUAL(0, poll_fds[0].revents);
|
||||
TEST_ASSERT_EQUAL(socket_fd, poll_fds[1].fd);
|
||||
TEST_ASSERT_EQUAL(POLLIN, poll_fds[1].revents);
|
||||
TEST_ASSERT_EQUAL(dummy_socket_fd, poll_fds[2].fd);
|
||||
TEST_ASSERT_EQUAL(0, poll_fds[2].revents);
|
||||
|
||||
int read_bytes = read(socket_fd, recv_message, sizeof(message));
|
||||
TEST_ASSERT_EQUAL(read_bytes, sizeof(message));
|
||||
TEST_ASSERT_EQUAL_MEMORY(message, recv_message, sizeof(message));
|
||||
|
||||
TEST_ASSERT_EQUAL(xSemaphoreTake(test_task_param.sem, 1000 / portTICK_PERIOD_MS), pdTRUE);
|
||||
vSemaphoreDelete(test_task_param.sem);
|
||||
|
||||
deinit(uart_fd, socket_fd);
|
||||
close(dummy_socket_fd);
|
||||
}
|
||||
|
||||
TEST_CASE("select() timeout", "[vfs]")
|
||||
{
|
||||
int uart_fd;
|
||||
@ -270,6 +383,44 @@ TEST_CASE("select() timeout", "[vfs]")
|
||||
deinit(uart_fd, socket_fd);
|
||||
}
|
||||
|
||||
TEST_CASE("poll() timeout", "[vfs]")
|
||||
{
|
||||
int uart_fd;
|
||||
int socket_fd;
|
||||
|
||||
init(&uart_fd, &socket_fd);
|
||||
|
||||
struct pollfd poll_fds[] = {
|
||||
{
|
||||
.fd = uart_fd,
|
||||
.events = POLLIN,
|
||||
},
|
||||
{
|
||||
.fd = socket_fd,
|
||||
.events = POLLIN,
|
||||
},
|
||||
};
|
||||
|
||||
int s = poll(poll_fds, sizeof(poll_fds)/sizeof(poll_fds[0]), 100);
|
||||
TEST_ASSERT_EQUAL(s, 0);
|
||||
TEST_ASSERT_EQUAL(uart_fd, poll_fds[0].fd);
|
||||
TEST_ASSERT_EQUAL(0, poll_fds[0].revents);
|
||||
TEST_ASSERT_EQUAL(socket_fd, poll_fds[1].fd);
|
||||
TEST_ASSERT_EQUAL(0, poll_fds[1].revents);
|
||||
|
||||
poll_fds[0].fd = -1;
|
||||
poll_fds[1].fd = -1;
|
||||
|
||||
s = poll(poll_fds, sizeof(poll_fds)/sizeof(poll_fds[0]), 100);
|
||||
TEST_ASSERT_EQUAL(s, 0);
|
||||
TEST_ASSERT_EQUAL(-1, poll_fds[0].fd);
|
||||
TEST_ASSERT_EQUAL(0, poll_fds[0].revents);
|
||||
TEST_ASSERT_EQUAL(-1, poll_fds[1].fd);
|
||||
TEST_ASSERT_EQUAL(0, poll_fds[1].revents);
|
||||
|
||||
deinit(uart_fd, socket_fd);
|
||||
}
|
||||
|
||||
static void select_task(void *param)
|
||||
{
|
||||
const test_task_param_t *test_task_param = param;
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2015-2017 Espressif Systems (Shanghai) PTE LTD
|
||||
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -1096,3 +1096,79 @@ int tcsendbreak(int fd, int duration)
|
||||
return ret;
|
||||
}
|
||||
#endif // CONFIG_SUPPORT_TERMIOS
|
||||
|
||||
int esp_vfs_poll(struct pollfd *fds, nfds_t nfds, int timeout)
|
||||
{
|
||||
struct timeval tv = {
|
||||
// timeout is in milliseconds
|
||||
.tv_sec = timeout / 1000,
|
||||
.tv_usec = (timeout % 1000) * 1000,
|
||||
};
|
||||
int max_fd = -1;
|
||||
fd_set readfds;
|
||||
fd_set writefds;
|
||||
fd_set errorfds;
|
||||
struct _reent* r = __getreent();
|
||||
int ret = 0;
|
||||
|
||||
if (fds == NULL) {
|
||||
__errno_r(r) = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_ZERO(&writefds);
|
||||
FD_ZERO(&errorfds);
|
||||
|
||||
for (int i = 0; i < nfds; ++i) {
|
||||
fds[i].revents = 0;
|
||||
|
||||
if (fds[i].fd < 0) {
|
||||
// revents should remain 0 and events ignored (according to the documentation of poll()).
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fds[i].fd >= MAX_FDS) {
|
||||
fds[i].revents |= POLLNVAL;
|
||||
++ret;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fds[i].events & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) {
|
||||
FD_SET(fds[i].fd, &readfds);
|
||||
FD_SET(fds[i].fd, &errorfds);
|
||||
max_fd = MAX(max_fd, fds[i].fd);
|
||||
}
|
||||
|
||||
if (fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) {
|
||||
FD_SET(fds[i].fd, &writefds);
|
||||
FD_SET(fds[i].fd, &errorfds);
|
||||
max_fd = MAX(max_fd, fds[i].fd);
|
||||
}
|
||||
}
|
||||
|
||||
const int select_ret = esp_vfs_select(max_fd + 1, &readfds, &writefds, &errorfds, timeout < 0 ? NULL: &tv);
|
||||
|
||||
if (select_ret > 0) {
|
||||
ret += select_ret;
|
||||
|
||||
for (int i = 0; i < nfds; ++i) {
|
||||
if (FD_ISSET(fds[i].fd, &readfds)) {
|
||||
fds[i].revents |= POLLIN;
|
||||
}
|
||||
|
||||
if (FD_ISSET(fds[i].fd, &writefds)) {
|
||||
fds[i].revents |= POLLOUT;
|
||||
}
|
||||
|
||||
if (FD_ISSET(fds[i].fd, &errorfds)) {
|
||||
fds[i].revents |= POLLERR;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret = select_ret;
|
||||
// keeping the errno from select()
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user