mirror of
https://github.com/espressif/esp-idf
synced 2025-03-09 09:09:10 -04:00
Merge branch 'feature/switch_to_new_vfs_api' into 'master'
feat(storage): switch to new VFS API Closes IDF-11473 See merge request espressif/esp-idf!32029
This commit is contained in:
commit
408ae4c560
@ -20,7 +20,7 @@ extern "C" {
|
||||
*
|
||||
* @return pointer to structure esp_vfs_t
|
||||
*/
|
||||
const esp_vfs_t *esp_vfs_uart_get_vfs(void);
|
||||
const esp_vfs_fs_ops_t *esp_vfs_uart_get_vfs(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -1032,8 +1032,29 @@ static int uart_tcflush(int fd, int select)
|
||||
}
|
||||
#endif // CONFIG_VFS_SUPPORT_TERMIOS
|
||||
|
||||
static const esp_vfs_t uart_vfs = {
|
||||
.flags = ESP_VFS_FLAG_DEFAULT,
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
static const esp_vfs_dir_ops_t s_vfs_uart_dir = {
|
||||
.access = &uart_access,
|
||||
};
|
||||
#endif // CONFIG_VFS_SUPPORT_DIR
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
static const esp_vfs_select_ops_t s_vfs_uart_select = {
|
||||
.start_select = &uart_start_select,
|
||||
.end_select = &uart_end_select,
|
||||
};
|
||||
#endif // CONFIG_VFS_SUPPORT_SELECT
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_TERMIOS
|
||||
static const esp_vfs_termios_ops_t s_vfs_uart_termios = {
|
||||
.tcsetattr = &uart_tcsetattr,
|
||||
.tcgetattr = &uart_tcgetattr,
|
||||
.tcdrain = &uart_tcdrain,
|
||||
.tcflush = &uart_tcflush,
|
||||
};
|
||||
#endif // CONFIG_VFS_SUPPORT_TERMIOS
|
||||
|
||||
static const esp_vfs_fs_ops_t s_vfs_uart = {
|
||||
.write = &uart_write,
|
||||
.open = &uart_open,
|
||||
.fstat = &uart_fstat,
|
||||
@ -1042,28 +1063,24 @@ static const esp_vfs_t uart_vfs = {
|
||||
.fcntl = &uart_fcntl,
|
||||
.fsync = &uart_fsync,
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
.access = &uart_access,
|
||||
.dir = &s_vfs_uart_dir,
|
||||
#endif // CONFIG_VFS_SUPPORT_DIR
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
.start_select = &uart_start_select,
|
||||
.end_select = &uart_end_select,
|
||||
.select = &s_vfs_uart_select,
|
||||
#endif // CONFIG_VFS_SUPPORT_SELECT
|
||||
#ifdef CONFIG_VFS_SUPPORT_TERMIOS
|
||||
.tcsetattr = &uart_tcsetattr,
|
||||
.tcgetattr = &uart_tcgetattr,
|
||||
.tcdrain = &uart_tcdrain,
|
||||
.tcflush = &uart_tcflush,
|
||||
.termios = &s_vfs_uart_termios,
|
||||
#endif // CONFIG_VFS_SUPPORT_TERMIOS
|
||||
};
|
||||
|
||||
const esp_vfs_t *esp_vfs_uart_get_vfs(void)
|
||||
const esp_vfs_fs_ops_t *esp_vfs_uart_get_vfs(void)
|
||||
{
|
||||
return &uart_vfs;
|
||||
return &s_vfs_uart;
|
||||
}
|
||||
|
||||
void uart_vfs_dev_register(void)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_vfs_register("/dev/uart", &uart_vfs, NULL));
|
||||
ESP_ERROR_CHECK(esp_vfs_register_fs("/dev/uart", &s_vfs_uart, ESP_VFS_FLAG_STATIC, NULL));
|
||||
}
|
||||
|
||||
int uart_vfs_dev_port_set_rx_line_endings(int uart_num, esp_line_endings_t mode)
|
||||
|
@ -20,7 +20,7 @@ extern "C" {
|
||||
*
|
||||
* @return pointer to structure esp_vfs_t
|
||||
*/
|
||||
const esp_vfs_t *esp_vfs_usb_serial_jtag_get_vfs(void);
|
||||
const esp_vfs_fs_ops_t *esp_vfs_usb_serial_jtag_get_vfs(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -571,8 +571,22 @@ void usb_serial_jtag_vfs_set_rx_line_endings(esp_line_endings_t mode)
|
||||
s_ctx.rx_mode = mode;
|
||||
}
|
||||
|
||||
static const esp_vfs_t usj_vfs = {
|
||||
.flags = ESP_VFS_FLAG_DEFAULT,
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
static const esp_vfs_select_ops_t s_vfs_jtag_select = {
|
||||
.start_select = &usb_serial_jtag_start_select,
|
||||
.end_select = &usb_serial_jtag_end_select,
|
||||
};
|
||||
#endif // CONFIG_VFS_SUPPORT_SELECT
|
||||
#ifdef CONFIG_VFS_SUPPORT_TERMIOS
|
||||
static const esp_vfs_termios_ops_t s_vfs_jtag_termios = {
|
||||
.tcsetattr = &usb_serial_jtag_tcsetattr,
|
||||
.tcgetattr = &usb_serial_jtag_tcgetattr,
|
||||
.tcdrain = &usb_serial_jtag_tcdrain,
|
||||
.tcflush = &usb_serial_jtag_tcflush,
|
||||
};
|
||||
#endif // CONFIG_VFS_SUPPORT_TERMIOS
|
||||
|
||||
static const esp_vfs_fs_ops_t s_vfs_jtag = {
|
||||
.write = &usb_serial_jtag_write,
|
||||
.open = &usb_serial_jtag_open,
|
||||
.fstat = &usb_serial_jtag_fstat,
|
||||
@ -580,27 +594,25 @@ static const esp_vfs_t usj_vfs = {
|
||||
.read = &usb_serial_jtag_read,
|
||||
.fcntl = &usb_serial_jtag_fcntl,
|
||||
.fsync = &usb_serial_jtag_fsync,
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
.start_select = &usb_serial_jtag_start_select,
|
||||
.end_select = &usb_serial_jtag_end_select,
|
||||
.select = &s_vfs_jtag_select,
|
||||
#endif // CONFIG_VFS_SUPPORT_SELECT
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_TERMIOS
|
||||
.tcsetattr = &usb_serial_jtag_tcsetattr,
|
||||
.tcgetattr = &usb_serial_jtag_tcgetattr,
|
||||
.tcdrain = &usb_serial_jtag_tcdrain,
|
||||
.tcflush = &usb_serial_jtag_tcflush,
|
||||
.termios = &s_vfs_jtag_termios,
|
||||
#endif // CONFIG_VFS_SUPPORT_TERMIOS
|
||||
};
|
||||
|
||||
const esp_vfs_t* esp_vfs_usb_serial_jtag_get_vfs(void)
|
||||
const esp_vfs_fs_ops_t* esp_vfs_usb_serial_jtag_get_vfs(void)
|
||||
{
|
||||
return &usj_vfs;
|
||||
return &s_vfs_jtag;
|
||||
}
|
||||
|
||||
esp_err_t usb_serial_jtag_vfs_register(void)
|
||||
{
|
||||
// "/dev/usb_serial_jtag" unfortunately is too long for vfs
|
||||
return esp_vfs_register("/dev/usbserjtag", &usj_vfs, NULL);
|
||||
return esp_vfs_register_fs("/dev/usbserjtag", &s_vfs_jtag, ESP_VFS_FLAG_STATIC, NULL);
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
|
||||
@ -615,7 +627,7 @@ ESP_SYSTEM_INIT_FN(init_vfs_usj, CORE, BIT(0), 111)
|
||||
ESP_SYSTEM_INIT_FN(init_vfs_usj_sec, CORE, BIT(0), 112)
|
||||
{
|
||||
// "/dev/seccondary_usb_serial_jtag" unfortunately is too long for vfs
|
||||
esp_vfs_register("/dev/secondary", &usj_vfs, NULL);
|
||||
esp_vfs_register_fs("/dev/secondary", &s_vfs_jtag, ESP_VFS_FLAG_STATIC, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
@ -773,8 +773,25 @@ static esp_err_t l2tap_end_select(void *end_select_args)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const esp_vfs_select_ops_t s_vfs_l2tap_select = {
|
||||
.start_select = &l2tap_start_select,
|
||||
.end_select = &l2tap_end_select,
|
||||
};
|
||||
#endif //CONFIG_VFS_SUPPORT_SELECT
|
||||
|
||||
static const esp_vfs_fs_ops_t s_vfs_l2tap = {
|
||||
.write = &l2tap_write,
|
||||
.open = &l2tap_open,
|
||||
.close = &l2tap_close,
|
||||
.read = &l2tap_read,
|
||||
.fcntl = &l2tap_fcntl,
|
||||
.ioctl = &l2tap_ioctl,
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
.select = &s_vfs_l2tap_select,
|
||||
#endif // CONFIG_VFS_SUPPORT_SELECT
|
||||
};
|
||||
|
||||
esp_err_t esp_vfs_l2tap_intf_register(l2tap_vfs_config_t *config)
|
||||
{
|
||||
l2tap_vfs_config_t def_config = L2TAP_VFS_CONFIG_DEFAULT();
|
||||
@ -786,20 +803,7 @@ esp_err_t esp_vfs_l2tap_intf_register(l2tap_vfs_config_t *config)
|
||||
|
||||
ESP_RETURN_ON_FALSE(!s_is_registered, ESP_ERR_INVALID_STATE, TAG, "vfs is already registered");
|
||||
s_is_registered = true;
|
||||
esp_vfs_t vfs = {
|
||||
.flags = ESP_VFS_FLAG_DEFAULT,
|
||||
.write = &l2tap_write,
|
||||
.open = &l2tap_open,
|
||||
.close = &l2tap_close,
|
||||
.read = &l2tap_read,
|
||||
.fcntl = &l2tap_fcntl,
|
||||
.ioctl = &l2tap_ioctl,
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
.start_select = &l2tap_start_select,
|
||||
.end_select = &l2tap_end_select,
|
||||
#endif // CONFIG_VFS_SUPPORT_SELECT
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(esp_vfs_register(config->base_path, &vfs, NULL), TAG, "vfs register error");
|
||||
ESP_RETURN_ON_ERROR(esp_vfs_register_fs(config->base_path, &s_vfs_l2tap, ESP_VFS_FLAG_STATIC, NULL), TAG, "vfs register error");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ extern "C" {
|
||||
*
|
||||
* @return pointer to structure esp_vfs_t
|
||||
*/
|
||||
const esp_vfs_t *esp_vfs_cdcacm_get_vfs(void);
|
||||
const esp_vfs_fs_ops_t *esp_vfs_cdcacm_get_vfs(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -300,8 +300,7 @@ void esp_vfs_dev_cdcacm_set_rx_line_endings(esp_line_endings_t mode)
|
||||
s_rx_mode = mode;
|
||||
}
|
||||
|
||||
static const esp_vfs_t vfs = {
|
||||
.flags = ESP_VFS_FLAG_DEFAULT,
|
||||
static const esp_vfs_fs_ops_t s_cdcacm_vfs = {
|
||||
.write = &cdcacm_write,
|
||||
.open = &cdcacm_open,
|
||||
.fstat = &cdcacm_fstat,
|
||||
@ -311,12 +310,12 @@ static const esp_vfs_t vfs = {
|
||||
.fsync = &cdcacm_fsync
|
||||
};
|
||||
|
||||
const esp_vfs_t *esp_vfs_cdcacm_get_vfs(void)
|
||||
const esp_vfs_fs_ops_t *esp_vfs_cdcacm_get_vfs(void)
|
||||
{
|
||||
return &vfs;
|
||||
return &s_cdcacm_vfs;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_dev_cdcacm_register(void)
|
||||
{
|
||||
return esp_vfs_register("/dev/cdcacm", &vfs, NULL);
|
||||
return esp_vfs_register_fs("/dev/cdcacm", &s_cdcacm_vfs, ESP_VFS_FLAG_STATIC, NULL);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_private/startup_internal.h"
|
||||
#include "esp_vfs_null.h"
|
||||
#include "esp_private/nullfs.h"
|
||||
|
||||
#define STRINGIFY(s) STRINGIFY2(s)
|
||||
#define STRINGIFY2(s) #s
|
||||
@ -37,10 +38,10 @@ typedef struct {
|
||||
|
||||
// Secondary register part.
|
||||
#if CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG
|
||||
const static esp_vfs_t *secondary_vfs = NULL;
|
||||
const static esp_vfs_fs_ops_t *secondary_vfs = NULL;
|
||||
#endif // Secondary part
|
||||
|
||||
const static esp_vfs_t *primary_vfs = NULL;
|
||||
const static esp_vfs_fs_ops_t *primary_vfs = NULL;
|
||||
|
||||
static vfs_console_context_t vfs_console = {0};
|
||||
|
||||
@ -121,8 +122,8 @@ static esp_err_t console_start_select(int nfds, fd_set *readfds, fd_set *writefd
|
||||
esp_vfs_select_sem_t select_sem, void **end_select_args)
|
||||
{
|
||||
// start_select is not guaranteed be implemented even though CONFIG_VFS_SUPPORT_SELECT is enabled in sdkconfig
|
||||
if (primary_vfs->start_select) {
|
||||
return primary_vfs->start_select(nfds, readfds, writefds, exceptfds, select_sem, end_select_args);
|
||||
if (primary_vfs->select->start_select) {
|
||||
return primary_vfs->select->start_select(nfds, readfds, writefds, exceptfds, select_sem, end_select_args);
|
||||
}
|
||||
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
@ -131,8 +132,8 @@ static esp_err_t console_start_select(int nfds, fd_set *readfds, fd_set *writefd
|
||||
esp_err_t console_end_select(void *end_select_args)
|
||||
{
|
||||
// end_select is not guaranteed be implemented even though CONFIG_VFS_SUPPORT_SELECT is enabled in sdkconfig
|
||||
if (primary_vfs->end_select) {
|
||||
return primary_vfs->end_select(end_select_args);
|
||||
if (primary_vfs->select->end_select) {
|
||||
return primary_vfs->select->end_select(end_select_args);
|
||||
}
|
||||
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
@ -163,8 +164,29 @@ int console_tcflush(int fd, int select)
|
||||
}
|
||||
#endif // CONFIG_VFS_SUPPORT_TERMIOS
|
||||
|
||||
static const esp_vfs_t vfs = {
|
||||
.flags = ESP_VFS_FLAG_DEFAULT,
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
static const esp_vfs_dir_ops_t s_vfs_console_dir = {
|
||||
.access = &console_access,
|
||||
};
|
||||
#endif // CONFIG_VFS_SUPPORT_DIR
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
static const esp_vfs_select_ops_t s_vfs_console_select = {
|
||||
.start_select = &console_start_select,
|
||||
.end_select = &console_end_select,
|
||||
};
|
||||
#endif // CONFIG_VFS_SUPPORT_SELECT
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_TERMIOS
|
||||
static const esp_vfs_termios_ops_t s_vfs_console_termios = {
|
||||
.tcsetattr = &console_tcsetattr,
|
||||
.tcgetattr = &console_tcgetattr,
|
||||
.tcdrain = &console_tcdrain,
|
||||
.tcflush = &console_tcflush,
|
||||
};
|
||||
#endif // CONFIG_VFS_SUPPORT_TERMIOS
|
||||
|
||||
static const esp_vfs_fs_ops_t s_vfs_console = {
|
||||
.write = &console_write,
|
||||
.open = &console_open,
|
||||
.fstat = &console_fstat,
|
||||
@ -172,24 +194,23 @@ static const esp_vfs_t vfs = {
|
||||
.read = &console_read,
|
||||
.fcntl = &console_fcntl,
|
||||
.fsync = &console_fsync,
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
.access = &console_access,
|
||||
.dir = &s_vfs_console_dir,
|
||||
#endif // CONFIG_VFS_SUPPORT_DIR
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
.start_select = &console_start_select,
|
||||
.end_select = &console_end_select,
|
||||
.select = &s_vfs_console_select,
|
||||
#endif // CONFIG_VFS_SUPPORT_SELECT
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_TERMIOS
|
||||
.tcsetattr = &console_tcsetattr,
|
||||
.tcgetattr = &console_tcgetattr,
|
||||
.tcdrain = &console_tcdrain,
|
||||
.tcflush = &console_tcflush,
|
||||
.termios = &s_vfs_console_termios,
|
||||
#endif // CONFIG_VFS_SUPPORT_TERMIOS
|
||||
};
|
||||
|
||||
static esp_err_t esp_vfs_dev_console_register(void)
|
||||
{
|
||||
return esp_vfs_register(ESP_VFS_DEV_CONSOLE, &vfs, NULL);
|
||||
return esp_vfs_register_fs(ESP_VFS_DEV_CONSOLE, &s_vfs_console, ESP_VFS_FLAG_STATIC, NULL);
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_console_register(void)
|
||||
|
@ -140,6 +140,42 @@ esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive, siz
|
||||
return esp_vfs_fat_register_cfg(&conf, out_fs);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
static const esp_vfs_dir_ops_t s_vfs_fat_dir = {
|
||||
.stat_p = &vfs_fat_stat,
|
||||
.link_p = &vfs_fat_link,
|
||||
.unlink_p = &vfs_fat_unlink,
|
||||
.rename_p = &vfs_fat_rename,
|
||||
.opendir_p = &vfs_fat_opendir,
|
||||
.closedir_p = &vfs_fat_closedir,
|
||||
.readdir_p = &vfs_fat_readdir,
|
||||
.readdir_r_p = &vfs_fat_readdir_r,
|
||||
.seekdir_p = &vfs_fat_seekdir,
|
||||
.telldir_p = &vfs_fat_telldir,
|
||||
.mkdir_p = &vfs_fat_mkdir,
|
||||
.rmdir_p = &vfs_fat_rmdir,
|
||||
.access_p = &vfs_fat_access,
|
||||
.truncate_p = &vfs_fat_truncate,
|
||||
.ftruncate_p = &vfs_fat_ftruncate,
|
||||
.utime_p = &vfs_fat_utime,
|
||||
};
|
||||
#endif // CONFIG_VFS_SUPPORT_DIR
|
||||
|
||||
static const esp_vfs_fs_ops_t s_vfs_fat = {
|
||||
.write_p = &vfs_fat_write,
|
||||
.lseek_p = &vfs_fat_lseek,
|
||||
.read_p = &vfs_fat_read,
|
||||
.pread_p = &vfs_fat_pread,
|
||||
.pwrite_p = &vfs_fat_pwrite,
|
||||
.open_p = &vfs_fat_open,
|
||||
.close_p = &vfs_fat_close,
|
||||
.fstat_p = &vfs_fat_fstat,
|
||||
.fsync_p = &vfs_fat_fsync,
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
.dir = &s_vfs_fat_dir,
|
||||
#endif // CONFIG_VFS_SUPPORT_DIR
|
||||
};
|
||||
|
||||
esp_err_t esp_vfs_fat_register_cfg(const esp_vfs_fat_conf_t* conf, FATFS** out_fs)
|
||||
{
|
||||
size_t ctx = find_context_index_by_path(conf->base_path);
|
||||
@ -152,37 +188,6 @@ esp_err_t esp_vfs_fat_register_cfg(const esp_vfs_fat_conf_t* conf, FATFS** out_f
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
const esp_vfs_t vfs = {
|
||||
.flags = ESP_VFS_FLAG_CONTEXT_PTR,
|
||||
.write_p = &vfs_fat_write,
|
||||
.lseek_p = &vfs_fat_lseek,
|
||||
.read_p = &vfs_fat_read,
|
||||
.pread_p = &vfs_fat_pread,
|
||||
.pwrite_p = &vfs_fat_pwrite,
|
||||
.open_p = &vfs_fat_open,
|
||||
.close_p = &vfs_fat_close,
|
||||
.fstat_p = &vfs_fat_fstat,
|
||||
.fsync_p = &vfs_fat_fsync,
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
.stat_p = &vfs_fat_stat,
|
||||
.link_p = &vfs_fat_link,
|
||||
.unlink_p = &vfs_fat_unlink,
|
||||
.rename_p = &vfs_fat_rename,
|
||||
.opendir_p = &vfs_fat_opendir,
|
||||
.closedir_p = &vfs_fat_closedir,
|
||||
.readdir_p = &vfs_fat_readdir,
|
||||
.readdir_r_p = &vfs_fat_readdir_r,
|
||||
.seekdir_p = &vfs_fat_seekdir,
|
||||
.telldir_p = &vfs_fat_telldir,
|
||||
.mkdir_p = &vfs_fat_mkdir,
|
||||
.rmdir_p = &vfs_fat_rmdir,
|
||||
.access_p = &vfs_fat_access,
|
||||
.truncate_p = &vfs_fat_truncate,
|
||||
.ftruncate_p = &vfs_fat_ftruncate,
|
||||
.utime_p = &vfs_fat_utime,
|
||||
#endif // CONFIG_VFS_SUPPORT_DIR
|
||||
};
|
||||
|
||||
size_t max_files = conf->max_files;
|
||||
if (max_files < 1) {
|
||||
max_files = 1; // ff_memalloc(max_files * sizeof(bool)) below will fail if max_files == 0
|
||||
@ -204,7 +209,7 @@ esp_err_t esp_vfs_fat_register_cfg(const esp_vfs_fat_conf_t* conf, FATFS** out_f
|
||||
strlcpy(fat_ctx->fat_drive, conf->fat_drive, sizeof(fat_ctx->fat_drive) - 1);
|
||||
strlcpy(fat_ctx->base_path, conf->base_path, sizeof(fat_ctx->base_path) - 1);
|
||||
|
||||
esp_err_t err = esp_vfs_register(conf->base_path, &vfs, fat_ctx);
|
||||
esp_err_t err = esp_vfs_register_fs(conf->base_path, &s_vfs_fat, ESP_VFS_FLAG_CONTEXT_PTR | ESP_VFS_FLAG_STATIC, fat_ctx);
|
||||
if (err != ESP_OK) {
|
||||
free(fat_ctx->o_append);
|
||||
free(fat_ctx);
|
||||
|
@ -399,6 +399,43 @@ esp_err_t esp_spiffs_gc(const char* partition_label, size_t size_to_gc)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
static const esp_vfs_dir_ops_t s_vfs_spiffs_dir = {
|
||||
.stat_p = &vfs_spiffs_stat,
|
||||
.link_p = &vfs_spiffs_link,
|
||||
.unlink_p = &vfs_spiffs_unlink,
|
||||
.rename_p = &vfs_spiffs_rename,
|
||||
.opendir_p = &vfs_spiffs_opendir,
|
||||
.closedir_p = &vfs_spiffs_closedir,
|
||||
.readdir_p = &vfs_spiffs_readdir,
|
||||
.readdir_r_p = &vfs_spiffs_readdir_r,
|
||||
.seekdir_p = &vfs_spiffs_seekdir,
|
||||
.telldir_p = &vfs_spiffs_telldir,
|
||||
.mkdir_p = &vfs_spiffs_mkdir,
|
||||
.rmdir_p = &vfs_spiffs_rmdir,
|
||||
.truncate_p = &vfs_spiffs_truncate,
|
||||
.ftruncate_p = &vfs_spiffs_ftruncate,
|
||||
#ifdef CONFIG_SPIFFS_USE_MTIME
|
||||
.utime_p = &vfs_spiffs_utime,
|
||||
#else
|
||||
.utime_p = NULL,
|
||||
#endif // CONFIG_SPIFFS_USE_MTIME
|
||||
};
|
||||
#endif // CONFIG_VFS_SUPPORT_DIR
|
||||
|
||||
static const esp_vfs_fs_ops_t s_vfs_spiffs = {
|
||||
.write_p = &vfs_spiffs_write,
|
||||
.lseek_p = &vfs_spiffs_lseek,
|
||||
.read_p = &vfs_spiffs_read,
|
||||
.open_p = &vfs_spiffs_open,
|
||||
.close_p = &vfs_spiffs_close,
|
||||
.fstat_p = &vfs_spiffs_fstat,
|
||||
.fsync_p = &vfs_spiffs_fsync,
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
.dir = &s_vfs_spiffs_dir,
|
||||
#endif // CONFIG_VFS_SUPPORT_DIR
|
||||
};
|
||||
|
||||
esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf)
|
||||
{
|
||||
assert(conf->base_path);
|
||||
@ -413,45 +450,13 @@ esp_err_t esp_vfs_spiffs_register(const esp_vfs_spiffs_conf_t * conf)
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
int vfs_flags = ESP_VFS_FLAG_CONTEXT_PTR;
|
||||
int vfs_flags = ESP_VFS_FLAG_CONTEXT_PTR | ESP_VFS_FLAG_STATIC;
|
||||
if (_efs[index]->partition->readonly) {
|
||||
vfs_flags |= ESP_VFS_FLAG_READONLY_FS;
|
||||
}
|
||||
|
||||
const esp_vfs_t vfs = {
|
||||
.flags = vfs_flags,
|
||||
.write_p = &vfs_spiffs_write,
|
||||
.lseek_p = &vfs_spiffs_lseek,
|
||||
.read_p = &vfs_spiffs_read,
|
||||
.open_p = &vfs_spiffs_open,
|
||||
.close_p = &vfs_spiffs_close,
|
||||
.fstat_p = &vfs_spiffs_fstat,
|
||||
.fsync_p = &vfs_spiffs_fsync,
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
.stat_p = &vfs_spiffs_stat,
|
||||
.link_p = &vfs_spiffs_link,
|
||||
.unlink_p = &vfs_spiffs_unlink,
|
||||
.rename_p = &vfs_spiffs_rename,
|
||||
.opendir_p = &vfs_spiffs_opendir,
|
||||
.closedir_p = &vfs_spiffs_closedir,
|
||||
.readdir_p = &vfs_spiffs_readdir,
|
||||
.readdir_r_p = &vfs_spiffs_readdir_r,
|
||||
.seekdir_p = &vfs_spiffs_seekdir,
|
||||
.telldir_p = &vfs_spiffs_telldir,
|
||||
.mkdir_p = &vfs_spiffs_mkdir,
|
||||
.rmdir_p = &vfs_spiffs_rmdir,
|
||||
.truncate_p = &vfs_spiffs_truncate,
|
||||
.ftruncate_p = &vfs_spiffs_ftruncate,
|
||||
#ifdef CONFIG_SPIFFS_USE_MTIME
|
||||
.utime_p = &vfs_spiffs_utime,
|
||||
#else
|
||||
.utime_p = NULL,
|
||||
#endif // CONFIG_SPIFFS_USE_MTIME
|
||||
#endif // CONFIG_VFS_SUPPORT_DIR
|
||||
};
|
||||
|
||||
strlcat(_efs[index]->base_path, conf->base_path, ESP_VFS_PATH_MAX + 1);
|
||||
err = esp_vfs_register(conf->base_path, &vfs, _efs[index]);
|
||||
err = esp_vfs_register_fs(conf->base_path, &s_vfs_spiffs, vfs_flags, _efs[index]);
|
||||
if (err != ESP_OK) {
|
||||
esp_spiffs_free(&_efs[index]);
|
||||
return err;
|
||||
|
24
components/vfs/include/esp_private/nullfs.h
Normal file
24
components/vfs/include/esp_private/nullfs.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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_fs_ops_t *esp_vfs_null_get_vfs(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -12,13 +12,6 @@
|
||||
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
|
||||
*
|
||||
|
@ -243,15 +243,15 @@ typedef struct {
|
||||
};
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
esp_vfs_dir_ops_t *dir; /*!< pointer to the dir subcomponent */
|
||||
const esp_vfs_dir_ops_t *dir; /*!< pointer to the dir subcomponent */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_TERMIOS
|
||||
esp_vfs_termios_ops_t *termios; /*!< pointer to the termios subcomponent */
|
||||
const esp_vfs_termios_ops_t *termios; /*!< pointer to the termios subcomponent */
|
||||
#endif
|
||||
|
||||
#if CONFIG_VFS_SUPPORT_SELECT || defined __DOXYGEN__
|
||||
esp_vfs_select_ops_t *select; /*!< pointer to the select subcomponent */
|
||||
const esp_vfs_select_ops_t *select; /*!< pointer to the select subcomponent */
|
||||
#endif
|
||||
|
||||
} esp_vfs_fs_ops_t;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_ops.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_private/startup_internal.h"
|
||||
@ -68,8 +69,13 @@ 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,
|
||||
#if CONFIG_VFS_SUPPORT_DIR
|
||||
static const esp_vfs_dir_ops_t s_vfs_null_dir = {
|
||||
.stat = &vfs_null_stat,
|
||||
};
|
||||
#endif // CONFIG_VFS_SUPPORT_DIR
|
||||
|
||||
static const esp_vfs_fs_ops_t s_vfs_null = {
|
||||
.write = &vfs_null_write,
|
||||
.lseek = &vfs_null_lseek,
|
||||
.read = &vfs_null_read,
|
||||
@ -78,22 +84,22 @@ static const esp_vfs_t s_vfs_null = {
|
||||
.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,
|
||||
#if CONFIG_VFS_SUPPORT_DIR
|
||||
.dir = &s_vfs_null_dir,
|
||||
#endif // CONFIG_VFS_SUPPORT_DIR
|
||||
};
|
||||
|
||||
const esp_vfs_t *esp_vfs_null_get_vfs(void)
|
||||
const esp_vfs_fs_ops_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);
|
||||
return esp_vfs_register_fs("/dev/null", &s_vfs_null, ESP_VFS_FLAG_STATIC, NULL);
|
||||
}
|
||||
|
||||
static ssize_t vfs_null_write(int fd, const void *data, size_t size)
|
||||
|
@ -86,16 +86,17 @@ static ssize_t esp_get_free_index(void) {
|
||||
}
|
||||
|
||||
static void esp_vfs_free_fs_ops(esp_vfs_fs_ops_t *vfs) {
|
||||
// We can afford to cast away the const qualifier here, because we know that we allocated the struct and therefore its safe
|
||||
#ifdef CONFIG_VFS_SUPPORT_TERMIOS
|
||||
free(vfs->termios);
|
||||
free((void*)vfs->termios);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
free(vfs->dir);
|
||||
free((void*)vfs->dir);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
free(vfs->select);
|
||||
free((void*)vfs->select);
|
||||
#endif
|
||||
|
||||
free(vfs);
|
||||
@ -113,36 +114,37 @@ static void esp_vfs_free_entry(vfs_entry_t *entry) {
|
||||
free(entry);
|
||||
}
|
||||
|
||||
static void esp_minify_vfs(const esp_vfs_t * const vfs, esp_vfs_fs_ops_t *min) {
|
||||
assert(vfs != NULL);
|
||||
assert(min != NULL);
|
||||
*min = (esp_vfs_fs_ops_t) {
|
||||
.write = vfs->write,
|
||||
.lseek = vfs->lseek,
|
||||
.read = vfs->read,
|
||||
.pread = vfs->pread,
|
||||
.pwrite = vfs->pwrite,
|
||||
.open = vfs->open,
|
||||
.close = vfs->close,
|
||||
.fstat = vfs->fstat,
|
||||
.fcntl = vfs->fcntl,
|
||||
.ioctl = vfs->ioctl,
|
||||
.fsync = vfs->fsync,
|
||||
typedef struct {
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
.dir = min->dir,
|
||||
esp_vfs_dir_ops_t *dir;
|
||||
#endif
|
||||
#ifdef CONFIG_VFS_SUPPORT_TERMIOS
|
||||
.termios = min->termios,
|
||||
esp_vfs_termios_ops_t *termios;
|
||||
#endif
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
.select = min->select,
|
||||
esp_vfs_select_ops_t *select;
|
||||
#endif
|
||||
};
|
||||
} vfs_component_proxy_t;
|
||||
|
||||
static void free_proxy_members(vfs_component_proxy_t *proxy) {
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
free(proxy->dir);
|
||||
#endif
|
||||
#ifdef CONFIG_VFS_SUPPORT_TERMIOS
|
||||
free(proxy->termios);
|
||||
#endif
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
free(proxy->select);
|
||||
#endif
|
||||
}
|
||||
|
||||
static esp_vfs_fs_ops_t *esp_minify_vfs(const esp_vfs_t * const vfs, vfs_component_proxy_t proxy) {
|
||||
assert(vfs != NULL);
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
// If the dir functions are not implemented, we don't need to convert them
|
||||
if (min->dir != NULL) {
|
||||
*(min->dir) = (esp_vfs_dir_ops_t) {
|
||||
if (proxy.dir != NULL) {
|
||||
*(proxy.dir) = (esp_vfs_dir_ops_t) {
|
||||
.stat = vfs->stat,
|
||||
.link = vfs->link,
|
||||
.unlink = vfs->unlink,
|
||||
@ -165,8 +167,8 @@ static void esp_minify_vfs(const esp_vfs_t * const vfs, esp_vfs_fs_ops_t *min) {
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_TERMIOS
|
||||
// If the termios functions are not implemented, we don't need to convert them
|
||||
if (min->termios != NULL) {
|
||||
*(min->termios) = (esp_vfs_termios_ops_t) {
|
||||
if (proxy.termios != NULL) {
|
||||
*(proxy.termios) = (esp_vfs_termios_ops_t) {
|
||||
.tcsetattr = vfs->tcsetattr,
|
||||
.tcgetattr = vfs->tcgetattr,
|
||||
.tcdrain = vfs->tcdrain,
|
||||
@ -180,8 +182,8 @@ static void esp_minify_vfs(const esp_vfs_t * const vfs, esp_vfs_fs_ops_t *min) {
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
// If the select functions are not implemented, we don't need to convert them
|
||||
if (min->select != NULL) {
|
||||
*(min->select) = (esp_vfs_select_ops_t) {
|
||||
if (proxy.select != NULL) {
|
||||
*(proxy.select) = (esp_vfs_select_ops_t) {
|
||||
.start_select = vfs->start_select,
|
||||
.socket_select = vfs->socket_select,
|
||||
.stop_socket_select = vfs->stop_socket_select,
|
||||
@ -192,63 +194,109 @@ static void esp_minify_vfs(const esp_vfs_t * const vfs, esp_vfs_fs_ops_t *min) {
|
||||
}
|
||||
#endif // CONFIG_VFS_SUPPORT_SELECT
|
||||
|
||||
}
|
||||
esp_vfs_fs_ops_t tmp = {
|
||||
.write = vfs->write,
|
||||
.lseek = vfs->lseek,
|
||||
.read = vfs->read,
|
||||
.pread = vfs->pread,
|
||||
.pwrite = vfs->pwrite,
|
||||
.open = vfs->open,
|
||||
.close = vfs->close,
|
||||
.fstat = vfs->fstat,
|
||||
.fcntl = vfs->fcntl,
|
||||
.ioctl = vfs->ioctl,
|
||||
.fsync = vfs->fsync,
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
.dir = proxy.dir,
|
||||
#endif
|
||||
#ifdef CONFIG_VFS_SUPPORT_TERMIOS
|
||||
.termios = proxy.termios,
|
||||
#endif
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
.select = proxy.select,
|
||||
#endif
|
||||
};
|
||||
|
||||
static esp_vfs_fs_ops_t* esp_vfs_duplicate_fs_ops(const esp_vfs_fs_ops_t *vfs) {
|
||||
esp_vfs_fs_ops_t *min = (esp_vfs_fs_ops_t*) heap_caps_malloc(sizeof(esp_vfs_fs_ops_t), VFS_MALLOC_FLAGS);
|
||||
if (min == NULL) {
|
||||
esp_vfs_fs_ops_t *out = heap_caps_malloc(sizeof(esp_vfs_fs_ops_t), VFS_MALLOC_FLAGS);
|
||||
if (out == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(min, vfs, sizeof(esp_vfs_fs_ops_t));
|
||||
// Doing this is the only way to correctly initialize const members of a struct according to C standard
|
||||
memcpy(out, &tmp, sizeof(esp_vfs_fs_ops_t));
|
||||
|
||||
// remove references to the original components
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
min->dir = NULL;
|
||||
#endif
|
||||
#ifdef CONFIG_VFS_SUPPORT_TERMIOS
|
||||
min->termios = NULL;
|
||||
#endif
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
min->select = NULL;
|
||||
#endif
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
static esp_vfs_fs_ops_t* esp_vfs_duplicate_fs_ops(const esp_vfs_fs_ops_t *orig) {
|
||||
vfs_component_proxy_t proxy = {};
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
if (vfs->dir != NULL) {
|
||||
min->dir = (esp_vfs_dir_ops_t*) heap_caps_malloc(sizeof(esp_vfs_dir_ops_t), VFS_MALLOC_FLAGS);
|
||||
if (min->dir == NULL) {
|
||||
if (orig->dir != NULL) {
|
||||
proxy.dir = (esp_vfs_dir_ops_t*) heap_caps_malloc(sizeof(esp_vfs_dir_ops_t), VFS_MALLOC_FLAGS);
|
||||
if (proxy.dir == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
memcpy(min->dir, vfs->dir, sizeof(esp_vfs_dir_ops_t));
|
||||
memcpy(proxy.dir, orig->dir, sizeof(esp_vfs_dir_ops_t));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_TERMIOS
|
||||
if (vfs->termios != NULL) {
|
||||
min->termios = (esp_vfs_termios_ops_t*) heap_caps_malloc(sizeof(esp_vfs_termios_ops_t), VFS_MALLOC_FLAGS);
|
||||
if (min->termios == NULL) {
|
||||
if (orig->termios != NULL) {
|
||||
proxy.termios = (esp_vfs_termios_ops_t*) heap_caps_malloc(sizeof(esp_vfs_termios_ops_t), VFS_MALLOC_FLAGS);
|
||||
if (proxy.termios == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
memcpy(min->termios, vfs->termios, sizeof(esp_vfs_termios_ops_t));
|
||||
memcpy(proxy.termios, orig->termios, sizeof(esp_vfs_termios_ops_t));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
if (vfs->select != NULL) {
|
||||
min->select = (esp_vfs_select_ops_t*) heap_caps_malloc(sizeof(esp_vfs_select_ops_t), VFS_MALLOC_FLAGS);
|
||||
if (min->select == NULL) {
|
||||
if (orig->select != NULL) {
|
||||
proxy.select = (esp_vfs_select_ops_t*) heap_caps_malloc(sizeof(esp_vfs_select_ops_t), VFS_MALLOC_FLAGS);
|
||||
if (proxy.select == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
memcpy(min->select, vfs->select, sizeof(esp_vfs_select_ops_t));
|
||||
memcpy(proxy.select, orig->select, sizeof(esp_vfs_select_ops_t));
|
||||
}
|
||||
#endif
|
||||
|
||||
return min;
|
||||
// This tediousness is required because of const members
|
||||
esp_vfs_fs_ops_t tmp = {
|
||||
.write = orig->write,
|
||||
.lseek = orig->lseek,
|
||||
.read = orig->read,
|
||||
.pread = orig->pread,
|
||||
.pwrite = orig->pwrite,
|
||||
.open = orig->open,
|
||||
.close = orig->close,
|
||||
.fstat = orig->fstat,
|
||||
.fcntl = orig->fcntl,
|
||||
.ioctl = orig->ioctl,
|
||||
.fsync = orig->fsync,
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
.dir = proxy.dir,
|
||||
#endif
|
||||
#ifdef CONFIG_VFS_SUPPORT_TERMIOS
|
||||
.termios = proxy.termios,
|
||||
#endif
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
.select = proxy.select,
|
||||
#endif
|
||||
};
|
||||
|
||||
esp_vfs_fs_ops_t *out = heap_caps_malloc(sizeof(esp_vfs_fs_ops_t), VFS_MALLOC_FLAGS);
|
||||
if (out == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memcpy(out, &tmp, sizeof(esp_vfs_fs_ops_t));
|
||||
|
||||
return out;
|
||||
|
||||
#if defined(CONFIG_VFS_SUPPORT_SELECT) || defined(CONFIG_VFS_SUPPORT_TERMIOS) || defined(CONFIG_VFS_SUPPORT_DIR)
|
||||
fail:
|
||||
#endif
|
||||
esp_vfs_free_fs_ops(min);
|
||||
free_proxy_members(&proxy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -263,16 +311,10 @@ static esp_err_t esp_vfs_make_fs_ops(const esp_vfs_t *vfs, esp_vfs_fs_ops_t **mi
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_vfs_fs_ops_t *main = (esp_vfs_fs_ops_t*) heap_caps_malloc(sizeof(esp_vfs_fs_ops_t), VFS_MALLOC_FLAGS);
|
||||
if (main == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
// Initialize all fields to NULL
|
||||
memset(main, 0, sizeof(esp_vfs_fs_ops_t));
|
||||
vfs_component_proxy_t proxy = {};
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
bool skip_dir =
|
||||
const bool skip_dir =
|
||||
vfs->stat == NULL &&
|
||||
vfs->link == NULL &&
|
||||
vfs->unlink == NULL &&
|
||||
@ -291,15 +333,15 @@ static esp_err_t esp_vfs_make_fs_ops(const esp_vfs_t *vfs, esp_vfs_fs_ops_t **mi
|
||||
vfs->utime == NULL;
|
||||
|
||||
if (!skip_dir) {
|
||||
main->dir = (esp_vfs_dir_ops_t*) heap_caps_malloc(sizeof(esp_vfs_dir_ops_t), VFS_MALLOC_FLAGS);
|
||||
if (main->dir == NULL) {
|
||||
proxy.dir = (esp_vfs_dir_ops_t*) heap_caps_malloc(sizeof(esp_vfs_dir_ops_t), VFS_MALLOC_FLAGS);
|
||||
if (proxy.dir == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_TERMIOS
|
||||
bool skip_termios =
|
||||
const bool skip_termios =
|
||||
vfs->tcsetattr == NULL &&
|
||||
vfs->tcgetattr == NULL &&
|
||||
vfs->tcdrain == NULL &&
|
||||
@ -309,15 +351,15 @@ static esp_err_t esp_vfs_make_fs_ops(const esp_vfs_t *vfs, esp_vfs_fs_ops_t **mi
|
||||
vfs->tcsendbreak == NULL;
|
||||
|
||||
if (!skip_termios) {
|
||||
main->termios = (esp_vfs_termios_ops_t*) heap_caps_malloc(sizeof(esp_vfs_termios_ops_t), VFS_MALLOC_FLAGS);
|
||||
if (main->termios == NULL) {
|
||||
proxy.termios = (esp_vfs_termios_ops_t*) heap_caps_malloc(sizeof(esp_vfs_termios_ops_t), VFS_MALLOC_FLAGS);
|
||||
if (proxy.termios == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_SELECT
|
||||
bool skip_select =
|
||||
const bool skip_select =
|
||||
vfs->start_select == NULL &&
|
||||
vfs->socket_select == NULL &&
|
||||
vfs->stop_socket_select == NULL &&
|
||||
@ -326,24 +368,25 @@ static esp_err_t esp_vfs_make_fs_ops(const esp_vfs_t *vfs, esp_vfs_fs_ops_t **mi
|
||||
vfs->end_select == NULL;
|
||||
|
||||
if (!skip_select) {
|
||||
main->select = (esp_vfs_select_ops_t*) heap_caps_malloc(sizeof(esp_vfs_select_ops_t), VFS_MALLOC_FLAGS);
|
||||
if (main->select == NULL) {
|
||||
proxy.select = (esp_vfs_select_ops_t*) heap_caps_malloc(sizeof(esp_vfs_select_ops_t), VFS_MALLOC_FLAGS);
|
||||
if (proxy.select == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_minify_vfs(vfs, main);
|
||||
esp_vfs_fs_ops_t *main = esp_minify_vfs(vfs, proxy);
|
||||
if (main == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*min = main;
|
||||
return ESP_OK;
|
||||
|
||||
#if defined(CONFIG_VFS_SUPPORT_SELECT) || defined(CONFIG_VFS_SUPPORT_TERMIOS) || defined(CONFIG_VFS_SUPPORT_DIR)
|
||||
fail:
|
||||
|
||||
esp_vfs_free_fs_ops(main);
|
||||
free_proxy_members(&proxy);
|
||||
return ESP_ERR_NO_MEM;
|
||||
#endif
|
||||
}
|
||||
|
||||
static esp_err_t esp_vfs_register_fs_common(const char* base_path, size_t len, const esp_vfs_fs_ops_t* vfs, int flags, void* ctx, int *vfs_index)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -13,6 +13,7 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_cpu.h"
|
||||
#include "include/esp_vfs.h"
|
||||
#include "openocd_semihosting.h"
|
||||
|
||||
#ifndef CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS
|
||||
@ -419,37 +420,44 @@ static void vfs_semihost_seekdir(void* ctx, DIR* pdir, long offset)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
static const esp_vfs_dir_ops_t s_vfs_semihost_dir = {
|
||||
.mkdir_p = &vfs_semihost_mkdir,
|
||||
.rmdir_p = &vfs_semihost_rmdir,
|
||||
.access_p = &vfs_semihost_access,
|
||||
.truncate_p = &vfs_semihost_truncate,
|
||||
.utime_p = &vfs_semihost_utime,
|
||||
.stat_p = &vfs_semihost_stat,
|
||||
.rename_p = &vfs_semihost_rename,
|
||||
.link_p = &vfs_semihost_link,
|
||||
.unlink_p = &vfs_semihost_unlink,
|
||||
.opendir_p = &vfs_semihost_opendir,
|
||||
.closedir_p = &vfs_semihost_closedir,
|
||||
.telldir_p = &vfs_semihost_telldir,
|
||||
.readdir_p = &vfs_semihost_readdir,
|
||||
.readdir_r_p = &vfs_semihost_readdir_r,
|
||||
.seekdir_p = &vfs_semihost_seekdir,
|
||||
};
|
||||
#endif
|
||||
|
||||
static const esp_vfs_fs_ops_t s_vfs_semihost = {
|
||||
.write_p = &vfs_semihost_write,
|
||||
.open_p = &vfs_semihost_open,
|
||||
.close_p = &vfs_semihost_close,
|
||||
.read_p = &vfs_semihost_read,
|
||||
.lseek_p = &vfs_semihost_lseek,
|
||||
.fsync_p = &vfs_semihost_fsync,
|
||||
.fstat_p = &vfs_semihost_fstat,
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
.dir = &s_vfs_semihost_dir,
|
||||
#endif
|
||||
};
|
||||
|
||||
esp_err_t esp_vfs_semihost_register(const char* base_path)
|
||||
{
|
||||
assert(base_path);
|
||||
|
||||
const esp_vfs_t vfs = {
|
||||
.flags = ESP_VFS_FLAG_CONTEXT_PTR,
|
||||
.write_p = &vfs_semihost_write,
|
||||
.open_p = &vfs_semihost_open,
|
||||
.close_p = &vfs_semihost_close,
|
||||
.read_p = &vfs_semihost_read,
|
||||
.lseek_p = &vfs_semihost_lseek,
|
||||
.fsync_p = &vfs_semihost_fsync,
|
||||
.fstat_p = &vfs_semihost_fstat,
|
||||
#ifdef CONFIG_VFS_SUPPORT_DIR
|
||||
.mkdir_p = &vfs_semihost_mkdir,
|
||||
.rmdir_p = &vfs_semihost_rmdir,
|
||||
.access_p = &vfs_semihost_access,
|
||||
.truncate_p = &vfs_semihost_truncate,
|
||||
.utime_p = &vfs_semihost_utime,
|
||||
.stat_p = &vfs_semihost_stat,
|
||||
.rename_p = &vfs_semihost_rename,
|
||||
.link_p = &vfs_semihost_link,
|
||||
.unlink_p = &vfs_semihost_unlink,
|
||||
.opendir_p = &vfs_semihost_opendir,
|
||||
.closedir_p = &vfs_semihost_closedir,
|
||||
.telldir_p = &vfs_semihost_telldir,
|
||||
.readdir_p = &vfs_semihost_readdir,
|
||||
.readdir_r_p = &vfs_semihost_readdir_r,
|
||||
.seekdir_p = &vfs_semihost_seekdir,
|
||||
#endif
|
||||
};
|
||||
ESP_LOGD(TAG, "Register semihosting driver '%s'", base_path);
|
||||
if (!esp_cpu_dbgr_is_attached()) {
|
||||
ESP_LOGE(TAG, "OpenOCD is not connected!");
|
||||
@ -478,7 +486,7 @@ esp_err_t esp_vfs_semihost_register(const char* base_path)
|
||||
ESP_LOGE(TAG, "Incompatible OpenOCD version detected. Please follow the getting started guides to install the required version.");
|
||||
}
|
||||
|
||||
err = esp_vfs_register(base_path, &vfs, &s_semhost_ctx[i]);
|
||||
err = esp_vfs_register_fs(base_path, &s_vfs_semihost, ESP_VFS_FLAG_STATIC | ESP_VFS_FLAG_CONTEXT_PTR, &s_semhost_ctx[i]);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Can't register the semihosting! Error: %s", esp_err_to_name(err));
|
||||
return err;
|
||||
|
@ -137,7 +137,7 @@ To send application output to a custom channel (for example, a WebSocket connect
|
||||
- ``fstat()`` — recommended, to provide correct buffering behavior for the I/O streams
|
||||
- ``fcntl()`` — only if non-blocking I/O has to be supported
|
||||
|
||||
Once you have created a custom VFS driver, use ``esp_vfs_register()`` to register it with VFS. Then, use ``fopen()`` to redirect ``stdout`` and ``stderr`` to the custom channel. For example:
|
||||
Once you have created a custom VFS driver, use :cpp:func:`esp_vfs_register_fs()` to register it with VFS. Then, use ``fopen()`` to redirect ``stdout`` and ``stderr`` to the custom channel. For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
|
@ -3,70 +3,96 @@ Virtual Filesystem Component
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Virtual filesystem (VFS) component provides a unified interface for drivers which can perform operations on file-like objects. These can be real filesystems (FAT, SPIFFS, etc.) or device drivers which provide a file-like interface.
|
||||
|
||||
This component allows C library functions, such as fopen and fprintf, to work with FS drivers. At a high level, each FS driver is associated with some path prefix. When one of C library functions needs to open a file, the VFS component searches for the FS driver associated with the file path and forwards the call to that driver. VFS also forwards read, write, and other calls for the given file to the same FS driver.
|
||||
This component allows C library functions, such as fopen and fprintf, to work with FS drivers. At a high level, each FS driver is mounted at some path prefix. When one of C library functions needs to open a file, the VFS component searches for the FS driver associated with the file path and forwards the call to that driver. VFS also forwards read, write, and other calls for the given file to the same FS driver.
|
||||
|
||||
For example, one can register a FAT filesystem driver with the ``/fat`` prefix and call ``fopen("/fat/file.txt", "w")``. Then the VFS component calls the function ``open`` of the FAT driver and pass the argument ``/file.txt`` to it together with appropriate mode flags. All subsequent calls to C library functions for the returned ``FILE*`` stream will also be forwarded to the FAT driver.
|
||||
For example, one can mount a FAT filesystem driver at the ``/fat`` prefix and call ``fopen("/fat/file.txt", "w")``. Then the VFS component calls the function ``open`` of the FAT driver and pass the argument ``/file.txt`` to it together with appropriate mode flags. All subsequent calls to C library functions for the returned ``FILE*`` stream will also be forwarded to the FAT driver.
|
||||
|
||||
|
||||
FS Registration
|
||||
---------------
|
||||
|
||||
To register an FS driver, an application needs to define an instance of the :cpp:type:`esp_vfs_t` structure and populate it with function pointers to FS APIs:
|
||||
.. note:: For previous version of the API (using :cpp:type:`esp_vfs_t`) see documentation for previous release.
|
||||
|
||||
To register an FS driver, an application needs to define an instance of the :cpp:type:`esp_vfs_fs_ops_t` structure and populate it with function pointers to FS APIs:
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
esp_vfs_t myfs = {
|
||||
.flags = ESP_VFS_FLAG_DEFAULT,
|
||||
.write = &myfs_write,
|
||||
.open = &myfs_open,
|
||||
// Both esp_vfs_fs_ops_t and its subcomponents have to have static storage
|
||||
static const esp_vfs_dir_ops_t myfs_dir = {
|
||||
.fstat = &myfs_fstat,
|
||||
.close = &myfs_close,
|
||||
.read = &myfs_read,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_vfs_register("/data", &myfs, NULL));
|
||||
|
||||
Depending on the way how the FS driver declares its API functions, either ``read``, ``write``, etc., or ``read_p``, ``write_p``, etc., should be used.
|
||||
|
||||
Case 1: API functions are declared without an extra context pointer (the FS driver is a singleton)::
|
||||
|
||||
ssize_t myfs_write(int fd, const void * data, size_t size);
|
||||
|
||||
// In definition of esp_vfs_t:
|
||||
.flags = ESP_VFS_FLAG_DEFAULT,
|
||||
static const esp_vfs_fs_ops_t myfs = {
|
||||
.write = &myfs_write,
|
||||
// ... other members initialized
|
||||
.open = &myfs_open,
|
||||
.close = &myfs_close,
|
||||
.read = &myfs_read,
|
||||
.dir = &myfs_dir,
|
||||
};
|
||||
|
||||
// When registering FS, context pointer (the third argument) is NULL:
|
||||
ESP_ERROR_CHECK(esp_vfs_register("/data", &myfs, NULL));
|
||||
ESP_ERROR_CHECK(esp_vfs_register_fs("/data", &myfs, ESP_VFS_FLAG_STATIC, NULL));
|
||||
|
||||
Case 2: API functions are declared with an extra context pointer (the FS driver supports multiple instances)::
|
||||
|
||||
Non-static :cpp:type:`esp_vfs_fs_ops_t`
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The recommended approach for registering filesystem is to use statically allocated :cpp:type:`esp_vfs_fs_ops_t` alongside ``ESP_VFS_FLAG_STATIC``, as it is more memory efficient. In cases where using static allocation is not possible, ``ESP_VFS_FLAG_STATIC`` can replaced with ``ESP_VFS_FLAG_DEFAULT``. This tells VFS to make a deep copy of the passed structure in RAM, this copy will be managed by VFS component.
|
||||
|
||||
.. highlight:: c
|
||||
|
||||
::
|
||||
|
||||
// Possibly local scope
|
||||
{
|
||||
esp_vfs_dir_ops_t myfs_dir = {
|
||||
.fstat = &myfs_fstat,
|
||||
};
|
||||
|
||||
bool some_condition = false;
|
||||
|
||||
esp_vfs_fs_ops_t myfs = {
|
||||
.write = some_condition ? &myfs_special_write : &myfs_write,
|
||||
// ... other members
|
||||
.dir = &myfs_dir,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_vfs_register_fs("/data", &myfs, ESP_VFS_FLAG_DEFAULT, NULL));
|
||||
}
|
||||
|
||||
|
||||
Context aware filesystem
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In some cases it might be beneficial, or even necessary to pass some context to the filesystem functions, such as mountpoint specific file descriptor table, when multiple instances of FS mounted. For this reason the :cpp:type:`esp_vfs_fs_ops_t` contains second version of each member with ``_p`` suffix, for example for ``read`` there is ``read_p``, these functions have additional first argument. When registering the FS, ``ESP_VFS_FLAG_CONTEXT_PTR`` needs to be specified and the pointer passed as the last argument.
|
||||
|
||||
::
|
||||
|
||||
ssize_t myfs_write(myfs_t* fs, int fd, const void * data, size_t size);
|
||||
|
||||
// In definition of esp_vfs_t:
|
||||
.flags = ESP_VFS_FLAG_CONTEXT_PTR,
|
||||
.write_p = &myfs_write,
|
||||
// ... other members initialized
|
||||
|
||||
// When registering FS, pass the FS context pointer into the third argument
|
||||
// When registering FS, pass the ESP_VFS_FLAG_CONTEXT_PTR flag, alongside FS context pointer into the third argument
|
||||
// (hypothetical myfs_mount function is used for illustrative purposes)
|
||||
myfs_t* myfs_inst1 = myfs_mount(partition1->offset, partition1->size);
|
||||
ESP_ERROR_CHECK(esp_vfs_register("/data1", &myfs, myfs_inst1));
|
||||
ESP_ERROR_CHECK(esp_vfs_register_fs("/data1", &myfs, ESP_VFS_FLAG_STATIC | ESP_VFS_FLAG_CONTEXT_PTR, myfs_inst1));
|
||||
|
||||
// Can register another instance:
|
||||
myfs_t* myfs_inst2 = myfs_mount(partition2->offset, partition2->size);
|
||||
ESP_ERROR_CHECK(esp_vfs_register("/data2", &myfs, myfs_inst2));
|
||||
ESP_ERROR_CHECK(esp_vfs_register_fs("/data2", &myfs, ESP_VFS_FLAG_STATIC | ESP_VFS_FLAG_CONTEXT_PTR, myfs_inst2));
|
||||
|
||||
|
||||
Synchronous Input/Output Multiplexing
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
-------------------------------------
|
||||
|
||||
Synchronous input/output multiplexing by :cpp:func:`select` is supported in the VFS component. The implementation works in the following way.
|
||||
|
||||
@ -82,8 +108,9 @@ Synchronous input/output multiplexing by :cpp:func:`select` is supported in the
|
||||
|
||||
6. The :cpp:func:`select` call ends and returns the appropriate results.
|
||||
|
||||
|
||||
Non-Socket VFS Drivers
|
||||
""""""""""""""""""""""
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you want to use :cpp:func:`select` with a file descriptor belonging to a non-socket VFS driver, then you need to register the driver with functions :cpp:func:`start_select` and :cpp:func:`end_select` similarly to the following example:
|
||||
|
||||
@ -91,7 +118,7 @@ If you want to use :cpp:func:`select` with a file descriptor belonging to a non-
|
||||
|
||||
::
|
||||
|
||||
// In definition of esp_vfs_t:
|
||||
// In definition of esp_vfs_select_ops_t:
|
||||
.start_select = &uart_start_select,
|
||||
.end_select = &uart_end_select,
|
||||
// ... other members initialized
|
||||
@ -113,7 +140,7 @@ Please check the following examples that demonstrate the use of :cpp:func:`selec
|
||||
|
||||
|
||||
Socket VFS Drivers
|
||||
""""""""""""""""""
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A socket VFS driver is using its own internal implementation of :cpp:func:`select` and non-socket VFS drivers notify it upon read/write/error conditions.
|
||||
|
||||
@ -123,7 +150,7 @@ A socket VFS driver needs to be registered with the following functions defined:
|
||||
|
||||
::
|
||||
|
||||
// In definition of esp_vfs_t:
|
||||
// In definition of esp_vfs_select_ops_t:
|
||||
.socket_select = &lwip_select,
|
||||
.get_socket_select_semaphore = &lwip_get_socket_select_semaphore,
|
||||
.stop_socket_select = &lwip_stop_socket_select,
|
||||
@ -146,6 +173,7 @@ Please see :component_file:`lwip/port/esp32xx/vfs_lwip.c` for a reference socket
|
||||
|
||||
You should not change the socket driver during an active :cpp:func:`select` call or you might experience some undefined behavior.
|
||||
|
||||
|
||||
Paths
|
||||
-----
|
||||
|
||||
@ -195,16 +223,6 @@ Standard I/O streams (``stdin``, ``stdout``, ``stderr``) are mapped to file desc
|
||||
Note that creating an eventfd with ``EFD_SUPPORT_ISR`` will cause interrupts to be temporarily disabled when reading, writing the file and during the beginning and the ending of the ``select()`` when this file is set.
|
||||
|
||||
|
||||
Minified VFS
|
||||
------------
|
||||
|
||||
To minimize RAM usage, an alternative version of :cpp:func:`esp_vfs_register` function, :cpp:func:`esp_vfs_register_fs` is provided. This version accepts :cpp:class:`esp_vfs_fs_ops_t` instead of :cpp:class:`esp_vfs_t` alongside separate argument for OR-ed flags. Unlike :cpp:func:`esp_vfs_register`, it can handle statically allocated struct, as long as the ``ESP_VFS_FLAG_STATIC`` is provided.
|
||||
|
||||
The :cpp:class:`esp_vfs_fs_ops_t` is split into separate structs based on features (directory operations, select support, termios support, ...). The main struct contains the basic functions (``read``, ``write``, ...), alongside pointers to the feature-specific structs. These pointers can be ``NULL`` indicating lack of support for all the functions provided by that struct, which decreases the required memory.
|
||||
|
||||
Internally the VFS component uses this version of API, with additional steps to convert the :cpp:class:`esp_vfs_t` to :cpp:class:`esp_vfs_fs_ops_t` upon registration.
|
||||
|
||||
|
||||
Well Known VFS Devices
|
||||
----------------------
|
||||
|
||||
@ -214,6 +232,7 @@ IDF defines several VFS devices that can be used by applications. These devices
|
||||
* ``/dev/null`` - file that discards all data written to it and returns EOF when read. It is automatically created if :ref:`CONFIG_VFS_INITIALIZE_DEV_NULL` is enabled.
|
||||
* ``/dev/console`` - file that is connected to the primary and secondary outputs specified in the menuconfig by :ref:`CONFIG_ESP_CONSOLE_UART` and :ref:`CONFIG_ESP_CONSOLE_SECONDARY` respectively. More information can be found here :doc:`../../api-guides/stdio`.
|
||||
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
@ -223,6 +242,7 @@ Application Examples
|
||||
|
||||
- :example:`storage/semihost_vfs` demonstrates how to use the semihosting VFS driver, including registering a host directory, redirecting stdout from UART to a file on the host, and reading and printing the content of a text file.
|
||||
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user