Update version of EALib.
Dependencies: FATFileSystem
Fork of EALib by
QSPIFileSystem.cpp
00001 /* 00002 * Copyright 2013 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 (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 /* For the Spansion memory the TOC occupies 256bytes and the TOC block will 00194 hold 256 TOCs. */ 00195 strcpy(memInfo.memName, "Spansion S25FL032"); 00196 memInfo.memSize = obj->memSize; 00197 memInfo.eraseBlockSize = 64*1024; 00198 memInfo.numEraseBlocks = memInfo.memSize / memInfo.eraseBlockSize; 00199 memInfo.tocSizeInBytes = sizeof(toc_entry_t) * memInfo.numEraseBlocks; 00200 memInfo.numTocs = memInfo.eraseBlockSize / memInfo.tocSizeInBytes; 00201 memInfo.tocBlockAddr = SPIFI_MEM_BASE + (NUM_BLOCKS * ERASE_SIZE) - (memInfo.numTocs * memInfo.tocSizeInBytes); 00202 break; 00203 00204 case SPIFI::Winbond_W25Q64FV: 00205 /* For the Winbond memory the TOC occupies 8192 bytes and that is bigger than 00206 one erase block (which is 4096 bytes). It is possible to either keep only 00207 one TOC or to create a couple to reduce wear on the memory. In this case 00208 the multiple TOCs option is used. */ 00209 strcpy(memInfo.memName, "Winbond W25Q64FV"); 00210 memInfo.memSize = obj->memSize; 00211 memInfo.eraseBlockSize = 4*1024; 00212 memInfo.numEraseBlocks = memInfo.memSize / memInfo.eraseBlockSize; 00213 memInfo.tocSizeInBytes = sizeof(toc_entry_t) * memInfo.numEraseBlocks; 00214 memInfo.numTocs = 8; 00215 memInfo.tocBlockAddr = SPIFI_MEM_BASE + (NUM_BLOCKS * ERASE_SIZE) - (memInfo.numTocs * memInfo.tocSizeInBytes); 00216 break; 00217 00218 case SPIFI::Macronix_MX25L6435EM2I: 00219 /* For the Macronix memory the TOC occupies 8192 bytes and that is bigger than 00220 one erase block (which is 4096 bytes). It is possible to either keep only 00221 one TOC or to create a couple to reduce wear on the memory. In this case 00222 the multiple TOCs option is used. */ 00223 strcpy(memInfo.memName, "Macronix_MX25L6435EM2I"); 00224 memInfo.memSize = obj->memSize; 00225 memInfo.eraseBlockSize = 4*1024; 00226 memInfo.numEraseBlocks = memInfo.memSize / memInfo.eraseBlockSize; 00227 memInfo.tocSizeInBytes = sizeof(toc_entry_t) * memInfo.numEraseBlocks; 00228 memInfo.numTocs = 8; 00229 memInfo.tocBlockAddr = SPIFI_MEM_BASE + (NUM_BLOCKS * ERASE_SIZE) - (memInfo.numTocs * memInfo.tocSizeInBytes); 00230 break; 00231 00232 case SPIFI::UnknownDevice: 00233 default: 00234 debug("INIT: Memory is unknown and may not work as expected\n"); 00235 00236 // Asuming it has 64Kb erase blocks (i.e. same setup as the Spansion S25FL032 00237 strcpy(memInfo.memName, "Unknown - check ID"); 00238 memInfo.memSize = obj->memSize; 00239 memInfo.eraseBlockSize = 64*1024; 00240 memInfo.numEraseBlocks = memInfo.memSize / memInfo.eraseBlockSize; 00241 memInfo.tocSizeInBytes = sizeof(toc_entry_t) * memInfo.numEraseBlocks; 00242 memInfo.numTocs = memInfo.eraseBlockSize / memInfo.tocSizeInBytes; 00243 memInfo.tocBlockAddr = SPIFI_MEM_BASE + (NUM_BLOCKS * ERASE_SIZE) - (memInfo.numTocs * memInfo.tocSizeInBytes); 00244 00245 /* 00246 * If this happens, check the manufacturer and device information 00247 * and compare with the data sheet for your chip. Also make sure 00248 * that the sector sizes are the same (i.e. 64KB) for your chip. 00249 * If everything is the same then add an exception for your chip. 00250 */ 00251 break; 00252 } 00253 00254 debug_if(QSPI_DBG, "INIT: Found %dMB %s\n", memInfo.memSize/0x100000, memInfo.memName); 00255 00256 if (TOC != NULL) { 00257 delete TOC; 00258 } 00259 TOC = (toc_entry_t*)malloc(TOC_SIZE); 00260 if (TOC == NULL) { 00261 debug_if(QSPI_DBG, "INIT: Failed to allocate memory for TOC\n"); 00262 spifi = NULL; 00263 return FS_ERR_MALLOC; 00264 } 00265 } 00266 if (activeTOC == -1) 00267 { 00268 return qspifs_readTOC(); 00269 } 00270 return FS_OK; 00271 } 00272 00273 /****************************************************************************** 00274 * 00275 * Description: 00276 * Converts the return value from one of the spifi_init(), spifi_program() 00277 * or spifi_erase() calls into a FS_* error code to simplify it for the 00278 * fs_qspi API user. 00279 * This function also attempts to detect the verification failure error. 00280 * When a verification error occurs the spifi_* functions returns the 00281 * conflicting address and not an error code. As this can be any address 00282 * it is difficult to test but this function converts it into the 00283 * FS_ERR_SPIFI_VERIFICATION error code which can be tested against. 00284 * 00285 * Params: 00286 * [in] rc - The return code from any of the spifi_* functions 00287 * 00288 * Returns: 00289 * FS_OK or one of the FS_ERR_* error codes 00290 * 00291 *****************************************************************************/ 00292 static fresult qspifs_translateSpifiError(int rc) 00293 { 00294 fresult res; 00295 if (rc == 0) 00296 { 00297 res = FS_OK; 00298 } 00299 else if ((rc >= FS_ERR_SPIFI_INTERNAL_ERROR) && (rc <= FS_ERR_SPIFI_ERASE_CONFLICT)) 00300 { 00301 // This is a known error code 00302 res = (fresult)rc; 00303 } 00304 else if (opers.options & (S_VERIFY_PROG | S_VERIFY_ERASE)) 00305 { 00306 // As verification was selected and rc is not in the list of known 00307 // codes this falls into this category in the User's Manual: 00308 // 00309 // "Other non-zero values can occur if options selects verification. 00310 // They will be the address in the SPIFI memory area at which the 00311 // first discrepancy was found." 00312 res = FS_ERR_SPIFI_VERIFICATION; 00313 } 00314 else 00315 { 00316 // Should never happen :-) as all listed error codes are covered but 00317 // to be on the safe side and not interpret this as a success, a generic 00318 // error is set. 00319 res = FS_ERR_SPIFI; 00320 } 00321 return res; 00322 } 00323 00324 /****************************************************************************** 00325 * 00326 * Description: 00327 * Reads the table of contents (TOC). The TOC is stored in the last erase 00328 * block on the QSPI flash. As the QSPI flash is not exactly RW (might 00329 * require erasing before writing) the TOC is relocated inside the erase 00330 * block everytime it is saved (see saveTOC()). The currently valid TOC 00331 * is allways the last one stored. 00332 * 00333 * Params: 00334 * None 00335 * 00336 * Returns: 00337 * FS_OK or one of the FS_ERR_* error codes 00338 * 00339 *****************************************************************************/ 00340 static fresult qspifs_readTOC(void) 00341 { 00342 int i, j; 00343 toc_entry_t* p; 00344 uint8_t invalid = 0; 00345 int lastValid = -1; 00346 00347 // Search for the first unused TOC, keeping track of the valid 00348 // ones as we go. 00349 for (i = 0; (i < NUM_TOCS) && !invalid; i++) 00350 { 00351 p = (toc_entry_t*)(TOC_BLOCK_ADDR + i*TOC_SIZE); 00352 for (j = 0; j < NUM_BLOCKS; j++) 00353 { 00354 if (!VALID_TOC_ENTRY(*p) || !MANDATORY_BITS_SET(*p)) 00355 { 00356 // invalid TOC entry, stop looking 00357 invalid = 1; 00358 break; 00359 } 00360 p++; 00361 } 00362 00363 if (!invalid) 00364 { 00365 // this TOC was ok, but perhaps there is a newer one? 00366 lastValid = i; 00367 } 00368 } 00369 00370 if (lastValid == -1) 00371 { 00372 // no valid TOCs on the flash 00373 return FS_ERR_NOT_FORMATTED; 00374 } 00375 else 00376 { 00377 // previous entry was ok so use that 00378 activeTOC = lastValid; 00379 p = (toc_entry_t*)(TOC_BLOCK_ADDR + activeTOC*TOC_SIZE); 00380 memcpy(TOC, p, TOC_SIZE); 00381 return FS_OK; 00382 } 00383 } 00384 00385 /****************************************************************************** 00386 * 00387 * Description: 00388 * Saves the table of contents (TOC). The TOC is stored in the last erase 00389 * block on the QSPI flash. As the QSPI flash is not exactly RW (might 00390 * require erasing before writing) the TOC is first compared with what is 00391 * stored in the QSPI flash and if there are no changes or all changes 00392 * only require bit changes 1->0 then the current TOC can be overwritten. 00393 * If bit value changes 0->1 are required then the current stored TOC 00394 * cannot be overwritten and the new TOC is instead stored in the next 00395 * available space. If the entire last block is filled then it is erased 00396 * and the new TOC is placed at the start of it. 00397 * 00398 * Params: 00399 * None 00400 * 00401 * Returns: 00402 * FS_OK or one of the FS_ERR_* error codes 00403 * 00404 *****************************************************************************/ 00405 static fresult qspifs_saveTOC(void) 00406 { 00407 int i, rc = 0; 00408 uint32_t* pSrc; 00409 uint32_t* pDest; 00410 uint32_t tmp; 00411 uint8_t identical = 1; 00412 00413 // active TOC same as the one we want to save? 00414 pSrc = (uint32_t*)TOC; 00415 pDest = (uint32_t*)(TOC_BLOCK_ADDR + activeTOC*TOC_SIZE); 00416 for (i = 0; i < NUM_TOC_ENTRIES; i++) 00417 { 00418 if (*pSrc != *pDest) 00419 { 00420 identical = 0; 00421 tmp = ((*pDest) ^ (*pSrc)) & (*pSrc); 00422 if (tmp > 0) 00423 { 00424 // found a change that contains 0->1 bit modification which 00425 // requires erasing or a new location 00426 activeTOC = (activeTOC + 1)%NUM_TOCS; 00427 if (activeTOC == 0) 00428 { 00429 // no more free TOCs so an erase is needed 00430 #if 0 00431 opers.options &= ~S_CALLER_ERASE; 00432 opers.options |= S_FORCE_ERASE; 00433 #else 00434 opers.dest = (char *) TOC_BLOCK_ADDR; 00435 opers.length = TOC_SIZE * NUM_TOCS; 00436 opers.scratch = NULL; 00437 opers.protect = 0; 00438 opers.options = S_NO_VERIFY; 00439 rc = spifi->spifi_erase(obj, &opers); 00440 if (rc) { 00441 return qspifs_translateSpifiError(rc); 00442 } 00443 #endif 00444 } 00445 break; 00446 } 00447 } 00448 pSrc++; 00449 pDest++; 00450 } 00451 00452 if (!identical) 00453 { 00454 opers.length = FS_MIN(TOC_SIZE, PROG_SIZE); 00455 opers.scratch = NULL; 00456 opers.protect = 0; 00457 opers.options = S_VERIFY_PROG | S_CALLER_ERASE; 00458 for (int i = 0; i < (TOC_SIZE / PROG_SIZE); i++) 00459 { 00460 opers.dest = (char *)(TOC_BLOCK_ADDR + activeTOC*TOC_SIZE + i*PROG_SIZE); 00461 rc = spifi->spifi_program(obj, ((char*)TOC)+i*PROG_SIZE, &opers); 00462 if (rc) 00463 { 00464 break; 00465 } 00466 } 00467 return qspifs_translateSpifiError(rc); 00468 } 00469 return FS_OK; 00470 } 00471 00472 /****************************************************************************** 00473 * 00474 * Description: 00475 * Searches the file system for a file with the specified name and 00476 * (if found) returns the file's position in the TOC. 00477 * 00478 * Note that the content of fh is only valid if FS_OK is returned. 00479 * 00480 * Params: 00481 * [in] filename - The name of the file to find 00482 * [out] fh - The handle with the file information 00483 * 00484 * Returns: 00485 * FS_OK or one of the FS_ERR_* error codes 00486 * 00487 *****************************************************************************/ 00488 static fresult qspifs_findFile(const char* filename, fileHandle_t* fh) 00489 { 00490 int i; 00491 00492 if (activeTOC == -1) 00493 { 00494 return FS_ERR_NOT_FORMATTED; 00495 } 00496 00497 // Look at all blocks except for the reserved ones 00498 for (i = 0; i < NUM_BLOCKS; i++) 00499 { 00500 if (TOC_IS_FILE(TOC[i]) && !TOC_IS_RESERVED(TOC[i])) 00501 { 00502 // found a file, see if name matches 00503 char* p = (char*)(SPIFI_MEM_BASE + i*ERASE_SIZE); 00504 if (strncmp(filename, p, HEADER_FNAME_LEN) == 0) 00505 { 00506 // found a matching name 00507 fh->tocIdx = i; 00508 fresult res = qspifs_fileSize(fh->tocIdx, &fh->size); 00509 if (res == FS_OK) { 00510 fh->lastBlock = fh->tocIdx + ((fh->size + HEADER_LEN)/ ERASE_SIZE); 00511 } 00512 return FS_OK; 00513 } 00514 } 00515 } 00516 return FS_ERR_NO_FILE; 00517 } 00518 00519 /****************************************************************************** 00520 * 00521 * Description: 00522 * Calculates and returns the file's size. 00523 * 00524 * Note that the content of pSize is only valid if FS_OK is returned. 00525 * 00526 * Params: 00527 * [in] tocIdx - The file's position in the TOC 00528 * [out] pSize - The file's size 00529 * 00530 * Returns: 00531 * FS_OK or one of the FS_ERR_* error codes 00532 * 00533 *****************************************************************************/ 00534 static fresult qspifs_fileSize(int tocIdx, uint32_t* pSize) 00535 { 00536 int i; 00537 00538 if (tocIdx < 0 || tocIdx > NUM_BLOCKS || !TOC_IS_FILE(TOC[tocIdx])) 00539 { 00540 return FS_ERR_NO_FILE; 00541 } 00542 00543 *pSize = 0; 00544 00545 // A file is always stored in sequential blocks so start with the files 00546 // first block and as long as it is full continue sum up the occupied 00547 // block sizes. As soon as a non-full block is found that must be the 00548 // file's last block. 00549 for (i = tocIdx; i < NUM_BLOCKS; i++) 00550 { 00551 *pSize += FILESIZE(TOC[i]); 00552 if (FILESIZE(TOC[i]) < ERASE_SIZE) 00553 { 00554 // last block in chain 00555 break; 00556 } 00557 } 00558 00559 // Remove the filename header from the file's size 00560 *pSize -= HEADER_LEN; 00561 00562 return FS_OK; 00563 } 00564 00565 /****************************************************************************** 00566 * 00567 * Description: 00568 * Erases everything in one block on the QSPI flash. 00569 * 00570 * Params: 00571 * [in] block - The block's number 00572 * 00573 * Returns: 00574 * FS_OK or one of the FS_ERR_* error codes 00575 * 00576 *****************************************************************************/ 00577 static fresult qspifs_eraseBlock(int block) 00578 { 00579 opers.dest = (char *)(block * ERASE_SIZE); 00580 opers.length = ERASE_SIZE; 00581 opers.scratch = NULL; 00582 opers.protect = 0; 00583 opers.options = S_NO_VERIFY; 00584 return qspifs_translateSpifiError(spifi->spifi_erase (obj, &opers)); 00585 } 00586 00587 /****************************************************************************** 00588 * 00589 * Description: 00590 * Creates a new file if there is enough space for it on the file system. 00591 * The TOC is searched for a unused sequence of blocks of at least the 00592 * needed size. That block is marked as used and the file's name is stored 00593 * in the first bytes of the file's first block. 00594 * 00595 * Note: The filename will not be tested for uniqueness. 00596 * Note: The value of pTocIdx will only be valid if FS_OK is returned. 00597 * 00598 * Params: 00599 * [in] filename - The name of the new file 00600 * [in] neededBlocks - The number of blocks (in sequence) to allocate 00601 * [out] pTocIdx - The new file's position in the TOC 00602 * 00603 * Returns: 00604 * FS_OK or one of the FS_ERR_* error codes 00605 * 00606 *****************************************************************************/ 00607 static fresult qspifs_allocateFile(const char* filename, int neededBlocks, int* pTocIdx) 00608 { 00609 int i, rc; 00610 00611 if (activeTOC == -1) 00612 { 00613 return FS_ERR_NOT_FORMATTED; 00614 } 00615 00616 // Look at all blocks except for the reserved ones 00617 for (i = 0; i < NUM_BLOCKS; i++) 00618 { 00619 //TODO: Improve search to use gaps to avoid having to move files 00620 // that are written to 00621 if (!USED_TOC_ENTRY(TOC[i]) && !TOC_IS_RESERVED(TOC[i])) 00622 { 00623 int j; 00624 for (j = 1; j < neededBlocks; j++) 00625 { 00626 if (USED_TOC_ENTRY(TOC[i+j]) || TOC_IS_RESERVED(TOC[i+j])) 00627 { 00628 // not enough free blocks in sequence, skip past these 00629 // tested entries and continue searching 00630 i += j; 00631 break; 00632 } 00633 } 00634 00635 if (j == neededBlocks) 00636 { 00637 const char* pSrc = filename; 00638 if (IS_ADDR_IN_SPIFI(filename)) 00639 { 00640 // The SPIFI ROM driver cannot write data from SPIFI into 00641 // SPIFI (i.e. cannot read and write at the same time). 00642 // The workaround is to copy the source data into a buffer 00643 // in local memory and use that as source for the write 00644 // instead. 00645 memcpy(addr_conflict_buff, filename, strlen(filename)+1); 00646 pSrc = addr_conflict_buff; 00647 } 00648 00649 // Erase the new file's first block and store the filename at the 00650 // start of it 00651 opers.length = strlen(pSrc)+1; 00652 opers.scratch = NULL; 00653 opers.protect = 0; 00654 opers.options = S_VERIFY_PROG | S_FORCE_ERASE;// S_CALLER_ERASE; 00655 opers.dest = (char *)(i*ERASE_SIZE); 00656 rc = spifi->spifi_program(obj, (char*)pSrc, &opers); 00657 if (rc) { 00658 return qspifs_translateSpifiError(rc); 00659 } 00660 00661 TOC[i] &= ~(TOC_VALID_MASK | TOC_USED_MASK | TOC_FILE_MASK | TOC_FSIZE_MASK); 00662 TOC[i] |= HEADER_LEN; 00663 00664 *pTocIdx = i; 00665 return FS_OK; 00666 } 00667 } 00668 } 00669 return FS_ERR_DISK_FULL; 00670 } 00671 00672 /****************************************************************************** 00673 * 00674 * Description: 00675 * Deletes the specified file by marking all its blocks as unused in 00676 * the TOC. 00677 * 00678 * Note: The deleted blocks are not erased here - that is done when they 00679 * are allocated the next time. 00680 * 00681 * Params: 00682 * [in] fh - The file handle with information about what to delete 00683 * 00684 * Returns: 00685 * None 00686 * 00687 *****************************************************************************/ 00688 static void qspifs_deleteFile(fileHandle_t* fh) 00689 { 00690 int i; 00691 00692 for (i = fh->lastBlock; i >= fh->tocIdx; i--) 00693 { 00694 TOC[i] = ~TOC_VALID_MASK; 00695 } 00696 } 00697 00698 /****************************************************************************** 00699 * 00700 * Description: 00701 * Ensures that the specified file can grow to the wanted size. 00702 * If the file size will increase enough to need one or more new blocks 00703 * and there isn't enough space then an attempt is made to move the 00704 * current file to a large enough space somewhere else. 00705 * 00706 * If there are more free block(s) at the end of the file then it is not 00707 * moved and instead those blocks are marked as used. 00708 * 00709 * Note: The filename will not be tested for uniqueness. 00710 * Note: The value of pTocIdx will only be valid if FS_OK is returned. 00711 * 00712 * Params: 00713 * [in/out] fh - The current file handle, might be updated after a move 00714 * [in] size - The wanted new size 00715 * 00716 * Returns: 00717 * FS_OK or one of the FS_ERR_* error codes 00718 * 00719 *****************************************************************************/ 00720 static fresult qspifs_allocateSpace(fileHandle_t* fh, uint32_t size) 00721 { 00722 uint16_t oldNumBlocks = (fh->size + HEADER_LEN) / ERASE_SIZE; 00723 uint16_t newNumBlocks = (fh->size + HEADER_LEN + size) / ERASE_SIZE; 00724 uint16_t numNeeded = newNumBlocks - oldNumBlocks; 00725 fresult res = FS_OK; 00726 00727 if (numNeeded > 0) 00728 { 00729 uint16_t i; 00730 for (i = 0; i < numNeeded; i++) 00731 { 00732 if (USED_TOC_ENTRY(TOC[fh->tocIdx + oldNumBlocks + 1 + i]) || 00733 TOC_IS_RESERVED(TOC[fh->tocIdx + oldNumBlocks + 1 + i])) 00734 { 00735 fileHandle_t fhNew; 00736 00737 // have to move the chain 00738 char* filename = (char*)(SPIFI_MEM_BASE + fh->tocIdx * ERASE_SIZE); 00739 res = qspifs_allocateFile(filename, newNumBlocks, &(fhNew.tocIdx)); 00740 if (res == FS_OK) 00741 { 00742 // copy data 00743 fhNew.lastBlock = fhNew.tocIdx; 00744 fhNew.size = 0; 00745 res = qspifs_write(&fhNew, (uint8_t*)(SPIFI_MEM_BASE + fh->tocIdx * ERASE_SIZE + HEADER_LEN), fh->size); 00746 } 00747 if (res == FS_OK) 00748 { 00749 // remove old entries 00750 qspifs_deleteFile(fh); 00751 00752 // modify old handle to point to new information 00753 fh->lastBlock = fhNew.lastBlock; 00754 fh->size = fhNew.size; 00755 fh->tocIdx = fhNew.tocIdx; 00756 } 00757 if (res != FS_OK) 00758 { 00759 // not possible to relocate the file => abort 00760 return res; 00761 } 00762 break; 00763 } 00764 } 00765 00766 // have space that is unused, so mark as used 00767 for (i = 0; i < numNeeded; i++) 00768 { 00769 int tocIdx = fh->tocIdx + oldNumBlocks + 1 + i; 00770 TOC[tocIdx] &= ~TOC_USED_MASK; 00771 qspifs_eraseBlock(tocIdx); 00772 } 00773 } 00774 00775 return res; 00776 } 00777 00778 /****************************************************************************** 00779 * 00780 * Description: 00781 * Adds a file system to the QSPI flash. The entire flash will be erase 00782 * except for the minReservedBytes first bytes. That reserved area (rounded 00783 * up to the closest even multiple of the erase block size) can be used 00784 * for anything and will never be touched by the file system. That area is 00785 * typically used for executing programs from when the internal flash is 00786 * full. 00787 * 00788 * The file system will have a table of content (TOC) placed at the start 00789 * of the last erase block on the flash. 00790 * 00791 * Params: 00792 * [in] minReservedBytes - The number of bytes to ignore at the start of 00793 * the flash. 00794 * 00795 * Returns: 00796 * FS_OK on success or one of the FS_ERR_* on failure 00797 * 00798 *****************************************************************************/ 00799 static fresult qspifs_format(unsigned int minReservedBytes) 00800 { 00801 int i, rc; 00802 int numReserved = 0; 00803 00804 if (minReservedBytes > 0) { 00805 numReserved = (minReservedBytes + ERASE_SIZE - 1) / ERASE_SIZE; 00806 if (numReserved >= (NUM_BLOCKS - 2)) { 00807 // Too many of the erase blocks are reserved - not even room for one file 00808 return FS_ERR_INVALID_PARAM; 00809 } 00810 } 00811 00812 #if 0 // works but is really slow 00813 // Erase all non-reserved blocks 00814 for (i = numReserved; i < NUM_BLOCKS; i++) { 00815 opers.dest = (char *) (i * ERASE_SIZE); 00816 opers.length = ERASE_SIZE; 00817 opers.scratch = NULL; 00818 opers.protect = 0; 00819 opers.options = S_NO_VERIFY; 00820 rc = spifi->spifi_erase(&obj, &opers); 00821 if (rc) { 00822 return qspifs_translateSpifiError(rc); 00823 } 00824 } 00825 #else 00826 // Erase all non-reserved blocks 00827 opers.dest = (char *) (numReserved * ERASE_SIZE); 00828 opers.length = MEM_SIZE - (numReserved * ERASE_SIZE); 00829 opers.scratch = NULL; 00830 opers.protect = 0; 00831 opers.options = S_NO_VERIFY; 00832 rc = spifi->spifi_erase(obj, &opers); 00833 if (rc) { 00834 return qspifs_translateSpifiError(rc); 00835 } 00836 #endif 00837 00838 // Create the TOC, mark requested blocks as reserved and mark the TOC's 00839 // block(s) as reserved as well. 00840 for (i = 0; i < numReserved; i++) { 00841 TOC[i] = ~(TOC_VALID_MASK | TOC_RESERVED_MASK); 00842 } 00843 for (; i < (NUM_BLOCKS - NUM_TOC_BLOCKS); i++) { 00844 TOC[i] = ~TOC_VALID_MASK; 00845 } 00846 for (; i < NUM_BLOCKS; i++) { 00847 TOC[i] = ~(TOC_VALID_MASK | TOC_RESERVED_MASK); 00848 } 00849 00850 // Save the TOC in the last block 00851 activeTOC = 0; 00852 fresult res = qspifs_saveTOC(); 00853 if (res != FS_OK) { 00854 activeTOC = -1; 00855 return res; 00856 } 00857 // opers.dest = (char *) TOC_BLOCK_ADDR; 00858 // opers.length = TOC_SIZE; 00859 // opers.scratch = NULL; 00860 // opers.protect = 0; 00861 // opers.options = S_VERIFY_PROG | S_CALLER_ERASE; 00862 // rc = spifi->spifi_program(&obj, (char*) TOC, &opers); 00863 // if (rc) { 00864 // return qspifs_translateSpifiError(rc); 00865 // } 00866 00867 // Read back TOC to be sure it worked 00868 return qspifs_readTOC(); 00869 } 00870 00871 /****************************************************************************** 00872 * 00873 * Description: 00874 * Deletes all files on the file system. This is a "quick format" that 00875 * leaves all blocks untouched and only modifies the TOC. Any reserved 00876 * blocks are kept reserved. 00877 * 00878 * The purpose of this function is to make it easy to clear the file system 00879 * without going through a time consuming complete erase every time. 00880 * 00881 * Params: 00882 * None 00883 * 00884 * Returns: 00885 * FS_OK on success or one of the FS_ERR_* on failure 00886 * 00887 *****************************************************************************/ 00888 // static fresult qspifs_deleteAllFiles(void) 00889 // { 00890 // for (int i = 0; i < NUM_BLOCKS; i++) 00891 // { 00892 // if (!TOC_IS_RESERVED(TOC[i])) { 00893 // TOC[i] = ~TOC_VALID_MASK; 00894 // } 00895 // } 00896 // 00897 // return qspifs_saveTOC(); 00898 // } 00899 00900 /****************************************************************************** 00901 * 00902 * Description: 00903 * Appends the data to the end of the file. 00904 * 00905 * Params: 00906 * [in] fh - The handle to the file as returned from fs_open_append() 00907 * [in] pData - The data to save 00908 * [in] size - Number of bytes to save 00909 * 00910 * Returns: 00911 * FS_OK on success or one of the FS_ERR_* on failure 00912 * 00913 *****************************************************************************/ 00914 static fresult qspifs_write(fileHandle_t* fh, const uint8_t * const pData, uint32_t size) 00915 { 00916 uint32_t left = size; 00917 const uint8_t* pSrc = pData; 00918 int rc, i; 00919 fresult res; 00920 int failed_attempts = 0; 00921 00922 do { 00923 res = qspifs_allocateSpace(fh, size); 00924 if (res != FS_OK) { 00925 break; 00926 } 00927 00928 opers.dest = (char *) (SPIFI_MEM_BASE + fh->tocIdx * ERASE_SIZE 00929 + HEADER_LEN + fh->size); 00930 opers.scratch = NULL; 00931 opers.protect = 0; 00932 opers.options = S_VERIFY_PROG; // | S_FORCE_ERASE; 00933 00934 while ((res == FS_OK) && (left > 0)) { 00935 if (left >= PROG_SIZE) { 00936 opers.length = PROG_SIZE; 00937 } else { 00938 opers.length = left; 00939 } 00940 if (IS_ADDR_IN_SPIFI(pData)) { 00941 memcpy(addr_conflict_buff, pSrc, opers.length); 00942 rc = spifi->spifi_program(obj, addr_conflict_buff, &opers); 00943 } else { 00944 rc = spifi->spifi_program(obj, (char*) pSrc, &opers); 00945 } 00946 res = qspifs_translateSpifiError(rc); 00947 if ((res == FS_ERR_SPIFI_VERIFICATION) 00948 && (++failed_attempts <= NUM_VERIFICATION_ATTEMPTS)) { 00949 // The verification process failed. 00950 // In all the observed occasions re-running the exact same 00951 // spifi_program command again yielded a 0 as a return value 00952 // the second time. 00953 // The quick'N'dirty fix is to re-run that program instruction 00954 // NUM_VERIFICATION_ATTEMPTS more time(s) when this happens. 00955 res = FS_OK; 00956 continue; 00957 } 00958 if (res != FS_OK) { 00959 // Got an error but cannot exit this function here as parts of the data 00960 // (previous loops?) may have been written so the TOC must be updated. 00961 break; 00962 } 00963 pSrc += opers.length; 00964 opers.dest += opers.length; 00965 left -= opers.length; 00966 failed_attempts = 0; 00967 } 00968 00969 // update file information 00970 fh->size = fh->size + size - left; 00971 fh->lastBlock = fh->tocIdx + ((fh->size + HEADER_LEN)/ ERASE_SIZE); 00972 left = fh->size + HEADER_LEN; 00973 for (i = 0; i <= (fh->lastBlock - fh->tocIdx); i++) { 00974 TOC[fh->tocIdx + i] &= ~TOC_FSIZE_MASK; 00975 TOC[fh->tocIdx + i] |= FS_MIN(ERASE_SIZE, left); 00976 left -= FILESIZE(TOC[fh->tocIdx + i]); 00977 } 00978 00979 if (res == FS_OK) { 00980 res = qspifs_saveTOC(); 00981 } else { 00982 // Want to save the TOC but not overwrite the previous error with 00983 // a possibly successful TOC saving thus making it seem like there 00984 // was no error 00985 qspifs_saveTOC(); 00986 } 00987 } while (0); 00988 00989 return res; 00990 } 00991 00992 /****************************************************************************** 00993 * 00994 * Description: 00995 * Tests if str starts with prefix. A prefix of NULL or an empty string 00996 * results in a positive result regardless of the content of str. 00997 * 00998 * Params: 00999 * [in] prefix - The prefix to look for 01000 * [in] str - The string to search for prefix 01001 * 01002 * Returns: 01003 * True if the specified string starts with prefix 01004 * 01005 *****************************************************************************/ 01006 static bool qspifs_startsWith(const char* prefix, const char* str) 01007 { 01008 const char* pA = prefix; 01009 const char* pB = str; 01010 01011 if (pA == NULL) 01012 { 01013 return true; 01014 } 01015 for (; *pA != '\0'; pA++, pB++) 01016 { 01017 if (*pB != *pA) 01018 { 01019 return false; 01020 } 01021 } 01022 01023 return true; 01024 } 01025 01026 /****************************************************************************** 01027 * Class Declarations 01028 *****************************************************************************/ 01029 01030 class QSPIFileHandle : public FileHandle { 01031 01032 public: 01033 QSPIFileHandle(fileHandle_t* handle, int flags); 01034 01035 virtual int close(); 01036 01037 virtual ssize_t write(const void *buffer, size_t length); 01038 01039 virtual ssize_t read(void *buffer, size_t length); 01040 01041 virtual int isatty(); 01042 01043 virtual off_t lseek(off_t position, int whence); 01044 01045 virtual int fsync(); 01046 01047 virtual off_t flen(); 01048 01049 protected: 01050 01051 fileHandle_t fh; 01052 bool allowReading; 01053 bool allowWriting; 01054 uint32_t pos; 01055 }; 01056 01057 class QSPIDirHandle : public DirHandle { 01058 01059 public: 01060 static QSPIDirHandle* openDir(const char* dirname); 01061 01062 virtual ~QSPIDirHandle(); 01063 01064 virtual int closedir(); 01065 virtual struct dirent *readdir(); 01066 virtual void rewinddir(); 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 int QSPIFileHandle::isatty() 01133 { 01134 return 0; 01135 } 01136 01137 off_t QSPIFileHandle::lseek(off_t position, int whence) 01138 { 01139 switch (whence) { 01140 case SEEK_SET: 01141 pos = position; 01142 break; 01143 01144 case SEEK_CUR: 01145 pos += position; 01146 break; 01147 01148 case SEEK_END: 01149 pos = fh.size + position; 01150 break; 01151 01152 default: 01153 return -1; 01154 } 01155 return pos; 01156 } 01157 01158 int QSPIFileHandle::fsync() 01159 { 01160 return 0; // always synced 01161 } 01162 01163 off_t QSPIFileHandle::flen() 01164 { 01165 return fh.size; 01166 } 01167 01168 QSPIDirHandle::QSPIDirHandle(const char* dirname) { 01169 size_t len = strlen(dirname); 01170 this->dirname = (char*)malloc(len + 2); // null termination and possible ending '/' 01171 if (this->dirname != NULL) { 01172 if (len == 0 || ((len == 1) && (dirname[0] == '/'))) { 01173 isRoot = true; 01174 this->dirname[0] = '\0'; 01175 } else { 01176 isRoot = false; 01177 memcpy(this->dirname, dirname, len+1); 01178 if (dirname[len - 1] != '/') { 01179 this->dirname[len] = '/'; 01180 this->dirname[len+1] = '\0'; 01181 } 01182 } 01183 cur_entry.d_name[HEADER_FNAME_STRLEN] = '\0'; 01184 rewinddir(); 01185 } 01186 } 01187 01188 QSPIDirHandle::~QSPIDirHandle() 01189 { 01190 if (dirname != NULL) { 01191 delete dirname; 01192 dirname = NULL; 01193 } 01194 } 01195 01196 QSPIDirHandle* QSPIDirHandle::openDir(const char* dirname) 01197 { 01198 QSPIDirHandle* d = new QSPIDirHandle(dirname); 01199 if (d->dirname == NULL) { 01200 // failed to allocate memory for the folder name 01201 delete d; 01202 d = NULL; 01203 } else if (!d->isRoot) { 01204 if (d->findFileWithPrefix(d->dirname, 0, NUM_BLOCKS) == NUM_BLOCKS) { 01205 // There are no files in this directory, i.e. it does not exist 01206 delete d; 01207 d = NULL; 01208 } 01209 } 01210 return d; 01211 } 01212 01213 int QSPIDirHandle::closedir() { 01214 delete this; 01215 return 0; 01216 } 01217 01218 int QSPIDirHandle::findFileWithPrefix(const char* prefix, int startTOCIdx, int maxTOCIdx) const 01219 { 01220 for (int i = startTOCIdx; i < maxTOCIdx; i++) { 01221 if (TOC_IS_FILE(TOC[i])) { 01222 const char* filename = (const char*) (SPIFI_MEM_BASE + i * ERASE_SIZE); 01223 if (qspifs_startsWith(prefix, filename)) { 01224 return i; 01225 } 01226 } 01227 } 01228 return NUM_BLOCKS; // no match 01229 } 01230 01231 01232 struct dirent *QSPIDirHandle::readdir() { 01233 if (nextTocIdx < NUM_BLOCKS) { 01234 for (int i = nextTocIdx; i < NUM_BLOCKS; i++) { 01235 int possible = findFileWithPrefix(dirname, i, NUM_BLOCKS); 01236 if (possible < NUM_BLOCKS) { 01237 const char* fullfilename = (const char*) (SPIFI_MEM_BASE + possible * ERASE_SIZE); 01238 const char* filename = fullfilename + strlen(dirname); 01239 01240 if (strchr(filename, '/') == NULL) { 01241 // file is not in any sub folder so it is truly in the wanted dir 01242 nextTocIdx = possible + 1; 01243 strcpy(cur_entry.d_name, filename); 01244 return &cur_entry; 01245 } 01246 01247 // this is a file in a subfolder and should not be reported, 01248 // but the folder name itself should 01249 strcpy(cur_entry.d_name, fullfilename); 01250 char* pSlash = strchr(cur_entry.d_name + strlen(dirname), '/'); 01251 pSlash++; 01252 *pSlash = '\0'; 01253 01254 // now that cur_entry.d_name contains the folder's complete 01255 // path with a trailing '/', see if it has occurred earlier 01256 int older = findFileWithPrefix(cur_entry.d_name, 0, i); 01257 if (older < possible) { 01258 // already reported, move past this entry 01259 i = possible; 01260 } else { 01261 // found a new subfolder 01262 nextTocIdx = possible + 1; 01263 strcpy(cur_entry.d_name, filename); 01264 char* pSlash = strchr(cur_entry.d_name, '/'); 01265 // pSlash++; //with ++ the returned dir name is "mydir/" without ++ "mydir" is returned 01266 *pSlash = '\0'; 01267 return &cur_entry; 01268 } 01269 } 01270 } 01271 } 01272 return NULL; 01273 } 01274 01275 void QSPIDirHandle::rewinddir() { 01276 nextTocIdx = 0; 01277 } 01278 01279 01280 QSPIFileSystem::QSPIFileSystem(const char* name) : 01281 FileSystemLike(name) { 01282 01283 activeTOC = -1; 01284 spifi = NULL; 01285 } 01286 01287 // All modes are supported but: 01288 // 01289 // 1) All writes are treated as appends 01290 // 2) Truncation is only to size 0, i.e. effectively a delete 01291 // 3) File position operations work like this: 01292 // ReadOnly - dictates where to read from 01293 // WriteOnly - ignored, writes are always at the end 01294 // ReadWrite - dictates where to read from, writes ignore it but 01295 // sets the position to the end afterwards 01296 // 01297 FileHandle *QSPIFileSystem::open(const char *filename, int flags) 01298 { 01299 fresult res = qspifs_init(); 01300 // if (res == FS_OK) { 01301 // if ((flags & O_ACCMODE) == O_RDONLY) { 01302 // // ok 01303 // } else if (flags & O_APPEND) { 01304 // // ok 01305 // } else { 01306 // // not supported yet, this includes all combination of flags 01307 // // allowing writing at specific positions in the file. This file system 01308 // // only allows appending 01309 // res = FS_ERR_INVALID_PARAM; 01310 // } 01311 // } 01312 if (res == FS_OK) { 01313 if (strlen(filename) > HEADER_FNAME_STRLEN) { 01314 // Filename is too long 01315 res = FS_ERR_INVALID_PARAM; 01316 } 01317 } 01318 if (res == FS_OK) { 01319 // Handle truncation by silently deleting the file before 01320 // attempting to open it 01321 if (flags & O_TRUNC) { 01322 remove(filename); 01323 } 01324 } 01325 if (res == FS_OK) { 01326 fileHandle_t fh = {0,0,0}; 01327 res = qspifs_findFile(filename, &fh); 01328 if ((res == FS_ERR_NO_FILE) && (flags & O_CREAT)) { 01329 res = qspifs_allocateFile(filename, 1, &fh.tocIdx); 01330 } 01331 if (res == FS_OK) { 01332 res = qspifs_saveTOC(); 01333 } 01334 if (res == FS_OK) { 01335 return new QSPIFileHandle(&fh, flags); 01336 } 01337 } 01338 debug_if(QSPI_DBG, "QSPIFS: Failed to open: %d\n", res); 01339 return NULL; 01340 } 01341 01342 int QSPIFileSystem::remove(const char *filename) 01343 { 01344 fileHandle_t fh = {0,0,0}; 01345 fresult res = qspifs_init(); 01346 if (res == FS_OK) { 01347 res = qspifs_findFile(filename, &fh); 01348 } 01349 if (res == FS_OK) { 01350 qspifs_deleteFile(&fh); 01351 res = qspifs_saveTOC(); 01352 } 01353 else if (res == FS_ERR_NO_FILE) { 01354 // file does not exist so treat it as a successful deletion 01355 res = FS_OK; 01356 } 01357 if (res != FS_OK) { 01358 debug_if(QSPI_DBG, "QSPIFS: Failed to delete %s: %d\n", filename, res); 01359 return -1; 01360 } 01361 return 0; 01362 } 01363 01364 int QSPIFileSystem::rename(const char *oldname, const char *newname) 01365 { 01366 fileHandle_t fhOld = {0,0,0}; 01367 fileHandle_t fhNew = {0,0,0}; 01368 01369 fresult res = qspifs_init(); 01370 if (res == FS_OK) { 01371 res = qspifs_findFile(oldname, &fhOld); 01372 } 01373 if (res == FS_OK) { 01374 // Make sure the destination file doesn't exist 01375 res = qspifs_findFile(newname, &fhNew); 01376 if (res == FS_OK) { 01377 res = FS_ERR_FILE_EXIST; 01378 } else if (res == FS_ERR_NO_FILE) { 01379 res = FS_OK; 01380 } 01381 } 01382 if (res == FS_OK) { 01383 int numNeededBlocks = 1 + ((fhOld.size + HEADER_LEN) / ERASE_SIZE); 01384 res = qspifs_allocateFile(newname, numNeededBlocks, &fhNew.tocIdx); 01385 if (res == FS_OK) { 01386 const uint8_t* pData = (const uint8_t*)(SPIFI_MEM_BASE + fhOld.tocIdx*ERASE_SIZE + HEADER_LEN); 01387 res = qspifs_write(&fhNew, pData, fhOld.size); 01388 if (res == FS_OK) { 01389 qspifs_deleteFile(&fhOld); 01390 } else { 01391 qspifs_deleteFile(&fhNew); 01392 } 01393 } 01394 qspifs_saveTOC(); 01395 } 01396 if (res != FS_OK) { 01397 debug_if(QSPI_DBG, "QSPIFS: Failed to rename '%s' to '%s': %d\n", oldname, newname, res); 01398 return -1; 01399 } 01400 return 0; 01401 } 01402 01403 DirHandle *QSPIFileSystem::opendir(const char *name) 01404 { 01405 FileHandle* fh = open(name, O_RDONLY); 01406 if (fh != NULL) { 01407 // Attempting to open a file as a dir 01408 delete fh; 01409 return NULL; 01410 } 01411 01412 // printf("opendir: name '%s'\n", name); 01413 if (strlen(name) <= HEADER_DNAME_MAXLEN) { 01414 return QSPIDirHandle::openDir(name); 01415 } 01416 return NULL; 01417 } 01418 01419 int QSPIFileSystem::mkdir(const char *name, mode_t mode) 01420 { 01421 // Creating folders is always successful as there are no folders in this filesystem 01422 return 0; 01423 } 01424 01425 int QSPIFileSystem::format(unsigned int fsSizeInMB) 01426 { 01427 fresult res = qspifs_init(); 01428 if (res == FS_OK || res == FS_ERR_NOT_FORMATTED) { 01429 if (((fsSizeInMB<<20) > memInfo.memSize) || (fsSizeInMB < 1)) { 01430 debug_if(QSPI_DBG, "QSPIFS: Failed to format to size %d MByte: error %d\n", fsSizeInMB, res); 01431 return -1; 01432 } 01433 activeTOC = -1; 01434 res = qspifs_format(memInfo.memSize - (fsSizeInMB<<20)); 01435 } 01436 01437 if (res != FS_OK) { 01438 debug_if(QSPI_DBG, "QSPIFS: Failed to format: %d\n", res); 01439 return -1; 01440 } 01441 return 0; 01442 } 01443 01444 bool QSPIFileSystem::isformatted() 01445 { 01446 fresult res = qspifs_init(); 01447 if (res == FS_OK) { 01448 return true; 01449 } else if (res == FS_ERR_NOT_FORMATTED) { 01450 return false; 01451 } 01452 debug_if(QSPI_DBG, "QSPIFS: Failed to detect status: %d\n", res); 01453 return false; 01454 } 01455 01456 bool QSPIFileSystem::getMemoryBoundaries(uint32_t* pStartAddr, uint32_t* pEndAddr) 01457 { 01458 if (isformatted()) 01459 { 01460 *pEndAddr = 0x28000000 + memInfo.memSize; 01461 01462 // Look at all blocks except for the reserved ones 01463 for (int i = 0; i < NUM_BLOCKS; i++) 01464 { 01465 if (!TOC_IS_RESERVED(TOC[i])) 01466 { 01467 // Found first non-reserved erase block, indicating the start of 01468 // the file system. 01469 *pStartAddr = SPIFI_MEM_BASE + i*ERASE_SIZE; 01470 return true; 01471 } 01472 } 01473 01474 // The entire file system seems to be reserved which should never happen 01475 // but just in case, report it as beeing 1MB in size. 01476 *pStartAddr = *pEndAddr - 1024*1024; 01477 return true; 01478 } 01479 return false; 01480 }
Generated on Thu Jul 14 2022 09:42:14 by 1.7.2