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