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:
Peter A. Bigot 2019-07-21 15:21:43 -05:00 committed by Carles Cufí
commit cfa64bde1c
9 changed files with 314 additions and 0 deletions

View 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})

View 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"

View 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:

View 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

View 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

View 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>;
};
};
};

View 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

View 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

View 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);
}