mFS file system library for EEPROM memory chips.
mfs.cpp
- Committer:
- HBP
- Date:
- 2011-02-21
- Revision:
- 7:5ac5121bb4e0
- Parent:
- 5:a0fe74dce80d
- Child:
- 9:52c01cb100ac
File content as of revision 7:5ac5121bb4e0:
/*CPP************************************************************************** * FILENAME : mfs.cpp * * * * DESCRIPTION : * * mFS file system implementation for mBED with external I2C EEEPROM. * * * * AUTHOR : Olli Vanhoja START DATE : 2011-02-21 * *****************************************************************************/ #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, 200000); } 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_ms(1); 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::renameFile(char oldFilename[20], char newFilename[20]) { unsigned int block; // Check if file exists block = getFirstBlockOfFile(oldFilename); if (block > BC) return 1; // File not found write(newFilename, block, 3, 20); 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() { // Flush file if needed and return to current position 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::rewind(uint16 n) { uint16 i; char cData[3]; needsFlush(); // Check if flush is needed for (i=0; i < n; i++) { blockPos--; // Change Block? if (blockPos < 3) { // Fetch link to next block fs->read(cData, currBlock, 0, 3); if (!(cData[0] & 0x80)) { currBlock = cData[2]; blockPos = BS-1; // Set block postion offset at end of the block } else { blockPos++; return 1; // This is the last block } } fs->read(cData, currBlock, blockPos, 1); if (cData[0] == mEOF) return 1; } return 0; // OK } char file::forward() { return forward(1); } char file::forward(uint16 n) { uint16 i; char cData[2]; needsFlush(); // Check if flush is needed for (i=0; i < n; i++) { blockPos++; // 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 { blockPos--; return 1; // This is the last block } } fs->read(cData, currBlock, blockPos, 1); if (cData[0] == mEOF) return 1; } 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++; } } if (data[i] != '\0') data[i] = '\0'; } // 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; }