mFS file system library for EEPROM memory chips.
Embed:
(wiki syntax)
Show/hide line numbers
mfs.cpp
Go to the documentation of this file.
00001 /** @file mfs.cpp */ 00002 /*CPP************************************************************************** 00003 * FILENAME : mfs.cpp * 00004 * * 00005 * DESCRIPTION : * 00006 * mFS file system implementation for mBED with external I2C EEEPROM. * 00007 * * 00008 * AUTHOR : Olli Vanhoja START DATE : 2011-02-21 * 00009 *****************************************************************************/ 00010 00011 #include "mbed.h" 00012 #include "mfs.h " 00013 #include "i2c_eeprom.h" 00014 00015 #define BLOCK_LLEN 2 /**< Block number link length in bytes */ 00016 #define RB 1+2*BLOCK_LLEN /**< Reseved bytes per block (1 attrb B, 2 B for next/prev pointers */ 00017 #define I2C_SPEED 200000 /**< I2C bus speed in Hz */ 00018 #define DEBUG /**< Adds extra safety in reading and writing */ 00019 00020 DigitalOut FlushLed(LED2); /**< Flush led */ 00021 00022 mfs::mfs(int i2c_address) 00023 { 00024 mem = new i2c_eeprom(i2c_address, I2C_SPEED); 00025 } 00026 00027 char mfs::read(char *data, uint32_t block, uint32_t byte, uint32_t n) 00028 { 00029 // Faster reading without DEBUG mode 00030 #ifdef DEBUG 00031 if ((byte+n-1 >= BS)) 00032 return 1; 00033 #endif 00034 mem->read(BS*block+byte, n, data); 00035 00036 return 0; 00037 } 00038 00039 char mfs::write(char *data, uint32_t block, uint32_t byte, uint32_t n) 00040 { 00041 // Faster writing without DEBUG mode 00042 #ifdef DEBUG 00043 if (byte+n >= BS) 00044 return 1; 00045 #endif 00046 mem->write(data, BS*block+byte, n); 00047 00048 return 0; 00049 } 00050 00051 char mfs::getNextFreeBlock(uint32_t *blockOut) 00052 { 00053 // Locate free block by seeking EERPOM 00054 char cFlags[1]; 00055 00056 for (*blockOut=0; *blockOut < BC; (*blockOut)++) 00057 { 00058 read(cFlags, *blockOut, 0, 1); 00059 if (cFlags[0] == 0x04) 00060 break; 00061 if (*blockOut >= BC-1) 00062 return 1; 00063 } 00064 00065 return 0; 00066 } 00067 00068 char mfs::findNextFile(uint32_t block, char *filenameOut, uint32_t *blockOut) 00069 { 00070 uint32_t i=block; 00071 char cFlags[1]; 00072 00073 while (i < BC) 00074 { 00075 read(cFlags, i, 0, 1); 00076 00077 if ((cFlags[0] & 0x8C) == 0x8C) 00078 break; // File found 00079 else 00080 i++; 00081 } 00082 00083 if(i == BC) 00084 { 00085 strcpy(filenameOut, ""); 00086 return 1; // Empty fs 00087 } 00088 00089 // Read filename 00090 read(filenameOut, i, RB, 20); 00091 *blockOut = i; // Return block number 00092 return 0; 00093 } 00094 00095 char mfs::getFirstBlockOfFile(char filename[20], uint32_t *blockOut) 00096 { 00097 *blockOut=0; 00098 char tmpFilename[20]=""; 00099 00100 while (1) 00101 { 00102 if (findNextFile(*blockOut, tmpFilename, blockOut) == 0) 00103 { 00104 if(strcmp(tmpFilename, filename) == 0) 00105 return 0; // File exists 00106 } 00107 else return 1; // File doesn't exist 00108 (*blockOut)++; 00109 } 00110 } 00111 00112 char mfs::createFile(char filename[20]) 00113 { 00114 char tmpFilename[20]; 00115 uint32_t n; 00116 uint32_t fb; 00117 00118 for (n=0; n < BC; n++) 00119 { 00120 if(findNextFile(n, tmpFilename, &n) == 0) 00121 { 00122 if(strcmp(tmpFilename, filename) == 0) 00123 return 1; // File exist 00124 } 00125 else break; // We already reached the edge of the universe 00126 n++; 00127 } 00128 00129 if(getNextFreeBlock(&fb) != 0) 00130 return 2; // Out of space 00131 00132 char cData[RB+20+1]; 00133 cData[0] = '\xCC'; // Necessary flags for a file 00134 cData[1] = '\0'; // No more blocks yet 00135 cData[2] = '\0'; 00136 cData[3] = '\0'; // First block so there is no prev blocks 00137 cData[4] = '\0'; 00138 cData[RB+20] = mEOF; // Set EOF at the begining 00139 00140 for (char i=0; i < 20; i++) 00141 cData[RB+i] = filename[i]; 00142 00143 // Create file 00144 write(cData, fb, 0, RB+20+1); 00145 00146 return 0; 00147 } 00148 00149 char mfs::removeFile(char filename[20]) 00150 { 00151 uint32_t block; 00152 char cData[RB-BLOCK_LLEN]; 00153 char cDataNew[RB] = {'\x04', '\0', '\0', '\0', '\0'}; 00154 uint32_t i=0; 00155 00156 // Check if file exists 00157 if (getFirstBlockOfFile(filename, &block) != 0) 00158 return 1; // File not found 00159 00160 read(cData, block, 0, RB-BLOCK_LLEN); 00161 00162 // Check credentials 00163 if (((cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3) & 0x04) != 0) 00164 return 2; // RO file 00165 00166 // Clear blocks reserved by the file 00167 while(1) 00168 { 00169 write(cDataNew, block, 0, RB); 00170 if ((cData[0] & 0x4C) == 0x4C) 00171 break; // End of file found 00172 else block = (uint32_t)(cData[1])<<8|cData[2]; // Set next block number 00173 i++; 00174 if (i > BC) 00175 return 1; // fs is corrupted 00176 read(cData, block, 0, RB-BLOCK_LLEN); 00177 } 00178 00179 return 0; // Everything went better than expected 00180 } 00181 00182 char mfs::renameFile(char oldFilename[20], char newFilename[20]) 00183 { 00184 uint32_t block; 00185 char cData[1]; 00186 00187 // Check if file exists 00188 if (getFirstBlockOfFile(oldFilename, &block) != 0) 00189 return 1; // File not found 00190 00191 // Check credentials 00192 read(cData, block, 0, 1); 00193 char flags = (cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3); 00194 if ((flags & 0x04) != 0) 00195 return 2; // RO file 00196 00197 write(newFilename, block, RB, 20); 00198 00199 return 0; // Everything went better than expected 00200 } 00201 00202 char mfs::setFileFlags(char *flags, char filename[20]) 00203 { 00204 /* RO|HIDDEN|LOCK * 00205 * H L */ 00206 00207 uint32_t n; 00208 char cData[1] = {'\0'}; 00209 char cFlags; 00210 00211 // Check if file exists 00212 if (getFirstBlockOfFile(filename, &n) != 0) 00213 return 1; // File not found 00214 00215 read(cData, n, 0, 1); 00216 cFlags = ((flags[0] & 0x01)|((flags[0] & 0x02) << 3)|((flags[0] & 0x04) << 3)); 00217 cData[0] = cData[0] & (~0x31) | cFlags; 00218 write(cData, n, 0, 1); 00219 00220 return 0; 00221 } 00222 00223 char mfs::getFileFlags(char *flags, char filename[20]) 00224 { 00225 /* RO|HIDDEN|LOCK * 00226 * H L */ 00227 00228 uint32_t n; 00229 char cData[1] = {'\0'}; 00230 00231 // Check if file exists 00232 if (getFirstBlockOfFile(filename, &n) != 0) 00233 return 1; // File not found 00234 00235 read(cData, n, 0, 1); 00236 flags[0] = (cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3); 00237 00238 return 0; 00239 } 00240 00241 // Return number of free blocks 00242 uint32_t mfs::free() 00243 { 00244 uint32_t blocks=0; 00245 uint32_t r; 00246 char cFlags[1]; 00247 00248 for (r=0; r < BC; r++) 00249 { 00250 read(cFlags, r, 0, 1); 00251 if (cFlags[0] == 0x04) 00252 blocks++; 00253 if (r >= BC-1) 00254 return blocks; 00255 } 00256 00257 return 0; 00258 } 00259 00260 uint32_t mfs::mkfs(bool createLabel) 00261 { 00262 uint32_t iAddr = 0; 00263 uint32_t i = 0; 00264 uint32_t bad = 0; // For counting bad block headers 00265 char cFlags[RB] = {'\0', '\0', '\0', '\0', '\0'}, o[1]; 00266 00267 if (createLabel == true) 00268 { 00269 // Write Volume label 00270 cFlags[0] = '\x0E'; 00271 mem->write(cFlags, iAddr, RB); 00272 iAddr = BS; 00273 i = 1; 00274 } 00275 00276 cFlags[0] = '\x04'; 00277 for (; i < BC; i++) 00278 { 00279 mem->write(cFlags, iAddr, RB); 00280 mem->read(iAddr, 1, o); 00281 if (o[0] != cFlags[0]) 00282 bad++; 00283 iAddr += BS; 00284 } 00285 00286 return bad; 00287 } 00288 00289 file::file(mfs *fs_ref, char filename[20], FileOpenMode operation) 00290 { 00291 fMode = operation; 00292 00293 fs = fs_ref; // Don't forget this :) 00294 00295 uint32_t n; 00296 char cData[1] = {'\0'}; 00297 00298 // Check if file exists 00299 if (fs->getFirstBlockOfFile(filename, &n) != 0) 00300 error("Oops, file \"%s\" not found! n=0x%X", filename, n); // File not found 00301 00302 fs->read(cData, n, 0, 1); 00303 char flags = (cData[0] & 0x01)|((cData[0] & 0x10) >> 3)|((cData[0] & 0x20) >> 3); 00304 00305 if ((fMode != RO) && ((flags & 0x04) != 0)) 00306 error("Oops, cant open in RW mode!"); 00307 00308 // Store FBOF number 00309 firstBlock = n; 00310 currBlock = n; 00311 blockPos = RB+20; // skip flags + pointers + filename 00312 byteCount = 0; // First byte of the file 00313 00314 // Initialize buffer 00315 for (unsigned int i=0; i < BUF; i++) 00316 buffer[i] = '\0'; 00317 bufPos = 0; 00318 } 00319 00320 file::~file() 00321 { 00322 flush(); 00323 } 00324 00325 char file::getBlockLink(BlockLinkType linkSelection, uint32_t *blockOut) 00326 { 00327 char cData[1+BLOCK_LLEN]; 00328 00329 if (linkSelection == NEXT) 00330 { 00331 // Fetch link to next block 00332 fs->read(cData, currBlock, 0, 1+BLOCK_LLEN); 00333 if ((cData[0] & 0x40) == 0) 00334 { 00335 *blockOut = ((uint32_t)(cData[1])) << 8; // Hbyte of next block link 00336 *blockOut |= (uint32_t)cData[2]; // Lbyte of next block link 00337 return 0; 00338 } else return 1; // Already at last block 00339 } else if (linkSelection == PREV) 00340 { 00341 if (currBlock != firstBlock) 00342 { 00343 fs->read(cData, currBlock, 1+BLOCK_LLEN, BLOCK_LLEN); 00344 *blockOut = ((uint32_t)(cData[0])) << 8; // Hbyte of next block link 00345 *blockOut |= (uint32_t)cData[1]; // Lbyte of next block link 00346 return 0; 00347 } else return 1; // Already at first block 00348 } 00349 00350 return 0; 00351 } 00352 00353 char file::removeFollowingBlocks(uint32_t block) 00354 { 00355 char cData[RB-BLOCK_LLEN]; 00356 char cDataNew[RB] = {'\x04', '\0', '\0', '\0', '\0'}; 00357 uint32_t i=0; 00358 00359 while(1) 00360 { 00361 fs->read(cData, block, 0, RB-BLOCK_LLEN); 00362 fs->write(cDataNew, block, 0, RB); 00363 if ((cData[0] & 0x4C) == 0x4C) 00364 break; // End of file found 00365 else block = (uint32_t)(cData[0])<<8|cData[1]; // Set next block number 00366 i++; 00367 if (i > BC) 00368 return 1; // fs is corrupted 00369 } 00370 00371 return 0; 00372 } 00373 00374 void file::rewind() 00375 { 00376 flush(); 00377 currBlock = firstBlock; 00378 blockPos = RB+20; // skip flags & pointers + filename 00379 byteCount = 0; 00380 } 00381 00382 char file::rewind(uint32_t n) 00383 { 00384 uint32_t i; 00385 uint32_t block; 00386 uint32_t varBlockOffset; 00387 00388 flush(); // Check if flush is needed 00389 00390 for (i=0; i < n; i++) 00391 { 00392 blockPos--; 00393 byteCount--; 00394 00395 // Change Block? 00396 if (blockPos == firstBlock) 00397 varBlockOffset = RB+20; 00398 else 00399 varBlockOffset = RB; 00400 if ((blockPos < varBlockOffset) && (currBlock > firstBlock)) 00401 { 00402 // Fetch link to previous block 00403 if(getBlockLink(PREV, &block) == 0) 00404 { 00405 currBlock = block; 00406 blockPos = BS-1; // blockPos should be set at the end of block 00407 } else { 00408 blockPos++; 00409 byteCount++; 00410 return 1; // This is the first block and byte 00411 } 00412 } 00413 } 00414 00415 return 0; // OK 00416 } 00417 00418 char file::forward() 00419 { 00420 return forward(1); 00421 } 00422 00423 char file::forward(uint32_t n) 00424 { 00425 uint32_t i; 00426 uint32_t block; // Next block number 00427 char cData[1]; 00428 00429 flush(); // Check if flush is needed 00430 00431 for (i=0; i < n; i++) 00432 { 00433 // If this is empty file EOF will be at first byte 00434 fs->read(cData, currBlock, blockPos, 1); 00435 if (cData[0] == mEOF) 00436 { 00437 return 1; 00438 } 00439 00440 blockPos++; 00441 byteCount++; 00442 00443 // Change Block? 00444 if (blockPos >= BS) 00445 { 00446 // Fetch link to next block 00447 if (getBlockLink(NEXT, &block) == 0) 00448 { 00449 currBlock = block; 00450 blockPos = RB; // Reset block position offset 00451 } else { 00452 blockPos--; 00453 byteCount--; 00454 return 1; // This is the last block & byte 00455 } 00456 } 00457 fs->read(cData, currBlock, blockPos, 1); 00458 if (cData[0] == mEOF) 00459 { 00460 rewind(1); // Get back to the byte before EOF 00461 return 1; 00462 } 00463 } 00464 00465 return 0; // OK 00466 } 00467 00468 char file::seek(uint32_t byte) 00469 { 00470 if (byte > byteCount) 00471 return forward(byte-byteCount); 00472 else if (byte < byteCount) 00473 return rewind(byteCount-byte); 00474 else return 0; 00475 } 00476 00477 // Respects mEOF and automatically sets '\0' at the end of string 00478 void file::read(char *data, uint32_t n) 00479 { 00480 uint32_t i; 00481 uint32_t block; 00482 char cData[1]; 00483 00484 flush(); 00485 00486 for (i=0; i < n; i++) 00487 { 00488 // Change block? 00489 if (blockPos >= BS-1) 00490 { 00491 // Fetch link to next block 00492 if (getBlockLink(NEXT, &block) == 0) 00493 { 00494 currBlock = block; 00495 blockPos = RB; // Reset block position offset 00496 } else goto stop; 00497 } 00498 00499 // Read data 00500 fs->read(cData, currBlock, blockPos, 1); 00501 if (cData[0] == mEOF) 00502 { 00503 stop: 00504 data[i]='\0'; 00505 return; 00506 } else { 00507 data[i] = cData[0]; 00508 blockPos++; 00509 byteCount++; 00510 } 00511 } 00512 if (data[n-1] != '\0') 00513 data[n-1] = '\0'; 00514 } 00515 00516 // Ignores mEOF and doesn't set '\0' markings 00517 void file::readBin(char *data, uint32_t n) 00518 { 00519 uint32_t i; 00520 uint32_t block; 00521 char cData[1]; 00522 00523 for (i=0; i < n; i++) 00524 { 00525 // Change block? 00526 if (blockPos == BS-1) 00527 { 00528 // Fetch link to next block 00529 if (getBlockLink(NEXT, &block) == 0) 00530 { 00531 currBlock = block; 00532 blockPos = RB; // Reset block position offset 00533 } else return; 00534 } 00535 00536 // Read data 00537 fs->read(cData, currBlock, blockPos, 1); 00538 data[i] = cData[0]; 00539 00540 blockPos++; 00541 byteCount++; 00542 } 00543 } 00544 00545 // Always binary 00546 char file::write(char *data, uint32_t n) 00547 { 00548 if (fMode == RO) return 1; 00549 00550 for (uint32_t i=0; i < n; i++) 00551 { 00552 // write to the buffer 00553 buffer[bufPos] = data[i]; 00554 bufPos++; 00555 00556 // If the buffer is full then flush 00557 if(bufPos == BUF) 00558 { 00559 if(flush() != 0); 00560 return 1; // Flush failed 00561 } 00562 } 00563 00564 return 0; 00565 } 00566 00567 char file::flush() 00568 { 00569 char cData[RB], cDataOB[RB-BLOCK_LLEN], c[1]; 00570 uint32_t nextFree; 00571 uint32_t i; 00572 uint32_t leftSpaceEOF; 00573 00574 bool destructiveFlag = false; // Set this true if there is any data to be removed 00575 uint32_t destructiveBlock=0; // First block to be removed by DWRITE 00576 00577 static bool fEOFow = false; /* END of file found while appending (and overwritten). 00578 Must be static because we don't want to found many EOF's. */ 00579 00580 00581 if (bufPos == 0) return 0; // File up-to date 00582 if (fMode == RO) return 1; 00583 00584 for (i=0; i <= bufPos; i++) 00585 { 00586 if(i%2) 00587 FlushLed = 1; 00588 else 00589 FlushLed = 0; 00590 00591 // Change Block? 00592 if ((bufPos - i) == 1) 00593 leftSpaceEOF = 1; 00594 else 00595 leftSpaceEOF = 0; 00596 if (blockPos >= BS-leftSpaceEOF) 00597 { 00598 // Fetch new unused block number 00599 if(fs->getNextFreeBlock(&nextFree) != 0) 00600 return 2; // No free space left 00601 00602 // Read flags from current block 00603 fs->read(cDataOB, currBlock, 0, RB-BLOCK_LLEN); 00604 00605 /* If destructive write is set then check if there is something 00606 to be marked for removal */ 00607 if (((cDataOB[0] & 0x40) != 0x40) && (fMode == DWRITE)) 00608 { 00609 destructiveFlag = true; 00610 destructiveBlock = (uint32_t)cDataOB[1]<<8|cDataOB[2]; 00611 goto allocate_new_block; 00612 } else if ((cDataOB[0] & 0x40) != 0x40) // fMode == AWRITE 00613 { 00614 // Update current block info 00615 currBlock = (uint32_t)cDataOB[1]<<8|cDataOB[2]; 00616 blockPos = RB; // Reset block position offset 00617 } else // There is no block to append so we allocate a new one 00618 { 00619 allocate_new_block: 00620 // Allocate new block for use 00621 cData[0] = 0x4C; // New flags 00622 cData[1] = '\0'; // Hbyte of Next Block link 00623 cData[2] = '\0'; // Lbyte of Next Block link 00624 cData[3] = (char)((currBlock & 0xff00) >> 8); // Hbyte of Prev Block link 00625 cData[4] = (char)(currBlock & 0x00ff); // Lbyte of Prev Block link 00626 fs->write(cData, nextFree, 0, RB); // Update Block Data 00627 00628 // Link old block with new block 00629 cDataOB[0] &= ~0x40; // Clear LBOF flag if set 00630 cDataOB[1] = (char)((nextFree & 0xff00) >> 8); // Hbyte of Next Block link 00631 cDataOB[2] = (char)(nextFree & 0x00ff); // Lbyte of Next Block link 00632 fs->write(cDataOB, currBlock, 0, RB-BLOCK_LLEN); // Update Block Data 00633 00634 // Update current block info 00635 currBlock = nextFree; 00636 blockPos = RB; // Reset block position offset 00637 } 00638 } 00639 00640 if (fMode == AWRITE) // Check if EOF is here 00641 { 00642 fs->read(c, currBlock, blockPos, 1); 00643 if ((c[0] == mEOF) && (fEOFow == false)) 00644 fEOFow = true; 00645 } 00646 00647 // Write file 00648 c[0]=buffer[i]; 00649 fs->write(c, currBlock, blockPos, 1); 00650 blockPos++; 00651 byteCount++; 00652 00653 // For fail safe, write EOF now 00654 if ((fMode == DWRITE)||(fEOFow == true)) 00655 fs->write((char[]){mEOF}, currBlock, blockPos, 1); // Write mEOF 00656 } 00657 00658 bufPos = 0; // Reset buffer position counter 00659 00660 /* If destructive write flag is set 00661 and there is data to be removed then remove data now */ 00662 if (destructiveFlag == true) 00663 if(removeFollowingBlocks(destructiveBlock) != 0) 00664 return 3; 00665 00666 FlushLed = 0; 00667 return 0; 00668 }
Generated on Tue Jul 19 2022 05:47:57 by
1.7.2