tom dunigan / SerialFlash

Dependents:   spiserialtst

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers SerialFlashDirectory.cpp Source File

SerialFlashDirectory.cpp

00001 /* SerialFlash Library - for filesystem-like access to SPI Serial Flash memory
00002  * https://github.com/PaulStoffregen/SerialFlash
00003  * Copyright (C) 2015, Paul Stoffregen, paul@pjrc.com
00004  *
00005  * Development of this library was funded by PJRC.COM, LLC by sales of Teensy.
00006  * Please support PJRC's efforts to develop open source software by purchasing
00007  * Teensy or other genuine PJRC products.
00008  *
00009  * Permission is hereby granted, free of charge, to any person obtaining a copy
00010  * of this software and associated documentation files (the "Software"), to deal
00011  * in the Software without restriction, including without limitation the rights
00012  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00013  * copies of the Software, and to permit persons to whom the Software is
00014  * furnished to do so, subject to the following conditions:
00015  *
00016  * The above copyright notice, development funding notice, and this permission
00017  * notice shall be included in all copies or substantial portions of the Software.
00018  *
00019  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00020  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00021  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00022  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00023  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00024  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00025  * THE SOFTWARE.
00026  */
00027 #include "mbed.h"
00028 #include "SerialFlash.h"
00029 //#include "util/crc16.h"
00030 
00031 /* On-chip SerialFlash file allocation data structures:
00032 
00033   uint32_t signature = 0xFA96554C;
00034   uint16_t maxfiles          
00035   uint16_t stringssize  // div by 4
00036   uint16_t hashes[maxfiles]
00037   struct {
00038     uint32_t file_begin
00039     uint32_t file_length
00040     uint16_t string_index  // div4
00041   } fileinfo[maxfiles]
00042   char strings[stringssize]
00043 
00044 A 32 bit signature is stored at the beginning of the flash memory.
00045 If 0xFFFFFFFF is seen, the entire chip should be assumed blank.
00046 If any value other than 0xFA96554C is found, a different data format
00047 is stored.  This could should refuse to access the flash.
00048 
00049 The next 4 bytes store number of files and size of the strings
00050 section, which allow the position of every other item to be found.
00051 The string section size is the 16 bit integer times 4, which allows
00052 up to 262140 bytes for string data.
00053 
00054 An array of 16 bit filename hashes allows for quick linear search
00055 for potentially matching filenames.  A hash value of 0xFFFF indicates
00056 no file is allocated for the remainder of the array.
00057 
00058 Following the hashes, and array of 10 byte structs give the location
00059 and length of the file's actual data, and the offset of its filename
00060 in the strings section.
00061 
00062 Strings are null terminated.  The remainder of the chip is file data.
00063 */
00064 
00065 #define DEFAULT_MAXFILES      600
00066 #define DEFAULT_STRINGS_SIZE  25560
00067 
00068 
00069 static uint32_t check_signature(void)
00070 {
00071     uint32_t sig[2];
00072 
00073     SerialFlash.read(0, sig, 8);
00074      //Serial.printf("sig: %08X %08X\n", sig[0], sig[1]);
00075     if (sig[0] == 0xFA96554C) return sig[1];
00076     if (sig[0] == 0xFFFFFFFF) {
00077         sig[0] = 0xFA96554C;
00078         sig[1] = ((uint32_t)(DEFAULT_STRINGS_SIZE/4) << 16) | DEFAULT_MAXFILES;
00079         SerialFlash.write(0, sig, 8);
00080         while (!SerialFlash.ready()) ; // TODO: timeout
00081         SerialFlash.read(0, sig, 8);
00082         if (sig[0] == 0xFA96554C) return sig[1];
00083     }
00084     return 0;
00085 }
00086 
00087 static uint16_t filename_hash(const char *filename)
00088 {
00089     // http://isthe.com/chongo/tech/comp/fnv/
00090     uint32_t hash = 2166136261;
00091     const char *p;
00092 
00093     for (p=filename; *p; p++) {
00094         hash ^= *p;
00095         hash *= 16777619;
00096     }
00097     hash = (hash % (uint32_t)0xFFFE) + 1; // all values except 0000 & FFFF
00098     return hash;
00099 }
00100 
00101 static bool filename_compare(const char *filename, uint32_t straddr)
00102 {
00103     unsigned int i;
00104     const char *p;
00105     char buf[16];
00106 
00107     p = filename;
00108     while (1) {
00109         SerialFlash.read(straddr, buf, sizeof(buf));
00110         straddr += sizeof(buf);
00111         for (i=0; i < sizeof(buf); i++) {
00112             if (*p++ != buf[i]) return false;
00113             if (buf[i] == 0) return true;
00114         }
00115     }
00116 }
00117 
00118 #if 0
00119 void pbuf(const void *buf, uint32_t len)
00120 {
00121   const uint8_t *p = (const uint8_t *)buf;
00122   do {
00123     Serial.printf("%02X ", *p++);
00124   } while (--len > 0);
00125   Serial.println();
00126 }
00127 #endif
00128 
00129 SerialFlashFile SerialFlashChip::open(const char *filename)
00130 {
00131     uint32_t maxfiles, straddr;
00132     uint16_t hash, hashtable[8];
00133     uint32_t i, n, index=0;
00134     uint32_t buf[3];
00135     SerialFlashFile file;
00136 
00137     maxfiles = check_signature();
00138      //Serial.printf("sig: %08X\n", maxfiles);
00139     if (!maxfiles) return file;
00140     maxfiles &= 0xFFFF;
00141     hash = filename_hash(filename);
00142      //Serial.printf("hash %04X for \"%s\"\n", hash, filename);
00143     while (index < maxfiles) {
00144         n = 8;
00145         if (n > maxfiles - index) n = maxfiles - index;
00146         SerialFlash.read(8 + index * 2, hashtable, n * 2);
00147          //Serial.printf(" read %u: ", 8 + index * 2);
00148          //pbuf(hashtable, n * 2);
00149         for (i=0; i < n; i++) {
00150             if (hashtable[i] == hash) {
00151                  //Serial.printf("  hash match at index %u\n", index+i);
00152                 buf[2] = 0;
00153                 SerialFlash.read(8 + maxfiles * 2 + (index+i) * 10, buf, 10);
00154 
00155                  //Serial.printf("  maxf=%d, index=%d, i=%d\n", maxfiles, index, i);
00156                  //Serial.printf("  read %u: ", 8 + maxfiles * 2 + (index+i) * 10);
00157                  //pbuf(buf, 10);
00158                 straddr = 8 + maxfiles * 12 + buf[2] * 4;
00159                  //Serial.printf("  straddr = %u\n", straddr);
00160                 if (filename_compare(filename, straddr)) {
00161                      //Serial.printf("  match!\n");
00162                      //Serial.printf("  addr = %u\n", buf[0]);
00163                      //Serial.printf("  len =  %u\n", buf[1]);
00164                     file.address = buf[0];
00165                     file.length = buf[1];
00166                     file.offset = 0;
00167                     file.dirindex = index + i;
00168                     return file;
00169                 }
00170             } else if (hashtable[i] == 0xFFFF) {
00171                 return file;
00172             }
00173         }
00174         index += n;
00175     }
00176     return file;
00177 }
00178 
00179 bool SerialFlashChip::exists(const char *filename)
00180 {
00181     SerialFlashFile file = open(filename);
00182     return (bool)file;
00183 }
00184 
00185 bool SerialFlashChip::remove(const char *filename)
00186 {
00187     SerialFlashFile file = open(filename);
00188     return remove(file);
00189 }
00190 
00191 bool SerialFlashChip::remove(SerialFlashFile &file)
00192 {
00193     // To "remove" a file, we simply zero its hash in the lookup
00194     // table, so it can't be found by open().  The space on the
00195     // flash memory is not freed.
00196     if (!file) return false;
00197     uint16_t hash;
00198     SerialFlash.read(8 + file.dirindex * 2, &hash, 2);
00199      //Serial.printf("remove hash %04X at %d index\n", hash, file.dirindex);
00200     hash ^= 0xFFFF;  // write zeros to all ones
00201     SerialFlash.write(8 + file.dirindex * 2, &hash, 2);
00202     while (!SerialFlash.ready()) ; // wait...  TODO: timeout
00203     SerialFlash.read(8 + file.dirindex * 2, &hash, 2);
00204     if (hash != 0)  {
00205          //Serial.printf("remove failed, hash %04X\n", hash);
00206         return false;
00207     }
00208     file.address = 0;
00209     file.length = 0;
00210     return true;
00211 }
00212 
00213 static uint32_t find_first_unallocated_file_index(uint32_t maxfiles)
00214 {
00215     uint16_t hashtable[8];
00216     uint32_t i, n, index=0;
00217 
00218     do {
00219         n = 8;
00220         if (index + n > maxfiles) n = maxfiles - index;
00221         SerialFlash.read(8 + index * 2, hashtable, n * 2);
00222         for (i=0; i < n; i++) {
00223             if (hashtable[i] == 0xFFFF) return index + i;
00224         }
00225         index += n;
00226     } while (index < maxfiles);
00227     return 0xFFFFFFFF;
00228 }
00229 
00230 static uint32_t string_length(uint32_t addr)
00231 {
00232     char buf[16];
00233     const char *p;
00234     uint32_t len=0;
00235 
00236     while (1) {
00237         SerialFlash.read(addr, buf, sizeof(buf));
00238         for (p=buf; p < buf + sizeof(buf); p++) {
00239             len++;
00240             if (*p == 0) return len;
00241         }
00242         addr += sizeof(buf);
00243     }
00244 }
00245 
00246 //  uint32_t signature = 0xFA96554C;
00247 //  uint16_t maxfiles          
00248 //  uint16_t stringssize  // div by 4
00249 //  uint16_t hashes[maxfiles]
00250 //  struct {
00251 //    uint32_t file_begin
00252 //    uint32_t file_length
00253 //    uint16_t string_index  // div 4
00254 //  } fileinfo[maxfiles]
00255 //  char strings[stringssize]
00256 
00257 bool SerialFlashChip::create(const char *filename, uint32_t length, uint32_t align)
00258 {
00259     uint32_t maxfiles, stringsize;
00260     uint32_t index, buf[3];
00261     uint32_t address, straddr, len;
00262     SerialFlashFile file;
00263 
00264     // check if the file already exists
00265     if (exists(filename)) return false;
00266 
00267     // first, get the filesystem parameters
00268     maxfiles = check_signature();
00269     if (!maxfiles) return false;
00270     stringsize = (maxfiles & 0xFFFF0000) >> 14;
00271     maxfiles &= 0xFFFF;
00272 
00273     // find the first unused slot for this file
00274     index = find_first_unallocated_file_index(maxfiles);
00275     if (index >= maxfiles) return false;
00276      //Serial.printf("index = %u\n", index);
00277     // compute where to store the filename and actual data
00278     straddr = 8 + maxfiles * 12;
00279     if (index == 0) {
00280         address = straddr + stringsize;
00281     } else {
00282         buf[2] = 0;
00283         SerialFlash.read(8 + maxfiles * 2 + (index-1) * 10, buf, 10);
00284         address = buf[0] + buf[1];
00285         straddr += buf[2] * 4;
00286         straddr += string_length(straddr);
00287         straddr = (straddr + 3) & 0x0003FFFC;
00288     }
00289      //Serial.printf("straddr = %u\n", straddr);
00290      //Serial.printf("address = %u\n", address);
00291      //Serial.printf("length = %u\n", length);
00292     if (align > 0) {
00293         // for files aligned to sectors, adjust addr & len
00294         address += align - 1;
00295         address /= align;
00296         address *= align;
00297          //Serial.printf("align address = %u\n", address);
00298         length += align - 1;
00299         length /= align;
00300         length *= align;
00301          //Serial.printf("align length = %u\n", length);
00302     } else {
00303         // always align every file to a page boundary
00304         // for predictable write latency and to guarantee
00305         // write suspend for reading another file can't
00306         // conflict on the same page (2 files never share
00307         // a write page).
00308         address = (address + 255) & 0xFFFFFF00;
00309     }
00310      //Serial.printf("address = %u\n", address);
00311     // last check, if enough space exists...
00312     len = strlen(filename);
00313     // TODO: check for enough string space for filename
00314     uint8_t id[3];
00315     SerialFlash.readID(id);
00316     if (address + length > SerialFlash.capacity(id)) return false;
00317 
00318     SerialFlash.write(straddr, filename, len+1);
00319     buf[0] = address;
00320     buf[1] = length;
00321     buf[2] = (straddr - (8 + maxfiles * 12)) / 4;
00322     SerialFlash.write(8 + maxfiles * 2 + index * 10, buf, 10);
00323      //Serial.printf("  write %u: ", 8 + maxfiles * 2 + index * 10);
00324      //pbuf(buf, 10);
00325     while (!SerialFlash.ready()) ;  // TODO: timeout
00326      
00327     buf[0] = filename_hash(filename);
00328      //Serial.printf("hash = %04X\n", buf[0]);
00329     SerialFlash.write(8 + index * 2, buf, 2);
00330     while (!SerialFlash.ready()) ;  // TODO: timeout
00331     return true;
00332 }
00333 
00334 bool SerialFlashChip::readdir(char *filename, uint32_t strsize, uint32_t &filesize)
00335 {
00336     uint32_t maxfiles, index, straddr;
00337     uint32_t i, n;
00338     uint32_t buf[2];
00339     uint16_t hash;
00340     char str[16], *p=filename;
00341 
00342     filename[0] = 0;
00343     maxfiles = check_signature();
00344     if (!maxfiles) return false;
00345     maxfiles &= 0xFFFF; 
00346     index = dirindex;
00347     while (1) {
00348         if (index >= maxfiles) return false;
00349          //Serial.printf("readdir, index = %u\n", index);
00350         SerialFlash.read(8 + index * 2, &hash, 2);
00351         if (hash != 0) break;
00352         index++;  // skip deleted entries
00353     }
00354     dirindex = index + 1;
00355     buf[1] = 0;
00356     SerialFlash.read(8 + 4 + maxfiles * 2 + index * 10, buf, 6);
00357     if (buf[0] == 0xFFFFFFFF) return false;
00358     filesize = buf[0];
00359     straddr = 8 + maxfiles * 12 + buf[1] * 4;
00360      //Serial.printf("  length = %u\n", buf[0]);
00361      //Serial.printf("  straddr = %u\n", straddr);
00362 
00363     while (strsize) {
00364         n = strsize;
00365         if (n > sizeof(str)) n = sizeof(str);
00366         SerialFlash.read(straddr, str, n);
00367         for (i=0; i < n; i++) {
00368             *p++ = str[i];
00369             if (str[i] == 0) {
00370                  //Serial.printf("  name = %s\n", filename);
00371                 return true;
00372             }
00373         }
00374         strsize -= n;
00375         straddr += n;
00376     }
00377     *(p - 1) = 0;
00378      //Serial.printf("  name(overflow) = %s\n", filename);
00379     return true;
00380 }
00381 
00382 
00383 void SerialFlashFile::erase()
00384 {
00385     uint32_t i, blocksize;
00386 
00387     blocksize = SerialFlash.blockSize();
00388     if (address & (blocksize - 1)) return; // must begin on a block boundary
00389     if (length & (blocksize - 1)) return;  // must be exact number of blocks
00390     for (i=0; i < length; i += blocksize) {
00391         SerialFlash.eraseBlock(address + i);
00392     }
00393 }
00394