cancel
Showing results for 
Search instead for 
Did you mean: 

STM32Cube USB Host Mass Storage Class problem: some USB flash drives are not recognized

ds1
Associate II
Posted on October 06, 2014 at 13:46

I found a problem with the current version 1.3.0 of STM32CubeF4 library. As it turned out STM32Cube USB host middleware does not work correctly with some USB flash drives I have, particularly the old ones with a capacity of about 2 GB. They are not recognized after being plugged in. After a thorough debugging I found out that these USB flash drives return STALL packets for 'Get Max LUN' requests. This behavior is in fact fully compliant with USB standard:

http://www.usb.org/developers/docs/devclass_docs/usbmassbulk_pdf

USB Mass Storage Class Bulk-Only TransportGet Max LUN device request is used to determine the number of logical unitssupported by the device.Devices that do not support multiple LUNs may STALL this command. But STM32Cube expects that the USB device will always return the maximum LUN supported for such requests. To fix the bug I had to modify the USBH_MSC_ClassRequest() function in usbh_msc.c. The MSC_REQ_GET_MAX_LUN case statement now looks like this:

case MSC_REQ_GET_MAX_LUN: 
/* Issue GetMaxLUN request */
// Try to get max LUN value.
// Devices that do not support multiple LUNs may STALL this command.
status = USBH_MSC_BOT_REQ_GetMaxLUN(phost, (uint8_t *)&MSC_Handle->max_lun);
if(status == USBH_OK)
{
MSC_Handle->max_lun = (uint8_t)(MSC_Handle->max_lun) + 1;
if(MSC_Handle->max_lun > MAX_SUPPORTED_LUN)
MSC_Handle->max_lun = MAX_SUPPORTED_LUN;
}
else if(status == USBH_NOT_SUPPORTED)
{
MSC_Handle->max_lun = 1;
}
else
{
status = USBH_BUSY;
break;
}
USBH_UsrLog (''Number of supported LUN: %lu'', (int32_t)(MSC_Handle->max_lun));
for(i = 0; i < 

MSC_Handle

-

>max_lun; i++)

{
MSC_Handle->unit[i].prev_ready_state = USBH_FAIL;
MSC_Handle->unit[i].state_changed = 0;
}
status = USBH_OK;
break;

Another bug that is fixed by this modification is that STM32Cube does not check against the MAX_SUPPORTED_LUN constant the max LUN value returned by a USB flash drive. This could cause a buffer overflow when using multi-volume USB flash drives. I hope this helps to improve the STM32Cube library. #stm32cube-usb-msc
4 REPLIES 4
ds1
Associate II
Posted on October 10, 2014 at 15:11

Did anyone have the same problem with USB flash drives?

Yes I have the same problem , some of the flash drives gets detected and reads successfully about 8MB of data where as some gets detected but stalls in reading the file from the flash, really waiting for any answer to resolve this issue .

kevino
Associate

I had the same problem, where some USB drives worked and others didn't. The post above by @ds​ was very helpful and solved my problem. Surprisingly, as of STM32Cube_FW_F7_V1.15.0, this still hasn't been fixed. Please fix this ST!

I'm posting a similar solution to @ds​, but minimizing the changes to the original code and including comments. A code snippet and a diff snippet are shown below.

Corrected code:

See file: \STM32Cube_FW_F7_V1.15.0\Middlewares\ST\STM32_USB_Host_Library\Class\MSC\Src\usbh_msc.c

case MSC_REQ_GET_MAX_LUN:
  /* Issue GetMaxLUN request */
  status = USBH_MSC_BOT_REQ_GetMaxLUN(phost, (uint8_t *)(void *)&MSC_Handle->max_lun);
 
  /* When devices do not support the GetMaxLun request, this should
     be considred as only one logical unit is supported */
  if(status == USBH_NOT_SUPPORTED)
  {
    MSC_Handle->max_lun = 0U;
    status = USBH_OK;
  }
 
  if(status == USBH_OK)
  {
    // ST code expects MSC_Handle->max_lun to indicate "Max number of LUNs", whereas
    //  usb.org specifies max LUN to be reported as the maximum LUN index (i.e. Max number of LUNs - 1).
    //  So add 1 to the value reported by the drive to conform with the ST code expectation.
    //  See usb.org specs: https://www.usb.org/document-library/mass-storage-bulk-only-10
    //  (search Get Max LUN)
    //  Also, MSC_Handle->max_lun is a 32-bit value, but USBH_MSC_BOT_REQ_GetMaxLUN() sets only
    //  the lower 8-bits, so a type cast to uint8_t is required to clear the unknown upper 24-bits.
    MSC_Handle->max_lun = (uint8_t)(MSC_Handle->max_lun) + 1U;
    if (MSC_Handle->max_lun > MAX_SUPPORTED_LUN) MSC_Handle->max_lun = MAX_SUPPORTED_LUN;
    USBH_UsrLog ("Number of supported LUN: %lu", (int32_t)(MSC_Handle->max_lun));
 
    for(i = 0U; i < MSC_Handle->max_lun; i++)
    {
      MSC_Handle->unit[i].prev_ready_state = USBH_FAIL;
      MSC_Handle->unit[i].state_changed = 0U;
    }
  }
  break;

Corrected code in diff format:

diff --git a/Middlewares/ST/STM32_USB_Host_Library/Class/MSC/Src/usbh_msc.c b/Middlewares/ST/STM32_USB_Host_Library/Class/MSC/Src/usbh_msc.c
index 6a9ee01..e57862a 100644
--- a/Middlewares/ST/STM32_USB_Host_Library/Class/MSC/Src/usbh_msc.c
+++ b/Middlewares/ST/STM32_USB_Host_Library/Class/MSC/Src/usbh_msc.c
@@ -288,7 +288,15 @@ static USBH_StatusTypeDef USBH_MSC_ClassRequest(USBH_HandleTypeDef *phost)
 
     if(status == USBH_OK)
     {
-      MSC_Handle->max_lun = (MSC_Handle->max_lun > MAX_SUPPORTED_LUN)? MAX_SUPPORTED_LUN : (uint8_t )(MSC_Handle->max_lun) + 1U;
+      // ST code expects MSC_Handle->max_lun to indicate "Max number of LUNs", whereas^M
+      //  usb.org specifies max LUN to be reported as the maximum LUN index (i.e. Max number of LUNs - 1).^M
+      //  So add 1 to the value reported by the drive to conform with the ST code expectation.^M
+      //  See usb.org specs: https://www.usb.org/document-library/mass-storage-bulk-only-10^M
+      //  (search Get Max LUN)^M
+      //  Also, MSC_Handle->max_lun is a 32-bit value, but USBH_MSC_BOT_REQ_GetMaxLUN() sets only^M
+      //  the lower 8-bits, so a type cast to uint8_t is required to clear the unknown upper 24-bits.^M
+      MSC_Handle->max_lun = (uint8_t)(MSC_Handle->max_lun) + 1U;^M
+      if (MSC_Handle->max_lun > MAX_SUPPORTED_LUN) MSC_Handle->max_lun = MAX_SUPPORTED_LUN;^M
       USBH_UsrLog ("Number of supported LUN: %lu", (int32_t)(MSC_Handle->max_lun));
 
       for(i = 0U; i < MSC_Handle->max_lun; i++)

The way ST implemented the "max LUN" portion of the driver is confusing, because it doesn't conform to the definition of "get max LUN" in the usb.org spec (see https://www.usb.org/document-library/mass-storage-bulk-only-10 and search "get max lun"). ST implemented the MSC_Handle->max_lun value to indicate the "number" of LUNs, whereas USB.org defines it as the "maximum LUN index" (i.e. maximum lun index of 1 where there are 2 LUNs.) So the primary code fix is to increment the "max lun" value read from the USB drive before using it in the rest of the ST driver.

The other problem with how ST implemented the driver is that MSC_Handle->max_lun is defined as a uint32_t, but the call to the USB drive to retrieve it expects a (uint8_t *), so only the lower 8-bits of the value get set, leaving the upper 24-bits unchanged. So it is mandatory to clear the upper 24-bits (or type cast to uint8_t) before using or comparing that value.

Before the code modifications above, a drive with 1 LUT returned a MSC_Handle->max_lun with a very large value (lower 8-bits were zero, as expected, but upper 24-bits were some large value), so MSC_Handle->max_lun got limited to MAX_SUPPORTED_LUN, which was 2, not 1, and the drive wouldn't mount.