Skip to content

feat(tusb_msc): Add device mount/unmount callback #122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
{
Comment on lines 656 to 657
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tud_umount_cb and tud_mount_cb are TinyUSB callbacks for the whole device. So they should not be defined at class level, but at device level (eg. in tinyusb.c)

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*/
Loading