| /* |
| * Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu> |
| * Copyright (c) 2025 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/fs/fs.h> |
| #include <nsi_errno.h> |
| |
| #include "cmdline.h" |
| #include "soc.h" |
| |
| #include "fuse_fs_access_bottom.h" |
| |
| #define NUMBER_OF_OPEN_FILES 128 |
| |
| static struct fs_file_t files[NUMBER_OF_OPEN_FILES]; |
| static uint8_t file_handles[NUMBER_OF_OPEN_FILES]; |
| |
| static const char default_fuse_mountpoint[] = "flash"; |
| |
| static const char *fuse_mountpoint; |
| |
| static ssize_t get_new_file_handle(void) |
| { |
| size_t idx; |
| |
| for (idx = 0; idx < ARRAY_SIZE(file_handles); ++idx) { |
| if (file_handles[idx] == 0) { |
| ++file_handles[idx]; |
| return idx; |
| } |
| } |
| |
| return -ENOMEM; |
| } |
| |
| static void release_file_handle(size_t handle) |
| { |
| if (handle < ARRAY_SIZE(file_handles)) { |
| --file_handles[handle]; |
| } |
| } |
| |
| static int ffa_stat_top(const char *path, struct ffa_dirent *entry_bottom) |
| { |
| struct fs_dirent entry; |
| int err; |
| |
| err = fs_stat(path, &entry); |
| |
| if (err != 0) { |
| return nsi_errno_to_mid(-err); |
| } |
| |
| entry_bottom->size = entry.size; |
| |
| if (entry.type == FS_DIR_ENTRY_DIR) { |
| entry_bottom->is_directory = true; |
| } else { |
| entry_bottom->is_directory = false; |
| } |
| |
| return 0; |
| } |
| |
| static int ffa_readmount_top(int *mnt_nbr, const char **mnt_name) |
| { |
| int err = fs_readmount(mnt_nbr, mnt_name); |
| |
| return nsi_errno_to_mid(-err); |
| } |
| |
| /* Status shared between readdir_* calls */ |
| static struct { |
| struct fs_dir_t dir; |
| struct fs_dirent entry; |
| } readdir_status; |
| |
| static int ffa_readdir_start(const char *path) |
| { |
| int err; |
| |
| fs_dir_t_init(&readdir_status.dir); |
| err = fs_opendir(&readdir_status.dir, path); |
| |
| return nsi_errno_to_mid(-err); |
| } |
| |
| static int ffa_readdir_read_next(struct ffa_dirent *entry_bottom) |
| { |
| int err; |
| |
| err = fs_readdir(&readdir_status.dir, &readdir_status.entry); |
| |
| if (err) { |
| return nsi_errno_to_mid(-err); |
| } |
| entry_bottom->name = readdir_status.entry.name; |
| entry_bottom->size = readdir_status.entry.size; |
| |
| if (readdir_status.entry.type == FS_DIR_ENTRY_DIR) { |
| entry_bottom->is_directory = true; |
| } else { |
| entry_bottom->is_directory = false; |
| } |
| |
| return 0; |
| } |
| |
| static void ffa_readdir_end(void) |
| { |
| (void)fs_closedir(&readdir_status.dir); |
| } |
| |
| static int ffa_create_top(const char *path, uint64_t *fh) |
| { |
| int err; |
| ssize_t handle; |
| |
| handle = get_new_file_handle(); |
| if (handle < 0) { |
| return nsi_errno_to_mid(-handle); |
| } |
| |
| *fh = handle; |
| |
| err = fs_open(&files[handle], path, FS_O_CREATE | FS_O_WRITE); |
| if (err != 0) { |
| release_file_handle(handle); |
| *fh = INVALID_FILE_HANDLE; |
| return nsi_errno_to_mid(-err); |
| } |
| |
| return 0; |
| } |
| |
| static int ffa_release_top(uint64_t fh) |
| { |
| fs_close(&files[fh]); |
| |
| release_file_handle(fh); |
| |
| return 0; |
| } |
| |
| static int ffa_read_top(uint64_t fh, char *buf, size_t size, off_t off) |
| { |
| int err; |
| |
| err = fs_seek(&files[fh], off, FS_SEEK_SET); |
| |
| if (err == 0) { |
| err = fs_read(&files[fh], buf, size); |
| } |
| |
| return nsi_errno_to_mid(-err); |
| } |
| |
| static int ffa_write_top(uint64_t fh, const char *buf, size_t size, off_t off) |
| { |
| int err; |
| |
| err = fs_seek(&files[fh], off, FS_SEEK_SET); |
| |
| if (err == 0) { |
| err = fs_write(&files[fh], buf, size); |
| } |
| |
| return nsi_errno_to_mid(-err); |
| } |
| |
| static int ffa_ftruncate_top(uint64_t fh, off_t size) |
| { |
| int err = fs_truncate(&files[fh], size); |
| |
| return nsi_errno_to_mid(-err); |
| } |
| |
| static int ffa_truncate_top(const char *path, off_t size) |
| { |
| int err; |
| static struct fs_file_t file; |
| |
| err = fs_open(&file, path, FS_O_CREATE | FS_O_WRITE); |
| if (err != 0) { |
| return nsi_errno_to_mid(-err); |
| } |
| |
| err = fs_truncate(&file, size); |
| if (err != 0) { |
| fs_close(&file); |
| } else { |
| err = fs_close(&file); |
| } |
| |
| return nsi_errno_to_mid(-err); |
| } |
| |
| static int ffa_mkdir_top(const char *path) |
| { |
| int err = fs_mkdir(path); |
| |
| return nsi_errno_to_mid(-err); |
| } |
| |
| static int ffa_unlink_top(const char *path) |
| { |
| int err = fs_unlink(path); |
| |
| return nsi_errno_to_mid(-err); |
| } |
| |
| struct ffa_op_callbacks op_callbacks = { |
| .readdir_start = ffa_readdir_start, |
| .readdir_read_next = ffa_readdir_read_next, |
| .readdir_end = ffa_readdir_end, |
| .stat = ffa_stat_top, |
| .readmount = ffa_readmount_top, |
| .mkdir = ffa_mkdir_top, |
| .create = ffa_create_top, |
| .release = ffa_release_top, |
| .read = ffa_read_top, |
| .write = ffa_write_top, |
| .ftruncate = ffa_ftruncate_top, |
| .truncate = ffa_truncate_top, |
| .unlink = ffa_unlink_top, |
| .rmdir = ffa_unlink_top, |
| }; |
| |
| static void fuse_top_dispath_thread(void *arg1, void *arg2, void *arg3) |
| { |
| ARG_UNUSED(arg1); |
| ARG_UNUSED(arg2); |
| ARG_UNUSED(arg3); |
| |
| #define COOLDOWN_TIME 10 |
| int cooldown_count = 0; |
| |
| while (true) { |
| if (ffa_is_op_pended()) { |
| ffa_run_pending_op(); |
| cooldown_count = COOLDOWN_TIME; |
| } else { |
| if (cooldown_count > 0) { |
| k_sleep(K_MSEC(1)); |
| cooldown_count--; |
| } else { |
| k_sleep(K_MSEC(20)); |
| } |
| } |
| } |
| } |
| |
| K_THREAD_DEFINE(fuse_op_handler, CONFIG_ARCH_POSIX_RECOMMENDED_STACK_SIZE, |
| fuse_top_dispath_thread, NULL, NULL, NULL, 100, 0, 0); |
| |
| static void fuse_fs_access_init(void) |
| { |
| size_t i = 0; |
| |
| while (i < ARRAY_SIZE(files)) { |
| fs_file_t_init(&files[i]); |
| ++i; |
| } |
| |
| if (fuse_mountpoint == NULL) { |
| fuse_mountpoint = default_fuse_mountpoint; |
| } |
| |
| ffsa_init_bottom(fuse_mountpoint, &op_callbacks); |
| } |
| |
| static void fuse_fs_access_cleanup(void) |
| { |
| if (fuse_mountpoint == NULL) { |
| return; |
| } |
| |
| ffsa_cleanup_bottom(fuse_mountpoint); |
| } |
| |
| static void fuse_fs_access_options(void) |
| { |
| static struct args_struct_t fuse_fs_access_options[] = { |
| { .manual = false, |
| .is_mandatory = false, |
| .is_switch = false, |
| .option = "flash-mount", |
| .name = "path", |
| .type = 's', |
| .dest = (void *)&fuse_mountpoint, |
| .call_when_found = NULL, |
| .descript = "Path to the directory where to mount flash" }, |
| ARG_TABLE_ENDMARKER |
| }; |
| |
| native_add_command_line_opts(fuse_fs_access_options); |
| } |
| |
| NATIVE_TASK(fuse_fs_access_options, PRE_BOOT_1, 1); |
| NATIVE_TASK(fuse_fs_access_init, PRE_BOOT_2, 1); |
| NATIVE_TASK(fuse_fs_access_cleanup, ON_EXIT, 1); |