Merge branch 'feature/vfs_optional' into 'master'

vfs: add option to disable VFS layer, saving some RAM and code size

See merge request espressif/esp-idf!7950
This commit is contained in:
Ivan Grokhotkov 2020-03-24 21:11:28 +08:00
commit 55c3c2b73d
27 changed files with 602 additions and 418 deletions

View file

@ -354,18 +354,21 @@ void start_cpu0_default(void)
esp_efuse_disable_basic_rom_console(); esp_efuse_disable_basic_rom_console();
#endif #endif
rtc_gpio_force_hold_dis_all(); rtc_gpio_force_hold_dis_all();
#ifdef CONFIG_VFS_SUPPORT_IO
esp_vfs_dev_uart_register(); 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); esp_reent_init(_GLOBAL_REENT);
#ifndef CONFIG_ESP_CONSOLE_UART_NONE
const char* default_uart_dev = "/dev/uart/" STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM); const char* default_uart_dev = "/dev/uart/" STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM);
_GLOBAL_REENT->_stdin = fopen(default_uart_dev, "r"); _GLOBAL_REENT->_stdin = fopen(default_uart_dev, "r");
_GLOBAL_REENT->_stdout = fopen(default_uart_dev, "w"); _GLOBAL_REENT->_stdout = fopen(default_uart_dev, "w");
_GLOBAL_REENT->_stderr = fopen(default_uart_dev, "w"); _GLOBAL_REENT->_stderr = fopen(default_uart_dev, "w");
#else #else // defined(CONFIG_VFS_SUPPORT_IO) && !defined(CONFIG_ESP_CONSOLE_UART_NONE)
_GLOBAL_REENT->_stdin = (FILE*) &__sf_fake_stdin; _REENT_SMALL_CHECK_INIT(_GLOBAL_REENT);
_GLOBAL_REENT->_stdout = (FILE*) &__sf_fake_stdout; #endif // defined(CONFIG_VFS_SUPPORT_IO) && !defined(CONFIG_ESP_CONSOLE_UART_NONE)
_GLOBAL_REENT->_stderr = (FILE*) &__sf_fake_stderr;
#endif
esp_timer_init(); esp_timer_init();
esp_set_time_from_rtc(); esp_set_time_from_rtc();
#if CONFIG_APPTRACE_ENABLE #if CONFIG_APPTRACE_ENABLE

View file

@ -284,18 +284,21 @@ void start_cpu0_default(void)
esp_efuse_disable_basic_rom_console(); esp_efuse_disable_basic_rom_console();
#endif #endif
rtc_gpio_force_hold_dis_all(); rtc_gpio_force_hold_dis_all();
#ifdef CONFIG_VFS_SUPPORT_IO
esp_vfs_dev_uart_register(); 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); esp_reent_init(_GLOBAL_REENT);
#ifndef CONFIG_ESP_CONSOLE_UART_NONE
const char *default_uart_dev = "/dev/uart/" STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM); const char *default_uart_dev = "/dev/uart/" STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM);
_GLOBAL_REENT->_stdin = fopen(default_uart_dev, "r"); _GLOBAL_REENT->_stdin = fopen(default_uart_dev, "r");
_GLOBAL_REENT->_stdout = fopen(default_uart_dev, "w"); _GLOBAL_REENT->_stdout = fopen(default_uart_dev, "w");
_GLOBAL_REENT->_stderr = fopen(default_uart_dev, "w"); _GLOBAL_REENT->_stderr = fopen(default_uart_dev, "w");
#else #else // defined(CONFIG_VFS_SUPPORT_IO) && !defined(CONFIG_ESP_CONSOLE_UART_NONE)
_GLOBAL_REENT->_stdin = (FILE *) &__sf_fake_stdin; _REENT_SMALL_CHECK_INIT(_GLOBAL_REENT);
_GLOBAL_REENT->_stdout = (FILE *) &__sf_fake_stdout; #endif // defined(CONFIG_VFS_SUPPORT_IO) && !defined(CONFIG_ESP_CONSOLE_UART_NONE)
_GLOBAL_REENT->_stderr = (FILE *) &__sf_fake_stderr;
#endif
esp_timer_init(); esp_timer_init();
esp_set_time_from_rtc(); esp_set_time_from_rtc();
#if CONFIG_APPTRACE_ENABLE #if CONFIG_APPTRACE_ENABLE

View file

@ -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_open(void* ctx, const char * path, int flags, int mode);
static int vfs_fat_close(void* ctx, int fd); 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_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); 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_link(void* ctx, const char* n1, const char* n2);
static int vfs_fat_unlink(void* ctx, const char *path); static int vfs_fat_unlink(void* ctx, const char *path);
static int vfs_fat_rename(void* ctx, const char *src, const char *dst); 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_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_truncate(void* ctx, const char *path, off_t length);
static int vfs_fat_utime(void* ctx, const char *path, const struct utimbuf *times); 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 }; static vfs_fat_ctx_t* s_fat_ctxs[FF_VOLUMES] = { NULL, NULL };
//backwards-compatibility with esp_vfs_fat_unregister() //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, .open_p = &vfs_fat_open,
.close_p = &vfs_fat_close, .close_p = &vfs_fat_close,
.fstat_p = &vfs_fat_fstat, .fstat_p = &vfs_fat_fstat,
.stat_p = &vfs_fat_stat,
.fsync_p = &vfs_fat_fsync, .fsync_p = &vfs_fat_fsync,
#ifdef CONFIG_VFS_SUPPORT_DIR
.stat_p = &vfs_fat_stat,
.link_p = &vfs_fat_link, .link_p = &vfs_fat_link,
.unlink_p = &vfs_fat_unlink, .unlink_p = &vfs_fat_unlink,
.rename_p = &vfs_fat_rename, .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, .access_p = &vfs_fat_access,
.truncate_p = &vfs_fat_truncate, .truncate_p = &vfs_fat_truncate,
.utime_p = &vfs_fat_utime, .utime_p = &vfs_fat_utime,
#endif // CONFIG_VFS_SUPPORT_DIR
}; };
size_t ctx_size = sizeof(vfs_fat_ctx_t) + max_files * sizeof(FIL); 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); 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; return 0;
} }
#ifdef CONFIG_VFS_SUPPORT_DIR
static inline mode_t get_stat_mode(bool is_dir) static inline mode_t get_stat_mode(bool is_dir)
{ {
return S_IRWXU | S_IRWXG | S_IRWXO | 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; return 0;
} }
#endif // CONFIG_VFS_SUPPORT_DIR

View file

@ -85,7 +85,6 @@ set(srcs
"lwip/src/netif/ppp/upap.c" "lwip/src/netif/ppp/upap.c"
"lwip/src/netif/ppp/utils.c" "lwip/src/netif/ppp/utils.c"
"lwip/src/netif/ppp/vj.c" "lwip/src/netif/ppp/vj.c"
"port/esp32/vfs_lwip.c"
"port/esp32/debug/lwip_debug.c" "port/esp32/debug/lwip_debug.c"
"port/esp32/freertos/sys_arch.c" "port/esp32/freertos/sys_arch.c"
"port/esp32/netif/dhcp_state.c" "port/esp32/netif/dhcp_state.c"
@ -129,6 +128,12 @@ if(CONFIG_ETH_ENABLED)
list(APPEND srcs "port/esp32/netif/ethernetif.c") list(APPEND srcs "port/esp32/netif/ethernetif.c")
endif() 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}" idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${include_dirs}" INCLUDE_DIRS "${include_dirs}"
LDFRAGMENTS linker.lf LDFRAGMENTS linker.lf

View file

@ -63,9 +63,12 @@ menu "LWIP"
to 16. to 16.
config LWIP_USE_ONLY_LWIP_SELECT config LWIP_USE_ONLY_LWIP_SELECT
bool "Support LWIP socket select() only" bool "Support LWIP socket select() only (DEPRECATED)"
default n default n
help 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 The virtual filesystem layer of select() redirects sockets to
lwip_select() and non-socket file descriptors to their respective driver lwip_select() and non-socket file descriptors to their respective driver
implementations. If this option is enabled then all calls of select() implementations. If this option is enabled then all calls of select()

View file

@ -29,6 +29,11 @@ COMPONENT_SRCDIRS := \
ifndef CONFIG_IDF_TARGET_ESP32 ifndef CONFIG_IDF_TARGET_ESP32
COMPONENT_OBJEXCLUDE := port/esp32/netif/ethernetif.o COMPONENT_OBJEXCLUDE := port/esp32/netif/ethernetif.o
endif 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 ifdef CONFIG_LWIP_PPP_SUPPORT
COMPONENT_SRCDIRS += lwip/src/netif/ppp lwip/src/netif/ppp/polarssl COMPONENT_SRCDIRS += lwip/src/netif/ppp lwip/src/netif/ppp/polarssl

View file

@ -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 <string.h>
#include <stdbool.h>
#include <stdarg.h>
#include <sys/errno.h>
#include <sys/lock.h>
#include <sys/fcntl.h>
#include <sys/ioctl.h>
#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 */
}

View file

@ -18,15 +18,20 @@
#include <sys/errno.h> #include <sys/errno.h>
#include <sys/lock.h> #include <sys/lock.h>
#include <sys/fcntl.h> #include <sys/fcntl.h>
#include "esp_vfs.h"
#include "esp_vfs_dev.h"
#include "esp_attr.h" #include "esp_attr.h"
#include "lwip/sockets.h" #include "esp_vfs.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#include "lwip/sockets.h"
#include "lwip/sys.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"); _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) static void lwip_stop_socket_select(void *sem)
{ {
sys_sem_signal(sem); //socket_select will return 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(); 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) 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, .read = &lwip_read,
.fcntl = &lwip_fcntl_r_wrapper, .fcntl = &lwip_fcntl_r_wrapper,
.ioctl = &lwip_ioctl_r_wrapper, .ioctl = &lwip_ioctl_r_wrapper,
#ifdef CONFIG_VFS_SUPPORT_SELECT
.socket_select = &lwip_select, .socket_select = &lwip_select,
.get_socket_select_semaphore = &lwip_get_socket_select_semaphore, .get_socket_select_semaphore = &lwip_get_socket_select_semaphore,
.stop_socket_select = &lwip_stop_socket_select, .stop_socket_select = &lwip_stop_socket_select,
.stop_socket_select_isr = &lwip_stop_socket_select_isr, .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 /* Non-LWIP file descriptors are from 0 to (LWIP_SOCKET_OFFSET-1). LWIP
* file descriptors are registered from LWIP_SOCKET_OFFSET to * file descriptors are registered from LWIP_SOCKET_OFFSET to

View file

@ -3,17 +3,13 @@ set(srcs
"heap.c" "heap.c"
"locks.c" "locks.c"
"poll.c" "poll.c"
"pread.c"
"pwrite.c"
"pthread.c" "pthread.c"
"random.c" "random.c"
"reent_init.c" "reent_init.c"
"select.c"
"syscall_table.c" "syscall_table.c"
"syscalls.c" "syscalls.c"
"termios.c" "termios.c"
"time.c" "time.c")
"utime.c")
set(include_dirs platform_include) set(include_dirs platform_include)
if(CONFIG_SPIRAM_CACHE_WORKAROUND) if(CONFIG_SPIRAM_CACHE_WORKAROUND)
@ -24,7 +20,6 @@ list(APPEND ldfragments newlib.lf)
idf_component_register(SRCS "${srcs}" idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${include_dirs}" INCLUDE_DIRS "${include_dirs}"
REQUIRES vfs
PRIV_REQUIRES soc esp_timer PRIV_REQUIRES soc esp_timer
LDFRAGMENTS "${ldfragments}") LDFRAGMENTS "${ldfragments}")

View file

@ -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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with 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 // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include <stddef.h>
#include <sys/poll.h> #include <sys/poll.h>
#include "esp_vfs.h" #include <sys/select.h>
#include <sys/errno.h>
#include <sys/param.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout) 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;
} }

View file

@ -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 <unistd.h>
#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);
}

View file

@ -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 <unistd.h>
#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);
}

View file

@ -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 <sys/select.h>
#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
}

View file

@ -59,23 +59,23 @@ static struct syscall_stub_table s_stub_table = {
._calloc_r = &_calloc_r, ._calloc_r = &_calloc_r,
._abort = &abort, ._abort = &abort,
._system_r = &_system_r, ._system_r = &_system_r,
._rename_r = &esp_vfs_rename, ._rename_r = &_rename_r,
._times_r = &_times_r, ._times_r = &_times_r,
._gettimeofday_r = &_gettimeofday_r, ._gettimeofday_r = &_gettimeofday_r,
._raise_r = &raise_r_stub, ._raise_r = &raise_r_stub,
._unlink_r = &esp_vfs_unlink, ._unlink_r = &_unlink_r,
._link_r = &esp_vfs_link, ._link_r = &_link_r,
._stat_r = &esp_vfs_stat, ._stat_r = &_stat_r,
._fstat_r = &esp_vfs_fstat, ._fstat_r = &_fstat_r,
._sbrk_r = &_sbrk_r, ._sbrk_r = &_sbrk_r,
._getpid_r = &_getpid_r, ._getpid_r = &_getpid_r,
._kill_r = &_kill_r, ._kill_r = &_kill_r,
._exit_r = NULL, // never called in ROM ._exit_r = NULL, // never called in ROM
._close_r = &esp_vfs_close, ._close_r = &_close_r,
._open_r = &esp_vfs_open, ._open_r = &_open_r,
._write_r = (int (*)(struct _reent *r, int, const void *, int)) &esp_vfs_write, ._write_r = (int (*)(struct _reent *r, int, const void *, int)) &_write_r,
._lseek_r = (int (*)(struct _reent *r, int, int, int)) &esp_vfs_lseek, ._lseek_r = (int (*)(struct _reent *r, int, int, int)) &_lseek_r,
._read_r = (int (*)(struct _reent *r, int, void *, int)) &esp_vfs_read, ._read_r = (int (*)(struct _reent *r, int, void *, int)) &_read_r,
._lock_init = &_lock_init, ._lock_init = &_lock_init,
._lock_init_recursive = &_lock_init_recursive, ._lock_init_recursive = &_lock_init_recursive,
._lock_close = &_lock_close, ._lock_close = &_lock_close,

View file

@ -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"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with 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 // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h> #include <stdarg.h>
@ -21,51 +20,111 @@
#include <errno.h> #include <errno.h>
#include <reent.h> #include <reent.h>
#include <sys/fcntl.h> #include <sys/fcntl.h>
#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; errno = ENOSYS;
return -1; 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) int _system_r(struct _reent *r, const char *str)
{ __attribute__((alias("syscall_not_implemented")));
__errno_r(r) = ENOSYS;
return -1;
}
int raise(int sig) int raise(int sig)
{ __attribute__((alias("syscall_not_implemented_aborts")));
abort();
}
int _raise_r(struct _reent *r, int sig) int _raise_r(struct _reent *r, int sig)
{ __attribute__((alias("syscall_not_implemented_aborts")));
abort();
}
void* _sbrk_r(struct _reent *r, ptrdiff_t sz) void* _sbrk_r(struct _reent *r, ptrdiff_t sz)
{ __attribute__((alias("syscall_not_implemented_aborts")));
abort();
}
int _getpid_r(struct _reent *r) int _getpid_r(struct _reent *r)
{ __attribute__((alias("syscall_not_implemented")));
__errno_r(r) = ENOSYS;
return -1;
}
int _kill_r(struct _reent *r, int pid, int sig) int _kill_r(struct _reent *r, int pid, int sig)
{ __attribute__((alias("syscall_not_implemented")));
__errno_r(r) = ENOSYS;
return -1;
}
void _exit(int __status) void _exit(int __status)
{ __attribute__((alias("syscall_not_implemented_aborts")));
abort();
} #pragma GCC diagnostic pop
/* Replaces newlib fcntl, which has been compiled without HAVE_FCNTL */ /* Replaces newlib fcntl, which has been compiled without HAVE_FCNTL */
int fcntl(int fd, int cmd, ...) int fcntl(int fd, int cmd, ...)

View file

@ -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 <utime.h>
#include "esp_vfs.h"
int utime(const char *path, const struct utimbuf *times)
{
return esp_vfs_utime(path, times);
}

View file

@ -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 int vfs_spiffs_close(void* ctx, int fd);
static off_t vfs_spiffs_lseek(void* ctx, int fd, off_t offset, int mode); 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); 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_stat(void* ctx, const char * path, struct stat * st);
static int vfs_spiffs_unlink(void* ctx, const char *path); static int vfs_spiffs_unlink(void* ctx, const char *path);
static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2); 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 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_mkdir(void* ctx, const char* name, mode_t mode);
static int vfs_spiffs_rmdir(void* ctx, const char* name); 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 #ifdef CONFIG_SPIFFS_USE_MTIME
static int vfs_spiffs_utime(void *ctx, const char *path, const struct utimbuf *times); 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]; 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, .open_p = &vfs_spiffs_open,
.close_p = &vfs_spiffs_close, .close_p = &vfs_spiffs_close,
.fstat_p = &vfs_spiffs_fstat, .fstat_p = &vfs_spiffs_fstat,
#ifdef CONFIG_VFS_SUPPORT_DIR
.stat_p = &vfs_spiffs_stat, .stat_p = &vfs_spiffs_stat,
.link_p = &vfs_spiffs_link, .link_p = &vfs_spiffs_link,
.unlink_p = &vfs_spiffs_unlink, .unlink_p = &vfs_spiffs_unlink,
@ -361,6 +364,7 @@ esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf)
#else #else
.utime_p = NULL, .utime_p = NULL,
#endif // CONFIG_SPIFFS_USE_MTIME #endif // CONFIG_SPIFFS_USE_MTIME
#endif // CONFIG_VFS_SUPPORT_DIR
}; };
esp_err_t err = esp_spiffs_init(conf); 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; return res;
} }
#ifdef CONFIG_VFS_SUPPORT_DIR
static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st) static int vfs_spiffs_stat(void* ctx, const char * path, struct stat * st)
{ {
assert(path); assert(path);
@ -730,36 +736,6 @@ static int vfs_spiffs_link(void* ctx, const char* n1, const char* n2)
return -1; 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 #ifdef CONFIG_SPIFFS_USE_MTIME
static int vfs_spiffs_update_mtime_value(spiffs *fs, const char *path, spiffs_time_t t) 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; return 0;
} }
#endif //CONFIG_SPIFFS_USE_MTIME #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;
}

View file

@ -1,7 +1,56 @@
menu "Virtual file system" 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 config VFS_SUPPRESS_SELECT_DEBUG_OUTPUT
bool "Suppress select() related debug outputs" bool "Suppress select() related debug outputs"
depends on VFS_SUPPORT_SELECT
default y default y
help help
Select() related functions might produce an unconveniently lot of Select() related functions might produce an unconveniently lot of
@ -10,12 +59,16 @@ menu "Virtual file system"
option. option.
config VFS_SUPPORT_TERMIOS config VFS_SUPPORT_TERMIOS
bool "Add support for termios.h" bool "Provide termios.h functions"
default y default y
# Very likely, only makes sense for UART VFS driver, which itself depends on VFS_SUPPORT_IO
depends on VFS_SUPPORT_IO
help help
Disabling this option can save memory when the support for termios.h is not required. Disabling this option can save memory when the support for termios.h is not required.
menu "Host File System I/O (Semihosting)" menu "Host File System I/O (Semihosting)"
depends on VFS_SUPPORT_IO
config SEMIHOSTFS_MAX_MOUNT_POINTS config SEMIHOSTFS_MAX_MOUNT_POINTS
int "Maximum number of the host filesystem mount points" int "Maximum number of the host filesystem mount points"
default 1 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_p)(void* ctx, int fd, struct stat * st); /*!< fstat with context pointer */
int (*fstat)(int fd, struct stat * st); /*!< fstat without context pointer */ int (*fstat)(int fd, struct stat * st); /*!< fstat without context pointer */
}; };
#ifdef CONFIG_VFS_SUPPORT_DIR
union { union {
int (*stat_p)(void* ctx, const char * path, struct stat * st); /*!< stat with context pointer */ 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 */ 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_p)(void* ctx, const char* name); /*!< rmdir with context pointer */
int (*rmdir)(const char* name); /*!< rmdir without context pointer */ int (*rmdir)(const char* name); /*!< rmdir without context pointer */
}; };
#endif // CONFIG_VFS_SUPPORT_DIR
union { union {
int (*fcntl_p)(void* ctx, int fd, int cmd, int arg); /*!< fcntl with context pointer */ 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 */ 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_p)(void* ctx, int fd); /*!< fsync with context pointer */
int (*fsync)(int fd); /*!< fsync without context pointer */ int (*fsync)(int fd); /*!< fsync without context pointer */
}; };
#ifdef CONFIG_VFS_SUPPORT_DIR
union { union {
int (*access_p)(void* ctx, const char *path, int amode); /*!< access with context pointer */ 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 */ 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_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 */ int (*utime)(const char *path, const struct utimbuf *times); /*!< utime without context pointer */
}; };
#endif // CONFIG_VFS_SUPPORT_DIR
#ifdef CONFIG_VFS_SUPPORT_TERMIOS #ifdef CONFIG_VFS_SUPPORT_TERMIOS
union { union {
int (*tcsetattr_p)(void *ctx, int fd, int optional_actions, const struct termios *p); /*!< tcsetattr with context pointer */ 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 */ int (*tcsendbreak)(int fd, int duration); /*!< tcsendbreak without context pointer */
}; };
#endif // CONFIG_VFS_SUPPORT_TERMIOS #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 */ /** 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); 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 */ /** 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); void* (*get_socket_select_semaphore)(void);
/** get_socket_select_semaphore returns semaphore allocated in the socket driver; set only for the socket driver */ /** 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); esp_err_t (*end_select)(void *end_select_args);
#endif // CONFIG_VFS_SUPPORT_SELECT
} esp_vfs_t; } 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); 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() * @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; 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) int esp_vfs_stat(struct _reent *r, const char * path, struct stat * st)
{ {
const vfs_entry_t* vfs = get_vfs_for_path(path); 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; 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) int esp_vfs_link(struct _reent *r, const char* n1, const char* n2)
{ {
const vfs_entry_t* vfs = get_vfs_for_path(n1); const vfs_entry_t* vfs = get_vfs_for_path(n1);
@ -571,35 +631,7 @@ int esp_vfs_rename(struct _reent *r, const char *src, const char *dst)
return ret; return ret;
} }
/* Create aliases for newlib syscalls DIR* esp_vfs_opendir(const char* name)
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)
{ {
const vfs_entry_t* vfs = get_vfs_for_path(name); const vfs_entry_t* vfs = get_vfs_for_path(name);
struct _reent* r = __getreent(); struct _reent* r = __getreent();
@ -616,7 +648,7 @@ DIR* opendir(const char* name)
return ret; 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); const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
struct _reent* r = __getreent(); struct _reent* r = __getreent();
@ -629,7 +661,7 @@ struct dirent* readdir(DIR* pdir)
return ret; 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); const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
struct _reent* r = __getreent(); struct _reent* r = __getreent();
@ -642,7 +674,7 @@ int readdir_r(DIR* pdir, struct dirent* entry, struct dirent** out_dirent)
return ret; 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); const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
struct _reent* r = __getreent(); struct _reent* r = __getreent();
@ -655,7 +687,7 @@ long telldir(DIR* pdir)
return ret; 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); const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
struct _reent* r = __getreent(); struct _reent* r = __getreent();
@ -666,12 +698,12 @@ void seekdir(DIR* pdir, long loc)
CHECK_AND_CALLV(r, vfs, seekdir, pdir, loc); CHECK_AND_CALLV(r, vfs, seekdir, pdir, loc);
} }
void rewinddir(DIR* pdir) void esp_vfs_rewinddir(DIR* pdir)
{ {
seekdir(pdir, 0); 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); const vfs_entry_t* vfs = get_vfs_for_index(pdir->dd_vfs_idx);
struct _reent* r = __getreent(); struct _reent* r = __getreent();
@ -684,7 +716,7 @@ int closedir(DIR* pdir)
return ret; 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); const vfs_entry_t* vfs = get_vfs_for_path(name);
struct _reent* r = __getreent(); struct _reent* r = __getreent();
@ -698,7 +730,7 @@ int mkdir(const char* name, mode_t mode)
return ret; return ret;
} }
int rmdir(const char* name) int esp_vfs_rmdir(const char* name)
{ {
const vfs_entry_t* vfs = get_vfs_for_path(name); const vfs_entry_t* vfs = get_vfs_for_path(name);
struct _reent* r = __getreent(); struct _reent* r = __getreent();
@ -712,51 +744,7 @@ int rmdir(const char* name)
return ret; return ret;
} }
int _fcntl_r(struct _reent *r, int fd, int cmd, int arg) int esp_vfs_access(const char *path, int amode)
{
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; int ret;
const vfs_entry_t* vfs = get_vfs_for_path(path); const vfs_entry_t* vfs = get_vfs_for_path(path);
@ -770,7 +758,7 @@ int access(const char *path, int amode)
return ret; return ret;
} }
int truncate(const char *path, off_t length) int esp_vfs_truncate(const char *path, off_t length)
{ {
int ret; int ret;
const vfs_entry_t* vfs = get_vfs_for_path(path); const vfs_entry_t* vfs = get_vfs_for_path(path);
@ -784,6 +772,10 @@ int truncate(const char *path, off_t length)
return ret; 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) 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) { for (int i = 0; i < end_index; ++i) {
@ -1074,7 +1066,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 #ifdef CONFIG_VFS_SUPPORT_TERMIOS
int tcgetattr(int fd, struct termios *p) int tcgetattr(int fd, struct termios *p)
{ {
const vfs_entry_t* vfs = get_vfs_for_fd(fd); const vfs_entry_t* vfs = get_vfs_for_fd(fd);
@ -1174,95 +1169,77 @@ int tcsendbreak(int fd, int duration)
} }
#endif // CONFIG_VFS_SUPPORT_TERMIOS #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) /* Create aliases for newlib syscalls
{
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) { These functions are also available in ROM as stubs which use the syscall table, but linking them
__errno_r(r) = ENOENT; directly here saves an additional function call when a software function is linked to one, and
return -1; 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
FD_ZERO(&readfds); #ifdef CONFIG_VFS_SUPPORT_SELECT
FD_ZERO(&writefds); int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout)
FD_ZERO(&errorfds); __attribute__((alias("esp_vfs_select")));
#endif // CONFIG_VFS_SUPPORT_SELECT
for (int i = 0; i < nfds; ++i) { #ifdef CONFIG_VFS_SUPPORT_DIR
fds[i].revents = 0; int _stat_r(struct _reent *r, const char * path, struct stat * st)
__attribute__((alias("esp_vfs_stat")));
if (fds[i].fd < 0) { int _link_r(struct _reent *r, const char* n1, const char* n2)
// revents should remain 0 and events ignored (according to the documentation of poll()). __attribute__((alias("esp_vfs_link")));
continue; 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)
if (fds[i].fd >= MAX_FDS) { __attribute__((alias("esp_vfs_rename")));
fds[i].revents |= POLLNVAL; int truncate(const char *path, off_t length)
++ret; __attribute__((alias("esp_vfs_truncate")));
continue; int access(const char *path, int amode)
} __attribute__((alias("esp_vfs_access")));
int utime(const char *path, const struct utimbuf *times)
if (fds[i].events & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { __attribute__((alias("esp_vfs_utime")));
FD_SET(fds[i].fd, &readfds); int rmdir(const char* name)
FD_SET(fds[i].fd, &errorfds); __attribute__((alias("esp_vfs_rmdir")));
max_fd = MAX(max_fd, fds[i].fd); int mkdir(const char* name, mode_t mode)
} __attribute__((alias("esp_vfs_mkdir")));
DIR* opendir(const char* name)
if (fds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) { __attribute__((alias("esp_vfs_opendir")));
FD_SET(fds[i].fd, &writefds); int closedir(DIR* pdir)
FD_SET(fds[i].fd, &errorfds); __attribute__((alias("esp_vfs_closedir")));
max_fd = MAX(max_fd, fds[i].fd); 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")));
const int select_ret = esp_vfs_select(max_fd + 1, &readfds, &writefds, &errorfds, timeout < 0 ? NULL: &tv); long telldir(DIR* pdir)
__attribute__((alias("esp_vfs_telldir")));
if (select_ret > 0) { void seekdir(DIR* pdir, long loc)
ret += select_ret; __attribute__((alias("esp_vfs_seekdir")));
void rewinddir(DIR* pdir)
for (int i = 0; i < nfds; ++i) { __attribute__((alias("esp_vfs_rewinddir")));
if (FD_ISSET(fds[i].fd, &readfds)) { #endif // CONFIG_VFS_SUPPORT_DIR
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) void vfs_include_syscalls_impl(void)
{ {

View file

@ -23,6 +23,14 @@
#include "freertos/task.h" #include "freertos/task.h"
#include "esp_vfs.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 #define LOG_LOCAL_LEVEL ESP_LOG_NONE
#include "esp_log.h" #include "esp_log.h"
const static char *TAG = "esp_semihost"; const static char *TAG = "esp_semihost";

View file

@ -116,6 +116,8 @@ static vfs_uart_context_t* s_ctx[UART_NUM] = {
#endif #endif
}; };
#ifdef CONFIG_VFS_SUPPORT_SELECT
typedef struct { typedef struct {
esp_vfs_select_sem_t select_sem; esp_vfs_select_sem_t select_sem;
fd_set *readfds; 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); 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) static int uart_open(const char * path, int flags, int mode)
{ {
// this is fairly primitive, we should check if file is opened read only, // 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; return result;
} }
#ifdef CONFIG_VFS_SUPPORT_DIR
static int uart_access(const char *path, int amode) static int uart_access(const char *path, int amode)
{ {
int ret = -1; int ret = -1;
@ -340,6 +346,8 @@ static int uart_access(const char *path, int amode)
return ret; return ret;
} }
#endif // CONFIG_VFS_SUPPORT_DIR
static int uart_fsync(int fd) static int uart_fsync(int fd)
{ {
assert(fd >= 0 && fd < 3); assert(fd >= 0 && fd < 3);
@ -349,6 +357,8 @@ static int uart_fsync(int fd)
return 0; return 0;
} }
#ifdef CONFIG_VFS_SUPPORT_SELECT
static esp_err_t register_select(uart_select_args_t *args) static esp_err_t register_select(uart_select_args_t *args)
{ {
esp_err_t ret = ESP_ERR_INVALID_ARG; 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; return ret;
} }
#endif // CONFIG_VFS_SUPPORT_SELECT
#ifdef CONFIG_VFS_SUPPORT_TERMIOS #ifdef CONFIG_VFS_SUPPORT_TERMIOS
static int uart_tcsetattr(int fd, int optional_actions, const struct termios *p) 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, .read = &uart_read,
.fcntl = &uart_fcntl, .fcntl = &uart_fcntl,
.fsync = &uart_fsync, .fsync = &uart_fsync,
#ifdef CONFIG_VFS_SUPPORT_DIR
.access = &uart_access, .access = &uart_access,
#endif // CONFIG_VFS_SUPPORT_DIR
#ifdef CONFIG_VFS_SUPPORT_SELECT
.start_select = &uart_start_select, .start_select = &uart_start_select,
.end_select = &uart_end_select, .end_select = &uart_end_select,
#endif // CONFIG_VFS_SUPPORT_SELECT
#ifdef CONFIG_VFS_SUPPORT_TERMIOS #ifdef CONFIG_VFS_SUPPORT_TERMIOS
.tcsetattr = &uart_tcsetattr, .tcsetattr = &uart_tcsetattr,
.tcgetattr = &uart_tcgetattr, .tcgetattr = &uart_tcgetattr,

View file

@ -31,6 +31,8 @@ void app_main(void)
printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024), printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external"); (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--) { for (int i = 10; i >= 0; i--) {
printf("Restarting in %d seconds...\n", i); printf("Restarting in %d seconds...\n", i);
vTaskDelay(1000 / portTICK_PERIOD_MS); vTaskDelay(1000 / portTICK_PERIOD_MS);

View file

@ -0,0 +1,2 @@
# Disable VFS support
CONFIG_VFS_SUPPORT_IO=n

View file

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