mFS file system library for EEPROM memory chips.
Diff: mfs.cpp
- 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