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.
Diff: ICE-Application/src/add-ons/ConfigFS/mfs.cpp
- Revision:
- 0:61364762ee0e
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ICE-Application/src/add-ons/ConfigFS/mfs.cpp Tue Jan 24 19:05:33 2017 +0000
@@ -0,0 +1,686 @@
+/** @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;
+}