2022-10-31 19:42:05 +00:00
/*
2022-12-19 05:35:59 +00:00
* SPDX - FileCopyrightText : 2022 - 2023 Espressif Systems ( Shanghai ) CO LTD
2022-10-31 19:42:05 +00:00
*
* SPDX - License - Identifier : Unlicense OR CC0 - 1.0
*/
/* DESCRIPTION:
* This example contains code to make ESP32 - S3 based device recognizable by USB - hosts as a USB Mass Storage Device .
2022-12-19 05:35:59 +00:00
* It either allows the embedded application i . e . example to access the partition or Host PC accesses the partition over USB MSC .
2022-10-31 19:42:05 +00:00
* They can ' t be allowed to access the partition at the same time .
* For different scenarios and behaviour , Refer to README of this example .
*/
# include <errno.h>
# include <dirent.h>
# include "esp_console.h"
2023-03-13 07:28:58 +00:00
# include "esp_check.h"
2023-07-24 12:25:42 +00:00
# include "esp_partition.h"
2023-03-13 07:28:58 +00:00
# include "driver/gpio.h"
2022-10-31 19:42:05 +00:00
# include "tinyusb.h"
# include "tusb_msc_storage.h"
2024-01-08 11:17:37 +00:00
# ifdef CONFIG_EXAMPLE_STORAGE_MEDIA_SDMMC
2023-03-13 07:28:58 +00:00
# include "diskio_impl.h"
# include "diskio_sdmmc.h"
# endif
2022-10-31 19:42:05 +00:00
2022-12-19 05:35:59 +00:00
static const char * TAG = " example_main " ;
2022-10-31 19:42:05 +00:00
2022-12-19 05:35:59 +00:00
/* TinyUSB descriptors
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-10-31 19:42:05 +00:00
# define EPNUM_MSC 1
# define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN)
enum {
ITF_NUM_MSC = 0 ,
ITF_NUM_TOTAL
} ;
enum {
EDPT_CTRL_OUT = 0x00 ,
EDPT_CTRL_IN = 0x80 ,
EDPT_MSC_OUT = 0x01 ,
EDPT_MSC_IN = 0x81 ,
} ;
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 ) ,
// 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 ) ,
} ;
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 , // This is Espressif VID. This needs to be changed according to Users / Customers
. 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
} ;
2022-12-19 05:35:59 +00:00
/*********************************************************************** TinyUSB descriptors*/
2022-10-31 19:42:05 +00:00
# define BASE_PATH " / data" // base path to mount the partition
2022-12-19 05:35:59 +00:00
# define PROMPT_STR CONFIG_IDF_TARGET
2023-03-13 07:28:58 +00:00
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 ) ;
2022-12-19 05:35:59 +00:00
const esp_console_cmd_t cmds [ ] = {
{
. command = " read " ,
. help = " read BASE_PATH/README.MD and print its contents " ,
. hint = NULL ,
2023-03-13 07:28:58 +00:00
. func = & console_read ,
2022-12-19 05:35:59 +00:00
} ,
{
. command = " write " ,
. help = " create file BASE_PATH/README.MD if it does not exist " ,
. hint = NULL ,
2023-03-13 07:28:58 +00:00
. func = & console_write ,
2022-12-19 05:35:59 +00:00
} ,
{
. command = " size " ,
. help = " show storage size and sector size " ,
. hint = NULL ,
2023-03-13 07:28:58 +00:00
. func = & console_size ,
2022-12-19 05:35:59 +00:00
} ,
{
. command = " expose " ,
. help = " Expose Storage to Host " ,
. hint = NULL ,
2023-03-13 07:28:58 +00:00
. func = & console_unmount ,
2022-12-19 05:35:59 +00:00
} ,
{
. command = " status " ,
. help = " Status of storage exposure over USB " ,
. hint = NULL ,
2023-03-13 07:28:58 +00:00
. func = & console_status ,
2022-12-19 05:35:59 +00:00
} ,
{
. command = " exit " ,
. help = " exit from application " ,
. hint = NULL ,
2023-03-13 07:28:58 +00:00
. func = & console_exit ,
2022-12-19 05:35:59 +00:00
}
} ;
2022-10-31 19:42:05 +00:00
// mount the partition and show all the files in BASE_PATH
static void _mount ( void )
{
ESP_LOGI ( TAG , " Mount storage... " ) ;
2023-03-13 07:28:58 +00:00
ESP_ERROR_CHECK ( tinyusb_msc_storage_mount ( BASE_PATH ) ) ;
2022-10-31 19:42:05 +00:00
// List all the files in this directory
ESP_LOGI ( TAG , " \n ls command output: " ) ;
struct dirent * d ;
DIR * dh = opendir ( BASE_PATH ) ;
if ( ! dh ) {
2023-01-04 06:53:49 +00:00
if ( errno = = ENOENT ) {
2022-10-31 19:42:05 +00:00
//If the directory is not found
ESP_LOGE ( TAG , " Directory doesn't exist %s " , BASE_PATH ) ;
} else {
//If the directory is not readable then throw error and exit
ESP_LOGE ( TAG , " Unable to read directory %s " , BASE_PATH ) ;
}
return ;
}
//While the next entry is not readable we will print directory files
while ( ( d = readdir ( dh ) ) ! = NULL ) {
printf ( " %s \n " , d - > d_name ) ;
}
return ;
}
2023-03-13 07:28:58 +00:00
// unmount storage
static int console_unmount ( int argc , char * * argv )
2022-10-31 19:42:05 +00:00
{
2023-03-13 07:28:58 +00:00
if ( tinyusb_msc_storage_in_use_by_usb_host ( ) ) {
ESP_LOGE ( TAG , " storage is already exposed " ) ;
return - 1 ;
2022-10-31 19:42:05 +00:00
}
ESP_LOGI ( TAG , " Unmount storage... " ) ;
2023-03-13 07:28:58 +00:00
ESP_ERROR_CHECK ( tinyusb_msc_storage_unmount ( ) ) ;
2022-10-31 19:42:05 +00:00
return 0 ;
}
// read BASE_PATH/README.MD and print its contents
2023-03-13 07:28:58 +00:00
static int console_read ( int argc , char * * argv )
2022-10-31 19:42:05 +00:00
{
2023-03-13 07:28:58 +00:00
if ( tinyusb_msc_storage_in_use_by_usb_host ( ) ) {
2022-10-31 19:42:05 +00:00
ESP_LOGE ( TAG , " storage exposed over USB. Application can't read from storage. " ) ;
return - 1 ;
}
ESP_LOGD ( TAG , " read from storage: " ) ;
const char * filename = BASE_PATH " /README.MD " ;
FILE * ptr = fopen ( filename , " r " ) ;
if ( ptr = = NULL ) {
ESP_LOGE ( TAG , " Filename not present - %s " , filename ) ;
return - 1 ;
}
char buf [ 1024 ] ;
while ( fgets ( buf , 1000 , ptr ) ! = NULL ) {
printf ( " %s " , buf ) ;
}
fclose ( ptr ) ;
return 0 ;
}
// create file BASE_PATH/README.MD if it does not exist
2023-03-13 07:28:58 +00:00
static int console_write ( int argc , char * * argv )
2022-10-31 19:42:05 +00:00
{
2023-03-13 07:28:58 +00:00
if ( tinyusb_msc_storage_in_use_by_usb_host ( ) ) {
2022-10-31 19:42:05 +00:00
ESP_LOGE ( TAG , " storage exposed over USB. Application can't write to storage. " ) ;
return - 1 ;
}
ESP_LOGD ( TAG , " write to storage: " ) ;
const char * filename = BASE_PATH " /README.MD " ;
FILE * fd = fopen ( filename , " r " ) ;
if ( ! fd ) {
ESP_LOGW ( TAG , " README.MD doesn't exist yet, creating " ) ;
fd = fopen ( filename , " w " ) ;
fprintf ( fd , " 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. \n " ) ;
fprintf ( fd , " In this example, ESP chip will be recognised by host (PC) as Mass Storage Device. \n " ) ;
fprintf ( fd , " 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. \n " ) ;
fclose ( fd ) ;
}
return 0 ;
}
// Show storage size and sector size
2023-03-13 07:28:58 +00:00
static int console_size ( int argc , char * * argv )
2022-10-31 19:42:05 +00:00
{
2023-03-13 07:28:58 +00:00
if ( tinyusb_msc_storage_in_use_by_usb_host ( ) ) {
2022-10-31 19:42:05 +00:00
ESP_LOGE ( TAG , " storage exposed over USB. Application can't access storage " ) ;
return - 1 ;
}
2023-03-13 07:28:58 +00:00
uint32_t sec_count = tinyusb_msc_storage_get_sector_count ( ) ;
uint32_t sec_size = tinyusb_msc_storage_get_sector_size ( ) ;
2022-12-19 05:35:59 +00:00
printf ( " Storage Capacity %lluMB \n " , ( ( uint64_t ) sec_count ) * sec_size / ( 1024 * 1024 ) ) ;
2022-10-31 19:42:05 +00:00
return 0 ;
}
// exit from application
2023-03-13 07:28:58 +00:00
static int console_status ( int argc , char * * argv )
2022-10-31 19:42:05 +00:00
{
2023-03-13 07:28:58 +00:00
printf ( " storage exposed over USB: %s \n " , tinyusb_msc_storage_in_use_by_usb_host ( ) ? " Yes " : " No " ) ;
2022-10-31 19:42:05 +00:00
return 0 ;
}
// exit from application
2023-03-13 07:28:58 +00:00
static int console_exit ( int argc , char * * argv )
2022-10-31 19:42:05 +00:00
{
2023-05-15 12:56:47 +00:00
tinyusb_msc_unregister_callback ( TINYUSB_MSC_EVENT_MOUNT_CHANGED ) ;
2023-03-13 07:28:58 +00:00
tinyusb_msc_storage_deinit ( ) ;
2022-10-31 19:42:05 +00:00
printf ( " Application Exiting \n " ) ;
exit ( 0 ) ;
return 0 ;
}
2023-05-15 12:56:47 +00:00
// callback that is delivered when storage is mounted/unmounted by application.
static void storage_mount_changed_cb ( tinyusb_msc_event_t * event )
{
ESP_LOGI ( TAG , " Storage mounted to application: %s " , event - > mount_changed_data . is_mounted ? " Yes " : " No " ) ;
}
2023-03-13 07:28:58 +00:00
# ifdef CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH
static esp_err_t storage_init_spiflash ( wl_handle_t * wl_handle )
2022-10-31 19:42:05 +00:00
{
2023-03-13 07:28:58 +00:00
ESP_LOGI ( TAG , " Initializing wear levelling " ) ;
2022-10-31 19:42:05 +00:00
2023-03-13 07:28:58 +00:00
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 ;
}
2022-12-19 05:35:59 +00:00
2023-03-13 07:28:58 +00:00
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 " ) ;
2022-12-19 05:35:59 +00:00
// By default, SD card frequency is initialized to SDMMC_FREQ_DEFAULT (20MHz)
// For setting a specific frequency, use host.max_freq_khz (range 400kHz - 40MHz for SDMMC)
// Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000;
sdmmc_host_t host = SDMMC_HOST_DEFAULT ( ) ;
// This initializes the slot without card detect (CD) and write protect (WP) signals.
// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT ( ) ;
// For SD Card, set bus width to use
# ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
slot_config . width = 4 ;
# else
slot_config . width = 1 ;
# endif // CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
// On chips where the GPIOs used for SD card can be configured, set the user defined values
# ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
slot_config . clk = CONFIG_EXAMPLE_PIN_CLK ;
slot_config . cmd = CONFIG_EXAMPLE_PIN_CMD ;
slot_config . d0 = CONFIG_EXAMPLE_PIN_D0 ;
# ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
slot_config . d1 = CONFIG_EXAMPLE_PIN_D1 ;
slot_config . d2 = CONFIG_EXAMPLE_PIN_D2 ;
slot_config . d3 = CONFIG_EXAMPLE_PIN_D3 ;
# endif // CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
# endif // CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
// Enable internal pullups on enabled pins. The internal pullups
// are insufficient however, please make sure 10k external pullups are
// connected on the bus. This is for debug / example purpose only.
slot_config . flags | = SDMMC_SLOT_FLAG_INTERNAL_PULLUP ;
2023-03-13 07:28:58 +00:00
// 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 ;
2022-12-19 05:35:59 +00:00
2023-03-13 07:28:58 +00:00
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 ;
}
2022-12-19 05:35:59 +00:00
# endif // CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH
2023-03-13 07:28:58 +00:00
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 = {
2023-05-15 12:56:47 +00:00
. wl_handle = wl_handle ,
2023-10-04 18:42:39 +00:00
. callback_mount_changed = storage_mount_changed_cb , /* First way to register the callback. This is while initializing the storage. */
. mount_config . max_files = 5 ,
2023-03-13 07:28:58 +00:00
} ;
ESP_ERROR_CHECK ( tinyusb_msc_storage_init_spiflash ( & config_spi ) ) ;
2023-05-15 12:56:47 +00:00
ESP_ERROR_CHECK ( tinyusb_msc_register_callback ( TINYUSB_MSC_EVENT_MOUNT_CHANGED , storage_mount_changed_cb ) ) ; /* Other way to register the callback i.e. registering using separate API. If the callback had been already registered, it will be overwritten. */
2023-03-13 07:28:58 +00:00
# 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 = {
2023-05-15 12:56:47 +00:00
. card = card ,
2023-10-04 18:42:39 +00:00
. callback_mount_changed = storage_mount_changed_cb , /* First way to register the callback. This is while initializing the storage. */
. mount_config . max_files = 5 ,
2023-03-13 07:28:58 +00:00
} ;
ESP_ERROR_CHECK ( tinyusb_msc_storage_init_sdmmc ( & config_sdmmc ) ) ;
2023-05-15 12:56:47 +00:00
ESP_ERROR_CHECK ( tinyusb_msc_register_callback ( TINYUSB_MSC_EVENT_MOUNT_CHANGED , storage_mount_changed_cb ) ) ; /* Other way to register the callback i.e. registering using separate API. If the callback had been already registered, it will be overwritten. */
2023-03-13 07:28:58 +00:00
# endif // CONFIG_EXAMPLE_STORAGE_MEDIA_SPIFLASH
2022-12-19 05:35:59 +00:00
//mounted in the app by default
_mount ( ) ;
2022-10-31 19:42:05 +00:00
ESP_LOGI ( TAG , " USB MSC initialization " ) ;
const tinyusb_config_t tusb_cfg = {
. device_descriptor = & descriptor_config ,
. string_descriptor = string_desc_arr ,
2023-02-23 10:16:37 +00:00
. string_descriptor_count = sizeof ( string_desc_arr ) / sizeof ( string_desc_arr [ 0 ] ) ,
2022-10-31 19:42:05 +00:00
. external_phy = false ,
. configuration_descriptor = desc_configuration ,
} ;
ESP_ERROR_CHECK ( tinyusb_driver_install ( & tusb_cfg ) ) ;
ESP_LOGI ( TAG , " USB MSC initialization DONE " ) ;
esp_console_repl_t * repl = NULL ;
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT ( ) ;
/* Prompt to be printed before each line.
* This can be customized , made dynamic , etc .
*/
repl_config . prompt = PROMPT_STR " > " ;
repl_config . max_cmdline_length = 64 ;
esp_console_register_help_command ( ) ;
esp_console_dev_uart_config_t hw_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT ( ) ;
ESP_ERROR_CHECK ( esp_console_new_repl_uart ( & hw_config , & repl_config , & repl ) ) ;
2022-12-19 05:35:59 +00:00
for ( int count = 0 ; count < sizeof ( cmds ) / sizeof ( esp_console_cmd_t ) ; count + + ) {
2023-09-28 01:43:12 +00:00
ESP_ERROR_CHECK ( esp_console_cmd_register ( & cmds [ count ] ) ) ;
2022-12-19 05:35:59 +00:00
}
2022-10-31 19:42:05 +00:00
ESP_ERROR_CHECK ( esp_console_start_repl ( repl ) ) ;
}