port of teensy SerialFlash library for SPI serial flash like winbond from https://github.com/PaulStoffregen/SerialFlash
Diff: SerialFlashDirectory.cpp
- Revision:
- 0:e5c9fd5789d7
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SerialFlashDirectory.cpp Thu Apr 14 12:05:15 2016 +0000 @@ -0,0 +1,394 @@ +/* SerialFlash Library - for filesystem-like access to SPI Serial Flash memory + * https://github.com/PaulStoffregen/SerialFlash + * Copyright (C) 2015, Paul Stoffregen, paul@pjrc.com + * + * Development of this library was funded by PJRC.COM, LLC by sales of Teensy. + * Please support PJRC's efforts to develop open source software by purchasing + * Teensy or other genuine PJRC products. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice, development funding notice, and this permission + * notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "mbed.h" +#include "SerialFlash.h" +//#include "util/crc16.h" + +/* On-chip SerialFlash file allocation data structures: + + uint32_t signature = 0xFA96554C; + uint16_t maxfiles + uint16_t stringssize // div by 4 + uint16_t hashes[maxfiles] + struct { + uint32_t file_begin + uint32_t file_length + uint16_t string_index // div4 + } fileinfo[maxfiles] + char strings[stringssize] + +A 32 bit signature is stored at the beginning of the flash memory. +If 0xFFFFFFFF is seen, the entire chip should be assumed blank. +If any value other than 0xFA96554C is found, a different data format +is stored. This could should refuse to access the flash. + +The next 4 bytes store number of files and size of the strings +section, which allow the position of every other item to be found. +The string section size is the 16 bit integer times 4, which allows +up to 262140 bytes for string data. + +An array of 16 bit filename hashes allows for quick linear search +for potentially matching filenames. A hash value of 0xFFFF indicates +no file is allocated for the remainder of the array. + +Following the hashes, and array of 10 byte structs give the location +and length of the file's actual data, and the offset of its filename +in the strings section. + +Strings are null terminated. The remainder of the chip is file data. +*/ + +#define DEFAULT_MAXFILES 600 +#define DEFAULT_STRINGS_SIZE 25560 + + +static uint32_t check_signature(void) +{ + uint32_t sig[2]; + + SerialFlash.read(0, sig, 8); + //Serial.printf("sig: %08X %08X\n", sig[0], sig[1]); + if (sig[0] == 0xFA96554C) return sig[1]; + if (sig[0] == 0xFFFFFFFF) { + sig[0] = 0xFA96554C; + sig[1] = ((uint32_t)(DEFAULT_STRINGS_SIZE/4) << 16) | DEFAULT_MAXFILES; + SerialFlash.write(0, sig, 8); + while (!SerialFlash.ready()) ; // TODO: timeout + SerialFlash.read(0, sig, 8); + if (sig[0] == 0xFA96554C) return sig[1]; + } + return 0; +} + +static uint16_t filename_hash(const char *filename) +{ + // http://isthe.com/chongo/tech/comp/fnv/ + uint32_t hash = 2166136261; + const char *p; + + for (p=filename; *p; p++) { + hash ^= *p; + hash *= 16777619; + } + hash = (hash % (uint32_t)0xFFFE) + 1; // all values except 0000 & FFFF + return hash; +} + +static bool filename_compare(const char *filename, uint32_t straddr) +{ + unsigned int i; + const char *p; + char buf[16]; + + p = filename; + while (1) { + SerialFlash.read(straddr, buf, sizeof(buf)); + straddr += sizeof(buf); + for (i=0; i < sizeof(buf); i++) { + if (*p++ != buf[i]) return false; + if (buf[i] == 0) return true; + } + } +} + +#if 0 +void pbuf(const void *buf, uint32_t len) +{ + const uint8_t *p = (const uint8_t *)buf; + do { + Serial.printf("%02X ", *p++); + } while (--len > 0); + Serial.println(); +} +#endif + +SerialFlashFile SerialFlashChip::open(const char *filename) +{ + uint32_t maxfiles, straddr; + uint16_t hash, hashtable[8]; + uint32_t i, n, index=0; + uint32_t buf[3]; + SerialFlashFile file; + + maxfiles = check_signature(); + //Serial.printf("sig: %08X\n", maxfiles); + if (!maxfiles) return file; + maxfiles &= 0xFFFF; + hash = filename_hash(filename); + //Serial.printf("hash %04X for \"%s\"\n", hash, filename); + while (index < maxfiles) { + n = 8; + if (n > maxfiles - index) n = maxfiles - index; + SerialFlash.read(8 + index * 2, hashtable, n * 2); + //Serial.printf(" read %u: ", 8 + index * 2); + //pbuf(hashtable, n * 2); + for (i=0; i < n; i++) { + if (hashtable[i] == hash) { + //Serial.printf(" hash match at index %u\n", index+i); + buf[2] = 0; + SerialFlash.read(8 + maxfiles * 2 + (index+i) * 10, buf, 10); + + //Serial.printf(" maxf=%d, index=%d, i=%d\n", maxfiles, index, i); + //Serial.printf(" read %u: ", 8 + maxfiles * 2 + (index+i) * 10); + //pbuf(buf, 10); + straddr = 8 + maxfiles * 12 + buf[2] * 4; + //Serial.printf(" straddr = %u\n", straddr); + if (filename_compare(filename, straddr)) { + //Serial.printf(" match!\n"); + //Serial.printf(" addr = %u\n", buf[0]); + //Serial.printf(" len = %u\n", buf[1]); + file.address = buf[0]; + file.length = buf[1]; + file.offset = 0; + file.dirindex = index + i; + return file; + } + } else if (hashtable[i] == 0xFFFF) { + return file; + } + } + index += n; + } + return file; +} + +bool SerialFlashChip::exists(const char *filename) +{ + SerialFlashFile file = open(filename); + return (bool)file; +} + +bool SerialFlashChip::remove(const char *filename) +{ + SerialFlashFile file = open(filename); + return remove(file); +} + +bool SerialFlashChip::remove(SerialFlashFile &file) +{ + // To "remove" a file, we simply zero its hash in the lookup + // table, so it can't be found by open(). The space on the + // flash memory is not freed. + if (!file) return false; + uint16_t hash; + SerialFlash.read(8 + file.dirindex * 2, &hash, 2); + //Serial.printf("remove hash %04X at %d index\n", hash, file.dirindex); + hash ^= 0xFFFF; // write zeros to all ones + SerialFlash.write(8 + file.dirindex * 2, &hash, 2); + while (!SerialFlash.ready()) ; // wait... TODO: timeout + SerialFlash.read(8 + file.dirindex * 2, &hash, 2); + if (hash != 0) { + //Serial.printf("remove failed, hash %04X\n", hash); + return false; + } + file.address = 0; + file.length = 0; + return true; +} + +static uint32_t find_first_unallocated_file_index(uint32_t maxfiles) +{ + uint16_t hashtable[8]; + uint32_t i, n, index=0; + + do { + n = 8; + if (index + n > maxfiles) n = maxfiles - index; + SerialFlash.read(8 + index * 2, hashtable, n * 2); + for (i=0; i < n; i++) { + if (hashtable[i] == 0xFFFF) return index + i; + } + index += n; + } while (index < maxfiles); + return 0xFFFFFFFF; +} + +static uint32_t string_length(uint32_t addr) +{ + char buf[16]; + const char *p; + uint32_t len=0; + + while (1) { + SerialFlash.read(addr, buf, sizeof(buf)); + for (p=buf; p < buf + sizeof(buf); p++) { + len++; + if (*p == 0) return len; + } + addr += sizeof(buf); + } +} + +// uint32_t signature = 0xFA96554C; +// uint16_t maxfiles +// uint16_t stringssize // div by 4 +// uint16_t hashes[maxfiles] +// struct { +// uint32_t file_begin +// uint32_t file_length +// uint16_t string_index // div 4 +// } fileinfo[maxfiles] +// char strings[stringssize] + +bool SerialFlashChip::create(const char *filename, uint32_t length, uint32_t align) +{ + uint32_t maxfiles, stringsize; + uint32_t index, buf[3]; + uint32_t address, straddr, len; + SerialFlashFile file; + + // check if the file already exists + if (exists(filename)) return false; + + // first, get the filesystem parameters + maxfiles = check_signature(); + if (!maxfiles) return false; + stringsize = (maxfiles & 0xFFFF0000) >> 14; + maxfiles &= 0xFFFF; + + // find the first unused slot for this file + index = find_first_unallocated_file_index(maxfiles); + if (index >= maxfiles) return false; + //Serial.printf("index = %u\n", index); + // compute where to store the filename and actual data + straddr = 8 + maxfiles * 12; + if (index == 0) { + address = straddr + stringsize; + } else { + buf[2] = 0; + SerialFlash.read(8 + maxfiles * 2 + (index-1) * 10, buf, 10); + address = buf[0] + buf[1]; + straddr += buf[2] * 4; + straddr += string_length(straddr); + straddr = (straddr + 3) & 0x0003FFFC; + } + //Serial.printf("straddr = %u\n", straddr); + //Serial.printf("address = %u\n", address); + //Serial.printf("length = %u\n", length); + if (align > 0) { + // for files aligned to sectors, adjust addr & len + address += align - 1; + address /= align; + address *= align; + //Serial.printf("align address = %u\n", address); + length += align - 1; + length /= align; + length *= align; + //Serial.printf("align length = %u\n", length); + } else { + // always align every file to a page boundary + // for predictable write latency and to guarantee + // write suspend for reading another file can't + // conflict on the same page (2 files never share + // a write page). + address = (address + 255) & 0xFFFFFF00; + } + //Serial.printf("address = %u\n", address); + // last check, if enough space exists... + len = strlen(filename); + // TODO: check for enough string space for filename + uint8_t id[3]; + SerialFlash.readID(id); + if (address + length > SerialFlash.capacity(id)) return false; + + SerialFlash.write(straddr, filename, len+1); + buf[0] = address; + buf[1] = length; + buf[2] = (straddr - (8 + maxfiles * 12)) / 4; + SerialFlash.write(8 + maxfiles * 2 + index * 10, buf, 10); + //Serial.printf(" write %u: ", 8 + maxfiles * 2 + index * 10); + //pbuf(buf, 10); + while (!SerialFlash.ready()) ; // TODO: timeout + + buf[0] = filename_hash(filename); + //Serial.printf("hash = %04X\n", buf[0]); + SerialFlash.write(8 + index * 2, buf, 2); + while (!SerialFlash.ready()) ; // TODO: timeout + return true; +} + +bool SerialFlashChip::readdir(char *filename, uint32_t strsize, uint32_t &filesize) +{ + uint32_t maxfiles, index, straddr; + uint32_t i, n; + uint32_t buf[2]; + uint16_t hash; + char str[16], *p=filename; + + filename[0] = 0; + maxfiles = check_signature(); + if (!maxfiles) return false; + maxfiles &= 0xFFFF; + index = dirindex; + while (1) { + if (index >= maxfiles) return false; + //Serial.printf("readdir, index = %u\n", index); + SerialFlash.read(8 + index * 2, &hash, 2); + if (hash != 0) break; + index++; // skip deleted entries + } + dirindex = index + 1; + buf[1] = 0; + SerialFlash.read(8 + 4 + maxfiles * 2 + index * 10, buf, 6); + if (buf[0] == 0xFFFFFFFF) return false; + filesize = buf[0]; + straddr = 8 + maxfiles * 12 + buf[1] * 4; + //Serial.printf(" length = %u\n", buf[0]); + //Serial.printf(" straddr = %u\n", straddr); + + while (strsize) { + n = strsize; + if (n > sizeof(str)) n = sizeof(str); + SerialFlash.read(straddr, str, n); + for (i=0; i < n; i++) { + *p++ = str[i]; + if (str[i] == 0) { + //Serial.printf(" name = %s\n", filename); + return true; + } + } + strsize -= n; + straddr += n; + } + *(p - 1) = 0; + //Serial.printf(" name(overflow) = %s\n", filename); + return true; +} + + +void SerialFlashFile::erase() +{ + uint32_t i, blocksize; + + blocksize = SerialFlash.blockSize(); + if (address & (blocksize - 1)) return; // must begin on a block boundary + if (length & (blocksize - 1)) return; // must be exact number of blocks + for (i=0; i < length; i += blocksize) { + SerialFlash.eraseBlock(address + i); + } +} +