mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-05 21:54:33 +02:00
Merge branch 'feature/usb_msc_device_driver' into 'master'
usb: modify example according to newly implemented USB MSC Device class driver Closes IDF-1600 See merge request espressif/esp-idf!22727
This commit is contained in:
@@ -139,67 +139,66 @@ To specify callbacks you can either set the pointer to your :cpp:type:`tusb_cdca
|
|||||||
USB Serial Console
|
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)
|
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
|
.. code-block:: c
|
||||||
|
|
||||||
static uint8_t const desc_configuration[] = {
|
static esp_err_t storage_init_spiflash(wl_handle_t *wl_handle)
|
||||||
// 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),
|
***
|
||||||
|
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
|
const tinyusb_msc_spiflash_config_t config_spi = {
|
||||||
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, TUD_OPT_HIGH_SPEED ? 512 : 64),
|
.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[] = {
|
- SD-Card
|
||||||
(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
|
|
||||||
|
|
||||||
.. code-block:: c
|
.. 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])
|
static esp_err_t storage_init_sdmmc(sdmmc_card_t **card)
|
||||||
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)
|
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||||
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize)
|
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||||
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize)
|
// For SD Card, set bus width to use
|
||||||
int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize)
|
|
||||||
|
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
|
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.
|
- 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.
|
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.
|
- 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.
|
- 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.
|
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
|
- 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.
|
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`:
|
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)"
|
- 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.
|
- iii. Select the GPIO Pin numbers for SD Card Pin.
|
||||||
- iv. Save the configuration.
|
- 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:
|
After the flashing you should see the output at idf monitor:
|
||||||
|
|
||||||
```
|
```
|
||||||
I (311) cpu_start: Starting scheduler on PRO CPU.
|
I (329) app_start: Starting scheduler on CPU0
|
||||||
I (0) cpu_start: Starting scheduler on APP CPU.
|
I (334) app_start: Starting scheduler on CPU1
|
||||||
I (332) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
I (334) main_task: Started on CPU0
|
||||||
I (332) example_msc_main: Initializing storage...
|
I (344) main_task: Calling app_main()
|
||||||
I (342) example_msc_storage: Initializing wear levelling
|
I (344) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||||
I (350) example_main: Mount storage...
|
I (354) example_main: Initializing storage...
|
||||||
I (350) example_sdmmc: Initializing FAT
|
I (364) example_main: Initializing wear levelling
|
||||||
I (372) example_msc_main: USB MSC initialization
|
I (374) example_main: Mount storage...
|
||||||
I (372) tusb_desc:
|
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 │
|
│ USB Device Descriptor Summary │
|
||||||
├───────────────────┬─────────────┤
|
├───────────────────┬─────────────┤
|
||||||
@@ -131,21 +138,13 @@ I (372) tusb_desc:
|
|||||||
├───────────────────┼─────────────┤
|
├───────────────────┼─────────────┤
|
||||||
│bNumConfigurations │ 0x1 │
|
│bNumConfigurations │ 0x1 │
|
||||||
└───────────────────┴─────────────┘
|
└───────────────────┴─────────────┘
|
||||||
I (532) TinyUSB: TinyUSB Driver installed
|
I (554) TinyUSB: TinyUSB Driver installed
|
||||||
I (532) example_msc_main: USB MSC initialization DONE
|
I (564) example_main: USB MSC initialization DONE
|
||||||
I (552) example_msc_main:
|
|
||||||
ls command output:
|
|
||||||
README.MD
|
|
||||||
.fseventsd
|
|
||||||
|
|
||||||
Type 'help' to get the list of commands.
|
Type 'help' to get the list of commands.
|
||||||
Use UP/DOWN arrows to navigate through command history.
|
Use UP/DOWN arrows to navigate through command history.
|
||||||
Press TAB when typing command name to auto-complete.
|
Press TAB when typing command name to auto-complete.
|
||||||
esp32s3> I (912) example_msc_main: tud_mount_cb MSC START: Expose Over USB
|
I (724) main_task: Returned from app_main()
|
||||||
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>
|
|
||||||
esp32s3>
|
esp32s3>
|
||||||
esp32s3> help
|
esp32s3> help
|
||||||
help
|
help
|
||||||
@@ -171,43 +170,32 @@ exit
|
|||||||
|
|
||||||
esp32s3>
|
esp32s3>
|
||||||
esp32s3> read
|
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)
|
Command returned non-zero error code: 0xffffffff (ESP_FAIL)
|
||||||
esp32s3> write
|
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)
|
Command returned non-zero error code: 0xffffffff (ESP_FAIL)
|
||||||
esp32s3> size
|
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)
|
Command returned non-zero error code: 0xffffffff (ESP_FAIL)
|
||||||
esp32s3> status
|
esp32s3> status
|
||||||
storage exposed over USB: Yes
|
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>
|
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
|
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.
|
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.
|
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.
|
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> write
|
||||||
esp32s3> size
|
esp32s3> size
|
||||||
storage size(1024000), sec_size(512)
|
Storage Capacity 0MB
|
||||||
esp32s3>
|
esp32s3> status
|
||||||
|
storage exposed over USB: No
|
||||||
esp32s3> expose
|
esp32s3> expose
|
||||||
I (76402) example_msc_main: Unmount storage...
|
I (181224) example_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>
|
|
||||||
esp32s3> status
|
esp32s3> status
|
||||||
storage exposed over USB: Yes
|
storage exposed over USB: Yes
|
||||||
esp32s3>
|
esp32s3>
|
||||||
|
@@ -2,12 +2,7 @@ set(srcs "tusb_msc_main.c")
|
|||||||
set(requires fatfs console)
|
set(requires fatfs console)
|
||||||
|
|
||||||
if(CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH)
|
if(CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH)
|
||||||
list(APPEND srcs "tusb_msc_storage_spiffs.c")
|
list(APPEND requires wear_levelling)
|
||||||
list(APPEND requires "wear_levelling")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CONFIG_EXAMPLE_STORAGE_MEDIA_SDMMCCARD)
|
|
||||||
list(APPEND srcs "tusb_msc_storage_sdmmc.c")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
idf_component_register(
|
idf_component_register(
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
menu "USB DEV MSC Example Configuration"
|
menu "USB Dev MSC Example Configuration"
|
||||||
|
|
||||||
choice EXAMPLE_STORAGE_MEDIA
|
choice EXAMPLE_STORAGE_MEDIA
|
||||||
prompt "Storage Media Used"
|
prompt "Storage Media Used"
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
## IDF Component Manager Manifest File
|
## IDF Component Manager Manifest File
|
||||||
dependencies:
|
dependencies:
|
||||||
espressif/esp_tinyusb: "^1.1"
|
espressif/esp_tinyusb: "^1.2"
|
||||||
idf: "^5.0"
|
idf: "^5.0"
|
||||||
|
@@ -11,182 +11,20 @@
|
|||||||
* For different scenarios and behaviour, Refer to README of this example.
|
* For different scenarios and behaviour, Refer to README of this example.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include "esp_log.h"
|
|
||||||
#include "esp_console.h"
|
#include "esp_console.h"
|
||||||
#include "tinyusb.h"
|
#include "esp_check.h"
|
||||||
#include "class/msc/msc_device.h"
|
|
||||||
#include "tusb_msc_storage.h"
|
|
||||||
#include "driver/gpio.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";
|
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
|
/* TinyUSB descriptors
|
||||||
********************************************************************* */
|
********************************************************************* */
|
||||||
#define EPNUM_MSC 1
|
#define EPNUM_MSC 1
|
||||||
@@ -239,53 +77,51 @@ static char const *string_desc_arr[] = {
|
|||||||
};
|
};
|
||||||
/*********************************************************************** TinyUSB descriptors*/
|
/*********************************************************************** TinyUSB descriptors*/
|
||||||
|
|
||||||
#define VBUS_MONITORING_GPIO_NUM GPIO_NUM_4
|
|
||||||
#define BASE_PATH "/data" // base path to mount the partition
|
#define BASE_PATH "/data" // base path to mount the partition
|
||||||
static bool is_mount = false;
|
|
||||||
|
|
||||||
#define PROMPT_STR CONFIG_IDF_TARGET
|
#define PROMPT_STR CONFIG_IDF_TARGET
|
||||||
static int f_unmount(int argc, char **argv);
|
static int console_unmount(int argc, char **argv);
|
||||||
static int f_read(int argc, char **argv);
|
static int console_read(int argc, char **argv);
|
||||||
static int f_write(int argc, char **argv);
|
static int console_write(int argc, char **argv);
|
||||||
static int f_size(int argc, char **argv);
|
static int console_size(int argc, char **argv);
|
||||||
static int f_status(int argc, char **argv);
|
static int console_status(int argc, char **argv);
|
||||||
static int f_exit(int argc, char **argv);
|
static int console_exit(int argc, char **argv);
|
||||||
const esp_console_cmd_t cmds[] = {
|
const esp_console_cmd_t cmds[] = {
|
||||||
{
|
{
|
||||||
.command = "read",
|
.command = "read",
|
||||||
.help = "read BASE_PATH/README.MD and print its contents",
|
.help = "read BASE_PATH/README.MD and print its contents",
|
||||||
.hint = NULL,
|
.hint = NULL,
|
||||||
.func = &f_read,
|
.func = &console_read,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.command = "write",
|
.command = "write",
|
||||||
.help = "create file BASE_PATH/README.MD if it does not exist",
|
.help = "create file BASE_PATH/README.MD if it does not exist",
|
||||||
.hint = NULL,
|
.hint = NULL,
|
||||||
.func = &f_write,
|
.func = &console_write,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.command = "size",
|
.command = "size",
|
||||||
.help = "show storage size and sector size",
|
.help = "show storage size and sector size",
|
||||||
.hint = NULL,
|
.hint = NULL,
|
||||||
.func = &f_size,
|
.func = &console_size,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.command = "expose",
|
.command = "expose",
|
||||||
.help = "Expose Storage to Host",
|
.help = "Expose Storage to Host",
|
||||||
.hint = NULL,
|
.hint = NULL,
|
||||||
.func = &f_unmount,
|
.func = &console_unmount,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.command = "status",
|
.command = "status",
|
||||||
.help = "Status of storage exposure over USB",
|
.help = "Status of storage exposure over USB",
|
||||||
.hint = NULL,
|
.hint = NULL,
|
||||||
.func = &f_status,
|
.func = &console_status,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.command = "exit",
|
.command = "exit",
|
||||||
.help = "exit from application",
|
.help = "exit from application",
|
||||||
.hint = NULL,
|
.hint = NULL,
|
||||||
.func = &f_exit,
|
.func = &console_exit,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -293,10 +129,8 @@ const esp_console_cmd_t cmds[] = {
|
|||||||
static void _mount(void)
|
static void _mount(void)
|
||||||
{
|
{
|
||||||
ESP_LOGI(TAG, "Mount storage...");
|
ESP_LOGI(TAG, "Mount storage...");
|
||||||
if (!is_mount) {
|
ESP_ERROR_CHECK(tinyusb_msc_storage_mount(BASE_PATH));
|
||||||
ESP_ERROR_CHECK(storage_mount(BASE_PATH));
|
|
||||||
is_mount = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// List all the files in this directory
|
// List all the files in this directory
|
||||||
ESP_LOGI(TAG, "\nls command output:");
|
ESP_LOGI(TAG, "\nls command output:");
|
||||||
@@ -319,29 +153,22 @@ static void _mount(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// unmount the partition
|
// unmount storage
|
||||||
static void _unmount(void)
|
static int console_unmount(int argc, char **argv)
|
||||||
{
|
{
|
||||||
if (!is_mount) {
|
if (tinyusb_msc_storage_in_use_by_usb_host()) {
|
||||||
ESP_LOGD(TAG, "storage exposed over USB...");
|
ESP_LOGE(TAG, "storage is already exposed");
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
ESP_LOGI(TAG, "Unmount storage...");
|
ESP_LOGI(TAG, "Unmount storage...");
|
||||||
ESP_ERROR_CHECK(storage_unmount());
|
ESP_ERROR_CHECK(tinyusb_msc_storage_unmount());
|
||||||
is_mount = false;
|
|
||||||
is_eject = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int f_unmount(int argc, char **argv)
|
|
||||||
{
|
|
||||||
_unmount();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read BASE_PATH/README.MD and print its contents
|
// 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.");
|
ESP_LOGE(TAG, "storage exposed over USB. Application can't read from storage.");
|
||||||
return -1;
|
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
|
// 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.");
|
ESP_LOGE(TAG, "storage exposed over USB. Application can't write to storage.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -382,56 +209,55 @@ static int f_write(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Show storage size and sector size
|
// 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");
|
ESP_LOGE(TAG, "storage exposed over USB. Application can't access storage");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
uint32_t sec_count = storage_get_sector_count();
|
uint32_t sec_count = tinyusb_msc_storage_get_sector_count();
|
||||||
uint32_t sec_size = storage_get_sector_size();
|
uint32_t sec_size = tinyusb_msc_storage_get_sector_size();
|
||||||
printf("Storage Capacity %lluMB\n", ((uint64_t) sec_count) * sec_size / (1024 * 1024));
|
printf("Storage Capacity %lluMB\n", ((uint64_t) sec_count) * sec_size / (1024 * 1024));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// exit from application
|
// 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// exit from application
|
// 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");
|
printf("Application Exiting\n");
|
||||||
exit(0);
|
exit(0);
|
||||||
return 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
|
#ifdef CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH
|
||||||
// configuration is to defined in case of SPI Flash
|
static esp_err_t storage_init_spiflash(wl_handle_t *wl_handle)
|
||||||
storage_config.storage_type = TINYUSB_STORAGE_SPI;
|
{
|
||||||
tinyusb_config_spiffs_t config_spi;
|
ESP_LOGI(TAG, "Initializing wear levelling");
|
||||||
storage_config.spiffs_config = &config_spi;
|
|
||||||
#else // CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH
|
const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL);
|
||||||
storage_config.storage_type = TINYUSB_STORAGE_SDMMC;
|
if (data_partition == NULL) {
|
||||||
tinyusb_config_sdmmc_t config_sdmmc;
|
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)
|
// 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)
|
// 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.
|
// connected on the bus. This is for debug / example purpose only.
|
||||||
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
|
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
|
||||||
|
|
||||||
config_sdmmc.host = host;
|
// not using ff_memalloc here, as allocation in internal RAM is preferred
|
||||||
config_sdmmc.slot_config = slot_config;
|
sd_card = (sdmmc_card_t *)malloc(sizeof(sdmmc_card_t));
|
||||||
storage_config.sdmmc_config = &config_sdmmc;
|
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
|
#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
|
//mounted in the app by default
|
||||||
_mount();
|
_mount();
|
||||||
@@ -485,8 +362,6 @@ void app_main(void)
|
|||||||
.string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]),
|
.string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]),
|
||||||
.external_phy = false,
|
.external_phy = false,
|
||||||
.configuration_descriptor = desc_configuration,
|
.configuration_descriptor = desc_configuration,
|
||||||
.self_powered = true,
|
|
||||||
.vbus_monitor_io = VBUS_MONITORING_GPIO_NUM,
|
|
||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
|
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
|
||||||
ESP_LOGI(TAG, "USB MSC initialization DONE");
|
ESP_LOGI(TAG, "USB MSC initialization DONE");
|
||||||
@@ -506,4 +381,3 @@ void app_main(void)
|
|||||||
}
|
}
|
||||||
ESP_ERROR_CHECK(esp_console_start_repl(repl));
|
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
|
@pytest.mark.usb_device
|
||||||
def test_usb_device_msc_example(dut: Dut) -> None:
|
def test_usb_device_msc_example(dut: Dut) -> None:
|
||||||
dut.expect('Mount storage')
|
dut.expect('Mount storage')
|
||||||
dut.expect('Initializing FAT')
|
dut.expect('TinyUSB Driver installed')
|
||||||
dut.expect('USB MSC initialization DONE')
|
dut.expect('USB MSC initialization DONE')
|
||||||
dut.write(' help')
|
dut.write(' help')
|
||||||
dut.expect('read')
|
dut.expect('read')
|
||||||
|
Reference in New Issue
Block a user