diff --git a/components/esp_system/system_init_fn.txt b/components/esp_system/system_init_fn.txt index b841acd726..1ba1dcc6dc 100644 --- a/components/esp_system/system_init_fn.txt +++ b/components/esp_system/system_init_fn.txt @@ -50,6 +50,7 @@ CORE: 105: init_newlib_time in components/esp_system/startup_funcs.c on BIT(0) CORE: 110: init_vfs_uart in components/esp_driver_uart/src/uart_vfs.c on BIT(0) CORE: 111: init_vfs_usj in components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_vfs.c on BIT(0) CORE: 112: init_vfs_usj_sec in components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_vfs.c on BIT(0) +CORE: 113: init_vfs_nullfs in components/vfs/nullfs.c on BIT(0) CORE: 114: init_vfs_console in components/esp_vfs_console/vfs_console.c on BIT(0) CORE: 115: init_newlib_stdio in components/newlib/newlib_init.c on BIT(0) diff --git a/components/esp_system/test_apps/console/main/test_app_main.c b/components/esp_system/test_apps/console/main/test_app_main.c index 0740cdfc50..48ffcb2b3f 100644 --- a/components/esp_system/test_apps/console/main/test_app_main.c +++ b/components/esp_system/test_apps/console/main/test_app_main.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ #include +#include #include "sdkconfig.h" #include "esp_rom_uart.h" @@ -15,6 +16,9 @@ #include "driver/uart.h" #include "soc/uart_channel.h" +#include +#include + #if CONFIG_ESP_CONSOLE_NONE /* Set up UART on UART_0 (console) to be able to notify pytest test case that app booted successfully @@ -49,6 +53,15 @@ void app_main(void) { printf("Hello World\n"); + int fd = open("/dev/null", O_RDWR); + assert(fd >= 0); // Standard check + + // Check if correct file descriptor is returned + // In this case it should be neither of 0, 1, 2 (== stdin, stdout, stderr) + assert(fd > 2); + + close(fd); + #if CONFIG_ESP_CONSOLE_NONE console_none_print(); #endif // CONFIG_ESP_CONSOLE_NONE diff --git a/components/esp_vfs_console/vfs_console.c b/components/esp_vfs_console/vfs_console.c index 980514c1d4..6e754e574b 100644 --- a/components/esp_vfs_console/vfs_console.c +++ b/components/esp_vfs_console/vfs_console.c @@ -15,6 +15,7 @@ #include "esp_vfs_console.h" #include "sdkconfig.h" #include "esp_private/startup_internal.h" +#include "esp_vfs_null.h" #define STRINGIFY(s) STRINGIFY2(s) #define STRINGIFY2(s) #s @@ -52,6 +53,8 @@ int console_open(const char * path, int flags, int mode) vfs_console.fd_primary = open("/dev/usbserjtag", flags, mode); #elif CONFIG_ESP_CONSOLE_USB_CDC vfs_console.fd_primary = open("/dev/cdcacm", flags, mode); +#else + vfs_console.fd_primary = open("/dev/null", flags, mode); #endif // Secondary port open @@ -207,13 +210,15 @@ esp_err_t esp_vfs_console_register(void) if (err != ESP_OK) { return err; } +#else + esp_vfs_null_register(); + primary_vfs = esp_vfs_null_get_vfs(); #endif // Secondary vfs part. #if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG secondary_vfs = esp_vfs_usb_serial_jtag_get_vfs(); #endif - err = esp_vfs_dev_console_register(); return err; } diff --git a/components/vfs/CMakeLists.txt b/components/vfs/CMakeLists.txt index bf884d0178..9c96f40d58 100644 --- a/components/vfs/CMakeLists.txt +++ b/components/vfs/CMakeLists.txt @@ -7,6 +7,7 @@ endif() list(APPEND sources "vfs.c" "vfs_eventfd.c" "vfs_semihost.c" + "nullfs.c" ) list(APPEND pr esp_timer diff --git a/components/vfs/include/esp_vfs_null.h b/components/vfs/include/esp_vfs_null.h new file mode 100644 index 0000000000..f3983894d0 --- /dev/null +++ b/components/vfs/include/esp_vfs_null.h @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include "esp_vfs.h" +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Get VFS structure for /dev/null + * + * @return VFS structure for /dev/null + */ +const esp_vfs_t *esp_vfs_null_get_vfs(void); + +/** + * @brief Register filesystem for /dev/null + * + * @return ESP_OK on success; any other value indicates an error + */ +esp_err_t esp_vfs_null_register(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/vfs/nullfs.c b/components/vfs/nullfs.c new file mode 100644 index 0000000000..761ae0be23 --- /dev/null +++ b/components/vfs/nullfs.c @@ -0,0 +1,297 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "esp_vfs.h" +#include "esp_log.h" +#include "esp_private/startup_internal.h" + +#include "esp_vfs_null.h" + +#define UNUSED(x) (void)(x) +typedef enum { + VFS_NULL_CLOSED = 0, + VFS_NULL_READ = 1 << 0, + VFS_NULL_WRITE = 1 << 1, +} vfs_null_flags_t; + +typedef uint64_t vfs_null_ctx_t; + +#define GET_FLAGS(ctx, fd) ((ctx >> (fd * 2)) & 0x3) +#define SET_FLAGS(ctx, fd, flags) (ctx |= (flags << (fd * 2))) + +#define WRITABLE(ctx, fd) (GET_FLAGS(ctx, fd) & VFS_NULL_WRITE) +#define READABLE(ctx, fd) (GET_FLAGS(ctx, fd) & VFS_NULL_READ) + +#define SET_WRITABLE(ctx, fd) SET_FLAGS(ctx, fd, VFS_NULL_WRITE) +#define SET_READABLE(ctx, fd) SET_FLAGS(ctx, fd, VFS_NULL_READ) + +#define FD_IN_RANGE(fd) (fd >= 0 && fd < 32) + +static vfs_null_ctx_t g_fds = 0; + +static const char* TAG = "nullfs"; + +static ssize_t vfs_null_write(int fd, const void *data, size_t size); +static off_t vfs_null_lseek(int fd, off_t offset, int whence); +static ssize_t vfs_null_read(int fd, void *data, size_t size); +static ssize_t vfs_null_pread(int fd, void *data, size_t size, off_t offset); +static ssize_t vfs_null_pwrite(int fd, const void *data, size_t size, off_t offset); +static int vfs_null_open(const char* path, int flags, int mode); +static int vfs_null_close(int fd); +static int vfs_null_fstat(int fd, struct stat *st); +#if CONFIG_VFS_SUPPORT_DIR +static int vfs_null_stat(const char* path, struct stat *st); +#endif // CONFIG_VFS_SUPPORT_DIR +static int vfs_null_fcntl(int fd, int cmd, int arg); +static int vfs_null_ioctl(int fd, int cmd, va_list args); +static int vfs_null_fsync(int fd); + +static const esp_vfs_t s_vfs_null = { + .flags = ESP_VFS_FLAG_DEFAULT, + .write = &vfs_null_write, + .lseek = &vfs_null_lseek, + .read = &vfs_null_read, + .pread = &vfs_null_pread, + .pwrite = &vfs_null_pwrite, + .open = &vfs_null_open, + .close = &vfs_null_close, + .fstat = &vfs_null_fstat, +#if CONFIG_VFS_SUPPORT_DIR + .stat = &vfs_null_stat, +#endif // CONFIG_VFS_SUPPORT_DIR + .fcntl = &vfs_null_fcntl, + .ioctl = &vfs_null_ioctl, + .fsync = &vfs_null_fsync, +}; + +const esp_vfs_t *esp_vfs_null_get_vfs(void) +{ + return &s_vfs_null; +} + +esp_err_t esp_vfs_null_register(void) +{ + return esp_vfs_register("/dev/null", &s_vfs_null, NULL); +} + +static ssize_t vfs_null_write(int fd, const void *data, size_t size) +{ + UNUSED(fd); + UNUSED(data); + + if (FD_IN_RANGE(fd) && WRITABLE(g_fds, fd)) { + return size; + } + + errno = EBADF; + return -1; +} + +static off_t vfs_null_lseek(int fd, off_t offset, int whence) +{ + UNUSED(fd); + UNUSED(offset); + UNUSED(whence); + + ESP_LOGD(TAG, "lseek %ld", offset); + + switch (whence) { + case SEEK_SET: + case SEEK_CUR: + case SEEK_END: + return 0; + default: + errno = EINVAL; + return -1; + } +} + +static ssize_t vfs_null_read(int fd, void *data, size_t size) +{ + UNUSED(fd); + UNUSED(data); + + ESP_LOGD(TAG, "read %u bytes", size); + + if (FD_IN_RANGE(fd) && READABLE(g_fds, fd)) { + return 0; // EOF + } + + errno = EBADF; + return -1; +} + +static int vfs_null_pread(int fd, void *data, size_t size, off_t offset) +{ + UNUSED(fd); + UNUSED(data); + UNUSED(size); + UNUSED(offset); + + errno = ESPIPE; + return -1; +} + +static int vfs_null_pwrite(int fd, const void *data, size_t size, off_t offset) +{ + UNUSED(fd); + UNUSED(data); + UNUSED(size); + UNUSED(offset); + + errno = ESPIPE; + return -1; +} + +static int vfs_null_get_empty_fd(void) +{ + for (int i = 0; i < 32; i++) { + if (GET_FLAGS(g_fds, i) == VFS_NULL_CLOSED) { + return i; + } + } + + return -1; +} + +static int vfs_null_open(const char* path, int flags, int mode) +{ + UNUSED(mode); + + ESP_LOGD(TAG, "open %s, flags %d", path, flags); + + // Possibly check if the flags are valid + + if (strcmp(path, "/") != 0) { + errno = ENOENT; + return -1; + } + + int fd = vfs_null_get_empty_fd(); + if (fd == -1) { + errno = EMFILE; + return -1; + } + + if (flags & O_RDWR) { + SET_READABLE(g_fds, fd); + SET_WRITABLE(g_fds, fd); + } else if (flags & O_WRONLY) { + SET_WRITABLE(g_fds, fd); + } else if (flags & O_RDONLY) { + SET_READABLE(g_fds, fd); + } else { + errno = EINVAL; + return -1; + } + + return fd; +} + +static int vfs_null_close(int fd) +{ + if (!FD_IN_RANGE(fd)) { + errno = EBADF; + return -1; + } + + SET_FLAGS(g_fds, fd, VFS_NULL_CLOSED); + return 0; +} + +static int vfs_null_fstat(int fd, struct stat *st) +{ + if (!FD_IN_RANGE(fd)) { + errno = EBADF; + return -1; + } + + memset(st, 0, sizeof(struct stat)); + st->st_mode = S_IFCHR | 0666; // Special character device with read/write permissions for everyone + st->st_nlink = 1; + st->st_uid = 0; + st->st_gid = 0; + st->st_rdev = makedev(1, 3); // Null device (from Linux) + + return 0; +} + +#if CONFIG_VFS_SUPPORT_DIR + +static int vfs_null_stat(const char* path, struct stat *st) +{ + if (strcmp(path, "/") != 0) { + errno = ENOENT; + return -1; + } + + memset(st, 0, sizeof(struct stat)); + st->st_mode = S_IFCHR | 0666; // Special character device with read/write permissions for everyone + st->st_nlink = 1; + st->st_uid = 0; + st->st_gid = 0; + st->st_rdev = makedev(1, 3); // Null device (from Linux) + + return 0; +} + +#endif // CONFIG_VFS_SUPPORT_DIR + +static int vfs_null_fcntl(int fd, int cmd, int arg) +{ + UNUSED(arg); + + if (!FD_IN_RANGE(fd)) { + errno = EBADF; + return -1; + } + + switch (cmd) { + default: + errno = ENOSYS; + return -1; + } +} + +static int vfs_null_ioctl(int fd, int cmd, va_list args) +{ + UNUSED(args); + + if (!FD_IN_RANGE(fd)) { + errno = EBADF; + return -1; + } + + switch (cmd) { + default: + errno = ENOSYS; + return -1; + } +} + +static int vfs_null_fsync(int fd) +{ + if (!FD_IN_RANGE(fd)) { + errno = EBADF; + return -1; + } + + return 0; +} + +#if CONFIG_ESP_CONSOLE_NONE +ESP_SYSTEM_INIT_FN(init_vfs_nullfs, CORE, BIT(0), 113) +{ + return esp_vfs_null_register(); +} +#endif