From d0d3c4a3a7fc6d2e76748446371f3788a0f5db7c Mon Sep 17 00:00:00 2001 From: "Peter A. Bigot" Date: Mon, 1 Jun 2020 12:08:58 -0500 Subject: [PATCH] fs: allow external file system implementations The documentation claims that Zephyr supports external file system implementations, and there's no reason not to do so. Rework the API to allow this. Note that the file system type cannot legally be an enum anymore, since we need to support file system types that don't have an identifier assigned in that enum. Rely on the implicit conversion of enum values to int to preserve backwards compatibility. Signed-off-by: Peter A. Bigot --- doc/reference/file_system/index.rst | 8 ++- include/fs/fs.h | 32 +++++++-- subsys/fs/Kconfig | 8 +++ subsys/fs/fs.c | 100 +++++++++++++++++++++------- 4 files changed, 115 insertions(+), 33 deletions(-) diff --git a/doc/reference/file_system/index.rst b/doc/reference/file_system/index.rst index 84b9d97603e..2ef9854a450 100644 --- a/doc/reference/file_system/index.rst +++ b/doc/reference/file_system/index.rst @@ -12,13 +12,15 @@ specific API or internal functions by introducing file system registration mechanisms. In Zephyr, any file system implementation or library can be plugged into or -pulled out through a file system registration API. +pulled out through a file system registration API. Each file system +implementation must have a globally unique integer identifier; use +:c:macro:`FS_TYPE_EXTERNAL_BASE` to avoid clashes with in-tree identifiers. .. code-block:: c - int fs_register(enum fs_type type, const struct fs_file_system_t *fs); + int fs_register(int type, const struct fs_file_system_t *fs); - int fs_unregister(enum fs_type type, const struct fs_file_system_t *fs); + int fs_unregister(int type, const struct fs_file_system_t *fs); Zephyr RTOS supports multiple instances of a file system by making use of the mount point as the disk volume name, which is used by the file system library diff --git a/include/fs/fs.h b/include/fs/fs.h index 8f6a001754b..76bf25bd107 100644 --- a/include/fs/fs.h +++ b/include/fs/fs.h @@ -45,12 +45,30 @@ enum fs_dir_entry_type { FS_DIR_ENTRY_DIR }; -enum fs_type { +/** @brief Enumeration to uniquely identify file system types. + * + * Zephyr supports in-tree file systems and external ones. Each + * requires a unique identifier used to register the file system + * implementation and to associate a mount point with the file system + * type. This anonymous enum defines global identifiers for the + * in-tree file systems. + * + * External file systems should be registered using unique identifiers + * starting at @c FS_TYPE_EXTERNAL_BASE. It is the responsibility of + * applications that use external file systems to ensure that these + * identifiers are unique if multiple file system implementations are + * used by the application. + */ +enum { + /** Identifier for in-tree FatFS file system. */ FS_FATFS = 0, - FS_LITTLEFS, - FS_TYPE_END, -}; + /** Identifier for in-tree LittleFS file system. */ + FS_LITTLEFS, + + /** Base identifier for external file systems. */ + FS_TYPE_EXTERNAL_BASE, +}; /** * @brief File system mount info structure @@ -65,7 +83,7 @@ enum fs_type { */ struct fs_mount_t { sys_dnode_t node; - enum fs_type type; + int type; const char *mnt_point; void *fs_data; void *storage_dev; @@ -494,7 +512,7 @@ int fs_statvfs(const char *path, struct fs_statvfs *stat); * @retval 0 Success * @retval -ERRNO errno code if error */ -int fs_register(enum fs_type type, const struct fs_file_system_t *fs); +int fs_register(int type, const struct fs_file_system_t *fs); /** * @brief Unregister a file system @@ -507,7 +525,7 @@ int fs_register(enum fs_type type, const struct fs_file_system_t *fs); * @retval 0 Success * @retval -ERRNO errno code if error */ -int fs_unregister(enum fs_type type, const struct fs_file_system_t *fs); +int fs_unregister(int type, const struct fs_file_system_t *fs); /** * @} diff --git a/subsys/fs/Kconfig b/subsys/fs/Kconfig index 4e96e77283a..dfc93291973 100644 --- a/subsys/fs/Kconfig +++ b/subsys/fs/Kconfig @@ -28,6 +28,14 @@ config FAT_FILESYSTEM_ELM help Use the ELM FAT File system implementation. +config FILE_SYSTEM_MAX_TYPES + int "Maximum number of distinct file system types allowed" + default 2 + help + Zephyr provides several file system types including FatFS and + LittleFS, but it is possible to define additional ones and + register them. A slot is required for each type. + menu "FatFs Settings" visible if FAT_FILESYSTEM_ELM diff --git a/subsys/fs/fs.c b/subsys/fs/fs.c index 6a278c960da..a662fa3d502 100644 --- a/subsys/fs/fs.c +++ b/subsys/fs/fs.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2018 Intel Corporation. + * Copyright (c) 2020 Peter Bigot Consulting, LLC * * SPDX-License-Identifier: Apache-2.0 */ @@ -23,8 +24,57 @@ static sys_dlist_t fs_mnt_list; /* lock to protect mount list operations */ static struct k_mutex mutex; -/* file system map table */ -static const struct fs_file_system_t *fs_map[FS_TYPE_END]; +/* Maps an identifier used in mount points to the file system + * implementation. + */ +struct registry_entry { + int type; + const struct fs_file_system_t *fstp; +}; +static struct registry_entry registry[CONFIG_FILE_SYSTEM_MAX_TYPES]; + +static inline void registry_clear_entry(struct registry_entry *ep) +{ + ep->fstp = NULL; +} + +static int registry_add(int type, + const struct fs_file_system_t *fstp) +{ + int rv = -ENOSPC; + + for (size_t i = 0; i < ARRAY_SIZE(registry); ++i) { + struct registry_entry *ep = ®istry[i]; + + if (ep->fstp == NULL) { + ep->type = type; + ep->fstp = fstp; + rv = 0; + break; + } + } + + return rv; +} + +static struct registry_entry *registry_find(int type) +{ + for (size_t i = 0; i < ARRAY_SIZE(registry); ++i) { + struct registry_entry *ep = ®istry[i]; + + if ((ep->fstp != NULL) && (ep->type == type)) { + return ep; + } + } + return NULL; +} + +static const struct fs_file_system_t *fs_type_get(int type) +{ + struct registry_entry *ep = registry_find(type); + + return (ep != NULL) ? ep->fstp : NULL; +} static int fs_get_mnt_point(struct fs_mount_t **mnt_pntp, const char *name, size_t *match_len) @@ -536,15 +586,15 @@ int fs_mount(struct fs_mount_t *mp) } k_mutex_lock(&mutex, K_FOREVER); - /* Check if requested file system is registered */ - if (mp->type >= FS_TYPE_END || fs_map[mp->type] == NULL) { - LOG_ERR("requested file system not registered!!"); + + /* Get file system information */ + fs = fs_type_get(mp->type); + if (fs == NULL) { + LOG_ERR("requested file system type not registered!!"); rc = -ENOENT; goto mount_err; } - /* Get fs interface from file system map */ - fs = fs_map[mp->type]; mp->mountp_len = strlen(mp->mnt_point); if ((mp->mnt_point[0] != '/') || @@ -664,39 +714,43 @@ int fs_readmount(int *number, const char **name) } /* Register File system */ -int fs_register(enum fs_type type, const struct fs_file_system_t *fs) +int fs_register(int type, const struct fs_file_system_t *fs) { int rc = 0; k_mutex_lock(&mutex, K_FOREVER); - if (type >= FS_TYPE_END) { - LOG_ERR("failed to register File system!!"); - rc = -EINVAL; - goto reg_err; + + if (fs_type_get(type) != NULL) { + rc = -EALREADY; + } else { + rc = registry_add(type, fs); } - fs_map[type] = fs; - LOG_DBG("fs registered of type(%u)", type); -reg_err: + k_mutex_unlock(&mutex); + + LOG_DBG("fs register %d: %d", type, rc); + return rc; } /* Unregister File system */ -int fs_unregister(enum fs_type type, const struct fs_file_system_t *fs) +int fs_unregister(int type, const struct fs_file_system_t *fs) { int rc = 0; + struct registry_entry *ep; k_mutex_lock(&mutex, K_FOREVER); - if ((type >= FS_TYPE_END) || - (fs_map[type] != fs)) { - LOG_ERR("failed to unregister File system!!"); + + ep = registry_find(type); + if ((ep == NULL) || (ep->fstp != fs)) { rc = -EINVAL; - goto unreg_err; + } else { + registry_clear_entry(ep); } - fs_map[type] = NULL; - LOG_DBG("fs unregistered of type(%u)", type); -unreg_err: + k_mutex_unlock(&mutex); + + LOG_DBG("fs unregister %d: %d", type, rc); return rc; }