From cf9bc9bdabfd8f067182b6cd506ff1c7b04b69ea Mon Sep 17 00:00:00 2001 From: Patrick Moessler Date: Thu, 13 Jun 2024 13:11:43 +0200 Subject: [PATCH] add psram tinyusb drive --- main/tusb_composite_main.c | 44 +- main/tusb_msc_psram.c | 1095 +++++++++++++++++------------------- main/tusb_msc_psram.h | 126 +++++ 3 files changed, 666 insertions(+), 599 deletions(-) diff --git a/main/tusb_composite_main.c b/main/tusb_composite_main.c index 332b508..35f6443 100644 --- a/main/tusb_composite_main.c +++ b/main/tusb_composite_main.c @@ -7,9 +7,11 @@ #include #include #include +#include "esp_heap_caps.h" #include "esp_partition.h" #include "esp_check.h" #include "tinyusb.h" +#include "tusb_msc_psram.h" #include "tusb_msc_storage.h" #include "tusb_cdc_acm.h" @@ -92,31 +94,41 @@ static void file_operations(void) ESP_LOGI(TAG, "Read from file: '%s'", line); } -static esp_err_t storage_init_spiflash(wl_handle_t *wl_handle) -{ - ESP_LOGI(TAG, "Initializing wear levelling"); +// 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; - } +// 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); -} +// return wl_mount(data_partition, wl_handle); +// } void app_main(void) { ESP_LOGI(TAG, "Initializing storage..."); - static wl_handle_t wl_handle = WL_INVALID_HANDLE; - ESP_ERROR_CHECK(storage_init_spiflash(&wl_handle)); + uint8_t* const psram_fs_mem = heap_caps_malloc(4*1024*1024, MALLOC_CAP_SPIRAM); - const tinyusb_msc_spiflash_config_t config_spi = { - .wl_handle = wl_handle + const tinyusb_msc_psram_config_t config_psram = { + .memory=psram_fs_mem, + .memory_size = 4*1024*1024 }; - ESP_ERROR_CHECK(tinyusb_msc_storage_init_spiflash(&config_spi)); - ESP_ERROR_CHECK(tinyusb_msc_storage_mount(BASE_PATH)); + + ESP_ERROR_CHECK(tinyusb_msc_storage_init_psram(&config_psram)); + ESP_ERROR_CHECK(tinyusb_msc_psram_mount(BASE_PATH)); + + // 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)); + // ESP_ERROR_CHECK(tinyusb_msc_storage_mount(BASE_PATH)); file_operations(); ESP_LOGI(TAG, "USB Composite initialization"); diff --git a/main/tusb_msc_psram.c b/main/tusb_msc_psram.c index 7f1467a..50398f6 100644 --- a/main/tusb_msc_psram.c +++ b/main/tusb_msc_psram.c @@ -5,12 +5,15 @@ */ #include +#include "common/tusb_common.h" +#include "diskio.h" #include "esp_log.h" #include "esp_err.h" #include "esp_check.h" #include "esp_vfs_fat.h" #include "diskio_impl.h" #include "esp_partition.h" +#include "tusb_msc_psram.h" #include "vfs_fat_internal.h" #include "tinyusb.h" #include "class/msc/msc_device.h" @@ -22,15 +25,11 @@ static const char *TAG = "tinyusb_msc_psram"; typedef struct { bool is_fat_mounted; const char *base_path; - union { - uint8_t* memory; - }; + uint8_t* memory; + size_t sectors; + uint8_t pdrv; esp_err_t (*mount)(BYTE pdrv); esp_err_t (*unmount)(void); - uint32_t (*sector_count)(void); - uint32_t (*sector_size)(void); - esp_err_t (*read)(size_t sector_size, uint32_t lba, uint32_t offset, size_t size, void *dest); - esp_err_t (*write)(size_t sector_size, size_t addr, uint32_t lba, uint32_t offset, size_t size, const void *src); tusb_msc_callback_t callback_mount_changed; tusb_msc_callback_t callback_premount_changed; int max_files; @@ -40,7 +39,29 @@ typedef struct { static tinyusb_msc_psram_handle_s *s_storage_handle; +static bool _psram_read (uint32_t lba, uint32_t offset,size_t size, uint8_t *dest) { + /*!< sector read function */ + if(!s_storage_handle || !s_storage_handle->memory){ + return false; + } + + memcpy(dest, s_storage_handle->memory + lba * FF_MAX_SS + offset, size); + return true; +} +static bool _psram_write (uint32_t lba, uint32_t offset,size_t size, const uint8_t *src) { + /*!< sector write function */ + if(!s_storage_handle || !s_storage_handle->memory){ + return false; + } + + memcpy(s_storage_handle->memory + lba * FF_MAX_SS + offset, src, size); + return true; +} + + + +static DSTATUS _diskio_psram_status (unsigned char pdrv); static DSTATUS _diskio_psram_init (unsigned char pdrv) { /*!< disk initialization function */ @@ -48,631 +69,539 @@ static DSTATUS _diskio_psram_init (unsigned char pdrv) { } static DSTATUS _diskio_psram_status (unsigned char pdrv) { - /*!< disk status check function */ - if(s_storage_handle && s_storage_handle->memory) { + /*!< disk status check function */ + if(s_storage_handle && s_storage_handle->memory && s_storage_handle->pdrv == pdrv) { return 0; } return STA_NOINIT; } static DRESULT _diskio_psram_read (unsigned char pdrv, unsigned char* buff, uint32_t sector, unsigned count) { - /*!< sector read function */ - FF_MAX_SS + /*!< sector read function */ + if(!s_storage_handle || !s_storage_handle->memory || pdrv!=s_storage_handle->pdrv){ + return RES_NOTRDY; + } + if(_psram_read(sector, 0, FF_MAX_SS*count, buff)){ + return RES_OK; + } + return RES_NOTRDY; } static DRESULT _diskio_psram_write (unsigned char pdrv, const unsigned char* buff, uint32_t sector, unsigned count) { /*!< sector write function */ + if(!s_storage_handle || !s_storage_handle->memory || pdrv!=s_storage_handle->pdrv){ + return RES_NOTRDY; + } + if(_psram_write(sector, 0, FF_MAX_SS*count, buff)){ + return RES_OK; + } + return RES_NOTRDY; } -static DRESULT _diskio_psram_ioctl (unsigned char pdrv, unsigned char cmd, void* buff) { - /*!< function to get info about disk and do some misc operations */ +static DRESULT _diskio_psram_ioctl(unsigned char pdrv, unsigned char cmd, + void *buff) { + /*!< function to get info about disk and do some misc operations */ + if (!s_storage_handle || !s_storage_handle->memory || + pdrv != s_storage_handle->pdrv) { + return RES_NOTRDY; + } + switch (cmd) { + case CTRL_SYNC: + /*nothing to do*/ + break; + case GET_SECTOR_COUNT: + ((unsigned int *)buff)[0] = s_storage_handle->sectors; + break; + case GET_SECTOR_SIZE: + ((unsigned int *)buff)[0] = FF_MAX_SS; + break; + case GET_BLOCK_SIZE: + ((unsigned int *)buff)[0] = + 1; // byte access. todo: check if 4 makes sense + break; + case CTRL_TRIM: + /*nothing to do*/ + break; + default: + return RES_PARERR; + } + return RES_OK; } - - -// } ff_diskio_impl_t; +static ff_diskio_impl_t *s_diskio_psram; - - - - - - - - - -static esp_err_t _mount_spiflash(BYTE pdrv) +static esp_err_t _mount_psram(BYTE pdrv) { - return ff_diskio_register(pdrv, s_storage_handle->wl_handle); + ff_diskio_register(pdrv, s_diskio_psram); + s_storage_handle->pdrv = pdrv; + return ESP_OK; } -static esp_err_t _unmount_spiflash(void) +static esp_err_t _unmount_psram(void) { - BYTE pdrv; - pdrv = ff_diskio_get_pdrv_wl(s_storage_handle->wl_handle); - if (pdrv == 0xff) { + if (s_storage_handle->pdrv == 0xff) { ESP_LOGE(TAG, "Invalid state"); return ESP_ERR_INVALID_STATE; } - ff_diskio_clear_pdrv_wl(s_storage_handle->wl_handle); - char drv[3] = {(char)('0' + pdrv), ':', 0}; + char drv[3] = {(char)('0' + s_storage_handle->pdrv), ':', 0}; f_mount(0, drv, 0); - ff_diskio_unregister(pdrv); + ff_diskio_unregister(s_storage_handle->pdrv); return ESP_OK; } -// static uint32_t _get_sector_count_spiflash(void) -// { -// uint32_t result = 0; -// assert(s_storage_handle->wl_handle != WL_INVALID_HANDLE); -// size_t size = wl_sector_size(s_storage_handle->wl_handle); -// if (size == 0) { -// ESP_LOGW(TAG, "WL Sector size is zero !!!"); -// result = 0; -// } else { -// result = (uint32_t)(wl_size(s_storage_handle->wl_handle) / size); -// } -// return result; -// } +static esp_err_t msc_psram_read_sector(uint32_t lba, + uint32_t offset, + size_t size, + void *dest) +{ + assert(s_storage_handle); -// static uint32_t _get_sector_size_spiflash(void) -// { -// assert(s_storage_handle->wl_handle != WL_INVALID_HANDLE); -// return (uint32_t)wl_sector_size(s_storage_handle->wl_handle); -// } + if (_psram_read(lba, offset, size, dest)) { + return ESP_OK; + } + return ESP_FAIL; +} -// static esp_err_t _read_sector_spiflash(size_t sector_size, -// uint32_t lba, -// uint32_t offset, -// size_t size, -// void *dest) -// { -// size_t temp = 0; -// size_t addr = 0; // Address of the data to be read, relative to the beginning of the partition. -// ESP_RETURN_ON_FALSE(!__builtin_umul_overflow(lba, sector_size, &temp), ESP_ERR_INVALID_SIZE, TAG, "overflow lba %lu sector_size %u", lba, sector_size); -// ESP_RETURN_ON_FALSE(!__builtin_uadd_overflow(temp, offset, &addr), ESP_ERR_INVALID_SIZE, TAG, "overflow addr %u offset %lu", temp, offset); -// return wl_read(s_storage_handle->wl_handle, addr, dest, size); -// } +static esp_err_t msc_psram_write_sector(uint32_t lba, + uint32_t offset, + size_t size, + const void *src) +{ + assert(s_storage_handle); + if (s_storage_handle->is_fat_mounted) { + ESP_LOGE(TAG, "can't write, FAT mounted"); + return ESP_ERR_INVALID_STATE; + } + size_t sector_size = tinyusb_msc_psram_get_sector_size(); + size_t temp = 0; + size_t addr = 0; // Address of the data to be read, relative to the beginning of the partition. + ESP_RETURN_ON_FALSE(!__builtin_umul_overflow(lba, sector_size, &temp), ESP_ERR_INVALID_SIZE, TAG, "overflow lba %lu sector_size %u", lba, sector_size); + ESP_RETURN_ON_FALSE(!__builtin_uadd_overflow(temp, offset, &addr), ESP_ERR_INVALID_SIZE, TAG, "overflow addr %u offset %lu", temp, offset); -// static esp_err_t _write_sector_spiflash(size_t sector_size, -// size_t addr, -// uint32_t lba, -// uint32_t offset, -// size_t size, -// const void *src) -// { -// ESP_RETURN_ON_ERROR(wl_erase_range(s_storage_handle->wl_handle, addr, size), -// TAG, "Failed to erase"); -// return wl_write(s_storage_handle->wl_handle, addr, src, size); -// } - -// #if SOC_SDMMC_HOST_SUPPORTED -// static esp_err_t _mount_sdmmc(BYTE pdrv) -// { -// ff_diskio_register_sdmmc(pdrv, s_storage_handle->card); -// ff_sdmmc_set_disk_status_check(pdrv, false); -// return ESP_OK; -// } - -// static esp_err_t _unmount_sdmmc(void) -// { -// BYTE pdrv; -// pdrv = ff_diskio_get_pdrv_card(s_storage_handle->card); -// if (pdrv == 0xff) { -// ESP_LOGE(TAG, "Invalid state"); -// return ESP_ERR_INVALID_STATE; -// } - -// char drv[3] = {(char)('0' + pdrv), ':', 0}; -// f_mount(0, drv, 0); -// ff_diskio_unregister(pdrv); - -// return ESP_OK; -// } - -// static uint32_t _get_sector_count_sdmmc(void) -// { -// assert(s_storage_handle->card); -// return (uint32_t)s_storage_handle->card->csd.capacity; -// } - -// static uint32_t _get_sector_size_sdmmc(void) -// { -// assert(s_storage_handle->card); -// return (uint32_t)s_storage_handle->card->csd.sector_size; -// } - -// static esp_err_t _read_sector_sdmmc(size_t sector_size, -// uint32_t lba, -// uint32_t offset, -// size_t size, -// void *dest) -// { -// return sdmmc_read_sectors(s_storage_handle->card, dest, lba, size / sector_size); -// } - -// static esp_err_t _write_sector_sdmmc(size_t sector_size, -// size_t addr, -// uint32_t lba, -// uint32_t offset, -// size_t size, -// const void *src) -// { -// return sdmmc_write_sectors(s_storage_handle->card, src, lba, size / sector_size); -// } -// #endif - -// static esp_err_t msc_storage_read_sector(uint32_t lba, -// uint32_t offset, -// size_t size, -// void *dest) -// { -// assert(s_storage_handle); -// size_t sector_size = tinyusb_msc_storage_get_sector_size(); -// return (s_storage_handle->read)(sector_size, lba, offset, size, dest); -// } - -// static esp_err_t msc_storage_write_sector(uint32_t lba, -// uint32_t offset, -// size_t size, -// const void *src) -// { -// assert(s_storage_handle); -// if (s_storage_handle->is_fat_mounted) { -// ESP_LOGE(TAG, "can't write, FAT mounted"); -// return ESP_ERR_INVALID_STATE; -// } -// size_t sector_size = tinyusb_msc_storage_get_sector_size(); -// size_t temp = 0; -// size_t addr = 0; // Address of the data to be read, relative to the beginning of the partition. -// ESP_RETURN_ON_FALSE(!__builtin_umul_overflow(lba, sector_size, &temp), ESP_ERR_INVALID_SIZE, TAG, "overflow lba %lu sector_size %u", lba, sector_size); -// ESP_RETURN_ON_FALSE(!__builtin_uadd_overflow(temp, offset, &addr), ESP_ERR_INVALID_SIZE, TAG, "overflow addr %u offset %lu", temp, offset); - -// if (addr % sector_size != 0 || size % sector_size != 0) { -// ESP_LOGE(TAG, "Invalid Argument lba(%lu) offset(%lu) size(%u) sector_size(%u)", lba, offset, size, sector_size); -// return ESP_ERR_INVALID_ARG; -// } -// return (s_storage_handle->write)(sector_size, addr, lba, offset, size, src); -// } - -// static esp_err_t _mount(char *drv, FATFS *fs) -// { -// void *workbuf = NULL; -// const size_t workbuf_size = 4096; -// esp_err_t ret; -// // 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_LOGW(TAG, "formatting card, allocation unit size=%d", alloc_unit_size); -// const MKFS_PARM opt = {(BYTE)FM_FAT, 0, 0, 0, alloc_unit_size}; -// 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; -// 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; -// } -// } -// return ESP_OK; -// fail: -// if (workbuf) { -// free(workbuf); -// } -// return ret; -// } - -// esp_err_t tinyusb_msc_storage_mount(const char *base_path) -// { -// esp_err_t ret = ESP_OK; -// assert(s_storage_handle); - -// if (s_storage_handle->is_fat_mounted) { -// return ESP_OK; -// } - -// tusb_msc_callback_t cb = s_storage_handle->callback_premount_changed; -// if (cb) { -// tinyusb_msc_event_t event = { -// .type = TINYUSB_MSC_EVENT_PREMOUNT_CHANGED, -// .mount_changed_data = { -// .is_mounted = s_storage_handle->is_fat_mounted -// } -// }; -// cb(&event); -// } - -// if (!base_path) { -// base_path = CONFIG_TINYUSB_MSC_MOUNT_PATH; -// } - -// // 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"); -// char drv[3] = {(char)('0' + pdrv), ':', 0}; - -// ESP_GOTO_ON_ERROR((s_storage_handle->mount)(pdrv), fail, TAG, "Failed pdrv=%d", pdrv); - -// FATFS *fs = NULL; -// ret = esp_vfs_fat_register(base_path, drv, s_storage_handle->max_files, &fs); -// if (ret == ESP_ERR_INVALID_STATE) { -// ESP_LOGD(TAG, "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; -// } - -// ESP_GOTO_ON_ERROR(_mount(drv, fs), fail, TAG, "Failed _mount"); - -// s_storage_handle->is_fat_mounted = true; -// s_storage_handle->base_path = base_path; - -// cb = s_storage_handle->callback_mount_changed; -// if (cb) { -// tinyusb_msc_event_t event = { -// .type = TINYUSB_MSC_EVENT_MOUNT_CHANGED, -// .mount_changed_data = { -// .is_mounted = s_storage_handle->is_fat_mounted -// } -// }; -// cb(&event); -// } - -// return ret; - -// fail: -// if (fs) { -// esp_vfs_fat_unregister_path(base_path); -// } -// ff_diskio_unregister(pdrv); -// s_storage_handle->is_fat_mounted = false; -// ESP_LOGW(TAG, "Failed to mount storage (0x%x)", ret); -// return ret; -// } - -// esp_err_t tinyusb_msc_storage_unmount(void) -// { -// if (!s_storage_handle) { -// return ESP_FAIL; -// } - -// if (!s_storage_handle->is_fat_mounted) { -// return ESP_OK; -// } - -// tusb_msc_callback_t cb = s_storage_handle->callback_premount_changed; -// if (cb) { -// tinyusb_msc_event_t event = { -// .type = TINYUSB_MSC_EVENT_PREMOUNT_CHANGED, -// .mount_changed_data = { -// .is_mounted = s_storage_handle->is_fat_mounted -// } -// }; -// cb(&event); -// } - -// esp_err_t err = (s_storage_handle->unmount)(); -// if (err) { -// return err; -// } -// err = esp_vfs_fat_unregister_path(s_storage_handle->base_path); -// s_storage_handle->base_path = NULL; -// s_storage_handle->is_fat_mounted = false; - -// cb = s_storage_handle->callback_mount_changed; -// if (cb) { -// tinyusb_msc_event_t event = { -// .type = TINYUSB_MSC_EVENT_MOUNT_CHANGED, -// .mount_changed_data = { -// .is_mounted = s_storage_handle->is_fat_mounted -// } -// }; -// cb(&event); -// } - -// return err; -// } - -// uint32_t tinyusb_msc_storage_get_sector_count(void) -// { -// assert(s_storage_handle); -// return (s_storage_handle->sector_count)(); -// } - -// uint32_t tinyusb_msc_storage_get_sector_size(void) -// { -// assert(s_storage_handle); -// return (s_storage_handle->sector_size)(); -// } - -// esp_err_t tinyusb_msc_storage_init_spiflash(const tinyusb_msc_spiflash_config_t *config) -// { -// assert(!s_storage_handle); -// s_storage_handle = (tinyusb_msc_psram_handle_s *)malloc(sizeof(tinyusb_msc_psram_handle_s)); -// ESP_RETURN_ON_FALSE(s_storage_handle, ESP_ERR_NO_MEM, TAG, "could not allocate new handle for storage"); -// s_storage_handle->mount = &_mount_spiflash; -// s_storage_handle->unmount = &_unmount_spiflash; -// s_storage_handle->sector_count = &_get_sector_count_spiflash; -// s_storage_handle->sector_size = &_get_sector_size_spiflash; -// s_storage_handle->read = &_read_sector_spiflash; -// s_storage_handle->write = &_write_sector_spiflash; -// s_storage_handle->is_fat_mounted = false; -// s_storage_handle->base_path = NULL; -// s_storage_handle->wl_handle = config->wl_handle; -// // In case the user does not set mount_config.max_files -// // and for backward compatibility with versions <1.4.2 -// // max_files is set to 2 -// const int max_files = config->mount_config.max_files; -// s_storage_handle->max_files = max_files > 0 ? max_files : 2; - -// /* Callbacks setting up*/ -// if (config->callback_mount_changed) { -// tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED, config->callback_mount_changed); -// } else { -// tinyusb_msc_unregister_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED); -// } -// if (config->callback_premount_changed) { -// tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_PREMOUNT_CHANGED, config->callback_premount_changed); -// } else { -// tinyusb_msc_unregister_callback(TINYUSB_MSC_EVENT_PREMOUNT_CHANGED); -// } - -// return ESP_OK; -// } - -// #if SOC_SDMMC_HOST_SUPPORTED -// esp_err_t tinyusb_msc_storage_init_sdmmc(const tinyusb_msc_sdmmc_config_t *config) -// { -// assert(!s_storage_handle); -// s_storage_handle = (tinyusb_msc_psram_handle_s *)malloc(sizeof(tinyusb_msc_psram_handle_s)); -// ESP_RETURN_ON_FALSE(s_storage_handle, ESP_ERR_NO_MEM, TAG, "could not allocate new handle for storage"); -// s_storage_handle->mount = &_mount_sdmmc; -// s_storage_handle->unmount = &_unmount_sdmmc; -// s_storage_handle->sector_count = &_get_sector_count_sdmmc; -// s_storage_handle->sector_size = &_get_sector_size_sdmmc; -// s_storage_handle->read = &_read_sector_sdmmc; -// s_storage_handle->write = &_write_sector_sdmmc; -// s_storage_handle->is_fat_mounted = false; -// s_storage_handle->base_path = NULL; -// s_storage_handle->card = config->card; -// // In case the user does not set mount_config.max_files -// // and for backward compatibility with versions <1.4.2 -// // max_files is set to 2 -// const int max_files = config->mount_config.max_files; -// s_storage_handle->max_files = max_files > 0 ? max_files : 2; - -// /* Callbacks setting up*/ -// if (config->callback_mount_changed) { -// tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED, config->callback_mount_changed); -// } else { -// tinyusb_msc_unregister_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED); -// } -// if (config->callback_premount_changed) { -// tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_PREMOUNT_CHANGED, config->callback_premount_changed); -// } else { -// tinyusb_msc_unregister_callback(TINYUSB_MSC_EVENT_PREMOUNT_CHANGED); -// } - -// return ESP_OK; -// } -// #endif - -// void tinyusb_msc_storage_deinit(void) -// { -// assert(s_storage_handle); -// free(s_storage_handle); -// s_storage_handle = NULL; -// } - -// esp_err_t tinyusb_msc_register_callback(tinyusb_msc_event_type_t event_type, -// tusb_msc_callback_t callback) -// { -// assert(s_storage_handle); -// switch (event_type) { -// case TINYUSB_MSC_EVENT_MOUNT_CHANGED: -// s_storage_handle->callback_mount_changed = callback; -// return ESP_OK; -// case TINYUSB_MSC_EVENT_PREMOUNT_CHANGED: -// s_storage_handle->callback_premount_changed = callback; -// return ESP_OK; -// default: -// ESP_LOGE(TAG, "Wrong event type"); -// return ESP_ERR_INVALID_ARG; -// } -// } - -// esp_err_t tinyusb_msc_unregister_callback(tinyusb_msc_event_type_t event_type) -// { -// assert(s_storage_handle); -// switch (event_type) { -// case TINYUSB_MSC_EVENT_MOUNT_CHANGED: -// s_storage_handle->callback_mount_changed = NULL; -// return ESP_OK; -// case TINYUSB_MSC_EVENT_PREMOUNT_CHANGED: -// s_storage_handle->callback_premount_changed = NULL; -// return ESP_OK; -// default: -// ESP_LOGE(TAG, "Wrong event type"); -// return ESP_ERR_INVALID_ARG; -// } -// } - -// bool tinyusb_msc_storage_in_use_by_usb_host(void) -// { -// assert(s_storage_handle); -// return !s_storage_handle->is_fat_mounted; -// } + if (addr % sector_size != 0 || size % sector_size != 0) { + ESP_LOGE(TAG, "Invalid Argument lba(%lu) offset(%lu) size(%u) sector_size(%u)", lba, offset, size, sector_size); + return ESP_ERR_INVALID_ARG; + } -// /* TinyUSB MSC callbacks -// ********************************************************************* */ + if (_psram_write(lba, offset, size, src)) { + return ESP_OK; + } + return ESP_FAIL; -// /** 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 +} -// // 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; -// const char vid[] = "TinyUSB"; -// const char pid[] = "Flash Storage"; -// const char rev[] = "0.1"; +static esp_err_t _mount(char *drv, FATFS *fs) +{ + void *workbuf = NULL; + const size_t workbuf_size = 4096; + esp_err_t ret; + // 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_LOGW(TAG, "formatting card, allocation unit size=%d", alloc_unit_size); + const MKFS_PARM opt = {(BYTE)FM_FAT, 0, 0, 0, alloc_unit_size}; + 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; + 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; + } + } + return ESP_OK; +fail: + if (workbuf) { + free(workbuf); + } + return ret; +} -// memcpy(vendor_id, vid, strlen(vid)); -// memcpy(product_id, pid, strlen(pid)); -// memcpy(product_rev, rev, strlen(rev)); -// } +esp_err_t tinyusb_msc_psram_mount(const char *base_path) +{ + esp_err_t ret = ESP_OK; + assert(s_storage_handle); -// // 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; -// bool result = false; + if (s_storage_handle->is_fat_mounted) { + return ESP_OK; + } -// if (s_storage_handle->is_fat_mounted) { -// tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, SCSI_CODE_ASC_MEDIUM_NOT_PRESENT, SCSI_CODE_ASCQ); -// result = false; -// } else { -// if (tinyusb_msc_storage_unmount() != ESP_OK) { -// ESP_LOGW(TAG, "tud_msc_test_unit_ready_cb() unmount Fails"); -// } -// result = true; -// } -// return result; -// } + tusb_msc_callback_t cb = s_storage_handle->callback_premount_changed; + if (cb) { + tinyusb_msc_event_t event = { + .type = TINYUSB_MSC_EVENT_PREMOUNT_CHANGED, + .mount_changed_data = { + .is_mounted = s_storage_handle->is_fat_mounted + } + }; + cb(&event); + } -// // 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; + if (!base_path) { + base_path = CONFIG_TINYUSB_MSC_MOUNT_PATH; + } -// uint32_t sec_count = tinyusb_msc_storage_get_sector_count(); -// uint32_t sec_size = tinyusb_msc_storage_get_sector_size(); -// *block_count = sec_count; -// *block_size = (uint16_t)sec_size; -// } + // 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"); + char drv[3] = {(char)('0' + pdrv), ':', 0}; -// // 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_GOTO_ON_ERROR((s_storage_handle->mount)(pdrv), fail, TAG, "Failed pdrv=%d", pdrv); -// if (load_eject && !start) { -// if (tinyusb_msc_storage_mount(s_storage_handle->base_path) != ESP_OK) { -// ESP_LOGW(TAG, "tud_msc_start_stop_cb() mount Fails"); -// } -// } -// return true; -// } + FATFS *fs = NULL; + ret = esp_vfs_fat_register(base_path, drv, s_storage_handle->max_files, &fs); + if (ret == ESP_ERR_INVALID_STATE) { + ESP_LOGD(TAG, "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; + } -// // 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_err_t err = msc_storage_read_sector(lba, offset, bufsize, buffer); -// if (err != ESP_OK) { -// ESP_LOGE(TAG, "msc_storage_read_sector failed: 0x%x", err); -// return 0; -// } -// return bufsize; -// } + ESP_GOTO_ON_ERROR(_mount(drv, fs), fail, TAG, "Failed _mount"); -// // 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_err_t err = msc_storage_write_sector(lba, offset, bufsize, buffer); -// if (err != ESP_OK) { -// ESP_LOGE(TAG, "msc_storage_write_sector failed: 0x%x", err); -// return 0; -// } -// return bufsize; -// } + s_storage_handle->is_fat_mounted = true; + s_storage_handle->base_path = base_path; -// /** -// * 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; + cb = s_storage_handle->callback_mount_changed; + if (cb) { + tinyusb_msc_event_t event = { + .type = TINYUSB_MSC_EVENT_MOUNT_CHANGED, + .mount_changed_data = { + .is_mounted = s_storage_handle->is_fat_mounted + } + }; + cb(&event); + } -// 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. */ -// 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; -// } + return ret; -// // Invoked when device is unmounted -// void tud_umount_cb(void) -// { -// if (tinyusb_msc_storage_mount(s_storage_handle->base_path) != ESP_OK) { -// ESP_LOGW(TAG, "tud_umount_cb() mount Fails"); -// } -// } +fail: + if (fs) { + esp_vfs_fat_unregister_path(base_path); + } + ff_diskio_unregister(pdrv); + s_storage_handle->is_fat_mounted = false; + ESP_LOGW(TAG, "Failed to mount storage (0x%x)", ret); + return ret; +} -// // Invoked when device is mounted (configured) -// void tud_mount_cb(void) -// { -// tinyusb_msc_storage_unmount(); -// } -// /*********************************************************************** TinyUSB MSC callbacks*/ +esp_err_t tinyusb_msc_psram_unmount(void) +{ + if (!s_storage_handle) { + return ESP_FAIL; + } + + if (!s_storage_handle->is_fat_mounted) { + return ESP_OK; + } + + tusb_msc_callback_t cb = s_storage_handle->callback_premount_changed; + if (cb) { + tinyusb_msc_event_t event = { + .type = TINYUSB_MSC_EVENT_PREMOUNT_CHANGED, + .mount_changed_data = { + .is_mounted = s_storage_handle->is_fat_mounted + } + }; + cb(&event); + } + + esp_err_t err = (s_storage_handle->unmount)(); + if (err) { + return err; + } + err = esp_vfs_fat_unregister_path(s_storage_handle->base_path); + s_storage_handle->base_path = NULL; + s_storage_handle->is_fat_mounted = false; + + cb = s_storage_handle->callback_mount_changed; + if (cb) { + tinyusb_msc_event_t event = { + .type = TINYUSB_MSC_EVENT_MOUNT_CHANGED, + .mount_changed_data = { + .is_mounted = s_storage_handle->is_fat_mounted + } + }; + cb(&event); + } + + return err; +} + +uint32_t tinyusb_msc_psram_get_sector_count(void) +{ + assert(s_storage_handle); + return s_storage_handle->sectors; +} + +uint32_t tinyusb_msc_psram_get_sector_size(void) +{ + return FF_MAX_SS; +} + +esp_err_t tinyusb_msc_storage_init_psram(const tinyusb_msc_psram_config_t *config) +{ + assert(!s_storage_handle); + assert(!s_diskio_psram); + assert(config); + assert(config->memory); + + s_diskio_psram = (ff_diskio_impl_t *)malloc(sizeof(ff_diskio_impl_t)); + ESP_RETURN_ON_FALSE(s_diskio_psram, ESP_ERR_NO_MEM, TAG, "could not allocate new handle for diskio_psram"); + + s_diskio_psram->init = &_diskio_psram_init; + s_diskio_psram->status = &_diskio_psram_status; + s_diskio_psram->read = &_diskio_psram_read; + s_diskio_psram->write = &_diskio_psram_write; + s_diskio_psram->ioctl = &_diskio_psram_ioctl; + + s_storage_handle = (tinyusb_msc_psram_handle_s *)malloc(sizeof(tinyusb_msc_psram_handle_s)); + ESP_RETURN_ON_FALSE(s_storage_handle, ESP_ERR_NO_MEM, TAG, "could not allocate new handle for storage"); + + s_storage_handle->mount = &_mount_psram; + s_storage_handle->unmount = &_unmount_psram; + s_storage_handle->is_fat_mounted = false; + s_storage_handle->base_path = NULL; + s_storage_handle->memory = config->memory; + s_storage_handle->sectors = config->memory_size / FF_MAX_SS; + // In case the user does not set mount_config.max_files + // and for backward compatibility with versions <1.4.2 + // max_files is set to 2 + const int max_files = config->mount_config.max_files; + s_storage_handle->max_files = max_files > 0 ? max_files : 2; + + /* Callbacks setting up*/ + if (config->callback_mount_changed) { + tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED, config->callback_mount_changed); + } else { + tinyusb_msc_unregister_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED); + } + if (config->callback_premount_changed) { + tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_PREMOUNT_CHANGED, config->callback_premount_changed); + } else { + tinyusb_msc_unregister_callback(TINYUSB_MSC_EVENT_PREMOUNT_CHANGED); + } + + return ESP_OK; +} + +void tinyusb_msc_psram_deinit(void) +{ + assert(s_storage_handle); + free(s_storage_handle); + s_storage_handle = NULL; +} + +esp_err_t tinyusb_msc_register_callback(tinyusb_msc_event_type_t event_type, + tusb_msc_callback_t callback) +{ + assert(s_storage_handle); + switch (event_type) { + case TINYUSB_MSC_EVENT_MOUNT_CHANGED: + s_storage_handle->callback_mount_changed = callback; + return ESP_OK; + case TINYUSB_MSC_EVENT_PREMOUNT_CHANGED: + s_storage_handle->callback_premount_changed = callback; + return ESP_OK; + default: + ESP_LOGE(TAG, "Wrong event type"); + return ESP_ERR_INVALID_ARG; + } +} + +esp_err_t tinyusb_msc_unregister_callback(tinyusb_msc_event_type_t event_type) +{ + assert(s_storage_handle); + switch (event_type) { + case TINYUSB_MSC_EVENT_MOUNT_CHANGED: + s_storage_handle->callback_mount_changed = NULL; + return ESP_OK; + case TINYUSB_MSC_EVENT_PREMOUNT_CHANGED: + s_storage_handle->callback_premount_changed = NULL; + return ESP_OK; + default: + ESP_LOGE(TAG, "Wrong event type"); + return ESP_ERR_INVALID_ARG; + } +} + +bool tinyusb_msc_psram_in_use_by_usb_host(void) +{ + assert(s_storage_handle); + return !s_storage_handle->is_fat_mounted; +} + + +/* 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 + +// 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; + 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; + bool result = false; + + if (s_storage_handle->is_fat_mounted) { + tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, SCSI_CODE_ASC_MEDIUM_NOT_PRESENT, SCSI_CODE_ASCQ); + result = false; + } else { + if (tinyusb_msc_psram_unmount() != ESP_OK) { + ESP_LOGW(TAG, "tud_msc_test_unit_ready_cb() unmount Fails"); + } + result = true; + } + return result; +} + +// 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 = tinyusb_msc_psram_get_sector_count(); + uint32_t sec_size = tinyusb_msc_psram_get_sector_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; + + if (load_eject && !start) { + if (tinyusb_msc_psram_mount(s_storage_handle->base_path) != ESP_OK) { + ESP_LOGW(TAG, "tud_msc_start_stop_cb() mount Fails"); + } + } + 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_err_t err = msc_psram_read_sector(lba, offset, bufsize, buffer); + if (err != ESP_OK) { + ESP_LOGE(TAG, "msc_psram_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_err_t err = msc_psram_write_sector(lba, offset, bufsize, buffer); + if (err != ESP_OK) { + ESP_LOGE(TAG, "msc_psram_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; + + 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. */ + 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) +{ + if (tinyusb_msc_psram_mount(s_storage_handle->base_path) != ESP_OK) { + ESP_LOGW(TAG, "tud_umount_cb() mount Fails"); + } +} + +// Invoked when device is mounted (configured) +void tud_mount_cb(void) +{ + tinyusb_msc_psram_unmount(); +} +/*********************************************************************** TinyUSB MSC callbacks*/ diff --git a/main/tusb_msc_psram.h b/main/tusb_msc_psram.h index e69de29..1123e44 100644 --- a/main/tusb_msc_psram.h +++ b/main/tusb_msc_psram.h @@ -0,0 +1,126 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/** + * @brief Configuration structure for psram initialization + * + * User configurable parameters that are used while + * initializing the PSRAM media. + */ +typedef struct { + uint8_t* memory; /*!< pointer to memory in PSRAM */ + size_t memory_size; /*!< requested size. note: allocated file system may be smaller due to sector size */ + tusb_msc_callback_t callback_mount_changed; /*!< Pointer to the function callback that will be delivered AFTER mount/unmount operation is successfully finished */ + tusb_msc_callback_t callback_premount_changed; /*!< Pointer to the function callback that will be delivered BEFORE mount/unmount operation is started */ + const esp_vfs_fat_mount_config_t mount_config; /*!< FATFS mount config */ +} tinyusb_msc_psram_config_t; + + +/** + * @brief Register storage type psram with tinyusb driver + * + * @param config pointer to the psram configuration + * @return esp_err_t + * - ESP_OK, if success; + * - ESP_ERR_NO_MEM, if there was no memory to allocate storage components; + */ +esp_err_t tinyusb_msc_storage_init_psram(const tinyusb_msc_psram_config_t *config); + + +/** + * @brief Deregister storage with tinyusb driver and frees the memory + * + */ +void tinyusb_msc_psram_deinit(void); + +/** + * @brief Register a callback invoking on MSC event. If the callback had been + * already registered, it will be overwritten + * + * @param event_type - type of registered event for a callback + * @param callback - callback function + * @return esp_err_t - ESP_OK or ESP_ERR_INVALID_ARG + */ +esp_err_t tinyusb_msc_register_callback(tinyusb_msc_event_type_t event_type, + tusb_msc_callback_t callback); + + +/** + * @brief Unregister a callback invoking on MSC event. + * + * @param event_type - type of registered event for a callback + * @return esp_err_t - ESP_OK or ESP_ERR_INVALID_ARG + */ +esp_err_t tinyusb_msc_unregister_callback(tinyusb_msc_event_type_t event_type); + +/** + * @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. + * When this function is called from the tinyusb callback functions, care must be taken + * so as to make sure that user callbacks must be completed within a + * specific time. Otherwise, MSC device may re-appear again on Host. + * + * @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 tinyusb_msc_psram_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). + * When this function is called from the tinyusb callback functions, care must be taken + * so as to make sure that user callbacks must be completed within a specific time. + * Otherwise, MSC device may not appear on Host. + * + * @return esp_err_t + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if FATFS is not registered in VFS + */ +esp_err_t tinyusb_msc_psram_unmount(void); + +/** + * @brief Get number of sectors in storage media + * + * @return usable size, in bytes + */ +uint32_t tinyusb_msc_psram_get_sector_count(void); + +/** + * @brief Get sector size of storage media + * + * @return sector count + */ +uint32_t tinyusb_msc_psram_get_sector_size(void); + +/** + * @brief Get status if storage media is exposed over USB to Host + * + * @return bool + * - true, if the storage media is exposed to Host + * - false, if the stoarge media is mounted on application (not exposed to Host) + */ +bool tinyusb_msc_psram_in_use_by_usb_host(void); + + +#ifdef __cplusplus +} +#endif