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.
Diff: main.cpp
- Revision:
- 0:bd0d999bb6fb
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Thu Sep 26 09:21:11 2013 +0000 @@ -0,0 +1,539 @@ +/****************************************************************************** + * 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/"); + } +}