Update version of EALib.

Dependencies:   FATFileSystem

Fork of EALib by IONX

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers QSPIFileSystem.cpp Source File

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 }