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 EmbeddedArtists AB

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers <title>QSPIFileSystem.cpp Source File</title>

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 }