1 /*
2 * Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
3 * Copyright (c) 2025 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr/kernel.h>
9 #include <zephyr/fs/fs.h>
10 #include <nsi_errno.h>
11
12 #include "cmdline.h"
13 #include "soc.h"
14
15 #include "fuse_fs_access_bottom.h"
16
17 #define NUMBER_OF_OPEN_FILES 128
18
19 static struct fs_file_t files[NUMBER_OF_OPEN_FILES];
20 static uint8_t file_handles[NUMBER_OF_OPEN_FILES];
21
22 static const char default_fuse_mountpoint[] = "flash";
23
24 static const char *fuse_mountpoint;
25
get_new_file_handle(void)26 static ssize_t get_new_file_handle(void)
27 {
28 size_t idx;
29
30 for (idx = 0; idx < ARRAY_SIZE(file_handles); ++idx) {
31 if (file_handles[idx] == 0) {
32 ++file_handles[idx];
33 return idx;
34 }
35 }
36
37 return -ENOMEM;
38 }
39
release_file_handle(size_t handle)40 static void release_file_handle(size_t handle)
41 {
42 if (handle < ARRAY_SIZE(file_handles)) {
43 --file_handles[handle];
44 }
45 }
46
ffa_stat_top(const char * path,struct ffa_dirent * entry_bottom)47 static int ffa_stat_top(const char *path, struct ffa_dirent *entry_bottom)
48 {
49 struct fs_dirent entry;
50 int err;
51
52 err = fs_stat(path, &entry);
53
54 if (err != 0) {
55 return nsi_errno_to_mid(-err);
56 }
57
58 entry_bottom->size = entry.size;
59
60 if (entry.type == FS_DIR_ENTRY_DIR) {
61 entry_bottom->is_directory = true;
62 } else {
63 entry_bottom->is_directory = false;
64 }
65
66 return 0;
67 }
68
ffa_readmount_top(int * mnt_nbr,const char ** mnt_name)69 static int ffa_readmount_top(int *mnt_nbr, const char **mnt_name)
70 {
71 int err = fs_readmount(mnt_nbr, mnt_name);
72
73 return nsi_errno_to_mid(-err);
74 }
75
76 /* Status shared between readdir_* calls */
77 static struct {
78 struct fs_dir_t dir;
79 struct fs_dirent entry;
80 } readdir_status;
81
ffa_readdir_start(const char * path)82 static int ffa_readdir_start(const char *path)
83 {
84 int err;
85
86 fs_dir_t_init(&readdir_status.dir);
87 err = fs_opendir(&readdir_status.dir, path);
88
89 return nsi_errno_to_mid(-err);
90 }
91
ffa_readdir_read_next(struct ffa_dirent * entry_bottom)92 static int ffa_readdir_read_next(struct ffa_dirent *entry_bottom)
93 {
94 int err;
95
96 err = fs_readdir(&readdir_status.dir, &readdir_status.entry);
97
98 if (err) {
99 return nsi_errno_to_mid(-err);
100 }
101 entry_bottom->name = readdir_status.entry.name;
102 entry_bottom->size = readdir_status.entry.size;
103
104 if (readdir_status.entry.type == FS_DIR_ENTRY_DIR) {
105 entry_bottom->is_directory = true;
106 } else {
107 entry_bottom->is_directory = false;
108 }
109
110 return 0;
111 }
112
ffa_readdir_end(void)113 static void ffa_readdir_end(void)
114 {
115 (void)fs_closedir(&readdir_status.dir);
116 }
117
ffa_create_top(const char * path,uint64_t * fh)118 static int ffa_create_top(const char *path, uint64_t *fh)
119 {
120 int err;
121 ssize_t handle;
122
123 handle = get_new_file_handle();
124 if (handle < 0) {
125 return nsi_errno_to_mid(-handle);
126 }
127
128 *fh = handle;
129
130 err = fs_open(&files[handle], path, FS_O_CREATE | FS_O_WRITE);
131 if (err != 0) {
132 release_file_handle(handle);
133 *fh = INVALID_FILE_HANDLE;
134 return nsi_errno_to_mid(-err);
135 }
136
137 return 0;
138 }
139
ffa_release_top(uint64_t fh)140 static int ffa_release_top(uint64_t fh)
141 {
142 fs_close(&files[fh]);
143
144 release_file_handle(fh);
145
146 return 0;
147 }
148
ffa_read_top(uint64_t fh,char * buf,size_t size,off_t off)149 static int ffa_read_top(uint64_t fh, char *buf, size_t size, off_t off)
150 {
151 int err;
152
153 err = fs_seek(&files[fh], off, FS_SEEK_SET);
154
155 if (err == 0) {
156 err = fs_read(&files[fh], buf, size);
157 }
158
159 return nsi_errno_to_mid(-err);
160 }
161
ffa_write_top(uint64_t fh,const char * buf,size_t size,off_t off)162 static int ffa_write_top(uint64_t fh, const char *buf, size_t size, off_t off)
163 {
164 int err;
165
166 err = fs_seek(&files[fh], off, FS_SEEK_SET);
167
168 if (err == 0) {
169 err = fs_write(&files[fh], buf, size);
170 }
171
172 return nsi_errno_to_mid(-err);
173 }
174
ffa_ftruncate_top(uint64_t fh,off_t size)175 static int ffa_ftruncate_top(uint64_t fh, off_t size)
176 {
177 int err = fs_truncate(&files[fh], size);
178
179 return nsi_errno_to_mid(-err);
180 }
181
ffa_truncate_top(const char * path,off_t size)182 static int ffa_truncate_top(const char *path, off_t size)
183 {
184 int err;
185 static struct fs_file_t file;
186
187 err = fs_open(&file, path, FS_O_CREATE | FS_O_WRITE);
188 if (err != 0) {
189 return nsi_errno_to_mid(-err);
190 }
191
192 err = fs_truncate(&file, size);
193 if (err != 0) {
194 fs_close(&file);
195 } else {
196 err = fs_close(&file);
197 }
198
199 return nsi_errno_to_mid(-err);
200 }
201
ffa_mkdir_top(const char * path)202 static int ffa_mkdir_top(const char *path)
203 {
204 int err = fs_mkdir(path);
205
206 return nsi_errno_to_mid(-err);
207 }
208
ffa_unlink_top(const char * path)209 static int ffa_unlink_top(const char *path)
210 {
211 int err = fs_unlink(path);
212
213 return nsi_errno_to_mid(-err);
214 }
215
216 struct ffa_op_callbacks op_callbacks = {
217 .readdir_start = ffa_readdir_start,
218 .readdir_read_next = ffa_readdir_read_next,
219 .readdir_end = ffa_readdir_end,
220 .stat = ffa_stat_top,
221 .readmount = ffa_readmount_top,
222 .mkdir = ffa_mkdir_top,
223 .create = ffa_create_top,
224 .release = ffa_release_top,
225 .read = ffa_read_top,
226 .write = ffa_write_top,
227 .ftruncate = ffa_ftruncate_top,
228 .truncate = ffa_truncate_top,
229 .unlink = ffa_unlink_top,
230 .rmdir = ffa_unlink_top,
231 };
232
fuse_top_dispath_thread(void * arg1,void * arg2,void * arg3)233 static void fuse_top_dispath_thread(void *arg1, void *arg2, void *arg3)
234 {
235 ARG_UNUSED(arg1);
236 ARG_UNUSED(arg2);
237 ARG_UNUSED(arg3);
238
239 #define COOLDOWN_TIME 10
240 int cooldown_count = 0;
241
242 while (true) {
243 if (ffa_is_op_pended()) {
244 ffa_run_pending_op();
245 cooldown_count = COOLDOWN_TIME;
246 } else {
247 if (cooldown_count > 0) {
248 k_sleep(K_MSEC(1));
249 cooldown_count--;
250 } else {
251 k_sleep(K_MSEC(20));
252 }
253 }
254 }
255 }
256
257 K_THREAD_DEFINE(fuse_op_handler, CONFIG_ARCH_POSIX_RECOMMENDED_STACK_SIZE,
258 fuse_top_dispath_thread, NULL, NULL, NULL, 100, 0, 0);
259
fuse_fs_access_init(void)260 static void fuse_fs_access_init(void)
261 {
262 size_t i = 0;
263
264 while (i < ARRAY_SIZE(files)) {
265 fs_file_t_init(&files[i]);
266 ++i;
267 }
268
269 if (fuse_mountpoint == NULL) {
270 fuse_mountpoint = default_fuse_mountpoint;
271 }
272
273 ffsa_init_bottom(fuse_mountpoint, &op_callbacks);
274 }
275
fuse_fs_access_cleanup(void)276 static void fuse_fs_access_cleanup(void)
277 {
278 if (fuse_mountpoint == NULL) {
279 return;
280 }
281
282 ffsa_cleanup_bottom(fuse_mountpoint);
283 }
284
fuse_fs_access_options(void)285 static void fuse_fs_access_options(void)
286 {
287 static struct args_struct_t fuse_fs_access_options[] = {
288 { .manual = false,
289 .is_mandatory = false,
290 .is_switch = false,
291 .option = "flash-mount",
292 .name = "path",
293 .type = 's',
294 .dest = (void *)&fuse_mountpoint,
295 .call_when_found = NULL,
296 .descript = "Path to the directory where to mount flash" },
297 ARG_TABLE_ENDMARKER
298 };
299
300 native_add_command_line_opts(fuse_fs_access_options);
301 }
302
303 NATIVE_TASK(fuse_fs_access_options, PRE_BOOT_1, 1);
304 NATIVE_TASK(fuse_fs_access_init, PRE_BOOT_2, 1);
305 NATIVE_TASK(fuse_fs_access_cleanup, ON_EXIT, 1);
306