From ced490fc6eb613ee0ef53852f3dbbd1155623e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rohl=C3=ADnek?= Date: Fri, 1 Nov 2024 14:05:31 +0100 Subject: [PATCH 01/12] feat(storage/vfs): make subcomponent pointers const --- components/vfs/include/esp_vfs_ops.h | 6 +- components/vfs/vfs.c | 201 ++++++++++++++++----------- 2 files changed, 125 insertions(+), 82 deletions(-) diff --git a/components/vfs/include/esp_vfs_ops.h b/components/vfs/include/esp_vfs_ops.h index bd5d635235..63f03508f2 100644 --- a/components/vfs/include/esp_vfs_ops.h +++ b/components/vfs/include/esp_vfs_ops.h @@ -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; diff --git a/components/vfs/vfs.c b/components/vfs/vfs.c index 1f02e0f550..52a8dfe31c 100644 --- a/components/vfs/vfs.c +++ b/components/vfs/vfs.c @@ -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) From 09d222c38d8a73e4e999c33fed532361571d1894 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rohl=C3=ADnek?= Date: Thu, 31 Oct 2024 11:35:48 +0100 Subject: [PATCH 02/12] feat(storage/fatfs): move fatfs to new VFS API --- components/fatfs/vfs/vfs_fat.c | 69 ++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/components/fatfs/vfs/vfs_fat.c b/components/fatfs/vfs/vfs_fat.c index bb44b635b5..6a72e80c2c 100644 --- a/components/fatfs/vfs/vfs_fat.c +++ b/components/fatfs/vfs/vfs_fat.c @@ -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); From 6b070392819b559757543fda1b1f4ad10d109830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rohl=C3=ADnek?= Date: Thu, 31 Oct 2024 12:29:59 +0100 Subject: [PATCH 03/12] feat(storage/spiffs): move spiffs to new VFS API --- components/spiffs/esp_spiffs.c | 73 ++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/components/spiffs/esp_spiffs.c b/components/spiffs/esp_spiffs.c index e1cffc6a5f..b4820f5098 100644 --- a/components/spiffs/esp_spiffs.c +++ b/components/spiffs/esp_spiffs.c @@ -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; From e5ab487e3c932bb95afd4ed50d0f49fc39d783c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rohl=C3=ADnek?= Date: Thu, 31 Oct 2024 13:25:36 +0100 Subject: [PATCH 04/12] feat(storage/vfs): update docs to new version of API --- docs/en/api-guides/stdio.rst | 2 +- docs/en/api-reference/storage/vfs.rst | 104 +++++++++++++++----------- 2 files changed, 63 insertions(+), 43 deletions(-) diff --git a/docs/en/api-guides/stdio.rst b/docs/en/api-guides/stdio.rst index e9bff46588..55d14f6f02 100644 --- a/docs/en/api-guides/stdio.rst +++ b/docs/en/api-guides/stdio.rst @@ -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 diff --git a/docs/en/api-reference/storage/vfs.rst b/docs/en/api-reference/storage/vfs.rst index b8e4bd3c75..0c788bb972 100644 --- a/docs/en/api-reference/storage/vfs.rst +++ b/docs/en/api-reference/storage/vfs.rst @@ -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 ------------- From bb305bd109d17a6d1697d6031044f58e9cf05f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rohl=C3=ADnek?= Date: Thu, 31 Oct 2024 14:13:04 +0100 Subject: [PATCH 05/12] feat(storage/esp_vfs_console): move cdcacm driver to new vfs API --- .../include/esp_private/esp_vfs_cdcacm.h | 2 +- components/esp_vfs_console/vfs_cdcacm.c | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/components/esp_vfs_console/include/esp_private/esp_vfs_cdcacm.h b/components/esp_vfs_console/include/esp_private/esp_vfs_cdcacm.h index b93d1a4da0..d03b06b3fc 100644 --- a/components/esp_vfs_console/include/esp_private/esp_vfs_cdcacm.h +++ b/components/esp_vfs_console/include/esp_private/esp_vfs_cdcacm.h @@ -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 } diff --git a/components/esp_vfs_console/vfs_cdcacm.c b/components/esp_vfs_console/vfs_cdcacm.c index 01545d96b5..98b3330d54 100644 --- a/components/esp_vfs_console/vfs_cdcacm.c +++ b/components/esp_vfs_console/vfs_cdcacm.c @@ -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); } From 39197864a173bbf6b730d5488bad70da61b9f6ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rohl=C3=ADnek?= Date: Fri, 1 Nov 2024 13:16:57 +0100 Subject: [PATCH 06/12] feat(storage/vfs): make part of nullfs API private --- components/esp_vfs_console/vfs_console.c | 1 + components/vfs/include/esp_private/nullfs.h | 24 +++++++++++++++++++++ components/vfs/include/esp_vfs_null.h | 7 ------ 3 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 components/vfs/include/esp_private/nullfs.h diff --git a/components/esp_vfs_console/vfs_console.c b/components/esp_vfs_console/vfs_console.c index f6a2d26096..df49d518ce 100644 --- a/components/esp_vfs_console/vfs_console.c +++ b/components/esp_vfs_console/vfs_console.c @@ -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 diff --git a/components/vfs/include/esp_private/nullfs.h b/components/vfs/include/esp_private/nullfs.h new file mode 100644 index 0000000000..6baec03b0e --- /dev/null +++ b/components/vfs/include/esp_private/nullfs.h @@ -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_t *esp_vfs_null_get_vfs(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/vfs/include/esp_vfs_null.h b/components/vfs/include/esp_vfs_null.h index f3983894d0..9602c6b663 100644 --- a/components/vfs/include/esp_vfs_null.h +++ b/components/vfs/include/esp_vfs_null.h @@ -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 * From 906b3df54b3c40b827c8ad5c43f8b3b16a07ad12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rohl=C3=ADnek?= Date: Fri, 1 Nov 2024 13:19:23 +0100 Subject: [PATCH 07/12] feat(storage/vfs): move nullfs to new API --- components/vfs/include/esp_private/nullfs.h | 2 +- components/vfs/nullfs.c | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/components/vfs/include/esp_private/nullfs.h b/components/vfs/include/esp_private/nullfs.h index 6baec03b0e..070b2b25e0 100644 --- a/components/vfs/include/esp_private/nullfs.h +++ b/components/vfs/include/esp_private/nullfs.h @@ -17,7 +17,7 @@ extern "C" { * * @return VFS structure for /dev/null */ -const esp_vfs_t *esp_vfs_null_get_vfs(void); +const esp_vfs_fs_ops_t *esp_vfs_null_get_vfs(void); #ifdef __cplusplus } diff --git a/components/vfs/nullfs.c b/components/vfs/nullfs.c index 8ac166d2dc..719620cb09 100644 --- a/components/vfs/nullfs.c +++ b/components/vfs/nullfs.c @@ -16,6 +16,7 @@ #include #include #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) From 0aa10dcb434d5161819998f8ad748e77f537fdd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rohl=C3=ADnek?= Date: Fri, 1 Nov 2024 12:06:17 +0100 Subject: [PATCH 08/12] feat(storage/vfs): move uart driver to new vfs API --- .../include/driver/esp_private/uart_vfs.h | 2 +- components/esp_driver_uart/src/uart_vfs.c | 41 +++++++++++++------ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/components/esp_driver_uart/include/driver/esp_private/uart_vfs.h b/components/esp_driver_uart/include/driver/esp_private/uart_vfs.h index c83e513ab5..b74996ae6f 100644 --- a/components/esp_driver_uart/include/driver/esp_private/uart_vfs.h +++ b/components/esp_driver_uart/include/driver/esp_private/uart_vfs.h @@ -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 } diff --git a/components/esp_driver_uart/src/uart_vfs.c b/components/esp_driver_uart/src/uart_vfs.c index 6e5a0dbfaf..6c38b41867 100644 --- a/components/esp_driver_uart/src/uart_vfs.c +++ b/components/esp_driver_uart/src/uart_vfs.c @@ -985,8 +985,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, @@ -995,28 +1016,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) From 4ff53bc9c71cc45ca1d3afa3da419d61dbd9ed63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rohl=C3=ADnek?= Date: Fri, 1 Nov 2024 12:23:59 +0100 Subject: [PATCH 09/12] feat(storage/vfs): move jtag driver to new vfs API --- .../driver/esp_private/usb_serial_jtag_vfs.h | 2 +- .../src/usb_serial_jtag_vfs.c | 36 ++++++++++++------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/components/esp_driver_usb_serial_jtag/include/driver/esp_private/usb_serial_jtag_vfs.h b/components/esp_driver_usb_serial_jtag/include/driver/esp_private/usb_serial_jtag_vfs.h index 7a94728580..4415447613 100644 --- a/components/esp_driver_usb_serial_jtag/include/driver/esp_private/usb_serial_jtag_vfs.h +++ b/components/esp_driver_usb_serial_jtag/include/driver/esp_private/usb_serial_jtag_vfs.h @@ -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 } diff --git a/components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_vfs.c b/components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_vfs.c index 3d74b7ae27..d24ef0c351 100644 --- a/components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_vfs.c +++ b/components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_vfs.c @@ -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 From 0cd2e72dc12e49b20ffb7ada03e4a68d9a0123f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rohl=C3=ADnek?= Date: Fri, 1 Nov 2024 12:24:57 +0100 Subject: [PATCH 10/12] feat(storage/esp_vfs_console): move console to new vfs API --- components/esp_vfs_console/vfs_console.c | 52 ++++++++++++++++-------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/components/esp_vfs_console/vfs_console.c b/components/esp_vfs_console/vfs_console.c index df49d518ce..676f4a0393 100644 --- a/components/esp_vfs_console/vfs_console.c +++ b/components/esp_vfs_console/vfs_console.c @@ -38,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}; @@ -122,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; @@ -132,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; @@ -164,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, @@ -173,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) From 7fe45180db98a5d485362c7210f2e0fc777e5bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rohl=C3=ADnek?= Date: Fri, 1 Nov 2024 13:08:48 +0100 Subject: [PATCH 11/12] feat(storage/vfs): move l2tap to new vfs API --- .../esp_netif/vfs_l2tap/esp_vfs_l2tap.c | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/components/esp_netif/vfs_l2tap/esp_vfs_l2tap.c b/components/esp_netif/vfs_l2tap/esp_vfs_l2tap.c index 69b1959f41..72685bcfa2 100644 --- a/components/esp_netif/vfs_l2tap/esp_vfs_l2tap.c +++ b/components/esp_netif/vfs_l2tap/esp_vfs_l2tap.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -580,8 +580,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(); @@ -593,20 +610,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; } From def8b9b6390578f2cdd02f04f4dd2920cee118a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rohl=C3=ADnek?= Date: Fri, 1 Nov 2024 13:09:39 +0100 Subject: [PATCH 12/12] feat(storage/vfs): move semihost to new vfs API --- components/vfs/vfs_semihost.c | 66 ++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/components/vfs/vfs_semihost.c b/components/vfs/vfs_semihost.c index 2c2fdaed23..3c3ccd85df 100644 --- a/components/vfs/vfs_semihost.c +++ b/components/vfs/vfs_semihost.c @@ -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;