mFS file system library for EEPROM memory chips.
mfs.cpp
- Committer:
- HBP
- Date:
- 2011-02-24
- Revision:
- 13:142b6be3e3c8
- Parent:
- 12:928346513c87
File content as of revision 13:142b6be3e3c8:
/** @file mfs.cpp */ /*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 BLOCK_LLEN 2 /**< Block number link length in bytes */ #define RB 1+2*BLOCK_LLEN /**< Reseved bytes per block (1 attrb B, 2 B for next/prev pointers */ #define I2C_SPEED 200000 /**< I2C bus speed in Hz */ #define DEBUG /**< Adds extra safety in reading and writing */ DigitalOut FlushLed(LED2); /**< Flush led */ mfs::mfs(int i2c_address) { mem = new i2c_eeprom(i2c_address, I2C_SPEED); } char mfs::read(char *data, uint32_t block, uint32_t byte, uint32_t n) { // Faster reading without DEBUG mode #ifdef DEBUG if ((byte+n-1 >= BS)) return 1; #endif mem->read(BS*block+byte, n, data); return 0; } char mfs::write(char *data, uint32_t block, uint32_t byte, uint32_t 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(uint32_t *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; } char mfs::findNextFile(uint32_t block, char *filenameOut, uint32_t *blockOut) { uint32_t 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 1; // Empty fs } // Read filename read(filenameOut, i, RB, 20); *blockOut = i; // Return block number return 0; } char mfs::getFirstBlockOfFile(char filename[20], uint32_t *blockOut) { *blockOut=0; char tmpFilename[20]=""; while (1) { if (findNextFile(*blockOut, tmpFilename, blockOut) == 0) { if(strcmp(tmpFilename, filename) == 0) return 0; // File exists } else return 1; // File doesn't exist (*blockOut)++; } } char mfs::createFile(char filename[20]) { char tmpFilename[20]; uint32_t n; uint32_t fb; for (n=0; n < BC; n++) { if(findNextFile(n, tmpFilename, &n) == 0) { if(strcmp(tmpFilename, filename) == 0) return 1; // File exist } else break; // We already reached the edge of the universe n++; } if(getNextFreeBlock(&fb) != 0) return 2; // Out of space char cData[RB+20+1]; cData[0] = '\xCC'; // Necessary flags for a file cData[1] = '\0'; // No more blocks yet cData[2] = '\0'; cData[3] = '\0'; // First block so there is no prev blocks cData[4] = '\0'; cData[RB+20] = mEOF; // Set EOF at the begining for (char i=0; i < 20; i++) cData[RB+i] = filename[i]; // Create file write(cData, fb, 0, RB+20+1); return 0; } char mfs::removeFile(char filename[20]) { uint32_t block; char cData[RB-BLOCK_LLEN]; char cDataNew[RB] = {'\x04', '\0', '\0', '\0', '\0'}; uint32_t i=0; // Check if file exists if (getFirstBlockOfFile(filename, &block) != 0) return 1; // File not found read(cData, block, 0, RB-BLOCK_LLEN); // Check credentials if (((cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3) & 0x04) != 0) return 2; // RO file // Clear blocks reserved by the file while(1) { write(cDataNew, block, 0, RB); if ((cData[0] & 0x4C) == 0x4C) break; // End of file found else block = (uint32_t)(cData[1])<<8|cData[2]; // Set next block number i++; if (i > BC) return 1; // fs is corrupted read(cData, block, 0, RB-BLOCK_LLEN); } return 0; // Everything went better than expected } char mfs::renameFile(char oldFilename[20], char newFilename[20]) { uint32_t block; char cData[1]; // Check if file exists if (getFirstBlockOfFile(oldFilename, &block) != 0) return 1; // File not found // Check credentials read(cData, block, 0, 1); char flags = (cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3); if ((flags & 0x04) != 0) return 2; // RO file write(newFilename, block, RB, 20); return 0; // Everything went better than expected } char mfs::setFileFlags(char *flags, char filename[20]) { /* RO|HIDDEN|LOCK * * H L */ uint32_t n; char cData[1] = {'\0'}; char cFlags; // Check if file exists if (getFirstBlockOfFile(filename, &n) != 0) return 1; // File not found read(cData, n, 0, 1); cFlags = ((flags[0] & 0x01)|((flags[0] & 0x02) << 3)|((flags[0] & 0x04) << 3)); cData[0] = cData[0] & (~0x31) | cFlags; write(cData, n, 0, 1); return 0; } char mfs::getFileFlags(char *flags, char filename[20]) { /* RO|HIDDEN|LOCK * * H L */ uint32_t n; char cData[1] = {'\0'}; // Check if file exists if (getFirstBlockOfFile(filename, &n) != 0) 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 uint32_t mfs::free() { uint32_t blocks=0; uint32_t 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; } uint32_t mfs::mkfs(bool createLabel) { uint32_t iAddr = 0; uint32_t i = 0; uint32_t bad = 0; // For counting bad block headers char cFlags[RB] = {'\0', '\0', '\0', '\0', '\0'}, o[1]; if (createLabel == true) { // Write Volume label cFlags[0] = '\x0E'; mem->write(cFlags, iAddr, RB); iAddr = BS; i = 1; } cFlags[0] = '\x04'; for (; i < BC; i++) { mem->write(cFlags, iAddr, RB); mem->read(iAddr, 1, o); if (o[0] != cFlags[0]) bad++; iAddr += BS; } return bad; } file::file(mfs *fs_ref, char filename[20], FileOpenMode operation) { fMode = operation; fs = fs_ref; // Don't forget this :) uint32_t n; char cData[1] = {'\0'}; // Check if file exists if (fs->getFirstBlockOfFile(filename, &n) != 0) 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 ((fMode != RO) && ((flags & 0x04) != 0)) error("Oops, cant open in RW mode!"); // Store FBOF number firstBlock = n; currBlock = n; blockPos = RB+20; // skip flags + pointers + filename byteCount = 0; // First byte of the file // Initialize buffer for (unsigned int i=0; i < BUF; i++) buffer[i] = '\0'; bufPos = 0; } file::~file() { flush(); } char file::getBlockLink(BlockLinkType linkSelection, uint32_t *blockOut) { char cData[1+BLOCK_LLEN]; if (linkSelection == NEXT) { // Fetch link to next block fs->read(cData, currBlock, 0, 1+BLOCK_LLEN); if ((cData[0] & 0x40) == 0) { *blockOut = ((uint32_t)(cData[1])) << 8; // Hbyte of next block link *blockOut |= (uint32_t)cData[2]; // Lbyte of next block link return 0; } else return 1; // Already at last block } else if (linkSelection == PREV) { if (currBlock != firstBlock) { fs->read(cData, currBlock, 1+BLOCK_LLEN, BLOCK_LLEN); *blockOut = ((uint32_t)(cData[0])) << 8; // Hbyte of next block link *blockOut |= (uint32_t)cData[1]; // Lbyte of next block link return 0; } else return 1; // Already at first block } return 0; } char file::removeFollowingBlocks(uint32_t block) { char cData[RB-BLOCK_LLEN]; char cDataNew[RB] = {'\x04', '\0', '\0', '\0', '\0'}; uint32_t i=0; while(1) { fs->read(cData, block, 0, RB-BLOCK_LLEN); fs->write(cDataNew, block, 0, RB); if ((cData[0] & 0x4C) == 0x4C) break; // End of file found else block = (uint32_t)(cData[0])<<8|cData[1]; // Set next block number i++; if (i > BC) return 1; // fs is corrupted } return 0; } void file::rewind() { flush(); currBlock = firstBlock; blockPos = RB+20; // skip flags & pointers + filename byteCount = 0; } char file::rewind(uint32_t n) { uint32_t i; uint32_t block; uint32_t varBlockOffset; flush(); // Check if flush is needed for (i=0; i < n; i++) { blockPos--; byteCount--; // Change Block? if (blockPos == firstBlock) varBlockOffset = RB+20; else varBlockOffset = RB; if ((blockPos < varBlockOffset) && (currBlock > firstBlock)) { // Fetch link to previous block if(getBlockLink(PREV, &block) == 0) { currBlock = block; blockPos = BS-1; // blockPos should be set at the end of block } else { blockPos++; byteCount++; return 1; // This is the first block and byte } } } return 0; // OK } char file::forward() { return forward(1); } char file::forward(uint32_t n) { uint32_t i; uint32_t block; // Next block number char cData[1]; flush(); // Check if flush is needed for (i=0; i < n; i++) { // If this is empty file EOF will be at first byte fs->read(cData, currBlock, blockPos, 1); if (cData[0] == mEOF) { return 1; } blockPos++; byteCount++; // Change Block? if (blockPos >= BS) { // Fetch link to next block if (getBlockLink(NEXT, &block) == 0) { currBlock = block; blockPos = RB; // Reset block position offset } else { blockPos--; byteCount--; return 1; // This is the last block & byte } } fs->read(cData, currBlock, blockPos, 1); if (cData[0] == mEOF) { rewind(1); // Get back to the byte before EOF return 1; } } return 0; // OK } char file::seek(uint32_t byte) { if (byte > byteCount) return forward(byte-byteCount); else if (byte < byteCount) return rewind(byteCount-byte); else return 0; } // Respects mEOF and automatically sets '\0' at the end of string void file::read(char *data, uint32_t n) { uint32_t i; uint32_t block; char cData[1]; flush(); for (i=0; i < n; i++) { // Change block? if (blockPos >= BS-1) { // Fetch link to next block if (getBlockLink(NEXT, &block) == 0) { currBlock = block; 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++; byteCount++; } } if (data[n-1] != '\0') data[n-1] = '\0'; } // Ignores mEOF and doesn't set '\0' markings void file::readBin(char *data, uint32_t n) { uint32_t i; uint32_t block; char cData[1]; for (i=0; i < n; i++) { // Change block? if (blockPos == BS-1) { // Fetch link to next block if (getBlockLink(NEXT, &block) == 0) { currBlock = block; blockPos = RB; // Reset block position offset } else return; } // Read data fs->read(cData, currBlock, blockPos, 1); data[i] = cData[0]; blockPos++; byteCount++; } } // Always binary char file::write(char *data, uint32_t n) { if (fMode == RO) return 1; for (uint32_t 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[RB], cDataOB[RB-BLOCK_LLEN], c[1]; uint32_t nextFree; uint32_t i; uint32_t leftSpaceEOF; bool destructiveFlag = false; // Set this true if there is any data to be removed uint32_t destructiveBlock=0; // First block to be removed by DWRITE static bool fEOFow = false; /* END of file found while appending (and overwritten). Must be static because we don't want to found many EOF's. */ if (bufPos == 0) return 0; // File up-to date if (fMode == RO) return 1; for (i=0; i <= bufPos; i++) { if(i%2) FlushLed = 1; else FlushLed = 0; // Change Block? if ((bufPos - i) == 1) leftSpaceEOF = 1; else leftSpaceEOF = 0; if (blockPos >= BS-leftSpaceEOF) { // Fetch new unused block number if(fs->getNextFreeBlock(&nextFree) != 0) return 2; // No free space left // Read flags from current block fs->read(cDataOB, currBlock, 0, RB-BLOCK_LLEN); /* If destructive write is set then check if there is something to be marked for removal */ if (((cDataOB[0] & 0x40) != 0x40) && (fMode == DWRITE)) { destructiveFlag = true; destructiveBlock = (uint32_t)cDataOB[1]<<8|cDataOB[2]; goto allocate_new_block; } else if ((cDataOB[0] & 0x40) != 0x40) // fMode == AWRITE { // Update current block info currBlock = (uint32_t)cDataOB[1]<<8|cDataOB[2]; blockPos = RB; // Reset block position offset } else // There is no block to append so we allocate a new one { allocate_new_block: // Allocate new block for use cData[0] = 0x4C; // New flags cData[1] = '\0'; // Hbyte of Next Block link cData[2] = '\0'; // Lbyte of Next Block link cData[3] = (char)((currBlock & 0xff00) >> 8); // Hbyte of Prev Block link cData[4] = (char)(currBlock & 0x00ff); // Lbyte of Prev Block link fs->write(cData, nextFree, 0, RB); // Update Block Data // Link old block with new block cDataOB[0] &= ~0x40; // Clear LBOF flag if set cDataOB[1] = (char)((nextFree & 0xff00) >> 8); // Hbyte of Next Block link cDataOB[2] = (char)(nextFree & 0x00ff); // Lbyte of Next Block link fs->write(cDataOB, currBlock, 0, RB-BLOCK_LLEN); // Update Block Data // Update current block info currBlock = nextFree; blockPos = RB; // Reset block position offset } } if (fMode == AWRITE) // Check if EOF is here { fs->read(c, currBlock, blockPos, 1); if ((c[0] == mEOF) && (fEOFow == false)) fEOFow = true; } // Write file c[0]=buffer[i]; fs->write(c, currBlock, blockPos, 1); blockPos++; byteCount++; // For fail safe, write EOF now if ((fMode == DWRITE)||(fEOFow == true)) fs->write((char[]){mEOF}, currBlock, blockPos, 1); // Write mEOF } bufPos = 0; // Reset buffer position counter /* If destructive write flag is set and there is data to be removed then remove data now */ if (destructiveFlag == true) if(removeFollowingBlocks(destructiveBlock) != 0) return 3; FlushLed = 0; return 0; }