diff --git a/components/newlib/CMakeLists.txt b/components/newlib/CMakeLists.txt index 208be1fe45..c6eed1a19e 100644 --- a/components/newlib/CMakeLists.txt +++ b/components/newlib/CMakeLists.txt @@ -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" diff --git a/components/newlib/platform_include/sys/poll.h b/components/newlib/platform_include/sys/poll.h index 6e0067347c..030da6bf48 100644 --- a/components/newlib/platform_include/sys/poll.h +++ b/components/newlib/platform_include/sys/poll.h @@ -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_ diff --git a/components/newlib/poll.c b/components/newlib/poll.c new file mode 100644 index 0000000000..481b13f00f --- /dev/null +++ b/components/newlib/poll.c @@ -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 +#include "esp_vfs.h" + +int poll(struct pollfd *fds, nfds_t nfds, int timeout) +{ + return esp_vfs_poll(fds, nfds, timeout); +} diff --git a/components/vfs/include/esp_vfs.h b/components/vfs/include/esp_vfs.h index e54a3e9835..e383c43632 100644 --- a/components/vfs/include/esp_vfs.h +++ b/components/vfs/include/esp_vfs.h @@ -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. @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include "sdkconfig.h" @@ -385,6 +386,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 diff --git a/components/vfs/test/test_vfs_select.c b/components/vfs/test/test_vfs_select.c index 8ec6794f44..341dec1c53 100644 --- a/components/vfs/test/test_vfs_select.c +++ b/components/vfs/test/test_vfs_select.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. @@ -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; diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c index 3032077eff..45633cd03a 100644 --- a/components/vfs/vfs.c +++ b/components/vfs/vfs.c @@ -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. @@ -1110,3 +1110,79 @@ int esp_vfs_utime(const char *path, const struct utimbuf *times) CHECK_AND_CALL(ret, r, vfs, utime, path_within_vfs, times); return ret; } + +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; +}