/* * Copyright (c) 2022 Lukasz Majewski, DENX Software Engineering GmbH * Copyright (c) 2019 Peter Bigot Consulting, LLC * * SPDX-License-Identifier: Apache-2.0 */ /* Sample which uses the filesystem API with littlefs */ #include #include #include #include #include #include #include LOG_MODULE_REGISTER(main); /* Matches LFS_NAME_MAX */ #define MAX_PATH_LEN 255 #define TEST_FILE_SIZE 547 static uint8_t file_test_pattern[TEST_FILE_SIZE]; static int lsdir(const char *path) { int res; struct fs_dir_t dirp; static struct fs_dirent entry; fs_dir_t_init(&dirp); /* Verify fs_opendir() */ res = fs_opendir(&dirp, path); if (res) { LOG_ERR("Error opening dir %s [%d]\n", path, res); return res; } LOG_PRINTK("\nListing dir %s ...\n", path); for (;;) { /* Verify fs_readdir() */ res = fs_readdir(&dirp, &entry); /* entry.name[0] == 0 means end-of-dir */ if (res || entry.name[0] == 0) { if (res < 0) { LOG_ERR("Error reading dir [%d]\n", res); } break; } if (entry.type == FS_DIR_ENTRY_DIR) { LOG_PRINTK("[DIR ] %s\n", entry.name); } else { LOG_PRINTK("[FILE] %s (size = %zu)\n", entry.name, entry.size); } } /* Verify fs_closedir() */ fs_closedir(&dirp); return res; } static int littlefs_increase_infile_value(char *fname) { uint8_t boot_count = 0; struct fs_file_t file; int rc, ret; fs_file_t_init(&file); rc = fs_open(&file, fname, FS_O_CREATE | FS_O_RDWR); if (rc < 0) { LOG_ERR("FAIL: open %s: %d", fname, rc); return rc; } rc = fs_read(&file, &boot_count, sizeof(boot_count)); if (rc < 0) { LOG_ERR("FAIL: read %s: [rd:%d]", fname, rc); goto out; } LOG_PRINTK("%s read count:%u (bytes: %d)\n", fname, boot_count, rc); rc = fs_seek(&file, 0, FS_SEEK_SET); if (rc < 0) { LOG_ERR("FAIL: seek %s: %d", fname, rc); goto out; } boot_count += 1; rc = fs_write(&file, &boot_count, sizeof(boot_count)); if (rc < 0) { LOG_ERR("FAIL: write %s: %d", fname, rc); goto out; } LOG_PRINTK("%s write new boot count %u: [wr:%d]\n", fname, boot_count, rc); out: ret = fs_close(&file); if (ret < 0) { LOG_ERR("FAIL: close %s: %d", fname, ret); return ret; } return (rc < 0 ? rc : 0); } static void incr_pattern(uint8_t *p, uint16_t size, uint8_t inc) { uint8_t fill = 0x55; if (p[0] % 2 == 0) { fill = 0xAA; } for (int i = 0; i < (size - 1); i++) { if (i % 8 == 0) { p[i] += inc; } else { p[i] = fill; } } p[size - 1] += inc; } static void init_pattern(uint8_t *p, uint16_t size) { uint8_t v = 0x1; memset(p, 0x55, size); for (int i = 0; i < size; i += 8) { p[i] = v++; } p[size - 1] = 0xAA; } static void print_pattern(uint8_t *p, uint16_t size) { int i, j = size / 16, k; for (k = 0, i = 0; k < j; i += 16, k++) { LOG_PRINTK("%02x %02x %02x %02x %02x %02x %02x %02x ", p[i], p[i+1], p[i+2], p[i+3], p[i+4], p[i+5], p[i+6], p[i+7]); LOG_PRINTK("%02x %02x %02x %02x %02x %02x %02x %02x\n", p[i+8], p[i+9], p[i+10], p[i+11], p[i+12], p[i+13], p[i+14], p[i+15]); /* Mark 512B (sector) chunks of the test file */ if ((k + 1) % 32 == 0) { LOG_PRINTK("\n"); } } for (; i < size; i++) { LOG_PRINTK("%02x ", p[i]); } LOG_PRINTK("\n"); } static int littlefs_binary_file_adj(char *fname) { struct fs_dirent dirent; struct fs_file_t file; int rc, ret; /* * Uncomment below line to force re-creation of the test pattern * file on the littlefs FS. */ /* fs_unlink(fname); */ fs_file_t_init(&file); rc = fs_open(&file, fname, FS_O_CREATE | FS_O_RDWR); if (rc < 0) { LOG_ERR("FAIL: open %s: %d", fname, rc); return rc; } rc = fs_stat(fname, &dirent); if (rc < 0) { LOG_ERR("FAIL: stat %s: %d", fname, rc); goto out; } /* Check if the file exists - if not just write the pattern */ if (rc == 0 && dirent.type == FS_DIR_ENTRY_FILE && dirent.size == 0) { LOG_INF("Test file: %s not found, create one!", fname); init_pattern(file_test_pattern, sizeof(file_test_pattern)); } else { rc = fs_read(&file, file_test_pattern, sizeof(file_test_pattern)); if (rc < 0) { LOG_ERR("FAIL: read %s: [rd:%d]", fname, rc); goto out; } incr_pattern(file_test_pattern, sizeof(file_test_pattern), 0x1); } LOG_PRINTK("------ FILE: %s ------\n", fname); print_pattern(file_test_pattern, sizeof(file_test_pattern)); rc = fs_seek(&file, 0, FS_SEEK_SET); if (rc < 0) { LOG_ERR("FAIL: seek %s: %d", fname, rc); goto out; } rc = fs_write(&file, file_test_pattern, sizeof(file_test_pattern)); if (rc < 0) { LOG_ERR("FAIL: write %s: %d", fname, rc); } out: ret = fs_close(&file); if (ret < 0) { LOG_ERR("FAIL: close %s: %d", fname, ret); return ret; } return (rc < 0 ? rc : 0); } #ifdef CONFIG_APP_LITTLEFS_STORAGE_FLASH static int littlefs_flash_erase(unsigned int id) { const struct flash_area *pfa; int rc; rc = flash_area_open(id, &pfa); if (rc < 0) { LOG_ERR("FAIL: unable to find flash area %u: %d\n", id, rc); return rc; } LOG_PRINTK("Area %u at 0x%x on %s for %u bytes\n", id, (unsigned int)pfa->fa_off, pfa->fa_dev->name, (unsigned int)pfa->fa_size); /* Optional wipe flash contents */ if (IS_ENABLED(CONFIG_APP_WIPE_STORAGE)) { rc = flash_area_flatten(pfa, 0, pfa->fa_size); LOG_ERR("Erasing flash area ... %d", rc); } flash_area_close(pfa); return rc; } #define PARTITION_NODE DT_NODELABEL(lfs1) #if DT_NODE_EXISTS(PARTITION_NODE) FS_FSTAB_DECLARE_ENTRY(PARTITION_NODE); #else /* PARTITION_NODE */ FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage); static struct fs_mount_t lfs_storage_mnt = { .type = FS_LITTLEFS, .fs_data = &storage, .storage_dev = (void *)FIXED_PARTITION_ID(storage_partition), .mnt_point = "/lfs", }; #endif /* PARTITION_NODE */ struct fs_mount_t *mountpoint = #if DT_NODE_EXISTS(PARTITION_NODE) &FS_FSTAB_ENTRY(PARTITION_NODE) #else &lfs_storage_mnt #endif ; static int littlefs_mount(struct fs_mount_t *mp) { int rc; rc = littlefs_flash_erase((uintptr_t)mp->storage_dev); if (rc < 0) { return rc; } /* Do not mount if auto-mount has been enabled */ #if !DT_NODE_EXISTS(PARTITION_NODE) || \ !(FSTAB_ENTRY_DT_MOUNT_FLAGS(PARTITION_NODE) & FS_MOUNT_FLAG_AUTOMOUNT) rc = fs_mount(mp); if (rc < 0) { LOG_PRINTK("FAIL: mount id %" PRIuPTR " at %s: %d\n", (uintptr_t)mp->storage_dev, mp->mnt_point, rc); return rc; } LOG_PRINTK("%s mount: %d\n", mp->mnt_point, rc); #else LOG_PRINTK("%s automounted\n", mp->mnt_point); #endif return 0; } #endif /* CONFIG_APP_LITTLEFS_STORAGE_FLASH */ #ifdef CONFIG_APP_LITTLEFS_STORAGE_BLK_SDMMC #if defined(CONFIG_DISK_DRIVER_SDMMC) #define DISK_NAME "SD" #elif defined(CONFIG_DISK_DRIVER_MMC) #define DISK_NAME "SD2" #else #error "No disk device defined, is your board supported?" #endif struct fs_littlefs lfsfs; static struct fs_mount_t __mp = { .type = FS_LITTLEFS, .fs_data = &lfsfs, .flags = FS_MOUNT_FLAG_USE_DISK_ACCESS, }; struct fs_mount_t *mountpoint = &__mp; static int littlefs_mount(struct fs_mount_t *mp) { static const char *disk_mount_pt = "/"DISK_NAME":"; static const char *disk_pdrv = DISK_NAME; mp->storage_dev = (void *)disk_pdrv; mp->mnt_point = disk_mount_pt; return fs_mount(mp); } #endif /* CONFIG_APP_LITTLEFS_STORAGE_BLK_SDMMC */ int main(void) { char fname1[MAX_PATH_LEN]; char fname2[MAX_PATH_LEN]; struct fs_statvfs sbuf; int rc; LOG_PRINTK("Sample program to r/w files on littlefs\n"); rc = littlefs_mount(mountpoint); if (rc < 0) { return 0; } snprintf(fname1, sizeof(fname1), "%s/boot_count", mountpoint->mnt_point); snprintf(fname2, sizeof(fname2), "%s/pattern.bin", mountpoint->mnt_point); rc = fs_statvfs(mountpoint->mnt_point, &sbuf); if (rc < 0) { LOG_PRINTK("FAIL: statvfs: %d\n", rc); goto out; } LOG_PRINTK("%s: bsize = %lu ; frsize = %lu ;" " blocks = %lu ; bfree = %lu\n", mountpoint->mnt_point, sbuf.f_bsize, sbuf.f_frsize, sbuf.f_blocks, sbuf.f_bfree); rc = lsdir(mountpoint->mnt_point); if (rc < 0) { LOG_PRINTK("FAIL: lsdir %s: %d\n", mountpoint->mnt_point, rc); goto out; } rc = littlefs_increase_infile_value(fname1); if (rc) { goto out; } rc = littlefs_binary_file_adj(fname2); if (rc) { goto out; } out: rc = fs_unmount(mountpoint); LOG_PRINTK("%s unmount: %d\n", mountpoint->mnt_point, rc); return 0; }