Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
ICE-Application/src/add-ons/ConfigFS/mfs.cpp
- Committer:
- jmarkel44
- Date:
- 2017-01-24
- Revision:
- 0:61364762ee0e
File content as of revision 0:61364762ee0e:
/** @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 MFS_I2C_SPEED 100000 /**< I2C bus speed in Hz */
#define DEBUG /**< Adds extra safety in reading and writing */
mfs::mfs(int i2c_address)
{
mem = new i2c_eeprom(i2c_address, MFS_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, FILENAME_LENGTH);
*blockOut = i; // Return block number
return 0;
}
char mfs::getFirstBlockOfFile(char filename[FILENAME_LENGTH], uint32_t *blockOut)
{
*blockOut=0;
char tmpFilename[FILENAME_LENGTH]="";
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[FILENAME_LENGTH])
{
char tmpFilename[FILENAME_LENGTH];
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+FILENAME_LENGTH+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+FILENAME_LENGTH] = mEOF; // Set EOF at the begining
for (char i=0; i < FILENAME_LENGTH; i++)
cData[RB+i] = filename[i];
// Create file
write(cData, fb, 0, RB+FILENAME_LENGTH+1);
return 0;
}
char mfs::removeFile(char filename[FILENAME_LENGTH])
{
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[FILENAME_LENGTH], char newFilename[FILENAME_LENGTH])
{
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, FILENAME_LENGTH);
return 0; // Everything went better than expected
}
char mfs::setFileFlags(char *flags, char filename[FILENAME_LENGTH])
{
/* 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[FILENAME_LENGTH])
{
/* 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[FILENAME_LENGTH], 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+FILENAME_LENGTH; // 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+FILENAME_LENGTH; // 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+FILENAME_LENGTH;
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];
if( flush() != 0 ) {
printf("(%s:%d): flush failed\r\n", __func__, __LINE__);
}
for (i=0; i < n; i++)
{
// Change block?
if (blockPos >= BS-1)
{
// printf("(%s:%d): pos=%d, BS-1=%d\r\n", __func__, __LINE__, 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)
{
// printf("(%s:%d): Found EOF: currBlock=%d, blockPos=%d\r\n", __func__, __LINE__, currBlock, blockPos);
stop:
data[i]='\0';
return;
} else {
data[i] = cData[0];
// printf("(%s:%d): blk=%d, pos=%d, char=%c\r\n", __func__, __LINE__, currBlock, blockPos, data[i]);
blockPos++;
byteCount++;
}
}
if (data[n-1] != '\0') {
printf("(%s:%d): Adding NULL\r\n", __func__, __LINE__ );
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)
{
printf("(%s:%d): flushing buffer: bufPos=%d\r\n", __func__, __LINE__, bufPos);
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( blockPos == 255 )
{
// printf("%s:%d:\r\n",__func__, __LINE__);
}
// Change Block?
if ((bufPos - i) == 1)
leftSpaceEOF = 1;
else
leftSpaceEOF = 0;
if (blockPos+1 >= BS-leftSpaceEOF)
{
// printf("(%s:%d): fetching unused block: currBlock=%d, blockPos=%d, bufPos=%d\r\n", __func__, __LINE__, currBlock, blockPos, bufPos );
// 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];
// printf("(%s:%d): destructive flag set: currBlock=%d, blockPos=%d, bufPos=%d\r\n", __func__, __LINE__, currBlock, blockPos, bufPos );
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
// printf("(%s:%d): resetting block position: currBlock=%d, blockPos=%d, bufPos=%d\r\n", __func__, __LINE__, currBlock, blockPos, bufPos );
} 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
// printf("(%s:%d): allocating new block: currBlock=%d, blockPos=%d, bufPos=%d\r\n", __func__, __LINE__, currBlock, blockPos, bufPos );
}
}
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);
// printf("(%s:%d): blk=%d, pos=%d, char=%c\r\n", __func__, __LINE__, currBlock, blockPos, c[0] );
blockPos++;
byteCount++;
// For fail safe, write EOF now
if ((fMode == DWRITE)||(fEOFow == true))
{
char fs_data[] = { mEOF };
fs->write(fs_data, currBlock, blockPos, 1); // Write mEOF
// printf("(%s:%d): Writing EOF, blk=%d, pos=%d, char=0x%x\r\n", __func__, __LINE__, currBlock, blockPos, fs_data[0] );
}
}
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;
}
}
return 0;
}