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