A board support package for the LPC4088 Display Module.
Dependencies: DM_HttpServer DM_USBHost
Dependents: lpc4088_displaymodule_emwin lpc4088_displaymodule_demo_sphere sampleGUI sampleEmptyGUI ... more
Fork of DMSupport by
QSPIFileSystem.cpp
00001 /* 00002 * Copyright 2014 Embedded Artists AB 00003 * 00004 * Licensed under the Apache License, Version 2.0 (the "License"); 00005 * you may not use this file except in compliance with the License. 00006 * You may obtain a copy of the License at 00007 * 00008 * http://www.apache.org/licenses/LICENSE-2.0 00009 * 00010 * Unless required by applicable law or agreed to in writing, software 00011 * distributed under the License is distributed on an "AS IS" BASIS, 00012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00013 * See the License for the specific language governing permissions and 00014 * limitations under the License. 00015 */ 00016 00017 #include "QSPIFileSystem.h" 00018 #include "mbed_debug.h" 00019 00020 #include "SPIFI.h" 00021 00022 /****************************************************************************** 00023 * Defines and typedefs 00024 *****************************************************************************/ 00025 00026 #define QSPI_DBG 0 00027 00028 #define IS_ADDR_IN_SPIFI(__addr) ( (((uint32_t)(__addr)) & 0xff000000) == SPIFI_MEM_BASE ) 00029 00030 #define MEM_SIZE (memInfo.memSize) 00031 #define ERASE_SIZE (memInfo.eraseBlockSize) 00032 #define NUM_BLOCKS ((int)(memInfo.numEraseBlocks)) 00033 00034 typedef uint32_t toc_entry_t; 00035 00036 #define TOC_BLOCK_ADDR (memInfo.tocBlockAddr) //(SPIFI_MEM_BASE + (NUM_BLOCKS - 1)*ERASE_SIZE) 00037 #define TOC_SIZE (memInfo.tocSizeInBytes) //(sizeof(toc_entry_t) * NUM_BLOCKS) 00038 #define NUM_TOCS (memInfo.numTocs) //((int)(ERASE_SIZE/TOC_SIZE)) 00039 #define NUM_TOC_BLOCKS ((NUM_TOCS * TOC_SIZE) / ERASE_SIZE) 00040 #define NUM_TOC_ENTRIES ((int)(TOC_SIZE/sizeof(toc_entry_t))) 00041 00042 #define TOC_UNUSED (0xffffffff) 00043 #define TOC_MAX (NUM_BLOCKS - 1) 00044 #define TOC_VALID_MASK (1UL<<31) 00045 #define TOC_RESERVED_MASK (1UL<<30) 00046 #define TOC_USED_MASK (1UL<<29) 00047 #define TOC_FILE_MASK (1UL<<28) 00048 #define TOC_FSIZE_MASK (0x3ffff) 00049 #define TOC_MANDAT_SET_MASK (0x0ffc0000) 00050 00051 #define MANDATORY_BITS_SET(__v) (((__v)&TOC_MANDAT_SET_MASK) == TOC_MANDAT_SET_MASK) 00052 00053 #define VALID_TOC_ENTRY(__v) (((__v)&TOC_VALID_MASK) == 0) 00054 #define USED_TOC_ENTRY(__v) (VALID_TOC_ENTRY(__v) && (((__v)&TOC_USED_MASK) == 0)) 00055 #define TOC_IS_FILE(__v) (USED_TOC_ENTRY(__v) && (((__v)&TOC_FILE_MASK) == 0)) 00056 #define TOC_IS_RESERVED(__v) (VALID_TOC_ENTRY(__v) && (((__v)&TOC_RESERVED_MASK) == 0)) 00057 #define FILESIZE(__v) ((__v) & 0x3ffff) 00058 00059 #define FS_MIN(__a, __b) (((__a) < (__b)) ? (__a) : (__b)) 00060 00061 // Mask to compare the different access modes. In LPCXpresso this was defined 00062 // but not in uVision 00063 #ifndef O_ACCMODE 00064 #define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR) 00065 #endif 00066 00067 00068 /* 00069 * The file header currently only consists of the filename (including path) 00070 * and the string terminating character, but by separating the file name 00071 * length from the size of the header in the code it allows future additions 00072 * to the header without too much code modification. 00073 */ 00074 #define HEADER_DNAME_MAXLEN (250) 00075 #define HEADER_FNAME_STRLEN (HEADER_DNAME_MAXLEN + 5) 00076 #define HEADER_FNAME_LEN (HEADER_FNAME_STRLEN + 1) 00077 #define HEADER_LEN (HEADER_FNAME_LEN) // only filename in header for now 00078 00079 typedef enum 00080 { 00081 FS_OK, 00082 FS_ERR_NOT_FORMATTED, 00083 FS_ERR_NO_FILE, 00084 FS_ERR_FILE_EXIST, 00085 FS_ERR_INVALID_PARAM, 00086 FS_ERR_DISK_FULL, 00087 FS_ERR_SPIFI, 00088 FS_ERR_MALLOC, 00089 00090 // FS_ERR_SPIFI_* return codes are listed in the User's Manual 00091 // as possible return values from spifi_init(), spifi_program() 00092 // and spifi_erase() calls. 00093 FS_ERR_SPIFI_INTERNAL_ERROR = 0x20002, // 0x20002, Internal error in API code 00094 FS_ERR_SPIFI_TIMEOUT = 0x20003, // 0x20003, Time-out waiting for program or erase to begin: protection could not be removed. 00095 FS_ERR_SPIFI_OPERAND = 0x20004, // 0x20004, Operand error (i.e. invalid params) 00096 FS_ERR_SPIFI_STATUS = 0x20005, // 0x20005, Device status error 00097 FS_ERR_SPIFI_EXT_DEVICE_ID = 0x20006, // 0x20006, Unknown extended device ID value 00098 FS_ERR_SPIFI_DEVICE_ID = 0x20007, // 0x20007, Unknown device ID code 00099 FS_ERR_SPIFI_DEVICE_TYPE = 0x20008, // 0x20008, Unknown device type code 00100 FS_ERR_SPIFI_MANUFACTURER = 0x20009, // 0x20009, Unknown manufacturer code 00101 FS_ERR_SPIFI_INVALID_JDEC_ID = 0x2000A, // 0x2000A, No operative serial flash (JEDEC ID all zeroes or all ones) 00102 FS_ERR_SPIFI_ERASE_CONFLICT = 0x2000B, // 0x2000B, S_CALLER_ERASE is included in options, and erasure is required. 00103 FS_ERR_SPIFI_VERIFICATION, // other, Other non-zero values can occur if options selects verification. 00104 // They will be the address in the SPIFI memory area at which the first discrepancy was found. 00105 } fresult; 00106 00107 // The number of times to re-attempt a spifi_program() or spifi_erase() 00108 // if the last one reported a verification error. 00109 #define NUM_VERIFICATION_ATTEMPTS (1) 00110 00111 typedef struct 00112 { 00113 uint32_t memSize; 00114 uint32_t eraseBlockSize; 00115 uint32_t numEraseBlocks; 00116 uint32_t tocBlockAddr; 00117 uint32_t numTocs; 00118 uint32_t tocSizeInBytes; 00119 char memName[30]; 00120 } meminfo_t; 00121 00122 typedef struct 00123 { 00124 int tocIdx; 00125 uint32_t size; 00126 uint16_t lastBlock; 00127 } fileHandle_t; 00128 00129 /****************************************************************************** 00130 * Local variables 00131 *****************************************************************************/ 00132 00133 static toc_entry_t* TOC = NULL; 00134 static int activeTOC = -1; 00135 00136 static const SPIFI_RTNS *spifi = NULL; 00137 static SPIFIobj* obj; 00138 static SPIFIopers opers; 00139 00140 static char addr_conflict_buff[PROG_SIZE]; 00141 00142 static meminfo_t memInfo = {0,0,0,0,0,0,{0}}; 00143 00144 /****************************************************************************** 00145 * Forward Declarations of Local Functions 00146 *****************************************************************************/ 00147 static fresult qspifs_init(); 00148 static fresult qspifs_translateSpifiError(int rc); 00149 static fresult qspifs_readTOC(void); 00150 static fresult qspifs_saveTOC(void); 00151 static fresult qspifs_findFile(const char* filename, fileHandle_t* fh); 00152 static fresult qspifs_fileSize(int tocIdx, uint32_t* pSize); 00153 static fresult qspifs_eraseBlock(int block); 00154 static fresult qspifs_allocateFile(const char* filename, int neededBlocks, int* pTocIdx); 00155 static void qspifs_deleteFile(fileHandle_t* fh); 00156 static fresult qspifs_allocateSpace(fileHandle_t* fh, uint32_t size); 00157 static fresult qspifs_format(unsigned int minReservedBytes); 00158 static fresult qspifs_write(fileHandle_t* fh, const uint8_t * const pData, uint32_t size); 00159 static bool qspifs_startsWith(const char* prefix, const char* str); 00160 00161 /****************************************************************************** 00162 * Local Functions 00163 *****************************************************************************/ 00164 00165 /****************************************************************************** 00166 * 00167 * Description: 00168 * Initializes spifi, identifies the chip and reads the file system's 00169 * table of content. 00170 * 00171 * Params: 00172 * None 00173 * 00174 * Returns: 00175 * FS_OK or one of the FS_ERR_* error codes 00176 * 00177 *****************************************************************************/ 00178 static fresult qspifs_init() 00179 { 00180 if (spifi == NULL) { 00181 SPIFI::SpifiError err; 00182 err = SPIFI::instance().init(); 00183 if (err != SPIFI::Ok) { 00184 spifi = NULL; 00185 return FS_ERR_SPIFI; 00186 } 00187 00188 SPIFI::instance().internalData(&obj, &spifi); 00189 00190 /* Make sure it is a tested flash module */ 00191 switch (SPIFI::instance().device()) { 00192 case SPIFI::Spansion_S25FL032: 00193 strcpy(memInfo.memName, "Spansion S25FL032"); 00194 break; 00195 00196 case SPIFI::Winbond_W25Q64FV: 00197 strcpy(memInfo.memName, "Winbond W25Q64FV"); 00198 break; 00199 00200 case SPIFI::Macronix_MX25L6435E: 00201 strcpy(memInfo.memName, "Macronix_MX25L6435E"); 00202 break; 00203 00204 case SPIFI::Macronix_MX25L12835F: 00205 strcpy(memInfo.memName, "Macronix_MX25L12835F"); 00206 break; 00207 00208 case SPIFI::SpecifiedInBios: 00209 strcpy(memInfo.memName, "Specified in BIOS"); 00210 break; 00211 00212 case SPIFI::UnknownDevice: 00213 default: 00214 debug("INIT: Memory is unknown and may not work as expected\n"); 00215 00216 strcpy(memInfo.memName, "Unknown - check ID"); 00217 00218 /* 00219 * If this happens, check the manufacturer and device information 00220 * and compare with the data sheet for your chip. Also make sure 00221 * that the sector sizes are the same (i.e. 64KB) for your chip. 00222 * If everything is the same then add an exception for your chip. 00223 */ 00224 break; 00225 } 00226 00227 /* 00228 * The size of the erase blocks gives the number of erase blocks on the 00229 * flash which in turn dictates the size of each TOC. For a flash with 00230 * a large erase block size (Spansion_S25FL032 has 64Kb blocks) the 00231 * the number of blocks is low, resulting in a small TOC with many fitting 00232 * inside a single erase block. 00233 * For a large flash with small erase block size (Macronix_MX25L12835F is 00234 * 16Mb with 4Kb erase blocks) the number of blocks is high, resulting in 00235 * a large TOC which doesn't even fit in one erase block. 00236 * 00237 * 4Mb, 64Kb erase block => TOC size 256b => 256 TOC/erase block 00238 * 8Mb, 4Kb erase block => TOC size 8192b => 0.5 TOC/erase block 00239 * 16Mb, 4Kb erase block => TOC size 16384b => 0.25 TOC/erase block 00240 * 00241 * In all cases we select a number of TOCs so that we use 64Kb of the 00242 * memory for them. This will reduce wear on the flash as the TOC can 00243 * be moved around. 00244 */ 00245 memInfo.memSize = obj->memSize; 00246 memInfo.eraseBlockSize = SPIFI::instance().eraseBlockSize(); 00247 memInfo.numEraseBlocks = memInfo.memSize / memInfo.eraseBlockSize; 00248 memInfo.tocSizeInBytes = sizeof(toc_entry_t) * memInfo.numEraseBlocks; 00249 memInfo.numTocs = (64*1024) / memInfo.tocSizeInBytes; 00250 if (memInfo.numTocs < 2) { 00251 // just in case a unknown size combination appears 00252 memInfo.numTocs = 2; 00253 } 00254 memInfo.tocBlockAddr = SPIFI_MEM_BASE + (NUM_BLOCKS * ERASE_SIZE) - (memInfo.numTocs * memInfo.tocSizeInBytes); 00255 00256 debug_if(QSPI_DBG, "INIT: Found %dMB %s\n", memInfo.memSize/0x100000, memInfo.memName); 00257 00258 if (TOC != NULL) { 00259 delete TOC; 00260 } 00261 TOC = (toc_entry_t*)malloc(TOC_SIZE); 00262 if (TOC == NULL) { 00263 debug_if(QSPI_DBG, "INIT: Failed to allocate memory for TOC\n"); 00264 spifi = NULL; 00265 return FS_ERR_MALLOC; 00266 } 00267 } 00268 if (activeTOC == -1) 00269 { 00270 return qspifs_readTOC(); 00271 } 00272 return FS_OK; 00273 } 00274 00275 /****************************************************************************** 00276 * 00277 * Description: 00278 * Converts the return value from one of the spifi_init(), spifi_program() 00279 * or spifi_erase() calls into a FS_* error code to simplify it for the 00280 * fs_qspi API user. 00281 * This function also attempts to detect the verification failure error. 00282 * When a verification error occurs the spifi_* functions returns the 00283 * conflicting address and not an error code. As this can be any address 00284 * it is difficult to test but this function converts it into the 00285 * FS_ERR_SPIFI_VERIFICATION error code which can be tested against. 00286 * 00287 * Params: 00288 * [in] rc - The return code from any of the spifi_* functions 00289 * 00290 * Returns: 00291 * FS_OK or one of the FS_ERR_* error codes 00292 * 00293 *****************************************************************************/ 00294 static fresult qspifs_translateSpifiError(int rc) 00295 { 00296 fresult res; 00297 if (rc == 0) 00298 { 00299 res = FS_OK; 00300 } 00301 else if ((rc >= FS_ERR_SPIFI_INTERNAL_ERROR) && (rc <= FS_ERR_SPIFI_ERASE_CONFLICT)) 00302 { 00303 // This is a known error code 00304 res = (fresult)rc; 00305 } 00306 else if (opers.options & (S_VERIFY_PROG | S_VERIFY_ERASE)) 00307 { 00308 // As verification was selected and rc is not in the list of known 00309 // codes this falls into this category in the User's Manual: 00310 // 00311 // "Other non-zero values can occur if options selects verification. 00312 // They will be the address in the SPIFI memory area at which the 00313 // first discrepancy was found." 00314 res = FS_ERR_SPIFI_VERIFICATION; 00315 } 00316 else 00317 { 00318 // Should never happen :-) as all listed error codes are covered but 00319 // to be on the safe side and not interpret this as a success, a generic 00320 // error is set. 00321 res = FS_ERR_SPIFI; 00322 } 00323 return res; 00324 } 00325 00326 /****************************************************************************** 00327 * 00328 * Description: 00329 * Reads the table of contents (TOC). The TOC is stored in the last erase 00330 * block on the QSPI flash. As the QSPI flash is not exactly RW (might 00331 * require erasing before writing) the TOC is relocated inside the erase 00332 * block everytime it is saved (see saveTOC()). The currently valid TOC 00333 * is allways the last one stored. 00334 * 00335 * Params: 00336 * None 00337 * 00338 * Returns: 00339 * FS_OK or one of the FS_ERR_* error codes 00340 * 00341 *****************************************************************************/ 00342 static fresult qspifs_readTOC(void) 00343 { 00344 int i, j; 00345 toc_entry_t* p; 00346 uint8_t invalid = 0; 00347 int lastValid = -1; 00348 00349 // Search for the first unused TOC, keeping track of the valid 00350 // ones as we go. 00351 for (i = 0; (i < (int)NUM_TOCS) && !invalid; i++) 00352 { 00353 p = (toc_entry_t*)(TOC_BLOCK_ADDR + i*TOC_SIZE); 00354 for (j = 0; j < NUM_BLOCKS; j++) 00355 { 00356 if (!VALID_TOC_ENTRY(*p) || !MANDATORY_BITS_SET(*p)) 00357 { 00358 // invalid TOC entry, stop looking 00359 invalid = 1; 00360 break; 00361 } 00362 p++; 00363 } 00364 00365 if (!invalid) 00366 { 00367 // this TOC was ok, but perhaps there is a newer one? 00368 lastValid = i; 00369 } 00370 } 00371 00372 if (lastValid == -1) 00373 { 00374 // no valid TOCs on the flash 00375 return FS_ERR_NOT_FORMATTED; 00376 } 00377 else 00378 { 00379 // previous entry was ok so use that 00380 activeTOC = lastValid; 00381 p = (toc_entry_t*)(TOC_BLOCK_ADDR + activeTOC*TOC_SIZE); 00382 memcpy(TOC, p, TOC_SIZE); 00383 return FS_OK; 00384 } 00385 } 00386 00387 /****************************************************************************** 00388 * 00389 * Description: 00390 * Saves the table of contents (TOC). The TOC is stored in the last erase 00391 * block on the QSPI flash. As the QSPI flash is not exactly RW (might 00392 * require erasing before writing) the TOC is first compared with what is 00393 * stored in the QSPI flash and if there are no changes or all changes 00394 * only require bit changes 1->0 then the current TOC can be overwritten. 00395 * If bit value changes 0->1 are required then the current stored TOC 00396 * cannot be overwritten and the new TOC is instead stored in the next 00397 * available space. If the entire last block is filled then it is erased 00398 * and the new TOC is placed at the start of it. 00399 * 00400 * Params: 00401 * None 00402 * 00403 * Returns: 00404 * FS_OK or one of the FS_ERR_* error codes 00405 * 00406 *****************************************************************************/ 00407 static fresult qspifs_saveTOC(void) 00408 { 00409 int i, rc = 0; 00410 uint32_t* pSrc; 00411 uint32_t* pDest; 00412 uint32_t tmp; 00413 uint8_t identical = 1; 00414 00415 // active TOC same as the one we want to save? 00416 pSrc = (uint32_t*)TOC; 00417 pDest = (uint32_t*)(TOC_BLOCK_ADDR + activeTOC*TOC_SIZE); 00418 for (i = 0; i < NUM_TOC_ENTRIES; i++) 00419 { 00420 if (*pSrc != *pDest) 00421 { 00422 identical = 0; 00423 tmp = ((*pDest) ^ (*pSrc)) & (*pSrc); 00424 if (tmp > 0) 00425 { 00426 // found a change that contains 0->1 bit modification which 00427 // requires erasing or a new location 00428 activeTOC = (activeTOC + 1)%NUM_TOCS; 00429 if (activeTOC == 0) 00430 { 00431 // no more free TOCs so an erase is needed 00432 #if 0 00433 opers.options &= ~S_CALLER_ERASE; 00434 opers.options |= S_FORCE_ERASE; 00435 #else 00436 opers.dest = (char *) TOC_BLOCK_ADDR; 00437 opers.length = TOC_SIZE * NUM_TOCS; 00438 opers.scratch = NULL; 00439 opers.protect = 0; 00440 opers.options = S_NO_VERIFY; 00441 rc = spifi->spifi_erase(obj, &opers); 00442 if (rc) { 00443 return qspifs_translateSpifiError(rc); 00444 } 00445 #endif 00446 } 00447 break; 00448 } 00449 } 00450 pSrc++; 00451 pDest++; 00452 } 00453 00454 if (!identical) 00455 { 00456 opers.length = FS_MIN(TOC_SIZE, PROG_SIZE); 00457 opers.scratch = NULL; 00458 opers.protect = 0; 00459 opers.options = S_VERIFY_PROG | S_CALLER_ERASE; 00460 for (uint32_t i = 0; i < (TOC_SIZE / PROG_SIZE); i++) 00461 { 00462 opers.dest = (char *)(TOC_BLOCK_ADDR + activeTOC*TOC_SIZE + i*PROG_SIZE); 00463 rc = spifi->spifi_program(obj, ((char*)TOC)+i*PROG_SIZE, &opers); 00464 if (rc) 00465 { 00466 break; 00467 } 00468 } 00469 return qspifs_translateSpifiError(rc); 00470 } 00471 return FS_OK; 00472 } 00473 00474 /****************************************************************************** 00475 * 00476 * Description: 00477 * Searches the file system for a file with the specified name and 00478 * (if found) returns the file's position in the TOC. 00479 * 00480 * Note that the content of fh is only valid if FS_OK is returned. 00481 * 00482 * Params: 00483 * [in] filename - The name of the file to find 00484 * [out] fh - The handle with the file information 00485 * 00486 * Returns: 00487 * FS_OK or one of the FS_ERR_* error codes 00488 * 00489 *****************************************************************************/ 00490 static fresult qspifs_findFile(const char* filename, fileHandle_t* fh) 00491 { 00492 int i; 00493 00494 if (activeTOC == -1) 00495 { 00496 return FS_ERR_NOT_FORMATTED; 00497 } 00498 00499 // Look at all blocks except for the reserved ones 00500 for (i = 0; i < NUM_BLOCKS; i++) 00501 { 00502 if (TOC_IS_FILE(TOC[i]) && !TOC_IS_RESERVED(TOC[i])) 00503 { 00504 // found a file, see if name matches 00505 char* p = (char*)(SPIFI_MEM_BASE + i*ERASE_SIZE); 00506 if (strncmp(filename, p, HEADER_FNAME_LEN) == 0) 00507 { 00508 // found a matching name 00509 fh->tocIdx = i; 00510 fresult res = qspifs_fileSize(fh->tocIdx, &fh->size); 00511 if (res == FS_OK) { 00512 fh->lastBlock = fh->tocIdx + ((fh->size + HEADER_LEN)/ ERASE_SIZE); 00513 } 00514 return FS_OK; 00515 } 00516 } 00517 } 00518 return FS_ERR_NO_FILE; 00519 } 00520 00521 /****************************************************************************** 00522 * 00523 * Description: 00524 * Calculates and returns the file's size. 00525 * 00526 * Note that the content of pSize is only valid if FS_OK is returned. 00527 * 00528 * Params: 00529 * [in] tocIdx - The file's position in the TOC 00530 * [out] pSize - The file's size 00531 * 00532 * Returns: 00533 * FS_OK or one of the FS_ERR_* error codes 00534 * 00535 *****************************************************************************/ 00536 static fresult qspifs_fileSize(int tocIdx, uint32_t* pSize) 00537 { 00538 int i; 00539 00540 if (tocIdx < 0 || tocIdx > NUM_BLOCKS || !TOC_IS_FILE(TOC[tocIdx])) 00541 { 00542 return FS_ERR_NO_FILE; 00543 } 00544 00545 *pSize = 0; 00546 00547 // A file is always stored in sequential blocks so start with the files 00548 // first block and as long as it is full continue sum up the occupied 00549 // block sizes. As soon as a non-full block is found that must be the 00550 // file's last block. 00551 for (i = tocIdx; i < NUM_BLOCKS; i++) 00552 { 00553 *pSize += FILESIZE(TOC[i]); 00554 if (FILESIZE(TOC[i]) < ERASE_SIZE) 00555 { 00556 // last block in chain 00557 break; 00558 } 00559 } 00560 00561 // Remove the filename header from the file's size 00562 *pSize -= HEADER_LEN; 00563 00564 return FS_OK; 00565 } 00566 00567 /****************************************************************************** 00568 * 00569 * Description: 00570 * Erases everything in one block on the QSPI flash. 00571 * 00572 * Params: 00573 * [in] block - The block's number 00574 * 00575 * Returns: 00576 * FS_OK or one of the FS_ERR_* error codes 00577 * 00578 *****************************************************************************/ 00579 static fresult qspifs_eraseBlock(int block) 00580 { 00581 opers.dest = (char *)(block * ERASE_SIZE); 00582 opers.length = ERASE_SIZE; 00583 opers.scratch = NULL; 00584 opers.protect = 0; 00585 opers.options = S_NO_VERIFY; 00586 return qspifs_translateSpifiError(spifi->spifi_erase (obj, &opers)); 00587 } 00588 00589 /****************************************************************************** 00590 * 00591 * Description: 00592 * Creates a new file if there is enough space for it on the file system. 00593 * The TOC is searched for a unused sequence of blocks of at least the 00594 * needed size. That block is marked as used and the file's name is stored 00595 * in the first bytes of the file's first block. 00596 * 00597 * Note: The filename will not be tested for uniqueness. 00598 * Note: The value of pTocIdx will only be valid if FS_OK is returned. 00599 * 00600 * Params: 00601 * [in] filename - The name of the new file 00602 * [in] neededBlocks - The number of blocks (in sequence) to allocate 00603 * [out] pTocIdx - The new file's position in the TOC 00604 * 00605 * Returns: 00606 * FS_OK or one of the FS_ERR_* error codes 00607 * 00608 *****************************************************************************/ 00609 static fresult qspifs_allocateFile(const char* filename, int neededBlocks, int* pTocIdx) 00610 { 00611 int i, rc; 00612 00613 if (activeTOC == -1) 00614 { 00615 return FS_ERR_NOT_FORMATTED; 00616 } 00617 00618 // Look at all blocks except for the reserved ones 00619 for (i = 0; i < NUM_BLOCKS; i++) 00620 { 00621 //TODO: Improve search to use gaps to avoid having to move files 00622 // that are written to 00623 if (!USED_TOC_ENTRY(TOC[i]) && !TOC_IS_RESERVED(TOC[i])) 00624 { 00625 int j; 00626 for (j = 1; j < neededBlocks; j++) 00627 { 00628 if (USED_TOC_ENTRY(TOC[i+j]) || TOC_IS_RESERVED(TOC[i+j])) 00629 { 00630 // not enough free blocks in sequence, skip past these 00631 // tested entries and continue searching 00632 i += j; 00633 break; 00634 } 00635 } 00636 00637 if (j == neededBlocks) 00638 { 00639 const char* pSrc = filename; 00640 if (IS_ADDR_IN_SPIFI(filename)) 00641 { 00642 // The SPIFI ROM driver cannot write data from SPIFI into 00643 // SPIFI (i.e. cannot read and write at the same time). 00644 // The workaround is to copy the source data into a buffer 00645 // in local memory and use that as source for the write 00646 // instead. 00647 memcpy(addr_conflict_buff, filename, strlen(filename)+1); 00648 pSrc = addr_conflict_buff; 00649 } 00650 00651 // Erase the new file's first block and store the filename at the 00652 // start of it 00653 opers.length = strlen(pSrc)+1; 00654 opers.scratch = NULL; 00655 opers.protect = 0; 00656 opers.options = S_VERIFY_PROG | S_FORCE_ERASE;// S_CALLER_ERASE; 00657 opers.dest = (char *)(i*ERASE_SIZE); 00658 rc = spifi->spifi_program(obj, (char*)pSrc, &opers); 00659 if (rc) { 00660 return qspifs_translateSpifiError(rc); 00661 } 00662 00663 TOC[i] &= ~(TOC_VALID_MASK | TOC_USED_MASK | TOC_FILE_MASK | TOC_FSIZE_MASK); 00664 TOC[i] |= HEADER_LEN; 00665 00666 *pTocIdx = i; 00667 return FS_OK; 00668 } 00669 } 00670 } 00671 return FS_ERR_DISK_FULL; 00672 } 00673 00674 /****************************************************************************** 00675 * 00676 * Description: 00677 * Deletes the specified file by marking all its blocks as unused in 00678 * the TOC. 00679 * 00680 * Note: The deleted blocks are not erased here - that is done when they 00681 * are allocated the next time. 00682 * 00683 * Params: 00684 * [in] fh - The file handle with information about what to delete 00685 * 00686 * Returns: 00687 * None 00688 * 00689 *****************************************************************************/ 00690 static void qspifs_deleteFile(fileHandle_t* fh) 00691 { 00692 int i; 00693 00694 for (i = fh->lastBlock; i >= fh->tocIdx; i--) 00695 { 00696 TOC[i] = ~TOC_VALID_MASK; 00697 } 00698 } 00699 00700 /****************************************************************************** 00701 * 00702 * Description: 00703 * Ensures that the specified file can grow to the wanted size. 00704 * If the file size will increase enough to need one or more new blocks 00705 * and there isn't enough space then an attempt is made to move the 00706 * current file to a large enough space somewhere else. 00707 * 00708 * If there are more free block(s) at the end of the file then it is not 00709 * moved and instead those blocks are marked as used. 00710 * 00711 * Note: The filename will not be tested for uniqueness. 00712 * Note: The value of pTocIdx will only be valid if FS_OK is returned. 00713 * 00714 * Params: 00715 * [in/out] fh - The current file handle, might be updated after a move 00716 * [in] size - The wanted new size 00717 * 00718 * Returns: 00719 * FS_OK or one of the FS_ERR_* error codes 00720 * 00721 *****************************************************************************/ 00722 static fresult qspifs_allocateSpace(fileHandle_t* fh, uint32_t size) 00723 { 00724 uint16_t oldNumBlocks = (fh->size + HEADER_LEN) / ERASE_SIZE; 00725 uint16_t newNumBlocks = (fh->size + HEADER_LEN + size) / ERASE_SIZE; 00726 uint16_t numNeeded = newNumBlocks - oldNumBlocks; 00727 fresult res = FS_OK; 00728 00729 if (numNeeded > 0) 00730 { 00731 uint16_t i; 00732 for (i = 0; i < numNeeded; i++) 00733 { 00734 if (USED_TOC_ENTRY(TOC[fh->tocIdx + oldNumBlocks + 1 + i]) || 00735 TOC_IS_RESERVED(TOC[fh->tocIdx + oldNumBlocks + 1 + i])) 00736 { 00737 fileHandle_t fhNew; 00738 00739 // have to move the chain 00740 char* filename = (char*)(SPIFI_MEM_BASE + fh->tocIdx * ERASE_SIZE); 00741 res = qspifs_allocateFile(filename, newNumBlocks, &(fhNew.tocIdx)); 00742 if (res == FS_OK) 00743 { 00744 // copy data 00745 fhNew.lastBlock = fhNew.tocIdx; 00746 fhNew.size = 0; 00747 res = qspifs_write(&fhNew, (uint8_t*)(SPIFI_MEM_BASE + fh->tocIdx * ERASE_SIZE + HEADER_LEN), fh->size); 00748 } 00749 if (res == FS_OK) 00750 { 00751 // remove old entries 00752 qspifs_deleteFile(fh); 00753 00754 // modify old handle to point to new information 00755 fh->lastBlock = fhNew.lastBlock; 00756 fh->size = fhNew.size; 00757 fh->tocIdx = fhNew.tocIdx; 00758 } 00759 if (res != FS_OK) 00760 { 00761 // not possible to relocate the file => abort 00762 return res; 00763 } 00764 break; 00765 } 00766 } 00767 00768 // have space that is unused, so mark as used 00769 for (i = 0; i < numNeeded; i++) 00770 { 00771 int tocIdx = fh->tocIdx + oldNumBlocks + 1 + i; 00772 TOC[tocIdx] &= ~TOC_USED_MASK; 00773 qspifs_eraseBlock(tocIdx); 00774 } 00775 } 00776 00777 return res; 00778 } 00779 00780 /****************************************************************************** 00781 * 00782 * Description: 00783 * Adds a file system to the QSPI flash. The entire flash will be erase 00784 * except for the minReservedBytes first bytes. That reserved area (rounded 00785 * up to the closest even multiple of the erase block size) can be used 00786 * for anything and will never be touched by the file system. That area is 00787 * typically used for executing programs from when the internal flash is 00788 * full. 00789 * 00790 * The file system will have a table of content (TOC) placed at the start 00791 * of the last erase block on the flash. 00792 * 00793 * Params: 00794 * [in] minReservedBytes - The number of bytes to ignore at the start of 00795 * the flash. 00796 * 00797 * Returns: 00798 * FS_OK on success or one of the FS_ERR_* on failure 00799 * 00800 *****************************************************************************/ 00801 static fresult qspifs_format(unsigned int minReservedBytes) 00802 { 00803 int rc; 00804 uint32_t i, numReserved = 0; 00805 00806 if (minReservedBytes > 0) { 00807 numReserved = (minReservedBytes + ERASE_SIZE - 1) / ERASE_SIZE; 00808 if (numReserved >= (uint32_t)(NUM_BLOCKS - 2)) { 00809 // Too many of the erase blocks are reserved - not even room for one file 00810 return FS_ERR_INVALID_PARAM; 00811 } 00812 } 00813 00814 #if 0 // works but is really slow 00815 // Erase all non-reserved blocks 00816 for (i = numReserved; i < NUM_BLOCKS; i++) { 00817 opers.dest = (char *) (i * ERASE_SIZE); 00818 opers.length = ERASE_SIZE; 00819 opers.scratch = NULL; 00820 opers.protect = 0; 00821 opers.options = S_NO_VERIFY; 00822 rc = spifi->spifi_erase(&obj, &opers); 00823 if (rc) { 00824 return qspifs_translateSpifiError(rc); 00825 } 00826 } 00827 #else 00828 // Erase all non-reserved blocks 00829 opers.dest = (char *) (numReserved * ERASE_SIZE); 00830 opers.length = MEM_SIZE - (numReserved * ERASE_SIZE); 00831 opers.scratch = NULL; 00832 opers.protect = 0; 00833 opers.options = S_NO_VERIFY; 00834 rc = spifi->spifi_erase(obj, &opers); 00835 if (rc) { 00836 return qspifs_translateSpifiError(rc); 00837 } 00838 #endif 00839 00840 // Create the TOC, mark requested blocks as reserved and mark the TOC's 00841 // block(s) as reserved as well. 00842 for (i = 0; i < numReserved; i++) { 00843 TOC[i] = ~(TOC_VALID_MASK | TOC_RESERVED_MASK); 00844 } 00845 for (; i < (NUM_BLOCKS - NUM_TOC_BLOCKS); i++) { 00846 TOC[i] = ~TOC_VALID_MASK; 00847 } 00848 for (; i < (uint32_t)NUM_BLOCKS; i++) { 00849 TOC[i] = ~(TOC_VALID_MASK | TOC_RESERVED_MASK); 00850 } 00851 00852 // Save the TOC in the last block 00853 activeTOC = 0; 00854 fresult res = qspifs_saveTOC(); 00855 if (res != FS_OK) { 00856 activeTOC = -1; 00857 return res; 00858 } 00859 // opers.dest = (char *) TOC_BLOCK_ADDR; 00860 // opers.length = TOC_SIZE; 00861 // opers.scratch = NULL; 00862 // opers.protect = 0; 00863 // opers.options = S_VERIFY_PROG | S_CALLER_ERASE; 00864 // rc = spifi->spifi_program(&obj, (char*) TOC, &opers); 00865 // if (rc) { 00866 // return qspifs_translateSpifiError(rc); 00867 // } 00868 00869 // Read back TOC to be sure it worked 00870 return qspifs_readTOC(); 00871 } 00872 00873 /****************************************************************************** 00874 * 00875 * Description: 00876 * Deletes all files on the file system. This is a "quick format" that 00877 * leaves all blocks untouched and only modifies the TOC. Any reserved 00878 * blocks are kept reserved. 00879 * 00880 * The purpose of this function is to make it easy to clear the file system 00881 * without going through a time consuming complete erase every time. 00882 * 00883 * Params: 00884 * None 00885 * 00886 * Returns: 00887 * FS_OK on success or one of the FS_ERR_* on failure 00888 * 00889 *****************************************************************************/ 00890 // static fresult qspifs_deleteAllFiles(void) 00891 // { 00892 // for (int i = 0; i < NUM_BLOCKS; i++) 00893 // { 00894 // if (!TOC_IS_RESERVED(TOC[i])) { 00895 // TOC[i] = ~TOC_VALID_MASK; 00896 // } 00897 // } 00898 // 00899 // return qspifs_saveTOC(); 00900 // } 00901 00902 /****************************************************************************** 00903 * 00904 * Description: 00905 * Appends the data to the end of the file. 00906 * 00907 * Params: 00908 * [in] fh - The handle to the file as returned from fs_open_append() 00909 * [in] pData - The data to save 00910 * [in] size - Number of bytes to save 00911 * 00912 * Returns: 00913 * FS_OK on success or one of the FS_ERR_* on failure 00914 * 00915 *****************************************************************************/ 00916 static fresult qspifs_write(fileHandle_t* fh, const uint8_t * const pData, uint32_t size) 00917 { 00918 uint32_t left = size; 00919 const uint8_t* pSrc = pData; 00920 int rc, i; 00921 fresult res; 00922 int failed_attempts = 0; 00923 00924 do { 00925 res = qspifs_allocateSpace(fh, size); 00926 if (res != FS_OK) { 00927 break; 00928 } 00929 00930 opers.dest = (char *) (SPIFI_MEM_BASE + fh->tocIdx * ERASE_SIZE 00931 + HEADER_LEN + fh->size); 00932 opers.scratch = NULL; 00933 opers.protect = 0; 00934 opers.options = S_VERIFY_PROG; // | S_FORCE_ERASE; 00935 00936 while ((res == FS_OK) && (left > 0)) { 00937 if (left >= PROG_SIZE) { 00938 opers.length = PROG_SIZE; 00939 } else { 00940 opers.length = left; 00941 } 00942 if (IS_ADDR_IN_SPIFI(pData)) { 00943 memcpy(addr_conflict_buff, pSrc, opers.length); 00944 rc = spifi->spifi_program(obj, addr_conflict_buff, &opers); 00945 } else { 00946 rc = spifi->spifi_program(obj, (char*) pSrc, &opers); 00947 } 00948 res = qspifs_translateSpifiError(rc); 00949 if ((res == FS_ERR_SPIFI_VERIFICATION) 00950 && (++failed_attempts <= NUM_VERIFICATION_ATTEMPTS)) { 00951 // The verification process failed. 00952 // In all the observed occasions re-running the exact same 00953 // spifi_program command again yielded a 0 as a return value 00954 // the second time. 00955 // The quick'N'dirty fix is to re-run that program instruction 00956 // NUM_VERIFICATION_ATTEMPTS more time(s) when this happens. 00957 res = FS_OK; 00958 continue; 00959 } 00960 if (res != FS_OK) { 00961 // Got an error but cannot exit this function here as parts of the data 00962 // (previous loops?) may have been written so the TOC must be updated. 00963 break; 00964 } 00965 pSrc += opers.length; 00966 opers.dest += opers.length; 00967 left -= opers.length; 00968 failed_attempts = 0; 00969 } 00970 00971 // update file information 00972 fh->size = fh->size + size - left; 00973 fh->lastBlock = fh->tocIdx + ((fh->size + HEADER_LEN)/ ERASE_SIZE); 00974 left = fh->size + HEADER_LEN; 00975 for (i = 0; i <= (fh->lastBlock - fh->tocIdx); i++) { 00976 TOC[fh->tocIdx + i] &= ~TOC_FSIZE_MASK; 00977 TOC[fh->tocIdx + i] |= FS_MIN(ERASE_SIZE, left); 00978 left -= FILESIZE(TOC[fh->tocIdx + i]); 00979 } 00980 00981 if (res == FS_OK) { 00982 res = qspifs_saveTOC(); 00983 } else { 00984 // Want to save the TOC but not overwrite the previous error with 00985 // a possibly successful TOC saving thus making it seem like there 00986 // was no error 00987 qspifs_saveTOC(); 00988 } 00989 } while (0); 00990 00991 return res; 00992 } 00993 00994 /****************************************************************************** 00995 * 00996 * Description: 00997 * Tests if str starts with prefix. A prefix of NULL or an empty string 00998 * results in a positive result regardless of the content of str. 00999 * 01000 * Params: 01001 * [in] prefix - The prefix to look for 01002 * [in] str - The string to search for prefix 01003 * 01004 * Returns: 01005 * True if the specified string starts with prefix 01006 * 01007 *****************************************************************************/ 01008 static bool qspifs_startsWith(const char* prefix, const char* str) 01009 { 01010 const char* pA = prefix; 01011 const char* pB = str; 01012 01013 if (pA == NULL) 01014 { 01015 return true; 01016 } 01017 for (; *pA != '\0'; pA++, pB++) 01018 { 01019 if (*pB != *pA) 01020 { 01021 return false; 01022 } 01023 } 01024 01025 return true; 01026 } 01027 01028 /****************************************************************************** 01029 * Class Declarations 01030 *****************************************************************************/ 01031 01032 class QSPIFileHandle : public FileHandle { 01033 01034 public: 01035 QSPIFileHandle(fileHandle_t* handle, int flags); 01036 01037 virtual int close(); 01038 01039 virtual ssize_t write(const void *buffer, size_t size); 01040 01041 virtual ssize_t read(void *buffer, size_t size); 01042 01043 virtual off_t seek(off_t offset, int whence = SEEK_SET); 01044 01045 virtual off_t size(); 01046 01047 protected: 01048 01049 fileHandle_t fh; 01050 bool allowReading; 01051 bool allowWriting; 01052 uint32_t pos; 01053 }; 01054 01055 class QSPIDirHandle : public DirHandle { 01056 01057 public: 01058 static QSPIDirHandle* openDir(const char* dirname); 01059 01060 virtual ~QSPIDirHandle(); 01061 01062 virtual ssize_t read(struct dirent *ent); 01063 virtual int close(); 01064 virtual void seek(off_t offset); 01065 virtual off_t tell(); 01066 virtual void rewind(); 01067 01068 private: 01069 QSPIDirHandle(const char* dirname); 01070 01071 int findFileWithPrefix(const char* prefix, int startTOCIdx, int maxTOCIdx) const; 01072 01073 protected: 01074 01075 char* dirname; 01076 int nextTocIdx; 01077 01078 bool isRoot; 01079 01080 struct dirent cur_entry; 01081 }; 01082 01083 /****************************************************************************** 01084 * Class Implementations 01085 *****************************************************************************/ 01086 01087 QSPIFileHandle::QSPIFileHandle(fileHandle_t* handle, int flags) 01088 { 01089 fh = *handle; 01090 int accmode = (flags & O_ACCMODE); 01091 allowReading = (accmode == O_RDONLY) || (accmode == O_RDWR); 01092 allowWriting = (accmode == O_WRONLY) || (accmode == O_RDWR) || (flags & O_APPEND); 01093 pos = 0; 01094 } 01095 01096 int QSPIFileHandle::close() 01097 { 01098 delete this; 01099 return 0; 01100 } 01101 01102 ssize_t QSPIFileHandle::write(const void *buffer, size_t length) 01103 { 01104 if (!allowWriting) { 01105 return -1; 01106 } 01107 fresult res = qspifs_write(&fh, (const uint8_t*)buffer, length); 01108 if (res == FS_OK) { 01109 // A write is always 'append' in this file system so the file 01110 // position is always end of file after a write 01111 pos = fh.size; 01112 return length; 01113 } 01114 return -1; 01115 } 01116 01117 ssize_t QSPIFileHandle::read(void *buffer, size_t length) 01118 { 01119 if (!allowReading) { 01120 return -1; 01121 } 01122 if (pos >= fh.size) { 01123 return 0; 01124 } 01125 uint32_t len = FS_MIN(length, fh.size - pos); 01126 const char* pData = (const char*)(SPIFI_MEM_BASE + fh.tocIdx*ERASE_SIZE + HEADER_LEN + pos); 01127 memcpy(buffer, pData, len); 01128 pos += len; 01129 return len; 01130 } 01131 01132 off_t QSPIFileHandle::seek(off_t position, int whence) 01133 { 01134 switch (whence) { 01135 case SEEK_SET: 01136 pos = position; 01137 break; 01138 01139 case SEEK_CUR: 01140 pos += position; 01141 break; 01142 01143 case SEEK_END: 01144 pos = fh.size + position; 01145 break; 01146 01147 default: 01148 return -1; 01149 } 01150 return pos; 01151 } 01152 01153 off_t QSPIFileHandle::size() 01154 { 01155 return fh.size; 01156 } 01157 01158 QSPIDirHandle::QSPIDirHandle(const char* dirname) { 01159 size_t len = strlen(dirname); 01160 this->dirname = (char*)malloc(len + 2); // null termination and possible ending '/' 01161 if (this->dirname != NULL) { 01162 if (len == 0 || ((len == 1) && (dirname[0] == '/'))) { 01163 isRoot = true; 01164 this->dirname[0] = '\0'; 01165 } else { 01166 isRoot = false; 01167 memcpy(this->dirname, dirname, len+1); 01168 if (dirname[len - 1] != '/') { 01169 this->dirname[len] = '/'; 01170 this->dirname[len+1] = '\0'; 01171 } 01172 } 01173 cur_entry.d_name[HEADER_FNAME_STRLEN] = '\0'; 01174 rewind(); 01175 } 01176 } 01177 01178 QSPIDirHandle::~QSPIDirHandle() 01179 { 01180 if (dirname != NULL) { 01181 delete dirname; 01182 dirname = NULL; 01183 } 01184 } 01185 01186 QSPIDirHandle* QSPIDirHandle::openDir(const char* dirname) 01187 { 01188 QSPIDirHandle* d = new QSPIDirHandle(dirname); 01189 if (d->dirname == NULL) { 01190 // failed to allocate memory for the folder name 01191 delete d; 01192 d = NULL; 01193 } else if (!d->isRoot) { 01194 if (d->findFileWithPrefix(d->dirname, 0, NUM_BLOCKS) == NUM_BLOCKS) { 01195 // There are no files in this directory, i.e. it does not exist 01196 delete d; 01197 d = NULL; 01198 } 01199 } 01200 return d; 01201 } 01202 01203 int QSPIDirHandle::close() { 01204 delete this; 01205 return 0; 01206 } 01207 01208 void QSPIDirHandle::seek(off_t offset) { 01209 } 01210 01211 off_t QSPIDirHandle::tell() { 01212 return -1; 01213 } 01214 01215 int QSPIDirHandle::findFileWithPrefix(const char* prefix, int startTOCIdx, int maxTOCIdx) const 01216 { 01217 for (int i = startTOCIdx; i < maxTOCIdx; i++) { 01218 if (TOC_IS_FILE(TOC[i])) { 01219 const char* filename = (const char*) (SPIFI_MEM_BASE + i * ERASE_SIZE); 01220 if (qspifs_startsWith(prefix, filename)) { 01221 return i; 01222 } 01223 } 01224 } 01225 return NUM_BLOCKS; // no match 01226 } 01227 01228 01229 ssize_t QSPIDirHandle::read(struct dirent *ent) { 01230 if (nextTocIdx < NUM_BLOCKS) { 01231 for (int i = nextTocIdx; i < NUM_BLOCKS; i++) { 01232 int possible = findFileWithPrefix(dirname, i, NUM_BLOCKS); 01233 if (possible < NUM_BLOCKS) { 01234 const char* fullfilename = (const char*) (SPIFI_MEM_BASE + possible * ERASE_SIZE); 01235 const char* filename = fullfilename + strlen(dirname); 01236 01237 if (strchr(filename, '/') == NULL) { 01238 // file is not in any sub folder so it is truly in the wanted dir 01239 nextTocIdx = possible + 1; 01240 strcpy(cur_entry.d_name, filename); 01241 01242 memcpy(ent->d_name, cur_entry.d_name, sizeof(cur_entry.d_name)); 01243 return 1; 01244 } 01245 01246 // this is a file in a subfolder and should not be reported, 01247 // but the folder name itself should 01248 strcpy(cur_entry.d_name, fullfilename); 01249 char* pSlash = strchr(cur_entry.d_name + strlen(dirname), '/'); 01250 pSlash++; 01251 *pSlash = '\0'; 01252 01253 // now that cur_entry.d_name contains the folder's complete 01254 // path with a trailing '/', see if it has occurred earlier 01255 int older = findFileWithPrefix(cur_entry.d_name, 0, i); 01256 if (older < possible) { 01257 // already reported, move past this entry 01258 i = possible; 01259 } else { 01260 // found a new subfolder 01261 nextTocIdx = possible + 1; 01262 strcpy(cur_entry.d_name, filename); 01263 char* pSlash = strchr(cur_entry.d_name, '/'); 01264 // pSlash++; //with ++ the returned dir name is "mydir/" without ++ "mydir" is returned 01265 *pSlash = '\0'; 01266 01267 memcpy(ent->d_name, cur_entry.d_name, sizeof(cur_entry.d_name)); 01268 return 1; 01269 } 01270 } 01271 } 01272 } 01273 return 0; 01274 } 01275 01276 void QSPIDirHandle::rewind() { 01277 nextTocIdx = 0; 01278 } 01279 01280 01281 QSPIFileSystem::QSPIFileSystem(const char* name) : 01282 FileSystemLike(name) { 01283 01284 activeTOC = -1; 01285 spifi = NULL; 01286 } 01287 01288 // All modes are supported but: 01289 // 01290 // 1) All writes are treated as appends 01291 // 2) Truncation is only to size 0, i.e. effectively a delete 01292 // 3) File position operations work like this: 01293 // ReadOnly - dictates where to read from 01294 // WriteOnly - ignored, writes are always at the end 01295 // ReadWrite - dictates where to read from, writes ignore it but 01296 // sets the position to the end afterwards 01297 // 01298 int QSPIFileSystem::open(FileHandle **file, const char *filename, int flags) 01299 { 01300 fresult res = qspifs_init(); 01301 // if (res == FS_OK) { 01302 // if ((flags & O_ACCMODE) == O_RDONLY) { 01303 // // ok 01304 // } else if (flags & O_APPEND) { 01305 // // ok 01306 // } else { 01307 // // not supported yet, this includes all combination of flags 01308 // // allowing writing at specific positions in the file. This file system 01309 // // only allows appending 01310 // res = FS_ERR_INVALID_PARAM; 01311 // } 01312 // } 01313 01314 if (res == FS_OK) { 01315 if (strlen(filename) > HEADER_FNAME_STRLEN) { 01316 // Filename is too long 01317 res = FS_ERR_INVALID_PARAM; 01318 } 01319 } 01320 if (res == FS_OK) { 01321 // Handle truncation by silently deleting the file before 01322 // attempting to open it 01323 if (flags & O_TRUNC) { 01324 remove(filename); 01325 } 01326 } 01327 if (res == FS_OK) { 01328 fileHandle_t fh = {0,0,0}; 01329 res = qspifs_findFile(filename, &fh); 01330 if ((res == FS_ERR_NO_FILE) && (flags & O_CREAT)) { 01331 res = qspifs_allocateFile(filename, 1, &fh.tocIdx); 01332 } 01333 if (res == FS_OK) { 01334 res = qspifs_saveTOC(); 01335 } 01336 if (res == FS_OK) { 01337 *file = new QSPIFileHandle(&fh, flags); 01338 } 01339 } 01340 if (res != FS_OK) { 01341 debug_if(QSPI_DBG, "QSPIFS: Failed to open: %d\n", res); 01342 return -res; 01343 } 01344 return 0; 01345 } 01346 01347 int QSPIFileSystem::remove(const char *filename) 01348 { 01349 fileHandle_t fh = {0,0,0}; 01350 fresult res = qspifs_init(); 01351 if (res == FS_OK) { 01352 res = qspifs_findFile(filename, &fh); 01353 } 01354 if (res == FS_OK) { 01355 qspifs_deleteFile(&fh); 01356 res = qspifs_saveTOC(); 01357 } 01358 else if (res == FS_ERR_NO_FILE) { 01359 // file does not exist so treat it as a successful deletion 01360 res = FS_OK; 01361 } 01362 if (res != FS_OK) { 01363 debug_if(QSPI_DBG, "QSPIFS: Failed to delete %s: %d\n", filename, res); 01364 return -1; 01365 } 01366 return 0; 01367 } 01368 01369 int QSPIFileSystem::rename(const char *oldname, const char *newname) 01370 { 01371 fileHandle_t fhOld = {0,0,0}; 01372 fileHandle_t fhNew = {0,0,0}; 01373 01374 fresult res = qspifs_init(); 01375 if (res == FS_OK) { 01376 res = qspifs_findFile(oldname, &fhOld); 01377 } 01378 if (res == FS_OK) { 01379 // Make sure the destination file doesn't exist 01380 res = qspifs_findFile(newname, &fhNew); 01381 if (res == FS_OK) { 01382 res = FS_ERR_FILE_EXIST; 01383 } else if (res == FS_ERR_NO_FILE) { 01384 res = FS_OK; 01385 } 01386 } 01387 if (res == FS_OK) { 01388 int numNeededBlocks = 1 + ((fhOld.size + HEADER_LEN) / ERASE_SIZE); 01389 res = qspifs_allocateFile(newname, numNeededBlocks, &fhNew.tocIdx); 01390 if (res == FS_OK) { 01391 const uint8_t* pData = (const uint8_t*)(SPIFI_MEM_BASE + fhOld.tocIdx*ERASE_SIZE + HEADER_LEN); 01392 res = qspifs_write(&fhNew, pData, fhOld.size); 01393 if (res == FS_OK) { 01394 qspifs_deleteFile(&fhOld); 01395 } else { 01396 qspifs_deleteFile(&fhNew); 01397 } 01398 } 01399 qspifs_saveTOC(); 01400 } 01401 if (res != FS_OK) { 01402 debug_if(QSPI_DBG, "QSPIFS: Failed to rename '%s' to '%s': %d\n", oldname, newname, res); 01403 return -1; 01404 } 01405 return 0; 01406 } 01407 01408 int QSPIFileSystem::open(DirHandle **dir, const char *path) 01409 { 01410 if (isformatted()) { 01411 if (*path == '\0') { 01412 // opendir("/qspi/") will result in a call to this function with path="" 01413 *dir = QSPIDirHandle::openDir(path); 01414 return 0; 01415 } 01416 01417 FileHandle* fh; 01418 if (open(&fh, path, O_RDONLY) == 0) { 01419 // Attempting to open a file as a dir 01420 delete fh; 01421 return -1; 01422 } 01423 01424 // printf("opendir: path '%s'\n", path); 01425 if (strlen(path) <= HEADER_DNAME_MAXLEN) { 01426 *dir = QSPIDirHandle::openDir(path); 01427 return 0; 01428 } 01429 } 01430 return -1; 01431 } 01432 01433 int QSPIFileSystem::mkdir(const char *name, mode_t mode) 01434 { 01435 // Creating folders is always successful as there are no folders in this filesystem 01436 return 0; 01437 } 01438 01439 int QSPIFileSystem::format(unsigned int fsSizeInMB) 01440 { 01441 fresult res = qspifs_init(); 01442 if (res == FS_OK || res == FS_ERR_NOT_FORMATTED) { 01443 if (((fsSizeInMB<<20) > memInfo.memSize) || (fsSizeInMB < 1)) { 01444 debug_if(QSPI_DBG, "QSPIFS: Failed to format to size %d MByte: error %d\n", fsSizeInMB, res); 01445 return -1; 01446 } 01447 activeTOC = -1; 01448 res = qspifs_format(memInfo.memSize - (fsSizeInMB<<20)); 01449 } 01450 01451 if (res != FS_OK) { 01452 debug_if(QSPI_DBG, "QSPIFS: Failed to format: %d\n", res); 01453 return -1; 01454 } 01455 return 0; 01456 } 01457 01458 bool QSPIFileSystem::isformatted() 01459 { 01460 fresult res = qspifs_init(); 01461 if (res == FS_OK) { 01462 return true; 01463 } else if (res == FS_ERR_NOT_FORMATTED) { 01464 return false; 01465 } 01466 debug_if(QSPI_DBG, "QSPIFS: Failed to detect status: %d\n", res); 01467 return false; 01468 } 01469 01470 bool QSPIFileSystem::getMemoryBoundaries(uint32_t* pStartAddr, uint32_t* pEndAddr) 01471 { 01472 if (isformatted()) 01473 { 01474 *pEndAddr = 0x28000000 + memInfo.memSize; 01475 01476 // Look at all blocks except for the reserved ones 01477 for (int i = 0; i < NUM_BLOCKS; i++) 01478 { 01479 if (!TOC_IS_RESERVED(TOC[i])) 01480 { 01481 // Found first non-reserved erase block, indicating the start of 01482 // the file system. 01483 *pStartAddr = SPIFI_MEM_BASE + i*ERASE_SIZE; 01484 return true; 01485 } 01486 } 01487 01488 // The entire file system seems to be reserved which should never happen 01489 // but just in case, report it as beeing 1MB in size. 01490 *pStartAddr = *pEndAddr - 1024*1024; 01491 return true; 01492 } 01493 return false; 01494 }
Generated on Tue Jul 12 2022 14:18:31 by 1.7.2