Skip to content

Commit

Permalink
samples: usb: add ffatdisk sample
Browse files Browse the repository at this point in the history
This sample has two volumes, FAT16 and FAT32, exported via
new USB device MSC support.

Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no>
  • Loading branch information
jfischer-no committed Oct 16, 2024
1 parent 38d33bc commit 5e5d632
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 0 deletions.
10 changes: 10 additions & 0 deletions samples/subsys/usb/ffat/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) 2023 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(ffat)

include(${ZEPHYR_BASE}/samples/subsys/usb/common/common.cmake)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})
9 changes: 9 additions & 0 deletions samples/subsys/usb/ffat/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright (c) 2023 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

# Source common USB sample options used to initialize new experimental USB
# device stack. The scope of these options is limited to USB samples in project
# tree, you cannot use them in your own application.
source "samples/subsys/usb/common/Kconfig.sample_usbd"

source "Kconfig.zephyr"
35 changes: 35 additions & 0 deletions samples/subsys/usb/ffat/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.. zephyr:code-sample:: usb-ffat
:name: USB FFAT
:relevant-api: usbd_api usbd_msc_device

Expose a FFAT disk over USB using the USB Mass Storage Class implementation.

Overview
********

This sample application demonstrates the FFAT disk implementation using the new
experimental USB device stack. The sample has two FFAT disks that emulate FAT16
and FAT32 file system formatted disks. There is no real disk or file system
behind them. The two disks emulate FAT formatted disks at runtime. When mounted
on the host side, you will find three files on the FFAT16 disk and two files on
the FFAT32 disk. You can read and write to the binary files; the text files are
read-only. Writing to a file means that for each block written to the disk, a
callback is invoked on the Zephyr side that can be used, for example, to update
firmware or load something into the device running the Zephyr RTOS.

Requirements
************

This project requires an experimental USB device driver (UDC API).

Building and Running
********************

This sample can be built for multiple boards, in this example we will build it
for the nRF52840DK board:

.. zephyr-app-commands::
:zephyr-app: samples/subsys/usb/ffat
:board: nrf52840dk/nrf52840
:goals: build flash
:compact:
23 changes: 23 additions & 0 deletions samples/subsys/usb/ffat/app.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

/ {
ffatdisk0 {
compatible = "zephyr,ffat-disk";
disk-name = "FFAT16";
sector-size = <512>;
sector-count = <8192>;
sector-per-cluster = <1>;
};

ffatdisk1 {
compatible = "zephyr,ffat32-disk", "zephyr,ffat-disk";
disk-name = "FFAT32";
sector-size = <512>;
sector-count = <7744512>;
sector-per-cluster = <8>;
};
};
11 changes: 11 additions & 0 deletions samples/subsys/usb/ffat/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CONFIG_USB_DEVICE_STACK_NEXT=y

CONFIG_STDOUT_CONSOLE=y
CONFIG_USBD_MSC_CLASS=y
CONFIG_USBD_MSC_LUNS_PER_INSTANCE=2

CONFIG_LOG=y
CONFIG_USBD_LOG_LEVEL_WRN=y
CONFIG_UDC_DRIVER_LOG_LEVEL_WRN=y

CONFIG_MAIN_STACK_SIZE=2048
14 changes: 14 additions & 0 deletions samples/subsys/usb/ffat/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
sample:
name: USB FFAT sample
common:
depends_on:
- usbd
integration_platforms:
- nrf52840dk/nrf52840
- nrf54h20dk/nrf54h20/cpuapp
- frdm_k64f
- nucleo_f413zh
- mimxrt1060_evk
tests:
sample.usbd.ffat:
tags: usb filesystem
152 changes: 152 additions & 0 deletions samples/subsys/usb/ffat/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <sample_usbd.h>

#include <stdint.h>
#include <version.h>

#include <zephyr/kernel.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/usb/usbd.h>
#include <zephyr/usb/class/usbd_msc.h>
#include <zephyr/storage/ffatdisk.h>

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);

#ifdef BUILD_VERSION
#define BANNER_VERSION STRINGIFY(BUILD_VERSION)
#else
#define BANNER_VERSION KERNEL_VERSION_STRING
#endif

const char txt_info_file[] =
"Zephyr RTOS\n"
"Build " BANNER_VERSION "\n"
"Board " CONFIG_BOARD "\n"
"Arch "CONFIG_ARCH "\n";

const char html_info_file[] =
"<!DOCTYPE html>\n"
"<html>\n"
"<head>\n"
"<style>\n"
"body {\n"
"background: #AF7FE4;\n"
"}\n"
".content {\n"
"max-width: 480px;\n"
"background: #F0F2F4;\n"
"margin: auto;\n"
"padding: 16px;\n"
"}\n"
"</style>\n"
"</head>\n"
"<body>\n"
"<div class=\"content\">\n"
"<h1>FFAT sample</h1>\n"
"<p><a href=\"https://www.zephyrproject.org/\">zephyrproject.org</a></p>\n"
"</div>\n"
"</body>\n"
"</html>\n";

struct binfile {
uint32_t s_tag;
uint32_t b_num;
uint8_t reserved[500];
uint32_t e_tag;
} __packed;

BUILD_ASSERT(sizeof(struct binfile) == 512U);

static int infofile_rd_cb(struct ffat_file *const f, const uint32_t sector,
uint8_t *const buf, const uint32_t size)
{
size_t f_off = size * sector;

if (f->size > f_off) {
size_t len = MIN(f->size - f_off, size);

memcpy(buf, (uint8_t *)f->priv + f_off, len);
LOG_DBG("Read %u bytes, sector %u file offset %zu, f->size %zu",
len, sector, f_off, f->size);
}

return 0;
}

static int binfile_rd_cb(struct ffat_file *const f, const uint32_t sector,
uint8_t *const buf, const uint32_t size)
{
size_t f_off = size * sector;

if (f->size > f_off) {
size_t len = MIN(f->size - f_off, size);
struct binfile *test = (void *)buf;

test->s_tag = sys_cpu_to_le32(0xDECAFBAD);
test->b_num = sys_cpu_to_le32(sector);
test->e_tag = sys_cpu_to_le32(0xDEADDA7A);

LOG_DBG("Read %u bytes, sector %u file offset %zu, f->size %zu",
len, sector, f_off, f->size);
}

return 0;
}

static int binfile_wr_cb(struct ffat_file *const f, const uint32_t sector,
const uint8_t *const buf, const uint32_t size)
{
size_t f_off = size * sector;

if (f->size > f_off) {
size_t len = MIN(f->size - f_off, size);

LOG_DBG("Write %u bytes, sector %u file offset %zu, f->size %zu",
len, sector, f_off, f->size);
}

return 0;
}

FFAT_FILE_DEFINE(readme, "FFAT16", "README TXT", sizeof(txt_info_file),
infofile_rd_cb, NULL, txt_info_file);
FFAT_FILE_DEFINE(html, "FFAT16", "INDEX HTM", sizeof(html_info_file),
infofile_rd_cb, NULL, html_info_file);
FFAT_FILE_DEFINE(bin, "FFAT16", "FOOBAZ BIN", 32768U,
binfile_rd_cb, binfile_wr_cb, NULL);

FFAT_FILE_DEFINE(readme32, "FFAT32", "README TXT", sizeof(txt_info_file),
infofile_rd_cb, NULL, txt_info_file);
FFAT_FILE_DEFINE(big, "FFAT32", "FOOBAZ BIN", 66051072UL,
binfile_rd_cb, binfile_wr_cb, NULL);

USBD_DEFINE_MSC_LUN(ffat16, "FFAT16", "Zephyr", "FFAT16", "0.00");
USBD_DEFINE_MSC_LUN(ffat32, "FFAT32", "Zephyr", "FFAT32", "0.00");

int main(void)
{
struct usbd_context *sample_usbd;
int ret;

sample_usbd = sample_usbd_init_device(NULL);
if (sample_usbd == NULL) {
LOG_ERR("Failed to initialize USB device");
return -ENODEV;
}

ret = usbd_enable(sample_usbd);
if (ret) {
LOG_ERR("Failed to enable device support");
return ret;
}

LOG_INF("FFAT sample is ready.");

return 0;
}

0 comments on commit 5e5d632

Please sign in to comment.