Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

samples: usb: mass: demonstrate mounting littlefs file systems on Linux/FreeBSD hosts #24696

Merged
merged 5 commits into from
May 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion samples/subsys/fs/littlefs/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ void main(void)
printk("Erasing flash area ... ");
rc = flash_area_erase(pfa, 0, pfa->fa_size);
printk("%d\n", rc);
flash_area_close(pfa);
}

flash_area_close(pfa);

rc = fs_mount(mp);
if (rc < 0) {
printk("FAIL: mount id %u at %s: %d\n",
Expand Down
5 changes: 5 additions & 0 deletions samples/subsys/usb/mass/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@
config USB_DEVICE_PID
default USB_PID_MASS_SAMPLE

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 "Kconfig.zephyr"
131 changes: 127 additions & 4 deletions samples/subsys/usb/mass/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,18 @@ device.
Building and Running
********************

This sample can be built for multiple boards. The selection between a RAM-based
or a FLASH-based disk can be selected using the ``overlay-ram-disk.conf`` or
the ``overlay-flash-disk.conf`` overlays. In this example we will build the sample
with a RAM-based disk:
This sample can be built for multiple boards, some generic and some
customized through configurations found in
:zephyr_file:`samples/subsys/usb/mass/boards` in the Zephyr project
tree.

Generic Example
===============

The selection between a RAM-based or a FLASH-based disk can be selected
using the ``overlay-ram-disk.conf`` or the ``overlay-flash-disk.conf``
overlays. In this example we will build the sample with a RAM-based
disk:

.. zephyr-app-commands::
:zephyr-app: samples/subsys/usb/mass
Expand Down Expand Up @@ -65,3 +73,118 @@ The disk contains a simple ``README.TXT`` file with the following content:

Files can be added or removed like with a simple USB disk, of course within
the 16KiB limit.

nrf52840dk_nrf52840 Example
===========================

This board configures to use the external 64 MiBi QSPI flash chip with a
64 KiBy `littlefs`_ partition compatible with the one produced by the
:ref:`littlefs-sample`. While a FAT-based file system can be mounted by
many systems automatically, mounting the littlefs file system on a Linux
or FreeBSD system can be accomplished using the `littlefs-FUSE`_ utility.

.. zephyr-app-commands::
:zephyr-app: samples/subsys/usb/mass
:board: nrf52840dk_nrf52840
:goals: build
:compact:

After you have built and flashed the sample app image to your board,
connect the board's two USB connectors (debug and nRF USB) to a host
running a littlefs-FUSE-capable operating system. The output to the
console will look something like this (file system contents will be
different):

.. code-block:: none

*** Booting Zephyr OS build zephyr-v2.2.0-1966-g7815942d5fc5 ***
Area 4 at 0x0 on MX25R64 for 65536 bytes
[00:00:00.005,310] <inf> main: The device is put in USB mass storage mode.

[00:00:00.009,002] <inf> littlefs: LittleFS version 2.2, disk version 2.0
[00:00:00.009,063] <inf> littlefs: FS at MX25R64:0x0 is 16 0x1000-byte blocks with 512 cye
[00:00:00.009,063] <inf> littlefs: sizes: rd 16 ; pr 16 ; ca 64 ; la 32
[00:00:00.011,718] <inf> littlefs: /lfs mounted
Mount /lfs: 0
/lfs: bsize = 16 ; frsize = 4096 ; blocks = 16 ; bfree = 13
/lfs opendir: 0
F 8 hi
F 128 linux
F 5 newfile
End of files

Determine the local device name from the system log, e.g.:

.. code-block:: none

Apr 25 08:10:25 tirzah kernel: [570310.921039] scsi 17:0:0:0: Direct-Access ZEPHYR ZEPHYR USB DISK 0.01 PQ: 0 ANSI: 0 CCS
Apr 25 08:10:25 tirzah kernel: [570310.921550] sd 17:0:0:0: Attached scsi generic sg4 type 0
Apr 25 08:10:25 tirzah kernel: [570310.922277] sd 17:0:0:0: [sdd] 128 512-byte logical blocks: (65.5 kB/64.0 KiB)
Apr 25 08:10:25 tirzah kernel: [570310.922696] sd 17:0:0:0: [sdd] Write Protect is off

This shows that the block device associated with the USB drive is
``/dev/sdd``:

.. code-block:: shell

tirzah[447]$ ll /dev/sdd
brw-rw---- 1 root disk 8, 48 Apr 25 08:10 /dev/sdd

This can be mounted as a file system with the following commands:

.. code-block:: shell

sudo chmod a+rw /dev/sdd # required to allow user access
mkdir /tmp/lfs
lfs \
--read_size=16 \
--prog_size=16 \
--block_size=4096 \
--block_count=16 \
--cache_size=64 \
--lookahead_size=32 \
/dev/sdd /tmp/lfs

which produces this output:

.. code-block:: none

tirzah[467]$ ls -l /tmp/lfs
total 0
-rwxrwxrwx 0 root root 8 Dec 31 1969 hi
-rwxrwxrwx 0 root root 128 Dec 31 1969 linux
-rwxrwxrwx 0 root root 5 Dec 31 1969 newfile

``lfs`` is a mount command and you should take care to unmount the
device before removing the USB drive:

.. code-block:: shell

umount /tmp/lfs

littlefs parameter selection
----------------------------

Be aware that the parameters passed to :command:`lfs` in the example
above **must** exactly match the corresponding parameters used to
initialize the file system. The required parameters can be observed
from the Zephyr mount log messages:

.. code-block:: none

[00:00:00.009,002] <inf> littlefs: LittleFS version 2.2, disk version 2.0
[00:00:00.009,063] <inf> littlefs: FS at MX25R64:0x0 is 16 0x1000-byte blocks with 512 cye
[00:00:00.009,063] <inf> littlefs: sizes: rd 16 ; pr 16 ; ca 64 ; la 32

* ``--read_size`` corresponds to the ``rd`` size and is 16;
* ``--prog_size`` corresponds to the ``pr`` size and is 16;
* ``--block_size`` comes from ``0x1000-byte blocks`` and is 4096 (0x1000);
* ``--block_count`` comes from ``16 0x1000-byte blocks`` and is 16;
* ``--cache_size`` comes from the ``ca`` size and is 64;
* ``--lookahead_size`` comes from the ``la`` size and is 32

If any of the parameters are inconsistent between the Zephyr and Linux
specification the file system will not mount correctly.

.. _littlefs: https://github.com/ARMmbed/littlefs
.. _littlefs-FUSE: https://github.com/ARMmbed/littlefs-fuse
23 changes: 23 additions & 0 deletions samples/subsys/usb/mass/boards/nrf52840dk_nrf52840.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Storage is on MX25R64
CONFIG_NORDIC_QSPI_NOR=y
CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096

# Must match the registered name for the selected disk access variant:
# "RAM" via DISK_RAM_VOLUME_NAME for DISK_ACCESS_RAM
# "NAND" via DISK_FLASH_VOLUME_NAME for DISK_ACCESS_FLASH
CONFIG_MASS_STORAGE_DISK_NAME="NAND"

CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_LITTLEFS=y
CONFIG_FLASH_LOG_LEVEL_INF=y

CONFIG_FLASH_MAP=y
CONFIG_FLASH_PAGE_LAYOUT=y

CONFIG_DISK_ACCESS_FLASH=y
CONFIG_DISK_FLASH_DEV_NAME="MX25R64"
CONFIG_DISK_FLASH_START=0x0
CONFIG_DISK_VOLUME_SIZE=0x10000
CONFIG_DISK_FLASH_MAX_RW_SIZE=4096
CONFIG_DISK_FLASH_ERASE_ALIGNMENT=0x1000
CONFIG_DISK_ERASE_BLOCK_SIZE=0x1000
20 changes: 20 additions & 0 deletions samples/subsys/usb/mass/boards/nrf52840dk_nrf52840.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2019 Peter Bigot Consulting, LLC
*
* SPDX-License-Identifier: Apache-2.0
*/

/delete-node/ &storage_partition;

&mx25r64 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;

partition@0 {
label = "storage";
reg = <0x00000000 0x00010000>;
};
};
};
26 changes: 0 additions & 26 deletions samples/subsys/usb/mass/prj_nrf52840_pca10056.conf

This file was deleted.

94 changes: 86 additions & 8 deletions samples/subsys/usb/mass/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,39 @@
#include <zephyr.h>
#include <logging/log.h>
#include <usb/usb_device.h>
#include <stdio.h>

LOG_MODULE_REGISTER(main);

#if CONFIG_DISK_ACCESS_FLASH && CONFIG_FAT_FILESYSTEM_ELM
#if CONFIG_DISK_ACCESS_FLASH
#if CONFIG_FAT_FILESYSTEM_ELM
#include <fs/fs.h>
#include <ff.h>

static FATFS fat_fs;
#define FATFS_MNTP "/NAND:"
#define FATFS_MNTP "/NAND:"

static struct fs_mount_t fatfs_mnt = {
static struct fs_mount_t fs_mnt = {
.type = FS_FATFS,
.mnt_point = FATFS_MNTP,
.fs_data = &fat_fs,
};
#endif
#elif CONFIG_FILE_SYSTEM_LITTLEFS
#include <fs/fs.h>
#include <fs/littlefs.h>
#include <storage/flash_map.h>

FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage);
static struct fs_mount_t fs_mnt = {
.type = FS_LITTLEFS,
.fs_data = &storage,
.storage_dev = (void *)DT_FLASH_AREA_STORAGE_ID,
.mnt_point = "/lfs",
};
#else
#error No recognized file system
#endif /* file system */
#endif /* CONFIG_DISK_ACCESS_FLASH */

void main(void)
{
Expand All @@ -38,11 +56,71 @@ void main(void)

LOG_INF("The device is put in USB mass storage mode.\n");

#if CONFIG_DISK_ACCESS_FLASH && CONFIG_FAT_FILESYSTEM_ELM
int res = fs_mount(&fatfs_mnt);
#if CONFIG_DISK_ACCESS_FLASH
struct fs_mount_t *mp = &fs_mnt;
unsigned int id = (uintptr_t)mp->storage_dev;
int rc;
const struct flash_area *pfa;

if (res < 0) {
LOG_ERR("Mount failed.");
rc = flash_area_open(id, &pfa);
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);

if (IS_ENABLED(CONFIG_APP_WIPE_STORAGE)) {
printk("Erasing flash area ... ");
rc = flash_area_erase(pfa, 0, pfa->fa_size);
printk("%d\n", rc);
}

rc = fs_mount(&fs_mnt);

/* Allow log messages to flush to avoid interleaved output */
k_sleep(K_MSEC(50));

printk("Mount %s: %d\n", fs_mnt.mnt_point, rc);

if (rc >= 0) {
struct fs_statvfs sbuf;

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_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:
flash_area_close(pfa);
#endif
}
2 changes: 1 addition & 1 deletion subsys/disk/disk_access_flash.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
static struct device *flash_dev;

/* flash read-copy-erase-write operation */
static u8_t read_copy_buf[CONFIG_DISK_ERASE_BLOCK_SIZE];
static u8_t __aligned(4) read_copy_buf[CONFIG_DISK_ERASE_BLOCK_SIZE];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__aligned(sizeof(u32_t)) ?

static u8_t *fs_buff = read_copy_buf;

/* calculate number of blocks required for a given size */
Expand Down
5 changes: 4 additions & 1 deletion subsys/usb/class/mass_storage.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,11 @@ static volatile u32_t defered_wr_sz;
* Keep block buffer larger than BLOCK_SIZE for the case
* the dCBWDataTransferLength is multiple of the BLOCK_SIZE and
* the length of the transferred data is not aligned to the BLOCK_SIZE.
*
* Align for cases where the underlying disk access requires word-aligned
* addresses.
*/
static u8_t page[BLOCK_SIZE + CONFIG_MASS_STORAGE_BULK_EP_MPS];
static u8_t __aligned(4) page[BLOCK_SIZE + CONFIG_MASS_STORAGE_BULK_EP_MPS];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__aligned(sizeof(u32_t)) ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most existing _aligned attributes in Zephyr use integer values directly rather than deriving them from sizeof(type).


/* Initialized during mass_storage_init() */
static u32_t memory_size;
Expand Down