samples/subsys/fs/littlefs: add a basic sample
Uses a littlefs file system to maintain a boot counter. Also tests some other functions. Signed-off-by: Peter A. Bigot <pab@pabigot.com>
This commit is contained in:
parent
fb73fcd4ba
commit
cfa64bde1c
9 changed files with 314 additions and 0 deletions
12
samples/subsys/fs/littlefs/CMakeLists.txt
Normal file
12
samples/subsys/fs/littlefs/CMakeLists.txt
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2019 Peter Bigot Consulting, LLC
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.13.1)
|
||||||
|
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
|
||||||
|
project(littlefs)
|
||||||
|
|
||||||
|
FILE(GLOB app_sources src/*.c)
|
||||||
|
target_sources(app PRIVATE ${app_sources})
|
11
samples/subsys/fs/littlefs/Kconfig
Normal file
11
samples/subsys/fs/littlefs/Kconfig
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Copyright (c) 2019 Peter Bigot Consulting, LLC
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
config APP_WIPE_STORAGE
|
||||||
|
bool "Option to clear the flash area before mounting"
|
||||||
|
help
|
||||||
|
Use this to force an existing file system to be created.
|
||||||
|
|
||||||
|
source "$ZEPHYR_BASE/Kconfig"
|
69
samples/subsys/fs/littlefs/README.rst
Normal file
69
samples/subsys/fs/littlefs/README.rst
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
.. _littlefs-sample:
|
||||||
|
|
||||||
|
littlefs File System Sample Application
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
Overview
|
||||||
|
********
|
||||||
|
|
||||||
|
This sample app demonstrates use of Zephyr's :ref:`file system API
|
||||||
|
<file_system>` over `littlefs`_, using a file system with a file that
|
||||||
|
counts the number of times the system has booted. Other information
|
||||||
|
about the file system is also displayed.
|
||||||
|
|
||||||
|
.. _littlefs:
|
||||||
|
https://github.com/ARMmbed/littlefs
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
************
|
||||||
|
|
||||||
|
The partition labeled "storage" will be used for the file system; see
|
||||||
|
:ref:`flash_partitions`. If that area does not already have a
|
||||||
|
compatible littlefs file system its contents will be replaced by an
|
||||||
|
empty file system. You will see diagnostics like this::
|
||||||
|
|
||||||
|
[00:00:00.010,192] <inf> littlefs: LittleFS version 2.0, disk version 2.0
|
||||||
|
[00:00:00.010,559] <err> littlefs: Corrupted dir pair at 0 1
|
||||||
|
[00:00:00.010,559] <wrn> littlefs: can't mount (LFS -84); formatting
|
||||||
|
|
||||||
|
The error and warning are normal for a new file system.
|
||||||
|
|
||||||
|
After the file system is mounted you'll also see::
|
||||||
|
|
||||||
|
[00:00:00.182,434] <inf> littlefs: filesystem mounted!
|
||||||
|
[00:00:00.867,034] <err> fs: failed get file or dir stat (-2)
|
||||||
|
|
||||||
|
This error is also normal for Zephyr not finding a file (the boot count,
|
||||||
|
in this case).
|
||||||
|
|
||||||
|
Building and Running
|
||||||
|
********************
|
||||||
|
|
||||||
|
This example should work on any board that provides a "storage"
|
||||||
|
partition. Two tested board targets are described below.
|
||||||
|
|
||||||
|
You can set ``CONFIG_APP_WIPE_STORAGE`` to force the file system to be
|
||||||
|
recreated.
|
||||||
|
|
||||||
|
NRF52840 Development Kit
|
||||||
|
========================
|
||||||
|
|
||||||
|
On this device the file system will be placed in the SOC flash.
|
||||||
|
|
||||||
|
.. zephyr-app-commands::
|
||||||
|
:zephyr-app: samples/subsys/fs/littlefs
|
||||||
|
:board: nrf52840_pca10056
|
||||||
|
:goals: build
|
||||||
|
:compact:
|
||||||
|
|
||||||
|
Particle Xenon
|
||||||
|
==============
|
||||||
|
|
||||||
|
On this device the file system will be placed on the external SPI NOR
|
||||||
|
flash memory.
|
||||||
|
|
||||||
|
.. zephyr-app-commands::
|
||||||
|
:zephyr-app: samples/subsys/fs/littlefs
|
||||||
|
:board: particle_xenon
|
||||||
|
:goals: build
|
||||||
|
:compact:
|
13
samples/subsys/fs/littlefs/boards/nrf52840_pca10056.conf
Normal file
13
samples/subsys/fs/littlefs/boards/nrf52840_pca10056.conf
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2019 Peter Bigot Consulting, LLC
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
# Need this when storage is on flash
|
||||||
|
CONFIG_MPU_ALLOW_FLASH_WRITE=y
|
||||||
|
|
||||||
|
# Need this when storage is on MX25R64
|
||||||
|
CONFIG_SPI=y
|
||||||
|
CONFIG_SPI_NOR=y
|
||||||
|
CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
|
8
samples/subsys/fs/littlefs/boards/particle_xenon.conf
Normal file
8
samples/subsys/fs/littlefs/boards/particle_xenon.conf
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2019 Peter Bigot Consulting, LLC
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
CONFIG_SPI=y
|
||||||
|
CONFIG_SPI_NOR=y
|
||||||
|
CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
|
33
samples/subsys/fs/littlefs/particle_xenon.overlay
Normal file
33
samples/subsys/fs/littlefs/particle_xenon.overlay
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Peter Bigot Consulting, LLC
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/delete-node/ &storage_partition;
|
||||||
|
|
||||||
|
&mx25l32 {
|
||||||
|
partitions {
|
||||||
|
compatible = "fixed-partitions";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
|
||||||
|
/* This is the littlefs v1 file system pre-installed
|
||||||
|
* by Particle.
|
||||||
|
*/
|
||||||
|
littlefs_partition: partition@0 {
|
||||||
|
label = "littlefs";
|
||||||
|
reg = <0x00000000 0x00200000>;
|
||||||
|
};
|
||||||
|
/* A 16 KiBy partition to use for the test. */
|
||||||
|
storage_partition: partition@200000 {
|
||||||
|
label = "storage";
|
||||||
|
reg = <0x00200000 0x00004000>;
|
||||||
|
};
|
||||||
|
/* A bigger partition for something else. */
|
||||||
|
partition@220000 {
|
||||||
|
label = "scratch";
|
||||||
|
reg = <0x00204000 0x001fc000>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
21
samples/subsys/fs/littlefs/prj.conf
Normal file
21
samples/subsys/fs/littlefs/prj.conf
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2019 Peter Bigot Consulting, LLC
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
# Optionally force the file system to be recreated
|
||||||
|
#CONFIG_APP_WIPE_STORAGE=y
|
||||||
|
|
||||||
|
# fs_dirent structures are big.
|
||||||
|
CONFIG_MAIN_STACK_SIZE=2048
|
||||||
|
|
||||||
|
CONFIG_LOG=y
|
||||||
|
#CONFIG_FS_LOG_LEVEL_DBG=y
|
||||||
|
|
||||||
|
CONFIG_FLASH=y
|
||||||
|
CONFIG_FLASH_MAP=y
|
||||||
|
CONFIG_FLASH_PAGE_LAYOUT=y
|
||||||
|
|
||||||
|
CONFIG_FILE_SYSTEM=y
|
||||||
|
CONFIG_FILE_SYSTEM_LITTLEFS=y
|
7
samples/subsys/fs/littlefs/sample.yaml
Normal file
7
samples/subsys/fs/littlefs/sample.yaml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
sample:
|
||||||
|
name: littlefs filesystem sample
|
||||||
|
tests:
|
||||||
|
sample.subsys.fs.littlefs:
|
||||||
|
build_only: true
|
||||||
|
platform_whitelist: nrf52840_pca10056 particle_xenon
|
||||||
|
tags: filesystem
|
140
samples/subsys/fs/littlefs/src/main.c
Normal file
140
samples/subsys/fs/littlefs/src/main.c
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019 Peter Bigot Consulting, LLC
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Sample which uses the filesystem API with littlefs */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <zephyr.h>
|
||||||
|
#include <device.h>
|
||||||
|
#include <fs/fs.h>
|
||||||
|
#include <fs/littlefs.h>
|
||||||
|
#include <storage/flash_map.h>
|
||||||
|
|
||||||
|
/* Matches LFS_NAME_MAX */
|
||||||
|
#define MAX_PATH_LEN 255
|
||||||
|
|
||||||
|
static struct fs_littlefs lfs_storage;
|
||||||
|
static struct fs_mount_t lfs_storage_mnt = {
|
||||||
|
.type = FS_LITTLEFS,
|
||||||
|
.fs_data = &lfs_storage,
|
||||||
|
.storage_dev = (void *)DT_FLASH_AREA_STORAGE_ID,
|
||||||
|
.mnt_point = "/lfs",
|
||||||
|
};
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
struct fs_mount_t *mp = &lfs_storage_mnt;
|
||||||
|
unsigned int id = (uintptr_t)mp->storage_dev;
|
||||||
|
char fname[MAX_PATH_LEN];
|
||||||
|
struct fs_statvfs sbuf;
|
||||||
|
const struct flash_area *pfa;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
snprintf(fname, sizeof(fname), "%s/boot_count", mp->mnt_point);
|
||||||
|
|
||||||
|
rc = flash_area_open(id, &pfa);
|
||||||
|
if (rc < 0) {
|
||||||
|
printk("FAIL: unable to find flash area %u: %d\n",
|
||||||
|
id, rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
printk("Erasing flash area ... ");
|
||||||
|
rc = flash_area_erase(pfa, 0, pfa->fa_size);
|
||||||
|
printk("%d\n", rc);
|
||||||
|
flash_area_close(pfa);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = fs_mount(mp);
|
||||||
|
if (rc < 0) {
|
||||||
|
printk("FAIL: mount id %u at %s: %d\n",
|
||||||
|
(unsigned int)mp->storage_dev, mp->mnt_point,
|
||||||
|
rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printk("%s mount: %d\n", mp->mnt_point, rc);
|
||||||
|
|
||||||
|
rc = fs_statvfs(mp->mnt_point, &sbuf);
|
||||||
|
if (rc < 0) {
|
||||||
|
printk("FAIL: statvfs: %d\n", rc);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
printk("%s: bsize = %lu ; frsize = %lu ;"
|
||||||
|
" blocks = %lu ; bfree = %lu\n",
|
||||||
|
mp->mnt_point,
|
||||||
|
sbuf.f_bsize, sbuf.f_frsize,
|
||||||
|
sbuf.f_blocks, sbuf.f_bfree);
|
||||||
|
|
||||||
|
struct fs_dirent dirent;
|
||||||
|
|
||||||
|
rc = fs_stat(fname, &dirent);
|
||||||
|
printk("%s stat: %d\n", fname, rc);
|
||||||
|
if (rc >= 0) {
|
||||||
|
printk("\tfn '%s' siz %u\n", dirent.name, dirent.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fs_file_t file;
|
||||||
|
|
||||||
|
rc = fs_open(&file, fname);
|
||||||
|
if (rc < 0) {
|
||||||
|
printk("FAIL: open %s: %d\n", fname, rc);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32_t boot_count = 0;
|
||||||
|
|
||||||
|
if (rc >= 0) {
|
||||||
|
rc = fs_read(&file, &boot_count, sizeof(boot_count));
|
||||||
|
printk("%s read count %u: %d\n", fname, boot_count, rc);
|
||||||
|
rc = fs_seek(&file, 0, FS_SEEK_SET);
|
||||||
|
printk("%s seek start: %d\n", fname, rc);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
boot_count += 1;
|
||||||
|
rc = fs_write(&file, &boot_count, sizeof(boot_count));
|
||||||
|
printk("%s write new boot count %u: %d\n", fname,
|
||||||
|
boot_count, rc);
|
||||||
|
|
||||||
|
rc = fs_close(&file);
|
||||||
|
printk("%s close: %d\n", fname, rc);
|
||||||
|
|
||||||
|
struct fs_dir_t dir = { 0 };
|
||||||
|
|
||||||
|
rc = fs_opendir(&dir, mp->mnt_point);
|
||||||
|
printk("%s opendir: %d\n", mp->mnt_point, rc);
|
||||||
|
|
||||||
|
while (rc >= 0) {
|
||||||
|
struct fs_dirent ent = { 0 };
|
||||||
|
|
||||||
|
rc = fs_readdir(&dir, &ent);
|
||||||
|
if (rc < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ent.name[0] == 0) {
|
||||||
|
printk("End of files\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
printk(" %c %u %s\n",
|
||||||
|
(ent.type == FS_DIR_ENTRY_FILE) ? 'F' : 'D',
|
||||||
|
ent.size,
|
||||||
|
ent.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)fs_closedir(&dir);
|
||||||
|
|
||||||
|
out:
|
||||||
|
rc = fs_unmount(mp);
|
||||||
|
printk("%s unmount: %d\n", mp->mnt_point, rc);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue