From b427b23ae0d77acb3ee699cd9826cfdf830684c2 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 20 Mar 2020 13:30:28 +0100 Subject: [PATCH 01/13] newlib: remove direct dependencies on VFS functions poll: use "select" not "esp_vfs_select" move to newlib pread, pwrite, select, utime: remove from newlib. VFS and/or LWIP will provide aliases for these functions. --- components/newlib/CMakeLists.txt | 6 +-- components/newlib/poll.c | 80 ++++++++++++++++++++++++++++++-- components/newlib/pread.c | 21 --------- components/newlib/pwrite.c | 21 --------- components/newlib/select.c | 64 ------------------------- components/newlib/utime.c | 21 --------- 6 files changed, 78 insertions(+), 135 deletions(-) delete mode 100644 components/newlib/pread.c delete mode 100644 components/newlib/pwrite.c delete mode 100644 components/newlib/select.c delete mode 100644 components/newlib/utime.c diff --git a/components/newlib/CMakeLists.txt b/components/newlib/CMakeLists.txt index fd3d5a5d9b..03101d79bb 100644 --- a/components/newlib/CMakeLists.txt +++ b/components/newlib/CMakeLists.txt @@ -3,17 +3,13 @@ set(srcs "heap.c" "locks.c" "poll.c" - "pread.c" - "pwrite.c" "pthread.c" "random.c" "reent_init.c" - "select.c" "syscall_table.c" "syscalls.c" "termios.c" - "time.c" - "utime.c") + "time.c") set(include_dirs platform_include) if(CONFIG_SPIRAM_CACHE_WORKAROUND) diff --git a/components/newlib/poll.c b/components/newlib/poll.c index 481b13f00f..c9f072fd57 100644 --- a/components/newlib/poll.c +++ b/components/newlib/poll.c @@ -1,4 +1,4 @@ -// Copyright 2019 Espressif Systems (Shanghai) PTE LTD +// Copyright 2019-2020 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. @@ -12,10 +12,84 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include -#include "esp_vfs.h" +#include +#include +#include int poll(struct pollfd *fds, nfds_t nfds, int timeout) { - return esp_vfs_poll(fds, nfds, 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 >= FD_SETSIZE) { + 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 = 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; } diff --git a/components/newlib/pread.c b/components/newlib/pread.c deleted file mode 100644 index d14bd7257d..0000000000 --- a/components/newlib/pread.c +++ /dev/null @@ -1,21 +0,0 @@ -// 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" - -ssize_t pread(int fd, void *dst, size_t size, off_t offset) -{ - return esp_vfs_pread(fd, dst, size, offset); -} diff --git a/components/newlib/pwrite.c b/components/newlib/pwrite.c deleted file mode 100644 index 78fd0b85ce..0000000000 --- a/components/newlib/pwrite.c +++ /dev/null @@ -1,21 +0,0 @@ -// 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" - -ssize_t pwrite(int fd, const void *src, size_t size, off_t offset) -{ - return esp_vfs_pwrite(fd, src, size, offset); -} diff --git a/components/newlib/select.c b/components/newlib/select.c deleted file mode 100644 index ac9de99da9..0000000000 --- a/components/newlib/select.c +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2018 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" -#include "sdkconfig.h" - -#ifdef CONFIG_LWIP_USE_ONLY_LWIP_SELECT -#include "lwip/sockets.h" - -#ifdef CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT -#define LOG_LOCAL_LEVEL ESP_LOG_NONE -#endif //CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT -#include "esp_log.h" - -static const char *TAG = "newlib_select"; - -static void log_fd_set(const char *fds_name, const fd_set *fds) -{ - if (fds_name && fds) { - ESP_LOGD(TAG, "FDs in %s =", fds_name); - for (int i = 0; i < MAX_FDS; ++i) { - if (FD_ISSET(i, fds)) { - ESP_LOGD(TAG, "%d", i); - } - } - } -} -#endif //CONFIG_LWIP_USE_ONLY_LWIP_SELECT - -int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout) -{ -#ifdef CONFIG_LWIP_USE_ONLY_LWIP_SELECT - ESP_LOGD(TAG, "lwip_select starts with nfds = %d", nfds); - if (timeout) { - ESP_LOGD(TAG, "timeout is %lds + %ldus", (long)timeout->tv_sec, timeout->tv_usec); - } - log_fd_set("readfds", readfds); - log_fd_set("writefds", writefds); - log_fd_set("errorfds", errorfds); - - int ret = lwip_select(nfds, readfds, writefds, errorfds, timeout); - - ESP_LOGD(TAG, "lwip_select returns %d", ret); - log_fd_set("readfds", readfds); - log_fd_set("writefds", writefds); - log_fd_set("errorfds", errorfds); - - return ret; -#else - return esp_vfs_select(nfds, readfds, writefds, errorfds, timeout); -#endif -} diff --git a/components/newlib/utime.c b/components/newlib/utime.c deleted file mode 100644 index c838fd45ab..0000000000 --- a/components/newlib/utime.c +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018 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 utime(const char *path, const struct utimbuf *times) -{ - return esp_vfs_utime(path, times); -} From 45e50f1f2a8176f748324fe9ea6d1a474ae1bc68 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 20 Mar 2020 13:31:05 +0100 Subject: [PATCH 02/13] newlib: move dirent.h, ioctl.h headers from VFS --- components/{vfs/include => newlib/platform_include}/sys/dirent.h | 0 components/{vfs/include => newlib/platform_include}/sys/ioctl.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename components/{vfs/include => newlib/platform_include}/sys/dirent.h (100%) rename components/{vfs/include => newlib/platform_include}/sys/ioctl.h (100%) diff --git a/components/vfs/include/sys/dirent.h b/components/newlib/platform_include/sys/dirent.h similarity index 100% rename from components/vfs/include/sys/dirent.h rename to components/newlib/platform_include/sys/dirent.h diff --git a/components/vfs/include/sys/ioctl.h b/components/newlib/platform_include/sys/ioctl.h similarity index 100% rename from components/vfs/include/sys/ioctl.h rename to components/newlib/platform_include/sys/ioctl.h From a3c0bcb0a01676c0dbdc522a41f6525439577b3d Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 20 Mar 2020 13:35:00 +0100 Subject: [PATCH 03/13] vfs: introduce options to turn off some features CONFIG_VFS_SUPPORT_IO: for basic I/O functions support CONFIG_VFS_SUPPORT_DIR: for file/directory functions support CONFIG_VFS_SUPPORT_SELECT: for select support --- components/vfs/Kconfig | 55 ++++++++- components/vfs/include/esp_vfs.h | 24 +--- components/vfs/vfs.c | 196 +++++++++++-------------------- components/vfs/vfs_semihost.c | 8 ++ components/vfs/vfs_uart.c | 16 +++ 5 files changed, 151 insertions(+), 148 deletions(-) diff --git a/components/vfs/Kconfig b/components/vfs/Kconfig index c18397bdcb..03af01de14 100644 --- a/components/vfs/Kconfig +++ b/components/vfs/Kconfig @@ -1,7 +1,56 @@ menu "Virtual file system" + config VFS_SUPPORT_IO + bool "Provide basic I/O functions" + default y + help + If enabled, the following functions are provided by the VFS component. + + open, close, read, write, pread, pwrite, lseek, fstat, fsync, ioctl, fcntl + + Filesystem drivers can then be registered to handle these functions + for specific paths. + + Disabling this option can save memory when the support for these functions + is not required. + + config VFS_SUPPORT_DIR + bool "Provide directory related functions" + default y + # If some filesystem is used, adding I/O support is probably needed and + # is not a big overhead, compared to code size of the filesystem, + # so make this depend on IO. + depends on VFS_SUPPORT_IO + help + If enabled, the following functions are provided by the VFS component. + + stat, link, unlink, rename, utime, access, truncate, rmdir, mkdir, + opendir, closedir, readdir, readdir_r, seekdir, telldir, rewinddir + + Filesystem drivers can then be registered to handle these functions + for specific paths. + + Disabling this option can save memory when the support for these functions + is not required. + + config VFS_SUPPORT_SELECT + bool "Provide select function" + default y + # Dependency on !LWIP_USE_ONLY_LWIP_SELECT is for compatibility + depends on VFS_SUPPORT_IO && !LWIP_USE_ONLY_LWIP_SELECT + help + If enabled, select function is provided by the VFS component, and can be used + on peripheral file descriptors (such as UART) and sockets at the same time. + + If disabled, the default select implementation will be provided by LWIP for + sockets only. + + Disabling this option can reduce code size if support for "select" on UART file + descriptors is not required. + config VFS_SUPPRESS_SELECT_DEBUG_OUTPUT bool "Suppress select() related debug outputs" + depends on VFS_SUPPORT_SELECT default y help Select() related functions might produce an unconveniently lot of @@ -10,12 +59,16 @@ menu "Virtual file system" option. config VFS_SUPPORT_TERMIOS - bool "Add support for termios.h" + bool "Provide termios.h functions" default y + # Very likely, only makes sense for UART VFS driver, which itself depends on VFS_SUPPORT_IO + depends on VFS_SUPPORT_IO help Disabling this option can save memory when the support for termios.h is not required. menu "Host File System I/O (Semihosting)" + depends on VFS_SUPPORT_IO + config SEMIHOSTFS_MAX_MOUNT_POINTS int "Maximum number of the host filesystem mount points" default 1 diff --git a/components/vfs/include/esp_vfs.h b/components/vfs/include/esp_vfs.h index fc720e626d..2fadb4f95f 100644 --- a/components/vfs/include/esp_vfs.h +++ b/components/vfs/include/esp_vfs.h @@ -132,6 +132,7 @@ typedef struct int (*fstat_p)(void* ctx, int fd, struct stat * st); /*!< fstat with context pointer */ int (*fstat)(int fd, struct stat * st); /*!< fstat without context pointer */ }; +#ifdef CONFIG_VFS_SUPPORT_DIR union { int (*stat_p)(void* ctx, const char * path, struct stat * st); /*!< stat with context pointer */ int (*stat)(const char * path, struct stat * st); /*!< stat without context pointer */ @@ -180,6 +181,7 @@ typedef struct int (*rmdir_p)(void* ctx, const char* name); /*!< rmdir with context pointer */ int (*rmdir)(const char* name); /*!< rmdir without context pointer */ }; +#endif // CONFIG_VFS_SUPPORT_DIR union { int (*fcntl_p)(void* ctx, int fd, int cmd, int arg); /*!< fcntl with context pointer */ int (*fcntl)(int fd, int cmd, int arg); /*!< fcntl without context pointer */ @@ -192,6 +194,7 @@ typedef struct int (*fsync_p)(void* ctx, int fd); /*!< fsync with context pointer */ int (*fsync)(int fd); /*!< fsync without context pointer */ }; +#ifdef CONFIG_VFS_SUPPORT_DIR union { int (*access_p)(void* ctx, const char *path, int amode); /*!< access with context pointer */ int (*access)(const char *path, int amode); /*!< access without context pointer */ @@ -204,6 +207,7 @@ typedef struct int (*utime_p)(void* ctx, const char *path, const struct utimbuf *times); /*!< utime with context pointer */ int (*utime)(const char *path, const struct utimbuf *times); /*!< utime without context pointer */ }; +#endif // CONFIG_VFS_SUPPORT_DIR #ifdef CONFIG_VFS_SUPPORT_TERMIOS union { int (*tcsetattr_p)(void *ctx, int fd, int optional_actions, const struct termios *p); /*!< tcsetattr with context pointer */ @@ -234,7 +238,7 @@ typedef struct int (*tcsendbreak)(int fd, int duration); /*!< tcsendbreak without context pointer */ }; #endif // CONFIG_VFS_SUPPORT_TERMIOS - +#ifdef CONFIG_VFS_SUPPORT_SELECT /** 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, esp_vfs_select_sem_t sem, void **end_select_args); /** socket select function for socket FDs with the functionality of POSIX select(); this should be set only for the socket VFS */ @@ -247,6 +251,7 @@ typedef struct void* (*get_socket_select_semaphore)(void); /** get_socket_select_semaphore returns semaphore allocated in the socket driver; set only for the socket driver */ esp_err_t (*end_select)(void *end_select_args); +#endif // CONFIG_VFS_SUPPORT_SELECT } esp_vfs_t; @@ -406,23 +411,6 @@ void esp_vfs_select_triggered(esp_vfs_select_sem_t sem); */ void esp_vfs_select_triggered_isr(esp_vfs_select_sem_t 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); - - /** * * @brief Implements the VFS layer of POSIX pread() diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c index 93bc00bab0..7118e83dbe 100644 --- a/components/vfs/vfs.c +++ b/components/vfs/vfs.c @@ -507,6 +507,52 @@ int esp_vfs_fstat(struct _reent *r, int fd, struct stat * st) return ret; } +int esp_vfs_fcntl_r(struct _reent *r, int fd, int cmd, int arg) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(fd); + const int local_fd = get_local_fd(vfs, fd); + if (vfs == NULL || local_fd < 0) { + __errno_r(r) = EBADF; + return -1; + } + int ret; + CHECK_AND_CALL(ret, r, vfs, fcntl, local_fd, cmd, arg); + return ret; +} + +int esp_vfs_ioctl(int fd, int cmd, ...) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(fd); + const int local_fd = get_local_fd(vfs, fd); + struct _reent* r = __getreent(); + if (vfs == NULL || local_fd < 0) { + __errno_r(r) = EBADF; + return -1; + } + int ret; + va_list args; + va_start(args, cmd); + CHECK_AND_CALL(ret, r, vfs, ioctl, local_fd, cmd, args); + va_end(args); + return ret; +} + +int esp_vfs_fsync(int fd) +{ + const vfs_entry_t* vfs = get_vfs_for_fd(fd); + const int local_fd = get_local_fd(vfs, fd); + struct _reent* r = __getreent(); + if (vfs == NULL || local_fd < 0) { + __errno_r(r) = EBADF; + return -1; + } + int ret; + CHECK_AND_CALL(ret, r, vfs, fsync, local_fd); + return ret; +} + +#ifdef CONFIG_VFS_SUPPORT_DIR + int esp_vfs_stat(struct _reent *r, const char * path, struct stat * st) { const vfs_entry_t* vfs = get_vfs_for_path(path); @@ -520,6 +566,20 @@ int esp_vfs_stat(struct _reent *r, const char * path, struct stat * st) return ret; } +int esp_vfs_utime(const char *path, const struct utimbuf *times) +{ + int ret; + const vfs_entry_t* vfs = get_vfs_for_path(path); + struct _reent* r = __getreent(); + if (vfs == NULL) { + __errno_r(r) = ENOENT; + return -1; + } + const char* path_within_vfs = translate_path(vfs, path); + CHECK_AND_CALL(ret, r, vfs, utime, path_within_vfs, times); + return ret; +} + int esp_vfs_link(struct _reent *r, const char* n1, const char* n2) { const vfs_entry_t* vfs = get_vfs_for_path(n1); @@ -712,50 +772,6 @@ int rmdir(const char* name) return ret; } -int _fcntl_r(struct _reent *r, int fd, int cmd, int arg) -{ - const vfs_entry_t* vfs = get_vfs_for_fd(fd); - const int local_fd = get_local_fd(vfs, fd); - if (vfs == NULL || local_fd < 0) { - __errno_r(r) = EBADF; - return -1; - } - int ret; - CHECK_AND_CALL(ret, r, vfs, fcntl, local_fd, cmd, arg); - return ret; -} - -int ioctl(int fd, int cmd, ...) -{ - const vfs_entry_t* vfs = get_vfs_for_fd(fd); - const int local_fd = get_local_fd(vfs, fd); - struct _reent* r = __getreent(); - if (vfs == NULL || local_fd < 0) { - __errno_r(r) = EBADF; - return -1; - } - int ret; - va_list args; - va_start(args, cmd); - CHECK_AND_CALL(ret, r, vfs, ioctl, local_fd, cmd, args); - va_end(args); - return ret; -} - -int fsync(int fd) -{ - const vfs_entry_t* vfs = get_vfs_for_fd(fd); - const int local_fd = get_local_fd(vfs, fd); - struct _reent* r = __getreent(); - if (vfs == NULL || local_fd < 0) { - __errno_r(r) = EBADF; - return -1; - } - int ret; - CHECK_AND_CALL(ret, r, vfs, fsync, local_fd); - return ret; -} - int access(const char *path, int amode) { int ret; @@ -784,6 +800,10 @@ int truncate(const char *path, off_t length) return ret; } +#endif // CONFIG_VFS_SUPPORT_DIR + +#ifdef CONFIG_VFS_SUPPORT_SELECT + static void call_end_selects(int end_index, const fds_triple_t *vfs_fds_triple, void **driver_args) { for (int i = 0; i < end_index; ++i) { @@ -1074,7 +1094,10 @@ void esp_vfs_select_triggered_isr(esp_vfs_select_sem_t sem, BaseType_t *woken) } } +#endif // CONFIG_VFS_SUPPORT_SELECT + #ifdef CONFIG_VFS_SUPPORT_TERMIOS + int tcgetattr(int fd, struct termios *p) { const vfs_entry_t* vfs = get_vfs_for_fd(fd); @@ -1174,95 +1197,10 @@ int tcsendbreak(int fd, int duration) } #endif // CONFIG_VFS_SUPPORT_TERMIOS -int esp_vfs_utime(const char *path, const struct utimbuf *times) -{ - int ret; - const vfs_entry_t* vfs = get_vfs_for_path(path); - struct _reent* r = __getreent(); - if (vfs == NULL) { - __errno_r(r) = ENOENT; - return -1; - } - const char* path_within_vfs = translate_path(vfs, path); - 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; -} void vfs_include_syscalls_impl(void) { diff --git a/components/vfs/vfs_semihost.c b/components/vfs/vfs_semihost.c index d97fa91887..f614078717 100644 --- a/components/vfs/vfs_semihost.c +++ b/components/vfs/vfs_semihost.c @@ -23,6 +23,14 @@ #include "freertos/task.h" #include "esp_vfs.h" +#ifndef CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS +#define CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS 1 +#endif + +#ifndef CONFIG_SEMIHOSTFS_HOST_PATH_MAX_LEN +#define CONFIG_SEMIHOSTFS_HOST_PATH_MAX_LEN 128 +#endif + #define LOG_LOCAL_LEVEL ESP_LOG_NONE #include "esp_log.h" const static char *TAG = "esp_semihost"; diff --git a/components/vfs/vfs_uart.c b/components/vfs/vfs_uart.c index e882d5e5e4..f93814a004 100644 --- a/components/vfs/vfs_uart.c +++ b/components/vfs/vfs_uart.c @@ -116,6 +116,8 @@ static vfs_uart_context_t* s_ctx[UART_NUM] = { #endif }; +#ifdef CONFIG_VFS_SUPPORT_SELECT + typedef struct { esp_vfs_select_sem_t select_sem; fd_set *readfds; @@ -132,6 +134,8 @@ static portMUX_TYPE s_registered_select_lock = portMUX_INITIALIZER_UNLOCKED; static esp_err_t uart_end_select(void *end_select_args); +#endif // CONFIG_VFS_SUPPORT_SELECT + static int uart_open(const char * path, int flags, int mode) { // this is fairly primitive, we should check if file is opened read only, @@ -319,6 +323,8 @@ static int uart_fcntl(int fd, int cmd, int arg) return result; } +#ifdef CONFIG_VFS_SUPPORT_DIR + static int uart_access(const char *path, int amode) { int ret = -1; @@ -340,6 +346,8 @@ static int uart_access(const char *path, int amode) return ret; } +#endif // CONFIG_VFS_SUPPORT_DIR + static int uart_fsync(int fd) { assert(fd >= 0 && fd < 3); @@ -349,6 +357,8 @@ static int uart_fsync(int fd) return 0; } +#ifdef CONFIG_VFS_SUPPORT_SELECT + static esp_err_t register_select(uart_select_args_t *args) { esp_err_t ret = ESP_ERR_INVALID_ARG; @@ -509,6 +519,8 @@ static esp_err_t uart_end_select(void *end_select_args) return ret; } +#endif // CONFIG_VFS_SUPPORT_SELECT + #ifdef CONFIG_VFS_SUPPORT_TERMIOS static int uart_tcsetattr(int fd, int optional_actions, const struct termios *p) { @@ -972,9 +984,13 @@ void esp_vfs_dev_uart_register(void) .read = &uart_read, .fcntl = &uart_fcntl, .fsync = &uart_fsync, +#ifdef CONFIG_VFS_SUPPORT_DIR .access = &uart_access, +#endif // CONFIG_VFS_SUPPORT_DIR +#ifdef CONFIG_VFS_SUPPORT_SELECT .start_select = &uart_start_select, .end_select = &uart_end_select, +#endif // CONFIG_VFS_SUPPORT_SELECT #ifdef CONFIG_VFS_SUPPORT_TERMIOS .tcsetattr = &uart_tcsetattr, .tcgetattr = &uart_tcgetattr, From cadab2cbcfa30ddc406d2cfe9a482cb1cf43299a Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 20 Mar 2020 13:36:44 +0100 Subject: [PATCH 04/13] vfs: define all implementations as esp_vfs_, create aliases This change allows (in the future) to test VFS on host, without having it conflict with the host C library. On host, all aliases would be disabled. --- components/vfs/vfs.c | 117 ++++++++++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 39 deletions(-) diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c index 7118e83dbe..005d050d48 100644 --- a/components/vfs/vfs.c +++ b/components/vfs/vfs.c @@ -631,35 +631,7 @@ int esp_vfs_rename(struct _reent *r, const char *src, const char *dst) return ret; } -/* Create aliases for newlib syscalls - - These functions are also available in ROM as stubs which use the syscall table, but linking them - directly here saves an additional function call when a software function is linked to one, and - makes linking with -stdlib easier. - */ -int _open_r(struct _reent *r, const char * path, int flags, int mode) - __attribute__((alias("esp_vfs_open"))); -ssize_t _write_r(struct _reent *r, int fd, const void * data, size_t size) - __attribute__((alias("esp_vfs_write"))); -off_t _lseek_r(struct _reent *r, int fd, off_t size, int mode) - __attribute__((alias("esp_vfs_lseek"))); -ssize_t _read_r(struct _reent *r, int fd, void * dst, size_t size) - __attribute__((alias("esp_vfs_read"))); -int _close_r(struct _reent *r, int fd) - __attribute__((alias("esp_vfs_close"))); -int _fstat_r(struct _reent *r, int fd, struct stat * st) - __attribute__((alias("esp_vfs_fstat"))); -int _stat_r(struct _reent *r, const char * path, struct stat * st) - __attribute__((alias("esp_vfs_stat"))); -int _link_r(struct _reent *r, const char* n1, const char* n2) - __attribute__((alias("esp_vfs_link"))); -int _unlink_r(struct _reent *r, const char *path) - __attribute__((alias("esp_vfs_unlink"))); -int _rename_r(struct _reent *r, const char *src, const char *dst) - __attribute__((alias("esp_vfs_rename"))); - - -DIR* opendir(const char* name) +DIR* esp_vfs_opendir(const char* name) { const vfs_entry_t* vfs = get_vfs_for_path(name); struct _reent* r = __getreent(); @@ -676,7 +648,7 @@ DIR* opendir(const char* name) return ret; } -struct dirent* readdir(DIR* pdir) +struct dirent* esp_vfs_readdir(DIR* pdir) { const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx); struct _reent* r = __getreent(); @@ -689,7 +661,7 @@ struct dirent* readdir(DIR* pdir) return ret; } -int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent) +int esp_vfs_readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent) { const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx); struct _reent* r = __getreent(); @@ -702,7 +674,7 @@ int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent) return ret; } -long telldir(DIR* pdir) +long esp_vfs_telldir(DIR* pdir) { const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx); struct _reent* r = __getreent(); @@ -715,7 +687,7 @@ long telldir(DIR* pdir) return ret; } -void seekdir(DIR* pdir, long loc) +void esp_vfs_seekdir(DIR* pdir, long loc) { const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx); struct _reent* r = __getreent(); @@ -726,12 +698,12 @@ void seekdir(DIR* pdir, long loc) CHECK_AND_CALLV(r, vfs, seekdir, pdir, loc); } -void rewinddir(DIR* pdir) +void esp_vfs_rewinddir(DIR* pdir) { seekdir(pdir, 0); } -int closedir(DIR* pdir) +int esp_vfs_closedir(DIR* pdir) { const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx); struct _reent* r = __getreent(); @@ -744,7 +716,7 @@ int closedir(DIR* pdir) return ret; } -int mkdir(const char* name, mode_t mode) +int esp_vfs_mkdir(const char* name, mode_t mode) { const vfs_entry_t* vfs = get_vfs_for_path(name); struct _reent* r = __getreent(); @@ -758,7 +730,7 @@ int mkdir(const char* name, mode_t mode) return ret; } -int rmdir(const char* name) +int esp_vfs_rmdir(const char* name) { const vfs_entry_t* vfs = get_vfs_for_path(name); struct _reent* r = __getreent(); @@ -772,7 +744,7 @@ int rmdir(const char* name) return ret; } -int access(const char *path, int amode) +int esp_vfs_access(const char *path, int amode) { int ret; const vfs_entry_t* vfs = get_vfs_for_path(path); @@ -786,7 +758,7 @@ int access(const char *path, int amode) return ret; } -int truncate(const char *path, off_t length) +int esp_vfs_truncate(const char *path, off_t length) { int ret; const vfs_entry_t* vfs = get_vfs_for_path(path); @@ -1198,9 +1170,76 @@ int tcsendbreak(int fd, int duration) #endif // CONFIG_VFS_SUPPORT_TERMIOS +/* Create aliases for newlib syscalls + These functions are also available in ROM as stubs which use the syscall table, but linking them + directly here saves an additional function call when a software function is linked to one, and + makes linking with -stdlib easier. + */ +#ifdef CONFIG_VFS_SUPPORT_IO +int _open_r(struct _reent *r, const char * path, int flags, int mode) + __attribute__((alias("esp_vfs_open"))); +int _close_r(struct _reent *r, int fd) + __attribute__((alias("esp_vfs_close"))); +ssize_t _read_r(struct _reent *r, int fd, void * dst, size_t size) + __attribute__((alias("esp_vfs_read"))); +ssize_t _write_r(struct _reent *r, int fd, const void * data, size_t size) + __attribute__((alias("esp_vfs_write"))); +ssize_t pread(int fd, void *dst, size_t size, off_t offset) + __attribute__((alias("esp_vfs_pread"))); +ssize_t pwrite(int fd, const void *src, size_t size, off_t offset) + __attribute__((alias("esp_vfs_pwrite"))); +off_t _lseek_r(struct _reent *r, int fd, off_t size, int mode) + __attribute__((alias("esp_vfs_lseek"))); +int _fcntl_r(struct _reent *r, int fd, int cmd, int arg) + __attribute__((alias("esp_vfs_fcntl_r"))); +int _fstat_r(struct _reent *r, int fd, struct stat * st) + __attribute__((alias("esp_vfs_fstat"))); +int fsync(int fd) + __attribute__((alias("esp_vfs_fsync"))); +int ioctl(int fd, int cmd, ...) + __attribute__((alias("esp_vfs_ioctl"))); +#endif // CONFIG_VFS_SUPPORT_IO +#ifdef CONFIG_VFS_SUPPORT_SELECT +int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout) + __attribute__((alias("esp_vfs_select"))); +#endif // CONFIG_VFS_SUPPORT_SELECT +#ifdef CONFIG_VFS_SUPPORT_DIR +int _stat_r(struct _reent *r, const char * path, struct stat * st) + __attribute__((alias("esp_vfs_stat"))); +int _link_r(struct _reent *r, const char* n1, const char* n2) + __attribute__((alias("esp_vfs_link"))); +int _unlink_r(struct _reent *r, const char *path) + __attribute__((alias("esp_vfs_unlink"))); +int _rename_r(struct _reent *r, const char *src, const char *dst) + __attribute__((alias("esp_vfs_rename"))); +int truncate(const char *path, off_t length) + __attribute__((alias("esp_vfs_truncate"))); +int access(const char *path, int amode) + __attribute__((alias("esp_vfs_access"))); +int utime(const char *path, const struct utimbuf *times) + __attribute__((alias("esp_vfs_utime"))); +int rmdir(const char* name) + __attribute__((alias("esp_vfs_rmdir"))); +int mkdir(const char* name, mode_t mode) + __attribute__((alias("esp_vfs_mkdir"))); +DIR* opendir(const char* name) + __attribute__((alias("esp_vfs_opendir"))); +int closedir(DIR* pdir) + __attribute__((alias("esp_vfs_closedir"))); +int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent) + __attribute__((alias("esp_vfs_readdir_r"))); +struct dirent* readdir(DIR* pdir) + __attribute__((alias("esp_vfs_readdir"))); +long telldir(DIR* pdir) + __attribute__((alias("esp_vfs_telldir"))); +void seekdir(DIR* pdir, long loc) + __attribute__((alias("esp_vfs_seekdir"))); +void rewinddir(DIR* pdir) + __attribute__((alias("esp_vfs_rewinddir"))); +#endif // CONFIG_VFS_SUPPORT_DIR void vfs_include_syscalls_impl(void) { From b203ac7b02094d4a15a6b49d20ca2ae8add55856 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 20 Mar 2020 13:39:22 +0100 Subject: [PATCH 05/13] lwip: handle CONFIG_VFS_SUPPORT_IO=n and CONFIG_VFS_SUPPORT_SELECT=n * If CONFIG_VFS_SUPPORT_IO=y, keep everything as it is now * If CONFIG_VFS_SUPPORT_IO=n, defined syscalls which LwIP can implement, such as read/write/close * Make LWIP-only select implementation dependent on CONFIG_VFS_SUPPORT_SELECT, deprecate CONFIG_LWIP_USE_ONLY_LWIP_SELECT --- components/lwip/CMakeLists.txt | 7 +- components/lwip/Kconfig | 5 +- components/lwip/component.mk | 5 ++ components/lwip/port/esp32/no_vfs_syscalls.c | 83 ++++++++++++++++++++ components/lwip/port/esp32/vfs_lwip.c | 21 ++++- 5 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 components/lwip/port/esp32/no_vfs_syscalls.c diff --git a/components/lwip/CMakeLists.txt b/components/lwip/CMakeLists.txt index c62fa3aaa9..580d9dc49e 100644 --- a/components/lwip/CMakeLists.txt +++ b/components/lwip/CMakeLists.txt @@ -85,7 +85,6 @@ set(srcs "lwip/src/netif/ppp/upap.c" "lwip/src/netif/ppp/utils.c" "lwip/src/netif/ppp/vj.c" - "port/esp32/vfs_lwip.c" "port/esp32/debug/lwip_debug.c" "port/esp32/freertos/sys_arch.c" "port/esp32/netif/dhcp_state.c" @@ -129,6 +128,12 @@ if(CONFIG_ETH_ENABLED) list(APPEND srcs "port/esp32/netif/ethernetif.c") endif() +if(CONFIG_VFS_SUPPORT_IO) + list(APPEND srcs "port/esp32/vfs_lwip.c") +else() + list(APPEND srcs "port/esp32/no_vfs_syscalls.c") +endif() + idf_component_register(SRCS "${srcs}" INCLUDE_DIRS "${include_dirs}" LDFRAGMENTS linker.lf diff --git a/components/lwip/Kconfig b/components/lwip/Kconfig index 1d8be501de..d0c247154a 100644 --- a/components/lwip/Kconfig +++ b/components/lwip/Kconfig @@ -63,9 +63,12 @@ menu "LWIP" to 16. config LWIP_USE_ONLY_LWIP_SELECT - bool "Support LWIP socket select() only" + bool "Support LWIP socket select() only (DEPRECATED)" default n help + This option is deprecated. Use VFS_SUPPORT_SELECT instead, which is + the inverse of this option. + The virtual filesystem layer of select() redirects sockets to lwip_select() and non-socket file descriptors to their respective driver implementations. If this option is enabled then all calls of select() diff --git a/components/lwip/component.mk b/components/lwip/component.mk index 3e57b75048..d4e53d6949 100644 --- a/components/lwip/component.mk +++ b/components/lwip/component.mk @@ -29,6 +29,11 @@ COMPONENT_SRCDIRS := \ ifndef CONFIG_IDF_TARGET_ESP32 COMPONENT_OBJEXCLUDE := port/esp32/netif/ethernetif.o endif +ifndef CONFIG_VFS_SUPPORT_IO + COMPONENT_OBJEXCLUDE += port/esp32/vfs_lwip.o +else + COMPONENT_OBJEXCLUDE += port/esp32/no_vfs_syscalls.o +endif ifdef CONFIG_LWIP_PPP_SUPPORT COMPONENT_SRCDIRS += lwip/src/netif/ppp lwip/src/netif/ppp/polarssl diff --git a/components/lwip/port/esp32/no_vfs_syscalls.c b/components/lwip/port/esp32/no_vfs_syscalls.c new file mode 100644 index 0000000000..668499c4b2 --- /dev/null +++ b/components/lwip/port/esp32/no_vfs_syscalls.c @@ -0,0 +1,83 @@ +// Copyright 2020 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 +#include +#include +#include +#include +#include +#include "sdkconfig.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" + +#ifdef CONFIG_VFS_SUPPORT_IO +#error This file should only be built when CONFIG_VFS_SUPPORT_IO=n +#endif + +/* Default implementations of read/write provided in newlib component, + * used as a fallback for console I/O. + */ +extern ssize_t _write_r_console(struct _reent *r, int fd, const void * data, size_t size); +extern ssize_t _read_r_console(struct _reent *r, int fd, const void * data, size_t size); + +ssize_t _write_r(struct _reent *r, int fd, const void * data, size_t size) +{ + if (fd < LWIP_SOCKET_OFFSET) { + return _write_r_console(r, fd, data, size); + } + return lwip_write(fd, data, size); +} + +ssize_t _read_r(struct _reent *r, int fd, void * dst, size_t size) +{ + if (fd < LWIP_SOCKET_OFFSET) { + return _read_r_console(r, fd, dst, size); + } + return lwip_read(fd, dst, size); +} + +int _close_r(struct _reent *r, int fd) +{ + if (fd < LWIP_SOCKET_OFFSET) { + errno = ENOSYS; + return -1; + } + return lwip_close(fd); +} + +int _fcntl_r(struct _reent *r, int fd, int cmd, int arg) +{ + return lwip_fcntl(fd, cmd, arg); +} + +int ioctl(int fd, int cmd, ...) +{ + va_list args; + va_start(args, cmd); + int res = lwip_ioctl(fd, cmd, va_arg(args, void*)); + va_end(args); + return res; +} + +int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout) +{ + return lwip_select(nfds, readfds, writefds, errorfds, timeout); +} + +void esp_vfs_lwip_sockets_register(void) +{ + /* Doesn't register anything, just a hook to force linking this file */ +} diff --git a/components/lwip/port/esp32/vfs_lwip.c b/components/lwip/port/esp32/vfs_lwip.c index 9ef74784cc..9704308e2e 100644 --- a/components/lwip/port/esp32/vfs_lwip.c +++ b/components/lwip/port/esp32/vfs_lwip.c @@ -18,15 +18,20 @@ #include #include #include -#include "esp_vfs.h" -#include "esp_vfs_dev.h" #include "esp_attr.h" -#include "lwip/sockets.h" +#include "esp_vfs.h" #include "sdkconfig.h" +#include "lwip/sockets.h" #include "lwip/sys.h" +#ifndef CONFIG_VFS_SUPPORT_IO +#error This file should only be built when CONFIG_VFS_SUPPORT_IO=y +#endif + _Static_assert(MAX_FDS >= CONFIG_LWIP_MAX_SOCKETS, "MAX_FDS < CONFIG_LWIP_MAX_SOCKETS"); +#ifdef CONFIG_VFS_SUPPORT_SELECT + static void lwip_stop_socket_select(void *sem) { sys_sem_signal(sem); //socket_select will return @@ -46,6 +51,14 @@ static void *lwip_get_socket_select_semaphore(void) */ return (void *) sys_thread_sem_get(); } +#else // CONFIG_VFS_SUPPORT_SELECT + +int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout) +{ + return lwip_select(nfds, readfds, writefds, errorfds, timeout); +} + +#endif // CONFIG_VFS_SUPPORT_SELECT static int lwip_fcntl_r_wrapper(int fd, int cmd, int arg) { @@ -68,10 +81,12 @@ void esp_vfs_lwip_sockets_register(void) .read = &lwip_read, .fcntl = &lwip_fcntl_r_wrapper, .ioctl = &lwip_ioctl_r_wrapper, +#ifdef CONFIG_VFS_SUPPORT_SELECT .socket_select = &lwip_select, .get_socket_select_semaphore = &lwip_get_socket_select_semaphore, .stop_socket_select = &lwip_stop_socket_select, .stop_socket_select_isr = &lwip_stop_socket_select_isr, +#endif // CONFIG_VFS_SUPPORT_SELECT }; /* Non-LWIP file descriptors are from 0 to (LWIP_SOCKET_OFFSET-1). LWIP * file descriptors are registered from LWIP_SOCKET_OFFSET to From 1f1ce38e5f3ab594d880cc99d2d03c9442ebaf60 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 20 Mar 2020 13:39:59 +0100 Subject: [PATCH 06/13] newlib: don't depend on VFS names when populating the syscall table --- components/newlib/syscall_table.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/components/newlib/syscall_table.c b/components/newlib/syscall_table.c index 778a1628be..8010cc656a 100644 --- a/components/newlib/syscall_table.c +++ b/components/newlib/syscall_table.c @@ -59,23 +59,23 @@ static struct syscall_stub_table s_stub_table = { ._calloc_r = &_calloc_r, ._abort = &abort, ._system_r = &_system_r, - ._rename_r = &esp_vfs_rename, + ._rename_r = &_rename_r, ._times_r = &_times_r, ._gettimeofday_r = &_gettimeofday_r, ._raise_r = &raise_r_stub, - ._unlink_r = &esp_vfs_unlink, - ._link_r = &esp_vfs_link, - ._stat_r = &esp_vfs_stat, - ._fstat_r = &esp_vfs_fstat, + ._unlink_r = &_unlink_r, + ._link_r = &_link_r, + ._stat_r = &_stat_r, + ._fstat_r = &_fstat_r, ._sbrk_r = &_sbrk_r, ._getpid_r = &_getpid_r, ._kill_r = &_kill_r, ._exit_r = NULL, // never called in ROM - ._close_r = &esp_vfs_close, - ._open_r = &esp_vfs_open, - ._write_r = (int (*)(struct _reent *r, int, const void *, int)) &esp_vfs_write, - ._lseek_r = (int (*)(struct _reent *r, int, int, int)) &esp_vfs_lseek, - ._read_r = (int (*)(struct _reent *r, int, void *, int)) &esp_vfs_read, + ._close_r = &_close_r, + ._open_r = &_open_r, + ._write_r = (int (*)(struct _reent *r, int, const void *, int)) &_write_r, + ._lseek_r = (int (*)(struct _reent *r, int, int, int)) &_lseek_r, + ._read_r = (int (*)(struct _reent *r, int, void *, int)) &_read_r, ._lock_init = &_lock_init, ._lock_init_recursive = &_lock_init_recursive, ._lock_close = &_lock_close, From 4cb10ea7c23a2f58b0545628ed9af6781657d957 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 20 Mar 2020 13:40:18 +0100 Subject: [PATCH 07/13] newlib: provide default implementations for more syscalls --- components/newlib/syscalls.c | 125 ++++++++++++++++++++++++++--------- 1 file changed, 92 insertions(+), 33 deletions(-) diff --git a/components/newlib/syscalls.c b/components/newlib/syscalls.c index 8b4b7aece8..c48cc5ee6e 100644 --- a/components/newlib/syscalls.c +++ b/components/newlib/syscalls.c @@ -1,4 +1,4 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2020 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. @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include #include #include #include @@ -21,51 +20,111 @@ #include #include #include +#include "sdkconfig.h" +#if CONFIG_IDF_TARGET_ESP32 +#include "esp32/rom/uart.h" +#elif CONFIG_IDF_TARGET_ESP32S2 +#include "esp32s2/rom/uart.h" +#endif -int system(const char* str) +static int syscall_not_implemented(void) { errno = ENOSYS; return -1; } +static int syscall_not_implemented_aborts(void) +{ + abort(); +} + +ssize_t _write_r_console(struct _reent *r, int fd, const void * data, size_t size) +{ + const char* cdata = (const char*) data; + if (fd == STDOUT_FILENO || fd == STDERR_FILENO) { + for (size_t i = 0; i < size; ++i) { + uart_tx_one_char(cdata[i]); + } + return size; + } + errno = EBADF; + return -1; +} + +ssize_t _read_r_console(struct _reent *r, int fd, void * data, size_t size) +{ + char* cdata = (char*) data; + if (fd == STDIN_FILENO) { + size_t received; + for (received = 0; received < size; ++received) { + int status = uart_rx_one_char((uint8_t*) &cdata[received]); + if (status != 0) { + break; + } + } + return received; + } + errno = EBADF; + return -1; +} + + +/* The following weak definitions of syscalls will be used unless + * another definition is provided. That definition may come from + * VFS, LWIP, or the application. + */ +ssize_t _read_r(struct _reent *r, int fd, void * dst, size_t size) + __attribute__((weak,alias("_read_r_console"))); +ssize_t _write_r(struct _reent *r, int fd, const void * data, size_t size) + __attribute__((weak,alias("_write_r_console"))); + + +/* The aliases below are to "syscall_not_implemented", which + * doesn't have the same signature as the original function. + * Disable type mismatch warnings for this reason. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wattribute-alias" + +int _open_r(struct _reent *r, const char * path, int flags, int mode) + __attribute__((weak,alias("syscall_not_implemented"))); +int _close_r(struct _reent *r, int fd) + __attribute__((weak,alias("syscall_not_implemented"))); +off_t _lseek_r(struct _reent *r, int fd, off_t size, int mode) + __attribute__((weak,alias("syscall_not_implemented"))); +int _fcntl_r(struct _reent *r, int fd, int cmd, int arg) + __attribute__((weak,alias("syscall_not_implemented"))); +int _fstat_r(struct _reent *r, int fd, struct stat * st) + __attribute__((weak,alias("syscall_not_implemented"))); +int _stat_r(struct _reent *r, const char * path, struct stat * st) + __attribute__((weak,alias("syscall_not_implemented"))); +int _link_r(struct _reent *r, const char* n1, const char* n2) + __attribute__((weak,alias("syscall_not_implemented"))); +int _unlink_r(struct _reent *r, const char *path) + __attribute__((weak,alias("syscall_not_implemented"))); +int _rename_r(struct _reent *r, const char *src, const char *dst) + __attribute__((weak,alias("syscall_not_implemented"))); + +/* These functions are not expected to be overridden */ +int system(const char* str) + __attribute__((alias("syscall_not_implemented"))); int _system_r(struct _reent *r, const char *str) -{ - __errno_r(r) = ENOSYS; - return -1; -} - + __attribute__((alias("syscall_not_implemented"))); int raise(int sig) -{ - abort(); -} - + __attribute__((alias("syscall_not_implemented_aborts"))); int _raise_r(struct _reent *r, int sig) -{ - abort(); -} - + __attribute__((alias("syscall_not_implemented_aborts"))); void* _sbrk_r(struct _reent *r, ptrdiff_t sz) -{ - abort(); -} - + __attribute__((alias("syscall_not_implemented_aborts"))); int _getpid_r(struct _reent *r) -{ - __errno_r(r) = ENOSYS; - return -1; -} - + __attribute__((alias("syscall_not_implemented"))); int _kill_r(struct _reent *r, int pid, int sig) -{ - __errno_r(r) = ENOSYS; - return -1; -} - + __attribute__((alias("syscall_not_implemented"))); void _exit(int __status) -{ - abort(); -} + __attribute__((alias("syscall_not_implemented_aborts"))); + +#pragma GCC diagnostic pop /* Replaces newlib fcntl, which has been compiled without HAVE_FCNTL */ int fcntl(int fd, int cmd, ...) From 33d3bff1964acf17c9919e19a0f34583a5b050e3 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 20 Mar 2020 14:01:55 +0100 Subject: [PATCH 08/13] newlib: don't require VFS --- components/newlib/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/components/newlib/CMakeLists.txt b/components/newlib/CMakeLists.txt index 03101d79bb..d801910c2d 100644 --- a/components/newlib/CMakeLists.txt +++ b/components/newlib/CMakeLists.txt @@ -20,7 +20,6 @@ list(APPEND ldfragments newlib.lf) idf_component_register(SRCS "${srcs}" INCLUDE_DIRS "${include_dirs}" - REQUIRES vfs PRIV_REQUIRES soc esp_timer LDFRAGMENTS "${ldfragments}") From 18bc25b3a69da8b28a23b96c1e446bb46f6549a5 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 20 Mar 2020 13:23:36 +0100 Subject: [PATCH 09/13] cpu_start: handle CONFIG_VFS_SUPPORT_IO --- components/esp32/cpu_start.c | 15 +++++++++------ components/esp32s2/cpu_start.c | 15 +++++++++------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 6115ec2c77..801b371076 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -354,18 +354,21 @@ void start_cpu0_default(void) esp_efuse_disable_basic_rom_console(); #endif rtc_gpio_force_hold_dis_all(); + +#ifdef CONFIG_VFS_SUPPORT_IO esp_vfs_dev_uart_register(); +#endif // CONFIG_VFS_SUPPORT_IO + +#if defined(CONFIG_VFS_SUPPORT_IO) && !defined(CONFIG_ESP_CONSOLE_UART_NONE) esp_reent_init(_GLOBAL_REENT); -#ifndef CONFIG_ESP_CONSOLE_UART_NONE const char* default_uart_dev = "/dev/uart/" STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM); _GLOBAL_REENT->_stdin = fopen(default_uart_dev, "r"); _GLOBAL_REENT->_stdout = fopen(default_uart_dev, "w"); _GLOBAL_REENT->_stderr = fopen(default_uart_dev, "w"); -#else - _GLOBAL_REENT->_stdin = (FILE*) &__sf_fake_stdin; - _GLOBAL_REENT->_stdout = (FILE*) &__sf_fake_stdout; - _GLOBAL_REENT->_stderr = (FILE*) &__sf_fake_stderr; -#endif +#else // defined(CONFIG_VFS_SUPPORT_IO) && !defined(CONFIG_ESP_CONSOLE_UART_NONE) + _REENT_SMALL_CHECK_INIT(_GLOBAL_REENT); +#endif // defined(CONFIG_VFS_SUPPORT_IO) && !defined(CONFIG_ESP_CONSOLE_UART_NONE) + esp_timer_init(); esp_set_time_from_rtc(); #if CONFIG_APPTRACE_ENABLE diff --git a/components/esp32s2/cpu_start.c b/components/esp32s2/cpu_start.c index 8846d1c1fd..262c8e5ff3 100644 --- a/components/esp32s2/cpu_start.c +++ b/components/esp32s2/cpu_start.c @@ -284,18 +284,21 @@ void start_cpu0_default(void) esp_efuse_disable_basic_rom_console(); #endif rtc_gpio_force_hold_dis_all(); + +#ifdef CONFIG_VFS_SUPPORT_IO esp_vfs_dev_uart_register(); +#endif // CONFIG_VFS_SUPPORT_IO + +#if defined(CONFIG_VFS_SUPPORT_IO) && !defined(CONFIG_ESP_CONSOLE_UART_NONE) esp_reent_init(_GLOBAL_REENT); -#ifndef CONFIG_ESP_CONSOLE_UART_NONE const char *default_uart_dev = "/dev/uart/" STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM); _GLOBAL_REENT->_stdin = fopen(default_uart_dev, "r"); _GLOBAL_REENT->_stdout = fopen(default_uart_dev, "w"); _GLOBAL_REENT->_stderr = fopen(default_uart_dev, "w"); -#else - _GLOBAL_REENT->_stdin = (FILE *) &__sf_fake_stdin; - _GLOBAL_REENT->_stdout = (FILE *) &__sf_fake_stdout; - _GLOBAL_REENT->_stderr = (FILE *) &__sf_fake_stderr; -#endif +#else // defined(CONFIG_VFS_SUPPORT_IO) && !defined(CONFIG_ESP_CONSOLE_UART_NONE) + _REENT_SMALL_CHECK_INIT(_GLOBAL_REENT); +#endif // defined(CONFIG_VFS_SUPPORT_IO) && !defined(CONFIG_ESP_CONSOLE_UART_NONE) + esp_timer_init(); esp_set_time_from_rtc(); #if CONFIG_APPTRACE_ENABLE From 8a47f43cef0ba96c90837d9f54705bbb4310ab0f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 20 Mar 2020 13:23:50 +0100 Subject: [PATCH 10/13] fatfs: handle CONFIG_VFS_FAT_SUPPORT_DIR --- components/fatfs/vfs/vfs_fat.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/components/fatfs/vfs/vfs_fat.c b/components/fatfs/vfs/vfs_fat.c index 5506b1158f..102cb9602b 100644 --- a/components/fatfs/vfs/vfs_fat.c +++ b/components/fatfs/vfs/vfs_fat.c @@ -73,8 +73,9 @@ static ssize_t vfs_fat_pwrite(void *ctx, int fd, const void *src, size_t size, o static int vfs_fat_open(void* ctx, const char * path, int flags, int mode); static int vfs_fat_close(void* ctx, int fd); static int vfs_fat_fstat(void* ctx, int fd, struct stat * st); -static int vfs_fat_stat(void* ctx, const char * path, struct stat * st); static int vfs_fat_fsync(void* ctx, int fd); +#ifdef CONFIG_VFS_SUPPORT_DIR +static int vfs_fat_stat(void* ctx, const char * path, struct stat * st); static int vfs_fat_link(void* ctx, const char* n1, const char* n2); static int vfs_fat_unlink(void* ctx, const char *path); static int vfs_fat_rename(void* ctx, const char *src, const char *dst); @@ -89,6 +90,7 @@ static int vfs_fat_rmdir(void* ctx, const char* name); static int vfs_fat_access(void* ctx, const char *path, int amode); static int vfs_fat_truncate(void* ctx, const char *path, off_t length); static int vfs_fat_utime(void* ctx, const char *path, const struct utimbuf *times); +#endif // CONFIG_VFS_SUPPORT_DIR static vfs_fat_ctx_t* s_fat_ctxs[FF_VOLUMES] = { NULL, NULL }; //backwards-compatibility with esp_vfs_fat_unregister() @@ -136,8 +138,9 @@ esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, siz .open_p = &vfs_fat_open, .close_p = &vfs_fat_close, .fstat_p = &vfs_fat_fstat, - .stat_p = &vfs_fat_stat, .fsync_p = &vfs_fat_fsync, +#ifdef CONFIG_VFS_SUPPORT_DIR + .stat_p = &vfs_fat_stat, .link_p = &vfs_fat_link, .unlink_p = &vfs_fat_unlink, .rename_p = &vfs_fat_rename, @@ -152,6 +155,7 @@ esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, siz .access_p = &vfs_fat_access, .truncate_p = &vfs_fat_truncate, .utime_p = &vfs_fat_utime, +#endif // CONFIG_VFS_SUPPORT_DIR }; size_t ctx_size = sizeof(vfs_fat_ctx_t) + max_files * sizeof(FIL); vfs_fat_ctx_t* fat_ctx = (vfs_fat_ctx_t*) ff_memalloc(ctx_size); @@ -511,6 +515,8 @@ static int vfs_fat_fstat(void* ctx, int fd, struct stat * st) return 0; } +#ifdef CONFIG_VFS_SUPPORT_DIR + static inline mode_t get_stat_mode(bool is_dir) { return S_IRWXU | S_IRWXG | S_IRWXO | @@ -954,3 +960,5 @@ static int vfs_fat_utime(void *ctx, const char *path, const struct utimbuf *time return 0; } + +#endif // CONFIG_VFS_SUPPORT_DIR From dc77ccb24e1f293fee698793bf0919066bef2c5f Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 20 Mar 2020 13:24:18 +0100 Subject: [PATCH 11/13] spiffs: handle CONFIG_VFS_SUPPORT_DIR --- components/spiffs/esp_spiffs.c | 74 +++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/components/spiffs/esp_spiffs.c b/components/spiffs/esp_spiffs.c index e013135b5d..c0205ba8ef 100644 --- a/components/spiffs/esp_spiffs.c +++ b/components/spiffs/esp_spiffs.c @@ -61,6 +61,7 @@ static ssize_t vfs_spiffs_read(void* ctx, int fd, void * dst, size_t size); static int vfs_spiffs_close(void* ctx, int fd); static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode); static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st); +#ifdef CONFIG_VFS_SUPPORT_DIR static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st); static int vfs_spiffs_unlink(void* ctx, const char *path); static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2); @@ -74,11 +75,12 @@ static long vfs_spiffs_telldir(void* ctx, DIR* pdir); static void vfs_spiffs_seekdir(void* ctx, DIR* pdir, long offset); static int vfs_spiffs_mkdir(void* ctx, const char* name, mode_t mode); static int vfs_spiffs_rmdir(void* ctx, const char* name); -static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file f); -static time_t vfs_spiffs_get_mtime(const spiffs_stat* s); #ifdef CONFIG_SPIFFS_USE_MTIME static int vfs_spiffs_utime(void *ctx, const char *path, const struct utimbuf *times); -#endif +#endif // CONFIG_SPIFFS_USE_MTIME +#endif // CONFIG_VFS_SUPPORT_DIR +static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file f); +static time_t vfs_spiffs_get_mtime(const spiffs_stat* s); static esp_spiffs_t * _efs[CONFIG_SPIFFS_MAX_PARTITIONS]; @@ -344,6 +346,7 @@ esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf) .open_p = &vfs_spiffs_open, .close_p = &vfs_spiffs_close, .fstat_p = &vfs_spiffs_fstat, +#ifdef CONFIG_VFS_SUPPORT_DIR .stat_p = &vfs_spiffs_stat, .link_p = &vfs_spiffs_link, .unlink_p = &vfs_spiffs_unlink, @@ -361,6 +364,7 @@ esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf) #else .utime_p = NULL, #endif // CONFIG_SPIFFS_USE_MTIME +#endif // CONFIG_VFS_SUPPORT_DIR }; esp_err_t err = esp_spiffs_init(conf); @@ -540,6 +544,8 @@ static int vfs_spiffs_fstat(void* ctx, int fd, struct stat * st) return res; } +#ifdef CONFIG_VFS_SUPPORT_DIR + static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st) { assert(path); @@ -730,36 +736,6 @@ static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2) return -1; } -static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file fd) -{ -#ifdef CONFIG_SPIFFS_USE_MTIME - spiffs_time_t t = (spiffs_time_t)time(NULL); - spiffs_stat s; - int ret = SPIFFS_OK; - if (CONFIG_SPIFFS_META_LENGTH > sizeof(t)) { - ret = SPIFFS_fstat(fs, fd, &s); - } - if (ret == SPIFFS_OK) { - memcpy(s.meta, &t, sizeof(t)); - ret = SPIFFS_fupdate_meta(fs, fd, s.meta); - } - if (ret != SPIFFS_OK) { - ESP_LOGW(TAG, "Failed to update mtime (%d)", ret); - } -#endif //CONFIG_SPIFFS_USE_MTIME -} - -static time_t vfs_spiffs_get_mtime(const spiffs_stat* s) -{ -#ifdef CONFIG_SPIFFS_USE_MTIME - spiffs_time_t t = 0; - memcpy(&t, s->meta, sizeof(t)); -#else - time_t t = 0; -#endif - return (time_t)t; -} - #ifdef CONFIG_SPIFFS_USE_MTIME static int vfs_spiffs_update_mtime_value(spiffs *fs, const char *path, spiffs_time_t t) { @@ -805,3 +781,35 @@ static int vfs_spiffs_utime(void *ctx, const char *path, const struct utimbuf *t return 0; } #endif //CONFIG_SPIFFS_USE_MTIME + +#endif // CONFIG_VFS_SUPPORT_DIR + +static void vfs_spiffs_update_mtime(spiffs *fs, spiffs_file fd) +{ +#ifdef CONFIG_SPIFFS_USE_MTIME + spiffs_time_t t = (spiffs_time_t)time(NULL); + spiffs_stat s; + int ret = SPIFFS_OK; + if (CONFIG_SPIFFS_META_LENGTH > sizeof(t)) { + ret = SPIFFS_fstat(fs, fd, &s); + } + if (ret == SPIFFS_OK) { + memcpy(s.meta, &t, sizeof(t)); + ret = SPIFFS_fupdate_meta(fs, fd, s.meta); + } + if (ret != SPIFFS_OK) { + ESP_LOGW(TAG, "Failed to update mtime (%d)", ret); + } +#endif //CONFIG_SPIFFS_USE_MTIME +} + +static time_t vfs_spiffs_get_mtime(const spiffs_stat* s) +{ +#ifdef CONFIG_SPIFFS_USE_MTIME + spiffs_time_t t = 0; + memcpy(&t, s->meta, sizeof(t)); +#else + time_t t = 0; +#endif + return (time_t)t; +} From 2548a3e52849b1d458fae1e5e38da48f5471f8cd Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 20 Mar 2020 13:40:54 +0100 Subject: [PATCH 12/13] ci: add startup tests with VFS disabled --- tools/test_apps/system/startup/sdkconfig.ci.no_vfs | 2 ++ tools/test_apps/system/startup/sdkconfig.ci.no_vfs_partial | 4 ++++ 2 files changed, 6 insertions(+) create mode 100644 tools/test_apps/system/startup/sdkconfig.ci.no_vfs create mode 100644 tools/test_apps/system/startup/sdkconfig.ci.no_vfs_partial diff --git a/tools/test_apps/system/startup/sdkconfig.ci.no_vfs b/tools/test_apps/system/startup/sdkconfig.ci.no_vfs new file mode 100644 index 0000000000..1f16f62e7b --- /dev/null +++ b/tools/test_apps/system/startup/sdkconfig.ci.no_vfs @@ -0,0 +1,2 @@ +# Disable VFS support +CONFIG_VFS_SUPPORT_IO=n diff --git a/tools/test_apps/system/startup/sdkconfig.ci.no_vfs_partial b/tools/test_apps/system/startup/sdkconfig.ci.no_vfs_partial new file mode 100644 index 0000000000..10d4c49b2e --- /dev/null +++ b/tools/test_apps/system/startup/sdkconfig.ci.no_vfs_partial @@ -0,0 +1,4 @@ +# Disable VFS support except for basic I/O +CONFIG_VFS_SUPPORT_SELECT=n +CONFIG_VFS_SUPPORT_DIR=n +CONFIG_VFS_SUPPORT_TERMIOS=n From 1e462be4ab519ce2e54a95cb3b4426e914b76f18 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 20 Mar 2020 13:42:53 +0100 Subject: [PATCH 13/13] examples: print free heap size in hello_world example --- examples/get-started/hello_world/main/hello_world_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/get-started/hello_world/main/hello_world_main.c b/examples/get-started/hello_world/main/hello_world_main.c index e77056c6cb..1bbb227906 100644 --- a/examples/get-started/hello_world/main/hello_world_main.c +++ b/examples/get-started/hello_world/main/hello_world_main.c @@ -31,6 +31,8 @@ void app_main(void) printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024), (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external"); + printf("Free heap: %d\n", esp_get_free_heap_size()); + for (int i = 10; i >= 0; i--) { printf("Restarting in %d seconds...\n", i); vTaskDelay(1000 / portTICK_PERIOD_MS);