The purpose of this application is to allow easy manipulation of the QSPI file system from a PC
Dependencies: EALib USBDevice mbed
The purpose of this application is to allow easy manipulation of the QSPI file system from a PC.
The application makes the LPC4088 QuickStart Board appear as a USB Memory Stick when connected to a PC. The PC will see the current content of the QSPI file system plus an image file of the file system that can be downloaded and (at a later time) be used to restore the file system to it's current state.
To use this application:
- Make sure that the QSPI file system has been formatted (using either the app_qspi_format application or one of the erase.* images).
- Download the app_qspifs_memstick application using drag-n-drop and then reset the board
- Optionally start a terminal program to read the status messages from the application
- Connect a USB cable to the micro USB slot on the back of the LPC4088 QuickStart Board, underneath the ethernet connector, and then to the PC
- The PC will install drivers if needed and then the USB Memory Stick will be available as a new drive
- Modify the file system to suit your needs
- With the USB cable still connected, press the button on the LPC4088 QuickStart Board
- The application will now:
- disconnect the USB Memory Stick
- write all changes to the QSPI flash memory
- create a new image file of the updated QSPI file system and store it in the .current/ folder
- connect the USB Memory Stick again
- Continue from step 6. until satisfied
Note 1: The file system that is exposed is a copy (in SDRAM) of the QSPI file system. The reason for this is that the USBMSD class requires a FAT file system.
Note 2: The image files created in step 8.3 above will be a *.fsX file (where the 'X' is the size of the file system in MB so *.fs1 for a 1MByte file system). The *.fsX file extensions are recognized by the HDK and can be used to drag-n-drop to the MBED drive in the same way as the *.bin files are. A *.fsX file will not overwrite the program stored in internal flash.
main.cpp
- Committer:
- embeddedartists
- Date:
- 2014-08-26
- Revision:
- 2:5a954ee65b33
- Parent:
- 0:bd0d999bb6fb
File content as of revision 2:5a954ee65b33:
/****************************************************************************** * Includes *****************************************************************************/ #include "mbed.h" #include "QSPIFileSystem.h" #include "USBMSD_RAMFS.h" #include "RAMFileSystem.h" #include "sdram.h" #include "crc.h" /****************************************************************************** * Typedefs and defines *****************************************************************************/ typedef bool (*syncFunc)(const char* name, bool isDir); #define RAM_FS_SIZE (20*1024*1024) //20MB /****************************************************************************** * Local variables *****************************************************************************/ QSPIFileSystem qspi("qspi"); //RAMFileSystem ramfs(0xA0000000, 20*1024*1024, "ram"); //USBMSD_RAMFS usbmsd(&ramfs); DigitalOut myled1(LED1); DigitalOut myled2(LED2); DigitalIn button(p23); DigitalOut myled(LED1); static char lsbuff[NAME_MAX+1]; static char image_file_name[128] = { '\0' }; /****************************************************************************** * Local functions *****************************************************************************/ static void ledShowProgress() { static int state = 0; state = (state + 1) % 2; switch (state) { case 0: myled1 = 1; myled2 = 0; break; case 1: default: myled1 = 0; myled2 = 1; } } static void handleError(const char* msg) { printf(msg); while(true) { myled1 = 1; myled2 = 1; wait(0.3); myled1 = 0; myled2 = 0; wait(0.3); } } static void waitForButtonPress() { printf("Press button to sync file systems\n"); myled1 = 1; myled2 = 1; while(button.read() == 1) { wait(0.1); } myled1 = 0; printf("Button pressed, now release it\n"); while(button.read() == 0) { wait(0.1); } } static void addNotFormattedFile() { FILE *fp = fopen("/ram/qspi_not_formatted.txt", "w"); if (fp != NULL) { fprintf(fp, "The QSPI file system has not be formatted and this program can't do it.\n"); fprintf(fp, "Format the QSPI file system and then run this program again!\n"); fclose(fp); } } static bool recursiveProcessFS(char* buff, const char* name, syncFunc func, bool adding) { uint32_t len = strlen(buff); if (len > 0) { if (buff[len - 1] != '/') { buff[len++] = '/'; buff[len] = '\0'; } } strcat(buff, name); len += strlen(name); DIR *d = opendir(buff); bool result = true; // success if (d != NULL) { if (adding) { // when processing in adding mode folders must be created before it's content result = func(buff, true); } struct dirent *p; while (result && ((p = readdir(d)) != NULL)) { result = recursiveProcessFS(buff, p->d_name, func, adding); buff[len] = '\0'; } closedir(d); if (result && !adding) { // when processing in removing mode folders must be deleted after it's content result = func(buff, true); } } else { // a file result = func(buff, false); } return result; } static uint32_t fileLen(FILE* f) { uint32_t pos = ftell(f); fseek(f, 0, SEEK_END); uint32_t size = ftell(f); fseek(f, pos, SEEK_SET); return size; } static bool copy(FILE* fSrc, FILE* fDst) { char buff[512]; uint32_t left = fileLen(fSrc); uint32_t num; uint32_t chunk; fseek(fSrc, 0, SEEK_SET); do { chunk = (left < 512) ? left : 512; num = fread(buff, 1, chunk, fSrc); if (num > 0) { uint32_t tmp = fwrite(buff, 1, num, fDst); if (tmp != num) { // failed to write return false; } left -= num; ledShowProgress(); } } while(num > 0 && left > 0); // copied entire file return true; } static bool identicalFiles(const char* srcName, const char* dstName) { FILE* fSrc = NULL; FILE* fDst = NULL; bool identical = false; do { fSrc = fopen(srcName, "r"); if (fSrc == NULL) { break; } fDst = fopen(dstName, "r"); if (fDst == NULL) { break; } if (fileLen(fSrc) != fileLen(fDst)) { break; } if (crc_Read(fSrc) != crc_Read(fDst)) { break; } // All tests passed so the files are identical identical = true; } while(false); if (fSrc != NULL) { fclose(fSrc); } if (fDst != NULL) { fclose(fDst); } return identical; } static bool addExisting(const char* srcName, bool isDir, const char* dstName) { bool result = true; // success if (isDir) { DIR *d = opendir(dstName); if (d == NULL) { if (dstName[1] != 'r') { printf("%s, new dir, adding\n", dstName); } if (mkdir(dstName, 0) != 0) { printf("Failed to create folder %s\n", dstName); result = false; // dir did not exist and could not be created } } else { closedir(d); } } else if (!identicalFiles(srcName, dstName)) { // Compare the files to avoid replacing with same FILE* fSrc = fopen(srcName, "r"); if (fSrc != NULL) { FILE* fDst = fopen(dstName, "w"); // open and truncate if (fDst != NULL) { if (dstName[1] != 'r') { printf("%s, changed, updating\n", dstName); } result = copy(fSrc, fDst); if (!result) { printf("Failed to copy %s to %s\n", srcName, dstName); } fclose(fDst); } else { printf("Failed to create file %s\n", dstName); result = false; // unable to create file } fclose(fSrc); } else { printf("Failed to copen source file file %s\n", srcName); result = false; // unable to open source } } else { if (dstName[1] != 'r') { printf("%s identical, skipping\n", dstName); } } return result; } static bool addExistingToQspi(const char* name, bool isDir) { // create the target file name by replacing /ram/ with /qspi/ char buff[256]; buff[0] = '\0'; strcat(buff, "/qspi/"); strcat(buff, name+5); // Don't add the file created by createImageFile() if (strcmp(name, image_file_name) == 0) { return true; } return addExisting(name, isDir, buff); } static bool addExistingToRAM(const char* name, bool isDir) { // create the target file name by replacing /qspi/ with /ram/ char buff[256]; buff[0] = '\0'; strcat(buff, "/ram/"); strcat(buff, name+6); return addExisting(name, isDir, buff); } static bool removeIfMissing(const char* toLookFor, const char* toRemove, bool isDir) { int result = 0; //success if (isDir) { DIR *d = opendir(toLookFor); if (d == NULL) { // dir doesn't exist => delete it if (toRemove[1] != 'r') { printf("%s, missing, deleting dir\n", toRemove); } result = remove(toRemove); ledShowProgress(); } else { // dir exist => don't delete closedir(d); } } else { FILE* f = fopen(toLookFor, "r"); if (f == NULL) { // file doesn't exist => delete it if (toRemove[1] != 'r') { printf("%s, missing, deleting file\n", toRemove); } result = remove(toRemove); ledShowProgress(); } else { // file exist => don't delete fclose(f); } } return (result == 0); } static bool removeMissingFromQspi(const char* name, bool isDir) { // create the target file name by replacing /qspi/ with /ram/ char buff[256]; buff[0] = '\0'; strcat(buff, "/ram/"); strcat(buff, name+6); removeIfMissing(buff, name, isDir); return true; } static bool removeMissingFromRAM(const char* name, bool isDir) { // create the target file name by replacing /ram/ with /qspi/ char buff[256]; buff[0] = '\0'; strcat(buff, "/qspi/"); strcat(buff, name+5); // Don't remove the file created by createImageFile() if (strcmp(name, image_file_name) == 0) { return true; } removeIfMissing(buff, name, isDir); return true; } static void syncDir(const char* to, const char* from) { printf("Starting to sync %s on top of %s (This may take time. LED1 & 2 blink for each file)\n", from, to); char* buff = (char*)malloc(512); if (buff != NULL) { buff[0] = '\0'; if (strcmp(to, "/qspi/") == 0) { if (!recursiveProcessFS(buff, to, removeMissingFromQspi, false)) { printf("Failed to remove files from %s that were missing on %s\n", to, from); } else { buff[0] = '\0'; if (!recursiveProcessFS(buff, from, addExistingToQspi, true)) { printf("Failed to add files to %s that existed on %s\n", to, from); } } } else { if (!recursiveProcessFS(buff, to, removeMissingFromRAM, false)) { printf("Failed to remove files from %s that were missing on %s\n", to, from); } else { buff[0] = '\0'; if (!recursiveProcessFS(buff, from, addExistingToRAM, true)) { printf("Failed to add files to %s that existed on %s\n", to, from); } } } free(buff); } printf("Sync completed\n"); } static void createImageFile() { uint32_t startAddr; uint32_t endAddr; uint32_t size; printf("Creating image of existing (if any) QSPI file system\n"); if (!qspi.getMemoryBoundaries(&startAddr, &endAddr)) { handleError("QSPI FS not formatted or impossible to determine it's size\n"); } // Align the start address to an even multiple of 1MB startAddr = startAddr & 0xfff00000; // Update the file to match the size of the file system size = endAddr - startAddr; if ((size < 0x00100000) || (size > 0x00800000) || ((size & 0xfffff) > 0)) { sprintf(lsbuff, "QSPI FS size is not supported (%u bytes)\n", size); handleError(lsbuff); } sprintf(image_file_name, "/ram/.current/fs_image.fs%d", (size >> 20)); // NOTE: The line below is very very !!!! important. For some weird reason the // RAM file system must have at least one folder on it before USB is connected. // If the RAM file system doesn't have any folders on it the result is that // the content of the RAM file system will be the same after USB is disconnected // as it was before connecting - regardless of what is added. Very strange indeed. mkdir("/ram/.current", 0); printf("QSPI FS max size is %d MB\n", (size >> 20)); FILE *fp = fopen(image_file_name, "w"); if (fp != NULL) { while (size > 0) { uint32_t written = fwrite((char*)(endAddr - size), 1, size, fp); size -= written; if (written == 0) { handleError("Failed to create QSPI image file\n"); } } fclose(fp); } } static bool list(const char* name, bool isDir) { if (isDir) { printf("d: %s\n", name); } else { FILE* f = fopen(name, "r"); if (f != NULL) { uint32_t len = fileLen(f); printf("f: %7u %s\n", len, name); fclose(f); } else { printf("f: ??? %s\n", name); } } return true; } static void recursiveList(const char* dirname) { printf("\nRecursive list of file and folders in %s\n", dirname); char* buff = (char*)malloc(512); if (buff != NULL) { buff[0] = '\0'; recursiveProcessFS(buff, dirname, list, true); free(buff); } } /****************************************************************************** * Main function *****************************************************************************/ int main() { printf("\n-----------------\n\nWelcome to the QSPI file system tool...\n"); // 1) Make sure that the button works // 2) Init SDRAM and allocate space for the RAM file system // 3) Setup RAM FS // 4a) If QSPI FS is not formatted: // i) Create a "qspi_not_formatted.txt" file in the root of the file system // 4b) QSPI FS formatted // i) Create an image file of the QSPI FS // ii) Sync QSPI FS on top of RAM FS // 5) Connect USB // 6a) If QSPI FS is not formatted: Loop forever doing nothing // 6b) QSPI FS formatted: Wait for button press // 7) Button pressed, Disconnect USB // 8) QSPI formatted // i) Sync RAM FS on top of QSPI FS // ii) Goto 3) // // 1) button.mode(PullUp); // 2) if (sdram_init()) { handleError("Failed to initialize SDRAM\n"); } void* fsmem = malloc(RAM_FS_SIZE); if (fsmem == NULL) { handleError("Failed to allocate memory for RAM file system\n"); } RAMFileSystem ramfs((uint32_t)fsmem, RAM_FS_SIZE, "ram"); USBMSD_RAMFS usbmsd(&ramfs); while(true) { // 3) ramfs.format(); // 4a) bool qspiFormatted = qspi.isformatted(); if (!qspiFormatted) { addNotFormattedFile(); } // 4b) else { //addTestFile(); createImageFile(); // Copy QSPI FS to RAM FS syncDir("/ram/", "/qspi/"); } // 5) printf("Insert the USB cable!\n"); printf("Starting USB...\n"); for (int i = 0; i < 10; i++) { if (usbmsd.connect()) { printf("Connected!\n"); break; } printf("Failed to connect USB, testing again...\n"); printf("Insert (or remove and then insert) the USB cable!\n"); wait(1); } // 6b) if (qspiFormatted) { waitForButtonPress(); } else { // 6a) no point in waiting for buttons if no file system while (1) {}; } // 7) usbmsd.disconnect(); printf("Disconnected!\n"); // 8) Copy RAM FS to QSPI FS recursiveList("/ram/"); syncDir("/qspi/", "/ram/"); } }