diff --git a/samples/subsys/usb/ffat/CMakeLists.txt b/samples/subsys/usb/ffat/CMakeLists.txt new file mode 100644 index 000000000000000..400de7649be31d8 --- /dev/null +++ b/samples/subsys/usb/ffat/CMakeLists.txt @@ -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}) diff --git a/samples/subsys/usb/ffat/Kconfig b/samples/subsys/usb/ffat/Kconfig new file mode 100644 index 000000000000000..96c5455894806dd --- /dev/null +++ b/samples/subsys/usb/ffat/Kconfig @@ -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" diff --git a/samples/subsys/usb/ffat/README.rst b/samples/subsys/usb/ffat/README.rst new file mode 100644 index 000000000000000..8796d44293e2d6a --- /dev/null +++ b/samples/subsys/usb/ffat/README.rst @@ -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: diff --git a/samples/subsys/usb/ffat/app.overlay b/samples/subsys/usb/ffat/app.overlay new file mode 100644 index 000000000000000..10247399a1ae316 --- /dev/null +++ b/samples/subsys/usb/ffat/app.overlay @@ -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>; + }; +}; diff --git a/samples/subsys/usb/ffat/prj.conf b/samples/subsys/usb/ffat/prj.conf new file mode 100644 index 000000000000000..50ef2804498b41d --- /dev/null +++ b/samples/subsys/usb/ffat/prj.conf @@ -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 diff --git a/samples/subsys/usb/ffat/sample.yaml b/samples/subsys/usb/ffat/sample.yaml new file mode 100644 index 000000000000000..d5d721f5be2a6f1 --- /dev/null +++ b/samples/subsys/usb/ffat/sample.yaml @@ -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 diff --git a/samples/subsys/usb/ffat/src/main.c b/samples/subsys/usb/ffat/src/main.c new file mode 100644 index 000000000000000..18fdfabe33760cf --- /dev/null +++ b/samples/subsys/usb/ffat/src/main.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +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[] = + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "
\n" + "

FFAT sample

\n" + "

zephyrproject.org

\n" + "
\n" + "\n" + "\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; +}