LPC43XX USB HOST Mass storage class - drive fails the second time it is plugged in

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LPC43XX USB HOST Mass storage class - drive fails the second time it is plugged in

2,309 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by iemc on Thu Oct 15 17:59:51 MST 2015

Chip: LPC4357 (1MB internal flash)
Manual: UM10503.pdf Rev. 1.9 — 18 February 2015
LPCOpen: lpcopen_2_16_lpcxpresso_nxp_lpcxpresso_4337.zip

Using the LPCUSBlib_MassStorageHost example code.

In release code I want the software to detect a drive being plugged in to the USB Host port which can happen multiple times. I have discovered two problems using the mass storage example code when I try to do this.

The first problem I have been able to work around, which I will explain below. The second problem I haven't been able to find a fix for. Please note that the example code works perfectly the first time a thumb drive is plugged in. It only fails when the drive is removed and plugged in again.

Is it expected to use the LPCOpen code for production software as is? I see this comment in the source code "Software that is described herein is for illustrative purposes only". Should I be looking for a commercial USB library?

Here is how I modified the LPCUSBlib_MassStorageHost example code.

When the event function EVENT_USB_Host_DeviceAttached is called I set a flag for the main loop to run the file test. The idea is to run the test each time a USB thumb drive is plugged in.

File: LPCUSBlib_MassStorageHost\example\src\MassStorageHost.c

bool    DoTestUSBHost; /* flag set by attached event to trigger test code */

int main(void)
{
  SetupHardware();

  while (1)
  {
    if (DoTestUSBHost)
    {
      DEBUGOUT("Mass Storage Host Demo running.\r\n");
      USB_ReadWriteFile();
      DEBUGOUT("Example completed.\r\n");
      DoTestUSBHost = false;
    }
  }
}

void EVENT_USB_Host_DeviceAttached(const uint8_t corenum)
{
  DEBUGOUT(("Device Attached on port %d\r\n"), corenum);
  DoTestUSBHost = true;
}


When I run this code it succeeds the first time I plug in a thumb drive. When I remove the drive and plug it in a second time I get the following error.

Error reading device block.

Which is in LPCUSBlib_MassStorageHost\example\src\MassStorageHost.c in this code.

/* Read sectors */
int FSUSB_DiskReadSectors(DISK_HANDLE_T *hDisk, void *buff, uint32_t secStart, uint32_t numSec)
{
  if (MS_Host_ReadDeviceBlocks(hDisk, 0, secStart, numSec, DiskCapacity.BlockSize, buff)) {
    DEBUGOUT("Error reading device block.\r\n");
    USB_Host_SetDeviceConfiguration(FlashDisk_MS_Interface.Config.PortNumber, 0);
    return 0;
  }
  return 1;
}


I traced this problem to fatfs\src\fs_usb.c

/* Disk Status */
static volatile DSTATUS Stat = STA_NOINIT;

/* Initialize Disk Drive */
DSTATUS disk_initialize(BYTE drv)
{
  if (drv) {
    return STA_NOINIT;        /* Supports only single drive */
  }
  /*  if (Stat & STA_NODISK) return Stat; *//* No card in the socket */

  if (Stat != STA_NOINIT) {
    return Stat;          /* card is already enumerated */

  }

  #if !_FS_READONLY
  FSUSB_InitRealTimeClock();
  #endif

  /* Initialize the Card Data Strucutre */
  hDisk = FSUSB_DiskInit();

  /* Reset */
  Stat = STA_NOINIT;

  FSUSB_DiskInsertWait(hDisk); /* Wait for card to be inserted */

  /* Enumerate the card once detected. Note this function may block for a little while. */
  if (!FSUSB_DiskAcquire(hDisk)) {
    DEBUGOUT("Disk Enumeration failed...\r\n");
    return Stat;
  }

  Stat &= ~STA_NOINIT;
  return Stat;

}


The Stat variable is initialized to STA_NOINIT. The first time disk_initialize() is called it clears the STA_NOINIT bit before returning.

So the when the drive is plugged in the second time and disk_initialize() is called again it returns at this statement.

  if (Stat != STA_NOINIT) {
    return Stat;          /* card is already enumerated */
  }


The code after that is therefore not called and the USB does not enumerate the thumb drive properly. The Stat variable is never set back to STA_NOINIT.

To work around this I added this function to MassStorageHost.c to do the necessary initialization that disk_initialize() failed to do.

bool USBHostWaitForReady(void)
{
  FSUSB_DiskInsertWait(&FlashDisk_MS_Interface);
  return (USB_HostState[FlashDisk_MS_Interface.Config.PortNumber] == HOST_STATE_Configured);
}


I then call this new function in the function USB_ReadWriteFile just before the call to f_mount().

/* Function to do the read/write to USB Disk */
static void USB_ReadWriteFile(void)
{
...snip...

  if (!USBHostWaitForReady()) /* FIX for disk_initialize(): STA_NOINIT is never reset! */
  {
    DEBUGOUT("\rUSBHostWaitForReady failed");
    return;
  }

  f_mount(0, &fatFS);   /* Register volume work area (never fails) */
...


With this change the drive is enumerated correctly the second time and the test succeeds.

But now another problem happens. When the test finished I see a spurious unattach, attach event. The test tried to run again and hangs. My debug log look like this.

EVENT_USB_Host_DeviceAttached
TestUSBHost started
FSUSB_DiskInsertWait begin
Total LUNs: 1 - Using first LUN in device.
Mass Storage Device Enumerated.
FSUSB_DiskInsertWait end
Create a new file (hello.txt).
Write a text data. (Hello world!)14 bytes written.
Close the file.
Opened file HELLO.TXT from USB Disk. Printing contents...
Hello world!

Close the file.
Deleted HELLO.TXT from USB Disk.
Test completed.
Example completed.
EVENT_USB_Host_DeviceUnattached
EVENT_USB_Host_DeviceAttached
TestUSBHost started
FSUSB_DiskInsertWait begin
Total LUNs: 1 - Using first LUN in device.
Mass Storage Device Enumerated.
FSUSB_DiskInsertWait end
Create a new file (hello.txt).
(hangs)

At this point I am stuck.
Labels (1)
5 Replies

1,195 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by iemc on Tue Oct 27 05:04:40 MST 2015
Thanks for the reply. I'll definitely try implementing a state machine as you suggest. It might be why I am seeing spurious disconnects.

Also, the FSUSB_DiskInit() goes to this code in MassStorageHost.c

/* Get the disk data structure */
DISK_HANDLE_T *FSUSB_DiskInit(void)
{
  return &FlashDisk_MS_Interface;
}


Which is returning a pointer to this struct also in MassStorageHost.c

/** LPCUSBlib Mass Storage Class driver interface configuration and state information. This structure is
 *  passed to all Mass Storage Class driver functions, so that multiple instances of the same class
 *  within a device can be differentiated from one another.
 */
static USB_ClassInfo_MS_Host_t FlashDisk_MS_Interface = {
  .Config = {
    .DataINPipeNumber       = 1,
    .DataINPipeDoubleBank   = false,

    .DataOUTPipeNumber      = 2,
    .DataOUTPipeDoubleBank  = false,
    .PortNumber = 1,
  },
};


It is called by disk_initialize in fs_usb.c which has problems with drives being re-inserted (because the static Stat variable is never set back to STA_NOINT).

1,195 Views
natarajbalasubr
Contributor I

I am using #LPC1769, I faced same problem.

I have added following function to fs_usb.c

void diskDeinitialize(){
    Stat = STA_NOINIT;
}

 

I called diskDeinitialize function in EVENT_USB_Host_DeviceUnattached

void EVENT_USB_Host_DeviceUnattached(const uint8_t corenum) {
    DEBUGOUT(("\r\nDevice Unattached on port %d\r\n"), corenum);
    diskDeinitialize();
}

 

For me it's serves the purpose.

Is this the correct way to fix the problem ??

0 Kudos

1,195 Views
Carlos_Mendoza
NXP Employee
NXP Employee

Hi Nataraj,

Your workaround is correct, that way the Stat variable is set back to STA_NOINT each time the device is unattached. The problem is that the USB Mass storage Host example was developed to read the files from USB mass storage device only the first time it is connected to the USB host port and then print the results to the UART console, it was not tested in the scenario when this is done multiple times.


Hope it helps!

Best Regards,
Carlos Mendoza
Technical Support Engineer

1,195 Views
natarajbalasubr
Contributor I

Hi Carlos_Mendoza,

Thanks for your response.

0 Kudos

1,195 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by ehughes on Mon Oct 26 17:51:12 MST 2015
I have successfully gotten this code to work.  Here are some tips  (this also applies to SD cards)

You really need to implement a state machine in a continuously running process that a manages the state of the drive and returning proper return codes for the functions in the fat_fs disk io layer.   Something like this:

switch(USB_HostEnumState)
    {
        case USB_HOST_STATE_DEVICE_INIT:

            USB_Stat = STA_NODISK | STA_NOINIT;

            hDisk = FSUSB_DiskInit();

            USB_HostEnumState =  USB_HOST_STATE_WAIT_FOR_DEVICE_ATTACH;

            break;

        case  USB_HOST_STATE_WAIT_FOR_DEVICE_ATTACH:

            USB_Stat = STA_NOINIT;

            if (USB_HostState[hDisk->Config.PortNumber] != HOST_STATE_Configured)
            {
                MS_Host_USBTask(hDisk);
                USB_USBTask(hDisk->Config.PortNumber, USB_MODE_Host);
            }


            break;

        case USB_HOST_STATE_DEVICE_ATTACHED_ENUMERATING:

            USB_Stat = STA_NOINIT;



            break;


        case USB_HOST_STATE_DEVICE_ATTACHED_GET_DISK_CAPACITY:

            USB_Stat = STA_NOINIT;

            if (FSUSB_GetDeviceCapacity(hDisk) == 0)
            {
                DEBUGOUT("Error retrieving device capacity.\r\n");
                USB_Host_SetDeviceConfiguration(hDisk->Config.PortNumber, 0);
                USB_HostEnumState = USB_HOST_STATE_DEVICE_ENUMERATION_ERROR;
            }
            else
            {
                USB_HostEnumState = USB_HOST_STATE_DEVICE_ATTACHED_READY;
                DEBUGOUT(("%lu blocks of %lu bytes on USB drive\r\n"), FSUSB_DiskGetSectorCnt(hDisk), FSUSB_DiskGetSectorSz(hDisk));
            }

            break;

        case USB_HOST_STATE_DEVICE_ATTACHED_READY:

            USB_Stat = 0;

            break;

        case USB_HOST_STATE_DEVICE_ENUMERATION_ERROR:

            USB_Stat =  STA_NOINIT;

            break;

        case USB_HOST_STATE_HOST_ERROR:
            USB_Stat =  STA_NOINIT;
            break;
    }


I found that this cannot be done reliably unless you you continously like fat_fs to the actual state of the drive.    I can't see where you reset the USB Port after spurious disconnection (FSUSB_DiskInit())   Look into that...
0 Kudos