Skip to content

Commit

Permalink
feat(tusb_msc): Add device mount/unmount callback
Browse files Browse the repository at this point in the history
  • Loading branch information
peter-marcisovsky committed Jan 20, 2025
1 parent f1ea66b commit 4230c66
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 41 deletions.
80 changes: 45 additions & 35 deletions device/esp_tinyusb/include/tusb_msc_storage.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -22,22 +22,24 @@ extern "C" {
* @brief Data provided to the input of the `callback_mount_changed` and `callback_premount_changed` callback
*/
typedef struct {
bool is_mounted; /*!< Flag if storage is mounted or not */
bool is_mounted; /*!< Flag if storage is mounted or not */
bool is_device_mounted; /*!< Flag if device is mounted or not */
} tinyusb_msc_event_mount_changed_data_t;

/**
* @brief Types of MSC events
*/
typedef enum {
TINYUSB_MSC_EVENT_MOUNT_CHANGED, /*!< Event type AFTER mount/unmount operation is successfully finished */
TINYUSB_MSC_EVENT_PREMOUNT_CHANGED /*!< Event type BEFORE mount/unmount operation is started */
TINYUSB_MSC_EVENT_MOUNT_CHANGED, /*!< Event type AFTER storage mount/unmount operation is successfully finished */
TINYUSB_MSC_EVENT_PREMOUNT_CHANGED, /*!< Event type BEFORE storage mount/unmount operation is started */
TINYUSB_MSC_EVENT_DEVICE_MOUNT_CHANGED, /*!< Event type at device mounted/unmounted event */
} tinyusb_msc_event_type_t;

/**
* @brief Describes an event passing to the input of a callbacks
*/
typedef struct {
tinyusb_msc_event_type_t type; /*!< Event type */
tinyusb_msc_event_type_t type; /*!< Event type */
union {
tinyusb_msc_event_mount_changed_data_t mount_changed_data; /*!< Data input of the callback */
};
Expand All @@ -56,10 +58,11 @@ typedef void(*tusb_msc_callback_t)(tinyusb_msc_event_t *event);
* initializing the sdmmc media.
*/
typedef struct {
sdmmc_card_t *card; /*!< Pointer to sdmmc card configuration structure */
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 */
sdmmc_card_t *card; /*!< Pointer to sdmmc card configuration structure */
tusb_msc_callback_t callback_mount_changed; /*!< Pointer to the function callback that will be delivered AFTER storage mount/unmount operation is successfully finished */
tusb_msc_callback_t callback_premount_changed; /*!< Pointer to the function callback that will be delivered BEFORE storage mount/unmount operation is started */
tusb_msc_callback_t callback_device_mount_changed; /*!< Pointer to the function callback that will be delivered when a device mounted/unmounted */
const esp_vfs_fat_mount_config_t mount_config; /*!< FATFS mount config */
} tinyusb_msc_sdmmc_config_t;
#endif

Expand All @@ -70,19 +73,20 @@ typedef struct {
* initializing the SPI Flash media.
*/
typedef struct {
wl_handle_t wl_handle; /*!< Pointer to spiflash wera-levelling handle */
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 */
wl_handle_t wl_handle; /*!< Pointer to spiflash wear-levelling handle */
tusb_msc_callback_t callback_mount_changed; /*!< Pointer to the function callback that will be delivered AFTER storage mount/unmount operation is successfully finished */
tusb_msc_callback_t callback_premount_changed; /*!< Pointer to the function callback that will be delivered BEFORE storage mount/unmount operation is started */
tusb_msc_callback_t callback_device_mount_changed; /*!< Pointer to the function callback that will be delivered when a device is unmounted, from tud_umount_cb() */
const esp_vfs_fat_mount_config_t mount_config; /*!< FATFS mount config */
} tinyusb_msc_spiflash_config_t;

/**
* @brief Register storage type spiflash with tinyusb driver
*
* @param config pointer to the spiflash configuration
* @return esp_err_t
* - ESP_OK, if success;
* - ESP_ERR_NO_MEM, if there was no memory to allocate storage components;
* @return
* - ESP_OK: SPI Flash storage initialized successfully
* - ESP_ERR_NO_MEM: if there was no memory to allocate storage components;
*/
esp_err_t tinyusb_msc_storage_init_spiflash(const tinyusb_msc_spiflash_config_t *config);

Expand All @@ -91,9 +95,9 @@ esp_err_t tinyusb_msc_storage_init_spiflash(const tinyusb_msc_spiflash_config_t
* @brief Register storage type sd-card with tinyusb driver
*
* @param config pointer to the sd card configuration
* @return esp_err_t
* - ESP_OK, if success;
* - ESP_ERR_NO_MEM, if there was no memory to allocate storage components;
* @return
* - ESP_OK: SDMMC storage initialized successfully
* - ESP_ERR_NO_MEM: There was no memory to allocate storage components;
*/
esp_err_t tinyusb_msc_storage_init_sdmmc(const tinyusb_msc_sdmmc_config_t *config);
#endif
Expand All @@ -109,17 +113,20 @@ void tinyusb_msc_storage_deinit(void);
*
* @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
* @return
* - ESP_OK: Callback successfully registered
* - ESP_ERR_INVALID_ARG: Invalid input argument - Invalid type of event
*/
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
* @return
* - ESP_OK: Callback successfully unregistered
* - ESP_ERR_INVALID_ARG: Invalid input argument - Invalid type of event
*/
esp_err_t tinyusb_msc_unregister_callback(tinyusb_msc_event_type_t event_type);

Expand All @@ -136,10 +143,10 @@ esp_err_t tinyusb_msc_unregister_callback(tinyusb_msc_event_type_t event_type);
* 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;
* @return
* - ESP_OK: Storage mounted successfully, or storage is already mounted
* - ESP_ERR_NOT_FOUND: The maximum count of volumes is already mounted
* - ESP_ERR_NO_MEM: Not enough memory, or too many VFSes already registered
*/
esp_err_t tinyusb_msc_storage_mount(const char *base_path);

Expand All @@ -154,32 +161,35 @@ esp_err_t tinyusb_msc_storage_mount(const char *base_path);
* 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
* @return
* - ESP_OK: Storage unmounted successfully, or storage is already unmounted
* - ESP_ERR_INVALID_STATE: FATFS is not registered in VFS
* - ESP_FAIL: Storage has been de-initialized
*/
esp_err_t tinyusb_msc_storage_unmount(void);

/**
* @brief Get number of sectors in storage media
*
* @return usable size, in bytes
* @return
* - usable size, in bytes
*/
uint32_t tinyusb_msc_storage_get_sector_count(void);

/**
* @brief Get sector size of storage media
*
* @return sector count
* @return
* - sector count
*/
uint32_t tinyusb_msc_storage_get_sector_size(void);

/**
* @brief Get status if storage media is exposed over USB to Host
* @brief Get status if storage media is exposed over USB to USB 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)
* @return
* - true: The storage media is exposed to USB Host
* - false: The storage media is mounted on application (not exposed to USB Host)
*/
bool tinyusb_msc_storage_in_use_by_usb_host(void);

Expand Down
68 changes: 62 additions & 6 deletions device/esp_tinyusb/tusb_msc_storage.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -18,6 +18,7 @@
#include "class/msc/msc_device.h"
#include "tusb_msc_storage.h"
#include "esp_vfs_fat.h"
#include "sdkconfig.h"
#if SOC_SDMMC_HOST_SUPPORTED
#include "diskio_sdmmc.h"
#endif
Expand All @@ -41,6 +42,7 @@ typedef struct {
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;
tusb_msc_callback_t callback_device_mount_changed;
int max_files;
} tinyusb_msc_storage_handle_s; /*!< MSC object */

Expand Down Expand Up @@ -393,18 +395,28 @@ esp_err_t tinyusb_msc_storage_init_spiflash(const tinyusb_msc_spiflash_config_t
const int max_files = config->mount_config.max_files;
s_storage_handle->max_files = max_files > 0 ? max_files : 2;

/* Callbacks setting up*/
// Callbacks setting up
// Storage mount changed callback
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);
}

// Storage premount changed callback
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);
}

// Device mount changed callback
if (config->callback_device_mount_changed) {
tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_DEVICE_MOUNT_CHANGED, config->callback_device_mount_changed);
} else {
tinyusb_msc_unregister_callback(TINYUSB_MSC_EVENT_DEVICE_MOUNT_CHANGED);
}

return ESP_OK;
}

Expand All @@ -429,18 +441,28 @@ esp_err_t tinyusb_msc_storage_init_sdmmc(const tinyusb_msc_sdmmc_config_t *confi
const int max_files = config->mount_config.max_files;
s_storage_handle->max_files = max_files > 0 ? max_files : 2;

/* Callbacks setting up*/
// Callbacks setting up
// Storage mount changed callback
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);
}

// Storage premount changed callback
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);
}

// Device mount changed callback
if (config->callback_device_mount_changed) {
tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_DEVICE_MOUNT_CHANGED, config->callback_device_mount_changed);
} else {
tinyusb_msc_unregister_callback(TINYUSB_MSC_EVENT_DEVICE_MOUNT_CHANGED);
}

return ESP_OK;
}
#endif
Expand All @@ -463,6 +485,9 @@ esp_err_t tinyusb_msc_register_callback(tinyusb_msc_event_type_t event_type,
case TINYUSB_MSC_EVENT_PREMOUNT_CHANGED:
s_storage_handle->callback_premount_changed = callback;
return ESP_OK;
case TINYUSB_MSC_EVENT_DEVICE_MOUNT_CHANGED:
s_storage_handle->callback_device_mount_changed = callback;
return ESP_OK;
default:
ESP_LOGE(TAG, "Wrong event type");
return ESP_ERR_INVALID_ARG;
Expand All @@ -479,6 +504,9 @@ esp_err_t tinyusb_msc_unregister_callback(tinyusb_msc_event_type_t event_type)
case TINYUSB_MSC_EVENT_PREMOUNT_CHANGED:
s_storage_handle->callback_premount_changed = NULL;
return ESP_OK;
case TINYUSB_MSC_EVENT_DEVICE_MOUNT_CHANGED:
s_storage_handle->callback_device_mount_changed = NULL;
return ESP_OK;
default:
ESP_LOGE(TAG, "Wrong event type");
return ESP_ERR_INVALID_ARG;
Expand Down Expand Up @@ -627,14 +655,42 @@ int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, u
// 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");
tusb_msc_callback_t device_umount_cb = s_storage_handle->callback_device_mount_changed;

// Check if device unmount callback is registered
// If not, automatically mount the storage to the FW at the device unmount event
if (device_umount_cb) {
tinyusb_msc_event_t event = {
.type = TINYUSB_MSC_EVENT_DEVICE_MOUNT_CHANGED,
.mount_changed_data = {
.is_device_mounted = false
}
};
device_umount_cb(&event);
} else {
if (tinyusb_msc_storage_mount(s_storage_handle->base_path) != ESP_OK) {
ESP_LOGW(TAG, "tud_umount_cb() storage mount Fails");
}
}
}

// Invoked when device is mounted (configured)
void tud_mount_cb(void)
{
tinyusb_msc_storage_unmount();
tusb_msc_callback_t device_mount_cb = s_storage_handle->callback_device_mount_changed;

// Check if device mount callback is registered
// If not, automatically unmount the storage from the FW (expose the storage to the USB Host) at the device mount event
if (device_mount_cb) {
tinyusb_msc_event_t event = {
.type = TINYUSB_MSC_EVENT_DEVICE_MOUNT_CHANGED,
.mount_changed_data = {
.is_device_mounted = true
}
};
device_mount_cb(&event);
} else {
tinyusb_msc_storage_unmount();
}
}
/*********************************************************************** TinyUSB MSC callbacks*/

0 comments on commit 4230c66

Please sign in to comment.