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
This commit is contained in:
Ivan Grokhotkov 2020-03-20 13:35:00 +01:00
parent 45e50f1f2a
commit a3c0bcb0a0
5 changed files with 151 additions and 148 deletions

View file

@ -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

View file

@ -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()

View file

@ -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)
{

View file

@ -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";

View file

@ -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,