//***************************************************************************** // // usb_host_msc.c - USB mass storage host application. // // Copyright (c) 2013-2014 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.0.12573 of the EK-TM4C1294XL Firmware Package. // //***************************************************************************** #include #include #include #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 //!

USB Mass Storage Class Host (usb_host_msc)

//! //! 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: //! http://elm-chan.org/fsw/ff/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]; //***************************************************************************** // // 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; //***************************************************************************** // // 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]); // // Open the file for reading. // fresult = f_open(&g_sFileObject, g_cTmpBuf, FA_READ); // // 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_cTmpBuf, sizeof(g_cTmpBuf) - 1, (UINT *)&ui32BytesRead); // // If there was an error reading, then print a newline and return the // error to the user. // if(fresult != FR_OK) { UARTprintf("\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); // // 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_cTmpBuf) - 1); // // 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; // // 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); // // 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)); } } }