2025-10-13 7:35 AM - edited 2025-10-13 7:43 AM
Hi ST community
// --- EDIT --- Missed preview and made some corrections / clarifications
I'm looking for a way to mount a SD card using the Azure RTOS UsbX MSC class, while the MCU is running. The code is mostly based on the ST examples, e.g.:
The example expects that the SD card is already inserted on power up - therefore the MX_SDMMC1_SD_Init() is called before the USBX device is registered. Therefore the LBA and Blocksize values are already available when the USBX device is registered.
I want to have a more general case, where I can remove and insert a SD card while the application is running. A separate thread (SD_thread_entry) which handles this behaviour and is waiting for the SD card pin to be detected. The card is then power reset, the HAL is initialized and the CardInfo read:
void SD_thread_entry(ULONG thread_input)
{
// Thread variables
ULONG flags;
bool isCardOk;
uint32_t sdCardstart;
// Thread init
if(GPIO_SD_isCardPresent()) {
tx_event_flags_set(&eventSd, EVT_CARD_INSERTED, TX_OR);
}
// Start of thread loop
while (true)
{
// Wait until card is detected
tx_event_flags_get(&eventSd, EVT_CARD_INSERTED, TX_OR_CLEAR, &flags, TX_WAIT_FOREVER);
// Setup SD card
// --------------------------------------------------------
// Power off SD card
printf("SD card power reset.\n");
GPIO_SD_powerEnable(false);
tx_thread_sleep(TX_TIMER_TICKS_PER_SECOND / 2); // wait 500ms
// Power on SD card
GPIO_SD_powerEnable(true);
tx_thread_sleep(TX_TIMER_TICKS_PER_SECOND / 2); // wait 500ms
// Check that card is still present
if (!GPIO_SD_isCardPresent())
{
continue;
}
// Init SD card
printf("SD card hardware init.\n");
MX_SDMMC1_SD_Init();
// Check card status
isCardOk = false;
sdCardstart = tx_time_get();
while (tx_time_get() - sdCardstart < 2000 && GPIO_SD_isCardPresent())
{
// fetch card state
HAL_SD_CardStateTypeDef state = HAL_SD_GetCardState(&hsd1);
if (state == HAL_SD_CARD_TRANSFER)
{
isCardOk = true;
break;
} else {
printf("SD card state: %lu, present: %d\n", state, GPIO_SD_isCardPresent());
}
// not yet ready - sleep 100ms
tx_thread_sleep(TX_TIMER_TICKS_PER_SECOND / 10);
}
if(!isCardOk) {
// Card error - TODO handle error
printf("SD card state failed\n");
continue;
}
// Get card info
if (HAL_SD_GetCardInfo(&hsd1, &SD_CardInfo) != HAL_OK)
{
// Card info error - TODO handle error
printf("SD card info failed\n");
continue;
}
// Update LBA and block size
USBX_updateSdParameters();
// Flag card ready
tx_event_flags_set(&eventSd, EVT_CARD_READY, TX_OR);
printf("SD card ready.\n");
// Card ready - wait for write requests or card removed events
while (true)
{
tx_event_flags_get(&eventSd, EVT_CARD_REMOVED, TX_OR_CLEAR, &flags, TX_WAIT_FOREVER);
if (flags & EVT_CARD_REMOVED)
{
break;
}
// perform SD card tasks
}
// Clear SD flags
tx_event_flags_get(&eventSd,
EVT_CARD_READY | EVT_READ_DONE | EVT_WRITE_DONE, TX_OR_CLEAR, &flags, TX_NO_WAIT);
printf("SD card not ready - waiting for insertion.\n");
// Deinit SD card
MX_SDMMC1_SD_DeInit();
}
}Only once the SD card is mounted, the blocksize and LBA are known. Therefore the following function is used to update the UsbX storage parameters:
VOID USBX_updateSdParameters() {
/* Initialize the storage class parameters for reading/writing to the Flash Disk */
storage_parameter.ux_slave_class_storage_parameter_lun[0].
ux_slave_class_storage_media_last_lba = USBD_STORAGE_GetMediaLastLba();
storage_parameter.ux_slave_class_storage_parameter_lun[0].
ux_slave_class_storage_media_block_length = USBD_STORAGE_GetMediaBlocklength();
}Furthermore, I implemented a simple media_status function, which just reports NO_SENSE (for card detected) or NOT_READY (for card not detected):
UINT USBD_STORAGE_Status(VOID *storage_instance, ULONG lun, ULONG media_id,
ULONG *media_status)
{
UINT status = UX_SUCCESS;
/* USER CODE BEGIN USBD_STORAGE_Status */
UX_PARAMETER_NOT_USED(storage_instance);
UX_PARAMETER_NOT_USED(lun);
UX_PARAMETER_NOT_USED(media_id);
// overwrite default success status
status = UX_ERROR;
// only report SD card if SD thread is ready
if (SD_isCardReady())
{
*media_status = UX_DEVICE_CLASS_STORAGE_SENSE_STATUS(UX_SLAVE_CLASS_STORAGE_SENSE_KEY_NO_SENSE, 0x00, 0x00);
return UX_SUCCESS; // media present
}
else
{
*media_status = UX_DEVICE_CLASS_STORAGE_SENSE_STATUS(UX_SLAVE_CLASS_STORAGE_SENSE_KEY_NOT_READY, 0x3A, 0x00);
return UX_ERROR; // media absent
}
/* USER CODE END USBD_STORAGE_Status */
return status;
}Each read/write/flush checks that the media status is correct or otherwise reports an UX_Error if not ready:
UINT USBD_STORAGE_Write(VOID *storage_instance, ULONG lun, UCHAR *data_pointer,
ULONG number_blocks, ULONG lba, ULONG *media_status)
{
UINT status = UX_SUCCESS;
/* USER CODE BEGIN USBD_STORAGE_Write */
if(UX_SUCCESS != USBD_STORAGE_Status(storage_instance, lun, 0, media_status)) {
return UX_ERROR;
}
status = SD_writeBlocks(data_pointer, number_blocks, lba);
/* USER CODE END USBD_STORAGE_Write */
return status;
}Is this the correct way to mount a SD card while the application is already running?
On a side note: I'm using a composite class of ACM (Virtual com port) and MSC (mass storage). Starting first with a ACM only class and then registering an addtional class later seems also not to work, probably because the enumeration of the device is already complete.
Unfortunately, the rtos docs regarding the media_status are not that clear and I don't really understand what to return there:
Thanks in advance and best regards
Daniel
2025-11-03 2:37 AM
Hi @DZimm.9
Thank you for your detailed inquiry and for sharing your implementation approach regarding dynamic SD card mounting with USBX MSC.
The scenario you are describing : handling SD card insertion/removal dynamically during runtime and updating the USB mass storage parameters accordingly, is indeed a more advanced and custom use case beyond the static initialization model provided in the standard examples.
An internal ticket is submitted to dedicated team (220990)
About On a side note: I'm using a composite class of ACM (Virtual com port) and MSC (mass storage). Starting first with a ACM only class and then registering an addtional class later seems also not to work, probably because the enumeration of the device is already complete.
> Indeed, you cannot switch between classes while USB peripheral is already running!
To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
2025-11-03 2:47 AM
Hi FBL
I unintentionally clicked marked as solution - can you remove this?
In the meantime, I found a solution which works, however I'm not sure if this is really the proper way to do it. There is a SCSI code to notify the host on a medium change. When the card insertion is detected, then an NOTIFY status is triggered, which updates the LBA and block size from the newly inserted SD card:
UINT USBD_STORAGE_Status(VOID *storage_instance, ULONG lun, ULONG media_id,
ULONG *media_status)
{
UINT status = UX_SUCCESS;
/* USER CODE BEGIN USBD_STORAGE_Status */
UX_PARAMETER_NOT_USED(media_id);
// overwrite default success status
status = UX_SUCCESS;
// Get media status
tSdStatus sdStatus = SD_getMediaStatus();
switch(sdStatus) {
case SD_STATUS_NOTIFY_HOST:
// Update LBA and BlockSize
UX_SLAVE_CLASS_STORAGE *storage = (UX_SLAVE_CLASS_STORAGE *)storage_instance;
UX_SLAVE_CLASS_STORAGE_LUN *lun_ptr = &storage->ux_slave_class_storage_lun[lun];
lun_ptr->ux_slave_class_storage_media_last_lba = SD_getMediaLastLba();
lun_ptr->ux_slave_class_storage_media_block_length = SD_getMediaBlockLength();
// SCSI SENSE: NOT READY TO READY TRANSITION – MEDIA CHANGED
*media_status = UX_DEVICE_CLASS_STORAGE_SENSE_STATUS(UX_SLAVE_CLASS_STORAGE_SENSE_KEY_UNIT_ATTENTION, 0x28, 0x00);
break;
case SD_STATUS_NOT_PRESENT:
// SCSI SENSE: MEDIUM NOT PRESENT
*media_status = UX_DEVICE_CLASS_STORAGE_SENSE_STATUS(UX_SLAVE_CLASS_STORAGE_SENSE_KEY_NOT_READY, 0x3A, 0x00);
break;
case SD_STATUS_INITIALIZING:
// SCSI SENSE: LOGICAL DRIVE NOT READY - BECOMING READY
*media_status = UX_DEVICE_CLASS_STORAGE_SENSE_STATUS(UX_SLAVE_CLASS_STORAGE_SENSE_KEY_NOT_READY, 0x04, 0x01);
break;
case SD_STATUS_READY:
// SCSI SENSE: NO SENSE - card ready / no error
*media_status = UX_DEVICE_CLASS_STORAGE_SENSE_STATUS(UX_SLAVE_CLASS_STORAGE_SENSE_KEY_NO_SENSE, 0x00, 0x00);
break;
case SD_STATUS_UNKNOWN:
// SCSI SENSE: UNKNOWN error
*media_status = UX_DEVICE_CLASS_STORAGE_SENSE_STATUS(UX_SLAVE_CLASS_STORAGE_SENSE_KEY_NO_SENSE, 0xFF, 0xFF);
break;
}
// Debug reporting
static ULONG lastMediaStatus = -1;
if(*media_status != lastMediaStatus) {
lastMediaStatus = *media_status;
printf("USB media status: %06X\n", (unsigned int)(*media_status));
}
// if other than ready, report UX_ERROR to trigger host status update
if(sdStatus != SD_STATUS_READY) {
return UX_ERROR;
}
/* USER CODE END USBD_STORAGE_Status */
return status;
}
All other MSC functions return UX_ERROR aslong the status is other than "NO SENSE" == 0. E.g. flush:
UINT USBD_STORAGE_Flush(VOID *storage_instance, ULONG lun, ULONG number_blocks,
ULONG lba, ULONG *media_status)
{
UINT status = UX_SUCCESS;
/* USER CODE BEGIN USBD_STORAGE_Flush */
status = UX_ERROR;
USBD_STORAGE_Status(storage_instance, lun, 0, media_status);
if(*media_status == 0) {
status = SD_flush();
}
/* USER CODE END USBD_STORAGE_Flush */
return status;
}Best regards
Daniel