]> granicus.if.org Git - esp-idf/commitdiff
VFS: Implement poll() based on select()
authorRoland Dobai <dobai.roland@gmail.com>
Tue, 19 Feb 2019 12:18:40 +0000 (13:18 +0100)
committerRoland Dobai <dobai.roland@gmail.com>
Mon, 25 Feb 2019 09:34:27 +0000 (10:34 +0100)
Closes https://github.com/espressif/esp-idf/issues/2945

components/newlib/CMakeLists.txt
components/newlib/platform_include/sys/poll.h
components/newlib/poll.c [new file with mode: 0644]
components/vfs/include/esp_vfs.h
components/vfs/test/test_vfs_select.c
components/vfs/vfs.c

index 208be1fe4558576492742e0fcdad3aaf46223999..c6eed1a19ec9a0358c1517cc19ff5ac425a873f4 100644 (file)
@@ -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"
index 6e0067347c8ff0a658f91bb19c5f68193c0908da..030da6bf4801b321c0c45ede69ca2a8de104a967 100644 (file)
@@ -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.
 // 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 (file)
index 0000000..481b13f
--- /dev/null
@@ -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);
+}
index e54a3e9835c20c608e1b793ca15476eb25a070aa..e383c4363237e66f9612f9b4264a263f1c2dc61c 100644 (file)
@@ -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 <sys/stat.h>
 #include <sys/time.h>
 #include <sys/termios.h>
+#include <sys/poll.h>
 #include <dirent.h>
 #include <string.h>
 #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
index 8ec6794f4429da2ab2be341f59bb9cbefb292c8c..341dec1c5359589c0d31d9b9df534314e6a7c4a5 100644 (file)
@@ -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;
index 3032077effe3e73475286a03817ea3503593b648..45633cd03ab2366bd91e64940b0cda6076a90e32 100644 (file)
@@ -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;
+}