The purpose of this application is to allow easy manipulation of the QSPI file system from a PC (**NOTE**: Application doesn't work with update to mbed OS 5 since USB Device support is currently not available for LPC4088)
Dependencies: DMBasicGUI DMSupport
Note
This application doesn't work with the latest updates to mbed OS 5 since USB Device support is currently not available for LPC4088
The purpose of this application is to allow easy manipulation of the QSPI file system from a PC.
The application makes the LPC4088 Display Module 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:
- Download the lpc4088_displaymodule_fs_aid 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 mini USB slot on the back of the LPC4088 Display Module, 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 USER/ISP/SW1 button on the LPC4088 Display Module
- 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 5. until satisfied
Note 1: If the QSPI doesn't have a file system on it or to replace the current one with a new, possibly of a different size, file system just add a file with the name format_qspi_X_mb (where X should be replace with the wanted file system size in Mb). For a 10 Mb file system the file name should be format_qspi_10_mb.
Note 2: 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 3: The image files created in step 7.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@0:06e35dd73c95, 2016-05-16 (annotated)
- Committer:
- alindvall
- Date:
- Mon May 16 07:05:18 2016 +0000
- Revision:
- 0:06e35dd73c95
- Child:
- 1:b04139d88c59
First version
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
alindvall | 0:06e35dd73c95 | 1 | /****************************************************************************** |
alindvall | 0:06e35dd73c95 | 2 | * Includes |
alindvall | 0:06e35dd73c95 | 3 | *****************************************************************************/ |
alindvall | 0:06e35dd73c95 | 4 | |
alindvall | 0:06e35dd73c95 | 5 | #include "mbed.h" |
alindvall | 0:06e35dd73c95 | 6 | #include "mbed_interface.h" |
alindvall | 0:06e35dd73c95 | 7 | #include "rtos.h" |
alindvall | 0:06e35dd73c95 | 8 | |
alindvall | 0:06e35dd73c95 | 9 | #include "DMBoard.h" |
alindvall | 0:06e35dd73c95 | 10 | #include "RAMFileSystem.h" |
alindvall | 0:06e35dd73c95 | 11 | #include "USBMSD_RAMFS.h" |
alindvall | 0:06e35dd73c95 | 12 | |
alindvall | 0:06e35dd73c95 | 13 | #include "crc.h" |
alindvall | 0:06e35dd73c95 | 14 | |
alindvall | 0:06e35dd73c95 | 15 | #include "lpc_swim.h" |
alindvall | 0:06e35dd73c95 | 16 | #include "lpc_swim_font.h" |
alindvall | 0:06e35dd73c95 | 17 | #include "lpc_winfreesystem14x16.h" |
alindvall | 0:06e35dd73c95 | 18 | |
alindvall | 0:06e35dd73c95 | 19 | /****************************************************************************** |
alindvall | 0:06e35dd73c95 | 20 | * Typedefs and defines |
alindvall | 0:06e35dd73c95 | 21 | *****************************************************************************/ |
alindvall | 0:06e35dd73c95 | 22 | |
alindvall | 0:06e35dd73c95 | 23 | typedef bool (*syncFunc)(const char* name, bool isDir); |
alindvall | 0:06e35dd73c95 | 24 | |
alindvall | 0:06e35dd73c95 | 25 | /* Size of RAM file system */ |
alindvall | 0:06e35dd73c95 | 26 | #define RAM_FS_SIZE (20*1024*1024) |
alindvall | 0:06e35dd73c95 | 27 | |
alindvall | 0:06e35dd73c95 | 28 | |
alindvall | 0:06e35dd73c95 | 29 | /****************************************************************************** |
alindvall | 0:06e35dd73c95 | 30 | * Local variables |
alindvall | 0:06e35dd73c95 | 31 | *****************************************************************************/ |
alindvall | 0:06e35dd73c95 | 32 | |
alindvall | 0:06e35dd73c95 | 33 | //DigitalOut myled1(LED1); |
alindvall | 0:06e35dd73c95 | 34 | //DigitalOut myled2(LED2); |
alindvall | 0:06e35dd73c95 | 35 | //DigitalIn button(p23); |
alindvall | 0:06e35dd73c95 | 36 | |
alindvall | 0:06e35dd73c95 | 37 | //DigitalOut myled(LED1); |
alindvall | 0:06e35dd73c95 | 38 | static char lsbuff[NAME_MAX+1]; |
alindvall | 0:06e35dd73c95 | 39 | |
alindvall | 0:06e35dd73c95 | 40 | static char image_file_name[128] = { '\0' }; |
alindvall | 0:06e35dd73c95 | 41 | |
alindvall | 0:06e35dd73c95 | 42 | /****************************************************************************** |
alindvall | 0:06e35dd73c95 | 43 | * Local functions |
alindvall | 0:06e35dd73c95 | 44 | *****************************************************************************/ |
alindvall | 0:06e35dd73c95 | 45 | |
alindvall | 0:06e35dd73c95 | 46 | |
alindvall | 0:06e35dd73c95 | 47 | static void ledShowProgress() |
alindvall | 0:06e35dd73c95 | 48 | { |
alindvall | 0:06e35dd73c95 | 49 | static int state = 0; |
alindvall | 0:06e35dd73c95 | 50 | state = (state + 1) % 2; |
alindvall | 0:06e35dd73c95 | 51 | |
alindvall | 0:06e35dd73c95 | 52 | DMBoard* board = &DMBoard::instance(); |
alindvall | 0:06e35dd73c95 | 53 | board->setLED(DMBoard::Led1, state==0); |
alindvall | 0:06e35dd73c95 | 54 | board->setLED(DMBoard::Led2, state!=0); |
alindvall | 0:06e35dd73c95 | 55 | } |
alindvall | 0:06e35dd73c95 | 56 | |
alindvall | 0:06e35dd73c95 | 57 | static void handleError(const char* msg) |
alindvall | 0:06e35dd73c95 | 58 | { |
alindvall | 0:06e35dd73c95 | 59 | DMBoard* board = &DMBoard::instance(); |
alindvall | 0:06e35dd73c95 | 60 | board->logger()->printf(msg); |
alindvall | 0:06e35dd73c95 | 61 | while(true) { |
alindvall | 0:06e35dd73c95 | 62 | board->setLED(DMBoard::Led1, false); |
alindvall | 0:06e35dd73c95 | 63 | board->setLED(DMBoard::Led2, false); |
alindvall | 0:06e35dd73c95 | 64 | wait(0.3); |
alindvall | 0:06e35dd73c95 | 65 | board->setLED(DMBoard::Led1, true); |
alindvall | 0:06e35dd73c95 | 66 | board->setLED(DMBoard::Led2, true); |
alindvall | 0:06e35dd73c95 | 67 | wait(0.3); |
alindvall | 0:06e35dd73c95 | 68 | } |
alindvall | 0:06e35dd73c95 | 69 | } |
alindvall | 0:06e35dd73c95 | 70 | |
alindvall | 0:06e35dd73c95 | 71 | static void waitForButtonPress() |
alindvall | 0:06e35dd73c95 | 72 | { |
alindvall | 0:06e35dd73c95 | 73 | DMBoard* board = &DMBoard::instance(); |
alindvall | 0:06e35dd73c95 | 74 | printf("Press button to sync file systems\n"); |
alindvall | 0:06e35dd73c95 | 75 | |
alindvall | 0:06e35dd73c95 | 76 | board->setLED(DMBoard::Led1, false); |
alindvall | 0:06e35dd73c95 | 77 | board->setLED(DMBoard::Led2, false); |
alindvall | 0:06e35dd73c95 | 78 | while(!board->buttonPressed()) { |
alindvall | 0:06e35dd73c95 | 79 | wait(0.1); |
alindvall | 0:06e35dd73c95 | 80 | } |
alindvall | 0:06e35dd73c95 | 81 | board->setLED(DMBoard::Led1, true); |
alindvall | 0:06e35dd73c95 | 82 | printf("Button pressed, now release it\n"); |
alindvall | 0:06e35dd73c95 | 83 | while(board->buttonPressed()) { |
alindvall | 0:06e35dd73c95 | 84 | wait(0.1); |
alindvall | 0:06e35dd73c95 | 85 | } |
alindvall | 0:06e35dd73c95 | 86 | } |
alindvall | 0:06e35dd73c95 | 87 | |
alindvall | 0:06e35dd73c95 | 88 | static void addNotFormattedFile() |
alindvall | 0:06e35dd73c95 | 89 | { |
alindvall | 0:06e35dd73c95 | 90 | FILE *fp = fopen("/ram/qspi_not_formatted.txt", "w"); |
alindvall | 0:06e35dd73c95 | 91 | if (fp != NULL) { |
alindvall | 0:06e35dd73c95 | 92 | fprintf(fp, "The QSPI file system has not be formatted and this program can't do it.\n"); |
alindvall | 0:06e35dd73c95 | 93 | fprintf(fp, "Format the QSPI file system and then run this program again!\n"); |
alindvall | 0:06e35dd73c95 | 94 | fclose(fp); |
alindvall | 0:06e35dd73c95 | 95 | } |
alindvall | 0:06e35dd73c95 | 96 | } |
alindvall | 0:06e35dd73c95 | 97 | |
alindvall | 0:06e35dd73c95 | 98 | static bool recursiveProcessFS(char* buff, const char* name, syncFunc func, bool adding) |
alindvall | 0:06e35dd73c95 | 99 | { |
alindvall | 0:06e35dd73c95 | 100 | uint32_t len = strlen(buff); |
alindvall | 0:06e35dd73c95 | 101 | if (len > 0) { |
alindvall | 0:06e35dd73c95 | 102 | if (buff[len - 1] != '/') { |
alindvall | 0:06e35dd73c95 | 103 | buff[len++] = '/'; |
alindvall | 0:06e35dd73c95 | 104 | buff[len] = '\0'; |
alindvall | 0:06e35dd73c95 | 105 | } |
alindvall | 0:06e35dd73c95 | 106 | } |
alindvall | 0:06e35dd73c95 | 107 | strcat(buff, name); |
alindvall | 0:06e35dd73c95 | 108 | len += strlen(name); |
alindvall | 0:06e35dd73c95 | 109 | |
alindvall | 0:06e35dd73c95 | 110 | DIR *d = opendir(buff); |
alindvall | 0:06e35dd73c95 | 111 | bool result = true; // success |
alindvall | 0:06e35dd73c95 | 112 | if (d != NULL) { |
alindvall | 0:06e35dd73c95 | 113 | if (adding) { |
alindvall | 0:06e35dd73c95 | 114 | // when processing in adding mode folders must be created before it's content |
alindvall | 0:06e35dd73c95 | 115 | result = func(buff, true); |
alindvall | 0:06e35dd73c95 | 116 | } |
alindvall | 0:06e35dd73c95 | 117 | struct dirent *p; |
alindvall | 0:06e35dd73c95 | 118 | while (result && ((p = readdir(d)) != NULL)) { |
alindvall | 0:06e35dd73c95 | 119 | result = recursiveProcessFS(buff, p->d_name, func, adding); |
alindvall | 0:06e35dd73c95 | 120 | buff[len] = '\0'; |
alindvall | 0:06e35dd73c95 | 121 | } |
alindvall | 0:06e35dd73c95 | 122 | closedir(d); |
alindvall | 0:06e35dd73c95 | 123 | if (result && !adding) { |
alindvall | 0:06e35dd73c95 | 124 | // when processing in removing mode folders must be deleted after it's content |
alindvall | 0:06e35dd73c95 | 125 | result = func(buff, true); |
alindvall | 0:06e35dd73c95 | 126 | } |
alindvall | 0:06e35dd73c95 | 127 | } else { |
alindvall | 0:06e35dd73c95 | 128 | // a file |
alindvall | 0:06e35dd73c95 | 129 | result = func(buff, false); |
alindvall | 0:06e35dd73c95 | 130 | } |
alindvall | 0:06e35dd73c95 | 131 | return result; |
alindvall | 0:06e35dd73c95 | 132 | } |
alindvall | 0:06e35dd73c95 | 133 | |
alindvall | 0:06e35dd73c95 | 134 | static uint32_t fileLen(FILE* f) |
alindvall | 0:06e35dd73c95 | 135 | { |
alindvall | 0:06e35dd73c95 | 136 | uint32_t pos = ftell(f); |
alindvall | 0:06e35dd73c95 | 137 | fseek(f, 0, SEEK_END); |
alindvall | 0:06e35dd73c95 | 138 | uint32_t size = ftell(f); |
alindvall | 0:06e35dd73c95 | 139 | fseek(f, pos, SEEK_SET); |
alindvall | 0:06e35dd73c95 | 140 | return size; |
alindvall | 0:06e35dd73c95 | 141 | } |
alindvall | 0:06e35dd73c95 | 142 | |
alindvall | 0:06e35dd73c95 | 143 | static bool copy(FILE* fSrc, FILE* fDst) |
alindvall | 0:06e35dd73c95 | 144 | { |
alindvall | 0:06e35dd73c95 | 145 | char buff[512]; |
alindvall | 0:06e35dd73c95 | 146 | uint32_t left = fileLen(fSrc); |
alindvall | 0:06e35dd73c95 | 147 | uint32_t num; |
alindvall | 0:06e35dd73c95 | 148 | uint32_t chunk; |
alindvall | 0:06e35dd73c95 | 149 | |
alindvall | 0:06e35dd73c95 | 150 | fseek(fSrc, 0, SEEK_SET); |
alindvall | 0:06e35dd73c95 | 151 | do { |
alindvall | 0:06e35dd73c95 | 152 | chunk = (left < 512) ? left : 512; |
alindvall | 0:06e35dd73c95 | 153 | num = fread(buff, 1, chunk, fSrc); |
alindvall | 0:06e35dd73c95 | 154 | if (num > 0) { |
alindvall | 0:06e35dd73c95 | 155 | uint32_t tmp = fwrite(buff, 1, num, fDst); |
alindvall | 0:06e35dd73c95 | 156 | if (tmp != num) { |
alindvall | 0:06e35dd73c95 | 157 | // failed to write |
alindvall | 0:06e35dd73c95 | 158 | return false; |
alindvall | 0:06e35dd73c95 | 159 | } |
alindvall | 0:06e35dd73c95 | 160 | left -= num; |
alindvall | 0:06e35dd73c95 | 161 | ledShowProgress(); |
alindvall | 0:06e35dd73c95 | 162 | } |
alindvall | 0:06e35dd73c95 | 163 | } while(num > 0 && left > 0); |
alindvall | 0:06e35dd73c95 | 164 | |
alindvall | 0:06e35dd73c95 | 165 | // copied entire file |
alindvall | 0:06e35dd73c95 | 166 | return true; |
alindvall | 0:06e35dd73c95 | 167 | } |
alindvall | 0:06e35dd73c95 | 168 | |
alindvall | 0:06e35dd73c95 | 169 | static bool identicalFiles(const char* srcName, const char* dstName) |
alindvall | 0:06e35dd73c95 | 170 | { |
alindvall | 0:06e35dd73c95 | 171 | FILE* fSrc = NULL; |
alindvall | 0:06e35dd73c95 | 172 | FILE* fDst = NULL; |
alindvall | 0:06e35dd73c95 | 173 | bool identical = false; |
alindvall | 0:06e35dd73c95 | 174 | do |
alindvall | 0:06e35dd73c95 | 175 | { |
alindvall | 0:06e35dd73c95 | 176 | fSrc = fopen(srcName, "r"); |
alindvall | 0:06e35dd73c95 | 177 | if (fSrc == NULL) { |
alindvall | 0:06e35dd73c95 | 178 | break; |
alindvall | 0:06e35dd73c95 | 179 | } |
alindvall | 0:06e35dd73c95 | 180 | fDst = fopen(dstName, "r"); |
alindvall | 0:06e35dd73c95 | 181 | if (fDst == NULL) { |
alindvall | 0:06e35dd73c95 | 182 | break; |
alindvall | 0:06e35dd73c95 | 183 | } |
alindvall | 0:06e35dd73c95 | 184 | if (fileLen(fSrc) != fileLen(fDst)) { |
alindvall | 0:06e35dd73c95 | 185 | break; |
alindvall | 0:06e35dd73c95 | 186 | } |
alindvall | 0:06e35dd73c95 | 187 | if (crc_File(fSrc) != crc_File(fDst)) { |
alindvall | 0:06e35dd73c95 | 188 | break; |
alindvall | 0:06e35dd73c95 | 189 | } |
alindvall | 0:06e35dd73c95 | 190 | |
alindvall | 0:06e35dd73c95 | 191 | // All tests passed so the files are identical |
alindvall | 0:06e35dd73c95 | 192 | identical = true; |
alindvall | 0:06e35dd73c95 | 193 | |
alindvall | 0:06e35dd73c95 | 194 | } while(false); |
alindvall | 0:06e35dd73c95 | 195 | |
alindvall | 0:06e35dd73c95 | 196 | if (fSrc != NULL) { |
alindvall | 0:06e35dd73c95 | 197 | fclose(fSrc); |
alindvall | 0:06e35dd73c95 | 198 | } |
alindvall | 0:06e35dd73c95 | 199 | if (fDst != NULL) { |
alindvall | 0:06e35dd73c95 | 200 | fclose(fDst); |
alindvall | 0:06e35dd73c95 | 201 | } |
alindvall | 0:06e35dd73c95 | 202 | |
alindvall | 0:06e35dd73c95 | 203 | return identical; |
alindvall | 0:06e35dd73c95 | 204 | } |
alindvall | 0:06e35dd73c95 | 205 | |
alindvall | 0:06e35dd73c95 | 206 | static bool addExisting(const char* srcName, bool isDir, const char* dstName) |
alindvall | 0:06e35dd73c95 | 207 | { |
alindvall | 0:06e35dd73c95 | 208 | bool result = true; // success |
alindvall | 0:06e35dd73c95 | 209 | if (isDir) { |
alindvall | 0:06e35dd73c95 | 210 | DIR *d = opendir(dstName); |
alindvall | 0:06e35dd73c95 | 211 | if (d == NULL) { |
alindvall | 0:06e35dd73c95 | 212 | if (dstName[1] != 'r') { printf("%s, new dir, adding\n", dstName); } |
alindvall | 0:06e35dd73c95 | 213 | if (mkdir(dstName, 0) != 0) { |
alindvall | 0:06e35dd73c95 | 214 | printf("Failed to create folder %s\n", dstName); |
alindvall | 0:06e35dd73c95 | 215 | result = false; // dir did not exist and could not be created |
alindvall | 0:06e35dd73c95 | 216 | } |
alindvall | 0:06e35dd73c95 | 217 | } else { |
alindvall | 0:06e35dd73c95 | 218 | closedir(d); |
alindvall | 0:06e35dd73c95 | 219 | } |
alindvall | 0:06e35dd73c95 | 220 | } else if (!identicalFiles(srcName, dstName)) { |
alindvall | 0:06e35dd73c95 | 221 | // Compare the files to avoid replacing with same |
alindvall | 0:06e35dd73c95 | 222 | FILE* fSrc = fopen(srcName, "r"); |
alindvall | 0:06e35dd73c95 | 223 | if (fSrc != NULL) { |
alindvall | 0:06e35dd73c95 | 224 | FILE* fDst = fopen(dstName, "w"); // open and truncate |
alindvall | 0:06e35dd73c95 | 225 | if (fDst != NULL) { |
alindvall | 0:06e35dd73c95 | 226 | if (dstName[1] != 'r') { printf("%s, changed, updating\n", dstName); } |
alindvall | 0:06e35dd73c95 | 227 | result = copy(fSrc, fDst); |
alindvall | 0:06e35dd73c95 | 228 | if (!result) { |
alindvall | 0:06e35dd73c95 | 229 | printf("Failed to copy %s to %s\n", srcName, dstName); |
alindvall | 0:06e35dd73c95 | 230 | } |
alindvall | 0:06e35dd73c95 | 231 | fclose(fDst); |
alindvall | 0:06e35dd73c95 | 232 | } else { |
alindvall | 0:06e35dd73c95 | 233 | printf("Failed to create file %s\n", dstName); |
alindvall | 0:06e35dd73c95 | 234 | result = false; // unable to create file |
alindvall | 0:06e35dd73c95 | 235 | } |
alindvall | 0:06e35dd73c95 | 236 | fclose(fSrc); |
alindvall | 0:06e35dd73c95 | 237 | } else { |
alindvall | 0:06e35dd73c95 | 238 | printf("Failed to copen source file file %s\n", srcName); |
alindvall | 0:06e35dd73c95 | 239 | result = false; // unable to open source |
alindvall | 0:06e35dd73c95 | 240 | } |
alindvall | 0:06e35dd73c95 | 241 | } else { |
alindvall | 0:06e35dd73c95 | 242 | if (dstName[1] != 'r') { printf("%s identical, skipping\n", dstName); } |
alindvall | 0:06e35dd73c95 | 243 | } |
alindvall | 0:06e35dd73c95 | 244 | return result; |
alindvall | 0:06e35dd73c95 | 245 | } |
alindvall | 0:06e35dd73c95 | 246 | |
alindvall | 0:06e35dd73c95 | 247 | static bool addExistingToQspi(const char* name, bool isDir) |
alindvall | 0:06e35dd73c95 | 248 | { |
alindvall | 0:06e35dd73c95 | 249 | // create the target file name by replacing /ram/ with /qspi/ |
alindvall | 0:06e35dd73c95 | 250 | char buff[256]; |
alindvall | 0:06e35dd73c95 | 251 | buff[0] = '\0'; |
alindvall | 0:06e35dd73c95 | 252 | strcat(buff, "/qspi/"); |
alindvall | 0:06e35dd73c95 | 253 | strcat(buff, name+5); |
alindvall | 0:06e35dd73c95 | 254 | |
alindvall | 0:06e35dd73c95 | 255 | // Don't add the file created by createImageFile() |
alindvall | 0:06e35dd73c95 | 256 | if (strcmp(name, image_file_name) == 0) { |
alindvall | 0:06e35dd73c95 | 257 | return true; |
alindvall | 0:06e35dd73c95 | 258 | } |
alindvall | 0:06e35dd73c95 | 259 | |
alindvall | 0:06e35dd73c95 | 260 | return addExisting(name, isDir, buff); |
alindvall | 0:06e35dd73c95 | 261 | } |
alindvall | 0:06e35dd73c95 | 262 | |
alindvall | 0:06e35dd73c95 | 263 | static bool addExistingToRAM(const char* name, bool isDir) |
alindvall | 0:06e35dd73c95 | 264 | { |
alindvall | 0:06e35dd73c95 | 265 | // create the target file name by replacing /qspi/ with /ram/ |
alindvall | 0:06e35dd73c95 | 266 | char buff[256]; |
alindvall | 0:06e35dd73c95 | 267 | buff[0] = '\0'; |
alindvall | 0:06e35dd73c95 | 268 | strcat(buff, "/ram/"); |
alindvall | 0:06e35dd73c95 | 269 | strcat(buff, name+6); |
alindvall | 0:06e35dd73c95 | 270 | return addExisting(name, isDir, buff); |
alindvall | 0:06e35dd73c95 | 271 | } |
alindvall | 0:06e35dd73c95 | 272 | |
alindvall | 0:06e35dd73c95 | 273 | static bool removeIfMissing(const char* toLookFor, const char* toRemove, bool isDir) |
alindvall | 0:06e35dd73c95 | 274 | { |
alindvall | 0:06e35dd73c95 | 275 | int result = 0; //success |
alindvall | 0:06e35dd73c95 | 276 | if (isDir) { |
alindvall | 0:06e35dd73c95 | 277 | DIR *d = opendir(toLookFor); |
alindvall | 0:06e35dd73c95 | 278 | if (d == NULL) { |
alindvall | 0:06e35dd73c95 | 279 | // dir doesn't exist => delete it |
alindvall | 0:06e35dd73c95 | 280 | if (toRemove[1] != 'r') { printf("%s, missing, deleting dir\n", toRemove); } |
alindvall | 0:06e35dd73c95 | 281 | result = remove(toRemove); |
alindvall | 0:06e35dd73c95 | 282 | ledShowProgress(); |
alindvall | 0:06e35dd73c95 | 283 | } else { |
alindvall | 0:06e35dd73c95 | 284 | // dir exist => don't delete |
alindvall | 0:06e35dd73c95 | 285 | closedir(d); |
alindvall | 0:06e35dd73c95 | 286 | } |
alindvall | 0:06e35dd73c95 | 287 | } else { |
alindvall | 0:06e35dd73c95 | 288 | FILE* f = fopen(toLookFor, "r"); |
alindvall | 0:06e35dd73c95 | 289 | if (f == NULL) { |
alindvall | 0:06e35dd73c95 | 290 | // file doesn't exist => delete it |
alindvall | 0:06e35dd73c95 | 291 | if (toRemove[1] != 'r') { printf("%s, missing, deleting file\n", toRemove); } |
alindvall | 0:06e35dd73c95 | 292 | result = remove(toRemove); |
alindvall | 0:06e35dd73c95 | 293 | ledShowProgress(); |
alindvall | 0:06e35dd73c95 | 294 | } else { |
alindvall | 0:06e35dd73c95 | 295 | // file exist => don't delete |
alindvall | 0:06e35dd73c95 | 296 | fclose(f); |
alindvall | 0:06e35dd73c95 | 297 | } |
alindvall | 0:06e35dd73c95 | 298 | } |
alindvall | 0:06e35dd73c95 | 299 | return (result == 0); |
alindvall | 0:06e35dd73c95 | 300 | } |
alindvall | 0:06e35dd73c95 | 301 | |
alindvall | 0:06e35dd73c95 | 302 | static bool removeMissingFromQspi(const char* name, bool isDir) |
alindvall | 0:06e35dd73c95 | 303 | { |
alindvall | 0:06e35dd73c95 | 304 | // create the target file name by replacing /qspi/ with /ram/ |
alindvall | 0:06e35dd73c95 | 305 | char buff[256]; |
alindvall | 0:06e35dd73c95 | 306 | buff[0] = '\0'; |
alindvall | 0:06e35dd73c95 | 307 | strcat(buff, "/ram/"); |
alindvall | 0:06e35dd73c95 | 308 | strcat(buff, name+6); |
alindvall | 0:06e35dd73c95 | 309 | removeIfMissing(buff, name, isDir); |
alindvall | 0:06e35dd73c95 | 310 | return true; |
alindvall | 0:06e35dd73c95 | 311 | } |
alindvall | 0:06e35dd73c95 | 312 | |
alindvall | 0:06e35dd73c95 | 313 | static bool removeMissingFromRAM(const char* name, bool isDir) |
alindvall | 0:06e35dd73c95 | 314 | { |
alindvall | 0:06e35dd73c95 | 315 | // create the target file name by replacing /ram/ with /qspi/ |
alindvall | 0:06e35dd73c95 | 316 | char buff[256]; |
alindvall | 0:06e35dd73c95 | 317 | buff[0] = '\0'; |
alindvall | 0:06e35dd73c95 | 318 | strcat(buff, "/qspi/"); |
alindvall | 0:06e35dd73c95 | 319 | strcat(buff, name+5); |
alindvall | 0:06e35dd73c95 | 320 | |
alindvall | 0:06e35dd73c95 | 321 | // Don't remove the file created by createImageFile() |
alindvall | 0:06e35dd73c95 | 322 | if (strcmp(name, image_file_name) == 0) { |
alindvall | 0:06e35dd73c95 | 323 | return true; |
alindvall | 0:06e35dd73c95 | 324 | } |
alindvall | 0:06e35dd73c95 | 325 | |
alindvall | 0:06e35dd73c95 | 326 | removeIfMissing(buff, name, isDir); |
alindvall | 0:06e35dd73c95 | 327 | return true; |
alindvall | 0:06e35dd73c95 | 328 | } |
alindvall | 0:06e35dd73c95 | 329 | |
alindvall | 0:06e35dd73c95 | 330 | static void syncDir(const char* to, const char* from) |
alindvall | 0:06e35dd73c95 | 331 | { |
alindvall | 0:06e35dd73c95 | 332 | printf("Starting to sync %s on top of %s (This may take time. LED1 & 2 blink for each file)\n", from, to); |
alindvall | 0:06e35dd73c95 | 333 | |
alindvall | 0:06e35dd73c95 | 334 | char* buff = (char*)malloc(512); |
alindvall | 0:06e35dd73c95 | 335 | if (buff != NULL) |
alindvall | 0:06e35dd73c95 | 336 | { |
alindvall | 0:06e35dd73c95 | 337 | buff[0] = '\0'; |
alindvall | 0:06e35dd73c95 | 338 | if (strcmp(to, "/qspi/") == 0) { |
alindvall | 0:06e35dd73c95 | 339 | if (!recursiveProcessFS(buff, to, removeMissingFromQspi, false)) { |
alindvall | 0:06e35dd73c95 | 340 | printf("Failed to remove files from %s that were missing on %s\n", to, from); |
alindvall | 0:06e35dd73c95 | 341 | } else { |
alindvall | 0:06e35dd73c95 | 342 | buff[0] = '\0'; |
alindvall | 0:06e35dd73c95 | 343 | if (!recursiveProcessFS(buff, from, addExistingToQspi, true)) { |
alindvall | 0:06e35dd73c95 | 344 | printf("Failed to add files to %s that existed on %s\n", to, from); |
alindvall | 0:06e35dd73c95 | 345 | } |
alindvall | 0:06e35dd73c95 | 346 | } |
alindvall | 0:06e35dd73c95 | 347 | } else { |
alindvall | 0:06e35dd73c95 | 348 | if (!recursiveProcessFS(buff, to, removeMissingFromRAM, false)) { |
alindvall | 0:06e35dd73c95 | 349 | printf("Failed to remove files from %s that were missing on %s\n", to, from); |
alindvall | 0:06e35dd73c95 | 350 | } else { |
alindvall | 0:06e35dd73c95 | 351 | buff[0] = '\0'; |
alindvall | 0:06e35dd73c95 | 352 | if (!recursiveProcessFS(buff, from, addExistingToRAM, true)) { |
alindvall | 0:06e35dd73c95 | 353 | printf("Failed to add files to %s that existed on %s\n", to, from); |
alindvall | 0:06e35dd73c95 | 354 | } |
alindvall | 0:06e35dd73c95 | 355 | } |
alindvall | 0:06e35dd73c95 | 356 | } |
alindvall | 0:06e35dd73c95 | 357 | free(buff); |
alindvall | 0:06e35dd73c95 | 358 | } |
alindvall | 0:06e35dd73c95 | 359 | printf("Sync completed\n"); |
alindvall | 0:06e35dd73c95 | 360 | } |
alindvall | 0:06e35dd73c95 | 361 | |
alindvall | 0:06e35dd73c95 | 362 | |
alindvall | 0:06e35dd73c95 | 363 | static void createImageFile(QSPIFileSystem* qspi) |
alindvall | 0:06e35dd73c95 | 364 | { |
alindvall | 0:06e35dd73c95 | 365 | uint32_t startAddr; |
alindvall | 0:06e35dd73c95 | 366 | uint32_t endAddr; |
alindvall | 0:06e35dd73c95 | 367 | uint32_t size; |
alindvall | 0:06e35dd73c95 | 368 | |
alindvall | 0:06e35dd73c95 | 369 | printf("Creating image of existing (if any) QSPI file system\n"); |
alindvall | 0:06e35dd73c95 | 370 | |
alindvall | 0:06e35dd73c95 | 371 | if (!qspi->getMemoryBoundaries(&startAddr, &endAddr)) |
alindvall | 0:06e35dd73c95 | 372 | { |
alindvall | 0:06e35dd73c95 | 373 | handleError("QSPI FS not formatted or impossible to determine it's size\n"); |
alindvall | 0:06e35dd73c95 | 374 | } |
alindvall | 0:06e35dd73c95 | 375 | |
alindvall | 0:06e35dd73c95 | 376 | // Align the start address to an even multiple of 1MB |
alindvall | 0:06e35dd73c95 | 377 | startAddr = startAddr & 0xfff00000; |
alindvall | 0:06e35dd73c95 | 378 | |
alindvall | 0:06e35dd73c95 | 379 | // Update the file to match the size of the file system |
alindvall | 0:06e35dd73c95 | 380 | size = endAddr - startAddr; |
alindvall | 0:06e35dd73c95 | 381 | if ((size < 0x00100000) || (size > 0x01000000) || ((size & 0xfffff) > 0)) |
alindvall | 0:06e35dd73c95 | 382 | { |
alindvall | 0:06e35dd73c95 | 383 | sprintf(lsbuff, "QSPI FS size is not supported (%u bytes)\n", size); |
alindvall | 0:06e35dd73c95 | 384 | handleError(lsbuff); |
alindvall | 0:06e35dd73c95 | 385 | } |
alindvall | 0:06e35dd73c95 | 386 | sprintf(image_file_name, "/ram/.current/fs_image.fs%d", (size >> 20)); |
alindvall | 0:06e35dd73c95 | 387 | |
alindvall | 0:06e35dd73c95 | 388 | // NOTE: The line below is very very !!!! important. For some weird reason the |
alindvall | 0:06e35dd73c95 | 389 | // RAM file system must have at least one folder on it before USB is connected. |
alindvall | 0:06e35dd73c95 | 390 | // If the RAM file system doesn't have any folders on it the result is that |
alindvall | 0:06e35dd73c95 | 391 | // the content of the RAM file system will be the same after USB is disconnected |
alindvall | 0:06e35dd73c95 | 392 | // as it was before connecting - regardless of what is added. Very strange indeed. |
alindvall | 0:06e35dd73c95 | 393 | mkdir("/ram/.current", 0); |
alindvall | 0:06e35dd73c95 | 394 | |
alindvall | 0:06e35dd73c95 | 395 | printf("QSPI FS max size is %d MB\n", (size >> 20)); |
alindvall | 0:06e35dd73c95 | 396 | |
alindvall | 0:06e35dd73c95 | 397 | FILE *fp = fopen(image_file_name, "w"); |
alindvall | 0:06e35dd73c95 | 398 | if (fp != NULL) |
alindvall | 0:06e35dd73c95 | 399 | { |
alindvall | 0:06e35dd73c95 | 400 | while (size > 0) |
alindvall | 0:06e35dd73c95 | 401 | { |
alindvall | 0:06e35dd73c95 | 402 | uint32_t written = fwrite((char*)(endAddr - size), 1, size, fp); |
alindvall | 0:06e35dd73c95 | 403 | size -= written; |
alindvall | 0:06e35dd73c95 | 404 | if (written == 0) |
alindvall | 0:06e35dd73c95 | 405 | { |
alindvall | 0:06e35dd73c95 | 406 | handleError("Failed to create QSPI image file\n"); |
alindvall | 0:06e35dd73c95 | 407 | } |
alindvall | 0:06e35dd73c95 | 408 | } |
alindvall | 0:06e35dd73c95 | 409 | fclose(fp); |
alindvall | 0:06e35dd73c95 | 410 | } |
alindvall | 0:06e35dd73c95 | 411 | } |
alindvall | 0:06e35dd73c95 | 412 | |
alindvall | 0:06e35dd73c95 | 413 | static bool list(const char* name, bool isDir) |
alindvall | 0:06e35dd73c95 | 414 | { |
alindvall | 0:06e35dd73c95 | 415 | if (isDir) { |
alindvall | 0:06e35dd73c95 | 416 | printf("d: %s\n", name); |
alindvall | 0:06e35dd73c95 | 417 | } else { |
alindvall | 0:06e35dd73c95 | 418 | FILE* f = fopen(name, "r"); |
alindvall | 0:06e35dd73c95 | 419 | if (f != NULL) { |
alindvall | 0:06e35dd73c95 | 420 | uint32_t len = fileLen(f); |
alindvall | 0:06e35dd73c95 | 421 | printf("f: %7u %s\n", len, name); |
alindvall | 0:06e35dd73c95 | 422 | fclose(f); |
alindvall | 0:06e35dd73c95 | 423 | } else { |
alindvall | 0:06e35dd73c95 | 424 | printf("f: ??? %s\n", name); |
alindvall | 0:06e35dd73c95 | 425 | } |
alindvall | 0:06e35dd73c95 | 426 | } |
alindvall | 0:06e35dd73c95 | 427 | return true; |
alindvall | 0:06e35dd73c95 | 428 | } |
alindvall | 0:06e35dd73c95 | 429 | |
alindvall | 0:06e35dd73c95 | 430 | static void recursiveList(const char* dirname) |
alindvall | 0:06e35dd73c95 | 431 | { |
alindvall | 0:06e35dd73c95 | 432 | printf("\nRecursive list of file and folders in %s\n", dirname); |
alindvall | 0:06e35dd73c95 | 433 | char* buff = (char*)malloc(512); |
alindvall | 0:06e35dd73c95 | 434 | if (buff != NULL) |
alindvall | 0:06e35dd73c95 | 435 | { |
alindvall | 0:06e35dd73c95 | 436 | buff[0] = '\0'; |
alindvall | 0:06e35dd73c95 | 437 | recursiveProcessFS(buff, dirname, list, true); |
alindvall | 0:06e35dd73c95 | 438 | free(buff); |
alindvall | 0:06e35dd73c95 | 439 | } |
alindvall | 0:06e35dd73c95 | 440 | } |
alindvall | 0:06e35dd73c95 | 441 | |
alindvall | 0:06e35dd73c95 | 442 | static void showInfoScreen() |
alindvall | 0:06e35dd73c95 | 443 | { |
alindvall | 0:06e35dd73c95 | 444 | static SWIM_WINDOW_T* win = NULL; |
alindvall | 0:06e35dd73c95 | 445 | static void* fb = NULL; |
alindvall | 0:06e35dd73c95 | 446 | |
alindvall | 0:06e35dd73c95 | 447 | Display* disp = DMBoard::instance().display(); |
alindvall | 0:06e35dd73c95 | 448 | win = (SWIM_WINDOW_T*)malloc(sizeof(SWIM_WINDOW_T)); |
alindvall | 0:06e35dd73c95 | 449 | fb = disp->allocateFramebuffer(); |
alindvall | 0:06e35dd73c95 | 450 | |
alindvall | 0:06e35dd73c95 | 451 | swim_window_open(win, |
alindvall | 0:06e35dd73c95 | 452 | disp->width(), disp->height(), // full size |
alindvall | 0:06e35dd73c95 | 453 | (COLOR_T*)fb, |
alindvall | 0:06e35dd73c95 | 454 | 0,0,disp->width()-1, disp->height()-1, // window position and size |
alindvall | 0:06e35dd73c95 | 455 | 0, // border |
alindvall | 0:06e35dd73c95 | 456 | BLUE, WHITE, BLACK); // colors: pen, backgr, forgr |
alindvall | 0:06e35dd73c95 | 457 | |
alindvall | 0:06e35dd73c95 | 458 | swim_set_font(win, (FONT_T*)&font_winfreesys14x16); |
alindvall | 0:06e35dd73c95 | 459 | |
alindvall | 0:06e35dd73c95 | 460 | // Show a message |
alindvall | 0:06e35dd73c95 | 461 | swim_put_text_centered_win(win, "In this version all instructions are printed on the console!", disp->height()/2 - 20); |
alindvall | 0:06e35dd73c95 | 462 | swim_put_text_centered_win(win, "Connect a terminal application using 115200, 8N1.", disp->height()/2 + 20); |
alindvall | 0:06e35dd73c95 | 463 | |
alindvall | 0:06e35dd73c95 | 464 | // Start display in default mode (16-bit) |
alindvall | 0:06e35dd73c95 | 465 | Display::DisplayError disperr = disp->powerUp(fb); |
alindvall | 0:06e35dd73c95 | 466 | if (disperr != Display::DisplayError_Ok) { |
alindvall | 0:06e35dd73c95 | 467 | DMBoard::instance().logger()->printf("Failed to initialize the display, got error %d\r\n", disperr); |
alindvall | 0:06e35dd73c95 | 468 | wait_ms(2000); // allow RtosLog to flush messages |
alindvall | 0:06e35dd73c95 | 469 | mbed_die(); |
alindvall | 0:06e35dd73c95 | 470 | } |
alindvall | 0:06e35dd73c95 | 471 | } |
alindvall | 0:06e35dd73c95 | 472 | |
alindvall | 0:06e35dd73c95 | 473 | /****************************************************************************** |
alindvall | 0:06e35dd73c95 | 474 | * Main function |
alindvall | 0:06e35dd73c95 | 475 | *****************************************************************************/ |
alindvall | 0:06e35dd73c95 | 476 | int main() |
alindvall | 0:06e35dd73c95 | 477 | { |
alindvall | 0:06e35dd73c95 | 478 | DMBoard::BoardError err; |
alindvall | 0:06e35dd73c95 | 479 | DMBoard* board = &DMBoard::instance(); |
alindvall | 0:06e35dd73c95 | 480 | RtosLog* log = board->logger(); |
alindvall | 0:06e35dd73c95 | 481 | err = board->init(); |
alindvall | 0:06e35dd73c95 | 482 | if (err != DMBoard::Ok) { |
alindvall | 0:06e35dd73c95 | 483 | log->printf("Failed to initialize the board, got error %d\r\n", err); |
alindvall | 0:06e35dd73c95 | 484 | wait_ms(2000); // allow RtosLog to flush messages |
alindvall | 0:06e35dd73c95 | 485 | mbed_die(); |
alindvall | 0:06e35dd73c95 | 486 | } |
alindvall | 0:06e35dd73c95 | 487 | |
alindvall | 0:06e35dd73c95 | 488 | log->printf("\n\n---\nQSPI file syncer app\nBuilt: " __DATE__ " at " __TIME__ "\n\n"); |
alindvall | 0:06e35dd73c95 | 489 | |
alindvall | 0:06e35dd73c95 | 490 | showInfoScreen(); |
alindvall | 0:06e35dd73c95 | 491 | |
alindvall | 0:06e35dd73c95 | 492 | // allocate a chunk of memory in the external SDRAM to use as a RAM file system |
alindvall | 0:06e35dd73c95 | 493 | void* fsmem = malloc(RAM_FS_SIZE); |
alindvall | 0:06e35dd73c95 | 494 | if (fsmem == NULL) { |
alindvall | 0:06e35dd73c95 | 495 | log->printf("Failed to allocate memory for RAM file system\n"); |
alindvall | 0:06e35dd73c95 | 496 | mbed_die(); |
alindvall | 0:06e35dd73c95 | 497 | } |
alindvall | 0:06e35dd73c95 | 498 | |
alindvall | 0:06e35dd73c95 | 499 | // create a file system based on the allocated memory |
alindvall | 0:06e35dd73c95 | 500 | RAMFileSystem ramfs((uint32_t)fsmem, RAM_FS_SIZE, "ram"); |
alindvall | 0:06e35dd73c95 | 501 | USBMSD_RAMFS usbmsd(&ramfs); |
alindvall | 0:06e35dd73c95 | 502 | |
alindvall | 0:06e35dd73c95 | 503 | while(true) |
alindvall | 0:06e35dd73c95 | 504 | { |
alindvall | 0:06e35dd73c95 | 505 | // add an empty file system on it |
alindvall | 0:06e35dd73c95 | 506 | ramfs.format(); |
alindvall | 0:06e35dd73c95 | 507 | |
alindvall | 0:06e35dd73c95 | 508 | QSPIFileSystem* qspi = board->getQspiFS(); |
alindvall | 0:06e35dd73c95 | 509 | bool qspiFormatted = qspi->isformatted(); |
alindvall | 0:06e35dd73c95 | 510 | if (!qspiFormatted) |
alindvall | 0:06e35dd73c95 | 511 | { |
alindvall | 0:06e35dd73c95 | 512 | addNotFormattedFile(); |
alindvall | 0:06e35dd73c95 | 513 | } |
alindvall | 0:06e35dd73c95 | 514 | else |
alindvall | 0:06e35dd73c95 | 515 | { |
alindvall | 0:06e35dd73c95 | 516 | createImageFile(qspi); |
alindvall | 0:06e35dd73c95 | 517 | |
alindvall | 0:06e35dd73c95 | 518 | // Copy QSPI FS to RAM FS |
alindvall | 0:06e35dd73c95 | 519 | syncDir("/ram/", "/qspi/"); |
alindvall | 0:06e35dd73c95 | 520 | } |
alindvall | 0:06e35dd73c95 | 521 | |
alindvall | 0:06e35dd73c95 | 522 | printf("Insert the USB cable!\n"); |
alindvall | 0:06e35dd73c95 | 523 | printf("Starting USB...\n"); |
alindvall | 0:06e35dd73c95 | 524 | for (int i = 0; i < 10; i++) |
alindvall | 0:06e35dd73c95 | 525 | { |
alindvall | 0:06e35dd73c95 | 526 | if (usbmsd.connect()) |
alindvall | 0:06e35dd73c95 | 527 | { |
alindvall | 0:06e35dd73c95 | 528 | printf("Connected!\n"); |
alindvall | 0:06e35dd73c95 | 529 | break; |
alindvall | 0:06e35dd73c95 | 530 | } |
alindvall | 0:06e35dd73c95 | 531 | printf("Failed to connect USB, testing again...\n"); |
alindvall | 0:06e35dd73c95 | 532 | printf("Insert (or remove and then insert) the USB cable!\n"); |
alindvall | 0:06e35dd73c95 | 533 | wait(1); |
alindvall | 0:06e35dd73c95 | 534 | } |
alindvall | 0:06e35dd73c95 | 535 | |
alindvall | 0:06e35dd73c95 | 536 | // 6b) |
alindvall | 0:06e35dd73c95 | 537 | if (qspiFormatted) |
alindvall | 0:06e35dd73c95 | 538 | { |
alindvall | 0:06e35dd73c95 | 539 | waitForButtonPress(); |
alindvall | 0:06e35dd73c95 | 540 | } |
alindvall | 0:06e35dd73c95 | 541 | else |
alindvall | 0:06e35dd73c95 | 542 | { |
alindvall | 0:06e35dd73c95 | 543 | // 6a) no point in waiting for buttons if no file system |
alindvall | 0:06e35dd73c95 | 544 | while (1) {}; |
alindvall | 0:06e35dd73c95 | 545 | } |
alindvall | 0:06e35dd73c95 | 546 | |
alindvall | 0:06e35dd73c95 | 547 | // 7) |
alindvall | 0:06e35dd73c95 | 548 | usbmsd.disconnect(); |
alindvall | 0:06e35dd73c95 | 549 | printf("Disconnected!\n"); |
alindvall | 0:06e35dd73c95 | 550 | |
alindvall | 0:06e35dd73c95 | 551 | // 8) Copy RAM FS to QSPI FS |
alindvall | 0:06e35dd73c95 | 552 | recursiveList("/ram/"); |
alindvall | 0:06e35dd73c95 | 553 | syncDir("/qspi/", "/ram/"); |
alindvall | 0:06e35dd73c95 | 554 | } |
alindvall | 0:06e35dd73c95 | 555 | } |
alindvall | 0:06e35dd73c95 | 556 |