1 /*
2  * Copyright (c) 2019 Bolt Innovation Management, LLC
3  * Copyright (c) 2019 Peter Bigot Consulting, LLC
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <stdio.h>
9 #include <string.h>
10 #include <zephyr/kernel.h>
11 #include <errno.h>
12 #include <zephyr/init.h>
13 #include <zephyr/fs/fs.h>
14 #include <zephyr/fs/fs_sys.h>
15 
16 #define LFS_LOG_REGISTER
17 #include <lfs_util.h>
18 
19 #include <lfs.h>
20 #include <zephyr/fs/littlefs.h>
21 #ifdef CONFIG_FS_LITTLEFS_FMP_DEV
22 #include <zephyr/drivers/flash.h>
23 #include <zephyr/storage/flash_map.h>
24 #endif
25 #ifdef CONFIG_FS_LITTLEFS_BLK_DEV
26 #include <zephyr/storage/disk_access.h>
27 #endif
28 
29 #include "fs_impl.h"
30 
31 /* note: one of the next options have to be enabled, at least */
32 BUILD_ASSERT(IS_ENABLED(CONFIG_FS_LITTLEFS_BLK_DEV) ||
33 	     IS_ENABLED(CONFIG_FS_LITTLEFS_FMP_DEV));
34 
35 struct lfs_file_data {
36 	struct lfs_file file;
37 	struct lfs_file_config config;
38 	void *cache_block;
39 };
40 
41 #define LFS_FILEP(fp) (&((struct lfs_file_data *)(fp->filep))->file)
42 
43 /* Global memory pool for open files and dirs */
44 K_MEM_SLAB_DEFINE_STATIC(file_data_pool, sizeof(struct lfs_file_data),
45 			 CONFIG_FS_LITTLEFS_NUM_FILES, 4);
46 K_MEM_SLAB_DEFINE_STATIC(lfs_dir_pool, sizeof(struct lfs_dir),
47 			 CONFIG_FS_LITTLEFS_NUM_DIRS, 4);
48 
49 /* Inferred overhead, in bytes, for each k_heap_aligned allocation for
50  * the filecache heap.  This relates to the CHUNK_UNIT parameter in
51  * the heap implementation, but that value is not visible outside the
52  * kernel.
53  * FIXME: value for this macro should be rather taken from the Kernel
54  * internals than set by user, but we do not have a way to do so now.
55  */
56 #define FC_HEAP_PER_ALLOC_OVERHEAD CONFIG_FS_LITTLEFS_HEAP_PER_ALLOC_OVERHEAD_SIZE
57 
58 #if (CONFIG_FS_LITTLEFS_FC_HEAP_SIZE - 0) <= 0
59 BUILD_ASSERT((CONFIG_FS_LITTLEFS_HEAP_PER_ALLOC_OVERHEAD_SIZE % 8) == 0);
60 /* Auto-generate heap size from cache size and number of files */
61 #undef CONFIG_FS_LITTLEFS_FC_HEAP_SIZE
62 #define CONFIG_FS_LITTLEFS_FC_HEAP_SIZE						\
63 	((CONFIG_FS_LITTLEFS_CACHE_SIZE + FC_HEAP_PER_ALLOC_OVERHEAD) *		\
64 	CONFIG_FS_LITTLEFS_NUM_FILES)
65 #endif /* CONFIG_FS_LITTLEFS_FC_HEAP_SIZE */
66 
67 static K_HEAP_DEFINE(file_cache_heap, CONFIG_FS_LITTLEFS_FC_HEAP_SIZE);
68 
littlefs_on_blkdev(int flags)69 static inline bool littlefs_on_blkdev(int flags)
70 {
71 	return (flags & FS_MOUNT_FLAG_USE_DISK_ACCESS) ? true : false;
72 }
73 
fc_allocate(size_t size)74 static inline void *fc_allocate(size_t size)
75 {
76 	void *ret = NULL;
77 
78 	ret = k_heap_alloc(&file_cache_heap, size, K_NO_WAIT);
79 
80 	return ret;
81 }
82 
fc_release(void * buf)83 static inline void fc_release(void *buf)
84 {
85 	k_heap_free(&file_cache_heap, buf);
86 }
87 
fs_lock(struct fs_littlefs * fs)88 static inline void fs_lock(struct fs_littlefs *fs)
89 {
90 	k_mutex_lock(&fs->mutex, K_FOREVER);
91 }
92 
fs_unlock(struct fs_littlefs * fs)93 static inline void fs_unlock(struct fs_littlefs *fs)
94 {
95 	k_mutex_unlock(&fs->mutex);
96 }
97 
lfs_to_errno(int error)98 static int lfs_to_errno(int error)
99 {
100 	if (error >= 0) {
101 		return error;
102 	}
103 
104 	switch (error) {
105 	default:
106 	case LFS_ERR_IO:        /* Error during device operation */
107 		return -EIO;
108 	case LFS_ERR_CORRUPT:   /* Corrupted */
109 		return -EFAULT;
110 	case LFS_ERR_NOENT:     /* No directory entry */
111 		return -ENOENT;
112 	case LFS_ERR_EXIST:     /* Entry already exists */
113 		return -EEXIST;
114 	case LFS_ERR_NOTDIR:    /* Entry is not a dir */
115 		return -ENOTDIR;
116 	case LFS_ERR_ISDIR:     /* Entry is a dir */
117 		return -EISDIR;
118 	case LFS_ERR_NOTEMPTY:  /* Dir is not empty */
119 		return -ENOTEMPTY;
120 	case LFS_ERR_BADF:      /* Bad file number */
121 		return -EBADF;
122 	case LFS_ERR_FBIG:      /* File too large */
123 		return -EFBIG;
124 	case LFS_ERR_INVAL:     /* Invalid parameter */
125 		return -EINVAL;
126 	case LFS_ERR_NOSPC:     /* No space left on device */
127 		return -ENOSPC;
128 	case LFS_ERR_NOMEM:     /* No more memory available */
129 		return -ENOMEM;
130 	}
131 }
132 
errno_to_lfs(int error)133 static int errno_to_lfs(int error)
134 {
135 	if (error >= 0) {
136 		return LFS_ERR_OK;
137 	}
138 
139 	switch (error) {
140 	default:
141 	case -EIO:              /* Error during device operation */
142 		return LFS_ERR_IO;
143 	case -EFAULT:		/* Corrupted */
144 		return LFS_ERR_CORRUPT;
145 	case -ENOENT:           /* No directory entry */
146 		return LFS_ERR_NOENT;
147 	case -EEXIST:           /* Entry already exists */
148 		return LFS_ERR_EXIST;
149 	case -ENOTDIR:          /* Entry is not a dir */
150 		return LFS_ERR_NOTDIR;
151 	case -EISDIR:           /* Entry is a dir */
152 		return LFS_ERR_ISDIR;
153 	case -ENOTEMPTY:        /* Dir is not empty */
154 		return LFS_ERR_NOTEMPTY;
155 	case -EBADF:            /* Bad file number */
156 		return LFS_ERR_BADF;
157 	case -EFBIG:            /* File too large */
158 		return LFS_ERR_FBIG;
159 	case -EINVAL:           /* Invalid parameter */
160 		return LFS_ERR_INVAL;
161 	case -ENOSPC:           /* No space left on device */
162 		return LFS_ERR_NOSPC;
163 	case -ENOMEM:           /* No more memory available */
164 		return LFS_ERR_NOMEM;
165 	}
166 }
167 
168 
169 #ifdef CONFIG_FS_LITTLEFS_FMP_DEV
170 
lfs_api_read(const struct lfs_config * c,lfs_block_t block,lfs_off_t off,void * buffer,lfs_size_t size)171 static int lfs_api_read(const struct lfs_config *c, lfs_block_t block,
172 			lfs_off_t off, void *buffer, lfs_size_t size)
173 {
174 	const struct flash_area *fa = c->context;
175 	size_t offset = block * c->block_size + off;
176 
177 	int rc = flash_area_read(fa, offset, buffer, size);
178 
179 	return errno_to_lfs(rc);
180 }
181 
lfs_api_prog(const struct lfs_config * c,lfs_block_t block,lfs_off_t off,const void * buffer,lfs_size_t size)182 static int lfs_api_prog(const struct lfs_config *c, lfs_block_t block,
183 			lfs_off_t off, const void *buffer, lfs_size_t size)
184 {
185 	const struct flash_area *fa = c->context;
186 	size_t offset = block * c->block_size + off;
187 
188 	int rc = flash_area_write(fa, offset, buffer, size);
189 
190 	return errno_to_lfs(rc);
191 }
192 
lfs_api_erase(const struct lfs_config * c,lfs_block_t block)193 static int lfs_api_erase(const struct lfs_config *c, lfs_block_t block)
194 {
195 	const struct flash_area *fa = c->context;
196 	size_t offset = block * c->block_size;
197 
198 	int rc = flash_area_erase(fa, offset, c->block_size);
199 
200 	return errno_to_lfs(rc);
201 }
202 #endif /* CONFIG_FS_LITTLEFS_FMP_DEV */
203 
204 #ifdef CONFIG_FS_LITTLEFS_BLK_DEV
lfs_api_read_blk(const struct lfs_config * c,lfs_block_t block,lfs_off_t off,void * buffer,lfs_size_t size)205 static int lfs_api_read_blk(const struct lfs_config *c, lfs_block_t block,
206 			    lfs_off_t off, void *buffer, lfs_size_t size)
207 {
208 	const char *disk = c->context;
209 	int rc = disk_access_read(disk, buffer, block,
210 				  size / c->block_size);
211 
212 	return errno_to_lfs(rc);
213 }
214 
lfs_api_prog_blk(const struct lfs_config * c,lfs_block_t block,lfs_off_t off,const void * buffer,lfs_size_t size)215 static int lfs_api_prog_blk(const struct lfs_config *c, lfs_block_t block,
216 			    lfs_off_t off, const void *buffer, lfs_size_t size)
217 {
218 	const char *disk = c->context;
219 	int rc = disk_access_write(disk, buffer, block, size / c->block_size);
220 
221 	return errno_to_lfs(rc);
222 }
223 
lfs_api_sync_blk(const struct lfs_config * c)224 static int lfs_api_sync_blk(const struct lfs_config *c)
225 {
226 	const char *disk = c->context;
227 	int rc = disk_access_ioctl(disk, DISK_IOCTL_CTRL_SYNC, NULL);
228 
229 	return errno_to_lfs(rc);
230 }
231 #else
lfs_api_read_blk(const struct lfs_config * c,lfs_block_t block,lfs_off_t off,void * buffer,lfs_size_t size)232 static int lfs_api_read_blk(const struct lfs_config *c, lfs_block_t block,
233 			    lfs_off_t off, void *buffer, lfs_size_t size)
234 {
235 	return 0;
236 }
237 
lfs_api_prog_blk(const struct lfs_config * c,lfs_block_t block,lfs_off_t off,const void * buffer,lfs_size_t size)238 static int lfs_api_prog_blk(const struct lfs_config *c, lfs_block_t block,
239 			    lfs_off_t off, const void *buffer, lfs_size_t size)
240 {
241 	return 0;
242 }
243 
lfs_api_sync_blk(const struct lfs_config * c)244 static int lfs_api_sync_blk(const struct lfs_config *c)
245 {
246 	return 0;
247 }
248 #endif /* CONFIG_FS_LITTLEFS_BLK_DEV */
lfs_api_erase_blk(const struct lfs_config * c,lfs_block_t block)249 static int lfs_api_erase_blk(const struct lfs_config *c, lfs_block_t block)
250 {
251 	return 0;
252 }
253 
lfs_api_sync(const struct lfs_config * c)254 static int lfs_api_sync(const struct lfs_config *c)
255 {
256 	return LFS_ERR_OK;
257 }
258 
release_file_data(struct fs_file_t * fp)259 static void release_file_data(struct fs_file_t *fp)
260 {
261 	struct lfs_file_data *fdp = fp->filep;
262 
263 	if (fdp->config.buffer) {
264 		fc_release(fdp->cache_block);
265 	}
266 
267 	k_mem_slab_free(&file_data_pool, &fp->filep);
268 	fp->filep = NULL;
269 }
270 
lfs_flags_from_zephyr(unsigned int zflags)271 static int lfs_flags_from_zephyr(unsigned int zflags)
272 {
273 	int flags = (zflags & FS_O_CREATE) ? LFS_O_CREAT : 0;
274 
275 	/* LFS_O_READONLY and LFS_O_WRONLY can be selected at the same time,
276 	 * this is not a mistake, together they create RDWR access.
277 	 */
278 	flags |= (zflags & FS_O_READ) ? LFS_O_RDONLY : 0;
279 	flags |= (zflags & FS_O_WRITE) ? LFS_O_WRONLY : 0;
280 
281 	flags |= (zflags & FS_O_APPEND) ? LFS_O_APPEND : 0;
282 
283 	return flags;
284 }
285 
littlefs_open(struct fs_file_t * fp,const char * path,fs_mode_t zflags)286 static int littlefs_open(struct fs_file_t *fp, const char *path,
287 			 fs_mode_t zflags)
288 {
289 	struct fs_littlefs *fs = fp->mp->fs_data;
290 	struct lfs *lfs = &fs->lfs;
291 	int flags = lfs_flags_from_zephyr(zflags);
292 	int ret = k_mem_slab_alloc(&file_data_pool, &fp->filep, K_NO_WAIT);
293 
294 	if (ret != 0) {
295 		return ret;
296 	}
297 
298 	struct lfs_file_data *fdp = fp->filep;
299 
300 	memset(fdp, 0, sizeof(*fdp));
301 
302 	fdp->cache_block = fc_allocate(lfs->cfg->cache_size);
303 	if (fdp->cache_block == NULL) {
304 		ret = -ENOMEM;
305 		goto out;
306 	}
307 
308 	fdp->config.buffer = fdp->cache_block;
309 	path = fs_impl_strip_prefix(path, fp->mp);
310 
311 	fs_lock(fs);
312 
313 	ret = lfs_file_opencfg(&fs->lfs, &fdp->file,
314 			       path, flags, &fdp->config);
315 
316 	fs_unlock(fs);
317 out:
318 	if (ret < 0) {
319 		release_file_data(fp);
320 	}
321 
322 	return lfs_to_errno(ret);
323 }
324 
littlefs_close(struct fs_file_t * fp)325 static int littlefs_close(struct fs_file_t *fp)
326 {
327 	struct fs_littlefs *fs = fp->mp->fs_data;
328 
329 	fs_lock(fs);
330 
331 	int ret = lfs_file_close(&fs->lfs, LFS_FILEP(fp));
332 
333 	fs_unlock(fs);
334 
335 	release_file_data(fp);
336 
337 	return lfs_to_errno(ret);
338 }
339 
littlefs_unlink(struct fs_mount_t * mountp,const char * path)340 static int littlefs_unlink(struct fs_mount_t *mountp, const char *path)
341 {
342 	struct fs_littlefs *fs = mountp->fs_data;
343 
344 	path = fs_impl_strip_prefix(path, mountp);
345 
346 	fs_lock(fs);
347 
348 	int ret = lfs_remove(&fs->lfs, path);
349 
350 	fs_unlock(fs);
351 	return lfs_to_errno(ret);
352 }
353 
littlefs_rename(struct fs_mount_t * mountp,const char * from,const char * to)354 static int littlefs_rename(struct fs_mount_t *mountp, const char *from,
355 			   const char *to)
356 {
357 	struct fs_littlefs *fs = mountp->fs_data;
358 
359 	from = fs_impl_strip_prefix(from, mountp);
360 	to = fs_impl_strip_prefix(to, mountp);
361 
362 	fs_lock(fs);
363 
364 	int ret = lfs_rename(&fs->lfs, from, to);
365 
366 	fs_unlock(fs);
367 	return lfs_to_errno(ret);
368 }
369 
littlefs_read(struct fs_file_t * fp,void * ptr,size_t len)370 static ssize_t littlefs_read(struct fs_file_t *fp, void *ptr, size_t len)
371 {
372 	struct fs_littlefs *fs = fp->mp->fs_data;
373 
374 	fs_lock(fs);
375 
376 	ssize_t ret = lfs_file_read(&fs->lfs, LFS_FILEP(fp), ptr, len);
377 
378 	fs_unlock(fs);
379 	return lfs_to_errno(ret);
380 }
381 
littlefs_write(struct fs_file_t * fp,const void * ptr,size_t len)382 static ssize_t littlefs_write(struct fs_file_t *fp, const void *ptr, size_t len)
383 {
384 	struct fs_littlefs *fs = fp->mp->fs_data;
385 
386 	fs_lock(fs);
387 
388 	ssize_t ret = lfs_file_write(&fs->lfs, LFS_FILEP(fp), ptr, len);
389 
390 	fs_unlock(fs);
391 	return lfs_to_errno(ret);
392 }
393 
394 BUILD_ASSERT((FS_SEEK_SET == LFS_SEEK_SET)
395 	     && (FS_SEEK_CUR == LFS_SEEK_CUR)
396 	     && (FS_SEEK_END == LFS_SEEK_END));
397 
littlefs_seek(struct fs_file_t * fp,off_t off,int whence)398 static int littlefs_seek(struct fs_file_t *fp, off_t off, int whence)
399 {
400 	struct fs_littlefs *fs = fp->mp->fs_data;
401 
402 	fs_lock(fs);
403 
404 	off_t ret = lfs_file_seek(&fs->lfs, LFS_FILEP(fp), off, whence);
405 
406 	fs_unlock(fs);
407 
408 	if (ret >= 0) {
409 		ret = 0;
410 	}
411 
412 	return lfs_to_errno(ret);
413 }
414 
littlefs_tell(struct fs_file_t * fp)415 static off_t littlefs_tell(struct fs_file_t *fp)
416 {
417 	struct fs_littlefs *fs = fp->mp->fs_data;
418 
419 	fs_lock(fs);
420 
421 	off_t ret = lfs_file_tell(&fs->lfs, LFS_FILEP(fp));
422 
423 	fs_unlock(fs);
424 	return ret;
425 }
426 
littlefs_truncate(struct fs_file_t * fp,off_t length)427 static int littlefs_truncate(struct fs_file_t *fp, off_t length)
428 {
429 	struct fs_littlefs *fs = fp->mp->fs_data;
430 
431 	fs_lock(fs);
432 
433 	int ret = lfs_file_truncate(&fs->lfs, LFS_FILEP(fp), length);
434 
435 	fs_unlock(fs);
436 	return lfs_to_errno(ret);
437 }
438 
littlefs_sync(struct fs_file_t * fp)439 static int littlefs_sync(struct fs_file_t *fp)
440 {
441 	struct fs_littlefs *fs = fp->mp->fs_data;
442 
443 	fs_lock(fs);
444 
445 	int ret = lfs_file_sync(&fs->lfs, LFS_FILEP(fp));
446 
447 	fs_unlock(fs);
448 	return lfs_to_errno(ret);
449 }
450 
littlefs_mkdir(struct fs_mount_t * mountp,const char * path)451 static int littlefs_mkdir(struct fs_mount_t *mountp, const char *path)
452 {
453 	struct fs_littlefs *fs = mountp->fs_data;
454 
455 	path = fs_impl_strip_prefix(path, mountp);
456 	fs_lock(fs);
457 
458 	int ret = lfs_mkdir(&fs->lfs, path);
459 
460 	fs_unlock(fs);
461 	return lfs_to_errno(ret);
462 }
463 
littlefs_opendir(struct fs_dir_t * dp,const char * path)464 static int littlefs_opendir(struct fs_dir_t *dp, const char *path)
465 {
466 	struct fs_littlefs *fs = dp->mp->fs_data;
467 
468 	if (k_mem_slab_alloc(&lfs_dir_pool, &dp->dirp, K_NO_WAIT) != 0) {
469 		return -ENOMEM;
470 	}
471 
472 	memset(dp->dirp, 0, sizeof(struct lfs_dir));
473 
474 	path = fs_impl_strip_prefix(path, dp->mp);
475 
476 	fs_lock(fs);
477 
478 	int ret = lfs_dir_open(&fs->lfs, dp->dirp, path);
479 
480 	fs_unlock(fs);
481 
482 	if (ret < 0) {
483 		k_mem_slab_free(&lfs_dir_pool, &dp->dirp);
484 	}
485 
486 	return lfs_to_errno(ret);
487 }
488 
info_to_dirent(const struct lfs_info * info,struct fs_dirent * entry)489 static void info_to_dirent(const struct lfs_info *info, struct fs_dirent *entry)
490 {
491 	entry->type = ((info->type == LFS_TYPE_DIR) ?
492 		       FS_DIR_ENTRY_DIR : FS_DIR_ENTRY_FILE);
493 	entry->size = info->size;
494 	strncpy(entry->name, info->name, sizeof(entry->name));
495 	entry->name[sizeof(entry->name) - 1] = '\0';
496 }
497 
littlefs_readdir(struct fs_dir_t * dp,struct fs_dirent * entry)498 static int littlefs_readdir(struct fs_dir_t *dp, struct fs_dirent *entry)
499 {
500 	struct fs_littlefs *fs = dp->mp->fs_data;
501 
502 	fs_lock(fs);
503 
504 	struct lfs_info info;
505 	int ret = lfs_dir_read(&fs->lfs, dp->dirp, &info);
506 
507 	fs_unlock(fs);
508 
509 	if (ret > 0) {
510 		info_to_dirent(&info, entry);
511 		ret = 0;
512 	} else if (ret == 0) {
513 		entry->name[0] = 0;
514 	}
515 
516 	return lfs_to_errno(ret);
517 }
518 
littlefs_closedir(struct fs_dir_t * dp)519 static int littlefs_closedir(struct fs_dir_t *dp)
520 {
521 	struct fs_littlefs *fs = dp->mp->fs_data;
522 
523 	fs_lock(fs);
524 
525 	int ret = lfs_dir_close(&fs->lfs, dp->dirp);
526 
527 	fs_unlock(fs);
528 
529 	k_mem_slab_free(&lfs_dir_pool, &dp->dirp);
530 
531 	return lfs_to_errno(ret);
532 }
533 
littlefs_stat(struct fs_mount_t * mountp,const char * path,struct fs_dirent * entry)534 static int littlefs_stat(struct fs_mount_t *mountp,
535 			 const char *path, struct fs_dirent *entry)
536 {
537 	struct fs_littlefs *fs = mountp->fs_data;
538 
539 	path = fs_impl_strip_prefix(path, mountp);
540 
541 	fs_lock(fs);
542 
543 	struct lfs_info info;
544 	int ret = lfs_stat(&fs->lfs, path, &info);
545 
546 	fs_unlock(fs);
547 
548 	if (ret >= 0) {
549 		info_to_dirent(&info, entry);
550 		ret = 0;
551 	}
552 
553 	return lfs_to_errno(ret);
554 }
555 
littlefs_statvfs(struct fs_mount_t * mountp,const char * path,struct fs_statvfs * stat)556 static int littlefs_statvfs(struct fs_mount_t *mountp,
557 			    const char *path, struct fs_statvfs *stat)
558 {
559 	struct fs_littlefs *fs = mountp->fs_data;
560 	struct lfs *lfs = &fs->lfs;
561 
562 	stat->f_bsize = lfs->cfg->prog_size;
563 	stat->f_frsize = lfs->cfg->block_size;
564 	stat->f_blocks = lfs->cfg->block_count;
565 
566 	path = fs_impl_strip_prefix(path, mountp);
567 
568 	fs_lock(fs);
569 
570 	ssize_t ret = lfs_fs_size(lfs);
571 
572 	fs_unlock(fs);
573 
574 	if (ret >= 0) {
575 		stat->f_bfree = stat->f_blocks - ret;
576 		ret = 0;
577 	}
578 
579 	return lfs_to_errno(ret);
580 }
581 
582 #ifdef CONFIG_FS_LITTLEFS_FMP_DEV
583 
584 /* Return maximum page size in a flash area.  There's no flash_area
585  * API to implement this, so we have to make one here.
586  */
587 struct get_page_ctx {
588 	const struct flash_area *area;
589 	lfs_size_t max_size;
590 };
591 
get_page_cb(const struct flash_pages_info * info,void * ctxp)592 static bool get_page_cb(const struct flash_pages_info *info, void *ctxp)
593 {
594 	struct get_page_ctx *ctx = ctxp;
595 
596 	size_t info_start = info->start_offset;
597 	size_t info_end = info_start + info->size - 1U;
598 	size_t area_start = ctx->area->fa_off;
599 	size_t area_end = area_start + ctx->area->fa_size - 1U;
600 
601 	/* Ignore pages outside the area */
602 	if (info_end < area_start) {
603 		return true;
604 	}
605 	if (info_start > area_end) {
606 		return false;
607 	}
608 
609 	if (info->size > ctx->max_size) {
610 		ctx->max_size = info->size;
611 	}
612 
613 	return true;
614 }
615 
616 /* Iterate over all page groups in the flash area and return the
617  * largest page size we see.  This works as long as the partition is
618  * aligned so that erasing with this size is supported throughout the
619  * partition.
620  */
get_block_size(const struct flash_area * fa)621 static lfs_size_t get_block_size(const struct flash_area *fa)
622 {
623 	struct get_page_ctx ctx = {
624 		.area = fa,
625 		.max_size = 0,
626 	};
627 	const struct device *dev = flash_area_get_device(fa);
628 
629 	flash_page_foreach(dev, get_page_cb, &ctx);
630 
631 	return ctx.max_size;
632 }
633 
littlefs_flash_init(struct fs_littlefs * fs,uintptr_t dev_id)634 static int littlefs_flash_init(struct fs_littlefs *fs, uintptr_t dev_id)
635 {
636 	unsigned int area_id = (uintptr_t)dev_id;
637 	const struct flash_area **fap = (const struct flash_area **)&fs->backend;
638 	const struct device *dev;
639 	int ret;
640 
641 	/* Open flash area */
642 	ret = flash_area_open(area_id, fap);
643 	if ((ret < 0) || (*fap == NULL)) {
644 		LOG_ERR("can't open flash area %d", area_id);
645 		return -ENODEV;
646 	}
647 
648 	LOG_DBG("FS area %u at 0x%x for %u bytes", area_id,
649 		(uint32_t)(*fap)->fa_off, (uint32_t)(*fap)->fa_size);
650 
651 	dev = flash_area_get_device(*fap);
652 	if (dev == NULL) {
653 		LOG_ERR("can't get flash device: %s",
654 			(*fap)->fa_dev->name);
655 		return -ENODEV;
656 	}
657 
658 	fs->backend = (void *) *fap;
659 	return 0;
660 }
661 #endif /* CONFIG_FS_LITTLEFS_FMP_DEV */
662 
littlefs_init_backend(struct fs_littlefs * fs,uintptr_t dev_id,int flags)663 static int littlefs_init_backend(struct fs_littlefs *fs, uintptr_t dev_id, int flags)
664 {
665 	int ret = 0;
666 
667 	if (!(IS_ENABLED(CONFIG_FS_LITTLEFS_FMP_DEV) && !littlefs_on_blkdev(flags)) &&
668 	    !(IS_ENABLED(CONFIG_FS_LITTLEFS_BLK_DEV) && littlefs_on_blkdev(flags))) {
669 		LOG_ERR("Can't init littlefs backend, review configs and flags 0x%08x", flags);
670 		return -ENOTSUP;
671 	}
672 
673 #ifdef CONFIG_FS_LITTLEFS_BLK_DEV
674 	if (littlefs_on_blkdev(flags)) {
675 		fs->backend = (void *) dev_id;
676 		ret = disk_access_init((char *) fs->backend);
677 		if (ret < 0) {
678 			LOG_ERR("Storage init ERROR!");
679 			return ret;
680 		}
681 	}
682 #endif /* CONFIG_FS_LITTLEFS_BLK_DEV */
683 #ifdef CONFIG_FS_LITTLEFS_FMP_DEV
684 	if (!littlefs_on_blkdev(flags)) {
685 		ret = littlefs_flash_init(fs, dev_id);
686 		if (ret < 0) {
687 			return ret;
688 		}
689 	}
690 #endif /* CONFIG_FS_LITTLEFS_FMP_DEV */
691 	return 0;
692 }
693 
littlefs_init_cfg(struct fs_littlefs * fs,int flags)694 static int littlefs_init_cfg(struct fs_littlefs *fs, int flags)
695 {
696 	BUILD_ASSERT(CONFIG_FS_LITTLEFS_READ_SIZE > 0);
697 	BUILD_ASSERT(CONFIG_FS_LITTLEFS_PROG_SIZE > 0);
698 	BUILD_ASSERT(CONFIG_FS_LITTLEFS_CACHE_SIZE > 0);
699 	BUILD_ASSERT(CONFIG_FS_LITTLEFS_LOOKAHEAD_SIZE > 0);
700 	BUILD_ASSERT((CONFIG_FS_LITTLEFS_LOOKAHEAD_SIZE % 8) == 0);
701 	BUILD_ASSERT((CONFIG_FS_LITTLEFS_CACHE_SIZE
702 		      % CONFIG_FS_LITTLEFS_READ_SIZE) == 0);
703 	BUILD_ASSERT((CONFIG_FS_LITTLEFS_CACHE_SIZE
704 		      % CONFIG_FS_LITTLEFS_PROG_SIZE) == 0);
705 
706 	struct lfs_config *lcp = &fs->cfg;
707 
708 	lfs_size_t read_size = lcp->read_size;
709 
710 	if (read_size == 0) {
711 		read_size = CONFIG_FS_LITTLEFS_READ_SIZE;
712 	}
713 
714 	lfs_size_t prog_size = lcp->prog_size;
715 
716 	if (prog_size == 0) {
717 		prog_size = CONFIG_FS_LITTLEFS_PROG_SIZE;
718 	}
719 
720 	/* Yes, you can override block size. */
721 	lfs_size_t block_size = lcp->block_size;
722 
723 	if (!(IS_ENABLED(CONFIG_FS_LITTLEFS_FMP_DEV) && !littlefs_on_blkdev(flags)) &&
724 	    !(IS_ENABLED(CONFIG_FS_LITTLEFS_BLK_DEV) && littlefs_on_blkdev(flags))) {
725 		LOG_ERR("Can't init littlefs config, review configs and flags 0x%08x", flags);
726 		return -ENOTSUP;
727 	}
728 
729 	if (block_size == 0) {
730 #ifdef CONFIG_FS_LITTLEFS_BLK_DEV
731 		if (littlefs_on_blkdev(flags)) {
732 			int ret = disk_access_ioctl((char *) fs->backend,
733 						DISK_IOCTL_GET_SECTOR_SIZE,
734 						&block_size);
735 			if (ret < 0) {
736 				LOG_ERR("Unable to get sector size");
737 				return ret;
738 			}
739 		}
740 #endif /* CONFIG_FS_LITTLEFS_BLK_DEV */
741 
742 #ifdef CONFIG_FS_LITTLEFS_FMP_DEV
743 		if (!littlefs_on_blkdev(flags)) {
744 			block_size = get_block_size((struct flash_area *)fs->backend);
745 		}
746 #endif /* CONFIG_FS_LITTLEFS_FMP_DEV */
747 	}
748 
749 	if (block_size == 0) {
750 		__ASSERT_NO_MSG(block_size != 0);
751 		return -EINVAL;
752 	}
753 
754 	int32_t block_cycles = lcp->block_cycles;
755 
756 	if (block_cycles == 0) {
757 		block_cycles = CONFIG_FS_LITTLEFS_BLOCK_CYCLES;
758 	}
759 	if (block_cycles <= 0) {
760 		/* Disable leveling (littlefs v2.1+ semantics) */
761 		block_cycles = -1;
762 	}
763 
764 	lfs_size_t cache_size = lcp->cache_size;
765 
766 	if (cache_size == 0) {
767 		cache_size = CONFIG_FS_LITTLEFS_CACHE_SIZE;
768 	}
769 
770 	lfs_size_t lookahead_size = lcp->lookahead_size;
771 
772 	if (lookahead_size == 0) {
773 		lookahead_size = CONFIG_FS_LITTLEFS_LOOKAHEAD_SIZE;
774 	}
775 
776 	/* No, you don't get to override this. */
777 	lfs_size_t block_count = 0;
778 
779 #ifdef CONFIG_FS_LITTLEFS_BLK_DEV
780 	if (littlefs_on_blkdev(flags)) {
781 		int ret = disk_access_ioctl((char *) fs->backend,
782 					DISK_IOCTL_GET_SECTOR_COUNT,
783 					&block_count);
784 		if (ret < 0) {
785 			LOG_ERR("Unable to get sector count!");
786 			return -EINVAL;
787 		}
788 		LOG_INF("FS at %s: is %u 0x%x-byte blocks with %u cycle",
789 			(char *) fs->backend, block_count, block_size,
790 			block_cycles);
791 	}
792 #endif /* CONFIG_FS_LITTLEFS_BLK_DEV */
793 
794 #ifdef CONFIG_FS_LITTLEFS_FMP_DEV
795 	if (!littlefs_on_blkdev(flags)) {
796 		block_count = ((struct flash_area *)fs->backend)->fa_size
797 			/ block_size;
798 		const struct device *dev =
799 			flash_area_get_device((struct flash_area *)fs->backend);
800 		LOG_INF("FS at %s:0x%x is %u 0x%x-byte blocks with %u cycle",
801 			dev->name,
802 			(uint32_t)((struct flash_area *)fs->backend)->fa_off,
803 			block_count, block_size, block_cycles);
804 		LOG_INF("sizes: rd %u ; pr %u ; ca %u ; la %u",
805 			read_size, prog_size, cache_size, lookahead_size);
806 	}
807 #endif /* CONFIG_FS_LITTLEFS_FMP_DEV */
808 
809 	__ASSERT_NO_MSG(prog_size != 0);
810 	__ASSERT_NO_MSG(read_size != 0);
811 	__ASSERT_NO_MSG(cache_size != 0);
812 	__ASSERT_NO_MSG(block_size != 0);
813 	__ASSERT_NO_MSG(block_count != 0);
814 
815 	__ASSERT((block_size % prog_size) == 0,
816 		 "erase size must be multiple of write size");
817 	__ASSERT((block_size % cache_size) == 0,
818 		 "cache size incompatible with block size");
819 
820 	lcp->context = fs->backend;
821 	/* Set the validated/defaulted values. */
822 	if (littlefs_on_blkdev(flags)) {
823 		lcp->read = lfs_api_read_blk;
824 		lcp->prog = lfs_api_prog_blk;
825 		lcp->erase = lfs_api_erase_blk;
826 
827 		lcp->read_size = block_size;
828 		lcp->prog_size = block_size;
829 		lcp->cache_size = block_size;
830 		lcp->lookahead_size = block_size * 4;
831 		lcp->sync = lfs_api_sync_blk;
832 
833 		LOG_INF("sizes: rd %u ; pr %u ; ca %u ; la %u",
834 			lcp->read_size, lcp->prog_size, lcp->cache_size,
835 			lcp->lookahead_size);
836 	} else {
837 		__ASSERT((((struct flash_area *)fs->backend)->fa_size %
838 			  block_size) == 0,
839 			 "partition size must be multiple of block size");
840 #ifdef CONFIG_FS_LITTLEFS_FMP_DEV
841 		lcp->read = lfs_api_read;
842 		lcp->prog = lfs_api_prog;
843 		lcp->erase = lfs_api_erase;
844 #endif
845 
846 		lcp->read_size = read_size;
847 		lcp->prog_size = prog_size;
848 		lcp->cache_size = cache_size;
849 		lcp->lookahead_size = lookahead_size;
850 		lcp->sync = lfs_api_sync;
851 	}
852 
853 	lcp->block_size = block_size;
854 	lcp->block_count = block_count;
855 	lcp->block_cycles = block_cycles;
856 	return 0;
857 }
858 
littlefs_init_fs(struct fs_littlefs * fs,int dev_id,int flags)859 static int littlefs_init_fs(struct fs_littlefs *fs, int dev_id, int flags)
860 {
861 	int ret = 0;
862 
863 	LOG_INF("LittleFS version %u.%u, disk version %u.%u",
864 		LFS_VERSION_MAJOR, LFS_VERSION_MINOR,
865 		LFS_DISK_VERSION_MAJOR, LFS_DISK_VERSION_MINOR);
866 
867 	if (fs->backend) {
868 		return -EBUSY;
869 	}
870 
871 	ret = littlefs_init_backend(fs, dev_id, flags);
872 	if (ret < 0) {
873 		return ret;
874 	}
875 
876 	ret = littlefs_init_cfg(fs, flags);
877 	if (ret < 0) {
878 		return ret;
879 	}
880 	return 0;
881 }
882 
littlefs_mount(struct fs_mount_t * mountp)883 static int littlefs_mount(struct fs_mount_t *mountp)
884 {
885 	int ret = 0;
886 	struct fs_littlefs *fs = mountp->fs_data;
887 
888 	/* Create and take mutex. */
889 	k_mutex_init(&fs->mutex);
890 	fs_lock(fs);
891 
892 	ret = littlefs_init_fs(fs, (uintptr_t)mountp->storage_dev, mountp->flags);
893 	if (ret < 0) {
894 		goto out;
895 	}
896 
897 	/* Mount it, formatting if needed. */
898 	ret = lfs_mount(&fs->lfs, &fs->cfg);
899 	if (ret < 0 &&
900 	    (mountp->flags & FS_MOUNT_FLAG_NO_FORMAT) == 0) {
901 		if ((mountp->flags & FS_MOUNT_FLAG_READ_ONLY) == 0) {
902 			LOG_WRN("can't mount (LFS %d); formatting", ret);
903 			ret = lfs_format(&fs->lfs, &fs->cfg);
904 			if (ret < 0) {
905 				LOG_ERR("format failed (LFS %d)", ret);
906 				ret = lfs_to_errno(ret);
907 				goto out;
908 			}
909 		} else {
910 			LOG_ERR("can not format read-only system");
911 			ret = -EROFS;
912 			goto out;
913 		}
914 
915 		ret = lfs_mount(&fs->lfs, &fs->cfg);
916 		if (ret < 0) {
917 			LOG_ERR("remount after format failed (LFS %d)", ret);
918 			ret = lfs_to_errno(ret);
919 			goto out;
920 		}
921 	} else {
922 		ret = lfs_to_errno(ret);
923 		goto out;
924 	}
925 
926 	LOG_INF("%s mounted", mountp->mnt_point);
927 
928 out:
929 	if (ret < 0) {
930 		fs->backend = NULL;
931 	}
932 
933 	fs_unlock(fs);
934 
935 	return ret;
936 }
937 
938 #if defined(CONFIG_FILE_SYSTEM_MKFS)
939 
940 FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(fs_cfg);
941 
littlefs_mkfs(uintptr_t dev_id,void * cfg,int flags)942 static int littlefs_mkfs(uintptr_t dev_id, void *cfg, int flags)
943 {
944 	int ret = 0;
945 	struct fs_littlefs *fs = &fs_cfg;
946 
947 	if (cfg != NULL) {
948 		fs = (struct fs_littlefs *)cfg;
949 	}
950 
951 	fs->backend = NULL;
952 
953 	/* Create and take mutex. */
954 	k_mutex_init(&fs->mutex);
955 	fs_lock(fs);
956 
957 	ret = littlefs_init_fs(fs, dev_id, flags);
958 	if (ret < 0) {
959 		goto out;
960 	}
961 
962 	ret = lfs_format(&fs->lfs, &fs->cfg);
963 	if (ret < 0) {
964 		LOG_ERR("format failed (LFS %d)", ret);
965 		ret = lfs_to_errno(ret);
966 		goto out;
967 	}
968 out:
969 	fs->backend = NULL;
970 	fs_unlock(fs);
971 	return ret;
972 }
973 
974 #endif /* CONFIG_FILE_SYSTEM_MKFS */
975 
littlefs_unmount(struct fs_mount_t * mountp)976 static int littlefs_unmount(struct fs_mount_t *mountp)
977 {
978 	struct fs_littlefs *fs = mountp->fs_data;
979 
980 	fs_lock(fs);
981 
982 	lfs_unmount(&fs->lfs);
983 
984 #ifdef CONFIG_FS_LITTLEFS_FMP_DEV
985 	if (!littlefs_on_blkdev(mountp->flags)) {
986 		flash_area_close(fs->backend);
987 	}
988 #endif /* CONFIG_FS_LITTLEFS_FMP_DEV */
989 
990 	fs->backend = NULL;
991 	fs_unlock(fs);
992 
993 	LOG_INF("%s unmounted", mountp->mnt_point);
994 
995 	return 0;
996 }
997 
998 /* File system interface */
999 static const struct fs_file_system_t littlefs_fs = {
1000 	.open = littlefs_open,
1001 	.close = littlefs_close,
1002 	.read = littlefs_read,
1003 	.write = littlefs_write,
1004 	.lseek = littlefs_seek,
1005 	.tell = littlefs_tell,
1006 	.truncate = littlefs_truncate,
1007 	.sync = littlefs_sync,
1008 	.opendir = littlefs_opendir,
1009 	.readdir = littlefs_readdir,
1010 	.closedir = littlefs_closedir,
1011 	.mount = littlefs_mount,
1012 	.unmount = littlefs_unmount,
1013 	.unlink = littlefs_unlink,
1014 	.rename = littlefs_rename,
1015 	.mkdir = littlefs_mkdir,
1016 	.stat = littlefs_stat,
1017 	.statvfs = littlefs_statvfs,
1018 #if defined(CONFIG_FILE_SYSTEM_MKFS)
1019 	.mkfs = littlefs_mkfs,
1020 #endif
1021 };
1022 
1023 #define DT_DRV_COMPAT zephyr_fstab_littlefs
1024 #define FS_PARTITION(inst) DT_PHANDLE_BY_IDX(DT_DRV_INST(inst), partition, 0)
1025 
1026 #define DEFINE_FS(inst) \
1027 static uint8_t __aligned(4) \
1028 	read_buffer_##inst[DT_INST_PROP(inst, cache_size)]; \
1029 static uint8_t __aligned(4) \
1030 	prog_buffer_##inst[DT_INST_PROP(inst, cache_size)]; \
1031 static uint32_t lookahead_buffer_##inst[DT_INST_PROP(inst, lookahead_size) \
1032 					/ sizeof(uint32_t)]; \
1033 BUILD_ASSERT(DT_INST_PROP(inst, read_size) > 0); \
1034 BUILD_ASSERT(DT_INST_PROP(inst, prog_size) > 0); \
1035 BUILD_ASSERT(DT_INST_PROP(inst, cache_size) > 0); \
1036 BUILD_ASSERT(DT_INST_PROP(inst, lookahead_size) > 0); \
1037 BUILD_ASSERT((DT_INST_PROP(inst, lookahead_size) % 8) == 0); \
1038 BUILD_ASSERT((DT_INST_PROP(inst, cache_size) \
1039 	      % DT_INST_PROP(inst, read_size)) == 0); \
1040 BUILD_ASSERT((DT_INST_PROP(inst, cache_size) \
1041 	      % DT_INST_PROP(inst, prog_size)) == 0); \
1042 static struct fs_littlefs fs_data_##inst = { \
1043 	.cfg = { \
1044 		.read_size = DT_INST_PROP(inst, read_size), \
1045 		.prog_size = DT_INST_PROP(inst, prog_size), \
1046 		.cache_size = DT_INST_PROP(inst, cache_size), \
1047 		.lookahead_size = DT_INST_PROP(inst, lookahead_size), \
1048 		.read_buffer = read_buffer_##inst, \
1049 		.prog_buffer = prog_buffer_##inst, \
1050 		.lookahead_buffer = lookahead_buffer_##inst, \
1051 	}, \
1052 }; \
1053 struct fs_mount_t FS_FSTAB_ENTRY(DT_DRV_INST(inst)) = { \
1054 	.type = FS_LITTLEFS, \
1055 	.mnt_point = DT_INST_PROP(inst, mount_point), \
1056 	.fs_data = &fs_data_##inst, \
1057 	.storage_dev = (void *)DT_FIXED_PARTITION_ID(FS_PARTITION(inst)), \
1058 	.flags = FSTAB_ENTRY_DT_MOUNT_FLAGS(DT_DRV_INST(inst)), \
1059 };
1060 
DT_INST_FOREACH_STATUS_OKAY(DEFINE_FS)1061 DT_INST_FOREACH_STATUS_OKAY(DEFINE_FS)
1062 
1063 #define REFERENCE_MOUNT(inst) (&FS_FSTAB_ENTRY(DT_DRV_INST(inst))),
1064 
1065 static void mount_init(struct fs_mount_t *mp)
1066 {
1067 
1068 	LOG_INF("littlefs partition at %s", mp->mnt_point);
1069 	if ((mp->flags & FS_MOUNT_FLAG_AUTOMOUNT) != 0) {
1070 		int rc = fs_mount(mp);
1071 
1072 		if (rc < 0) {
1073 			LOG_ERR("Automount %s failed: %d",
1074 				mp->mnt_point, rc);
1075 		} else {
1076 			LOG_INF("Automount %s succeeded",
1077 				mp->mnt_point);
1078 		}
1079 	}
1080 }
1081 
littlefs_init(void)1082 static int littlefs_init(void)
1083 {
1084 	static struct fs_mount_t *partitions[] = {
1085 		DT_INST_FOREACH_STATUS_OKAY(REFERENCE_MOUNT)
1086 	};
1087 
1088 	int rc = fs_register(FS_LITTLEFS, &littlefs_fs);
1089 
1090 	if (rc == 0) {
1091 		struct fs_mount_t **mpi = partitions;
1092 
1093 		while (mpi < (partitions + ARRAY_SIZE(partitions))) {
1094 			mount_init(*mpi++);
1095 		}
1096 	}
1097 
1098 	return rc;
1099 }
1100 
1101 SYS_INIT(littlefs_init, POST_KERNEL, 99);
1102