]> granicus.if.org Git - esp-idf/commitdiff
Allow VFS file descriptors in select()
authorRoland Dobai <dobai.roland@gmail.com>
Mon, 19 Feb 2018 13:14:02 +0000 (14:14 +0100)
committerRoland Dobai <dobai.roland@gmail.com>
Tue, 17 Apr 2018 09:25:30 +0000 (11:25 +0200)
26 files changed:
components/driver/include/driver/uart_select.h [new file with mode: 0644]
components/driver/uart.c
components/lwip/include/lwip/lwip/sockets.h
components/lwip/include/lwip/lwip/sys.h
components/lwip/include/lwip/port/lwipopts.h
components/lwip/port/freertos/sys_arch.c
components/lwip/port/vfs_lwip.c
components/newlib/include/sys/select.h [new file with mode: 0644]
components/newlib/include/sys/types.h
components/newlib/select.c [new file with mode: 0644]
components/vfs/README.rst
components/vfs/include/esp_vfs.h
components/vfs/test/test_vfs_select.c [new file with mode: 0644]
components/vfs/vfs.c
components/vfs/vfs_uart.c
docs/en/api-reference/peripherals/uart.rst
examples/peripherals/uart_select/Makefile [new file with mode: 0644]
examples/peripherals/uart_select/README.md [new file with mode: 0644]
examples/peripherals/uart_select/main/component.mk [new file with mode: 0644]
examples/peripherals/uart_select/main/uart_select_example_main.c [new file with mode: 0644]
examples/protocols/udp_multicast/main/udp_multicast_example_main.c
examples/system/select/Makefile [new file with mode: 0644]
examples/system/select/README.md [new file with mode: 0644]
examples/system/select/main/component.mk [new file with mode: 0644]
examples/system/select/main/select_example.c [new file with mode: 0644]
tools/unit-test-app/main/app_main.c

diff --git a/components/driver/include/driver/uart_select.h b/components/driver/include/driver/uart_select.h
new file mode 100644 (file)
index 0000000..486621d
--- /dev/null
@@ -0,0 +1,49 @@
+
+// Copyright 2015-2016 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.
+
+#ifndef _UART_SELECT_H_
+#define _UART_SELECT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "driver/uart.h"
+
+typedef enum {
+    UART_SELECT_READ_NOTIF,
+    UART_SELECT_WRITE_NOTIF,
+    UART_SELECT_ERROR_NOTIF,
+} uart_select_notif_t;
+
+typedef void (*uart_select_notif_callback_t)(uart_port_t uart_num, uart_select_notif_t uart_select_notif, BaseType_t *task_woken);
+
+/**
+ * @brief Set notification callback function for select() events
+ * @param uart_num UART port number
+ * @param uart_select_notif_callback callback function
+ */
+void uart_set_select_notif_callback(uart_port_t uart_num, uart_select_notif_callback_t uart_select_notif_callback);
+
+/**
+ * @brief Get mutex guarding select() notifications
+ */
+portMUX_TYPE *uart_get_selectlock();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //_UART_SELECT_H_
index efd56abf8479076362577475ba2f5bfb830b18a6..1454e8a0c6d73569db8e6cb61f247875f23bafd9 100644 (file)
@@ -29,6 +29,7 @@
 #include "soc/uart_struct.h"
 #include "driver/uart.h"
 #include "driver/gpio.h"
+#include "driver/uart_select.h"
 
 #define XOFF (char)0x13
 #define XON (char)0x11
@@ -100,12 +101,14 @@ typedef struct {
     uint8_t tx_brk_flg;                 /*!< Flag to indicate to send a break signal in the end of the item sending procedure */
     uint8_t tx_brk_len;                 /*!< TX break signal cycle length/number */
     uint8_t tx_waiting_brk;             /*!< Flag to indicate that TX FIFO is ready to send break signal after FIFO is empty, do not push data into TX FIFO right now.*/
+    uart_select_notif_callback_t uart_select_notif_callback; /*!< Notification about select() events */
 } uart_obj_t;
 
 static uart_obj_t *p_uart_obj[UART_NUM_MAX] = {0};
 /* DRAM_ATTR is required to avoid UART array placed in flash, due to accessed from ISR */
 static DRAM_ATTR uart_dev_t* const UART[UART_NUM_MAX] = {&UART0, &UART1, &UART2};
 static portMUX_TYPE uart_spinlock[UART_NUM_MAX] = {portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED, portMUX_INITIALIZER_UNLOCKED};
+static portMUX_TYPE uart_selectlock = portMUX_INITIALIZER_UNLOCKED;
 
 esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit)
 {
@@ -831,6 +834,11 @@ static void uart_rx_intr_handler_default(void *param)
                     uart_clear_intr_status(uart_num, UART_RXFIFO_TOUT_INT_CLR_M | UART_RXFIFO_FULL_INT_CLR_M);
                     uart_event.type = UART_DATA;
                     uart_event.size = rx_fifo_len;
+                    UART_ENTER_CRITICAL_ISR(&uart_selectlock);
+                    if (p_uart->uart_select_notif_callback) {
+                        p_uart->uart_select_notif_callback(uart_num, UART_SELECT_READ_NOTIF, &HPTaskAwoken);
+                    }
+                    UART_EXIT_CRITICAL_ISR(&uart_selectlock);
                 }
                 p_uart->rx_stash_len = rx_fifo_len;
                 //If we fail to push data to ring buffer, we will have to stash the data, and send next time.
@@ -889,15 +897,30 @@ static void uart_rx_intr_handler_default(void *param)
             uart_reg->int_clr.rxfifo_ovf = 1;
             UART_EXIT_CRITICAL_ISR(&uart_spinlock[uart_num]);
             uart_event.type = UART_FIFO_OVF;
+            UART_ENTER_CRITICAL_ISR(&uart_selectlock);
+            if (p_uart->uart_select_notif_callback) {
+                p_uart->uart_select_notif_callback(uart_num, UART_SELECT_ERROR_NOTIF, &HPTaskAwoken);
+            }
+            UART_EXIT_CRITICAL_ISR(&uart_selectlock);
         } else if(uart_intr_status & UART_BRK_DET_INT_ST_M) {
             uart_reg->int_clr.brk_det = 1;
             uart_event.type = UART_BREAK;
         } else if(uart_intr_status & UART_FRM_ERR_INT_ST_M) {
             uart_reg->int_clr.frm_err = 1;
             uart_event.type = UART_FRAME_ERR;
+            UART_ENTER_CRITICAL_ISR(&uart_selectlock);
+            if (p_uart->uart_select_notif_callback) {
+                p_uart->uart_select_notif_callback(uart_num, UART_SELECT_ERROR_NOTIF, &HPTaskAwoken);
+            }
+            UART_EXIT_CRITICAL_ISR(&uart_selectlock);
         } else if(uart_intr_status & UART_PARITY_ERR_INT_ST_M) {
             uart_reg->int_clr.parity_err = 1;
             uart_event.type = UART_PARITY_ERR;
+            UART_ENTER_CRITICAL_ISR(&uart_selectlock);
+            if (p_uart->uart_select_notif_callback) {
+                p_uart->uart_select_notif_callback(uart_num, UART_SELECT_ERROR_NOTIF, &HPTaskAwoken);
+            }
+            UART_EXIT_CRITICAL_ISR(&uart_selectlock);
         } else if(uart_intr_status & UART_TX_BRK_DONE_INT_ST_M) {
             UART_ENTER_CRITICAL_ISR(&uart_spinlock[uart_num]);
             uart_reg->conf0.txd_brk = 0;
@@ -1255,6 +1278,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b
             p_uart_obj[uart_num]->tx_ring_buf = NULL;
             p_uart_obj[uart_num]->tx_buf_size = 0;
         }
+        p_uart_obj[uart_num]->uart_select_notif_callback = NULL;
     } else {
         ESP_LOGE(UART_TAG, "UART driver already installed");
         return ESP_FAIL;
@@ -1342,3 +1366,15 @@ esp_err_t uart_driver_delete(uart_port_t uart_num)
     }
     return ESP_OK;
 }
+
+void uart_set_select_notif_callback(uart_port_t uart_num, uart_select_notif_callback_t uart_select_notif_callback)
+{
+    if (uart_num < UART_NUM_MAX && p_uart_obj[uart_num]) {
+        p_uart_obj[uart_num]->uart_select_notif_callback = (uart_select_notif_callback_t) uart_select_notif_callback;
+    }
+}
+
+portMUX_TYPE *uart_get_selectlock()
+{
+    return &uart_selectlock;
+}
index d8e4c7ffbebd7bdfea60fe36cdb020aa86d0da41..3003487211e3aab995e33c7364d8137289307e95 100755 (executable)
@@ -38,6 +38,7 @@
 
 #if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
 
+#include <sys/select.h>
 #include <stddef.h> /* for size_t */
 #include <string.h> /* for FD_ZERO */
 
@@ -591,8 +592,10 @@ static inline int sendto(int s,const void *dataptr,size_t size,int flags,const s
 { return lwip_sendto_r(s,dataptr,size,flags,to,tolen); }
 static inline int socket(int domain,int type,int protocol)
 { return lwip_socket(domain,type,protocol); }
+#ifndef ESP_VFS_SELECT
 static inline int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,struct timeval *timeout)
 { return lwip_select(maxfdp1,readset,writeset,exceptset,timeout); }
+#endif /* ESP_VFS_SELECT */
 static inline int ioctlsocket(int s,long cmd,void *argp)
 { return lwip_ioctl_r(s,cmd,argp); }
 
@@ -645,8 +648,10 @@ static inline int sendto(int s,const void *dataptr,size_t size,int flags,const s
 { return lwip_sendto(s,dataptr,size,flags,to,tolen); }
 static inline int socket(int domain,int type,int protocol)
 { return lwip_socket(domain,type,protocol); }
+#ifndef ESP_VFS_SELECT
 static inline int select(int maxfdp1,fd_set t*readset,fd_set *writeset,fd_set *exceptset,struct timeval *timeout)
 { return lwip_select(maxfdp1,readset,writeset,exceptset,timeout); }
+#endif /* ESP_VFS_SELECT */
 static inline int ioctlsocket(int s,long cmd,void *argp)
 { return lwip_ioctl(s,cmd,argp); }
 
index 86d0f3b336c3829072d2d865008c79fb13029233..67729e3b73af5cd729f0c2130422fd30d11a5b45 100755 (executable)
@@ -148,6 +148,10 @@ err_t sys_sem_new(sys_sem_t *sem, u8_t count);
 /** Signals a semaphore
  * @param sem the semaphore to signal */
 void sys_sem_signal(sys_sem_t *sem);
+/** Signals a semaphore (ISR version)
+ * @param sem the semaphore to signal
+ * @return non-zero if a higher priority task has been woken  */
+int sys_sem_signal_isr(sys_sem_t *sem);
 /** Wait for a semaphore for the specified timeout
  * @param sem the semaphore to wait for
  * @param timeout timeout in milliseconds to wait (0 = wait forever)
index 3e8d26ee3b8cf5abbccb983281e635b5f4c86c48..13d3ec38922fd327865670844b214a01281f54a5 100644 (file)
 #include <sys/time.h>
 #include <sys/fcntl.h>
 #include <sys/ioctl.h>
+#include <sys/types.h>
 #include "esp_task.h"
 #include "esp_system.h"
 #include "sdkconfig.h"
+#include "esp_vfs.h"
 
 /* Enable all Espressif-only options */
 
 #define ESP_DHCP_TIMER                  1
 #define ESP_LWIP_LOGI(...)              ESP_LOGI("lwip", __VA_ARGS__)
 #define ESP_PING                        1
+#define ESP_VFS_SELECT                  1
 
 #if CONFIG_LWIP_IRAM_OPTIMIZATION
 #define ESP_IRAM_ATTR                   IRAM_ATTR
index 77eaef0a118df3523b444ee67827a5831ff3971d..2c56969ade036d8fd3aac779ee70e087f1baf2dc 100755 (executable)
@@ -133,6 +133,15 @@ sys_sem_signal(sys_sem_t *sem)
     xSemaphoreGive(*sem);
 }
 
+/*-----------------------------------------------------------------------------------*/
+// Signals a semaphore (from ISR)
+int sys_sem_signal_isr(sys_sem_t *sem)
+{
+    BaseType_t woken = pdFALSE;
+    xSemaphoreGiveFromISR(*sem, &woken);
+    return woken == pdTRUE;
+}
+
 /*-----------------------------------------------------------------------------------*/
 /*
   Blocks the thread while waiting for the semaphore to be
index aa623f01037637044376a461ece3aa6608f7ead8..ad764fe7b6eb30b00816247fcd003473b2c6613c 100644 (file)
 #include "lwip/sockets.h"
 #include "sdkconfig.h"
 
-/* LWIP is a special case for VFS use.
-
-   From the LWIP side:
-   - We set LWIP_SOCKET_OFFSET dynamically at VFS registration time so that native LWIP socket functions & VFS functions
-   see the same fd space. This is necessary to mix POSIX file operations defined in VFS with POSIX socket operations defined
-   in LWIP, without needing to wrap all of them.
-
-   From the VFS side:
-   - ESP_VFS_FLAG_SHARED_FD_SPACE is set, so unlike other VFS implementations the FDs that the LWIP "VFS" sees and the
-   FDs that the user sees are the same FDs.
+/* Non-LWIP file descriptors are from 0 to (LWIP_SOCKET_OFFSET-1).
+ * LWIP file descriptors are from LWIP_SOCKET_OFFSET to FD_SETSIZE-1.
 */
 
 int lwip_socket_offset;
@@ -45,7 +37,7 @@ static int lwip_ioctl_r_wrapper(int fd, int cmd, va_list args);
 void esp_vfs_lwip_sockets_register()
 {
     esp_vfs_t vfs = {
-        .flags = ESP_VFS_FLAG_DEFAULT | ESP_VFS_FLAG_SHARED_FD_SPACE,
+        .flags = ESP_VFS_FLAG_DEFAULT,
         .write = &lwip_write_r,
         .open = NULL,
         .fstat = NULL,
@@ -53,6 +45,7 @@ void esp_vfs_lwip_sockets_register()
         .read = &lwip_read_r,
         .fcntl = &lwip_fcntl_r_wrapper,
         .ioctl = &lwip_ioctl_r_wrapper,
+        .socket_select = &lwip_select
     };
     int max_fd;
 
diff --git a/components/newlib/include/sys/select.h b/components/newlib/include/sys/select.h
new file mode 100644 (file)
index 0000000..eec864b
--- /dev/null
@@ -0,0 +1,31 @@
+// Copyright 2015-2016 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.
+
+#ifndef __ESP_SYS_SELECT_H__
+#define __ESP_SYS_SELECT_H__
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif //__ESP_SYS_SELECT_H__
index 446946a36bee9850c8b9f1e927a95e86f3ac2501..ed33e0a617e417cf751a7a39901b72f875be6504 100644 (file)
@@ -221,9 +221,6 @@ typedef unsigned int mode_t _ST_INT32;
 
 typedef unsigned short nlink_t;
 
-/* FD_SET and friends are still LWIP only */
-# if !defined(ESP_PLATFORM)
-
 /* We don't define fd_set and friends if we are compiling POSIX
    source, or if we have included (or may include as indicated
    by __USE_W32_SOCKETS) the W32api winsock[2].h header which
@@ -269,7 +266,6 @@ typedef     struct _types_fd_set {
 }))
 
 # endif        /* !(defined (_POSIX_SOURCE) || defined (_WINSOCK_H) || defined (_WINSOCKAPI_) || defined (__USE_W32_SOCKETS)) */
-#endif /* !defined(ESP_PLATFORM) */
 
 #undef __MS_types__
 #undef _ST_INT32
diff --git a/components/newlib/select.c b/components/newlib/select.c
new file mode 100644 (file)
index 0000000..501b2a7
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright 2015-2016 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/select.h>
+#include "esp_vfs.h"
+
+int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout)
+{
+    return esp_vfs_select(nfds, readfds, writefds, errorfds, timeout);
+}
index b99c72134bbf7627bcf21ffec3b472ce192809db..a180e16513bfab769f57267907c83cadc84909a8 100644 (file)
@@ -64,6 +64,35 @@ Case 2: API functions are declared with an extra context pointer (FS driver supp
     myfs_t* myfs_inst2 = myfs_mount(partition2->offset, partition2->size);
     ESP_ERROR_CHECK(esp_vfs_register("/data2", &myfs, myfs_inst2));
 
+Synchronous input/output multiplexing
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you want to use synchronous input/output multiplexing by :cpp:func:`select`
+then you need to register the VFS with :cpp:func:`start_select` and
+:cpp:func:`end_select` functions similarly to the following example:
+
+.. highlight:: c
+
+::
+
+    // In definition of esp_vfs_t:
+        .start_select = &uart_start_select,
+        .end_select = &uart_end_select,
+    // ... other members initialized
+
+:cpp:func:`start_select` is called for setting up the environment for
+detection of read/write/error conditions on file descriptors belonging to the
+given VFS. :cpp:func:`end_select` is called to stop/deinitialize/free the
+environment which was setup by :cpp:func:`start_select`. Please refer to the
+reference implementation for the UART peripheral in
+:component_file:`vfs/vfs_uart.c` and most particularly to functions
+:cpp:func:`esp_vfs_dev_uart_register`, :cpp:func:`uart_start_select` and
+:cpp:func:`uart_end_select`.
+
+Examples demonstrating the use of :cpp:func:`select` with VFS file descriptors
+are the :example:`peripherals/uart_select` and the :example:`system/select`
+examples.
+
 Paths
 -----
 
@@ -93,41 +122,10 @@ When opening files, FS driver will only be given relative path to files. For exa
 
 VFS doesn't impose a limit on total file path length, but it does limit FS path prefix to ``ESP_VFS_PATH_MAX`` characters. Individual FS drivers may have their own filename length limitations.
 
-
 File descriptors
 ----------------
 
-It is suggested that filesystem drivers should use small positive integers as file descriptors. VFS component assumes that ``CONFIG_MAX_FD_BITS`` bits (12 by default) are sufficient to represent a file descriptor.
-
-While file descriptors returned by VFS component to newlib library are rarely seen by the application, the following details may be useful for debugging purposes. File descriptors returned by VFS component are composed of two parts: FS driver ID, and the actual file descriptor. Because newlib stores file descriptors as 16-bit integers, VFS component is also limited by 16 bits to store both parts.
-
-Lower ``CONFIG_MAX_FD_BITS`` bits are used to store zero-based file descriptor. The per-filesystem FD obtained from the FS ``open`` call, and this result is stored in the lower bits of the FD. Higher bits are used to save the index of FS in the internal table of registered filesystems.
-
-When VFS component receives a call from newlib which has a file descriptor, this file descriptor is translated back to the FS-specific file descriptor. First, higher bits of FD are used to identify the FS. Then only the lower ``CONFIG_MAX_FD_BITS`` bits of the fd are masked in, and resulting FD is passed to the FS driver.
-
-.. highlight:: none
-
-::
-
-       FD as seen by newlib                                    FD as seen by FS driver
-
-    +-------+---------------+                               +------------------------+
-    | FS id | Zero—based FD |     +-------------------------->                        |
-    +---+---+------+--------+     |                          +------------------------+
-        |          |              |
-        |          +--------------+
-        |
-        |       +-------------+
-        |       | Table of    |
-        |       | registered  |
-        |       | filesystems |
-        |       +-------------+    +-------------+
-        +------->  entry      +----> esp_vfs_t   |
-        index   +-------------+    | structure   |
-                |             |    |             |
-                |             |    |             |
-                +-------------+    +-------------+
-
+File descriptors are small positive integers from ``0`` to ``FD_SETSIZE - 1`` where ``FD_SETSIZE`` is defined in newlib's ``sys/types.h``. The largest file descriptors (configured by ``CONFIG_LWIP_MAX_SOCKETS``) are reserved for sockets. The VFS component contains a lookup-table called ``fd_vfs`` for mapping file descriptors to VFS driver indexes registered in the ``s_vfs`` array.
 
 Standard IO streams (stdin, stdout, stderr)
 -------------------------------------------
index ab645fccf682543ad109f1cae0f4363be1783fa1..d84579fa7fa48f2e4da91c4495accb603f26fbee 100644 (file)
 #include <stdint.h>
 #include <stddef.h>
 #include <stdarg.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/semphr.h"
 #include "esp_err.h"
 #include <sys/types.h>
 #include <sys/reent.h>
 #include <sys/stat.h>
+#include <sys/time.h>
 #include <dirent.h>
+#include <string.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -43,20 +47,9 @@ extern "C" {
  */
 #define ESP_VFS_FLAG_CONTEXT_PTR    1
 
-/**
- * Flag which indicates that the FD space of the VFS implementation should be made
- * the same as the FD space in newlib. This means that the normal masking off
- * of VFS-independent fd bits is ignored and the full user-facing fd is passed to
- * the VFS implementation.
- *
- * Set the p_minimum_fd & p_maximum_fd pointers when registering the socket in
- * order to know what range of FDs can be used with the registered VFS.
- *
- * This is mostly useful for LWIP which shares the socket FD space with
- * socket-specific functions.
- *
- */
-#define ESP_VFS_FLAG_SHARED_FD_SPACE   2
+#if (FD_SETSIZE < CONFIG_LWIP_MAX_SOCKETS)
+#error "FD_SETSIZE < CONFIG_LWIP_MAX_SOCKETS"
+#endif
 
 /**
  * @brief VFS definition structure
@@ -81,7 +74,7 @@ extern "C" {
  */
 typedef struct
 {
-    int flags;      /*!< ESP_VFS_FLAG_CONTEXT_PTR or ESP_VFS_FLAG_DEFAULT, plus optionally ESP_VFS_FLAG_SHARED_FD_SPACE  */
+    int flags;      /*!< ESP_VFS_FLAG_CONTEXT_PTR or ESP_VFS_FLAG_DEFAULT */
     union {
         ssize_t (*write_p)(void* p, int fd, const void * data, size_t size);
         ssize_t (*write)(int fd, const void * data, size_t size);
@@ -166,6 +159,12 @@ typedef struct
         int (*fsync_p)(void* ctx, int fd);
         int (*fsync)(int fd);
     };
+    /** 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, void *callback_handle);
+    /** socket select function for socket FDs with similar functionality to 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);
+    /** end_select is called to stop the I/O multiplexing and deinitialize the environment created by start_select for the given VFS */
+    void (*end_select)();
 } esp_vfs_t;
 
 
@@ -199,8 +198,8 @@ esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ct
  *
  * @param vfs  Pointer to esp_vfs_t. Meaning is the same as for esp_vfs_register().
  * @param ctx Pointer to context structure. Meaning is the same as for esp_vfs_register().
- * @param p_min_fd If non-NULL, on success this variable is written with the minimum (global/user-facing) FD that this VFS will use. This is useful when ESP_VFS_FLAG_SHARED_FD_SPACE is set in vfs->flags.
- * @param p_max_fd If non-NULL, on success this variable is written with one higher than the maximum (global/user-facing) FD that this VFS will use. This is useful when ESP_VFS_FLAG_SHARED_FD_SPACE is set in vfs->flags.
+ * @param p_min_fd If non-NULL, on success this variable is written with the minimum (global/user-facing) FD that this VFS will use.
+ * @param p_max_fd If non-NULL, on success this variable is written with one higher than the maximum (global/user-facing) FD that this VFS will use.
  *
  * @return  ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are
  *          registered.
@@ -233,10 +232,55 @@ int esp_vfs_unlink(struct _reent *r, const char *path);
 int esp_vfs_rename(struct _reent *r, const char *src, const char *dst);
 /**@}*/
 
+/**
+ * @brief Synchronous I/O multiplexing which implements the functionality of POSIX select() for VFS
+ * @param nfds      Specifies the range of descriptors which should be checked.
+ *                  The first nfds descriptors will be checked in each set.
+ * @param readfds   If not NULL, then points to a descriptor set that on input
+ *                  specifies which descriptors should be checked for being
+ *                  ready to read, and on output indicates which descriptors
+ *                  are ready to read.
+ * @param writefds  If not NULL, then points to a descriptor set that on input
+ *                  specifies which descriptors should be checked for being
+ *                  ready to write, and on output indicates which descriptors
+ *                  are ready to write.
+ * @param errorfds  If not NULL, then points to a descriptor set that on input
+ *                  specifies which descriptors should be checked for error
+ *                  conditions, and on output indicates which descriptors
+ *                  have error conditions.
+ * @param timeout   If not NULL, then points to timeval structure which
+ *                  specifies the time period after which the functions should
+ *                  time-out and return. If it is NULL, then the function will
+ *                  not time-out.
+ *
+ * @return      The number of descriptors set in the descriptor sets, or -1
+ *              when an error (specified by errno) have occurred.
+ */
+int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);
+
+/**
+ * @brief Notification from a VFS driver about a read/write/error condition
+ *
+ * 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 callback_handle Callback handle which was provided by start_select.
+ */
+void esp_vfs_select_triggered(void *callback_handle);
+
+/**
+ * @brief Notification from a VFS driver about a read/write/error condition (ISR version)
+ *
+ * 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 callback_handle Callback handle which was provided by start_select.
+ * @param woken should be set to pdTRUE if the function wakes up a task with higher priority
+ */
+void esp_vfs_select_triggered_isr(void *callback_handle, BaseType_t *woken);
 
 #ifdef __cplusplus
 } // extern "C"
 #endif
 
-
 #endif //__ESP_VFS_H__
diff --git a/components/vfs/test/test_vfs_select.c b/components/vfs/test/test_vfs_select.c
new file mode 100644 (file)
index 0000000..98f0e83
--- /dev/null
@@ -0,0 +1,208 @@
+// Copyright 2015-2016 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 <stdio.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include "unity.h"
+#include "soc/uart_struct.h"
+#include "freertos/FreeRTOS.h"
+#include "driver/uart.h"
+#include "esp_vfs.h"
+#include "esp_vfs_dev.h"
+#include "lwip/sockets.h"
+#include "lwip/netdb.h"
+
+typedef struct {
+    int fd;
+    int delay_ms;
+} test_task_param_t;
+
+static const char message[] = "Hello world!";
+
+static int socket_init()
+{
+    const struct addrinfo hints = {
+        .ai_family = AF_INET,
+        .ai_socktype = SOCK_DGRAM,
+    };
+    struct addrinfo *res;
+    int err;
+    struct sockaddr_in saddr = { 0 };
+    int socket_fd = -1;
+
+    err = getaddrinfo("localhost", "80", &hints, &res);
+    TEST_ASSERT_EQUAL(err, 0);
+    TEST_ASSERT_NOT_NULL(res);
+
+    socket_fd = socket(res->ai_family, res->ai_socktype, 0);
+    TEST_ASSERT(socket_fd >= 0);
+
+    saddr.sin_family = PF_INET;
+    saddr.sin_port = htons(80);
+    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+    err = bind(socket_fd, (struct sockaddr *) &saddr, sizeof(struct sockaddr_in));
+    TEST_ASSERT(err >= 0);
+
+    err = connect(socket_fd, res->ai_addr, res->ai_addrlen);
+    TEST_ASSERT_EQUAL_MESSAGE(err, 0, "Socket connection failed");
+
+    freeaddrinfo(res);
+
+    return socket_fd;
+}
+
+static void uart1_init()
+{
+    uart_config_t uart_config = {
+        .baud_rate = 115200,
+        .data_bits = UART_DATA_8_BITS,
+        .parity    = UART_PARITY_DISABLE,
+        .stop_bits = UART_STOP_BITS_1,
+        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
+    };
+    uart_param_config(UART_NUM_1, &uart_config);
+    uart_driver_install(UART_NUM_1, 256, 256, 0, NULL, 0);
+}
+
+static void send_task(void *param)
+{
+    const test_task_param_t *test_task_param = param;
+    vTaskDelay(test_task_param->delay_ms / portTICK_PERIOD_MS);
+    write(test_task_param->fd, message, sizeof(message));
+    vTaskDelete(NULL);
+}
+
+static inline void start_task(const test_task_param_t *test_task_param)
+{
+    xTaskCreate(send_task, "send_task", 4*1024, (void *) test_task_param, 5, NULL);
+}
+
+static void init(int *uart_fd, int *socket_fd)
+{
+    uart1_init();
+    UART1.conf0.loopback = 1;
+
+    *uart_fd = open("/dev/uart/1", O_RDWR);
+    TEST_ASSERT_NOT_EQUAL_MESSAGE(*uart_fd, -1, "Cannot open UART");
+
+    esp_vfs_dev_uart_use_driver(*uart_fd);
+
+    *socket_fd = socket_init();
+}
+
+static void deinit(int uart_fd, int socket_fd)
+{
+    esp_vfs_dev_uart_use_nonblocking(uart_fd);
+    close(uart_fd);
+    UART1.conf0.loopback = 0;
+    uart_driver_delete(UART_NUM_1);
+
+    close(socket_fd);
+}
+
+TEST_CASE("UART can do select()", "[vfs]")
+{
+    int uart_fd;
+    int socket_fd;
+    struct timeval tv = {
+        .tv_sec = 0,
+        .tv_usec = 100000,
+    };
+    char recv_message[sizeof(message)];
+
+    init(&uart_fd, &socket_fd);
+
+    fd_set rfds;
+    FD_ZERO(&rfds);
+    FD_SET(uart_fd, &rfds);
+    FD_SET(socket_fd, &rfds);
+
+    const test_task_param_t test_task_param = {
+        .fd = uart_fd,
+        .delay_ms = 50,
+    };
+    start_task(&test_task_param);
+
+    const int s = select(MAX(uart_fd, socket_fd) + 1, &rfds, NULL, NULL, &tv);
+    TEST_ASSERT_EQUAL(s, 1);
+    TEST_ASSERT(FD_ISSET(uart_fd, &rfds));
+    TEST_ASSERT_UNLESS(FD_ISSET(socket_fd, &rfds));
+
+    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));
+
+    deinit(uart_fd, socket_fd);
+}
+
+TEST_CASE("socket can do select()", "[vfs]")
+{
+    int uart_fd;
+    int socket_fd;
+    struct timeval tv = {
+        .tv_sec = 0,
+        .tv_usec = 100000,
+    };
+    char recv_message[sizeof(message)];
+
+    init(&uart_fd, &socket_fd);
+
+    fd_set rfds;
+    FD_ZERO(&rfds);
+    FD_SET(uart_fd, &rfds);
+    FD_SET(socket_fd, &rfds);
+
+    const test_task_param_t test_task_param = {
+        .fd = socket_fd,
+        .delay_ms = 50,
+    };
+    start_task(&test_task_param);
+
+    const int s = select(MAX(uart_fd, socket_fd) + 1, &rfds, NULL, NULL, &tv);
+    TEST_ASSERT_EQUAL(s, 1);
+    TEST_ASSERT_UNLESS(FD_ISSET(uart_fd, &rfds));
+    TEST_ASSERT(FD_ISSET(socket_fd, &rfds));
+
+    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));
+
+    deinit(uart_fd, socket_fd);
+}
+
+TEST_CASE("select() timeout", "[vfs]")
+{
+    int uart_fd;
+    int socket_fd;
+    struct timeval tv = {
+        .tv_sec = 0,
+        .tv_usec = 100000,
+    };
+
+    init(&uart_fd, &socket_fd);
+
+    fd_set rfds;
+    FD_ZERO(&rfds);
+    FD_SET(uart_fd, &rfds);
+    FD_SET(socket_fd, &rfds);
+
+    const int s = select(MAX(uart_fd, socket_fd) + 1, &rfds, NULL, NULL, &tv);
+    TEST_ASSERT_EQUAL(s, 0);
+    TEST_ASSERT_UNLESS(FD_ISSET(uart_fd, &rfds));
+    TEST_ASSERT_UNLESS(FD_ISSET(socket_fd, &rfds));
+
+    deinit(uart_fd, socket_fd);
+}
index 5dbd462c2e5a52b356ecf263e49b2980c29d93a6..249d66750dd92f3a01a9fb1e20d995b425f0abfe 100644 (file)
 #include <sys/fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/unistd.h>
+#include <sys/param.h>
 #include <dirent.h>
 #include "esp_vfs.h"
 #include "esp_log.h"
+#include "port/arch/sys_arch.h"
+#include "lwip/sys.h"
 
-/*
- * File descriptors visible by the applications are composed of two parts.
- * Lower CONFIG_MAX_FD_BITS bits are used for the actual file descriptor.
- * Next (16 - CONFIG_MAX_FD_BITS - 1) bits are used to identify the VFS this
- * descriptor corresponds to.
- * Highest bit is zero.
- * We can only use 16 bits because newlib stores file descriptor as short int.
- */
-
-#ifndef CONFIG_MAX_FD_BITS
-#define CONFIG_MAX_FD_BITS 12
-#endif
-
-#define MAX_VFS_ID_BITS (16 - CONFIG_MAX_FD_BITS - 1)
-// mask of actual file descriptor (e.g. 0x00000fff)
-#define VFS_FD_MASK     ((1 << CONFIG_MAX_FD_BITS) - 1)
-// max number of VFS entries
-#define VFS_MAX_COUNT   ((1 << MAX_VFS_ID_BITS) - 1)
-// mask of VFS id (e.g. 0x00007000)
-#define VFS_INDEX_MASK  (VFS_MAX_COUNT << CONFIG_MAX_FD_BITS)
-#define VFS_INDEX_S     CONFIG_MAX_FD_BITS
+#define VFS_MAX_COUNT   8   /* max number of VFS entries (registered filesystems) */
+#define MIN_LWIP_FD     (FD_SETSIZE - CONFIG_LWIP_MAX_SOCKETS)
 
 #define LEN_PATH_PREFIX_IGNORED SIZE_MAX /* special length value for VFS which is never recognised by open() */
 
@@ -55,10 +39,19 @@ typedef struct vfs_entry_ {
     int offset;             // index of this structure in s_vfs array
 } vfs_entry_t;
 
+typedef struct {
+    bool isset; // none or at least one bit is set in the following 3 fd sets
+    fd_set readfds;
+    fd_set writefds;
+    fd_set errorfds;
+} fds_triple_t;
+
 static vfs_entry_t* s_vfs[VFS_MAX_COUNT] = { 0 };
 static size_t s_vfs_count = 0;
 
-static esp_err_t esp_vfs_register_common(const char* base_path, size_t len, const esp_vfs_t* vfs, void* ctx, int *p_minimum_fd, int *p_maximum_fd)
+static short fd_vfs[FD_SETSIZE] = { [0 ... FD_SETSIZE-1] = -1 };
+
+static esp_err_t esp_vfs_register_common(const char* base_path, size_t len, const esp_vfs_t* vfs, void* ctx, int *vfs_entry_index)
 {
     if (len != LEN_PATH_PREFIX_IGNORED) {
         if ((len != 0 && len < 2) || (len > ESP_VFS_PATH_MAX)) {
@@ -96,11 +89,8 @@ static esp_err_t esp_vfs_register_common(const char* base_path, size_t len, cons
     entry->ctx = ctx;
     entry->offset = index;
 
-    if (p_minimum_fd != NULL) {
-        *p_minimum_fd = index << VFS_INDEX_S;
-    }
-    if (p_maximum_fd != NULL) {
-        *p_maximum_fd = (index + 1) << VFS_INDEX_S;
+    if (vfs_entry_index) {
+        *vfs_entry_index = index;
     }
 
     return ESP_OK;
@@ -108,12 +98,29 @@ static esp_err_t esp_vfs_register_common(const char* base_path, size_t len, cons
 
 esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx)
 {
-    return esp_vfs_register_common(base_path, strlen(base_path), vfs, ctx, NULL, NULL);
+    return esp_vfs_register_common(base_path, strlen(base_path), vfs, ctx, NULL);
 }
 
 esp_err_t esp_vfs_register_socket_space(const esp_vfs_t *vfs, void *ctx, int *p_min_fd, int *p_max_fd)
 {
-    return esp_vfs_register_common("", LEN_PATH_PREFIX_IGNORED, vfs, ctx, p_min_fd, p_max_fd);
+    int index = -1;
+    esp_err_t ret = esp_vfs_register_common("", LEN_PATH_PREFIX_IGNORED, vfs, ctx, &index);
+
+    if (ret == ESP_OK) {
+        if (p_min_fd != NULL) {
+            *p_min_fd = MIN_LWIP_FD;
+        }
+
+        if (p_max_fd != NULL) {
+            *p_max_fd = FD_SETSIZE;
+        }
+    }
+
+    for (int i = MIN_LWIP_FD; i < FD_SETSIZE; ++i) {
+        fd_vfs[i] = index;
+    }
+
+    return ret;
 }
 
 esp_err_t esp_vfs_unregister(const char* base_path)
@@ -126,27 +133,40 @@ esp_err_t esp_vfs_unregister(const char* base_path)
         if (memcmp(base_path, vfs->path_prefix, vfs->path_prefix_len) == 0) {
             free(vfs);
             s_vfs[i] = NULL;
+
+            // Delete all references from the FD lookup-table
+            for (int j = 0; j < FD_SETSIZE; ++j) {
+                if (fd_vfs[j] == i) {
+                    fd_vfs[j] = -1;
+                }
+            }
+
             return ESP_OK;
         }
     }
     return ESP_ERR_INVALID_STATE;
 }
 
-static const vfs_entry_t* get_vfs_for_fd(int fd)
+static inline const vfs_entry_t* get_vfs_for_index(int index)
 {
-    int index = ((fd & VFS_INDEX_MASK) >> VFS_INDEX_S);
-    if (index >= s_vfs_count) {
+    if (index < 0 || index >= s_vfs_count) {
         return NULL;
+    } else {
+        return s_vfs[index];
     }
-    return s_vfs[index];
 }
 
-static int translate_fd(const vfs_entry_t* vfs, int fd)
+static inline bool fd_valid(int fd)
+{
+    return (fd < FD_SETSIZE) && (fd >= 0);
+}
+
+static inline const vfs_entry_t* get_vfs_for_fd(int fd)
 {
-    if (vfs->vfs.flags & ESP_VFS_FLAG_SHARED_FD_SPACE) {
-        return fd;
+    if (fd_valid(fd)) {
+        return get_vfs_for_index(fd_vfs[fd]);
     } else {
-        return fd & VFS_FD_MASK;
+        return NULL;
     }
 }
 
@@ -256,10 +276,15 @@ int esp_vfs_open(struct _reent *r, const char * path, int flags, int mode)
     const char* path_within_vfs = translate_path(vfs, path);
     int ret;
     CHECK_AND_CALL(ret, r, vfs, open, path_within_vfs, flags, mode);
-    if (ret < 0) {
-        return ret;
+    if (ret >= MIN_LWIP_FD) {
+        CHECK_AND_CALL(ret, r, vfs, close, ret);
+        __errno_r(r) = ENFILE;
+        return -1;
     }
-    return ret + (vfs->offset << VFS_INDEX_S);
+    if (ret >= 0) {
+        fd_vfs[ret] = vfs->offset;
+    }
+    return ret;
 }
 
 ssize_t esp_vfs_write(struct _reent *r, int fd, const void * data, size_t size)
@@ -269,9 +294,8 @@ ssize_t esp_vfs_write(struct _reent *r, int fd, const void * data, size_t size)
         __errno_r(r) = EBADF;
         return -1;
     }
-    int local_fd = translate_fd(vfs, fd);
     ssize_t ret;
-    CHECK_AND_CALL(ret, r, vfs, write, local_fd, data, size);
+    CHECK_AND_CALL(ret, r, vfs, write, fd, data, size);
     return ret;
 }
 
@@ -282,9 +306,8 @@ off_t esp_vfs_lseek(struct _reent *r, int fd, off_t size, int mode)
         __errno_r(r) = EBADF;
         return -1;
     }
-    int local_fd = translate_fd(vfs, fd);
     off_t ret;
-    CHECK_AND_CALL(ret, r, vfs, lseek, local_fd, size, mode);
+    CHECK_AND_CALL(ret, r, vfs, lseek, fd, size, mode);
     return ret;
 }
 
@@ -295,9 +318,8 @@ ssize_t esp_vfs_read(struct _reent *r, int fd, void * dst, size_t size)
         __errno_r(r) = EBADF;
         return -1;
     }
-    int local_fd = translate_fd(vfs, fd);
     ssize_t ret;
-    CHECK_AND_CALL(ret, r, vfs, read, local_fd, dst, size);
+    CHECK_AND_CALL(ret, r, vfs, read, fd, dst, size);
     return ret;
 }
 
@@ -309,9 +331,13 @@ int esp_vfs_close(struct _reent *r, int fd)
         __errno_r(r) = EBADF;
         return -1;
     }
-    int local_fd = translate_fd(vfs, fd);
     int ret;
-    CHECK_AND_CALL(ret, r, vfs, close, local_fd);
+    CHECK_AND_CALL(ret, r, vfs, close, fd);
+    if (fd < MIN_LWIP_FD) {
+        fd_vfs[fd] = -1;
+    } // else { Leave alone socket FDs because sockets are not created by
+    // esp_vfs_open and therefore fd_vfs won't be set again.
+    //}
     return ret;
 }
 
@@ -322,9 +348,8 @@ int esp_vfs_fstat(struct _reent *r, int fd, struct stat * st)
         __errno_r(r) = EBADF;
         return -1;
     }
-    int local_fd = translate_fd(vfs, fd);
     int ret;
-    CHECK_AND_CALL(ret, r, vfs, fstat, local_fd, st);
+    CHECK_AND_CALL(ret, r, vfs, fstat, fd, st);
     return ret;
 }
 
@@ -404,14 +429,14 @@ DIR* opendir(const char* name)
     DIR* ret;
     CHECK_AND_CALLP(ret, r, vfs, opendir, path_within_vfs);
     if (ret != NULL) {
-        ret->dd_vfs_idx = vfs->offset << VFS_INDEX_S;
+        ret->dd_vfs_idx = vfs->offset;
     }
     return ret;
 }
 
 struct dirent* readdir(DIR* pdir)
 {
-    const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx);
+    const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
     struct _reent* r = __getreent();
     if (vfs == NULL) {
        __errno_r(r) = EBADF;
@@ -424,7 +449,7 @@ struct dirent* readdir(DIR* pdir)
 
 int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent)
 {
-    const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx);
+    const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
     struct _reent* r = __getreent();
     if (vfs == NULL) {
         errno = EBADF;
@@ -437,7 +462,7 @@ int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent)
 
 long telldir(DIR* pdir)
 {
-    const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx);
+    const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
     struct _reent* r = __getreent();
     if (vfs == NULL) {
         errno = EBADF;
@@ -450,7 +475,7 @@ long telldir(DIR* pdir)
 
 void seekdir(DIR* pdir, long loc)
 {
-    const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx);
+    const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
     struct _reent* r = __getreent();
     if (vfs == NULL) {
         errno = EBADF;
@@ -466,7 +491,7 @@ void rewinddir(DIR* pdir)
 
 int closedir(DIR* pdir)
 {
-    const vfs_entry_t* vfs = get_vfs_for_fd(pdir->dd_vfs_idx);
+    const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
     struct _reent* r = __getreent();
     if (vfs == NULL) {
         errno = EBADF;
@@ -513,11 +538,10 @@ int fcntl(int fd, int cmd, ...)
         __errno_r(r) = EBADF;
         return -1;
     }
-    int local_fd = translate_fd(vfs, fd);
     int ret;
     va_list args;
     va_start(args, cmd);
-    CHECK_AND_CALL(ret, r, vfs, fcntl, local_fd, cmd, args);
+    CHECK_AND_CALL(ret, r, vfs, fcntl, fd, cmd, args);
     va_end(args);
     return ret;
 }
@@ -530,11 +554,10 @@ int ioctl(int fd, int cmd, ...)
         __errno_r(r) = EBADF;
         return -1;
     }
-    int local_fd = translate_fd(vfs, fd);
     int ret;
     va_list args;
     va_start(args, cmd);
-    CHECK_AND_CALL(ret, r, vfs, ioctl, local_fd, cmd, args);
+    CHECK_AND_CALL(ret, r, vfs, ioctl, fd, cmd, args);
     va_end(args);
     return ret;
 }
@@ -547,8 +570,166 @@ int fsync(int fd)
         __errno_r(r) = EBADF;
         return -1;
     }
-    int local_fd = translate_fd(vfs, fd);
     int ret;
-    CHECK_AND_CALL(ret, r, vfs, fsync, local_fd);
+    CHECK_AND_CALL(ret, r, vfs, fsync, fd);
     return ret;
 }
+
+static void call_end_selects(int end_index, const fds_triple_t *vfs_fds_triple)
+{
+    for (int i = 0; i < end_index; ++i) {
+        const vfs_entry_t *vfs = get_vfs_for_index(i);
+        const fds_triple_t *item = &vfs_fds_triple[i];
+        if (vfs && vfs->vfs.end_select && item->isset) {
+            vfs->vfs.end_select();
+        }
+    }
+}
+
+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;
+
+    for (int i = 0; i < size; ++i) {
+        const fds_triple_t *item = &vfs_fds_triple[i];
+        if (item->isset) {
+            for (int j  = 0; j < sizeof(item->readfds.fds_bits)/sizeof(item->readfds.fds_bits[0]); ++j) {
+                if (readfds != NULL) {
+                    readfds->fds_bits[j] |= item->readfds.fds_bits[j];
+                    ret += __builtin_popcountl(item->readfds.fds_bits[j]);
+                }
+                if (writefds != NULL) {
+                    writefds->fds_bits[j] |= item->writefds.fds_bits[j];
+                    ret += __builtin_popcountl(item->writefds.fds_bits[j]);
+                }
+                if (errorfds != NULL) {
+                    errorfds->fds_bits[j] |= item->errorfds.fds_bits[j];
+                    ret += __builtin_popcountl(item->errorfds.fds_bits[j]);
+                }
+            }
+        }
+    }
+
+    return ret;
+}
+
+int esp_vfs_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout)
+{
+    const int non_socket_nfds = MIN(nfds, MIN_LWIP_FD);
+    int ret = 0;
+    struct _reent* r = __getreent();
+
+    if (nfds > FD_SETSIZE || nfds < 0) {
+        __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;
+        return -1;
+    }
+
+    for (int i = 0; i < non_socket_nfds; ++i) {
+        if (fd_vfs[i] < 0) {
+            continue;
+        }
+        fds_triple_t *item = &vfs_fds_triple[fd_vfs[i]]; // FD sets for VFS which belongs to FD i
+        if (readfds && FD_ISSET(i, readfds)) {
+            item->isset = true;
+            FD_SET(i, &item->readfds);
+            FD_CLR(i, readfds);
+        }
+        if (writefds && FD_ISSET(i, writefds)) {
+            item->isset = true;
+            FD_SET(i, &item->writefds);
+            FD_CLR(i, writefds);
+        }
+        if (errorfds && FD_ISSET(i, errorfds)) {
+            item->isset = true;
+            FD_SET(i, &item->errorfds);
+            FD_CLR(i, errorfds);
+        }
+    }
+
+    // all non-socket VFSs have their FD sets in vfs_fds_triple
+    // the global readfds, writefds and errorfds contain only socket FDs
+
+    void *callback_handle = sys_thread_sem_get();
+    if (!callback_handle) {
+        __errno_r(r) = ENOMEM;
+        return -1;
+    }
+    sys_arch_sem_wait(callback_handle, 1); //ensure the semaphore is taken so we are not signalled out-of-sequence; 0 timeout would block forever
+    for (int i = 0; i < s_vfs_count; ++i) {
+        const vfs_entry_t *vfs = get_vfs_for_index(i);
+        fds_triple_t *item = &vfs_fds_triple[i];
+
+        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, callback_handle);
+
+            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);
+                free(vfs_fds_triple);
+                __errno_r(r) = ENOMEM;
+                return -1;
+            }
+        }
+    }
+
+    int socket_select_called = 0;
+    for (int i = MIN_LWIP_FD; i < FD_SETSIZE; ++i) {
+        const vfs_entry_t* vfs = get_vfs_for_fd(i);
+        if (vfs != NULL && vfs->vfs.socket_select != NULL) {
+            // find socket VFS and call socket_select
+            ret = vfs->vfs.socket_select(nfds, readfds, writefds, errorfds, timeout);
+            socket_select_called = 1;
+            break;
+        }
+    }
+    if (!socket_select_called) {
+        uint32_t timeout_ms = 0;
+
+        if (readfds) {
+            FD_ZERO(readfds);
+        }
+        if (writefds) {
+            FD_ZERO(writefds);
+        }
+        if (errorfds) {
+            FD_ZERO(errorfds);
+        }
+
+        if (timeout) {
+            timeout_ms = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
+            timeout_ms = MAX(timeout_ms, 1); //make sure it is not 0 which would be in infinite wait
+        }
+        sys_arch_sem_wait(callback_handle, timeout_ms);
+        sys_sem_free(callback_handle);
+    }
+
+    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);
+    }
+    //don't free callback_handle because it is freed in lwip_select (vfs.socket_select)
+    free(vfs_fds_triple);
+    return ret;
+}
+
+void esp_vfs_select_triggered(void *callback_handle)
+{
+    if (callback_handle) {
+        sys_sem_signal(callback_handle); //vfs->vfs.socket_select will return
+    }
+}
+
+void esp_vfs_select_triggered_isr(void *callback_handle, BaseType_t *woken)
+{
+    if (callback_handle && sys_sem_signal_isr(callback_handle) && woken) {
+        *woken = pdTRUE;
+    }
+}
index 033ff6345fca69f3126885d380f0333cca076ae9..4e9d91a6d0c08a7b6de99e565f5561fa19a07f1b 100644 (file)
@@ -24,6 +24,7 @@
 #include "soc/uart_struct.h"
 #include "driver/uart.h"
 #include "sdkconfig.h"
+#include "driver/uart_select.h"
 
 // TODO: make the number of UARTs chip dependent
 #define UART_NUM 3
@@ -56,6 +57,14 @@ static int s_peek_char[UART_NUM] = { NONE, NONE, NONE };
 // driver is used.
 static bool s_non_blocking[UART_NUM];
 
+static fd_set *_readfds = NULL;
+static fd_set *_writefds = NULL;
+static fd_set *_errorfds = NULL;
+static fd_set *_readfds_orig = NULL;
+static fd_set *_writefds_orig = NULL;
+static fd_set *_errorfds_orig = NULL;
+static void *_vfs_callback_handle = NULL;
+
 // Newline conversion mode when transmitting
 static esp_line_endings_t s_tx_mode =
 #if CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF
@@ -267,6 +276,111 @@ static int uart_fcntl(int fd, int cmd, va_list args)
     return result;
 }
 
+static void select_notif_callback(uart_port_t uart_num, uart_select_notif_t uart_select_notif, BaseType_t *task_woken)
+{
+    switch (uart_select_notif) {
+        case UART_SELECT_READ_NOTIF:
+            if (FD_ISSET(uart_num, _readfds_orig)) {
+                FD_SET(uart_num, _readfds);
+                esp_vfs_select_triggered_isr(_vfs_callback_handle, 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(_vfs_callback_handle, 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(_vfs_callback_handle, task_woken);
+            }
+            break;
+    }
+}
+
+static esp_err_t uart_start_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, void *callback_handle)
+{
+    const int max_fds = nfds < UART_NUM ? nfds : UART_NUM;
+
+    taskENTER_CRITICAL(uart_get_selectlock());
+
+    if (_readfds || _writefds || _errorfds || _readfds_orig || _writefds_orig || _errorfds_orig) {
+        taskEXIT_CRITICAL(uart_get_selectlock());
+        return ESP_ERR_INVALID_STATE;
+    }
+
+    if ((_readfds_orig = malloc(sizeof(fd_set))) == NULL) {
+        taskEXIT_CRITICAL(uart_get_selectlock());
+        return ESP_ERR_NO_MEM;
+    }
+
+    if ((_writefds_orig = malloc(sizeof(fd_set))) == NULL) {
+        taskEXIT_CRITICAL(uart_get_selectlock());
+        return ESP_ERR_NO_MEM;
+    }
+
+    if ((_errorfds_orig = malloc(sizeof(fd_set))) == NULL) {
+        taskEXIT_CRITICAL(uart_get_selectlock());
+        return ESP_ERR_NO_MEM;
+    }
+
+    _vfs_callback_handle = callback_handle;
+
+    //uart_set_select_notif_callback set the callbacks in UART ISR
+    for (int i = 0; i < max_fds; ++i) {
+        if (FD_ISSET(i, readfds) || FD_ISSET(i, writefds) || FD_ISSET(i, exceptfds)) {
+            uart_set_select_notif_callback(i, select_notif_callback);
+        }
+    }
+
+    _readfds = readfds;
+    _writefds = writefds;
+    _errorfds = exceptfds;
+
+    *_readfds_orig = *readfds;
+    *_writefds_orig = *writefds;
+    *_errorfds_orig = *exceptfds;
+
+    FD_ZERO(readfds);
+    FD_ZERO(writefds);
+    FD_ZERO(exceptfds);
+
+    taskEXIT_CRITICAL(uart_get_selectlock());
+
+    return ESP_OK;
+}
+
+static void uart_end_select()
+{
+    taskENTER_CRITICAL(uart_get_selectlock());
+    _vfs_callback_handle = NULL;
+    for (int i = 0; i < UART_NUM; ++i) {
+        uart_set_select_notif_callback(i, NULL);
+    }
+
+    _readfds = NULL;
+    _writefds = NULL;
+    _errorfds = NULL;
+
+    if (_readfds_orig) {
+        free(_readfds_orig);
+        _readfds_orig = NULL;
+    }
+
+    if (_writefds_orig) {
+        free(_writefds_orig);
+        _writefds_orig = NULL;
+    }
+
+    if (_errorfds_orig) {
+        free(_errorfds_orig);
+        _errorfds_orig = NULL;
+    }
+    taskEXIT_CRITICAL(uart_get_selectlock());
+}
+
 void esp_vfs_dev_uart_register()
 {
     esp_vfs_t vfs = {
@@ -276,7 +390,9 @@ void esp_vfs_dev_uart_register()
         .fstat = &uart_fstat,
         .close = &uart_close,
         .read = &uart_read,
-        .fcntl = &uart_fcntl
+        .fcntl = &uart_fcntl,
+        .start_select = &uart_start_select,
+        .end_select = &uart_end_select,
     };
     ESP_ERROR_CHECK(esp_vfs_register("/dev/uart", &vfs, NULL));
 }
index d7c82c37edfc65ec8bbb5b7c19dfb165517ef09d..ea2e9615b79c8545875a383f475478f6983a9824 100644 (file)
@@ -142,6 +142,8 @@ Demonstration of how to report various communication events and how to use pater
 
 Transmitting and receiveing with the same UART in two separate FreeRTOS tasks: :example:`peripherals/uart_async_rxtxtasks`.
 
+Using synchronous I/O multiplexing for UART file descriptors: :example:`peripherals/uart_select`.
+
 
 API Reference
 -------------
diff --git a/examples/peripherals/uart_select/Makefile b/examples/peripherals/uart_select/Makefile
new file mode 100644 (file)
index 0000000..010458f
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := uart_select
+
+include $(IDF_PATH)/make/project.mk
diff --git a/examples/peripherals/uart_select/README.md b/examples/peripherals/uart_select/README.md
new file mode 100644 (file)
index 0000000..966e986
--- /dev/null
@@ -0,0 +1,13 @@
+# UART Select Example
+
+The UART select example is for demonstrating the use of `select()` for
+synchronous I/O multiplexing on the UART interface. The example waits for a
+character from UART using `select()` until a blocking read without delay or a
+successful non-blocking read is possible.
+
+Please note that the same result can be achieved by using `uart_read_bytes()`
+but the use of `select()` allows to use it together with other virtual
+file system (VFS) drivers, e.g. LWIP sockets. For a more comprehensive example
+please refer to `system/select`.
+
+See the README.md file in the upper level 'examples' directory for more information about examples.
diff --git a/examples/peripherals/uart_select/main/component.mk b/examples/peripherals/uart_select/main/component.mk
new file mode 100644 (file)
index 0000000..44bd2b5
--- /dev/null
@@ -0,0 +1,3 @@
+#
+# Main Makefile. This is basically the same as a component makefile.
+#
diff --git a/examples/peripherals/uart_select/main/uart_select_example_main.c b/examples/peripherals/uart_select/main/uart_select_example_main.c
new file mode 100644 (file)
index 0000000..c69e4fe
--- /dev/null
@@ -0,0 +1,90 @@
+/* UART Select Example
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+#include <stdio.h>
+#include <sys/fcntl.h>
+#include <sys/errno.h>
+#include <sys/unistd.h>
+#include <sys/select.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_log.h"
+#include "esp_vfs.h"
+#include "esp_vfs_dev.h"
+#include "driver/uart.h"
+
+static const char* TAG = "uart_select_example";
+
+static void uart_select_task()
+{
+    uart_config_t uart_config = {
+        .baud_rate = 115200,
+        .data_bits = UART_DATA_8_BITS,
+        .parity    = UART_PARITY_DISABLE,
+        .stop_bits = UART_STOP_BITS_1,
+        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
+    };
+    uart_param_config(UART_NUM_0, &uart_config);
+    uart_driver_install(UART_NUM_0, 2*1024, 0, 0, NULL, 0);
+
+    while (1) {
+        int fd;
+
+        if ((fd = open("/dev/uart/0", O_RDWR)) == -1) {
+            ESP_LOGE(TAG, "Cannot open UART");
+            vTaskDelay(5000 / portTICK_PERIOD_MS);
+            continue;
+        }
+
+        // We have a driver now installed so set up the read/write functions to use driver also.
+        esp_vfs_dev_uart_use_driver(fd);
+
+        while (1) {
+            int s;
+            fd_set rfds;
+            struct timeval tv = {
+                .tv_sec = 5,
+                .tv_usec = 0,
+            };
+
+            FD_ZERO(&rfds);
+            FD_SET(fd, &rfds);
+
+            s = select(fd + 1, &rfds, NULL, NULL, &tv);
+
+            if (s < 0) {
+                ESP_LOGE(TAG, "Select failed: errno %d", errno);
+                break;
+            } else if (s == 0) {
+                ESP_LOGI(TAG, "Timeout has been reached and nothing has been received");
+            } else {
+                if (FD_ISSET(fd, &rfds)) {
+                    char buf;
+                    if (read(fd, &buf, 1) > 0) {
+                            ESP_LOGI(TAG, "Received: %c", buf);
+                    } else {
+                        ESP_LOGE(TAG, "UART read error");
+                        break;
+                    }
+                } else {
+                    ESP_LOGE(TAG, "No FD has been set in select()");
+                    break;
+                }
+            }
+        }
+
+        close(fd);
+    }
+
+    vTaskDelete(NULL);
+}
+
+void app_main()
+{
+    xTaskCreate(uart_select_task, "uart_select_task", 4*1024, NULL, 5, NULL);
+}
index 830b00e07fd7d73a8d0c60f55344c2f6d03b1f55..18678d0ea04b82a8778517ee4d71fcec35f98416 100644 (file)
@@ -420,7 +420,7 @@ static void mcast_example_task(void *pvParameters)
             FD_ZERO(&rfds);
             FD_SET(sock, &rfds);
 
-            int s = lwip_select(sock + 1, &rfds, NULL, NULL, &tv);
+            int s = select(sock + 1, &rfds, NULL, NULL, &tv);
             if (s < 0) {
                 ESP_LOGE(TAG, "Select failed: errno %d", errno);
                 err = -1;
diff --git a/examples/system/select/Makefile b/examples/system/select/Makefile
new file mode 100644 (file)
index 0000000..0b30811
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := select-example
+
+include $(IDF_PATH)/make/project.mk
+
diff --git a/examples/system/select/README.md b/examples/system/select/README.md
new file mode 100644 (file)
index 0000000..888d461
--- /dev/null
@@ -0,0 +1,11 @@
+# Synchronous I/O multiplexing example
+
+The example demonstrates the use of synchronous I/O multiplexing by the select()
+function with UART and socket file descriptors. The example starts three tasks:
+1. The first task writes periodically to the UART1 file descriptor.
+2. The second task writes periodically to the socket descriptor.
+3. Both UART1 and the socket are configured to act as loopbacks. The third
+   task detects by the use of select() whether it is possible to read from
+   UART1 or the socket, and receives the sent messages from the other tasks.
+
+See the README.md file in the upper level 'examples' directory for more information about examples.
diff --git a/examples/system/select/main/component.mk b/examples/system/select/main/component.mk
new file mode 100644 (file)
index 0000000..0b9d758
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
+
diff --git a/examples/system/select/main/select_example.c b/examples/system/select/main/select_example.c
new file mode 100644 (file)
index 0000000..3d7e6d3
--- /dev/null
@@ -0,0 +1,207 @@
+/* Hello World Example
+
+   This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+   Unless required by applicable law or agreed to in writing, this
+   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+   CONDITIONS OF ANY KIND, either express or implied.
+*/
+#include <stdio.h>
+#include <sys/fcntl.h>
+#include <sys/errno.h>
+#include <sys/unistd.h>
+#include <sys/param.h>
+#include <sys/select.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_log.h"
+#include "esp_vfs.h"
+#include "esp_vfs_dev.h"
+#include "driver/uart.h"
+#include "tcpip_adapter.h"
+#include "lwip/sockets.h"
+#include "lwip/netdb.h"
+
+static const char* TAG = "uart_select_example";
+
+static int uart_fd = -1;
+static int socket_fd = -1;
+
+static void socket_deinit()
+{
+    close(socket_fd);
+    socket_fd = -1;
+}
+
+static void socket_init()
+{
+    const struct addrinfo hints = {
+        .ai_family = AF_INET,
+        .ai_socktype = SOCK_DGRAM,
+    };
+    struct addrinfo *res;
+    int err;
+    struct sockaddr_in saddr = { 0 };
+
+    tcpip_adapter_init();
+
+    err = getaddrinfo("localhost", "80", &hints, &res);
+
+    if (err != 0 || res == NULL) {
+        ESP_LOGE(TAG, "DNS lookup failed: %d", errno);
+        return;
+    }
+
+    socket_fd = socket(res->ai_family, res->ai_socktype, 0);
+
+    if (socket_fd < 0) {
+        ESP_LOGE(TAG, "Failed to allocate socket.");
+        freeaddrinfo(res);
+        return;
+    }
+
+    saddr.sin_family = PF_INET;
+    saddr.sin_port = htons(80);
+    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+    err = bind(socket_fd, (struct sockaddr *) &saddr, sizeof(struct sockaddr_in));
+    if (err < 0) {
+        ESP_LOGE(TAG, "Failed to bind socket");
+        freeaddrinfo(res);
+        socket_deinit();
+        return;
+    }
+
+    if (connect(socket_fd, res->ai_addr, res->ai_addrlen) != 0) {
+        ESP_LOGE(TAG, "Socket connection failed: %d", errno);
+        freeaddrinfo(res);
+        socket_deinit();
+        return;
+    }
+
+    freeaddrinfo(res);
+}
+
+static void uart1_deinit()
+{
+    close(uart_fd);
+    uart_fd = -1;
+    uart_driver_delete(UART_NUM_1);
+    UART1.conf0.loopback = 0;
+}
+
+static void uart1_init()
+{
+    uart_config_t uart_config = {
+        .baud_rate = 115200,
+        .data_bits = UART_DATA_8_BITS,
+        .parity    = UART_PARITY_DISABLE,
+        .stop_bits = UART_STOP_BITS_1,
+        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE
+    };
+    uart_param_config(UART_NUM_1, &uart_config);
+    uart_driver_install(UART_NUM_1, 256, 0, 0, NULL, 0);
+    UART1.conf0.loopback = 1;
+
+    if ((uart_fd = open("/dev/uart/1", O_RDWR | O_NONBLOCK)) == -1) {
+        ESP_LOGE(TAG, "Cannot open UART1");
+        uart1_deinit();
+    }
+
+    esp_vfs_dev_uart_use_driver(uart_fd);
+}
+
+static void uart1_write_task(void *param)
+{
+    char buf[20];
+
+    uart1_init();
+
+    for (uint8_t i = 1;; ++i) {
+        vTaskDelay(4000 / portTICK_PERIOD_MS);
+
+        snprintf(buf, sizeof(buf), "UART message U%d", i);
+        int write_bytes = write(uart_fd, buf, strlen(buf));
+        if (write_bytes < 0) {
+            ESP_LOGE(TAG, "UART1 write error");
+        } else {
+            ESP_LOGI(TAG, "%d bytes were sent to UART1: %s", write_bytes, buf);
+        }
+    }
+
+    uart1_deinit(uart_fd);
+    vTaskDelete(NULL);
+}
+
+static void socket_write_task(void *param)
+{
+    char buf[20];
+
+    socket_init();
+
+    for (uint8_t i = 1;; ++i) {
+        vTaskDelay(3000 / portTICK_PERIOD_MS);
+
+        snprintf(buf, sizeof(buf), "Socket message S%d", i);
+        int write_bytes = write(socket_fd, buf, strlen(buf));
+        if (write_bytes < 0) {
+            ESP_LOGE(TAG, "Socket write error");
+        } else {
+            ESP_LOGI(TAG, "%d bytes were written to socket: %s", write_bytes, buf);
+        }
+    }
+
+    socket_deinit();
+    vTaskDelete(NULL);
+}
+
+static void check_and_print(int fd, const fd_set *rfds, const char *src_msg)
+{
+    char buf[100];
+    int read_bytes;
+
+    if (FD_ISSET(fd, rfds)) {
+        if ((read_bytes = read(fd, buf, sizeof(buf)-1)) > 0) {
+            buf[read_bytes] = '\0';
+            ESP_LOGI(TAG, "%d bytes were received through %s: %s", read_bytes, src_msg, buf);
+        } else {
+            ESP_LOGE(TAG, "%s read error", src_msg);
+        }
+    }
+}
+
+static void select_task(void *param)
+{
+    while (1) {
+        int s;
+        fd_set rfds;
+        struct timeval tv = {
+            .tv_sec = 1,
+            .tv_usec = 0,
+        };
+
+        FD_ZERO(&rfds);
+        FD_SET(uart_fd, &rfds);
+        FD_SET(socket_fd, &rfds);
+
+        s = select(MAX(uart_fd, socket_fd) + 1, &rfds, NULL, NULL, &tv);
+
+        if (s < 0) {
+            ESP_LOGE(TAG, "Select failed: errno %d", errno);
+        } else if (s == 0) {
+            ESP_LOGI(TAG, "Timeout has been reached and nothing has been received");
+        } else {
+            check_and_print(socket_fd, &rfds, "socket");
+            check_and_print(uart_fd, &rfds, "UART1");
+        }
+    }
+
+    vTaskDelete(NULL);
+}
+
+void app_main()
+{
+    xTaskCreate(uart1_write_task, "uart1_write_task", 4*1024, NULL, 5, NULL);
+    xTaskCreate(socket_write_task, "socket_write_task", 4*1024, NULL, 5, NULL);
+    vTaskDelay(1000 / portTICK_PERIOD_MS);
+    xTaskCreate(select_task, "select_task", 4*1024, NULL, 5, NULL);
+}
index a7a7e8754216aff3e1eb8cbe9442997f7179e8db..f1c18fcbfb0c1979338c20418f2e9ac63c183c0b 100644 (file)
@@ -3,6 +3,7 @@
 #include "freertos/task.h"
 #include "unity.h"
 #include "unity_config.h"
+#include "tcpip_adapter.h"
 
 void unityTask(void *pvParameters)
 {
@@ -12,6 +13,10 @@ void unityTask(void *pvParameters)
 
 void app_main()
 {
+    // TCP/IP adapter is initialized here because it leaks memory so the
+    // initialization in test cases would make the test fail because of leak.
+    tcpip_adapter_init();
+
     // Note: if unpinning this task, change the way run times are calculated in
     // unity_platform
     xTaskCreatePinnedToCore(unityTask, "unityTask", 8192, NULL,