Просмотр исходного кода

lwip: Route LWIP socket POSIX I/O functions via IDF VFS layer

No more conflicts between LWIP & newlib read(), write(), fcntl(), etc.

select() still only works if all of the fds are sockets.

Closes https://github.com/espressif/esp-idf/issues/273
Angus Gratton 8 лет назад
Родитель
Сommit
3ebf7923d3

+ 1 - 0
components/lwip/include/lwip/port/arch/sys_arch.h

@@ -37,6 +37,7 @@
 #include "freertos/task.h"
 #include "freertos/queue.h"
 #include "freertos/semphr.h"
+#include "arch/vfs_lwip.h"
 
 #ifdef __cplusplus
 extern "C" {

+ 28 - 0
components/lwip/include/lwip/port/arch/vfs_lwip.h

@@ -0,0 +1,28 @@
+// Copyright 2017 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.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Internal declarations used to ingreate LWIP port layer
+   to ESP-IDF VFS for POSIX I/O.
+*/
+extern unsigned lwip_socket_offset;
+
+void esp_vfs_lwip_sockets_register();
+
+#ifdef __cplusplus
+}
+#endif

+ 14 - 0
components/lwip/include/lwip/port/lwipopts.h

@@ -34,6 +34,7 @@
 
 #include <stdlib.h>
 #include <time.h>
+#include <unistd.h>
 #include <sys/time.h>
 #include <sys/fcntl.h>
 #include "esp_task.h"
@@ -687,6 +688,19 @@
 #define ETHARP_TRUST_IP_MAC             CONFIG_LWIP_ETHARP_TRUST_IP_MAC
 
 
+/**
+ * POSIX I/O functions are mapped to LWIP via the VFS layer
+ * (see port/vfs_lwip.c)
+ */
+#define LWIP_POSIX_SOCKETS_IO_NAMES     0
+
+
+/**
+ * Socket offset is also determined via the VFS layer at
+ * filesystem registration time (see port/vfs_lwip.c)
+ */
+#define LWIP_SOCKET_OFFSET              lwip_socket_offset
+
 /* Enable all Espressif-only options */
 
 #define ESP_LWIP                        1

+ 1 - 0
components/lwip/port/freertos/sys_arch.c

@@ -433,6 +433,7 @@ sys_init(void)
     if (ERR_OK != sys_mutex_new(&g_lwip_protect_mutex)) {
         ESP_LOGE(TAG, "sys_init: failed to init lwip protect mutex\n");
     }
+    esp_vfs_lwip_sockets_register();
 }
 
 /*-----------------------------------------------------------------------------------*/

+ 78 - 0
components/lwip/port/vfs_lwip.c

@@ -0,0 +1,78 @@
+// Copyright 2017 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 <string.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <sys/errno.h>
+#include <sys/lock.h>
+#include <sys/fcntl.h>
+#include "esp_vfs.h"
+#include "esp_vfs_dev.h"
+#include "esp_attr.h"
+#include "soc/uart_struct.h"
+#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.
+*/
+
+unsigned lwip_socket_offset;
+
+static int lwip_fcntl_r_wrapper(int fd, int cmd, va_list args);
+static int lwip_ioctl_r_wrapper(int fd, int cmd, va_list args);
+
+void esp_vfs_lwip_sockets_register()
+{
+    esp_vfs_t vfs = {
+        .fd_offset = 0,
+        .flags = ESP_VFS_FLAG_DEFAULT | ESP_VFS_FLAG_SHARED_FD_SPACE,
+        .write = &lwip_write_r,
+        .open = NULL,
+        .fstat = NULL,
+        .close = &lwip_close_r,
+        .read = &lwip_read_r,
+        .fcntl = &lwip_fcntl_r_wrapper,
+        .ioctl = &lwip_ioctl_r_wrapper,
+    };
+    unsigned max_fd;
+
+    ESP_ERROR_CHECK(esp_vfs_register_socket_space(&vfs, NULL, &lwip_socket_offset, &max_fd));
+
+    /* LWIP can't be allowed to create more sockets than fit in the per-VFS fd space. Currently this isn't configurable
+     * but it's set much larger than CONFIG_LWIP_MAX_SOCKETS should ever be (max 2^12 FDs).
+     */
+    assert(CONFIG_LWIP_MAX_SOCKETS <= max_fd - lwip_socket_offset);
+}
+
+static int lwip_fcntl_r_wrapper(int fd, int cmd, va_list args)
+{
+    return lwip_fcntl_r(fd, cmd, va_arg(args, int));
+}
+
+static int lwip_ioctl_r_wrapper(int fd, int cmd, va_list args)
+{
+    return lwip_ioctl_r(fd, cmd, va_arg(args, void *));
+}
+
+

+ 32 - 1
components/vfs/include/esp_vfs.h

@@ -43,6 +43,21 @@ 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
+
 /**
  * @brief VFS definition structure
  *
@@ -68,7 +83,7 @@ extern "C" {
 typedef struct
 {
     int fd_offset;  /*!< file descriptor offset, determined by the FS driver */
-    int flags;      /*!< ESP_VFS_FLAG_CONTEXT_PTR or ESP_VFS_FLAG_DEFAULT */
+    int flags;      /*!< ESP_VFS_FLAG_CONTEXT_PTR or ESP_VFS_FLAG_DEFAULT, plus optionally ESP_VFS_FLAG_SHARED_FD_SPACE  */
     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);
@@ -174,6 +189,22 @@ typedef struct
 esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx);
 
 
+/**
+ * Special case function for registering a VFS that uses a method other than
+ * open() to open new file descriptors.
+ *
+ * This is a special-purpose function intended for registering LWIP sockets to VFS.
+ *
+ * @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.
+ *
+ * @return  ESP_OK if successful, ESP_ERR_NO_MEM if too many VFSes are
+ *          registered.
+ */
+esp_err_t esp_vfs_register_socket_space(const esp_vfs_t *vfs, void *ctx, unsigned *p_min_fd, unsigned *p_max_fd);
+
 /**
  * Unregister a virtual filesystem for given path prefix
  *

+ 39 - 10
components/vfs/vfs.c

@@ -41,6 +41,8 @@
 #define VFS_INDEX_MASK  (VFS_MAX_COUNT << CONFIG_MAX_FD_BITS)
 #define VFS_INDEX_S     CONFIG_MAX_FD_BITS
 
+#define LEN_PATH_PREFIX_IGNORED SIZE_MAX /* special length value for VFS which is never recognised by open() */
+
 typedef struct vfs_entry_ {
     esp_vfs_t vfs;          // contains pointers to VFS functions
     char path_prefix[ESP_VFS_PATH_MAX]; // path prefix mapped to this VFS
@@ -52,14 +54,15 @@ typedef struct vfs_entry_ {
 static vfs_entry_t* s_vfs[VFS_MAX_COUNT] = { 0 };
 static size_t s_vfs_count = 0;
 
-esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ctx)
+static esp_err_t esp_vfs_register_common(const char* base_path, size_t len, const esp_vfs_t* vfs, void* ctx, unsigned *p_minimum_fd, unsigned *p_maximum_fd)
 {
-    size_t len = strlen(base_path);
-    if ((len != 0 && len < 2)|| len > ESP_VFS_PATH_MAX) {
-        return ESP_ERR_INVALID_ARG;
-    }
-    if ((len > 0 && base_path[0] != '/') || base_path[len - 1] == '/') {
-        return ESP_ERR_INVALID_ARG;
+    if (len != LEN_PATH_PREFIX_IGNORED) {
+        if ((len != 0 && len < 2) || (len > ESP_VFS_PATH_MAX)) {
+            return ESP_ERR_INVALID_ARG;
+        }
+        if ((len > 0 && base_path[0] != '/') || base_path[len - 1] == '/') {
+            return ESP_ERR_INVALID_ARG;
+        }
     }
     vfs_entry_t *entry = (vfs_entry_t*) malloc(sizeof(vfs_entry_t));
     if (entry == NULL) {
@@ -79,14 +82,36 @@ esp_err_t esp_vfs_register(const char* base_path, const esp_vfs_t* vfs, void* ct
         ++s_vfs_count;
     }
     s_vfs[index] = entry;
-    strcpy(entry->path_prefix, base_path); // we have already verified argument length
+    if (len != LEN_PATH_PREFIX_IGNORED) {
+        strcpy(entry->path_prefix, base_path); // we have already verified argument length
+    } else {
+        bzero(entry->path_prefix, sizeof(entry->path_prefix));
+    }
     memcpy(&entry->vfs, vfs, sizeof(esp_vfs_t));
     entry->path_prefix_len = len;
     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;
+    }
+
     return ESP_OK;
 }
 
+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);
+}
+
+esp_err_t esp_vfs_register_socket_space(const esp_vfs_t *vfs, void *ctx, unsigned *p_min_fd, unsigned *p_max_fd)
+{
+    return esp_vfs_register_common("", LEN_PATH_PREFIX_IGNORED, vfs, ctx, p_min_fd, p_max_fd);
+}
+
 esp_err_t esp_vfs_unregister(const char* base_path)
 {
     for (size_t i = 0; i < s_vfs_count; ++i) {
@@ -114,7 +139,11 @@ static const vfs_entry_t* get_vfs_for_fd(int fd)
 
 static int translate_fd(const vfs_entry_t* vfs, int fd)
 {
-    return (fd & VFS_FD_MASK) + vfs->vfs.fd_offset;
+    if (vfs->vfs.flags & ESP_VFS_FLAG_SHARED_FD_SPACE) {
+        return fd + vfs->vfs.fd_offset;
+    } else {
+        return (fd & VFS_FD_MASK) + vfs->vfs.fd_offset;
+    }
 }
 
 static const char* translate_path(const vfs_entry_t* vfs, const char* src_path)
@@ -134,7 +163,7 @@ static const vfs_entry_t* get_vfs_for_path(const char* path)
     size_t len = strlen(path);
     for (size_t i = 0; i < s_vfs_count; ++i) {
         const vfs_entry_t* vfs = s_vfs[i];
-        if (!vfs) {
+        if (!vfs || vfs->path_prefix_len == LEN_PATH_PREFIX_IGNORED) {
             continue;
         }
         // match path prefix