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