mFS file system library for EEPROM memory chips.

Revision:
0:cbf45dde2b49
Child:
5:a0fe74dce80d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mfs.cpp	Mon Feb 21 07:35:16 2011 +0000
@@ -0,0 +1,506 @@
+/*H****************************************************************************
+* FILENAME :        mfs.cpp                                                   *
+*                                                                             *
+* DESCRIPTION :                                                               *
+*       mFS file system implementation for mBED with external I2C EEEPROM.    *
+*                                                                             *
+* AUTHOR :    Olli Vanhoja        START DATE :    2011-02-21                  *
+*******************************************************************************
+*
+* CHANGES :
+*
+* VERSION DATE       WHO             DETAIL
+* 0.1     2011-02-21 Olli Vanhoja    Initial release version
+*
+*H*/
+
+#include "mbed.h"
+#include "mfs.h"
+#include "i2c_eeprom.h"
+
+#define RB 3 // Reseved bytes per block (1 attrb B, 2 B for next/prev pointers
+#define DEBUG // Adds extra safety to reading and writing
+
+DigitalOut FlushLed(LED2);
+
+//extern Serial pc(USBTX, USBRX);
+
+/* mFS ************************************************************************
+ * | VOLinfo | fs | Data block 1 | etc..                                      *
+ * VOLinfo = | szVolname[256] | unsigned int nFreeBlocks |                    *
+ * fsH[n]  = | RObit | LOCKbit | FEndBit | FBeingBit | (12-bit) next |        *
+ * fsL[n]  = | 4-bits unused | 12-bit address |                               *
+ *****************************************************************************/
+
+mfs::mfs(int i2c_address)
+{
+    mem = new i2c_eeprom(i2c_address);
+}
+
+char mfs::read(char *data, char block, unsigned int byte, unsigned int n)
+{
+    // Faster reading without DEBUG mode
+    #ifdef DEBUG
+    if ((byte+n-1 >= BS))
+        return 1;
+    #endif
+    mem->read(BS*block+byte, n, data);
+    //wait_us(100);
+    return 0;
+}
+
+char mfs::write(char *data, char block, unsigned int byte, unsigned int n)
+{
+    // Faster writing without DEBUG mode
+    #ifdef DEBUG
+    if (byte+n >= BS)
+        return 1;
+    #endif
+    mem->write(data, BS*block+byte, n);
+    return 0;
+}
+
+char mfs::getNextFreeBlock(char *blockOut)
+{    
+    // Locate free block by seeking EERPOM
+    char cFlags[1];
+    
+    for (*blockOut=0; *blockOut < BC; (*blockOut)++)
+    {
+        read(cFlags, *blockOut, 0, 1);
+        if (cFlags[0] == 0x04)
+            break;
+        if (*blockOut >= BC-1)
+            return 1;
+    }
+    
+    return 0;
+}
+
+unsigned int mfs::findNextFile(unsigned int block, char *filenameOut)
+{
+    unsigned int i=block;
+    char cFlags[1];
+    
+    while (i < BC)
+    {
+        read(cFlags, i, 0, 1);
+        
+        if ((cFlags[0] & 0x8C) == 0x8C)
+            break; // File found
+        else
+            i++;
+    }
+    
+    if(i == BC)
+    {
+        strcpy(filenameOut, "");
+        return 0xffff; // Empty fs
+    }
+    
+    // Read filename
+    read(filenameOut, i, 3, 20);
+    return i; // Return block number
+}
+
+uint16 mfs::getFirstBlockOfFile(char filename[20])
+{
+    uint16 block=0;
+    char tmpFilename[20]="";
+
+    while (block < BC)
+    {
+        block = findNextFile(block, tmpFilename);
+        if (block < BC)
+        {
+            if(strcmp(tmpFilename, filename) == 0)
+               return block; // File exists
+        }
+        else return 0xFFFF; // File doesn't exist
+        block++;
+    }
+
+    return 0xFFFF; // ??
+}
+
+char mfs::createFile(char filename[20])
+{
+    char tmpFilename[20];
+    unsigned int n;
+    char fb;
+
+    for (n=0; n < BC; n++)
+    {
+        n=findNextFile(n, tmpFilename);
+        if(n < BC)
+        {
+            if(strcmp(tmpFilename, filename) == 0)
+                return 1; // File exist
+        }
+        else break; // We already reached the edge of the universe
+        n++;
+    }
+
+    if(getNextFreeBlock(&fb))
+        return 2; // Out of space
+    
+    char cData[23];
+    cData[0] = '\xCC'; // Necessary flags for file
+    cData[1] = '\0'; // Only this block yet
+    cData[2] = '\0'; // First block there could't be prev blocks
+    
+    for (char i=0; i < 20; i++)
+        cData[3+i] = filename[i];
+    
+    // Create file
+    write(cData, fb, 0, 23);
+        
+    return 0;
+}
+
+char mfs::removeFile(char filename[20])
+{
+    unsigned int block;
+    char cData[3] = {'\0','\0','\0'};
+
+    // Check if file exists
+    block = getFirstBlockOfFile(filename);
+    if (block > BC)
+        return 1; // File not found
+    
+    // Clear blocks reserver by the file
+    unsigned int i=0;
+    char tmp_cData[2];
+    while(1)
+    {
+        read(cData, block, 0, 2);
+        tmp_cData[0] = cData[0];
+        tmp_cData[1] = cData[1];
+        
+        // Clear the block
+        cData[0] = '\x04';
+        write(cData, block, 0, 3);
+        
+        if ((tmp_cData[0] & 0x4C) != 0)
+            break; // End of File found
+        else if (i >= BC)
+            return 2; // fs is corrupted
+        block = tmp_cData[1];
+
+        i++;
+    }
+    
+    return 0; // Everything went better than expected
+}
+
+char mfs::setFileFlags(char *flags, char filename[20])
+{
+    /* RO|HIDDEN|LOCK  *
+     * H            L */
+    
+    uint16 n;
+    char cData[1] = {'\0'};
+
+    // Check if file exists
+    n = getFirstBlockOfFile(filename);
+    if (n > BC)
+        return 1; // File not found
+    
+    read(cData, n, 0, 1);
+    cData[0] |= (flags[0] & 0x01)|((flags[0] & 0x02) << 3)|((flags[0] & 0x04) << 3);
+    write(cData, n, 0, 1);
+    
+    return 0;
+}
+
+char mfs::getFileFlags(char *flags, char filename[20])
+{
+    /* RO|HIDDEN|LOCK  *
+     * H            L */
+    
+    uint16 n;
+    char cData[1] = {'\0'};
+
+    // Check if file exists
+    n = getFirstBlockOfFile(filename);
+    if (n > BC)
+        return 1; // File not found
+    
+    read(cData, n, 0, 1);
+    flags[0] = (cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3);
+    
+    return 0;
+}
+
+// Return number of free blocks
+uint16 mfs::free()
+{
+    uint16 blocks=0;
+    uint16 r;
+    char cFlags[1];
+    
+    for (r=0; r < BC; r++)
+    {
+        read(cFlags, r, 0, 1);
+        if (cFlags[0] == 0x04)
+            blocks++;
+        if (r >= BC-1)
+            return blocks;
+    }
+    
+    return 0;
+}
+
+char mfs::mkfs(char createLabel)
+{
+    unsigned int iAddr = 0;
+    uint16 i = 0;
+    uint16 bad = 0; // For counting bad block headers
+    char cFlags[] = {'\0', '\0', '\0'}, a[1];
+    
+    if (createLabel >= 1)
+    {
+        // Write Volume label
+        cFlags[0] = '\x0E';
+        mem->write(a, iAddr, 3);
+        iAddr = BS;
+        i = 1;
+    }
+    
+    cFlags[0] = '\x04';
+    for (; i < BC; i++)
+    {
+        mem->write(cFlags, iAddr, 3);
+        mem->read(iAddr, 1, a);
+        if (a[0] != cFlags[0])
+            bad++;
+        iAddr += BS;
+    }
+    
+    return bad;
+}
+
+file::file(mfs *fs_ref, char filename[20], char operation)
+{
+    // Operations:
+    // 0 = Open RO
+    // 1 = Open RW
+    attr = operation;
+    
+    fs = fs_ref; // Don't forget this :)
+    
+    uint16 n;
+    char cData[3] = {'\0','\0','\0'};
+
+    // Check if file exists
+    n = fs->getFirstBlockOfFile(filename);
+    if (n == 0xFFFF)
+        error("Oops, file \"%s\" not found! n=0x%X", filename, n); // File not found
+    
+    fs->read(cData, n, 0, 1);
+    char flags = (cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3);
+    
+    if (attr == 1)
+    {
+        if ((flags & 0x04) != 0)
+            error("Oops, cant open in RW mode!");
+    }
+
+    // Store FBOF number
+    firstBlock = n;
+    currBlock = n;
+    blockPos = RB+20; // skip flags + pointers + filename
+
+    // Initialize buffer
+    for (unsigned int i=0; i < BUF; i++)
+        buffer[i] = '\0';
+    bufPos = 0;
+}
+
+file::~file()
+{
+    flush();
+}
+
+void file::needsFlush()
+{
+    if (bufPos > 0)
+    {
+        char tmpBlock = currBlock;
+        unsigned int tmpPos = blockPos;
+        
+        if(flush() != 0)
+            error("Flush failed!");
+        currBlock = tmpBlock;
+        blockPos = tmpPos;
+    }
+}
+
+void file::rewind()
+{
+    flush();
+    currBlock = firstBlock;
+    blockPos = RB+20; // skip flags & pointers + filename
+}
+
+char file::forward()
+{
+    char cData[2];
+
+    needsFlush();
+    
+    // Fetch link to next block
+    fs->read(cData, currBlock, 0, 2);
+    if (!(cData[0] & 0x40))
+    {
+        currBlock = cData[1];
+        blockPos = RB; // Reset block position offset
+    } else return 1; // Last block
+    
+    return 0; // OK
+}
+
+// Respects mEOF and automatically sets '\0' at the end of string
+void file::read(char *data, unsigned int n)
+{
+    unsigned int i;
+    char cData[2];
+    
+    needsFlush();
+    
+    for (i=0; i < n; i++)
+    {
+        // Change block?
+        if (blockPos == BS-1)
+        {
+            // Fetch link to next block
+            fs->read(cData, currBlock, 0, 2);
+            if (!(cData[0] & 0x40))
+            {
+                currBlock = cData[1];
+                blockPos = RB; // Reset block position offset
+            } else goto stop;
+        }
+        
+        // Read data
+        fs->read(cData, currBlock, blockPos, 1);
+        if (cData[0] == mEOF)
+        {
+            stop:
+            data[i]='\0';
+            return;
+        } else data[i] = cData[0];
+        
+        blockPos++;
+    }
+    //data[i-1] = '\0'; // Is this needed?
+}
+
+// Ignores mEOF and doesn't set '\0' markings
+void file::readBin(char *data, unsigned int n)
+{
+    unsigned int i;
+    char cData[2];
+    
+
+    
+    for (i=0; i < n; i++)
+    {
+        // Change block?
+        if (blockPos == BS-1)
+        {
+            // Fetch link to next block
+            fs->read(cData, currBlock, 0, 2);
+            if (!(cData[0] & 0x40))
+            {
+                currBlock = cData[1];
+                blockPos = RB; // Reset block position offset
+            } else return;
+        }
+        
+        // Read data
+        fs->read(cData, currBlock, blockPos, 1);
+        data[i] = cData[0];
+        
+        blockPos++;
+    }
+    data[i-1] = '\0';
+}
+
+// Always binary
+char file::write(char *data, unsigned int n)
+{
+    if (attr == 0) return 1;
+    
+    for (unsigned int i=0; i < n; i++)
+    {
+        // write to the buffer
+        buffer[bufPos] = data[i];
+        bufPos++;
+        
+        // If the buffer is full then flush
+        if(bufPos == BUF)
+        {
+            if(flush() != 0);
+                return 1; // Flush failed
+        }
+    }
+    
+    return 0;
+}
+
+char file::flush()
+{
+    char cData[3], c[1];
+    char nextFree;
+    unsigned int i;
+    
+    if (bufPos == 0) return 0; // File up-to date
+    if (attr == 0) return 1;
+    
+    for (i=0; i < bufPos; i++)
+    {
+        if(i%2)
+            FlushLed = 1;
+        else
+            FlushLed = 0;
+        
+        // Change Block?
+        if (blockPos >= BS-2) // Must left space for mEOF
+        {
+            // Fetch new unused block number
+            if(fs->getNextFreeBlock(&nextFree) != 0)
+                return 1;
+            
+            // Link old block with new block
+            fs->read(cData, currBlock, 0, 3);
+            cData[0] &= ~0x40; // Clear LBOF flag if set
+            cData[1] = nextFree;
+            cData[2] = '\0';
+            fs->write(cData, currBlock, 0, 3); // Update Block Data
+            
+            // Take new block into use
+            blockPos = RB; // Reset block position offset
+            cData[0] = 0x4C; // New flags
+            cData[1] = '\0';
+            cData[2] = currBlock; // Prev Block
+            currBlock = nextFree; // Change current block to new one
+            fs->write(cData, currBlock, 0, 3); // Update Block Data
+        }
+        
+        if (blockPos < 3)
+            error("");
+        
+        // Write file
+        c[0]=buffer[i];
+        fs->write(c, currBlock, blockPos, 1);
+        blockPos++;
+    }
+    // Write mEOF
+    fs->write((char[]){mEOF}, currBlock, blockPos+1, 1);
+    
+    bufPos = 0; // Reset buffer position counter
+    
+    FlushLed = 0;
+    return 0;
+}
\ No newline at end of file