AnsweredAssumed Answered

Missing data when writing to USB key with FatFs, STMF2xx Host library

Question asked by storah.chris on Jan 9, 2013
I am using a STMF207Z (Waveshare Open207) with the latest STM USB peripheral library and have come across a problem with the usbh_msc_fatfs disk_write implementation where certain writes to a USB key are not performed resulting in corrupt files and in some cases the superblock.

If I repeat writing 20 bytes using f_write, followed by an f_sync, every so often (usually when the data spans the end of the 512 sector) it will fail to write the data. The bytes between 5F0 and 600 tend to trigger the problem, where 16 bytes need to be written at the end of the sector and 4 at the start of the next.

Debugging showed that the loop in disk_write dropped out without USBH_MSC_Write10 setting up the USBH_MSC_CBWData block due to USBH_MSC_BOTXferParam.CmdStateMachine being CMD_WAIT_STATUS (it was in the process of performing a read - status?). After several loops, USBH_MSC_HandleBOTXfer eventually completes the active operation which results in Write10 returning USBH_MSC_OK - without writing any data.

A fix I have implemented is to set a "sendstatecalled" flag when the CMD_SEND_STATE case is entered, and ensure that is set before exiting the disk_write function.

// usbh_msc_fatfs.c

...

u8 sendstatecalled = 0;
 
#if _READONLY == 0
DRESULT disk_write (
                    BYTE drv,           /* Physical drive number (0) */
                    const BYTE *buff,   /* Pointer to the data to be written */
                    DWORD sector,       /* Start sector number (LBA) */
                    BYTE count          /* Sector count (1..255) */
                      )
{
  BYTE status = USBH_MSC_OK;
  if (debug) printf("disk_write called for sector %d, count %d\n", sector, count);
  if (drv || !count) return RES_PARERR;
  if (Stat & STA_NOINIT) return RES_NOTRDY;
  if (Stat & STA_PROTECT) return RES_WRPRT;
   
   
  if(HCD_IsDeviceConnected(&USB_OTG_Core))
  {
    sendstatecalled = 0;
 
    do
    {
        status = . (&USB_OTG_Core,(BYTE*)buff,sector,512 * count);
        USBH_MSC_HandleBOTXfer(&USB_OTG_Core, &USB_Host);
 
        if(!HCD_IsDeviceConnected(&USB_OTG_Core))
        {
          return RES_ERROR;
        }
 
        // ensure the call was made before exiting
        if (sendstatecalled == 0) status = USBH_MSC_BUSY;
 
    } while (status == USBH_MSC_BUSY);
  }
   
  if(status == USBH_MSC_OK)
    return RES_OK;
  return RES_ERROR;
}
#endif /* _READONLY == 0 */

// usbh_msc_scsi.c
 
...
 
uint8_t USBH_MSC_Write10(USB_OTG_CORE_HANDLE *pdev,
                         uint8_t *dataBuffer,
                         uint32_t address,
                         uint32_t nbOfbytes)
{
  uint8_t index;
  USBH_MSC_Status_TypeDef status = USBH_MSC_BUSY;
  uint16_t nbOfPages;
   
  if(HCD_IsDeviceConnected(pdev))
  
    switch(USBH_MSC_BOTXferParam.CmdStateMachine)
    {
    case CMD_SEND_STATE:  
        sendstatecalled = 1;
 
...

This is a quick fix that for this situation, and may affect reads or other functionality that I haven't tested. Has anyone else come across this problem, and if so is there a better fix?.

Thanks,
Chris

Outcomes