diff --git a/components/vfs/Kconfig b/components/vfs/Kconfig index c18397bdc..03af01de1 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 fc720e626..2fadb4f95 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 93bc00bab..7118e83db 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 d97fa9188..f61407871 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 e882d5e5e..f93814a00 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,