mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
usb: modify example according to newly implemented usbd msc driver
This commit is contained in:
parent
9f2012156c
commit
55914d0c12
@ -139,67 +139,66 @@ To specify callbacks you can either set the pointer to your :cpp:type:`tusb_cdca
|
||||
USB Serial Console
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The driver allows to redirect all standard application streams (stdinm stdout, stderr) to the USB Serial Device and return them to UART using :cpp:func:`esp_tusb_init_console`/:cpp:func:`esp_tusb_deinit_console` functions.
|
||||
The driver allows to redirect all standard application streams (stdin, stdout, stderr) to the USB Serial Device and return them to UART using :cpp:func:`esp_tusb_init_console`/:cpp:func:`esp_tusb_deinit_console` functions.
|
||||
|
||||
USB Mass Storage Device (MSC)
|
||||
-----------------------------
|
||||
|
||||
If the MSC CONFIG_TINYUSB_MSC_ENABLED option is enabled, the USB MSC Device can be initialized as shown below (see example below).
|
||||
If the MSC CONFIG_TINYUSB_MSC_ENABLED option is enabled in Menuconfig, the ESP Chip can be used as USB MSC Device. The storage media (spi-flash or sd-card) can be initialized as shown below (see example below).
|
||||
|
||||
- SPI-Flash
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static uint8_t const desc_configuration[] = {
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
|
||||
static esp_err_t storage_init_spiflash(wl_handle_t *wl_handle)
|
||||
{
|
||||
***
|
||||
esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL);
|
||||
***
|
||||
wl_mount(data_partition, wl_handle);
|
||||
***
|
||||
}
|
||||
storage_init_spiflash(&wl_handle);
|
||||
|
||||
// Interface number, string index, EP Out & EP In address, EP size
|
||||
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, TUD_OPT_HIGH_SPEED ? 512 : 64),
|
||||
const tinyusb_msc_spiflash_config_t config_spi = {
|
||||
.wl_handle = wl_handle
|
||||
};
|
||||
tinyusb_msc_storage_init_spiflash(&config_spi);
|
||||
|
||||
static tusb_desc_device_t descriptor_config = {
|
||||
.bLength = sizeof(descriptor_config),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.idVendor = 0x303A,
|
||||
.idProduct = 0x4002,
|
||||
.bcdDevice = 0x100,
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
|
||||
static char const *string_desc_arr[] = {
|
||||
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
|
||||
"TinyUSB", // 1: Manufacturer
|
||||
"TinyUSB Device", // 2: Product
|
||||
"123456", // 3: Serials
|
||||
"Example MSC", // 4. MSC
|
||||
};
|
||||
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.device_descriptor = &descriptor_config,
|
||||
.string_descriptor = string_desc_arr,
|
||||
.external_phy = false,
|
||||
.configuration_descriptor = desc_configuration,
|
||||
};
|
||||
tinyusb_driver_install(&tusb_cfg);
|
||||
|
||||
The mandatory callbacks that are required to be implemented are
|
||||
- SD-Card
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
|
||||
bool tud_msc_test_unit_ready_cb(uint8_t lun)
|
||||
void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size)
|
||||
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
|
||||
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize)
|
||||
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize)
|
||||
int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize)
|
||||
static esp_err_t storage_init_sdmmc(sdmmc_card_t **card)
|
||||
{
|
||||
***
|
||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
// For SD Card, set bus width to use
|
||||
|
||||
slot_config.width = 4;
|
||||
slot_config.clk = CONFIG_EXAMPLE_PIN_CLK;
|
||||
slot_config.cmd = CONFIG_EXAMPLE_PIN_CMD;
|
||||
slot_config.d0 = CONFIG_EXAMPLE_PIN_D0;
|
||||
slot_config.d1 = CONFIG_EXAMPLE_PIN_D1;
|
||||
slot_config.d2 = CONFIG_EXAMPLE_PIN_D2;
|
||||
slot_config.d3 = CONFIG_EXAMPLE_PIN_D3;
|
||||
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
|
||||
|
||||
sd_card = (sdmmc_card_t *)malloc(sizeof(sdmmc_card_t));
|
||||
(*host.init)();
|
||||
sdmmc_host_init_slot(host.slot, (const sdmmc_slot_config_t *) &slot_config);
|
||||
sdmmc_card_init(&host, sd_card);
|
||||
***
|
||||
}
|
||||
storage_init_sdmmc(&card);
|
||||
|
||||
const tinyusb_msc_sdmmc_config_t config_sdmmc = {
|
||||
.card = card
|
||||
};
|
||||
tinyusb_msc_storage_init_sdmmc(&config_sdmmc);
|
||||
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
@ -25,7 +25,7 @@ As a USB stack, a TinyUSB component is used.
|
||||
- Result: Host PC can't access the partition over USB MSC. Application example can perform operations (read, write) on partition.
|
||||
2. USB which accesses the ESP MSC Partition is already plugged-in at boot time.
|
||||
- Result: Host PC recongnize it as removable device and can access the partition over USB MSC. Application example can't perform any operation on partition.
|
||||
3. USB which accesses the ESP MSC Partition is plugged-in at boo-up. After boot-up, it is ejected on Host PC manually by user.
|
||||
3. USB which accesses the ESP MSC Partition is plugged-in at boot-up. After boot-up, it is ejected on Host PC manually by user.
|
||||
- Result: Host PC can't access the partition over USB MSC. Application example can perform operations (read, write) on partition.
|
||||
4. USB which accesses the ESP MSC Partition is plugged-in at boot-up. It is then unplugged(removed) from Host PC manually by user.
|
||||
- Result: The behaviour is different for bus-powered devices and self-powered devices
|
||||
@ -75,7 +75,7 @@ Note that even if card's D3 line is not connected to the ESP chip, it still has
|
||||
|
||||
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"
|
||||
- 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.
|
||||
@ -97,15 +97,22 @@ See the Getting Started Guide for full steps to configure and use ESP-IDF to bui
|
||||
After the flashing you should see the output at idf monitor:
|
||||
|
||||
```
|
||||
I (311) cpu_start: Starting scheduler on PRO CPU.
|
||||
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:
|
||||
I (329) app_start: Starting scheduler on CPU0
|
||||
I (334) app_start: Starting scheduler on CPU1
|
||||
I (334) main_task: Started on CPU0
|
||||
I (344) main_task: Calling app_main()
|
||||
I (344) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (354) example_main: Initializing storage...
|
||||
I (364) example_main: Initializing wear levelling
|
||||
I (374) example_main: Mount storage...
|
||||
I (374) example_main:
|
||||
ls command output:
|
||||
.fseventsd
|
||||
_pic.jpg
|
||||
.__pic.jpg
|
||||
README.MD
|
||||
I (384) example_main: USB MSC initialization
|
||||
I (384) tusb_desc:
|
||||
┌─────────────────────────────────┐
|
||||
│ USB Device Descriptor Summary │
|
||||
├───────────────────┬─────────────┤
|
||||
@ -131,21 +138,13 @@ I (372) tusb_desc:
|
||||
├───────────────────┼─────────────┤
|
||||
│bNumConfigurations │ 0x1 │
|
||||
└───────────────────┴─────────────┘
|
||||
I (532) TinyUSB: TinyUSB Driver installed
|
||||
I (532) example_msc_main: USB MSC initialization DONE
|
||||
I (552) example_msc_main:
|
||||
ls command output:
|
||||
README.MD
|
||||
.fseventsd
|
||||
I (554) TinyUSB: TinyUSB Driver installed
|
||||
I (564) example_main: USB MSC initialization DONE
|
||||
|
||||
Type 'help' to get the list of commands.
|
||||
Use UP/DOWN arrows to navigate through command history.
|
||||
Press TAB when typing command name to auto-complete.
|
||||
esp32s3> I (912) example_msc_main: tud_mount_cb MSC START: Expose Over USB
|
||||
I (912) example_msc_main: Unmount storage...
|
||||
I (2032) example_msc_main: tud_msc_scsi_cb() invoked: SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
|
||||
I (2032) example_msc_main: tud_msc_capacity_cb() size(1024000), sec_size(512)
|
||||
esp32s3>
|
||||
I (724) main_task: Returned from app_main()
|
||||
esp32s3>
|
||||
esp32s3> help
|
||||
help
|
||||
@ -171,43 +170,32 @@ exit
|
||||
|
||||
esp32s3>
|
||||
esp32s3> read
|
||||
E (19102) example_msc_main: storage exposed over USB. Application can't read from storage.
|
||||
E (80054) example_main: storage exposed over USB. Application can't read from storage.
|
||||
Command returned non-zero error code: 0xffffffff (ESP_FAIL)
|
||||
esp32s3> write
|
||||
E (22412) example_msc_main: storage exposed over USB. Application can't write to storage.
|
||||
E (83134) example_main: storage exposed over USB. Application can't write to storage.
|
||||
Command returned non-zero error code: 0xffffffff (ESP_FAIL)
|
||||
esp32s3> size
|
||||
E (24962) example_msc_main: storage exposed over USB. Application can't access storage
|
||||
E (85354) example_main: storage exposed over USB. Application can't access storage
|
||||
Command returned non-zero error code: 0xffffffff (ESP_FAIL)
|
||||
esp32s3> status
|
||||
storage exposed over USB: Yes
|
||||
esp32s3> expose
|
||||
E (108344) example_main: storage is already exposed
|
||||
Command returned non-zero error code: 0xffffffff (ESP_FAIL)
|
||||
esp32s3>
|
||||
esp32s3>
|
||||
esp32s3> I (49692) example_msc_main: tud_msc_scsi_cb() invoked: SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
|
||||
I (49692) example_msc_main: tud_msc_start_stop_cb() invoked, power_condition=0, start=0, load_eject=1
|
||||
I (49702) example_msc_main: tud_msc_start_stop_cb: MSC EJECT: Mount on Example
|
||||
I (49712) example_msc_main: Mount storage...
|
||||
I (49712) example_msc_storage: Initializing FAT
|
||||
I (49712) example_msc_main:
|
||||
ls command output:
|
||||
README.MD
|
||||
esp32s3>
|
||||
esp32s3>
|
||||
esp32s3> status
|
||||
storage exposed over USB: No
|
||||
esp32s3> read
|
||||
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.
|
||||
In this example, ESP chip will be recognised by host (PC) as Mass Storage Device.
|
||||
Upon connection to USB host (PC), the example application will initialize the storage module and then the storage will be seen as removable device on PC.
|
||||
esp32s3> write
|
||||
esp32s3> size
|
||||
storage size(1024000), sec_size(512)
|
||||
esp32s3>
|
||||
Storage Capacity 0MB
|
||||
esp32s3> status
|
||||
storage exposed over USB: No
|
||||
esp32s3> expose
|
||||
I (76402) example_msc_main: Unmount storage...
|
||||
esp32s3> I (76772) example_msc_main: tud_msc_scsi_cb() invoked: SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
|
||||
I (76772) example_msc_main: tud_msc_capacity_cb() size(1024000), sec_size(512)
|
||||
esp32s3>
|
||||
I (181224) example_main: Unmount storage...
|
||||
esp32s3> status
|
||||
storage exposed over USB: Yes
|
||||
esp32s3>
|
||||
|
@ -2,12 +2,7 @@ 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")
|
||||
list(APPEND requires wear_levelling)
|
||||
endif()
|
||||
|
||||
idf_component_register(
|
||||
|
@ -1,4 +1,4 @@
|
||||
menu "USB DEV MSC Example Configuration"
|
||||
menu "USB Dev MSC Example Configuration"
|
||||
|
||||
choice EXAMPLE_STORAGE_MEDIA
|
||||
prompt "Storage Media Used"
|
||||
|
@ -1,4 +1,4 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_tinyusb: "^1.1"
|
||||
espressif/esp_tinyusb: "^1.2"
|
||||
idf: "^5.0"
|
||||
|
@ -11,182 +11,20 @@
|
||||
* For different scenarios and behaviour, Refer to README of this example.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_console.h"
|
||||
#include "tinyusb.h"
|
||||
#include "class/msc/msc_device.h"
|
||||
#include "tusb_msc_storage.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "tinyusb.h"
|
||||
#include "tusb_msc_storage.h"
|
||||
#ifdef CONFIG_EXAMPLE_STORAGE_MEDIA_SDMMCCARD
|
||||
#include "diskio_impl.h"
|
||||
#include "diskio_sdmmc.h"
|
||||
#endif
|
||||
|
||||
static const char *TAG = "example_main";
|
||||
|
||||
/* TinyUSB MSC callbacks
|
||||
********************************************************************* */
|
||||
|
||||
/** SCSI ASC/ASCQ codes. **/
|
||||
/** User can add and use more codes as per the need of the application **/
|
||||
#define SCSI_CODE_ASC_MEDIUM_NOT_PRESENT 0x3A /** SCSI ASC code for 'MEDIUM NOT PRESENT' **/
|
||||
#define SCSI_CODE_ASC_INVALID_COMMAND_OPERATION_CODE 0x20 /** SCSI ASC code for 'INVALID COMMAND OPERATION CODE' **/
|
||||
#define SCSI_CODE_ASCQ 0x00
|
||||
|
||||
static void _mount(void);
|
||||
static void _unmount(void);
|
||||
static bool is_eject = false;
|
||||
|
||||
// Invoked when received SCSI_CMD_INQUIRY
|
||||
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
|
||||
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
|
||||
{
|
||||
(void) lun;
|
||||
ESP_LOGD(TAG, "tud_msc_inquiry_cb() invoked");
|
||||
|
||||
const char vid[] = "TinyUSB";
|
||||
const char pid[] = "Flash Storage";
|
||||
const char rev[] = "0.1";
|
||||
|
||||
memcpy(vendor_id, vid, strlen(vid));
|
||||
memcpy(product_id, pid, strlen(pid));
|
||||
memcpy(product_rev, rev, strlen(rev));
|
||||
}
|
||||
|
||||
// Invoked when received Test Unit Ready command.
|
||||
// return true allowing host to read/write this LUN e.g SD card inserted
|
||||
bool tud_msc_test_unit_ready_cb(uint8_t lun)
|
||||
{
|
||||
(void) lun;
|
||||
ESP_LOGD(TAG, "tud_msc_test_unit_ready_cb() invoked");
|
||||
|
||||
if (is_eject) {
|
||||
tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, SCSI_CODE_ASC_MEDIUM_NOT_PRESENT, SCSI_CODE_ASCQ);
|
||||
return false;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "tud_msc_test_unit_ready_cb: MSC START: Expose Over USB");
|
||||
_unmount();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
|
||||
// Application update block count and block size
|
||||
void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size)
|
||||
{
|
||||
(void) lun;
|
||||
|
||||
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
|
||||
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
|
||||
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
|
||||
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
|
||||
{
|
||||
(void) lun;
|
||||
(void) power_condition;
|
||||
ESP_LOGI(TAG, "tud_msc_start_stop_cb() invoked, power_condition=%d, start=%d, load_eject=%d", power_condition, start, load_eject);
|
||||
|
||||
if (load_eject && !start) {
|
||||
is_eject = true;
|
||||
ESP_LOGI(TAG, "tud_msc_start_stop_cb: MSC EJECT: Mount on Example");
|
||||
_mount();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Invoked when received SCSI READ10 command
|
||||
// - Address = lba * BLOCK_SIZE + offset
|
||||
// - Application fill the buffer (up to bufsize) with address contents and return number of read byte.
|
||||
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize)
|
||||
{
|
||||
ESP_LOGD(TAG, "tud_msc_read10_cb() invoked, lun=%d, lba=%lu, offset=%lu, bufsize=%lu", lun, lba, offset, bufsize);
|
||||
|
||||
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;
|
||||
}
|
||||
return bufsize;
|
||||
}
|
||||
|
||||
// Invoked when received SCSI WRITE10 command
|
||||
// - Address = lba * BLOCK_SIZE + offset
|
||||
// - 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, bufsize=%lu", lun, lba, offset, bufsize);
|
||||
|
||||
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;
|
||||
}
|
||||
return bufsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when received an SCSI command not in built-in list below.
|
||||
* - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, TEST_UNIT_READY, START_STOP_UNIT, MODE_SENSE6, REQUEST_SENSE
|
||||
* - READ10 and WRITE10 has their own callbacks
|
||||
*
|
||||
* \param[in] lun Logical unit number
|
||||
* \param[in] scsi_cmd SCSI command contents which application must examine to response accordingly
|
||||
* \param[out] buffer Buffer for SCSI Data Stage.
|
||||
* - For INPUT: application must fill this with response.
|
||||
* - For OUTPUT it holds the Data from host
|
||||
* \param[in] bufsize Buffer's length.
|
||||
*
|
||||
* \return Actual bytes processed, can be zero for no-data command.
|
||||
* \retval negative Indicate error e.g unsupported command, tinyusb will \b STALL the corresponding
|
||||
* endpoint and return failed status in command status wrapper phase.
|
||||
*/
|
||||
int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize)
|
||||
{
|
||||
int32_t ret;
|
||||
|
||||
ESP_LOGD(TAG, "tud_msc_scsi_cb() invoked. bufsize=%d", bufsize);
|
||||
|
||||
switch (scsi_cmd[0]) {
|
||||
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
|
||||
/* SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL is the Prevent/Allow Medium Removal
|
||||
command (1Eh) that requests the library to enable or disable user access to
|
||||
the storage media/partition. */
|
||||
ESP_LOGI(TAG, "tud_msc_scsi_cb() invoked: SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL");
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "tud_msc_scsi_cb() invoked: %d", scsi_cmd[0]);
|
||||
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_CODE_ASC_INVALID_COMMAND_OPERATION_CODE, SCSI_CODE_ASCQ);
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Invoked when device is unmounted
|
||||
void tud_umount_cb(void)
|
||||
{
|
||||
is_eject = true;
|
||||
ESP_LOGI(TAG, "tud_umount_cb: Mount on Example");
|
||||
_mount();
|
||||
}
|
||||
|
||||
// Invoked when device is mounted (configured)
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "tud_mount_cb MSC START: Expose Over USB");
|
||||
_unmount();
|
||||
}
|
||||
/*********************************************************************** TinyUSB MSC callbacks*/
|
||||
|
||||
/* Application Code
|
||||
********************************************************************* */
|
||||
|
||||
/* TinyUSB descriptors
|
||||
********************************************************************* */
|
||||
#define EPNUM_MSC 1
|
||||
@ -239,53 +77,51 @@ static char const *string_desc_arr[] = {
|
||||
};
|
||||
/*********************************************************************** 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);
|
||||
static int console_unmount(int argc, char **argv);
|
||||
static int console_read(int argc, char **argv);
|
||||
static int console_write(int argc, char **argv);
|
||||
static int console_size(int argc, char **argv);
|
||||
static int console_status(int argc, char **argv);
|
||||
static int console_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,
|
||||
.func = &console_read,
|
||||
},
|
||||
{
|
||||
.command = "write",
|
||||
.help = "create file BASE_PATH/README.MD if it does not exist",
|
||||
.hint = NULL,
|
||||
.func = &f_write,
|
||||
.func = &console_write,
|
||||
},
|
||||
{
|
||||
.command = "size",
|
||||
.help = "show storage size and sector size",
|
||||
.hint = NULL,
|
||||
.func = &f_size,
|
||||
.func = &console_size,
|
||||
},
|
||||
{
|
||||
.command = "expose",
|
||||
.help = "Expose Storage to Host",
|
||||
.hint = NULL,
|
||||
.func = &f_unmount,
|
||||
.func = &console_unmount,
|
||||
},
|
||||
{
|
||||
.command = "status",
|
||||
.help = "Status of storage exposure over USB",
|
||||
.hint = NULL,
|
||||
.func = &f_status,
|
||||
.func = &console_status,
|
||||
},
|
||||
{
|
||||
.command = "exit",
|
||||
.help = "exit from application",
|
||||
.hint = NULL,
|
||||
.func = &f_exit,
|
||||
.func = &console_exit,
|
||||
}
|
||||
};
|
||||
|
||||
@ -293,10 +129,8 @@ const esp_console_cmd_t cmds[] = {
|
||||
static void _mount(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Mount storage...");
|
||||
if (!is_mount) {
|
||||
ESP_ERROR_CHECK(storage_mount(BASE_PATH));
|
||||
is_mount = true;
|
||||
}
|
||||
ESP_ERROR_CHECK(tinyusb_msc_storage_mount(BASE_PATH));
|
||||
|
||||
|
||||
// List all the files in this directory
|
||||
ESP_LOGI(TAG, "\nls command output:");
|
||||
@ -319,29 +153,22 @@ static void _mount(void)
|
||||
return;
|
||||
}
|
||||
|
||||
// unmount the partition
|
||||
static void _unmount(void)
|
||||
// unmount storage
|
||||
static int console_unmount(int argc, char **argv)
|
||||
{
|
||||
if (!is_mount) {
|
||||
ESP_LOGD(TAG, "storage exposed over USB...");
|
||||
return;
|
||||
if (tinyusb_msc_storage_in_use_by_usb_host()) {
|
||||
ESP_LOGE(TAG, "storage is already exposed");
|
||||
return -1;
|
||||
}
|
||||
ESP_LOGI(TAG, "Unmount storage...");
|
||||
ESP_ERROR_CHECK(storage_unmount());
|
||||
is_mount = false;
|
||||
is_eject = false;
|
||||
}
|
||||
|
||||
static int f_unmount(int argc, char **argv)
|
||||
{
|
||||
_unmount();
|
||||
ESP_ERROR_CHECK(tinyusb_msc_storage_unmount());
|
||||
return 0;
|
||||
}
|
||||
|
||||
// read BASE_PATH/README.MD and print its contents
|
||||
static int f_read(int argc, char **argv)
|
||||
static int console_read(int argc, char **argv)
|
||||
{
|
||||
if (!is_mount) {
|
||||
if (tinyusb_msc_storage_in_use_by_usb_host()) {
|
||||
ESP_LOGE(TAG, "storage exposed over USB. Application can't read from storage.");
|
||||
return -1;
|
||||
}
|
||||
@ -361,9 +188,9 @@ static int f_read(int argc, char **argv)
|
||||
}
|
||||
|
||||
// create file BASE_PATH/README.MD if it does not exist
|
||||
static int f_write(int argc, char **argv)
|
||||
static int console_write(int argc, char **argv)
|
||||
{
|
||||
if (!is_mount) {
|
||||
if (tinyusb_msc_storage_in_use_by_usb_host()) {
|
||||
ESP_LOGE(TAG, "storage exposed over USB. Application can't write to storage.");
|
||||
return -1;
|
||||
}
|
||||
@ -382,56 +209,55 @@ static int f_write(int argc, char **argv)
|
||||
}
|
||||
|
||||
// Show storage size and sector size
|
||||
static int f_size(int argc, char **argv)
|
||||
static int console_size(int argc, char **argv)
|
||||
{
|
||||
if (!is_mount) {
|
||||
if (tinyusb_msc_storage_in_use_by_usb_host()) {
|
||||
ESP_LOGE(TAG, "storage exposed over USB. Application can't access storage");
|
||||
return -1;
|
||||
}
|
||||
uint32_t sec_count = storage_get_sector_count();
|
||||
uint32_t sec_size = storage_get_sector_size();
|
||||
uint32_t sec_count = tinyusb_msc_storage_get_sector_count();
|
||||
uint32_t sec_size = tinyusb_msc_storage_get_sector_size();
|
||||
printf("Storage Capacity %lluMB\n", ((uint64_t) sec_count) * sec_size / (1024 * 1024));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// exit from application
|
||||
static int f_status(int argc, char **argv)
|
||||
static int console_status(int argc, char **argv)
|
||||
{
|
||||
printf("storage exposed over USB: %s\n", is_mount ? "No" : "Yes");
|
||||
printf("storage exposed over USB: %s\n", tinyusb_msc_storage_in_use_by_usb_host() ? "Yes" : "No");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// exit from application
|
||||
static int f_exit(int argc, char **argv)
|
||||
static int console_exit(int argc, char **argv)
|
||||
{
|
||||
tinyusb_msc_storage_deinit();
|
||||
printf("Application Exiting\n");
|
||||
exit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// 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,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.pull_up_en = true,
|
||||
.pull_down_en = false,
|
||||
};
|
||||
ESP_ERROR_CHECK(gpio_config(&vbus_gpio_config));
|
||||
|
||||
ESP_LOGI(TAG, "Initializing storage...");
|
||||
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;
|
||||
static esp_err_t storage_init_spiflash(wl_handle_t *wl_handle)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initializing wear levelling");
|
||||
|
||||
const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL);
|
||||
if (data_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to find FATFS partition. Check the partition table.");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
return wl_mount(data_partition, wl_handle);
|
||||
}
|
||||
#else // CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH
|
||||
static esp_err_t storage_init_sdmmc(sdmmc_card_t **card)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
bool host_init = false;
|
||||
sdmmc_card_t *sd_card;
|
||||
|
||||
ESP_LOGI(TAG, "Initializing SDCard");
|
||||
|
||||
// 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)
|
||||
@ -467,13 +293,64 @@ void app_main(void)
|
||||
// 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;
|
||||
// not using ff_memalloc here, as allocation in internal RAM is preferred
|
||||
sd_card = (sdmmc_card_t *)malloc(sizeof(sdmmc_card_t));
|
||||
ESP_GOTO_ON_FALSE(sd_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, sd_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, sd_card);
|
||||
*card = sd_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 (sd_card) {
|
||||
free(sd_card);
|
||||
sd_card = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif // CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH
|
||||
|
||||
ESP_ERROR_CHECK(storage_init(&storage_config));
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initializing storage...");
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH
|
||||
static wl_handle_t wl_handle = WL_INVALID_HANDLE;
|
||||
ESP_ERROR_CHECK(storage_init_spiflash(&wl_handle));
|
||||
|
||||
const tinyusb_msc_spiflash_config_t config_spi = {
|
||||
.wl_handle = wl_handle
|
||||
};
|
||||
ESP_ERROR_CHECK(tinyusb_msc_storage_init_spiflash(&config_spi));
|
||||
#else // CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH
|
||||
static sdmmc_card_t *card = NULL;
|
||||
ESP_ERROR_CHECK(storage_init_sdmmc(&card));
|
||||
|
||||
const tinyusb_msc_sdmmc_config_t config_sdmmc = {
|
||||
.card = card
|
||||
};
|
||||
ESP_ERROR_CHECK(tinyusb_msc_storage_init_sdmmc(&config_sdmmc));
|
||||
#endif // CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH
|
||||
|
||||
//mounted in the app by default
|
||||
_mount();
|
||||
@ -485,8 +362,6 @@ void app_main(void)
|
||||
.string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]),
|
||||
.external_phy = false,
|
||||
.configuration_descriptor = desc_configuration,
|
||||
.self_powered = true,
|
||||
.vbus_monitor_io = VBUS_MONITORING_GPIO_NUM,
|
||||
};
|
||||
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
|
||||
ESP_LOGI(TAG, "USB MSC initialization DONE");
|
||||
@ -506,4 +381,3 @@ void app_main(void)
|
||||
}
|
||||
ESP_ERROR_CHECK(esp_console_start_repl(repl));
|
||||
}
|
||||
/*********************************************************************** Application Code*/
|
||||
|
@ -1,166 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#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.
|
||||
*
|
||||
* 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_NO_MEM, if there was no memory to allocate storage components;
|
||||
*/
|
||||
esp_err_t storage_init(const tinyusb_config_storage_t *config);
|
||||
|
||||
/**
|
||||
* @brief Mount the storage partition locally on the firmware application.
|
||||
*
|
||||
* Get the available drive number. Register spi flash partition.
|
||||
* Connect POSIX and C standard library IO function with FATFS.
|
||||
* Mounts the partition.
|
||||
* This API is used by the firmware application. If the storage partition is
|
||||
* mounted by this API, host (PC) can't access the storage via MSC.
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Unmount the storage partition from the firmware application.
|
||||
*
|
||||
* 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 (recognized) by host (PC).
|
||||
*
|
||||
* @return esp_err_t
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if FATFS is not registered in VFS
|
||||
*/
|
||||
esp_err_t storage_unmount(void);
|
||||
|
||||
/**
|
||||
* @brief Get number of sectors in storage media
|
||||
*
|
||||
* @return usable size, in bytes
|
||||
*/
|
||||
uint32_t storage_get_sector_count(void);
|
||||
|
||||
/**
|
||||
* @brief Get sector size of storage media
|
||||
*
|
||||
* @return sector count
|
||||
*/
|
||||
uint32_t storage_get_sector_size(void);
|
||||
|
||||
/**
|
||||
* @brief Read data from the storage
|
||||
*
|
||||
* @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.
|
||||
* @return esp_err_t
|
||||
* - ESP_OK, if data was read successfully;
|
||||
* - ESP_ERR_INVALID_ARG, if src_offset exceeds partition size;
|
||||
* - 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(uint32_t lba, uint32_t offset, size_t size, void *dest);
|
||||
|
||||
/**
|
||||
* @brief Write data to the storage
|
||||
*
|
||||
* Before writing data to flash, corresponding region of flash needs to be erased.
|
||||
*
|
||||
* @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.
|
||||
* @return esp_err_t
|
||||
* - ESP_OK, if data was written successfully;
|
||||
* - ESP_ERR_INVALID_ARG, if dst_offset exceeds partition size;
|
||||
* - 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(uint32_t lba, uint32_t offset, size_t size, const void *src);
|
||||
|
||||
/*********************************************************************** Public functions*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,276 +0,0 @@
|
||||
/*
|
||||
* 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,196 +0,0 @@
|
||||
/*
|
||||
* 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 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 = "msc_spiflash";
|
||||
|
||||
esp_err_t storage_init(const tinyusb_config_storage_t *config)
|
||||
{
|
||||
assert(config->storage_type == TINYUSB_STORAGE_SPI);
|
||||
ESP_LOGI(TAG, "Initializing wear levelling");
|
||||
|
||||
const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL);
|
||||
if (data_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to find FATFS partition. Check the partition table.");
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
return wl_mount(data_partition, &s_wl_handle);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
esp_err_t storage_mount(const char *base_path)
|
||||
{
|
||||
const size_t workbuf_size = 4096;
|
||||
void *workbuf = NULL;
|
||||
esp_err_t ret;
|
||||
|
||||
if (s_fat_mounted) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Initializing FAT");
|
||||
|
||||
// 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_LOGD(TAG, "using pdrv=%i", pdrv);
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
ESP_GOTO_ON_ERROR(ff_diskio_register_wl_partition(pdrv, s_wl_handle),
|
||||
fail, TAG, "Failed pdrv=%d", pdrv);
|
||||
|
||||
FATFS *fs;
|
||||
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 (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_vfs_fat_register failed (0x%x)", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Try to mount partition
|
||||
FRESULT fresult = f_mount(fs, drv, 1);
|
||||
if (fresult != FR_OK) {
|
||||
ESP_LOGW(TAG, "f_mount failed (%d)", fresult);
|
||||
if (!((fresult == FR_NO_FILESYSTEM || fresult == FR_INT_ERR))) {
|
||||
ret = ESP_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
workbuf = ff_memalloc(workbuf_size);
|
||||
if (workbuf == NULL) {
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto fail;
|
||||
}
|
||||
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(
|
||||
CONFIG_WL_SECTOR_SIZE,
|
||||
4096);
|
||||
|
||||
ESP_LOGI(TAG, "Formatting FATFS partition, allocation unit size=%d", alloc_unit_size);
|
||||
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) {
|
||||
ret = ESP_FAIL;
|
||||
ESP_LOGE(TAG, "f_mkfs failed (%d)", fresult);
|
||||
goto fail;
|
||||
}
|
||||
free(workbuf);
|
||||
workbuf = NULL;
|
||||
ESP_LOGI(TAG, "Mounting again");
|
||||
fresult = f_mount(fs, drv, 0);
|
||||
if (fresult != FR_OK) {
|
||||
ret = ESP_FAIL;
|
||||
ESP_LOGE(TAG, "f_mount failed after formatting (%d)", fresult);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
s_fat_mounted = true;
|
||||
s_base_path = base_path;
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
fail:
|
||||
free(workbuf);
|
||||
esp_vfs_fat_unregister_path(base_path);
|
||||
ff_diskio_unregister(pdrv);
|
||||
s_fat_mounted = false;
|
||||
ESP_LOGW(TAG, "Failed to mount storage (0x%x)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t storage_unmount(void)
|
||||
{
|
||||
if (!s_fat_mounted) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
BYTE pdrv = ff_diskio_get_pdrv_wl(s_wl_handle);
|
||||
if (pdrv == 0xff) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
f_mount(0, drv, 0);
|
||||
ff_diskio_unregister(pdrv);
|
||||
ff_diskio_clear_pdrv_wl(s_wl_handle);
|
||||
esp_err_t err = esp_vfs_fat_unregister_path(s_base_path);
|
||||
s_base_path = NULL;
|
||||
s_fat_mounted = false;
|
||||
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
uint32_t storage_get_sector_count(void)
|
||||
{
|
||||
assert(s_wl_handle != WL_INVALID_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);
|
||||
}
|
||||
|
||||
uint32_t storage_get_sector_size(void)
|
||||
{
|
||||
assert(s_wl_handle != WL_INVALID_HANDLE);
|
||||
|
||||
return (uint32_t)wl_sector_size(s_wl_handle);
|
||||
}
|
||||
|
||||
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(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;
|
||||
}
|
||||
size_t sector_size = wl_sector_size(s_wl_handle);
|
||||
if (addr % sector_size != 0 || size % sector_size != 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
@ -8,7 +8,7 @@ from pytest_embedded import Dut
|
||||
@pytest.mark.usb_device
|
||||
def test_usb_device_msc_example(dut: Dut) -> None:
|
||||
dut.expect('Mount storage')
|
||||
dut.expect('Initializing FAT')
|
||||
dut.expect('TinyUSB Driver installed')
|
||||
dut.expect('USB MSC initialization DONE')
|
||||
dut.write(' help')
|
||||
dut.expect('read')
|
||||
|
Loading…
x
Reference in New Issue
Block a user