mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
USB Dev: Integrate sdcard as a storage media that is accessed by USB-host as a USB Mass Storage Device
This commit is contained in:
parent
1d2469fbd1
commit
80cad41cc2
@ -144,7 +144,7 @@ The driver allows to redirect all standard application streams (stdinm stdout, s
|
||||
USB Mass Storage Device (MSC)
|
||||
-----------------------------
|
||||
|
||||
If the MSC CONFIG_TINYUSB_MSC_ENABLED option is enabled and SPI Flash Wear Levelling WL_SECTOR_SIZE is set to 512 and WL_SECTOR_MODE is set to PERF in Menuconfig, the USB MSC Device can be initialized as shown below (see example below).
|
||||
If the MSC CONFIG_TINYUSB_MSC_ENABLED option is enabled, the USB MSC Device can be initialized as shown below (see example below).
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
|
@ -7,11 +7,14 @@
|
||||
|
||||
Mass Storage Devices are one of the most common USB devices. It use Mass Storage Class (MSC) that allow access to their internal data storage.
|
||||
This example contains code to make ESP based device recognizable by USB-hosts as a USB Mass Storage Device.
|
||||
It either allows the embedded application ie example to access the partition or Host PC accesses the partition over USB MSC.
|
||||
It either allows the embedded application i.e. example to access the partition or Host PC accesses the partition over USB MSC.
|
||||
They can't be allowed to access the partition at the same time.
|
||||
The access to the underlying block device is provided by functions in tusb_msc_storage.c
|
||||
|
||||
In this example, data is read/written from/to SPI Flash through wear-levelling APIs. Wear leveling is a technique that helps to distribute wear and tear among sectors more evenly without requiring any attention from the user. As a result, it helps in extending the life of each sector of the Flash memory.
|
||||
This example supports storage media of two types:
|
||||
1. SPI Flash
|
||||
2. SD MMC Card
|
||||
|
||||
Data is read/written from/to SPI Flash through wear-levelling APIs. Wear leveling is a technique that helps to distribute wear and tear among sectors more evenly without requiring any attention from the user. As a result, it helps in extending the life of each sector of the Flash memory.
|
||||
|
||||
As a USB stack, a TinyUSB component is used.
|
||||
|
||||
@ -33,7 +36,8 @@ As a USB stack, a TinyUSB component is used.
|
||||
|
||||
### Hardware Required
|
||||
|
||||
Any ESP board that have USB-OTG supported.
|
||||
1. If the storage media is SPI Flash, any ESP board that have USB-OTG is supported.
|
||||
2. If the storage media is SD MMC Card, any ESP board with SD MMC card slot and an SD card is required. For Ex - ESP32-S3-USB-OTG
|
||||
|
||||
### Pin Assignment
|
||||
|
||||
@ -43,8 +47,39 @@ See common pin assignments for USB Device examples from [upper level](../../READ
|
||||
|
||||
Next, for Self-Powered Devices with VBUS monitoring, user must set ``self_powered`` to ``true`` and ``vbus_monitor_io`` to GPIO number (``VBUS_MONITORING_GPIO_NUM``) that will be used for VBUS monitoring.
|
||||
|
||||
### Additional Pin assignments for ESP32-S3 for accessing SD MMC Card
|
||||
|
||||
On ESP32-S3, SDMMC peripheral is connected to GPIO pins using GPIO matrix. This allows arbitrary GPIOs to be used to connect an SD card. In this example, GPIOs can be configured in two ways:
|
||||
|
||||
1. Using menuconfig: Run `idf.py menuconfig` in the project directory, open "USB DEV MSC Example Configuration" and select "SDMMC CARD" for "Storage Media Used".
|
||||
2. In the source code: See the initialization of ``sdmmc_slot_config_t slot_config`` structure in the example code.
|
||||
|
||||
The table below lists the default pin assignments.
|
||||
|
||||
When using an ESP32-S3-USB-OTG board, this example runs without any extra modifications required. Only an SD card needs to be inserted into the slot.
|
||||
|
||||
ESP32-S3 pin | SD card pin | Notes
|
||||
--------------|-------------|------------
|
||||
GPIO36 | CLK | 10k pullup
|
||||
GPIO35 | CMD | 10k pullup
|
||||
GPIO37 | D0 | 10k pullup
|
||||
GPIO38 | D1 | not used in 1-line SD mode; 10k pullup in 4-line mode
|
||||
GPIO33 | D2 | not used in 1-line SD mode; 10k pullup in 4-line mode
|
||||
GPIO34 | D3 | not used in 1-line SD mode, but card's D3 pin must have a 10k pullup
|
||||
|
||||
By default, this example uses 4 line SD mode, utilizing 6 pins: CLK, CMD, D0 - D3. It is possible to use 1-line mode (CLK, CMD, D0) by changing "SD/MMC bus width" in the example configuration menu (see `CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_1`).
|
||||
|
||||
Note that even if card's D3 line is not connected to the ESP chip, it still has to be pulled up, otherwise the card will go into SPI protocol mode.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
1. By default, the example will compile to access SPI Flash as storage media. Here, SPI Flash Wear Levelling WL_SECTOR_SIZE is set to 512 and WL_SECTOR_MODE is set to PERF in Menuconfig.
|
||||
2. In order to access SD MMC card as storage media, configuration has to be changed using `idf.py menuconfig`:
|
||||
- i. Open "USB DEV MSC Example Configuration" and select "SDMMC CARD" for "Storage Media Used"
|
||||
- ii. Open "SD/MMC bus width" and select between "4 lines (D0 - D3)" or "1 line (D0)"
|
||||
- iii. Select the GPIO Pin numbers for SD Card Pin.
|
||||
- iv. Save the configuration.
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```bash
|
||||
@ -67,6 +102,8 @@ I (0) cpu_start: Starting scheduler on APP CPU.
|
||||
I (332) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (332) example_msc_main: Initializing storage...
|
||||
I (342) example_msc_storage: Initializing wear levelling
|
||||
I (350) example_main: Mount storage...
|
||||
I (350) example_sdmmc: Initializing FAT
|
||||
I (372) example_msc_main: USB MSC initialization
|
||||
I (372) tusb_desc:
|
||||
┌─────────────────────────────────┐
|
||||
@ -96,8 +133,6 @@ I (372) tusb_desc:
|
||||
└───────────────────┴─────────────┘
|
||||
I (532) TinyUSB: TinyUSB Driver installed
|
||||
I (532) example_msc_main: USB MSC initialization DONE
|
||||
I (542) example_msc_main: Mount storage...
|
||||
I (542) example_msc_storage: Initializing FAT
|
||||
I (552) example_msc_main:
|
||||
ls command output:
|
||||
README.MD
|
||||
|
@ -1,5 +1,17 @@
|
||||
set(srcs "tusb_msc_main.c")
|
||||
set(requires fatfs console)
|
||||
|
||||
if(CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH)
|
||||
list(APPEND srcs "tusb_msc_storage_spiffs.c")
|
||||
list(APPEND requires "wear_levelling")
|
||||
endif()
|
||||
|
||||
if(CONFIG_EXAMPLE_STORAGE_MEDIA_SDMMCCARD)
|
||||
list(APPEND srcs "tusb_msc_storage_sdmmc.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(
|
||||
SRCS "tusb_msc_storage.c" "tusb_msc_main.c"
|
||||
SRCS "${srcs}"
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES fatfs wear_levelling console
|
||||
)
|
||||
REQUIRES "${requires}"
|
||||
)
|
||||
|
@ -0,0 +1,69 @@
|
||||
menu "USB DEV MSC Example Configuration"
|
||||
|
||||
choice EXAMPLE_STORAGE_MEDIA
|
||||
prompt "Storage Media Used"
|
||||
default EXAMPLE_STORAGE_MEDIA_SPIFLASH
|
||||
help
|
||||
Select the storage media that is exposed to USB host.
|
||||
|
||||
config EXAMPLE_STORAGE_MEDIA_SPIFLASH
|
||||
bool "SPI FLASH"
|
||||
|
||||
config EXAMPLE_STORAGE_MEDIA_SDMMCCARD
|
||||
bool "SDMMC CARD"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
endchoice
|
||||
|
||||
if EXAMPLE_STORAGE_MEDIA_SDMMCCARD
|
||||
|
||||
choice EXAMPLE_SDMMC_BUS_WIDTH
|
||||
prompt "SD/MMC bus width"
|
||||
default EXAMPLE_SDMMC_BUS_WIDTH_4
|
||||
help
|
||||
Select the bus width of SD or MMC interface.
|
||||
Note that even if 1 line mode is used, D3 pin of the SD card must
|
||||
have a pull-up resistor connected. Otherwise the card may enter
|
||||
SPI mode, the only way to recover from which is to cycle power to the card.
|
||||
|
||||
config EXAMPLE_SDMMC_BUS_WIDTH_4
|
||||
bool "4 lines (D0 - D3)"
|
||||
|
||||
config EXAMPLE_SDMMC_BUS_WIDTH_1
|
||||
bool "1 line (D0)"
|
||||
endchoice
|
||||
|
||||
if SOC_SDMMC_USE_GPIO_MATRIX
|
||||
|
||||
config EXAMPLE_PIN_CMD
|
||||
int "CMD GPIO number"
|
||||
default 35 if IDF_TARGET_ESP32S3
|
||||
|
||||
config EXAMPLE_PIN_CLK
|
||||
int "CLK GPIO number"
|
||||
default 36 if IDF_TARGET_ESP32S3
|
||||
|
||||
config EXAMPLE_PIN_D0
|
||||
int "D0 GPIO number"
|
||||
default 37 if IDF_TARGET_ESP32S3
|
||||
|
||||
if EXAMPLE_SDMMC_BUS_WIDTH_4
|
||||
|
||||
config EXAMPLE_PIN_D1
|
||||
int "D1 GPIO number"
|
||||
default 38 if IDF_TARGET_ESP32S3
|
||||
|
||||
config EXAMPLE_PIN_D2
|
||||
int "D2 GPIO number"
|
||||
default 33 if IDF_TARGET_ESP32S3
|
||||
|
||||
config EXAMPLE_PIN_D3
|
||||
int "D3 GPIO number"
|
||||
default 34 if IDF_TARGET_ESP32S3
|
||||
|
||||
endif # EXAMPLE_SDMMC_BUS_WIDTH_4
|
||||
|
||||
endif # SOC_SDMMC_USE_GPIO_MATRIX
|
||||
|
||||
endif # EXAMPLE_STORAGE_MEDIA_SDMMCCARD
|
||||
|
||||
endmenu
|
@ -1,14 +1,13 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
/* DESCRIPTION:
|
||||
* This example contains code to make ESP32-S3 based device recognizable by USB-hosts as a USB Mass Storage Device.
|
||||
* It either allows the embedded application ie example to access the partition or Host PC accesses the partition over USB MSC.
|
||||
* It either allows the embedded application i.e. example to access the partition or Host PC accesses the partition over USB MSC.
|
||||
* They can't be allowed to access the partition at the same time.
|
||||
* The access to the underlying block device is provided by functions in tusb_msc_storage.c
|
||||
* For different scenarios and behaviour, Refer to README of this example.
|
||||
*/
|
||||
|
||||
@ -22,10 +21,10 @@
|
||||
#include "tusb_msc_storage.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
static const char *TAG = "example_msc_main";
|
||||
#define PROMPT_STR CONFIG_IDF_TARGET
|
||||
static const char *TAG = "example_main";
|
||||
|
||||
/********* TinyUSB MSC callbacks ***************/
|
||||
/* TinyUSB MSC callbacks
|
||||
********************************************************************* */
|
||||
|
||||
/** SCSI ASC/ASCQ codes. **/
|
||||
/** User can add and use more codes as per the need of the application **/
|
||||
@ -76,11 +75,11 @@ void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_siz
|
||||
{
|
||||
(void) lun;
|
||||
|
||||
size_t size = storage_get_size();
|
||||
size_t sec_size = storage_get_sector_size();
|
||||
ESP_LOGI(TAG, "tud_msc_capacity_cb() size(%d), sec_size(%d)", size, sec_size);
|
||||
*block_count = size / sec_size;
|
||||
*block_size = sec_size;
|
||||
uint32_t sec_count = storage_get_sector_count();
|
||||
uint32_t sec_size = storage_get_sector_size();
|
||||
ESP_LOGD(TAG, "tud_msc_capacity_cb() sec_count(%lu), sec_size(%lu)", sec_count, sec_size);
|
||||
*block_count = sec_count;
|
||||
*block_size = (uint16_t)sec_size;
|
||||
}
|
||||
|
||||
// Invoked when received Start Stop Unit command
|
||||
@ -107,8 +106,7 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buff
|
||||
{
|
||||
ESP_LOGD(TAG, "tud_msc_read10_cb() invoked, lun=%d, lba=%lu, offset=%lu, bufsize=%lu", lun, lba, offset, bufsize);
|
||||
|
||||
size_t addr = lba * storage_get_sector_size() + offset;
|
||||
esp_err_t err = storage_read_sector(addr, bufsize, buffer);
|
||||
esp_err_t err = storage_read_sector(lba, offset, bufsize, buffer);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "storage_read_sector failed: 0x%x", err);
|
||||
return 0;
|
||||
@ -121,10 +119,9 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buff
|
||||
// - Application write data from buffer to address contents (up to bufsize) and return number of written byte.
|
||||
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize)
|
||||
{
|
||||
ESP_LOGD(TAG, "tud_msc_write10_cb() invoked, lun=%d, lba=%lu, offset=%lu", lun, lba, offset);
|
||||
ESP_LOGD(TAG, "tud_msc_write10_cb() invoked, lun=%d, lba=%lu, offset=%lu, bufsize=%lu", lun, lba, offset, bufsize);
|
||||
|
||||
size_t addr = lba * storage_get_sector_size() + offset;
|
||||
esp_err_t err = storage_write_sector(addr, bufsize, buffer);
|
||||
esp_err_t err = storage_write_sector(lba, offset, bufsize, buffer);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "storage_write_sector failed: 0x%x", err);
|
||||
return 0;
|
||||
@ -185,13 +182,15 @@ void tud_mount_cb(void)
|
||||
ESP_LOGI(TAG, "tud_mount_cb MSC START: Expose Over USB");
|
||||
_unmount();
|
||||
}
|
||||
/*********************************************************************** TinyUSB MSC callbacks*/
|
||||
|
||||
/************* Application Code *******************/
|
||||
/* Application Code
|
||||
********************************************************************* */
|
||||
|
||||
/************* TinyUSB descriptors ****************/
|
||||
/* TinyUSB descriptors
|
||||
********************************************************************* */
|
||||
#define EPNUM_MSC 1
|
||||
#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN)
|
||||
#define VBUS_MONITORING_GPIO_NUM GPIO_NUM_4
|
||||
|
||||
enum {
|
||||
ITF_NUM_MSC = 0,
|
||||
@ -238,10 +237,58 @@ static char const *string_desc_arr[] = {
|
||||
"123456", // 3: Serials
|
||||
"Example MSC", // 4. MSC
|
||||
};
|
||||
/*********************************************************************** TinyUSB descriptors*/
|
||||
|
||||
#define VBUS_MONITORING_GPIO_NUM GPIO_NUM_4
|
||||
#define BASE_PATH "/data" // base path to mount the partition
|
||||
static bool is_mount = false;
|
||||
|
||||
#define PROMPT_STR CONFIG_IDF_TARGET
|
||||
static int f_unmount(int argc, char **argv);
|
||||
static int f_read(int argc, char **argv);
|
||||
static int f_write(int argc, char **argv);
|
||||
static int f_size(int argc, char **argv);
|
||||
static int f_status(int argc, char **argv);
|
||||
static int f_exit(int argc, char **argv);
|
||||
const esp_console_cmd_t cmds[] = {
|
||||
{
|
||||
.command = "read",
|
||||
.help = "read BASE_PATH/README.MD and print its contents",
|
||||
.hint = NULL,
|
||||
.func = &f_read,
|
||||
},
|
||||
{
|
||||
.command = "write",
|
||||
.help = "create file BASE_PATH/README.MD if it does not exist",
|
||||
.hint = NULL,
|
||||
.func = &f_write,
|
||||
},
|
||||
{
|
||||
.command = "size",
|
||||
.help = "show storage size and sector size",
|
||||
.hint = NULL,
|
||||
.func = &f_size,
|
||||
},
|
||||
{
|
||||
.command = "expose",
|
||||
.help = "Expose Storage to Host",
|
||||
.hint = NULL,
|
||||
.func = &f_unmount,
|
||||
},
|
||||
{
|
||||
.command = "status",
|
||||
.help = "Status of storage exposure over USB",
|
||||
.hint = NULL,
|
||||
.func = &f_status,
|
||||
},
|
||||
{
|
||||
.command = "exit",
|
||||
.help = "exit from application",
|
||||
.hint = NULL,
|
||||
.func = &f_exit,
|
||||
}
|
||||
};
|
||||
|
||||
// mount the partition and show all the files in BASE_PATH
|
||||
static void _mount(void)
|
||||
{
|
||||
@ -341,9 +388,9 @@ static int f_size(int argc, char **argv)
|
||||
ESP_LOGE(TAG, "storage exposed over USB. Application can't access storage");
|
||||
return -1;
|
||||
}
|
||||
size_t size = storage_get_size();
|
||||
size_t sec_size = storage_get_sector_size();
|
||||
printf("storage size(%d), sec_size(%d)\n", size, sec_size);
|
||||
uint32_t sec_count = storage_get_sector_count();
|
||||
uint32_t sec_size = storage_get_sector_size();
|
||||
printf("Storage Capacity %lluMB\n", ((uint64_t) sec_count) * sec_size / (1024 * 1024));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -364,7 +411,7 @@ static int f_exit(int argc, char **argv)
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Configure GPIO Pin for vbus monitorung
|
||||
// Configure GPIO Pin for vbus monitoring
|
||||
const gpio_config_t vbus_gpio_config = {
|
||||
.pin_bit_mask = BIT64(VBUS_MONITORING_GPIO_NUM),
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
@ -375,7 +422,61 @@ void app_main(void)
|
||||
ESP_ERROR_CHECK(gpio_config(&vbus_gpio_config));
|
||||
|
||||
ESP_LOGI(TAG, "Initializing storage...");
|
||||
ESP_ERROR_CHECK(storage_init());
|
||||
tinyusb_config_storage_t storage_config;
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH
|
||||
// configuration is to defined in case of SPI Flash
|
||||
storage_config.storage_type = TINYUSB_STORAGE_SPI;
|
||||
tinyusb_config_spiffs_t config_spi;
|
||||
storage_config.spiffs_config = &config_spi;
|
||||
#else // CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH
|
||||
storage_config.storage_type = TINYUSB_STORAGE_SDMMC;
|
||||
tinyusb_config_sdmmc_t config_sdmmc;
|
||||
|
||||
// By default, SD card frequency is initialized to SDMMC_FREQ_DEFAULT (20MHz)
|
||||
// For setting a specific frequency, use host.max_freq_khz (range 400kHz - 40MHz for SDMMC)
|
||||
// Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000;
|
||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||
|
||||
// This initializes the slot without card detect (CD) and write protect (WP) signals.
|
||||
// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
|
||||
// For SD Card, set bus width to use
|
||||
#ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
|
||||
slot_config.width = 4;
|
||||
#else
|
||||
slot_config.width = 1;
|
||||
#endif // CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
|
||||
|
||||
// On chips where the GPIOs used for SD card can be configured, set the user defined values
|
||||
#ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
|
||||
slot_config.clk = CONFIG_EXAMPLE_PIN_CLK;
|
||||
slot_config.cmd = CONFIG_EXAMPLE_PIN_CMD;
|
||||
slot_config.d0 = CONFIG_EXAMPLE_PIN_D0;
|
||||
#ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
|
||||
slot_config.d1 = CONFIG_EXAMPLE_PIN_D1;
|
||||
slot_config.d2 = CONFIG_EXAMPLE_PIN_D2;
|
||||
slot_config.d3 = CONFIG_EXAMPLE_PIN_D3;
|
||||
#endif // CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
|
||||
|
||||
#endif // CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
|
||||
|
||||
// Enable internal pullups on enabled pins. The internal pullups
|
||||
// are insufficient however, please make sure 10k external pullups are
|
||||
// connected on the bus. This is for debug / example purpose only.
|
||||
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
|
||||
|
||||
config_sdmmc.host = host;
|
||||
config_sdmmc.slot_config = slot_config;
|
||||
storage_config.sdmmc_config = &config_sdmmc;
|
||||
|
||||
#endif // CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH
|
||||
|
||||
ESP_ERROR_CHECK(storage_init(&storage_config));
|
||||
|
||||
//mounted in the app by default
|
||||
_mount();
|
||||
|
||||
ESP_LOGI(TAG, "USB MSC initialization");
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
@ -389,9 +490,6 @@ void app_main(void)
|
||||
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
|
||||
ESP_LOGI(TAG, "USB MSC initialization DONE");
|
||||
|
||||
//mounted in the app by default
|
||||
_mount();
|
||||
|
||||
esp_console_repl_t *repl = NULL;
|
||||
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
|
||||
/* Prompt to be printed before each line.
|
||||
@ -402,54 +500,9 @@ void app_main(void)
|
||||
esp_console_register_help_command();
|
||||
esp_console_dev_uart_config_t hw_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl));
|
||||
|
||||
const esp_console_cmd_t cmd_read = {
|
||||
.command = "read",
|
||||
.help = "read BASE_PATH/README.MD and print its contents",
|
||||
.hint = NULL,
|
||||
.func = &f_read,
|
||||
};
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_read) );
|
||||
|
||||
const esp_console_cmd_t cmd_write = {
|
||||
.command = "write",
|
||||
.help = "create file BASE_PATH/README.MD if it does not exist",
|
||||
.hint = NULL,
|
||||
.func = &f_write,
|
||||
};
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_write) );
|
||||
|
||||
const esp_console_cmd_t cmd_size = {
|
||||
.command = "size",
|
||||
.help = "show storage size and sector size",
|
||||
.hint = NULL,
|
||||
.func = &f_size,
|
||||
};
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_size) );
|
||||
|
||||
const esp_console_cmd_t cmd_umount = {
|
||||
.command = "expose",
|
||||
.help = "Expose Storage to Host",
|
||||
.hint = NULL,
|
||||
.func = &f_unmount,
|
||||
};
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_umount) );
|
||||
|
||||
const esp_console_cmd_t cmd_status = {
|
||||
.command = "status",
|
||||
.help = "Status of storage exposure over USB",
|
||||
.hint = NULL,
|
||||
.func = &f_status,
|
||||
};
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_status) );
|
||||
|
||||
const esp_console_cmd_t cmd_exit = {
|
||||
.command = "exit",
|
||||
.help = "exit from application",
|
||||
.hint = NULL,
|
||||
.func = &f_exit,
|
||||
};
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd_exit) );
|
||||
|
||||
for (int count = 0; count < sizeof(cmds) / sizeof(esp_console_cmd_t); count++) {
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmds[count]) );
|
||||
}
|
||||
ESP_ERROR_CHECK(esp_console_start_repl(repl));
|
||||
}
|
||||
/*********************************************************************** Application Code*/
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@ -12,25 +12,73 @@ extern "C" {
|
||||
|
||||
#include <stddef.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
|
||||
/* structs, unions
|
||||
********************************************************************* */
|
||||
|
||||
/**
|
||||
* @brief enum for storage media that are supported
|
||||
*/
|
||||
typedef enum {
|
||||
TINYUSB_STORAGE_INVALID,
|
||||
TINYUSB_STORAGE_SPI,
|
||||
TINYUSB_STORAGE_SDMMC,
|
||||
TINYUSB_STORAGE_MAX,
|
||||
} tinyusb_storage_type_t;
|
||||
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
/**
|
||||
* @brief Configuration structure for sdmmc initialization
|
||||
*
|
||||
* User configurable parameters that are used while
|
||||
* initializing the sdmmc media.
|
||||
*/
|
||||
typedef struct {
|
||||
sdmmc_host_t host;
|
||||
sdmmc_slot_config_t slot_config;
|
||||
} tinyusb_config_sdmmc_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configuration structure for spiffs initialization
|
||||
*
|
||||
* User configurable parameters that are used while
|
||||
* initializing the SPI Flash media.
|
||||
*/
|
||||
typedef struct {
|
||||
// Place holder. Currently, nothing to configure
|
||||
} tinyusb_config_spiffs_t;
|
||||
|
||||
/**
|
||||
* @brief Configuration structure for storage initialization
|
||||
*/
|
||||
typedef struct {
|
||||
tinyusb_storage_type_t storage_type;
|
||||
union {
|
||||
tinyusb_config_spiffs_t *spiffs_config;
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
tinyusb_config_sdmmc_t *sdmmc_config;
|
||||
#endif
|
||||
};
|
||||
} tinyusb_config_storage_t;
|
||||
/*********************************************************************** structs, unions */
|
||||
/* Public functions
|
||||
********************************************************************* */
|
||||
|
||||
/**
|
||||
* @brief Initialize the Storage.
|
||||
*
|
||||
* First find 'first partition' based on one or more parameters (ie DATA, FAT).
|
||||
* Mount WL for defined partition.
|
||||
* Initialize the instance of WL instance.
|
||||
* Initialize the instance of storage media.
|
||||
* Once the storage is initialized, other storage functions can be used.
|
||||
*
|
||||
* @param config pointer to tinyusb_config_storage_t to pass
|
||||
* user defined configuration during initialization.
|
||||
* @return esp_err_t
|
||||
* - ESP_OK, if success;
|
||||
* - ESP_ERR_NOT_FOUND, if Failed to find FATFS partition;
|
||||
* - ESP_ERR_INVALID_ARG, if WL allocation was unsuccessful;
|
||||
* - ESP_ERR_NO_MEM, if there was no memory to allocate WL components;
|
||||
* - ESP_ERR_NO_MEM, if there was no memory to allocate storage components;
|
||||
*/
|
||||
esp_err_t storage_init(void);
|
||||
esp_err_t storage_init(const tinyusb_config_storage_t *config);
|
||||
|
||||
/**
|
||||
* @brief Mount the storage partition locally on the firmware application.
|
||||
@ -44,6 +92,7 @@ esp_err_t storage_init(void);
|
||||
* @param base_path path prefix where FATFS should be registered
|
||||
* @return esp_err_t
|
||||
* - ESP_OK, if success;
|
||||
* - ESP_ERR_NOT_FOUND if the maximum count of volumes is already mounted
|
||||
* - ESP_ERR_NO_MEM if not enough memory or too many VFSes already registered;
|
||||
*/
|
||||
esp_err_t storage_mount(const char *base_path);
|
||||
@ -54,7 +103,7 @@ esp_err_t storage_mount(const char *base_path);
|
||||
* Unmount the partition. Unregister diskio driver.
|
||||
* Unregister the SPI flash partition.
|
||||
* Finally, Un-register FATFS from VFS.
|
||||
* After this function is called, storage device can be seen (recongnized) by host (PC).
|
||||
* After this function is called, storage device can be seen (recognized) by host (PC).
|
||||
*
|
||||
* @return esp_err_t
|
||||
* - ESP_OK on success
|
||||
@ -63,24 +112,24 @@ esp_err_t storage_mount(const char *base_path);
|
||||
esp_err_t storage_unmount(void);
|
||||
|
||||
/**
|
||||
* @brief Get size of the WL storage
|
||||
* @brief Get number of sectors in storage media
|
||||
*
|
||||
* @return usable size, in bytes
|
||||
*/
|
||||
size_t storage_get_size(void);
|
||||
uint32_t storage_get_sector_count(void);
|
||||
|
||||
/**
|
||||
* @brief Get sector size of the WL instance
|
||||
* @brief Get sector size of storage media
|
||||
*
|
||||
* @return sector size, in bytes
|
||||
* @return sector count
|
||||
*/
|
||||
size_t storage_get_sector_size(void);
|
||||
uint32_t storage_get_sector_size(void);
|
||||
|
||||
/**
|
||||
* @brief Read data from the WL storage
|
||||
* @brief Read data from the storage
|
||||
*
|
||||
* @param addr Address of the data to be read, relative to the
|
||||
* beginning of the partition.
|
||||
* @param lba Logical block address of the location of block
|
||||
* @param offset offset within the lba
|
||||
* @param size Size of data to be read, in bytes.
|
||||
* @param dest Pointer to the buffer where data should be stored.
|
||||
* Pointer must be non-NULL and buffer must be at least 'size' bytes long.
|
||||
@ -90,16 +139,15 @@ size_t storage_get_sector_size(void);
|
||||
* - ESP_ERR_INVALID_SIZE, if read would go out of bounds of the partition;
|
||||
* - or one of error codes from lower-level flash driver.
|
||||
*/
|
||||
esp_err_t storage_read_sector(size_t addr, size_t size, void *dest);
|
||||
esp_err_t storage_read_sector(uint32_t lba, uint32_t offset, size_t size, void *dest);
|
||||
|
||||
/**
|
||||
* @brief Write data to the WL storage
|
||||
* @brief Write data to the storage
|
||||
*
|
||||
* Before writing data to flash, corresponding region of flash needs to be erased.
|
||||
* This is done internally using wl_erase_range function.
|
||||
*
|
||||
* @param addr Address where the data should be written, relative to the
|
||||
* beginning of the partition.
|
||||
* @param lba Logical block address of the location of block
|
||||
* @param offset offset within the lba
|
||||
* @param size Size of data to be written, in bytes.
|
||||
* @param src Pointer to the source buffer. Pointer must be non-NULL and
|
||||
* buffer must be at least 'size' bytes long.
|
||||
@ -109,7 +157,7 @@ esp_err_t storage_read_sector(size_t addr, size_t size, void *dest);
|
||||
* - ESP_ERR_INVALID_SIZE, if write would go out of bounds of the partition;
|
||||
* - or one of error codes from lower-level flash driver.
|
||||
*/
|
||||
esp_err_t storage_write_sector(size_t addr, size_t size, const void *src);
|
||||
esp_err_t storage_write_sector(uint32_t lba, uint32_t offset, size_t size, const void *src);
|
||||
|
||||
/*********************************************************************** Public functions*/
|
||||
|
||||
|
@ -0,0 +1,276 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
// DESCRIPTION:
|
||||
// This file contains the code for accessing the storage medium in SD card.
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "diskio_impl.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
#include "diskio_sdmmc.h"
|
||||
#include "tusb_msc_storage.h"
|
||||
|
||||
static sdmmc_card_t *s_card;
|
||||
static bool s_fat_mounted;
|
||||
static const char *s_base_path;
|
||||
|
||||
static const char *TAG = "msc_sdmmc";
|
||||
|
||||
esp_err_t storage_init(const tinyusb_config_storage_t *config)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
bool host_init = false;
|
||||
assert(config->storage_type == TINYUSB_STORAGE_SDMMC);
|
||||
|
||||
ESP_LOGI(TAG, "Initializing SDCard");
|
||||
|
||||
sdmmc_host_t host = config->sdmmc_config->host;
|
||||
sdmmc_slot_config_t slot_config = config->sdmmc_config->slot_config;
|
||||
|
||||
// not using ff_memalloc here, as allocation in internal RAM is preferred
|
||||
s_card = (sdmmc_card_t *)malloc(sizeof(sdmmc_card_t));
|
||||
ESP_GOTO_ON_FALSE(s_card, ESP_ERR_NO_MEM, clean, TAG, "could not allocate new sdmmc_card_t");
|
||||
|
||||
ESP_GOTO_ON_ERROR((*host.init)(), clean, TAG, "Host Config Init fail");
|
||||
host_init = true;
|
||||
|
||||
ESP_GOTO_ON_ERROR(sdmmc_host_init_slot(host.slot, (const sdmmc_slot_config_t *) &slot_config),
|
||||
clean, TAG, "Host init slot fail");
|
||||
|
||||
while (sdmmc_card_init(&host, s_card)) {
|
||||
ESP_LOGE(TAG, "The detection pin of the slot is disconnected(Insert uSD card). Retrying...");
|
||||
vTaskDelay(pdMS_TO_TICKS(3000));
|
||||
}
|
||||
|
||||
// Card has been initialized, print its properties
|
||||
sdmmc_card_print_info(stdout, s_card);
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
clean:
|
||||
if (host_init) {
|
||||
if (host.flags & SDMMC_HOST_FLAG_DEINIT_ARG) {
|
||||
host.deinit_p(host.slot);
|
||||
} else {
|
||||
(*host.deinit)();
|
||||
}
|
||||
}
|
||||
if (s_card) {
|
||||
free(s_card);
|
||||
s_card = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline size_t esp_vfs_fat_get_allocation_unit_size(
|
||||
size_t sector_size, size_t requested_size)
|
||||
{
|
||||
size_t alloc_unit_size = requested_size;
|
||||
const size_t max_sectors_per_cylinder = 128;
|
||||
const size_t max_size = sector_size * max_sectors_per_cylinder;
|
||||
alloc_unit_size = MAX(alloc_unit_size, sector_size);
|
||||
alloc_unit_size = MIN(alloc_unit_size, max_size);
|
||||
return alloc_unit_size;
|
||||
}
|
||||
|
||||
static esp_err_t partition_card(const esp_vfs_fat_mount_config_t *mount_config,
|
||||
const char *drv, sdmmc_card_t *card, BYTE pdrv)
|
||||
{
|
||||
FRESULT res = FR_OK;
|
||||
esp_err_t err;
|
||||
const size_t workbuf_size = 4096;
|
||||
void *workbuf = NULL;
|
||||
ESP_LOGW(TAG, "partitioning card");
|
||||
|
||||
workbuf = ff_memalloc(workbuf_size);
|
||||
if (workbuf == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
LBA_t plist[] = {100, 0, 0, 0};
|
||||
res = f_fdisk(pdrv, plist, workbuf);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_fdisk failed (%d)", res);
|
||||
goto fail;
|
||||
}
|
||||
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(
|
||||
card->csd.sector_size,
|
||||
mount_config->allocation_unit_size);
|
||||
ESP_LOGW(TAG, "formatting card, allocation unit size=%d", alloc_unit_size);
|
||||
const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, alloc_unit_size};
|
||||
res = f_mkfs(drv, &opt, workbuf, workbuf_size);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_mkfs failed (%d)", res);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
free(workbuf);
|
||||
return ESP_OK;
|
||||
fail:
|
||||
free(workbuf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t mount_to_vfs_fat(const esp_vfs_fat_mount_config_t *mount_config, sdmmc_card_t *card, uint8_t pdrv,
|
||||
const char *base_path)
|
||||
{
|
||||
FATFS *fs = NULL;
|
||||
esp_err_t err;
|
||||
ff_diskio_register_sdmmc(pdrv, card);
|
||||
ff_sdmmc_set_disk_status_check(pdrv, mount_config->disk_status_check_enable);
|
||||
ESP_LOGD(TAG, "using pdrv=%i", pdrv);
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
// connect FATFS to VFS
|
||||
err = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs);
|
||||
if (err == ESP_ERR_INVALID_STATE) {
|
||||
// it's okay, already registered with VFS
|
||||
} else if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Try to mount partition
|
||||
FRESULT res = f_mount(fs, drv, 1);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGW(TAG, "failed to mount card (%d)", res);
|
||||
if (!((res == FR_NO_FILESYSTEM || res == FR_INT_ERR)
|
||||
&& mount_config->format_if_mount_failed)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = partition_card(mount_config, drv, card, pdrv);
|
||||
if (err != ESP_OK) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ESP_LOGW(TAG, "mounting again");
|
||||
res = f_mount(fs, drv, 0);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_mount failed after formatting (%d)", res);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
|
||||
fail:
|
||||
if (fs) {
|
||||
f_mount(NULL, drv, 0);
|
||||
}
|
||||
esp_vfs_fat_unregister_path(base_path);
|
||||
ff_diskio_unregister(pdrv);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t storage_mount(const char *base_path)
|
||||
{
|
||||
if (s_fat_mounted) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Initializing FAT");
|
||||
|
||||
// Options for mounting the filesystem.
|
||||
// If format_if_mount_failed is set to true, SD card will be partitioned and
|
||||
// formatted in case when mounting fails.
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = true,
|
||||
.max_files = 5,
|
||||
.allocation_unit_size = 16 * 1024
|
||||
};
|
||||
|
||||
// connect driver to FATFS
|
||||
BYTE pdrv = 0xFF;
|
||||
ESP_RETURN_ON_ERROR(ff_diskio_get_drive(&pdrv), TAG,
|
||||
"The maximum count of volumes is already mounted");
|
||||
|
||||
ESP_LOGI(TAG, "using pdrv=%i", pdrv);
|
||||
|
||||
ESP_RETURN_ON_ERROR(mount_to_vfs_fat(&mount_config, s_card, pdrv, base_path), TAG,
|
||||
"Failed to mount storage");
|
||||
|
||||
s_fat_mounted = true;
|
||||
s_base_path = base_path;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t unmount_card_core(const char *base_path, sdmmc_card_t *card)
|
||||
{
|
||||
BYTE pdrv = ff_diskio_get_pdrv_card(card);
|
||||
if (pdrv == 0xff) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// unmount
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
f_mount(0, drv, 0);
|
||||
// release SD driver
|
||||
ff_diskio_unregister(pdrv);
|
||||
|
||||
return esp_vfs_fat_unregister_path(base_path);
|
||||
}
|
||||
|
||||
esp_err_t storage_unmount(void)
|
||||
{
|
||||
if (!s_fat_mounted) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t err = unmount_card_core(s_base_path, s_card);
|
||||
|
||||
s_base_path = NULL;
|
||||
s_fat_mounted = false;
|
||||
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
uint32_t storage_get_sector_count(void)
|
||||
{
|
||||
assert(s_card);
|
||||
|
||||
return (uint32_t)s_card->csd.capacity;
|
||||
}
|
||||
|
||||
uint32_t storage_get_sector_size(void)
|
||||
{
|
||||
assert(s_card);
|
||||
|
||||
return (uint32_t)s_card->csd.sector_size;
|
||||
}
|
||||
|
||||
esp_err_t storage_read_sector(uint32_t lba, uint32_t offset, size_t size, void *dest)
|
||||
{
|
||||
assert(s_card);
|
||||
return sdmmc_read_sectors(s_card, dest, lba, size / storage_get_sector_size());
|
||||
}
|
||||
|
||||
esp_err_t storage_write_sector(uint32_t lba, uint32_t offset, size_t size, const void *src)
|
||||
{
|
||||
assert(s_card);
|
||||
|
||||
if (s_fat_mounted) {
|
||||
ESP_LOGE(TAG, "can't write, FAT mounted");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
size_t addr = lba * storage_get_sector_size() + offset; // Address of the data to be read, relative to the beginning of the partition.
|
||||
size_t sector_size = s_card->csd.sector_size;
|
||||
if (addr % sector_size != 0 || size % sector_size != 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_ERROR(sdmmc_erase_sectors(s_card, lba, size / storage_get_sector_size(), SDMMC_ERASE_ARG),
|
||||
TAG, "Failed to erase");
|
||||
|
||||
return sdmmc_write_sectors(s_card, src, lba, size / storage_get_sector_size());
|
||||
}
|
@ -1,30 +1,32 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
// DESCRIPTION:
|
||||
// This file contains the code for accessing the storage medium ie SPI Flash.
|
||||
// This file contains the code for accessing the storage medium i.e. SPI Flash.
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "diskio_impl.h"
|
||||
#include "diskio_wl.h"
|
||||
#include "wear_levelling.h"
|
||||
#include "esp_partition.h"
|
||||
#include "tusb_msc_storage.h"
|
||||
|
||||
static wl_handle_t s_wl_handle = WL_INVALID_HANDLE;
|
||||
static bool s_fat_mounted;
|
||||
static const char *s_base_path;
|
||||
|
||||
static const char *TAG = "example_msc_storage";
|
||||
static const char *TAG = "msc_spiflash";
|
||||
|
||||
esp_err_t storage_init(void)
|
||||
esp_err_t storage_init(const tinyusb_config_storage_t *config)
|
||||
{
|
||||
assert(config->storage_type == TINYUSB_STORAGE_SPI);
|
||||
ESP_LOGI(TAG, "Initializing wear levelling");
|
||||
esp_err_t err;
|
||||
|
||||
const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL);
|
||||
if (data_partition == NULL) {
|
||||
@ -32,13 +34,7 @@ esp_err_t storage_init(void)
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
err = wl_mount(data_partition, &s_wl_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "failed to mount wear levelling layer (0x%x)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
return wl_mount(data_partition, &s_wl_handle);
|
||||
}
|
||||
|
||||
static inline size_t esp_vfs_fat_get_allocation_unit_size(
|
||||
@ -56,7 +52,7 @@ esp_err_t storage_mount(const char *base_path)
|
||||
{
|
||||
const size_t workbuf_size = 4096;
|
||||
void *workbuf = NULL;
|
||||
esp_err_t err;
|
||||
esp_err_t ret;
|
||||
|
||||
if (s_fat_mounted) {
|
||||
return ESP_OK;
|
||||
@ -66,24 +62,20 @@ esp_err_t storage_mount(const char *base_path)
|
||||
|
||||
// connect driver to FATFS
|
||||
BYTE pdrv = 0xFF;
|
||||
if (ff_diskio_get_drive(&pdrv) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "the maximum count of volumes is already mounted");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(ff_diskio_get_drive(&pdrv), TAG,
|
||||
"The maximum count of volumes is already mounted");
|
||||
ESP_LOGD(TAG, "using pdrv=%i", pdrv);
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
err = ff_diskio_register_wl_partition(pdrv, s_wl_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ff_diskio_register_wl_partition failed pdrv=%d (0x%x)", pdrv, err);
|
||||
goto fail;
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(ff_diskio_register_wl_partition(pdrv, s_wl_handle),
|
||||
fail, TAG, "Failed pdrv=%d", pdrv);
|
||||
|
||||
FATFS *fs;
|
||||
err = esp_vfs_fat_register(base_path, drv, 2, &fs);
|
||||
if (err == ESP_ERR_INVALID_STATE) {
|
||||
ret = esp_vfs_fat_register(base_path, drv, 2, &fs);
|
||||
if (ret == ESP_ERR_INVALID_STATE) {
|
||||
// it's okay, already registered with VFS
|
||||
} else if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_vfs_fat_register failed (0x%x)", err);
|
||||
} else if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_vfs_fat_register failed (0x%x)", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -92,12 +84,12 @@ esp_err_t storage_mount(const char *base_path)
|
||||
if (fresult != FR_OK) {
|
||||
ESP_LOGW(TAG, "f_mount failed (%d)", fresult);
|
||||
if (!((fresult == FR_NO_FILESYSTEM || fresult == FR_INT_ERR))) {
|
||||
err = ESP_FAIL;
|
||||
ret = ESP_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
workbuf = ff_memalloc(workbuf_size);
|
||||
if (workbuf == NULL) {
|
||||
err = ESP_ERR_NO_MEM;
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto fail;
|
||||
}
|
||||
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(
|
||||
@ -108,7 +100,7 @@ esp_err_t storage_mount(const char *base_path)
|
||||
const MKFS_PARM opt = {(BYTE)FM_FAT, 0, 0, 0, 0};
|
||||
fresult = f_mkfs("", &opt, workbuf, workbuf_size); // Use default volume
|
||||
if (fresult != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ret = ESP_FAIL;
|
||||
ESP_LOGE(TAG, "f_mkfs failed (%d)", fresult);
|
||||
goto fail;
|
||||
}
|
||||
@ -117,7 +109,7 @@ esp_err_t storage_mount(const char *base_path)
|
||||
ESP_LOGI(TAG, "Mounting again");
|
||||
fresult = f_mount(fs, drv, 0);
|
||||
if (fresult != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ret = ESP_FAIL;
|
||||
ESP_LOGE(TAG, "f_mount failed after formatting (%d)", fresult);
|
||||
goto fail;
|
||||
}
|
||||
@ -132,8 +124,8 @@ fail:
|
||||
esp_vfs_fat_unregister_path(base_path);
|
||||
ff_diskio_unregister(pdrv);
|
||||
s_fat_mounted = false;
|
||||
ESP_LOGW(TAG, "Failed to mount storage (0x%x)", err);
|
||||
return err;
|
||||
ESP_LOGW(TAG, "Failed to mount storage (0x%x)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t storage_unmount(void)
|
||||
@ -159,31 +151,36 @@ esp_err_t storage_unmount(void)
|
||||
|
||||
}
|
||||
|
||||
size_t storage_get_size(void)
|
||||
uint32_t storage_get_sector_count(void)
|
||||
{
|
||||
assert(s_wl_handle != WL_INVALID_HANDLE);
|
||||
|
||||
return wl_size(s_wl_handle);
|
||||
size_t size = wl_sector_size(s_wl_handle);
|
||||
if (size == 0) {
|
||||
ESP_LOGW(TAG, "WL Sector size is zero !!!");
|
||||
return 0;
|
||||
}
|
||||
return (uint32_t)(wl_size(s_wl_handle) / size);
|
||||
}
|
||||
|
||||
size_t storage_get_sector_size(void)
|
||||
uint32_t storage_get_sector_size(void)
|
||||
{
|
||||
assert(s_wl_handle != WL_INVALID_HANDLE);
|
||||
|
||||
return wl_sector_size(s_wl_handle);
|
||||
return (uint32_t)wl_sector_size(s_wl_handle);
|
||||
}
|
||||
|
||||
esp_err_t storage_read_sector(size_t addr, size_t size, void *dest)
|
||||
esp_err_t storage_read_sector(uint32_t lba, uint32_t offset, size_t size, void *dest)
|
||||
{
|
||||
assert(s_wl_handle != WL_INVALID_HANDLE);
|
||||
|
||||
size_t addr = lba * storage_get_sector_size() + offset; // Address of the data to be read, relative to the beginning of the partition.
|
||||
return wl_read(s_wl_handle, addr, dest, size);
|
||||
}
|
||||
|
||||
esp_err_t storage_write_sector(size_t addr, size_t size, const void *src)
|
||||
esp_err_t storage_write_sector(uint32_t lba, uint32_t offset, size_t size, const void *src)
|
||||
{
|
||||
assert(s_wl_handle != WL_INVALID_HANDLE);
|
||||
|
||||
size_t addr = lba * storage_get_sector_size() + offset; // Address of the data to be read, relative to the beginning of the partition.
|
||||
if (s_fat_mounted) {
|
||||
ESP_LOGE(TAG, "can't write, FAT mounted");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
@ -192,10 +189,8 @@ esp_err_t storage_write_sector(size_t addr, size_t size, const void *src)
|
||||
if (addr % sector_size != 0 || size % sector_size != 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_err_t err = wl_erase_range(s_wl_handle, addr, size);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "wl_erase_range failed (0x%x)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_ERROR(wl_erase_range(s_wl_handle, addr, size),
|
||||
TAG, "Failed to erase");
|
||||
return wl_write(s_wl_handle, addr, src, size);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
@ -7,9 +7,9 @@ from pytest_embedded import Dut
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.usb_device
|
||||
def test_usb_device_msc_example(dut: Dut) -> None:
|
||||
dut.expect('USB MSC initialization DONE')
|
||||
dut.expect('Mount storage')
|
||||
dut.expect('Initializing FAT')
|
||||
dut.expect('USB MSC initialization DONE')
|
||||
dut.write(' help')
|
||||
dut.expect('read')
|
||||
dut.expect('write')
|
||||
|
@ -0,0 +1,2 @@
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
CONFIG_EXAMPLE_STORAGE_MEDIA_SDMMCCARD=y
|
Loading…
x
Reference in New Issue
Block a user