This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TM4C1294NCPDT: How to deal with blocking R/W in USB properly?

Part Number: TM4C1294NCPDT
Other Parts Discussed in Thread: EK-TM4C1294XL

I am using TM4C1294's USB host mode to access a thumb drive, everything runs fine except when I remove the drive during a USB R/W, it will hang in the blocking API like USBHCDPipeWrite, since the R/W will never finish due to the removal of the thumb drive.

Any one already had this issue taken care? I hope the USB stack will stop the R/W with an error code and wait for USB thumb drive to be reconnected

Thanks!

  • Hello David,

    This is the first time I've heard of the USBHCDPipeWrite hanging on device removal, however there have been issues reported in the past where it hangs due to having USBHCDPipeWrite send more than 64 bytes. Is it possible the hanging could be related to that instead?

    What steps have you taken to be sure that only the removal during R/W is the root cause, and can you replicate them on TI EVM hardware so I can attempt to take the same steps to observe the issue and debug the stack on my end? It will be difficult for me to offer much constructive advice unless I can view the issue in debug on my end.

    Lastly, which version TivaWare are you running?
  • Starting with usb_host_msc
    add codes to copy file in cmd_cat (see codes below)
    if I don't use f_sync, it will work fine, when I remove the thumb, it will show an error and wait for next thumb. If I use f_sync after f_write, and remove the thumb while writing to it, good chance it will lock up

    ________________ modified usb_host_msc.c ____________________

    //*****************************************************************************
    //
    // usb_host_msc.c - USB mass storage host application.
    //
    // Copyright (c) 2013-2016 Texas Instruments Incorporated. All rights reserved.
    // Software License Agreement
    //
    // Texas Instruments (TI) is supplying this software for use solely and
    // exclusively on TI's microcontroller products. The software is owned by
    // TI and/or its suppliers, and is protected under applicable copyright
    // laws. You may not combine this software with "viral" open-source
    // software in order to form a larger program.
    //
    // THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
    // NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
    // NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    // A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
    // CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
    // DAMAGES, FOR ANY REASON WHATSOEVER.
    //
    // This is part of revision 2.1.3.156 of the EK-TM4C1294XL Firmware Package.
    //
    //*****************************************************************************

    #include <stdint.h>
    #include <stdbool.h>
    #include <string.h>
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "driverlib/gpio.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/rom.h"
    #include "driverlib/rom_map.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/uart.h"
    #include "driverlib/udma.h"
    #include "usblib/usblib.h"
    #include "usblib/usbmsc.h"
    #include "usblib/host/usbhost.h"
    #include "usblib/host/usbhmsc.h"
    #include "utils/cmdline.h"
    #include "utils/uartstdio.h"
    #include "fatfs/src/ff.h"
    #include "drivers/pinout.h"

    //*****************************************************************************
    //
    //! \addtogroup example_list
    //! <h1>USB Mass Storage Class Host (usb_host_msc)</h1>
    //!
    //! This example application demonstrates reading a file system from a USB mass
    //! storage class device. It makes use of FatFs, a FAT file system driver. It
    //! provides a simple command console via the UART for issuing commands to view
    //! and navigate the file system on the mass storage device.
    //!
    //! The first UART, which is connected to the Tiva C Series virtual serial port
    //! on the evaluation board, is configured for 115,200 bits per second, and
    //! 8-N-1 mode. When the program is started a message will be printed to the
    //! terminal. Type ``help'' for command help.
    //!
    //! For additional details about FatFs, see the following site:
    //! elm-chan.org/.../00index_e.html
    //
    //*****************************************************************************

    //*****************************************************************************
    //
    // The number of SysTick ticks per second.
    //
    //*****************************************************************************
    #define TICKS_PER_SECOND 100
    #define MS_PER_SYSTICK (1000 / TICKS_PER_SECOND)

    //*****************************************************************************
    //
    // Our running system tick counter and a global used to determine the time
    // elapsed since last call to GetTickms().
    //
    //*****************************************************************************
    uint32_t g_ui32SysTickCount;
    uint32_t g_ui32LastTick;

    //*****************************************************************************
    //
    // Defines the size of the buffers that hold the path, or temporary data from
    // the memory card. There are two buffers allocated of this size. The buffer
    // size must be large enough to hold the longest expected full path name,
    // including the file name, and a trailing null character.
    //
    //*****************************************************************************
    #define PATH_BUF_SIZE 80

    //*****************************************************************************
    //
    // Defines the size of the buffer that holds the command line.
    //
    //*****************************************************************************
    #define CMD_BUF_SIZE 64

    //*****************************************************************************
    //
    // Defines the number of times to call to check if the attached device is
    // ready.
    //
    //*****************************************************************************
    #define USBMSC_DRIVE_RETRY 4

    //*****************************************************************************
    //
    // This buffer holds the full path to the current working directory. Initially
    // it is root ("/").
    //
    //*****************************************************************************
    static char g_cCwdBuf[PATH_BUF_SIZE] = "/";

    //*****************************************************************************
    //
    // A temporary data buffer used when manipulating file paths, or reading data
    // from the memory card.
    //
    //*****************************************************************************
    static char g_cTmpBuf[PATH_BUF_SIZE];
    static char g_cTmpBuf2[PATH_BUF_SIZE];
    static char g_cTmpBuf3[2048];

    //*****************************************************************************
    //
    // The buffer that holds the command line.
    //
    //*****************************************************************************
    static char g_cCmdBuf[CMD_BUF_SIZE];

    //*****************************************************************************
    //
    // Current FAT fs state.
    //
    //*****************************************************************************
    static FATFS g_sFatFs;
    static DIR g_sDirObject;
    static FILINFO g_sFileInfo;
    static FIL g_sFileObject;
    static FIL g_sFileObject2;

    //*****************************************************************************
    //
    // A structure that holds a mapping between an FRESULT numerical code,
    // and a string representation. FRESULT codes are returned from the FatFs
    // FAT file system driver.
    //
    //*****************************************************************************
    typedef struct
    {
    FRESULT fresult;
    char *pcResultStr;
    }
    tFresultString;

    //*****************************************************************************
    //
    // A macro to make it easy to add result codes to the table.
    //
    //*****************************************************************************
    #define FRESULT_ENTRY(f) { (f), (#f) }

    //*****************************************************************************
    //
    // A table that holds a mapping between the numerical FRESULT code and
    // it's name as a string. This is used for looking up error codes for
    // printing to the console.
    //
    //*****************************************************************************
    tFresultString g_sFresultStrings[] =
    {
    FRESULT_ENTRY(FR_OK),
    FRESULT_ENTRY(FR_DISK_ERR),
    FRESULT_ENTRY(FR_INT_ERR),
    FRESULT_ENTRY(FR_NOT_READY),
    FRESULT_ENTRY(FR_NO_FILE),
    FRESULT_ENTRY(FR_NO_PATH),
    FRESULT_ENTRY(FR_INVALID_NAME),
    FRESULT_ENTRY(FR_DENIED),
    FRESULT_ENTRY(FR_EXIST),
    FRESULT_ENTRY(FR_INVALID_OBJECT),
    FRESULT_ENTRY(FR_WRITE_PROTECTED),
    FRESULT_ENTRY(FR_INVALID_DRIVE),
    FRESULT_ENTRY(FR_NOT_ENABLED),
    FRESULT_ENTRY(FR_NO_FILESYSTEM),
    FRESULT_ENTRY(FR_MKFS_ABORTED),
    FRESULT_ENTRY(FR_TIMEOUT),
    FRESULT_ENTRY(FR_LOCKED),
    FRESULT_ENTRY(FR_NOT_ENOUGH_CORE),
    FRESULT_ENTRY(FR_TOO_MANY_OPEN_FILES),
    FRESULT_ENTRY(FR_INVALID_PARAMETER)
    };

    //*****************************************************************************
    //
    // A macro that holds the number of result codes.
    //
    //*****************************************************************************
    #define NUM_FRESULT_CODES (sizeof(g_sFresultStrings) / sizeof(tFresultString))

    //*****************************************************************************
    //
    // The size of the host controller's memory pool in bytes.
    //
    //*****************************************************************************
    #define HCD_MEMORY_SIZE 128

    //*****************************************************************************
    //
    // The memory pool to provide to the Host controller driver.
    //
    //*****************************************************************************
    uint8_t g_pHCDPool[HCD_MEMORY_SIZE];

    //*****************************************************************************
    //
    // The instance data for the MSC driver.
    //
    //*****************************************************************************
    tUSBHMSCInstance *g_psMSCInstance = 0;

    //*****************************************************************************
    //
    // Declare the USB Events driver interface.
    //
    //*****************************************************************************
    DECLARE_EVENT_DRIVER(g_sUSBEventDriver, 0, 0, USBHCDEvents);

    //*****************************************************************************
    //
    // The global that holds all of the host drivers in use in the application.
    // In this case, only the MSC class is loaded.
    //
    //*****************************************************************************
    static tUSBHostClassDriver const * const g_ppHostClassDrivers[] =
    {
    &g_sUSBHostMSCClassDriver,
    &g_sUSBEventDriver
    };

    //*****************************************************************************
    //
    // Hold the current state for the application.
    //
    //*****************************************************************************
    typedef enum
    {
    //
    // No device is present.
    //
    STATE_NO_DEVICE,

    //
    // Mass storage device is being enumerated.
    //
    STATE_DEVICE_ENUM,

    //
    // Mass storage device is ready.
    //
    STATE_DEVICE_READY,

    //
    // An unsupported device has been attached.
    //
    STATE_UNKNOWN_DEVICE,

    //
    // A mass storage device was connected but failed to ever report ready.
    //
    STATE_TIMEOUT_DEVICE,

    //
    // A power fault has occurred.
    //
    STATE_POWER_FAULT
    }
    tState;

    volatile tState g_eState;
    volatile tState g_eUIState;

    //*****************************************************************************
    //
    // This global holds the number of class drivers in the g_ppHostClassDrivers
    // list.
    //
    //*****************************************************************************
    static const uint32_t g_ui32NumHostClassDrivers =
    sizeof(g_ppHostClassDrivers) / sizeof(tUSBHostClassDriver *);

    //*****************************************************************************
    //
    // The control table used by the uDMA controller. This table must be aligned
    // to a 1024 byte boundary. In this application uDMA is only used for USB,
    // so only the first 6 channels are needed.
    //
    //*****************************************************************************
    #if defined(ewarm)
    #pragma data_alignment=1024
    tDMAControlTable g_sDMAControlTable[6];
    #elif defined(ccs)
    #pragma DATA_ALIGN(g_sDMAControlTable, 1024)
    tDMAControlTable g_sDMAControlTable[6];
    #else
    tDMAControlTable g_sDMAControlTable[6] __attribute__ ((aligned(1024)));
    #endif

    //*****************************************************************************
    //
    // The current USB operating mode - Host, Device or unknown.
    //
    //*****************************************************************************
    tUSBMode g_eCurrentUSBMode;

    //*****************************************************************************
    //
    // The class of the unknown device.
    //
    //*****************************************************************************
    uint32_t g_ui32UnknownClass;

    //*****************************************************************************
    //
    // The error routine that is called if the driver library encounters an error.
    //
    //*****************************************************************************
    #ifdef DEBUG
    void
    __error__(char *pcFilename, uint32_t ui32Line)
    {
    }
    #endif

    //*****************************************************************************
    //
    // This is the handler for this SysTick interrupt.
    //
    //*****************************************************************************
    void
    SysTickIntHandler(void)
    {
    //
    // Update our tick counter.
    //
    g_ui32SysTickCount++;
    }

    //*****************************************************************************
    //
    // This function returns the number of ticks since the last time this function
    // was called.
    //
    //*****************************************************************************
    uint32_t
    GetTickms(void)
    {
    uint32_t ui32RetVal;
    uint32_t ui32Saved;

    ui32RetVal = g_ui32SysTickCount;
    ui32Saved = ui32RetVal;

    if(ui32Saved > g_ui32LastTick)
    {
    ui32RetVal = ui32Saved - g_ui32LastTick;
    }
    else
    {
    ui32RetVal = g_ui32LastTick - ui32Saved;
    }

    //
    // This could miss a few milliseconds but the timings here are on a
    // much larger scale.
    //
    g_ui32LastTick = ui32Saved;

    //
    // Return the number of milliseconds since the last time this was called.
    //
    return(ui32RetVal * MS_PER_SYSTICK);
    }

    //*****************************************************************************
    //
    // This function returns a string representation of an error code that was
    // returned from a function call to FatFs. It can be used for printing human
    // readable error messages.
    //
    //*****************************************************************************
    const char *
    StringFromFresult(FRESULT fresult)
    {
    uint32_t ui32Idx;

    //
    // Enter a loop to search the error code table for a matching error code.
    //
    for(ui32Idx = 0; ui32Idx < NUM_FRESULT_CODES; ui32Idx++)
    {
    //
    // If a match is found, then return the string name of the error code.
    //
    if(g_sFresultStrings[ui32Idx].fresult == fresult)
    {
    return(g_sFresultStrings[ui32Idx].pcResultStr);
    }
    }

    //
    // At this point no matching code was found, so return a string indicating
    // unknown error.
    //
    return("UNKNOWN ERROR CODE");
    }

    //*****************************************************************************
    //
    // This function implements the "ls" command. It opens the current directory
    // and enumerates through the contents, and prints a line for each item it
    // finds. It shows details such as file attributes, time and date, and the
    // file size, along with the name. It shows a summary of file sizes at the end
    // along with free space.
    //
    //*****************************************************************************
    int
    Cmd_ls(int argc, char *argv[])
    {
    uint32_t ui32TotalSize;
    uint32_t ui32FileCount;
    uint32_t ui32DirCount;
    FRESULT fresult;
    FATFS *pFatFs;

    //
    // Do not attempt to do anything if there is not a drive attached.
    //
    if(g_eState != STATE_DEVICE_READY)
    {
    return(FR_NOT_READY);
    }

    //
    // Open the current directory for access.
    //
    fresult = f_opendir(&g_sDirObject, g_cCwdBuf);

    //
    // Check for error and return if there is a problem.
    //
    if(fresult != FR_OK)
    {
    return(fresult);
    }

    ui32TotalSize = 0;
    ui32FileCount = 0;
    ui32DirCount = 0;

    //
    // Enter loop to enumerate through all directory entries.
    //
    while(1)
    {
    //
    // Read an entry from the directory.
    //
    fresult = f_readdir(&g_sDirObject, &g_sFileInfo);

    //
    // Check for error and return if there is a problem.
    //
    if(fresult != FR_OK)
    {
    return(fresult);
    }

    //
    // If the file name is blank, then this is the end of the listing.
    //
    if(!g_sFileInfo.fname[0])
    {
    break;
    }

    //
    // If the attribute is directory, then increment the directory count.
    //
    if(g_sFileInfo.fattrib & AM_DIR)
    {
    ui32DirCount++;
    }

    //
    // Otherwise, it is a file. Increment the file count, and add in the
    // file size to the total.
    //
    else
    {
    ui32FileCount++;
    ui32TotalSize += g_sFileInfo.fsize;
    }

    //
    // Print the entry information on a single line with formatting to show
    // the attributes, date, time, size, and name.
    //
    UARTprintf("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9u %s\n",
    (g_sFileInfo.fattrib & AM_DIR) ? 'D' : '-',
    (g_sFileInfo.fattrib & AM_RDO) ? 'R' : '-',
    (g_sFileInfo.fattrib & AM_HID) ? 'H' : '-',
    (g_sFileInfo.fattrib & AM_SYS) ? 'S' : '-',
    (g_sFileInfo.fattrib & AM_ARC) ? 'A' : '-',
    (g_sFileInfo.fdate >> 9) + 1980,
    (g_sFileInfo.fdate >> 5) & 15,
    g_sFileInfo.fdate & 31,
    (g_sFileInfo.ftime >> 11),
    (g_sFileInfo.ftime >> 5) & 63,
    g_sFileInfo.fsize,
    g_sFileInfo.fname);
    }

    //
    // Print summary lines showing the file, dir, and size totals.
    //
    UARTprintf("\n%4u File(s),%10u bytes total\n%4u Dir(s)",
    ui32FileCount, ui32TotalSize, ui32DirCount);

    //
    // Get the free space.
    //
    fresult = f_getfree("/", &ui32TotalSize, &pFatFs);

    //
    // Check for error and return if there is a problem.
    //
    if(fresult != FR_OK)
    {
    return(fresult);
    }

    //
    // Display the amount of free space that was calculated.
    //
    UARTprintf(", %10uK bytes free\n", ui32TotalSize * pFatFs->csize / 2);

    //
    // Made it to here, return with no errors.
    //
    return(0);
    }

    //*****************************************************************************
    //
    // This function implements the "cd" command. It takes an argument that
    // specifies the directory to make the current working directory. Path
    // separators must use a forward slash "/". The argument to cd can be one of
    // the following:
    //
    // * root ("/")
    // * a fully specified path ("/my/path/to/mydir")
    // * a single directory name that is in the current directory ("mydir")
    // * parent directory ("..")
    //
    // It does not understand relative paths, so don't try something like this:
    // ("../my/new/path")
    //
    // Once the new directory is specified, it attempts to open the directory to
    // make sure it exists. If the new path is opened successfully, then the
    // current working directory (cwd) is changed to the new path.
    //
    //*****************************************************************************
    int
    Cmd_cd(int argc, char *argv[])
    {
    uint32_t ui32Idx;
    FRESULT fresult;

    //
    // Do not attempt to do anything if there is not a drive attached.
    //
    if(g_eState != STATE_DEVICE_READY)
    {
    return(FR_NOT_READY);
    }

    //
    // Copy the current working path into a temporary buffer so it can be
    // manipulated.
    //
    strcpy(g_cTmpBuf, g_cCwdBuf);

    //
    // If the first character is /, then this is a fully specified path, and it
    // should just be used as-is.
    //
    if(argv[1][0] == '/')
    {
    //
    // Make sure the new path is not bigger than the cwd buffer.
    //
    if(strlen(argv[1]) + 1 > sizeof(g_cCwdBuf))
    {
    UARTprintf("Resulting path name is too long\n");
    return(0);
    }

    //
    // If the new path name (in argv[1]) is not too long, then copy it
    // into the temporary buffer so it can be checked.
    //
    else
    {
    strncpy(g_cTmpBuf, argv[1], sizeof(g_cTmpBuf));
    }
    }

    //
    // If the argument is .. then attempt to remove the lowest level on the
    // CWD.
    //
    else if(!strcmp(argv[1], ".."))
    {
    //
    // Get the index to the last character in the current path.
    //
    ui32Idx = strlen(g_cTmpBuf) - 1;

    //
    // Back up from the end of the path name until a separator (/) is
    // found, or until we bump up to the start of the path.
    //
    while((g_cTmpBuf[ui32Idx] != '/') && (ui32Idx > 1))
    {
    //
    // Back up one character.
    //
    ui32Idx--;
    }

    //
    // Now we are either at the lowest level separator in the current path,
    // or at the beginning of the string (root). So set the new end of
    // string here, effectively removing that last part of the path.
    //
    g_cTmpBuf[ui32Idx] = 0;
    }

    //
    // Otherwise this is just a normal path name from the current directory,
    // and it needs to be appended to the current path.
    //
    else
    {
    //
    // Test to make sure that when the new additional path is added on to
    // the current path, there is room in the buffer for the full new path.
    // It needs to include a new separator, and a trailing null character.
    //
    if(strlen(g_cTmpBuf) + strlen(argv[1]) + 1 + 1 > sizeof(g_cCwdBuf))
    {
    UARTprintf("Resulting path name is too long\n");
    return(0);
    }

    //
    // The new path is okay, so add the separator and then append the new
    // directory to the path.
    //
    else
    {
    //
    // If not already at the root level, then append a /
    //
    if(strcmp(g_cTmpBuf, "/"))
    {
    strcat(g_cTmpBuf, "/");
    }

    //
    // Append the new directory to the path.
    //
    strcat(g_cTmpBuf, argv[1]);
    }
    }

    //
    // At this point, a candidate new directory path is in chTmpBuf. Try to
    // open it to make sure it is valid.
    //
    fresult = f_opendir(&g_sDirObject, g_cTmpBuf);

    //
    // If it can't be opened, then it is a bad path. Inform user and return.
    //
    if(fresult != FR_OK)
    {
    UARTprintf("cd: %s\n", g_cTmpBuf);
    return(fresult);
    }

    //
    // Otherwise, it is a valid new path, so copy it into the CWD.
    //
    else
    {
    strncpy(g_cCwdBuf, g_cTmpBuf, sizeof(g_cCwdBuf));
    }

    //
    // Return success.
    //
    return(0);
    }

    //*****************************************************************************
    //
    // This function implements the "pwd" command. It simply prints the current
    // working directory.
    //
    //*****************************************************************************
    int
    Cmd_pwd(int argc, char *argv[])
    {
    //
    // Do not attempt to do anything if there is not a drive attached.
    //
    if(g_eState != STATE_DEVICE_READY)
    {
    return(FR_NOT_READY);
    }

    //
    // Print the CWD to the console.
    //
    UARTprintf("%s\n", g_cCwdBuf);

    //
    // Return success.
    //
    return(0);
    }

    //*****************************************************************************
    //
    // This function implements the "cat" command. It reads the contents of a file
    // and prints it to the console. This should only be used on text files. If
    // it is used on a binary file, then a bunch of garbage is likely to printed on
    // the console.
    //
    //*****************************************************************************
    int
    Cmd_cat(int argc, char *argv[])
    {
    FRESULT fresult;
    uint32_t ui32BytesRead;

    //
    // Do not attempt to do anything if there is not a drive attached.
    //
    if(g_eState != STATE_DEVICE_READY)
    {
    return(FR_NOT_READY);
    }

    //
    // First, check to make sure that the current path (CWD), plus the file
    // name, plus a separator and trailing null, will all fit in the temporary
    // buffer that will be used to hold the file name. The file name must be
    // fully specified, with path, to FatFs.
    //
    if(strlen(g_cCwdBuf) + strlen(argv[1]) + 1 + 1 > sizeof(g_cTmpBuf))
    {
    UARTprintf("Resulting path name is too long\n");
    return(0);
    }

    //
    // Copy the current path to the temporary buffer so it can be manipulated.
    //
    strcpy(g_cTmpBuf, g_cCwdBuf);

    //
    // If not already at the root level, then append a separator.
    //
    if(strcmp("/", g_cCwdBuf))
    {
    strcat(g_cTmpBuf, "/");
    }

    //
    // Now finally, append the file name to result in a fully specified file.
    //
    strcat(g_cTmpBuf, argv[1]);
    strcpy(g_cTmpBuf2,g_cTmpBuf);
    g_cTmpBuf2[strlen(g_cTmpBuf)-2]='y';

    //
    // Open the file for reading.
    //
    fresult = f_open(&g_sFileObject, g_cTmpBuf, FA_READ);

    fresult = f_open(&g_sFileObject2, g_cTmpBuf2, FA_WRITE|FA_CREATE_ALWAYS);

    //
    // If there was some problem opening the file, then return an error.
    //
    if(fresult != FR_OK)
    {
    return(fresult);
    }

    //
    // Enter a loop to repeatedly read data from the file and display it, until
    // the end of the file is reached.
    //
    do
    {
    //
    // Read a block of data from the file. Read as much as can fit in the
    // temporary buffer, including a space for the trailing null.
    //
    fresult = f_read(&g_sFileObject, g_cTmpBuf3, sizeof(g_cTmpBuf3) ,
    (UINT *)&ui32BytesRead);
    fresult = f_write(&g_sFileObject2, g_cTmpBuf3, sizeof(g_cTmpBuf3) ,
    (UINT *)&ui32BytesRead);
    f_sync (&g_sFileObject2);

    //
    // If there was an error reading, then print a newline and return the
    // error to the user.
    //
    if(fresult != FR_OK)
    {
    UARTprintf("Error\n");
    return(fresult);
    }

    //
    // Null terminate the last block that was read to make it a null
    // terminated string that can be used with printf.
    //
    //g_cTmpBuf[ui32BytesRead] = 0;

    //
    // Print the last chunk of the file that was received.
    //
    //UARTprintf("%s", g_cTmpBuf);
    UARTprintf(".");

    //
    // Continue reading until less than the full number of bytes are read.
    // That means the end of the buffer was reached.
    //
    }
    while(ui32BytesRead == sizeof(g_cTmpBuf3));

    //
    // Return success.
    //
    return(0);
    }

    //*****************************************************************************
    //
    // This function implements the "help" command. It prints a simple list of the
    // available commands with a brief description.
    //
    //*****************************************************************************
    int
    Cmd_help(int argc, char *argv[])
    {
    tCmdLineEntry *pEntry;

    //
    // Print some header text.
    //
    UARTprintf("\nAvailable commands\n");
    UARTprintf("------------------\n");

    //
    // Point at the beginning of the command table.
    //
    pEntry = &g_psCmdTable[0];

    //
    // Enter a loop to read each entry from the command table. The end of the
    // table has been reached when the command name is NULL.
    //
    while(pEntry->pcCmd)
    {
    //
    // Print the command name and the brief description.
    //
    UARTprintf("%s%s\n", pEntry->pcCmd, pEntry->pcHelp);

    //
    // Advance to the next entry in the table.
    //
    pEntry++;
    }

    //
    // Return success.
    //
    return(0);
    }

    //*****************************************************************************
    //
    // This is the table that holds the command names, implementing functions, and
    // brief description.
    //
    //*****************************************************************************
    tCmdLineEntry g_psCmdTable[] =
    {
    { "help", Cmd_help, " : Display list of commands" },
    { "h", Cmd_help, " : alias for help" },
    { "?", Cmd_help, " : alias for help" },
    { "ls", Cmd_ls, " : Display list of files" },
    { "chdir", Cmd_cd, ": Change directory" },
    { "cd", Cmd_cd, " : alias for chdir" },
    { "pwd", Cmd_pwd, " : Show current working directory" },
    { "cat", Cmd_cat, " : Show contents of a text file" },
    { 0, 0, 0 }
    };

    //*****************************************************************************
    //
    // This is the callback from the MSC driver.
    //
    // \param ui32Instance is the driver instance which is needed when
    // communicating with the driver.
    // \param ui32Event is one of the events defined by the driver.
    // \param pvData is a pointer to data passed into the initial call to register
    // the callback.
    //
    // This function handles callback events from the MSC driver. The only events
    // currently handled are the MSC_EVENT_OPEN and MSC_EVENT_CLOSE. This allows
    // the main routine to know when an MSC device has been detected and
    // enumerated and when an MSC device has been removed from the system.
    //
    // \return Returns \e true on success or \e false on failure.
    //
    //*****************************************************************************
    void
    MSCCallback(tUSBHMSCInstance *ps32Instance, uint32_t ui32Event, void *pvData)
    {
    //
    // Determine the event.
    //
    switch(ui32Event)
    {
    //
    // Called when the device driver has successfully enumerated an MSC
    // device.
    //
    case MSC_EVENT_OPEN:
    {
    //
    // Proceed to the enumeration state.
    //
    g_eState = STATE_DEVICE_ENUM;
    break;
    }

    //
    // Called when the device driver has been unloaded due to error or
    // the device is no longer present.
    //
    case MSC_EVENT_CLOSE:
    {
    //
    // Go back to the "no device" state and wait for a new connection.
    //
    g_eState = STATE_NO_DEVICE;

    break;
    }

    default:
    {
    break;
    }
    }
    }

    //*****************************************************************************
    //
    // This is the generic callback from host stack.
    //
    // \param pvData is actually a pointer to a tEventInfo structure.
    //
    // This function will be called to inform the application when a USB event has
    // occurred that is outside those related to the mass storage device. At this
    // point this is used to detect unsupported devices being inserted and removed.
    // It is also used to inform the application when a power fault has occurred.
    // This function is required when the g_USBGenericEventDriver is included in
    // the host controller driver array that is passed in to the
    // USBHCDRegisterDrivers() function.
    //
    // \return None.
    //
    //*****************************************************************************
    void
    USBHCDEvents(void *pvData)
    {
    tEventInfo *pEventInfo;

    //
    // Cast this pointer to its actual type.
    //
    pEventInfo = (tEventInfo *)pvData;

    switch(pEventInfo->ui32Event)
    {
    //
    // Unknown device detected.
    //
    case USB_EVENT_UNKNOWN_CONNECTED:
    {
    //
    // Save the unknown class.
    //
    g_ui32UnknownClass = pEventInfo->ui32Instance;

    //
    // An unknown device was detected.
    //
    g_eState = STATE_UNKNOWN_DEVICE;

    break;
    }

    //
    // Keyboard has been unplugged.
    //
    case USB_EVENT_DISCONNECTED:
    {
    //
    // Unknown device has been removed.
    //
    g_eState = STATE_NO_DEVICE;

    break;
    }

    case USB_EVENT_POWER_FAULT:
    {
    //
    // No power means no device is present.
    //
    g_eState = STATE_POWER_FAULT;

    break;
    }

    default:
    {
    break;
    }
    }
    }

    //*****************************************************************************
    //
    // This function reads a line of text from the UART console. The USB host main
    // function is called throughout this process to keep USB alive and well.
    //
    //*****************************************************************************
    void
    ReadLine(void)
    {
    uint32_t ui32Idx, ui32Prompt;
    uint8_t ui8Char;
    tState eStateCopy;
    uint32_t ui32DriveTimeout;

    //
    // Start reading at the beginning of the command buffer and print a prompt.
    //
    g_cCmdBuf[0] = '\0';
    ui32Idx = 0;
    ui32Prompt = 1;

    //
    // Initialize the drive timeout.
    //
    ui32DriveTimeout = USBMSC_DRIVE_RETRY;

    //
    // Loop forever. This loop will be explicitly broken out of when the line
    // has been fully read.
    //
    while(1)
    {
    //
    // See if a mass storage device has been enumerated.
    //
    if(g_eState == STATE_DEVICE_ENUM)
    {
    //
    // Take it easy on the Mass storage device if it is slow to
    // start up after connecting.
    //
    if(USBHMSCDriveReady(g_psMSCInstance) != 0)
    {
    //
    // Wait about 500ms before attempting to check if the
    // device is ready again.
    //
    SysCtlDelay(SysCtlClockGet()/(3*2));

    //
    // Decrement the retry count.
    //
    ui32DriveTimeout--;

    //
    // If the timeout is hit then go to the
    // STATE_TIMEOUT_DEVICE state.
    //
    if(ui32DriveTimeout == 0)
    {
    g_eState = STATE_TIMEOUT_DEVICE;
    }

    break;
    }

    //
    // Reset the working directory to the root.
    //
    g_cCwdBuf[0] = '/';
    g_cCwdBuf[1] = '\0';

    //
    // Attempt to open the directory. Some drives take longer to
    // start up than others, and this may fail (even though the USB
    // device has enumerated) if it is still initializing.
    //
    f_mount(0, &g_sFatFs);
    if(f_opendir(&g_sDirObject, g_cCwdBuf) == FR_OK)
    {
    //
    // The drive is fully ready, so move to that state.
    //
    g_eState = STATE_DEVICE_READY;
    }
    }

    //
    // See if the state has changed. We make a copy of g_eUIState to
    // prevent a compiler warning about undefined order of volatile
    // accesses.
    //
    eStateCopy = g_eUIState;
    if(g_eState != eStateCopy)
    {
    //
    // Determine the new state.
    //
    switch(g_eState)
    {
    //
    // A previously connected device has been disconnected.
    //
    case STATE_NO_DEVICE:
    {
    if(g_eUIState == STATE_UNKNOWN_DEVICE)
    {
    UARTprintf("\nUnknown device disconnected.\n");
    }
    else
    {
    UARTprintf("\nMass storage device disconnected.\n");
    }
    ui32Prompt = 1;
    break;
    }

    //
    // A mass storage device is being enumerated.
    //
    case STATE_DEVICE_ENUM:
    {
    break;
    }

    //
    // A mass storage device has been enumerated and initialized.
    //
    case STATE_DEVICE_READY:
    {
    UARTprintf("\nMass storage device connected.\n");
    ui32Prompt = 1;
    break;
    }

    //
    // An unknown device has been connected.
    //
    case STATE_UNKNOWN_DEVICE:
    {
    UARTprintf("Unknown Device Class (0x%02x) Connected.\n",
    g_ui32UnknownClass);
    ui32Prompt = 1;
    break;
    }

    //
    // The connected mass storage device is not reporting ready.
    //
    case STATE_TIMEOUT_DEVICE:
    {
    //
    // If this is the first time in this state then print a
    // message.
    //
    UARTprintf("Device Timeout.\n");
    ui32Prompt = 1;

    break;
    }

    //
    // A power fault has occurred.
    //
    case STATE_POWER_FAULT:
    {
    UARTprintf("\nPower fault.\n");
    ui32Prompt = 1;
    break;
    }
    }

    //
    // Save the current state.
    //
    g_eUIState = g_eState;
    }

    //
    // Print a prompt if necessary.
    //
    if(ui32Prompt)
    {
    //
    // Print the prompt based on the current state.
    //
    if(g_eState == STATE_DEVICE_READY)
    {
    UARTprintf("%s> %s", g_cCwdBuf, g_cCmdBuf);
    }
    else if(g_eState == STATE_UNKNOWN_DEVICE)
    {
    UARTprintf("UNKNOWN> %s", g_cCmdBuf);
    }
    else
    {
    UARTprintf("NODEV> %s", g_cCmdBuf);
    }

    //
    // The prompt no longer needs to be printed.
    //
    ui32Prompt = 0;
    }

    //
    // Loop while there are characters that have been received from the
    // UART.
    //
    while(ROM_UARTCharsAvail(UART0_BASE))
    {
    //
    // Read the next character from the UART.
    //
    ui8Char = UARTgetc();

    //
    // See if this character is a backspace and there is at least one
    // character in the input line.
    //
    if((ui8Char == '\b') && (ui32Idx != 0))
    {
    //
    // Erase the lsat character from the input line.
    //
    UARTprintf("\b \b");
    ui32Idx--;
    g_cCmdBuf[ui32Idx] = '\0';
    }

    //
    // See if this character is a newline.
    //
    else if((ui8Char == '\r') || (ui8Char == '\n'))
    {
    //
    // Return to the caller.
    //
    UARTprintf("\n");
    return;
    }

    //
    // See if this character is an escape or Ctrl-U.
    //
    else if((ui8Char == 0x1b) || (ui8Char == 0x15))
    {
    //
    // Erase all characters in the input buffer.
    //
    while(ui32Idx)
    {
    UARTprintf("\b \b");
    ui32Idx--;
    }
    g_cCmdBuf[0] = '\0';
    }

    //
    // See if this is a printable ASCII character.
    //
    else if((ui8Char >= ' ') && (ui8Char <= '~') &&
    (ui32Idx < (sizeof(g_cCmdBuf) - 1)))
    {
    //
    // Add this character to the input buffer.
    //
    g_cCmdBuf[ui32Idx++] = ui8Char;
    g_cCmdBuf[ui32Idx] = '\0';
    UARTprintf("%c", ui8Char);
    }
    }

    //
    // Tell the OTG state machine how much time has passed in
    // milliseconds since the last call.
    //
    USBOTGMain(GetTickms());
    }
    }

    //*****************************************************************************
    //
    // This is the main loop that runs the application.
    //
    //*****************************************************************************
    int
    main(void)
    {
    int iStatus;
    uint32_t ui32SysClock, ui32PLLRate;

    //
    // Initially wait for device connection.
    //
    g_eState = STATE_NO_DEVICE;
    g_eUIState = STATE_NO_DEVICE;

    //
    // Run from the PLL at 120 MHz.
    //
    ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
    SYSCTL_OSC_MAIN |
    SYSCTL_USE_PLL |
    SYSCTL_CFG_VCO_480), 120000000);

    //
    // Configure the device pins.
    //
    PinoutSet(false, true);

    //
    // Enable the UART and print a brief message.
    //
    UARTStdioConfig(0, 115200, ui32SysClock);
    UARTprintf("\033[2J\033[H");
    UARTprintf("USB Mass Storage Host program\n");
    UARTprintf("Type \'help\' for help.\n\n");

    //
    // Configure SysTick for a 100Hz interrupt.
    //
    ROM_SysTickPeriodSet(ui32SysClock / TICKS_PER_SECOND);
    ROM_SysTickEnable();
    ROM_SysTickIntEnable();

    //
    // Enable the uDMA controller and set up the control table base.
    //
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
    ROM_uDMAEnable();
    ROM_uDMAControlBaseSet(g_sDMAControlTable);

    //
    // Initialize the USB stack mode and pass in a mode callback.
    //
    USBStackModeSet(0, eUSBModeHost, 0);

    //
    // Register the host class drivers.
    //
    USBHCDRegisterDrivers(0, g_ppHostClassDrivers, g_ui32NumHostClassDrivers);

    //
    // Open an instance of the mass storage class driver.
    //
    g_psMSCInstance = USBHMSCDriveOpen(0, MSCCallback);

    //
    // Initialize the power configuration. This sets the power enable signal
    // to be active high and does not enable the power fault.
    //
    USBHCDPowerConfigInit(0, USBHCD_VBUS_AUTO_HIGH | USBHCD_VBUS_FILTER);

    //
    // Tell the USB library the CPU clock and the PLL frequency. This is a
    // new requirement for TM4C129 devices.
    //
    SysCtlVCOGet(SYSCTL_XTAL_25MHZ, &ui32PLLRate);
    USBHCDFeatureSet(0, USBLIB_FEATURE_CPUCLK, &ui32SysClock);
    USBHCDFeatureSet(0, USBLIB_FEATURE_USBPLL, &ui32PLLRate);

    //
    // Initialize the USB controller for host operation.
    //
    USBHCDInit(0, g_pHCDPool, HCD_MEMORY_SIZE);

    //
    // Initialize the file system.
    //
    f_mount(0, &g_sFatFs);

    //
    // Enter an infinite loop for reading and processing commands from the
    // user.
    //
    while(1)
    {
    //
    // Get a line of text from the user.
    //
    ReadLine();
    if(g_cCmdBuf[0] == '\0')
    {
    continue;
    }

    //
    // Pass the line from the user to the command processor.
    // It will be parsed and valid commands executed.
    //
    iStatus = CmdLineProcess(g_cCmdBuf);

    //
    // Handle the case of bad command.
    //
    if(iStatus == CMDLINE_BAD_CMD)
    {
    UARTprintf("Bad command!\n");
    }

    //
    // Handle the case of too many arguments.
    //
    else if(iStatus == CMDLINE_TOO_MANY_ARGS)
    {
    UARTprintf("Too many arguments for command processor!\n");
    }

    //
    // Otherwise the command was executed. Print the error
    // code if one was returned.
    //
    else if(iStatus != 0)
    {
    UARTprintf("Command returned error code %s\n",
    StringFromFresult((FRESULT)iStatus));
    }
    }
    }
  • Hello David,

    This looks to be related to the USBHCDPipeWrite API still, but not the issue I mentioned before. I tracked the root cause from the fatfs third party library where the disk_write API in far_usbmsc.c uses a call to USBHMSCBlockWrite. Tracing that through the USB stack shows that it calls USBHSCSIWrite10 which then calls USBHSCSISendCommand which is where USBHCDPipeWrite is finally invoked.

    Inside of USBHCDPipeWrite, there is a check made for the DISCONNECT event, which is the following:

                if(g_sUSBHCD.ui32IntEvents & (INT_EVENT_DISCONNECT |
                                              INT_EVENT_VBUS_ERR |
                                              INT_EVENT_POWER_FAULT))
    

    This check for some reason is failing when it receives the result of both INT_EVENT_DISCONNECT and INT_EVENT_SOF which equates to 0x14.

    It looks like it could be related to that the g_sUSBHCD struct is declared as a static. Another post I found had the advice of turning g_sUSBHCD to a volatile and that solved the users problem. Unfortunately on my end, it doesn't seem to do so, but I'd like you to try that and report back if you get any improvement with that tweak.

    I'll continue to investigate this tomorrow.

  • No, it didn't solve the problem
  • Hello David,

    This is still an open issue on my end. I am awaiting another engineer from a different product group to assist me with debug and finding a solution to this issue as I have exhausted all the ideas on my end. However, I am going to on business travel for part of next week and won't be able to sync with him until Thursday at the very earliest.
  • Thank you very much for your follow up!

    Have nice weekend!

  • Hello David,

    Sorry for the delay on this topic.

    It looks like the issue is actually stemming from the compiler rather than the source code. What version CCS + ARM compiler are you using?

    When I moved the example and usblib into CCS v8 and built both within v8 with the 18.1.1.LTS compiler (note you have to re-link the library for this) then the issue is not present any longer.

    We are not sure why the compiler is causing this issue, but as the latest CCS+ARM compiler solves the problem, we are not planning to investigate it further with the CCS team at this moment. Will making an update to the latest CCS+ARM Compiler be reasonable for your situation?

  • I am using CCS 6.2

    If I download and install CCS 6. Can't I just import my current project and go?
  • Hello David,

    I haven't tried this on CCS v6.2, if you give me a day or so, I can try V6.2 and see what the behavior is for that version?

    Regarding your question, I am not sure I fully understand? Did you mean CCS 8? If so, as long as you have TM4C support and support for your emulators installed with the CCS v8 install then yes you should be able to import and run the project without issues.
  • Don't waste your time on CCS v6.2, since that was where I first found the problem

    My understanding is once I upgrade to CCSv8, this problem will no longer exist (with my current project without any modification), right?
  • Hello David,

    Yes, CCSv8 and also make sure you have the 18.1.1.LTS (or newer) compiler - with that combination, the issue is no longer present from all the testing myself and another engineer did (so 2 separate PCs thus far... hopefully yours will be #3!)
  • Just installed  CCSv8 and tried the same program I sent you, and after a few tried, it locked up at the same place.

  • I installed CCSv8 and used the same test program I posted here, to copy a big file, and unplug USB flash drive while the data was being copied

    After a few tries, it locked up at the same USBHCDPipeWrite

    You have to give the test a few more tries, and I bet you see the same problem
  • Hello David,

    I tried it over two dozen times with multiple boards. I did not use the project you posted here though, I used the TivaWare usb_host_msc example from our latest TivaWare. I will try with the project you posted later today or tomorrow and assess from there.
  • Hi David,
    Also which ARM Compiler was used?
  • the problem with usb_host_msc is the lack of write and sync, so you will not hit the problem.
  • Whatever installed by default
  • Hello David,

    Observed the issue again and still investigating. At first glance I am thinking the Int Handler isn't setting a flag correctly.
  • Hello David,

    I have been further investigating this issue, but haven't made much headway towards a solution. I have confirmed the USB driver is correctly detecting the DISCONNECT event in the Interrupt Handler (so the flag IS set correctly, unlike my initial thoughts), but when the Pipe Write function has to process the change in flag that it is failing to correctly enter the condition for the DISCONNECT event. At this point I think it is a C code and/or compiler issue. I am going to try and sync with some folks outside of the TM4C group regarding options on how to address this.
  • Thanks for the update!