1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-19 23:22:16 +03:00

Merge branch 'ficeto-esp8266' into esp8266

* ficeto-esp8266: (31 commits)
  leftovers
  Update to the latest SPIFFS git and cleanup
  add Exception Causes (EXCCAUSE) docu
  fix possible null ptr in EEPROM.cpp
  Align the start of functions to the next power-of-two greater than 4, skipping up to 3 bytes.
  improve os_printf handling when buffer full.  - wait for free buffer in hw fifo
  force all os_malloc calls to request a aligned size.  - this fix Fatal exception (9) by unaligned class memory
  add some __attribute__ for compiler to get better optimizations and warning handle
  fix possible problems in EEPROM regarding interrupt handling and SPI flash blocking
  spiffs fixes
  improve includes add ssid and ip to debug out
  add examples/WiFiMulti/WiFiMulti.ino
  add support for list of AP connections  - auto select ssid with best signal  - for debugging enable DEBUG_WIFI_MULTI macro and call Serial.setDebugOutput(true);
  fix start address so erase works
  disable all interrupts when reading from spiffs
  printf to print instead of write
  fix uart triggering reset when spi has been read/written
  ESP8266WiFiClass::waitForConnectResult()
  add Print::printf
  fix data types
  ...
This commit is contained in:
Ivan Grokhotkov 2015-05-18 12:19:50 +03:00
commit c32518974b
43 changed files with 1457 additions and 820 deletions

View File

@ -2,7 +2,6 @@ menu.UploadSpeed=Upload Speed
menu.CpuFrequency=CPU Frequency menu.CpuFrequency=CPU Frequency
menu.FlashSize=Flash Size menu.FlashSize=Flash Size
menu.FlashFreq=Flash Frequency menu.FlashFreq=Flash Frequency
menu.FlashMode=Flash Mode
############################################################## ##############################################################
generic.name=Generic ESP8266 Module generic.name=Generic ESP8266 Module
@ -24,6 +23,8 @@ generic.build.flash_mode=qio
generic.build.flash_size=512K generic.build.flash_size=512K
generic.build.flash_freq=40 generic.build.flash_freq=40
generic.build.flash_ld=eagle.flash.512k.ld generic.build.flash_ld=eagle.flash.512k.ld
generic.build.spiffs_start=0x6B000
generic.build.spiffs_end=0x7B000
generic.menu.CpuFrequency.80=80 MHz generic.menu.CpuFrequency.80=80 MHz
generic.menu.CpuFrequency.80.build.f_cpu=80000000L generic.menu.CpuFrequency.80.build.f_cpu=80000000L
@ -49,39 +50,46 @@ generic.menu.UploadSpeed.512000.upload.speed=512000
generic.menu.UploadSpeed.921600=921600 generic.menu.UploadSpeed.921600=921600
generic.menu.UploadSpeed.921600.upload.speed=921600 generic.menu.UploadSpeed.921600.upload.speed=921600
generic.menu.FlashSize.512K=512K generic.menu.FlashSize.512K=512K (64K SPIFFS)
generic.menu.FlashSize.512K.build.flash_size=512K generic.menu.FlashSize.512K.build.flash_size=512K
generic.menu.FlashSize.512K.build.flash_ld=eagle.flash.512k.ld generic.menu.FlashSize.512K.build.flash_ld=eagle.flash.512k.ld
generic.menu.FlashSize.256K=256K generic.menu.FlashSize.512K.build.spiffs_start=0x6B000
generic.menu.FlashSize.256K.build.flash_size=256K generic.menu.FlashSize.512K.build.spiffs_end=0x7B000
generic.menu.FlashSize.256K.build.flash_ld=eagle.flash.256k.ld generic.menu.FlashSize.1M512=1M (512K SPIFFS)
generic.menu.FlashSize.1M=1M generic.menu.FlashSize.1M512.build.flash_size=1M
generic.menu.FlashSize.1M.build.flash_size=1M generic.menu.FlashSize.1M512.build.flash_ld=eagle.flash.1m512.ld
generic.menu.FlashSize.1M.build.flash_ld=eagle.flash.1m.ld generic.menu.FlashSize.1M512.build.spiffs_start=0x6B000
generic.menu.FlashSize.2M=2M generic.menu.FlashSize.1M512.build.spiffs_end=0xFB000
generic.menu.FlashSize.1M256=1M (256K SPIFFS)
generic.menu.FlashSize.1M256.build.flash_size=1M
generic.menu.FlashSize.1M256.build.flash_ld=eagle.flash.1m256.ld
generic.menu.FlashSize.1M256.build.spiffs_start=0xAB000
generic.menu.FlashSize.1M256.build.spiffs_end=0xFB000
generic.menu.FlashSize.1M128=1M (128K SPIFFS)
generic.menu.FlashSize.1M128.build.flash_size=1M
generic.menu.FlashSize.1M128.build.flash_ld=eagle.flash.1m128.ld
generic.menu.FlashSize.1M128.build.spiffs_start=0xCB000
generic.menu.FlashSize.1M128.build.spiffs_end=0xFB000
generic.menu.FlashSize.1M64=1M (64K SPIFFS)
generic.menu.FlashSize.1M64.build.flash_size=1M
generic.menu.FlashSize.1M64.build.flash_ld=eagle.flash.1m64.ld
generic.menu.FlashSize.1M64.build.spiffs_start=0xEB000
generic.menu.FlashSize.1M64.build.spiffs_end=0xFB000
generic.menu.FlashSize.2M=2M (1M SPIFFS)
generic.menu.FlashSize.2M.build.flash_size=2M generic.menu.FlashSize.2M.build.flash_size=2M
generic.menu.FlashSize.2M.build.flash_ld=eagle.flash.2m.ld generic.menu.FlashSize.2M.build.flash_ld=eagle.flash.2m.ld
generic.menu.FlashSize.4M=4M generic.menu.FlashSize.2M.build.spiffs_start=0x100000
generic.menu.FlashSize.2M.build.spiffs_end=0x1FB000
generic.menu.FlashSize.4M=4M (3M SPIFFS)
generic.menu.FlashSize.4M.build.flash_size=4M generic.menu.FlashSize.4M.build.flash_size=4M
generic.menu.FlashSize.4M.build.flash_ld=eagle.flash.4m.ld generic.menu.FlashSize.4M.build.flash_ld=eagle.flash.4m.ld
generic.menu.FlashSize.4M.build.spiffs_start=0x100000
generic.menu.FlashSize.4M.build.spiffs_end=0x3FB000
generic.menu.FlashFreq.40=40MHz # generic.menu.FlashFreq.40=40MHz
generic.menu.FlashFreq.40.build.flash_freq=40 # generic.menu.FlashFreq.40.build.flash_freq=40
generic.menu.FlashFreq.20=20MHz # generic.menu.FlashFreq.80=80MHz
generic.menu.FlashFreq.20.build.flash_freq=20 # generic.menu.FlashFreq.80.build.flash_freq=80
generic.menu.FlashFreq.26=26.7MHz
generic.menu.FlashFreq.26.build.flash_freq=26.7
generic.menu.FlashFreq.80=80MHz
generic.menu.FlashFreq.80.build.flash_freq=80
generic.menu.FlashMode.qio=QIO
generic.menu.FlashMode.qio.build.flash_mode=qio
generic.menu.FlashMode.qout=QOUT
generic.menu.FlashMode.qout.build.flash_mode=qout
generic.menu.FlashMode.dio=DIO
generic.menu.FlashMode.dio.build.flash_mode=dio
generic.menu.FlashMode.dout=DOUT
generic.menu.FlashMode.dout.build.flash_mode=dout
############################################################## ##############################################################
modwifi.name=Olimex MOD-WIFI-ESP8266(-DEV) modwifi.name=Olimex MOD-WIFI-ESP8266(-DEV)
@ -104,6 +112,8 @@ modwifi.build.flash_mode=qio
modwifi.build.flash_size=2M modwifi.build.flash_size=2M
modwifi.build.flash_freq=40 modwifi.build.flash_freq=40
modwifi.build.flash_ld=eagle.flash.2m.ld modwifi.build.flash_ld=eagle.flash.2m.ld
modwifi.build.spiffs_start=0x100000
modwifi.build.spiffs_end=0x1FB000
modwifi.menu.CpuFrequency.80=80 MHz modwifi.menu.CpuFrequency.80=80 MHz
modwifi.menu.CpuFrequency.80.build.f_cpu=80000000L modwifi.menu.CpuFrequency.80.build.f_cpu=80000000L
@ -150,6 +160,8 @@ nodemcu.build.flash_mode=qio
nodemcu.build.flash_size=4M nodemcu.build.flash_size=4M
nodemcu.build.flash_freq=40 nodemcu.build.flash_freq=40
nodemcu.build.flash_ld=eagle.flash.4m.ld nodemcu.build.flash_ld=eagle.flash.4m.ld
nodemcu.build.spiffs_start=0x100000
nodemcu.build.spiffs_end=0x3FB000
nodemcu.menu.CpuFrequency.80=80 MHz nodemcu.menu.CpuFrequency.80=80 MHz
nodemcu.menu.CpuFrequency.80.build.f_cpu=80000000L nodemcu.menu.CpuFrequency.80.build.f_cpu=80000000L
@ -176,9 +188,6 @@ nodemcu.menu.UploadSpeed.512000.upload.speed=512000
nodemcu.menu.UploadSpeed.921600=921600 nodemcu.menu.UploadSpeed.921600=921600
nodemcu.menu.UploadSpeed.921600.upload.speed=921600 nodemcu.menu.UploadSpeed.921600.upload.speed=921600
nodemcu.menu.FlashSize.4M=4M
nodemcu.menu.FlashSize.4M.build.flash_size=4M
############################################################## ##############################################################
# wifio.name=Wifio # wifio.name=Wifio
# #
@ -197,6 +206,8 @@ nodemcu.menu.FlashSize.4M.build.flash_size=4M
# wifio.build.flash_size=512K # wifio.build.flash_size=512K
# wifio.build.flash_freq=40 # wifio.build.flash_freq=40
# wifio.build.flash_ld=eagle.flash.512k.ld # wifio.build.flash_ld=eagle.flash.512k.ld
# wifio.build.spiffs_start=0x6B000
# wifio.build.spiffs_end=0x7B000
# #
# wifio.menu.CpuFrequency.80=80MHz # wifio.menu.CpuFrequency.80=80MHz
# wifio.menu.CpuFrequency.80.build.f_cpu=80000000L # wifio.menu.CpuFrequency.80.build.f_cpu=80000000L

View File

@ -135,7 +135,7 @@ void ets_intr_unlock();
extern uint32_t interruptsState; extern uint32_t interruptsState;
#define interrupts() xt_enable_interrupts(interruptsState) #define interrupts() xt_enable_interrupts(interruptsState)
#define noInterrupts() xt_disable_interrupts(interruptsState, 15) #define noInterrupts() __asm__ __volatile__("rsil %0,15; esync; isync; dsync" : "=a" (interruptsState))
#define clockCyclesPerMicrosecond() ( F_CPU / 1000000L ) #define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )
#define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() ) #define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() )

197
cores/esp8266/FileSystem.cpp Executable file → Normal file
View File

@ -22,45 +22,72 @@
#include "Arduino.h" #include "Arduino.h"
bool FSClass::mount() { bool FSClass::mount() {
if (_mounted) if(SPIFFS_mounted(&_filesystemStorageHandle)) return true;
return true; int res = spiffs_mount();
if(res != 0){
_mounted = spiffs_mount(); int formated = SPIFFS_format(&_filesystemStorageHandle);
return _mounted; if(formated != 0) return false;
res = spiffs_mount();
}
return (res == 0);
} }
void FSClass::unmount() { void FSClass::unmount() {
if (!_mounted) if(SPIFFS_mounted(&_filesystemStorageHandle))
return; SPIFFS_unmount(&_filesystemStorageHandle);
spiffs_unmount();
_mounted = false;
} }
bool FSClass::format() { bool FSClass::format() {
return spiffs_format(); if(!SPIFFS_mounted(&_filesystemStorageHandle)){
spiffs_mount();
}
SPIFFS_unmount(&_filesystemStorageHandle);
int formated = SPIFFS_format(&_filesystemStorageHandle);
if(formated != 0) return false;
return (spiffs_mount() == 0);
} }
bool FSClass::exists(const char *filename) { bool FSClass::check() {
return SPIFFS_check(&_filesystemStorageHandle) == 0;
}
bool FSClass::exists(char *filename) {
spiffs_stat stat = {0}; spiffs_stat stat = {0};
if (SPIFFS_stat(&_filesystemStorageHandle, filename, &stat) < 0) if (SPIFFS_stat(&_filesystemStorageHandle, filename, &stat) < 0)
return false; return false;
return stat.name[0] != '\0'; return stat.name[0] != '\0';
} }
bool FSClass::create(const char *filepath){ bool FSClass::create(char *filepath){
return SPIFFS_creat(&_filesystemStorageHandle, filepath, 0) == 0; return SPIFFS_creat(&_filesystemStorageHandle, filepath, 0) == 0;
} }
bool FSClass::remove(const char *filepath){ bool FSClass::remove(char *filepath){
return SPIFFS_remove(&_filesystemStorageHandle, filepath) == 0; return SPIFFS_remove(&_filesystemStorageHandle, filepath) == 0;
} }
bool FSClass::rename(const char *filename, const char *newname) { bool FSClass::rename(char *filename, char *newname) {
return SPIFFS_rename(&_filesystemStorageHandle, filename, newname) == 0; return SPIFFS_rename(&_filesystemStorageHandle, filename, newname) == 0;
} }
FSFile FSClass::open(const char *filename, uint8_t mode) { size_t FSClass::totalBytes(){
u32_t totalBytes;
u32_t usedBytes;
if(SPIFFS_info(&_filesystemStorageHandle, &totalBytes, &usedBytes) == 0)
return totalBytes;
return 0;
}
size_t FSClass::usedBytes(){
u32_t totalBytes;
u32_t usedBytes;
if(SPIFFS_info(&_filesystemStorageHandle, &totalBytes, &usedBytes) == 0)
return usedBytes;
return 0;
}
FSFile FSClass::open(char *filename, uint8_t mode) {
if(String(filename) == "" || String(filename) == "/") return FSFile("/");
int repeats = 0; int repeats = 0;
bool notExist; bool notExist;
bool canRecreate = (mode & SPIFFS_CREAT) == SPIFFS_CREAT; bool canRecreate = (mode & SPIFFS_CREAT) == SPIFFS_CREAT;
@ -71,7 +98,7 @@ FSFile FSClass::open(const char *filename, uint8_t mode) {
res = SPIFFS_open(&_filesystemStorageHandle, filename, (spiffs_flags)mode, 0); res = SPIFFS_open(&_filesystemStorageHandle, filename, (spiffs_flags)mode, 0);
int code = SPIFFS_errno(&_filesystemStorageHandle); int code = SPIFFS_errno(&_filesystemStorageHandle);
if (res < 0){ if (res < 0){
debugf("open errno %d\n", code); SPIFFS_API_DBG_E("open errno %d\n", code);
notExist = (code == SPIFFS_ERR_NOT_FOUND || code == SPIFFS_ERR_DELETED || code == SPIFFS_ERR_FILE_DELETED || code == SPIFFS_ERR_IS_FREE); notExist = (code == SPIFFS_ERR_NOT_FOUND || code == SPIFFS_ERR_DELETED || code == SPIFFS_ERR_FILE_DELETED || code == SPIFFS_ERR_IS_FREE);
if (notExist && canRecreate) if (notExist && canRecreate)
remove(filename); // fix for deleted files remove(filename); // fix for deleted files
@ -84,6 +111,14 @@ FSFile FSClass::open(const char *filename, uint8_t mode) {
return FSFile(); return FSFile();
} }
FSFile FSClass::open(spiffs_dirent* entry, uint8_t mode){
int res = SPIFFS_open_by_dirent(&_filesystemStorageHandle, entry, (spiffs_flags)mode, 0);
if(res){
return FSFile(res);
}
return FSFile();
}
FSClass FS; FSClass FS;
FSFile::FSFile() { FSFile::FSFile() {
@ -91,95 +126,140 @@ FSFile::FSFile() {
_stats = {0}; _stats = {0};
} }
FSFile::FSFile(String path) {
if(path == "/"){
_file = 0x1;
os_sprintf((char*)_stats.name, "%s", (char*)path.c_str());
_stats.size = 0;
_stats.type = SPIFFS_TYPE_DIR;
SPIFFS_opendir(&_filesystemStorageHandle, (char*)_stats.name, &_dir);
} else {
_file = SPIFFS_open(&_filesystemStorageHandle, (char *)path.c_str(), (spiffs_flags)FSFILE_READ, 0);
if(SPIFFS_fstat(&_filesystemStorageHandle, _file, &_stats) != 0){
SPIFFS_API_DBG_E("fstat errno %d\n", SPIFFS_errno(&_filesystemStorageHandle));
}
//debugf("FSFile name: %s, size: %d, type: %d\n", _stats.name, _stats.size, _stats.type);
if(_stats.type == SPIFFS_TYPE_DIR){
SPIFFS_opendir(&_filesystemStorageHandle, (char*)_stats.name, &_dir);
}
}
}
FSFile::FSFile(file_t f) { FSFile::FSFile(file_t f) {
_file = f; _file = f;
if(SPIFFS_fstat(&_filesystemStorageHandle, _file, &_stats) != 0){ if(SPIFFS_fstat(&_filesystemStorageHandle, _file, &_stats) != 0){
debugf("mount errno %d\n", SPIFFS_errno(&_filesystemStorageHandle)); SPIFFS_API_DBG_E("fstat errno %d\n", SPIFFS_errno(&_filesystemStorageHandle));
}
//debugf("FSFile name: %s, size: %d, type: %d\n", _stats.name, _stats.size, _stats.type);
if(_stats.type == SPIFFS_TYPE_DIR){
SPIFFS_opendir(&_filesystemStorageHandle, (char*)_stats.name, &_dir);
} }
} }
void FSFile::close() { void FSFile::close() {
if (! _file) return; if (! _file) return;
if(_stats.type == SPIFFS_TYPE_DIR){
SPIFFS_closedir(&_dir);
}
if(os_strlen((char*)_stats.name) > 1)
SPIFFS_close(&_filesystemStorageHandle, _file); SPIFFS_close(&_filesystemStorageHandle, _file);
_file = 0; _file = 0;
} }
char * FSFile::name(){
return (char*)_stats.name;
}
bool FSFile::isDirectory(void) {
return _stats.type == SPIFFS_TYPE_DIR;
}
void FSFile::rewindDirectory() {
if (!_file || !isDirectory()) return;
SPIFFS_closedir(&_dir);
SPIFFS_opendir(&_filesystemStorageHandle, (char*)_stats.name, &_dir);
}
FSFile FSFile::openNextFile(){
if (!_file || !isDirectory()) return FSFile();
struct spiffs_dirent e;
struct spiffs_dirent *pe = &e;
if ((pe = SPIFFS_readdir(&_dir, pe))){
return FS.open((char *)pe->name);
}
return FSFile();
}
uint32_t FSFile::size() { uint32_t FSFile::size() {
if(! _file) return 0; if(!_file) return 0;
if(_stats.size) return _stats.size;
uint32_t pos = SPIFFS_tell(&_filesystemStorageHandle, _file); uint32_t pos = SPIFFS_tell(&_filesystemStorageHandle, _file);
SPIFFS_lseek(&_filesystemStorageHandle, _file, 0, SPIFFS_SEEK_END); SPIFFS_lseek(&_filesystemStorageHandle, _file, 0, SPIFFS_SEEK_END);
uint32_t size = SPIFFS_tell(&_filesystemStorageHandle, _file); _stats.size = SPIFFS_tell(&_filesystemStorageHandle, _file);
SPIFFS_lseek(&_filesystemStorageHandle, _file, pos, SPIFFS_SEEK_SET); SPIFFS_lseek(&_filesystemStorageHandle, _file, pos, SPIFFS_SEEK_SET);
return size; return _stats.size;
}
int FSFile::available() {
if (!_file) return 0;
uint32_t pos = SPIFFS_tell(&_filesystemStorageHandle, _file);
return size() - pos;
} }
uint32_t FSFile::seek(uint32_t pos) { uint32_t FSFile::seek(uint32_t pos) {
if (! _file) return 0; if (!_file) return 0;
return SPIFFS_lseek(&_filesystemStorageHandle, _file, pos, SPIFFS_SEEK_SET); return SPIFFS_lseek(&_filesystemStorageHandle, _file, pos, SPIFFS_SEEK_SET);
} }
uint32_t FSFile::position() { uint32_t FSFile::position() {
if (! _file) return 0; if (!_file) return 0;
return SPIFFS_tell(&_filesystemStorageHandle, _file); return SPIFFS_tell(&_filesystemStorageHandle, _file);
} }
bool FSFile::eof() { bool FSFile::eof() {
if (! _file) return 0; if (!_file) return 0;
return SPIFFS_eof(&_filesystemStorageHandle, _file); return SPIFFS_eof(&_filesystemStorageHandle, _file);
} }
bool FSFile::isDirectory(void) {
return false;
}
int FSFile::read(void *buf, uint16_t nbyte) { int FSFile::read(void *buf, uint16_t nbyte) {
if (! _file) return -1; if (! _file || isDirectory()) return -1;
return SPIFFS_read(&_filesystemStorageHandle, _file, buf, nbyte); return SPIFFS_read(&_filesystemStorageHandle, _file, buf, nbyte);
} }
int FSFile::read() { int FSFile::read() {
if (! _file) return -1; if (! _file || isDirectory()) return -1;
int val; int val;
if(SPIFFS_read(&_filesystemStorageHandle, _file, &val, 1) != 1) return -1; if(SPIFFS_read(&_filesystemStorageHandle, _file, &val, 1) != 1) return -1;
return val; return val;
} }
int FSFile::peek() { int FSFile::peek() {
if (! _file) return 0; if (! _file || isDirectory()) return 0;
int c = read(); int c = read();
SPIFFS_lseek(&_filesystemStorageHandle, _file, -1, SPIFFS_SEEK_CUR); SPIFFS_lseek(&_filesystemStorageHandle, _file, -1, SPIFFS_SEEK_CUR);
return c; return c;
} }
int FSFile::available() {
if (! _file) return 0;
uint32_t pos = SPIFFS_tell(&_filesystemStorageHandle, _file);
SPIFFS_lseek(&_filesystemStorageHandle, _file, 0, SPIFFS_SEEK_END);
uint32_t size = SPIFFS_tell(&_filesystemStorageHandle, _file);
SPIFFS_lseek(&_filesystemStorageHandle, _file, pos, SPIFFS_SEEK_SET);
return size - pos;
}
size_t FSFile::write(const uint8_t *buf, size_t size){ size_t FSFile::write(const uint8_t *buf, size_t size){
if (! _file) return 0; if (! _file || isDirectory()) return 0;
int res = SPIFFS_write(&_filesystemStorageHandle, _file, (uint8_t *)buf, size); int res = SPIFFS_write(&_filesystemStorageHandle, _file, (uint8_t *)buf, size);
return (res > 0)?(size_t)res:0; return (res > 0)?(size_t)res:0;
} }
size_t FSFile::write(uint8_t val) { size_t FSFile::write(uint8_t val) {
if (! _file) return 0; if (! _file || isDirectory()) return 0;
return write(&val, 1); return write(&val, 1);
} }
void FSFile::flush(){ void FSFile::flush(){
if (! _file) return; if (! _file || isDirectory()) return;
SPIFFS_fflush(&_filesystemStorageHandle, _file); SPIFFS_fflush(&_filesystemStorageHandle, _file);
} }
uint32_t FSFile::remove(){ bool FSFile::remove(){
if (! _file) return 0; if (! _file) return 0;
return SPIFFS_fremove(&_filesystemStorageHandle, _file); close();
_file = 0; return SPIFFS_remove(&_filesystemStorageHandle, (char *)_stats.name) == 0;
} }
int FSFile::lastError(){ int FSFile::lastError(){
@ -187,28 +267,5 @@ int FSFile::lastError(){
} }
void FSFile::clearError(){ void FSFile::clearError(){
_filesystemStorageHandle.errno = SPIFFS_OK; _filesystemStorageHandle.err_code = SPIFFS_OK;
} }
char * FSFile::name(){
return 0;
}
/*
spiffs_DIR *dirOpen(spiffs_DIR *d){
return SPIFFS_opendir(&_filesystemStorageHandle, 0, d);
}
int dirClose(spiffs_DIR *d){
return SPIFFS_closedir(d);
}
file_t dirOpenFile(spiffs_dirent* entry, uint8_t flags){
return SPIFFS_open_by_dirent(&_filesystemStorageHandle, entry, (spiffs_flags)flags, 0);
}
*/

31
cores/esp8266/FileSystem.h Executable file → Normal file
View File

@ -26,14 +26,17 @@
class String; class String;
#define FSFILE_READ SPIFFS_RDONLY #define FSFILE_READ SPIFFS_RDONLY
#define FSFILE_WRITE (SPIFFS_RDONLY | SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_APPEND | SPIFFS_TRUNC) #define FSFILE_WRITE (SPIFFS_RDONLY | SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_APPEND )
#define FSFILE_OVERWRITE (SPIFFS_RDONLY | SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_APPEND | SPIFFS_TRUNC )
class FSFile : public Stream { class FSFile : public Stream {
private: private:
spiffs_stat _stats; spiffs_stat _stats;
file_t _file; file_t _file;
spiffs_DIR _dir;
public: public:
FSFile(String path);
FSFile(file_t f); FSFile(file_t f);
FSFile(void); FSFile(void);
virtual size_t write(uint8_t); virtual size_t write(uint8_t);
@ -44,16 +47,18 @@ public:
virtual void flush(); virtual void flush();
int read(void *buf, uint16_t nbyte); int read(void *buf, uint16_t nbyte);
uint32_t seek(uint32_t pos); uint32_t seek(uint32_t pos);
uint32_t remove();
uint32_t position(); uint32_t position();
uint32_t size(); uint32_t size();
bool eof(); bool eof();
void close(); void close();
bool remove();
int lastError(); int lastError();
void clearError(); void clearError();
operator bool() { return _file > 0; } operator bool() { return _file > 0; }
char * name(); char * name();
bool isDirectory(void); bool isDirectory(void);
void rewindDirectory(void);
FSFile openNextFile(void);
template<typename T> size_t write(T &src){ template<typename T> size_t write(T &src){
const size_t bufferSize = 64; const size_t bufferSize = 64;
@ -80,18 +85,28 @@ public:
class FSClass { class FSClass {
private: private:
bool _mounted = false;
public: public:
bool mount(); bool mount();
void unmount(); void unmount();
bool format(); bool format();
bool exists(const char *filename); bool check();
bool create(const char *filepath); bool exists(char *filename);
bool remove(const char *filepath); bool create(char *filepath);
bool rename(const char *filename, const char *newname); bool remove(char *filepath);
bool rename(char *filename, char *newname);
size_t totalBytes();
size_t usedBytes();
size_t size(){ return _filesystemStorageHandle.cfg.phys_size; }
size_t blockSize(){ return _filesystemStorageHandle.cfg.log_block_size; }
size_t totalBlocks(){ return _filesystemStorageHandle.block_count; }
size_t freeBlocks(){ return _filesystemStorageHandle.free_blocks; }
size_t pageSize(){ return _filesystemStorageHandle.cfg.log_page_size; }
size_t allocatedPages(){ return _filesystemStorageHandle.stats_p_allocated; }
size_t deletedPages(){ return _filesystemStorageHandle.stats_p_deleted; }
FSFile open(const char *filename, uint8_t mode = FSFILE_READ); FSFile open(char *filename, uint8_t mode = FSFILE_READ);
FSFile open(spiffs_dirent* entry, uint8_t mode = FSFILE_READ);
private: private:
friend class FSFile; friend class FSFile;

View File

@ -120,7 +120,7 @@ void ICACHE_RAM_ATTR uart_interrupt_handler(uart_t* uart) {
// -------------- UART 0 -------------- // -------------- UART 0 --------------
if(Serial.isRxEnabled()) { if(Serial.isRxEnabled()) {
while(U0IS & (1 << UIFF)) { while(U0IS & (1 << UIFF)) {
Serial._rx_complete_irq((char)(U0F & 0xff)); Serial._rx_complete_irq((char) (U0F & 0xff));
U0IC = (1 << UIFF); U0IC = (1 << UIFF);
} }
} }
@ -135,7 +135,7 @@ void ICACHE_RAM_ATTR uart_interrupt_handler(uart_t* uart) {
if(Serial1.isRxEnabled()) { if(Serial1.isRxEnabled()) {
while(U1IS & (1 << UIFF)) { while(U1IS & (1 << UIFF)) {
Serial1._rx_complete_irq((char)(U1F & 0xff)); Serial1._rx_complete_irq((char) (U1F & 0xff));
U1IC = (1 << UIFF); U1IC = (1 << UIFF);
} }
} }
@ -357,19 +357,19 @@ void uart_swap(uart_t* uart) {
switch(uart->uart_nr) { switch(uart->uart_nr) {
case UART0: case UART0:
if(uart->txPin == 1 && uart->rxPin == 3) { if(uart->txPin == 1 && uart->rxPin == 3) {
pinMode(15, FUNCTION_4);//TX pinMode(15, FUNCTION_4); //TX
pinMode(13, FUNCTION_4);//RX pinMode(13, FUNCTION_4); //RX
USWAP |= (1 << USWAP0); USWAP |= (1 << USWAP0);
pinMode(1, INPUT);//TX pinMode(1, INPUT); //TX
pinMode(3, INPUT);//RX pinMode(3, INPUT); //RX
uart->rxPin = 13; uart->rxPin = 13;
uart->txPin = 15; uart->txPin = 15;
} else { } else {
pinMode(1, SPECIAL);//TX pinMode(1, SPECIAL); //TX
pinMode(3, SPECIAL);//RX pinMode(3, SPECIAL); //RX
USWAP &= ~(1 << USWAP0); USWAP &= ~(1 << USWAP0);
pinMode(15, INPUT);//TX pinMode(15, INPUT); //TX
pinMode(13, INPUT);//RX pinMode(13, INPUT); //RX
uart->rxPin = 3; uart->rxPin = 3;
uart->txPin = 1; uart->txPin = 1;
} }
@ -392,30 +392,50 @@ void uart_ignore_char(char c) {
void uart0_write_char(char c) { void uart0_write_char(char c) {
if(&Serial != NULL && Serial.isTxEnabled()) { if(&Serial != NULL && Serial.isTxEnabled()) {
if(Serial.availableForWrite() > 0) {
if(c == '\n') { if(c == '\n') {
Serial.write('\r'); Serial.write('\r');
} }
Serial.write(c); Serial.write(c);
} else { return;
}
}
// wait for the Hardware FIFO
while(true) {
if(((USS(0) >> USTXC) & 0xff) <= (UART_TX_FIFO_SIZE - 2)) {
break;
}
}
if(c == '\n') { if(c == '\n') {
USF(0) = '\r'; USF(0) = '\r';
} }
USF(0) = c; USF(0) = c;
}
} }
void uart1_write_char(char c) { void uart1_write_char(char c) {
if(&Serial1 != NULL && Serial1.isTxEnabled()) { if(&Serial1 != NULL && Serial1.isTxEnabled()) {
if(Serial1.availableForWrite() > 0) {
if(c == '\n') { if(c == '\n') {
Serial1.write('\r'); Serial1.write('\r');
} }
Serial1.write(c); Serial1.write(c);
} else { return;
}
}
// wait for the Hardware FIFO
while(true) {
if(((USS(1) >> USTXC) & 0xff) <= (UART_TX_FIFO_SIZE - 2)) {
break;
}
}
if(c == '\n') { if(c == '\n') {
USF(1) = '\r'; USF(1) = '\r';
} }
USF(1) = c; USF(1) = c;
}
} }
static int s_uart_debug_nr = UART0; static int s_uart_debug_nr = UART0;
@ -465,11 +485,11 @@ void HardwareSerial::begin(unsigned long baud, byte config) {
} }
if(_uart->rxEnabled) { if(_uart->rxEnabled) {
if (!_rx_buffer) if(!_rx_buffer)
_rx_buffer = new cbuf(SERIAL_RX_BUFFER_SIZE); _rx_buffer = new cbuf(SERIAL_RX_BUFFER_SIZE);
} }
if(_uart->txEnabled) { if(_uart->txEnabled) {
if (!_tx_buffer) if(!_tx_buffer)
_tx_buffer = new cbuf(SERIAL_TX_BUFFER_SIZE); _tx_buffer = new cbuf(SERIAL_TX_BUFFER_SIZE);
} }
_written = false; _written = false;

View File

@ -30,6 +30,7 @@
#include "Print.h" #include "Print.h"
extern "C" { extern "C" {
#include "c_types.h" #include "c_types.h"
#include "ets_sys.h"
} }
// Public Methods ////////////////////////////////////////////////////////////// // Public Methods //////////////////////////////////////////////////////////////
@ -43,6 +44,16 @@ size_t ICACHE_FLASH_ATTR Print::write(const uint8_t *buffer, size_t size) {
return n; return n;
} }
size_t Print::printf(const char *format, ...) {
va_list arg;
va_start(arg, format);
char temp[256];
size_t len = ets_vsnprintf(temp, 256, format, arg);
len = print(temp);
va_end(arg);
return len;
}
size_t ICACHE_FLASH_ATTR Print::print(const __FlashStringHelper *ifsh) { size_t ICACHE_FLASH_ATTR Print::print(const __FlashStringHelper *ifsh) {
PGM_P p = reinterpret_cast<PGM_P>(ifsh); PGM_P p = reinterpret_cast<PGM_P>(ifsh);

View File

@ -63,6 +63,7 @@ class Print {
return write((const uint8_t *) buffer, size); return write((const uint8_t *) buffer, size);
} }
size_t printf(const char * format, ...);
size_t print(const __FlashStringHelper *); size_t print(const __FlashStringHelper *);
size_t print(const String &); size_t print(const String &);
size_t print(const char[]); size_t print(const char[]);

View File

@ -26,10 +26,12 @@ extern "C" {
} }
void *operator new(size_t size) { void *operator new(size_t size) {
size = ((size + 3) & ~((size_t)0x3));
return os_malloc(size); return os_malloc(size);
} }
void *operator new[](size_t size) { void *operator new[](size_t size) {
size = ((size + 3) & ~((size_t)0x3));
return os_malloc(size); return os_malloc(size);
} }

View File

@ -188,10 +188,10 @@ char * dtostrf(double number, signed char width, unsigned char prec, char *s) {
++out; ++out;
} }
while(prec-- > 0) { for (unsigned char decShift = prec; decShift > 0; decShift--) {
remainder *= 10.0; remainder *= 10.0;
} }
sprintf(out, "%d", (int) remainder); sprintf(out, "%0*d", prec, (int)remainder);
return s; return s;
} }

View File

@ -38,6 +38,7 @@
#include "user_interface.h" #include "user_interface.h"
void* malloc(size_t size) { void* malloc(size_t size) {
size = ((size + 3) & ~((size_t)0x3));
return os_malloc(size); return os_malloc(size);
} }
@ -46,6 +47,7 @@ void free(void* ptr) {
} }
void* realloc(void* ptr, size_t size) { void* realloc(void* ptr, size_t size) {
size = ((size + 3) & ~((size_t)0x3));
return os_realloc(ptr, size); return os_realloc(ptr, size);
} }

View File

@ -1,44 +0,0 @@
#############################################################
# Required variables for each makefile
# Discard this section from all parent makefiles
# Expected variables (with automatic defaults):
# CSRCS (all "C" files in the dir)
# SUBDIRS (all subdirs with a Makefile)
# GEN_LIBS - list of libs to be generated ()
# GEN_IMAGES - list of images to be generated ()
# COMPONENTS_xxx - a list of libs/objs in the form
# subdir/lib to be extracted and rolled up into
# a generated lib/image xxx.a ()
#
ifndef PDIR
GEN_LIBS = spiffs.a
endif
#############################################################
# Configuration i.e. compile options etc.
# Target specific stuff (defines etc.) goes in here!
# Generally values applying to a tree are captured in the
# makefile at its root level - these are then overridden
# for a subtree within the makefile rooted therein
#
#DEFINES +=
#############################################################
# Recursion Magic - Don't touch this!!
#
# Each subtree potentially has an include directory
# corresponding to the common APIs applicable to modules
# rooted at that subtree. Accordingly, the INCLUDE PATH
# of a module can only contain the include directories up
# its parent path, and not its siblings
#
# Required for each makefile to inherit from the parent
#
INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./
INCLUDES += -I ../libc
INCLUDES += -I ../platform
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile

View File

@ -0,0 +1,86 @@
SPIFFS (SPI Flash File System)
V0.3.0
Copyright (c) 2013-2015 Peter Andersson (pelleplutt1976<at>gmail.com)
For legal stuff, see LICENCE in this directory. Basically, you may do whatever
you want with the source. Use, modify, sell, print it out, roll it and smoke it
- as long as I won't be held responsible.
Love to hear feedback though!
* INTRODUCTION
Spiffs is a file system intended for SPI NOR flash devices on embedded targets.
Spiffs is designed with following characteristics in mind:
- Small (embedded) targets, sparse RAM without heap
- Only big areas of data (blocks) can be erased
- An erase will reset all bits in block to ones
- Writing pulls one to zeroes
- Zeroes can only be pulled to ones by erase
- Wear leveling
* FEATURES
What spiffs does:
- Specifically designed for low ram usage
- Uses statically sized ram buffers, independent of number of files
- Posix-like api: open, close, read, write, seek, stat, etc
- It can be run on any NOR flash, not only SPI flash - theoretically also on
embedded flash of an microprocessor
- Multiple spiffs configurations can be run on same target - and even on same
SPI flash device
- Implements static wear leveling
- Built in file system consistency checks
What spiffs does not:
- Presently, spiffs does not support directories. It produces a flat
structure. Creating a file with path "tmp/myfile.txt" will create a file
called "tmp/myfile.txt" instead of a "myfile.txt" under directory "tmp".
- It is not a realtime stack. One write operation might take much longer than
another.
- Poor scalability. Spiffs is intended for small memory devices - the normal
sizes for SPI flashes. Going beyond ~128MB is probably a bad idea. This is
a side effect of the design goal to use as little ram as possible.
- Presently, it does not detect or handle bad blocks.
* MORE INFO
For integration, see the docs/INTEGRATION file.
For use and design, see the docs/TECH_SPEC file.
For testing and contributions, see the docs/IMPLEMENTING file.
* HISTORY
0.3.0
Added existing namecheck when creating files
Lots of static analysis bugs #6
Added rename func
Fix SPIFFS_read length when reading beyond file size
Added reading beyond file length testcase
Made build a bit more configurable
Changed name in spiffs from "errno" to "err_code" due to conflicts compiling
in mingw
Improved GC checks, fixed an append bug, more robust truncate for very special
case
GC checks preempts GC, truncate even less picky
Struct alignment needed for some targets, define in spiffs config #10
Spiffs filesystem magic, definable in config
New config defines:
SPIFFS_USE_MAGIC - enable or disable magic check upon mount
SPIFFS_ALIGNED_OBJECT_INDEX_TABLES - alignment for certain targets
New API functions:
SPIFFS_rename - rename files
SPIFFS_clearerr - clears last errno
SPIFFS_info - returns info on used and total bytes in fs
SPIFFS_format - formats the filesystem
SPIFFS_mounted - checks if filesystem is mounted

View File

15
cores/esp8266/spiffs/TODO Normal file
View File

@ -0,0 +1,15 @@
* When mending lost pages, also see if they fit into length specified in object index header
SPIFFS2 thoughts
* Instead of exact object id:s in the object lookup tables, use a hash of span index and object id.
Eg. object id xor:ed with bit-reversed span index.
This should decrease number of actual pages that needs to be visited when looking thru the obj lut.
* Logical number of each block. When moving stuff in a garbage collected page, the free
page is assigned the same number as the garbage collected. Thus, object index pages do not have to
be rewritten.
* Steal one page, use as a bit parity page. When starting an fs modification operation, write one bit
as zero. When ending, write another bit as zero. On mount, if number of zeroes in page is uneven, a
check is automatically run.

View File

@ -1 +0,0 @@
* When mending lost pages, also see if they fit into length specified in object index header

View File

@ -1,73 +0,0 @@
// Based on NodeMCU platform_flash
// https://github.com/nodemcu/nodemcu-firmware
#ifndef SYSTEM_FLASHMEM_H_
#define SYSTEM_FLASHMEM_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "spiffs.h"
#include "spi_flash.h"
#define INTERNAL_FLASH_WRITE_UNIT_SIZE 4
#define INTERNAL_FLASH_READ_UNIT_SIZE 4
#define FLASH_TOTAL_SEC_COUNT (flashmem_get_size_sectors())
#define SYS_PARAM_SEC_COUNT 4
#define FLASH_WORK_SEC_COUNT (FLASH_TOTAL_SEC_COUNT - SYS_PARAM_SEC_COUNT)
#define INTERNAL_FLASH_SECTOR_SIZE SPI_FLASH_SEC_SIZE
#define INTERNAL_FLASH_SIZE ( (FLASH_WORK_SEC_COUNT) * INTERNAL_FLASH_SECTOR_SIZE )
#define INTERNAL_FLASH_START_ADDRESS 0x40200000
typedef struct
{
uint8_t unknown0;
uint8_t unknown1;
enum
{
MODE_QIO = 0,
MODE_QOUT = 1,
MODE_DIO = 2,
MODE_DOUT = 15,
} mode : 8;
enum
{
SPEED_40MHZ = 0,
SPEED_26MHZ = 1,
SPEED_20MHZ = 2,
SPEED_80MHZ = 15,
} speed : 4;
enum
{
SIZE_4MBIT = 0,
SIZE_2MBIT = 1,
SIZE_8MBIT = 2,
SIZE_16MBIT = 3,
SIZE_32MBIT = 4,
} size : 4;
} STORE_TYPEDEF_ATTR SPIFlashInfo;
extern uint32_t flashmem_write( const void *from, uint32_t toaddr, uint32_t size );
extern uint32_t flashmem_read( void *to, uint32_t fromaddr, uint32_t size );
extern bool flashmem_erase_sector( uint32_t sector_id );
extern SPIFlashInfo flashmem_get_info();
extern uint8_t flashmem_get_size_type();
extern uint32_t flashmem_get_size_bytes();
extern uint16_t flashmem_get_size_sectors();
uint32_t flashmem_find_sector( uint32_t address, uint32_t *pstart, uint32_t *pend );
uint32_t flashmem_get_sector_of_address( uint32_t addr );
extern uint32_t flashmem_write_internal( const void *from, uint32_t toaddr, uint32_t size );
extern uint32_t flashmem_read_internal( void *to, uint32_t fromaddr, uint32_t size );
extern uint32_t flashmem_get_first_free_block_address();
#ifdef __cplusplus
}
#endif
#endif /* SYSTEM_FLASHMEM_H_ */

View File

@ -1,146 +0,0 @@
#include "spiffs.h"
#define LOG_PAGE_SIZE 256
spiffs _filesystemStorageHandle;
static u8_t spiffs_work_buf[LOG_PAGE_SIZE*2];
static u8_t spiffs_fds[32*4];
static u8_t spiffs_cache[(LOG_PAGE_SIZE+32)*4];
static s32_t api_spiffs_read(u32_t addr, u32_t size, u8_t *dst){
flashmem_read(dst, addr, size);
return SPIFFS_OK;
}
static s32_t api_spiffs_write(u32_t addr, u32_t size, u8_t *src){
flashmem_write(src, addr, size);
return SPIFFS_OK;
}
static s32_t api_spiffs_erase(u32_t addr, u32_t size){
debugf("api_spiffs_erase");
u32_t sect_first = flashmem_get_sector_of_address(addr);
u32_t sect_last = sect_first;
while( sect_first <= sect_last )
if( !flashmem_erase_sector( sect_first ++ ) )
return SPIFFS_ERR_INTERNAL;
return SPIFFS_OK;
}
/*******************
The W25Q32BV array is organized into 16,384 programmable pages of 256-bytes each. Up to 256 bytes can be programmed at a time.
Pages can be erased in groups of 16 (4KB sector erase), groups of 128 (32KB block erase), groups of 256 (64KB block erase) or
the entire chip (chip erase). The W25Q32BV has 1,024 erasable sectors and 64 erasable blocks respectively.
The small 4KB sectors allow for greater flexibility in applications that require data and parameter storage.
********************/
extern uint32_t _SPIFFS_start;
extern uint32_t _SPIFFS_end;
spiffs_config spiffs_get_storage_config()
{
spiffs_config cfg = {0};
if ((u32_t)&_SPIFFS_start == 0) return cfg;
cfg.phys_addr = (u32_t)&_SPIFFS_start;
cfg.phys_size = (u32_t)((u32_t)&_SPIFFS_end - (u32_t)&_SPIFFS_start);
cfg.phys_erase_block = INTERNAL_FLASH_SECTOR_SIZE; // according to datasheet
cfg.log_block_size = INTERNAL_FLASH_SECTOR_SIZE * 2; // Important to make large
cfg.log_page_size = LOG_PAGE_SIZE; // as we said
return cfg;
}
bool spiffs_format_internal(){
spiffs_config cfg = spiffs_get_storage_config();
if (cfg.phys_addr == 0){
SYSTEM_ERROR("Can't format file system, wrong address");
return false;
}
u32_t sect_first, sect_last;
sect_first = flashmem_get_first_free_block_address();
sect_last = flashmem_get_sector_of_address((u32_t)&_SPIFFS_end);
debugf("sect_first: %x, sect_last: %x\n", sect_first, sect_last);
while( sect_first <= sect_last ){
if(!flashmem_erase_sector( sect_first ++ ))
return false;
}
return true;
}
bool spiffs_mount(){
spiffs_config cfg = spiffs_get_storage_config();
if (cfg.phys_addr == 0){
SYSTEM_ERROR("Can't start file system, wrong address");
return false;
}
debugf("fs.start:%x, size:%d Kb\n", cfg.phys_addr, cfg.phys_size / 1024);
cfg.hal_read_f = api_spiffs_read;
cfg.hal_write_f = api_spiffs_write;
cfg.hal_erase_f = api_spiffs_erase;
uint32_t dat;
bool writeFirst = false;
flashmem_read(&dat, cfg.phys_addr, 4);
if (dat == UINT32_MAX){
debugf("First init file system");
if(!spiffs_format_internal()){
SYSTEM_ERROR("Can't format file system");
return false;
}
writeFirst = true;
}
int res = SPIFFS_mount(&_filesystemStorageHandle,
&cfg,
spiffs_work_buf,
spiffs_fds,
sizeof(spiffs_fds),
spiffs_cache,
sizeof(spiffs_cache),
NULL);
debugf("mount res: %d\n", res);
if(res != 0) return false;
if (writeFirst){
file_t fd = SPIFFS_open(&_filesystemStorageHandle, "initialize_fs_header.dat", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0);
SPIFFS_write(&_filesystemStorageHandle, fd, (u8_t *)"1", 1);
SPIFFS_fremove(&_filesystemStorageHandle, fd);
SPIFFS_close(&_filesystemStorageHandle, fd);
}
return true;
}
void spiffs_unmount(){
SPIFFS_unmount(&_filesystemStorageHandle);
}
bool spiffs_format(){
spiffs_unmount();
if(!spiffs_format_internal()) return false;
spiffs_mount();
return true;
}
void test_spiffs(){
char buf[12] = {0};
spiffs_file fd;
spiffs_stat st = {0};
SPIFFS_stat(&_filesystemStorageHandle, "my_file.txt", &st);
if (st.size <= 0){
fd = SPIFFS_open(&_filesystemStorageHandle, "my_file.txt", SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0);
if (SPIFFS_write(&_filesystemStorageHandle, fd, (u8_t *)"Hello world", 11) < 0)
debugf("errno %d\n", SPIFFS_errno(&_filesystemStorageHandle));
SPIFFS_close(&_filesystemStorageHandle, fd);
debugf("file created");
} else debugf("file %s exist :)", st.name);
fd = SPIFFS_open(&_filesystemStorageHandle, "my_file.txt", SPIFFS_RDWR, 0);
if (SPIFFS_read(&_filesystemStorageHandle, fd, (u8_t *)buf, 11) < 0) debugf("errno %d\n", SPIFFS_errno(&_filesystemStorageHandle));
SPIFFS_close(&_filesystemStorageHandle, fd);
debugf("--> %s <--\n", buf);
}

112
cores/esp8266/spiffs/spiffs.h Executable file → Normal file
View File

@ -5,15 +5,15 @@
* Author: petera * Author: petera
*/ */
#ifndef SPIFFS_H_ #ifndef SPIFFS_H_
#define SPIFFS_H_ #define SPIFFS_H_
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#include "spiffs_config.h" #include "spiffs_config.h"
#include "flashmem.h"
#define SPIFFS_OK 0 #define SPIFFS_OK 0
#define SPIFFS_ERR_NOT_MOUNTED -10000 #define SPIFFS_ERR_NOT_MOUNTED -10000
@ -40,6 +40,13 @@ extern "C" {
#define SPIFFS_ERR_NOT_WRITABLE -10021 #define SPIFFS_ERR_NOT_WRITABLE -10021
#define SPIFFS_ERR_NOT_READABLE -10022 #define SPIFFS_ERR_NOT_READABLE -10022
#define SPIFFS_ERR_CONFLICTING_NAME -10023 #define SPIFFS_ERR_CONFLICTING_NAME -10023
#define SPIFFS_ERR_NOT_CONFIGURED -10024
#define SPIFFS_ERR_NOT_A_FS -10025
#define SPIFFS_ERR_MOUNTED -10026
#define SPIFFS_ERR_ERASE_FAIL -10027
#define SPIFFS_ERR_MAGIC_NOT_POSSIBLE -10028
#define SPIFFS_ERR_INTERNAL -10050 #define SPIFFS_ERR_INTERNAL -10050
@ -81,19 +88,21 @@ typedef enum {
} spiffs_check_report; } spiffs_check_report;
/* file system check callback function */ /* file system check callback function */
typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report, u32_t arg1, u32_t arg2); typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report,
u32_t arg1, u32_t arg2);
#ifndef SPIFFS_DBG #ifndef SPIFFS_DBG
#define SPIFFS_DBG(...) printf(__VA_ARGS__) #define SPIFFS_DBG(...) \
print(__VA_ARGS__)
#endif #endif
#ifndef SPIFFS_GC_DBG #ifndef SPIFFS_GC_DBG
#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__) #define SPIFFS_GC_DBG(...) c_printf(__VA_ARGS__)
#endif #endif
#ifndef SPIFFS_CACHE_DBG #ifndef SPIFFS_CACHE_DBG
#define SPIFFS_CACHE_DBG(...) printf(__VA_ARGS__) #define SPIFFS_CACHE_DBG(...) c_printf(__VA_ARGS__)
#endif #endif
#ifndef SPIFFS_CHECK_DBG #ifndef SPIFFS_CHECK_DBG
#define SPIFFS_CHECK_DBG(...) printf(__VA_ARGS__) #define SPIFFS_CHECK_DBG(...) c_printf(__VA_ARGS__)
#endif #endif
/* Any write to the filehandle is appended to end of the file */ /* Any write to the filehandle is appended to end of the file */
@ -182,7 +191,7 @@ typedef struct {
u32_t fd_count; u32_t fd_count;
// last error // last error
s32_t errno; s32_t err_code;
// current number of free blocks // current number of free blocks
u32_t free_blocks; u32_t free_blocks;
@ -212,6 +221,11 @@ typedef struct {
// check callback function // check callback function
spiffs_check_callback check_cb_f; spiffs_check_callback check_cb_f;
// mounted flag
u8_t mounted;
// config magic
u32_t config_magic;
} spiffs; } spiffs;
/* spiffs file status struct */ /* spiffs file status struct */
@ -239,7 +253,10 @@ typedef struct {
// functions // functions
/** /**
* Initializes the file system dynamic parameters and mounts the filesystem * Initializes the file system dynamic parameters and mounts the filesystem.
* If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS
* if the flash does not contain a recognizable file system.
* In this case, SPIFFS_format must be called prior to remounting.
* @param fs the file system struct * @param fs the file system struct
* @param config the physical and logical configuration of the file system * @param config the physical and logical configuration of the file system
* @param work a memory work buffer comprising 2*config->log_page_size * @param work a memory work buffer comprising 2*config->log_page_size
@ -268,7 +285,7 @@ void SPIFFS_unmount(spiffs *fs);
* @param path the path of the new file * @param path the path of the new file
* @param mode ignored, for posix compliance * @param mode ignored, for posix compliance
*/ */
s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode); s32_t SPIFFS_creat(spiffs *fs, char *path, spiffs_mode mode);
/** /**
* Opens/creates a file. * Opens/creates a file.
@ -279,7 +296,8 @@ s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode);
* SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT * SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT
* @param mode ignored, for posix compliance * @param mode ignored, for posix compliance
*/ */
spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode); spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode mode);
/** /**
* Opens a file by given dir entry. * Opens a file by given dir entry.
@ -304,7 +322,7 @@ spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_fl
* @param len how much to read * @param len how much to read
* @returns number of bytes read, or -1 if error * @returns number of bytes read, or -1 if error
*/ */
s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, u32_t len); s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len);
/** /**
* Writes to given filehandle. * Writes to given filehandle.
@ -314,7 +332,7 @@ s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, u32_t len);
* @param len how much to write * @param len how much to write
* @returns number of bytes written, or -1 if error * @returns number of bytes written, or -1 if error
*/ */
s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len); s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len);
/** /**
* Moves the read/write file offset * Moves the read/write file offset
@ -332,7 +350,7 @@ s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence);
* @param fs the file system struct * @param fs the file system struct
* @param path the path of the file to remove * @param path the path of the file to remove
*/ */
s32_t SPIFFS_remove(spiffs *fs, const char *path); s32_t SPIFFS_remove(spiffs *fs, char *path);
/** /**
* Removes a file by filehandle * Removes a file by filehandle
@ -347,7 +365,7 @@ s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh);
* @param path the path of the file to stat * @param path the path of the file to stat
* @param s the stat struct to populate * @param s the stat struct to populate
*/ */
s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s); s32_t SPIFFS_stat(spiffs *fs, char *path, spiffs_stat *s);
/** /**
* Gets file status by filehandle * Gets file status by filehandle
@ -375,9 +393,9 @@ void SPIFFS_close(spiffs *fs, spiffs_file fh);
* Renames a file * Renames a file
* @param fs the file system struct * @param fs the file system struct
* @param old path of file to rename * @param old path of file to rename
* @param new new path of file * @param newPath new path of file
*/ */
s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newname); s32_t SPIFFS_rename(spiffs *fs, char *old, char *newPath);
/** /**
* Returns last error of last file operation. * Returns last error of last file operation.
@ -385,6 +403,12 @@ s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newname);
*/ */
s32_t SPIFFS_errno(spiffs *fs); s32_t SPIFFS_errno(spiffs *fs);
/**
* Clears last error.
* @param fs the file system struct
*/
void SPIFFS_clearerr(spiffs *fs);
/** /**
* Opens a directory stream corresponding to the given name. * Opens a directory stream corresponding to the given name.
* The stream is positioned at the first entry in the directory. * The stream is positioned at the first entry in the directory.
@ -394,7 +418,7 @@ s32_t SPIFFS_errno(spiffs *fs);
* @param name the name of the directory * @param name the name of the directory
* @param d pointer the directory stream to be populated * @param d pointer the directory stream to be populated
*/ */
spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d); spiffs_DIR *SPIFFS_opendir(spiffs *fs, char *name, spiffs_DIR *d);
/** /**
* Closes a directory stream * Closes a directory stream
@ -416,12 +440,51 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e);
*/ */
s32_t SPIFFS_check(spiffs *fs); s32_t SPIFFS_check(spiffs *fs);
/**
* Returns number of total bytes available and number of used bytes.
* This is an estimation, and depends on if there a many files with little
* data or few files with much data.
* NB: If used number of bytes exceeds total bytes, a SPIFFS_check should
* run. This indicates a power loss in midst of things. In worst case
* (repeated powerlosses in mending or gc) you might have to delete some files.
*
* @param fs the file system struct
* @param total total number of bytes in filesystem
* @param used used number of bytes in filesystem
*/
s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used);
/**
* Formats the entire file system. All data will be lost.
* The filesystem must not be mounted when calling this.
*
* NB: formatting is awkward. Due to backwards compatibility, SPIFFS_mount
* MUST be called prior to formatting in order to configure the filesystem.
* If SPIFFS_mount succeeds, SPIFFS_unmount must be called before calling
* SPIFFS_format.
* If SPIFFS_mount fails, SPIFFS_format can be called directly without calling
* SPIFFS_unmount first.
*/
s32_t SPIFFS_format(spiffs *fs);
/**
* Returns nonzero if spiffs is mounted, or zero if unmounted.
*/
u8_t SPIFFS_mounted(spiffs *fs);
/** /**
* Check if EOF reached. * Check if EOF reached.
* @param fs the file system struct * @param fs the file system struct
* @param fh the filehandle of the file to check * @param fh the filehandle of the file to check
*/ */
s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh); s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh);
/**
* Get the current position of the data pointer.
* @param fs the file system struct
* @param fh the filehandle of the open file
*/
s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh); s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh);
#if SPIFFS_TEST_VISUALISATION #if SPIFFS_TEST_VISUALISATION
@ -448,19 +511,10 @@ u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages);
#endif #endif
#endif #endif
#if SPIFFS_CACHE #include "spiffs_esp8266.h"
#endif
bool spiffs_mount();
void spiffs_unmount();
bool spiffs_format();
spiffs_config spiffs_get_storage_config();
extern void test_spiffs();
extern spiffs _filesystemStorageHandle;
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif /* SPIFFS_H_ */ #endif /* SPIFFS_H_ */

0
cores/esp8266/spiffs/spiffs_cache.c Executable file → Normal file
View File

4
cores/esp8266/spiffs/spiffs_check.c Executable file → Normal file
View File

@ -597,7 +597,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
data_spix_offset + i, data_pix, cur_pix); data_spix_offset + i, data_pix, cur_pix);
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
// index bad also, cannot mend this file // index bad also, cannot mend this file
SPIFFS_CHECK_DBG("PA: FIXUP: index bad %u, cannot mend - delete object\n", res); SPIFFS_CHECK_DBG("PA: FIXUP: index bad %d, cannot mend - delete object\n", res);
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0); if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0);
// delete file // delete file
res = spiffs_page_delete(fs, cur_pix); res = spiffs_page_delete(fs, cur_pix);
@ -763,7 +763,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
res = spiffs_rewrite_index(fs, p_hdr.obj_id, p_hdr.span_ix, cur_pix, objix_pix); res = spiffs_rewrite_index(fs, p_hdr.obj_id, p_hdr.span_ix, cur_pix, objix_pix);
if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) {
// index bad also, cannot mend this file // index bad also, cannot mend this file
SPIFFS_CHECK_DBG("PA: FIXUP: index bad %u, cannot mend!\n", res); SPIFFS_CHECK_DBG("PA: FIXUP: index bad %d, cannot mend!\n", res);
if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); if (fs->check_cb_f) fs->check_cb_f(SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0);
res = spiffs_page_delete(fs, cur_pix); res = spiffs_page_delete(fs, cur_pix);
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);

44
cores/esp8266/spiffs/spiffs_config.h Executable file → Normal file
View File

@ -8,24 +8,11 @@
#ifndef SPIFFS_CONFIG_H_ #ifndef SPIFFS_CONFIG_H_
#define SPIFFS_CONFIG_H_ #define SPIFFS_CONFIG_H_
// ----------- 8< ------------
// Following includes are for the linux test build of spiffs
// These may/should/must be removed/altered/replaced in your target
// #include "params_test.h"
//#include "c_stdio.h"
//#include "c_stdlib.h"
//#include "c_string.h"
#include "mem.h" #include "mem.h"
#include "c_types.h" #include "c_types.h"
#include "stddef.h" #include "stddef.h"
#include "osapi.h" #include "osapi.h"
#include "ets_sys.h" #include "ets_sys.h"
// ----------- >8 ------------
#define IRAM_ATTR __attribute__((section(".iram.text")))
#define STORE_TYPEDEF_ATTR __attribute__((aligned(4),packed))
#define STORE_ATTR __attribute__((aligned(4)))
#define SPIFFS_CHACHE 0
#define c_memcpy os_memcpy #define c_memcpy os_memcpy
#define c_printf os_printf #define c_printf os_printf
@ -56,24 +43,22 @@ typedef uint8_t u8_t;
#endif #endif
// compile time switches // compile time switches
#define debugf(fmt, ...) //os_printf(fmt"\r\n", ##__VA_ARGS__)
#define SYSTEM_ERROR(fmt, ...) //os_printf("ERROR: " fmt "\r\n", ##__VA_ARGS__)
// Set generic spiffs debug output call. // Set generic spiffs debug output call.
#ifndef SPIFFS_DGB #ifndef SPIFFS_DGB
#define SPIFFS_DBG(...) //os_printf(__VA_ARGS__) #define SPIFFS_DBG(...) //c_printf(__VA_ARGS__)
#endif #endif
// Set spiffs debug output call for garbage collecting. // Set spiffs debug output call for garbage collecting.
#ifndef SPIFFS_GC_DGB #ifndef SPIFFS_GC_DGB
#define SPIFFS_GC_DBG(...) //os_printf(__VA_ARGS__) #define SPIFFS_GC_DBG(...) //c_printf(__VA_ARGS__)
#endif #endif
// Set spiffs debug output call for caching. // Set spiffs debug output call for caching.
#ifndef SPIFFS_CACHE_DGB #ifndef SPIFFS_CACHE_DGB
#define SPIFFS_CACHE_DBG(...) //os_printf(__VA_ARGS__) #define SPIFFS_CACHE_DBG(...) //c_printf(__VA_ARGS__)
#endif #endif
// Set spiffs debug output call for system consistency checks. // Set spiffs debug output call for system consistency checks.
#ifndef SPIFFS_CHECK_DGB #ifndef SPIFFS_CHECK_DGB
#define SPIFFS_CHECK_DBG(...) //os_printf(__VA_ARGS__) #define SPIFFS_CHECK_DBG(...) //c_printf(__VA_ARGS__)
#endif #endif
// Enable/disable API functions to determine exact number of bytes // Enable/disable API functions to determine exact number of bytes
@ -108,7 +93,7 @@ typedef uint8_t u8_t;
// Define maximum number of gc runs to perform to reach desired free pages. // Define maximum number of gc runs to perform to reach desired free pages.
#ifndef SPIFFS_GC_MAX_RUNS #ifndef SPIFFS_GC_MAX_RUNS
#define SPIFFS_GC_MAX_RUNS 3 #define SPIFFS_GC_MAX_RUNS 5
#endif #endif
// Enable/disable statistics on gc. Debug/test purpose only. // Enable/disable statistics on gc. Debug/test purpose only.
@ -150,14 +135,22 @@ typedef uint8_t u8_t;
#define SPIFFS_COPY_BUFFER_STACK (64) #define SPIFFS_COPY_BUFFER_STACK (64)
#endif #endif
// Enable this to have an identifiable spiffs filesystem. This will look for
// a magic in all sectors to determine if this is a valid spiffs system or
// not on mount point. If not, SPIFFS_format must be called prior to mounting
// again.
#ifndef SPIFFS_USE_MAGIC
#define SPIFFS_USE_MAGIC (0)
#endif
// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level // SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level
// These should be defined on a multithreaded system // These should be defined on a multithreaded system
// define this to entering a mutex if you're running on a multithreaded system // define this to enter a mutex if you're running on a multithreaded system
#ifndef SPIFFS_LOCK #ifndef SPIFFS_LOCK
#define SPIFFS_LOCK(fs) #define SPIFFS_LOCK(fs)
#endif #endif
// define this to exiting a mutex if you're running on a multithreaded system // define this to exit a mutex if you're running on a multithreaded system
#ifndef SPIFFS_UNLOCK #ifndef SPIFFS_UNLOCK
#define SPIFFS_UNLOCK(fs) #define SPIFFS_UNLOCK(fs)
#endif #endif
@ -190,7 +183,12 @@ typedef uint8_t u8_t;
#endif #endif
#endif #endif
// Set SPFIFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function // Enable this if your target needs aligned data for index tables
#ifndef SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 1
#endif
// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function
// in the api. This function will visualize all filesystem using given printf // in the api. This function will visualize all filesystem using given printf
// function. // function.
#ifndef SPIFFS_TEST_VISUALISATION #ifndef SPIFFS_TEST_VISUALISATION

View File

@ -0,0 +1,224 @@
#include "spiffs_esp8266.h"
#include "spi_flash.h"
#include "esp8266_peri.h"
#include "Arduino.h"
/*
FLASH ACCESS FUNCTIONS
*/
//lowest level sector erase method
bool flashmem_erase_sector( uint32_t sector_id ){
WDT_RESET();
noInterrupts();
bool erased = spi_flash_erase_sector( sector_id ) == SPI_FLASH_RESULT_OK;
interrupts();
return erased;
}
//lowest level data write method
uint32_t flashmem_write_internal( const void *from, uint32_t toaddr, uint32_t size ){
toaddr -= INTERNAL_FLASH_START_ADDRESS;
SpiFlashOpResult r;
const uint32_t blkmask = INTERNAL_FLASH_WRITE_UNIT_SIZE - 1;
uint32_t *apbuf = NULL;
if(((uint32_t)from) & blkmask){
apbuf = (uint32_t *)os_malloc(size);
if(!apbuf)
return 0;
os_memcpy(apbuf, from, size);
}
WDT_RESET();
noInterrupts();
r = spi_flash_write(toaddr, apbuf?(uint32 *)apbuf:(uint32 *)from, size);
interrupts();
if(apbuf)
os_free(apbuf);
if(SPI_FLASH_RESULT_OK == r)
return size;
else{
SPIFFS_API_DBG_E( "ERROR in flash_write: r=%d at %08X\n", ( int )r, ( unsigned )toaddr+INTERNAL_FLASH_START_ADDRESS );
return 0;
}
}
//lowest level data read method
uint32_t flashmem_read_internal( void *to, uint32_t fromaddr, uint32_t size ){
fromaddr -= INTERNAL_FLASH_START_ADDRESS;
SpiFlashOpResult r;
WDT_RESET();
noInterrupts();
r = spi_flash_read(fromaddr, (uint32 *)to, size);
interrupts();
if(SPI_FLASH_RESULT_OK == r)
return size;
else{
SPIFFS_API_DBG_E( "ERROR in flash_read: r=%d at %08X\n", ( int )r, ( unsigned )fromaddr+INTERNAL_FLASH_START_ADDRESS );
return 0;
}
}
//mid level data write method
uint32_t flashmem_write( const void *from, uint32_t toaddr, uint32_t size ){
uint32_t temp, rest, ssize = size;
unsigned i;
char tmpdata[ INTERNAL_FLASH_WRITE_UNIT_SIZE ];
const uint8_t *pfrom = ( const uint8_t* )from;
const uint32_t blksize = INTERNAL_FLASH_WRITE_UNIT_SIZE;
const uint32_t blkmask = INTERNAL_FLASH_WRITE_UNIT_SIZE - 1;
// Align the start
if(toaddr & blkmask){
rest = toaddr & blkmask;
temp = toaddr & ~blkmask; // this is the actual aligned address
// c_memcpy( tmpdata, ( const void* )temp, blksize );
flashmem_read_internal( tmpdata, temp, blksize );
for( i = rest; size && ( i < blksize ); i ++, size --, pfrom ++ )
tmpdata[ i ] = *pfrom;
flashmem_write_internal( tmpdata, temp, blksize );
if( size == 0 )
return ssize;
toaddr = temp + blksize;
}
// The start address is now a multiple of blksize
// Compute how many bytes we can write as multiples of blksize
rest = size & blkmask;
temp = size & ~blkmask;
// Program the blocks now
if(temp){
flashmem_write_internal( pfrom, toaddr, temp );
toaddr += temp;
pfrom += temp;
}
// And the final part of a block if needed
if(rest){
// c_memcpy( tmpdata, ( const void* )toaddr, blksize );
flashmem_read_internal( tmpdata, toaddr, blksize );
for( i = 0; size && ( i < rest ); i ++, size --, pfrom ++ )
tmpdata[ i ] = *pfrom;
flashmem_write_internal( tmpdata, toaddr, blksize );
}
return ssize;
}
//mid level data write method
uint32_t flashmem_read( void *to, uint32_t fromaddr, uint32_t size ){
uint32_t temp, rest, ssize = size;
unsigned i;
char tmpdata[ INTERNAL_FLASH_READ_UNIT_SIZE ];
uint8_t *pto = ( uint8_t* )to;
const uint32_t blksize = INTERNAL_FLASH_READ_UNIT_SIZE;
const uint32_t blkmask = INTERNAL_FLASH_READ_UNIT_SIZE - 1;
// Align the start
if(fromaddr & blkmask){
rest = fromaddr & blkmask;
temp = fromaddr & ~blkmask; // this is the actual aligned address
flashmem_read_internal( tmpdata, temp, blksize );
for( i = rest; size && ( i < blksize ); i ++, size --, pto ++ )
*pto = tmpdata[ i ];
if( size == 0 )
return ssize;
fromaddr = temp + blksize;
}
// The start address is now a multiple of blksize
// Compute how many bytes we can read as multiples of blksize
rest = size & blkmask;
temp = size & ~blkmask;
// Program the blocks now
if(temp){
flashmem_read_internal( pto, fromaddr, temp );
fromaddr += temp;
pto += temp;
}
// And the final part of a block if needed
if(rest){
flashmem_read_internal( tmpdata, fromaddr, blksize );
for( i = 0; size && ( i < rest ); i ++, size --, pto ++ )
*pto = tmpdata[ i ];
}
return ssize;
}
//shorthand when start and end addresses of the sector are not needed
uint32_t flashmem_get_sector_of_address( uint32_t addr ){
return (addr - INTERNAL_FLASH_START_ADDRESS) / INTERNAL_FLASH_SECTOR_SIZE;;
}
/*
SPIFFS BOOTSTRAP
*/
//SPIFFS Address Range (defined in eagle ld)
extern uint32_t _SPIFFS_start;
extern uint32_t _SPIFFS_end;
//SPIFFS Storage Handle
spiffs _filesystemStorageHandle;
//SPIFFS Buffers (INTERNAL_FLASH_PAGE_SIZE = 256) Total 1792 bytes
static u8_t spiffs_work_buf[INTERNAL_FLASH_PAGE_SIZE*2]; //512 bytes
static u8_t spiffs_fds[32*4]; //128 bytes
static u8_t spiffs_cache[(INTERNAL_FLASH_PAGE_SIZE+32)*4]; //1152 bytes
//SPIFFS API Read CallBack
static s32_t api_spiffs_read(u32_t addr, u32_t size, u8_t *dst){
SPIFFS_API_DBG_V("api_spiffs_read: 0x%08x len: %u\n", addr, size);
flashmem_read(dst, addr, size);
return SPIFFS_OK;
}
//SPIFFS API Write CallBack
static s32_t api_spiffs_write(u32_t addr, u32_t size, u8_t *src){
SPIFFS_API_DBG_V("api_spiffs_write: 0x%08x len: %u\n", addr, size);
flashmem_write(src, addr, size);
return SPIFFS_OK;
}
//SPIFFS API Erase CallBack
static s32_t api_spiffs_erase(u32_t addr, u32_t size){
SPIFFS_API_DBG_V("api_spiffs_erase: 0x%08x len: %u\n", addr, size);
u32_t sect_first = flashmem_get_sector_of_address(addr);
u32_t sect_last = flashmem_get_sector_of_address(addr+size);
while( sect_first <= sect_last )
if( !flashmem_erase_sector( sect_first ++ ) )
return SPIFFS_ERR_INTERNAL;
return SPIFFS_OK;
}
// Our own SPIFFS Setup Method
// All of the above gets put in the configuration
// and a mount attempt is made, initializing the storage handle
// that is used in all further api calls
s32_t spiffs_mount(){
u32_t start_address = (u32_t)&_SPIFFS_start;
u32_t end_address = (u32_t)&_SPIFFS_end;
if (start_address == 0 || start_address >= end_address){
SPIFFS_API_DBG_E("Can't start file system, wrong address");
return SPIFFS_ERR_NOT_CONFIGURED;
}
spiffs_config cfg = {0};
cfg.phys_addr = start_address;
cfg.phys_size = end_address - start_address;
cfg.phys_erase_block = INTERNAL_FLASH_SECTOR_SIZE; // according to datasheet
cfg.log_block_size = INTERNAL_FLASH_SECTOR_SIZE * 2; // Important to make large
cfg.log_page_size = INTERNAL_FLASH_PAGE_SIZE; // according to datasheet
cfg.hal_read_f = api_spiffs_read;
cfg.hal_write_f = api_spiffs_write;
cfg.hal_erase_f = api_spiffs_erase;
SPIFFS_API_DBG_V("spiffs_mount: start:%x, size:%d Kb\n", cfg.phys_addr, cfg.phys_size / 1024);
s32_t res = SPIFFS_mount(&_filesystemStorageHandle,
&cfg,
spiffs_work_buf,
spiffs_fds,
sizeof(spiffs_fds),
spiffs_cache,
sizeof(spiffs_cache),
NULL);
SPIFFS_API_DBG_V("spiffs_mount: %d\n", res);
return res;
}

View File

@ -0,0 +1,40 @@
#ifndef SYSTEM_FLASHMEM_H_
#define SYSTEM_FLASHMEM_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "spiffs.h"
/*******************
The W25Q32BV array is organized into 16,384 programmable pages of 256-bytes each. Up to 256 bytes can be programmed at a time.
Pages can be erased in groups of 16 (4KB sector erase), groups of 128 (32KB block erase), groups of 256 (64KB block erase) or
the entire chip (chip erase). The W25Q32BV has 1,024 erasable sectors and 64 erasable blocks respectively.
The small 4KB sectors allow for greater flexibility in applications that require data and parameter storage.
********************/
#define SPIFFS_API_DBG_V(fmt, ...) //os_printf(fmt, ##__VA_ARGS__)
#define SPIFFS_API_DBG_E(fmt, ...) //os_printf("ERROR: " fmt , ##__VA_ARGS__)
#define INTERNAL_FLASH_PAGE_SIZE 256
#define INTERNAL_FLASH_SECTOR_SIZE 4096
#define INTERNAL_FLASH_START_ADDRESS 0x40200000
#define INTERNAL_FLASH_WRITE_UNIT_SIZE 4
#define INTERNAL_FLASH_READ_UNIT_SIZE 4
extern spiffs _filesystemStorageHandle;
extern uint32_t flashmem_write( const void *from, uint32_t toaddr, uint32_t size );
extern uint32_t flashmem_read( void *to, uint32_t fromaddr, uint32_t size );
extern bool flashmem_erase_sector( uint32_t sector_id );
uint32_t flashmem_find_sector( uint32_t address, uint32_t *pstart, uint32_t *pend );
uint32_t flashmem_get_sector_of_address( uint32_t addr );
s32_t spiffs_mount();
#ifdef __cplusplus
}
#endif
#endif /* SYSTEM_FLASHMEM_H_ */

View File

@ -1,224 +0,0 @@
#include "flashmem.h"
#include "esp8266_peri.h"
// Based on NodeMCU platform_flash
// https://github.com/nodemcu/nodemcu-firmware
extern uint32_t _SPIFFS_start;
uint32_t flashmem_write( const void *from, uint32_t toaddr, uint32_t size )
{
uint32_t temp, rest, ssize = size;
unsigned i;
char tmpdata[ INTERNAL_FLASH_WRITE_UNIT_SIZE ];
const uint8_t *pfrom = ( const uint8_t* )from;
const uint32_t blksize = INTERNAL_FLASH_WRITE_UNIT_SIZE;
const uint32_t blkmask = INTERNAL_FLASH_WRITE_UNIT_SIZE - 1;
// Align the start
if( toaddr & blkmask )
{
rest = toaddr & blkmask;
temp = toaddr & ~blkmask; // this is the actual aligned address
// c_memcpy( tmpdata, ( const void* )temp, blksize );
flashmem_read_internal( tmpdata, temp, blksize );
for( i = rest; size && ( i < blksize ); i ++, size --, pfrom ++ )
tmpdata[ i ] = *pfrom;
flashmem_write_internal( tmpdata, temp, blksize );
if( size == 0 )
return ssize;
toaddr = temp + blksize;
}
// The start address is now a multiple of blksize
// Compute how many bytes we can write as multiples of blksize
rest = size & blkmask;
temp = size & ~blkmask;
// Program the blocks now
if( temp )
{
flashmem_write_internal( pfrom, toaddr, temp );
toaddr += temp;
pfrom += temp;
}
// And the final part of a block if needed
if( rest )
{
// c_memcpy( tmpdata, ( const void* )toaddr, blksize );
flashmem_read_internal( tmpdata, toaddr, blksize );
for( i = 0; size && ( i < rest ); i ++, size --, pfrom ++ )
tmpdata[ i ] = *pfrom;
flashmem_write_internal( tmpdata, toaddr, blksize );
}
return ssize;
}
uint32_t flashmem_read( void *to, uint32_t fromaddr, uint32_t size )
{
uint32_t temp, rest, ssize = size;
unsigned i;
char tmpdata[ INTERNAL_FLASH_READ_UNIT_SIZE ];
uint8_t *pto = ( uint8_t* )to;
const uint32_t blksize = INTERNAL_FLASH_READ_UNIT_SIZE;
const uint32_t blkmask = INTERNAL_FLASH_READ_UNIT_SIZE - 1;
// Align the start
if( fromaddr & blkmask )
{
rest = fromaddr & blkmask;
temp = fromaddr & ~blkmask; // this is the actual aligned address
flashmem_read_internal( tmpdata, temp, blksize );
for( i = rest; size && ( i < blksize ); i ++, size --, pto ++ )
*pto = tmpdata[ i ];
if( size == 0 )
return ssize;
fromaddr = temp + blksize;
}
// The start address is now a multiple of blksize
// Compute how many bytes we can read as multiples of blksize
rest = size & blkmask;
temp = size & ~blkmask;
// Program the blocks now
if( temp )
{
flashmem_read_internal( pto, fromaddr, temp );
fromaddr += temp;
pto += temp;
}
// And the final part of a block if needed
if( rest )
{
flashmem_read_internal( tmpdata, fromaddr, blksize );
for( i = 0; size && ( i < rest ); i ++, size --, pto ++ )
*pto = tmpdata[ i ];
}
return ssize;
}
bool flashmem_erase_sector( uint32_t sector_id )
{
WRITE_PERI_REG(0x60000914, 0x73);
return spi_flash_erase_sector( sector_id ) == SPI_FLASH_RESULT_OK;
}
SPIFlashInfo flashmem_get_info()
{
volatile SPIFlashInfo spi_flash_info STORE_ATTR;
spi_flash_info = *((SPIFlashInfo *)(INTERNAL_FLASH_START_ADDRESS));
return spi_flash_info;
}
uint8_t flashmem_get_size_type()
{
return flashmem_get_info().size;
}
uint32_t flashmem_get_size_bytes()
{
uint32_t flash_size = 0;
switch (flashmem_get_info().size)
{
case SIZE_2MBIT:
// 2Mbit, 256kByte
flash_size = 256 * 1024;
break;
case SIZE_4MBIT:
// 4Mbit, 512kByte
flash_size = 512 * 1024;
break;
case SIZE_8MBIT:
// 8Mbit, 1MByte
flash_size = 1 * 1024 * 1024;
break;
case SIZE_16MBIT:
// 16Mbit, 2MByte
flash_size = 2 * 1024 * 1024;
break;
case SIZE_32MBIT:
// 32Mbit, 4MByte
flash_size = 4 * 1024 * 1024;
break;
default:
// Unknown flash size, fall back mode.
flash_size = 512 * 1024;
break;
}
return flash_size;
}
uint16_t flashmem_get_size_sectors()
{
return flashmem_get_size_bytes() / SPI_FLASH_SEC_SIZE;
}
// Helper function: find the flash sector in which an address resides
// Return the sector number, as well as the start and end address of the sector
uint32_t flashmem_find_sector( uint32_t address, uint32_t *pstart, uint32_t *pend )
{
address -= INTERNAL_FLASH_START_ADDRESS;
// All the sectors in the flash have the same size, so just align the address
uint32_t sect_id = address / INTERNAL_FLASH_SECTOR_SIZE;
if( pstart )
*pstart = sect_id * INTERNAL_FLASH_SECTOR_SIZE + INTERNAL_FLASH_START_ADDRESS;
if( pend )
*pend = ( sect_id + 1 ) * INTERNAL_FLASH_SECTOR_SIZE + INTERNAL_FLASH_START_ADDRESS - 1;
return sect_id;
}
uint32_t flashmem_get_sector_of_address( uint32_t addr )
{
return flashmem_find_sector( addr, NULL, NULL );
}
/////////////////////////////////////////////////////
uint32_t flashmem_write_internal( const void *from, uint32_t toaddr, uint32_t size )
{
toaddr -= INTERNAL_FLASH_START_ADDRESS;
SpiFlashOpResult r;
const uint32_t blkmask = INTERNAL_FLASH_WRITE_UNIT_SIZE - 1;
uint32_t *apbuf = NULL;
if( ((uint32_t)from) & blkmask ){
apbuf = (uint32_t *)os_malloc(size);
if(!apbuf)
return 0;
os_memcpy(apbuf, from, size);
}
WDT_RESET();
r = spi_flash_write(toaddr, apbuf?(uint32 *)apbuf:(uint32 *)from, size);
if(apbuf)
os_free(apbuf);
if(SPI_FLASH_RESULT_OK == r)
return size;
else{
SYSTEM_ERROR( "ERROR in flash_write: r=%d at %08X\n", ( int )r, ( unsigned )toaddr+INTERNAL_FLASH_START_ADDRESS );
return 0;
}
}
uint32_t flashmem_read_internal( void *to, uint32_t fromaddr, uint32_t size )
{
fromaddr -= INTERNAL_FLASH_START_ADDRESS;
SpiFlashOpResult r;
WDT_RESET();
r = spi_flash_read(fromaddr, (uint32 *)to, size);
if(SPI_FLASH_RESULT_OK == r)
return size;
else{
SYSTEM_ERROR( "ERROR in flash_read: r=%d at %08X\n", ( int )r, ( unsigned )fromaddr+INTERNAL_FLASH_START_ADDRESS );
return 0;
}
}
uint32_t flashmem_get_first_free_block_address(){
if ((uint32_t)&_SPIFFS_start == 0){
return 0;
}
debugf("_SPIFFS_start:%08x\n", (uint32_t)&_SPIFFS_start);
// Round the total used flash size to the closest flash block address
uint32_t end;
flashmem_find_sector( (uint32_t)&_SPIFFS_start - 1, NULL, &end);
return end + 1;
}

80
cores/esp8266/spiffs/spiffs_gc.c Executable file → Normal file
View File

@ -8,31 +8,11 @@ static s32_t spiffs_gc_erase_block(
spiffs *fs, spiffs *fs,
spiffs_block_ix bix) { spiffs_block_ix bix) {
s32_t res; s32_t res;
u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, bix);
s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs);
SPIFFS_GC_DBG("gc: erase block %d\n", bix); SPIFFS_GC_DBG("gc: erase block %d\n", bix);
res = spiffs_erase_block(fs, bix);
// here we ignore res, just try erasing the block
while (size > 0) {
SPIFFS_GC_DBG("gc: erase %08x:%08x\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs));
(void)fs->cfg.hal_erase_f(addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs));
addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs);
size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs);
}
fs->free_blocks++;
// register erase count for this block
res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0,
SPIFFS_ERASE_COUNT_PADDR(fs, bix),
sizeof(spiffs_obj_id), (u8_t *)&fs->max_erase_count);
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);
fs->max_erase_count++;
if (fs->max_erase_count == SPIFFS_OBJ_ID_IX_FLAG) {
fs->max_erase_count = 0;
}
#if SPIFFS_CACHE #if SPIFFS_CACHE
{ {
u32_t i; u32_t i;
@ -119,17 +99,25 @@ s32_t spiffs_gc_check(
spiffs *fs, spiffs *fs,
u32_t len) { u32_t len) {
s32_t res; s32_t res;
u32_t free_pages = s32_t free_pages =
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * fs->block_count (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count-2)
- fs->stats_p_allocated - fs->stats_p_deleted; - fs->stats_p_allocated - fs->stats_p_deleted;
int tries = 0; int tries = 0;
if (fs->free_blocks > 3 && if (fs->free_blocks > 3 &&
len < free_pages * SPIFFS_DATA_PAGE_SIZE(fs)) { (s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
return SPIFFS_OK; return SPIFFS_OK;
} }
//printf("gcing started %d dirty, blocks %d free, want %d bytes\n", fs->stats_p_allocated + fs->stats_p_deleted, fs->free_blocks, len); u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs);
// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) {
// SPIFFS_GC_DBG("gc: full freeblk:%d needed:%d free:%d dele:%d\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
// return SPIFFS_ERR_FULL;
// }
if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) {
SPIFFS_GC_DBG("gc_check: full freeblk:%d needed:%d free:%d dele:%d\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted);
return SPIFFS_ERR_FULL;
}
do { do {
SPIFFS_GC_DBG("\ngc_check #%d: run gc free_blocks:%d pfree:%d pallo:%d pdele:%d [%d] len:%d of %d\n", SPIFFS_GC_DBG("\ngc_check #%d: run gc free_blocks:%d pfree:%d pallo:%d pdele:%d [%d] len:%d of %d\n",
@ -140,18 +128,20 @@ s32_t spiffs_gc_check(
spiffs_block_ix *cands; spiffs_block_ix *cands;
int count; int count;
spiffs_block_ix cand; spiffs_block_ix cand;
res = spiffs_gc_find_candidate(fs, &cands, &count); s32_t prev_free_pages = free_pages;
// if the fs is crammed, ignore block age when selecting candidate - kind of a bad state
res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0);
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);
if (count == 0) { if (count == 0) {
SPIFFS_GC_DBG("gc_check: no candidates, return\n"); SPIFFS_GC_DBG("gc_check: no candidates, return\n");
return res; return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL;
} }
#if SPIFFS_GC_STATS #if SPIFFS_GC_STATS
fs->stats_gc_runs++; fs->stats_gc_runs++;
#endif #endif
cand = cands[0]; cand = cands[0];
fs->cleaning = 1; fs->cleaning = 1;
//printf("gcing: cleaning block %d\n", cand); //c_printf("gcing: cleaning block %d\n", cand);
res = spiffs_gc_clean(fs, cand); res = spiffs_gc_clean(fs, cand);
fs->cleaning = 0; fs->cleaning = 0;
if (res < 0) { if (res < 0) {
@ -168,16 +158,28 @@ s32_t spiffs_gc_check(
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);
free_pages = free_pages =
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * fs->block_count (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
- fs->stats_p_allocated - fs->stats_p_deleted; - fs->stats_p_allocated - fs->stats_p_deleted;
} while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 || if (prev_free_pages <= 0 && prev_free_pages == free_pages) {
len > free_pages*SPIFFS_DATA_PAGE_SIZE(fs))); // abort early to reduce wear, at least tried once
SPIFFS_GC_DBG("gc_check: finished\n"); SPIFFS_GC_DBG("gc_check: early abort, no result on gc when fs crammed\n");
break;
}
//printf("gcing finished %d dirty, blocks %d free, %d pages free, %d tries, res %d\n", } while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 ||
// fs->stats_p_allocated + fs->stats_p_deleted, (s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)));
// fs->free_blocks, free_pages, tries, res);
free_pages =
(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2)
- fs->stats_p_allocated - fs->stats_p_deleted;
if ((s32_t)len > free_pages*(s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) {
res = SPIFFS_ERR_FULL;
}
SPIFFS_GC_DBG("gc_check: finished, %d dirty, blocks %d free, %d pages free, %d tries, res %d\n",
fs->stats_p_allocated + fs->stats_p_deleted,
fs->free_blocks, free_pages, tries, res);
return res; return res;
} }
@ -223,7 +225,8 @@ s32_t spiffs_gc_erase_page_stats(
s32_t spiffs_gc_find_candidate( s32_t spiffs_gc_find_candidate(
spiffs *fs, spiffs *fs,
spiffs_block_ix **block_candidates, spiffs_block_ix **block_candidates,
int *candidate_count) { int *candidate_count,
char fs_crammed) {
s32_t res = SPIFFS_OK; s32_t res = SPIFFS_OK;
u32_t blocks = fs->block_count; u32_t blocks = fs->block_count;
spiffs_block_ix cur_block = 0; spiffs_block_ix cur_block = 0;
@ -249,6 +252,7 @@ s32_t spiffs_gc_find_candidate(
while (res == SPIFFS_OK && blocks--) { while (res == SPIFFS_OK && blocks--) {
u16_t deleted_pages_in_block = 0; u16_t deleted_pages_in_block = 0;
u16_t used_pages_in_block = 0; u16_t used_pages_in_block = 0;
int obj_lookup_page = 0; int obj_lookup_page = 0;
// check each object lookup page // check each object lookup page
while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) {
@ -295,9 +299,9 @@ s32_t spiffs_gc_find_candidate(
s32_t score = s32_t score =
deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET + deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET +
used_pages_in_block * SPIFFS_GC_HEUR_W_USED + used_pages_in_block * SPIFFS_GC_HEUR_W_USED +
erase_age * SPIFFS_GC_HEUR_W_ERASE_AGE; erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE);
int cand_ix = 0; int cand_ix = 0;
SPIFFS_GC_DBG("\ngc_check: bix:%d del:%d use:%d score:%d\n", cur_block, deleted_pages_in_block, used_pages_in_block, score); SPIFFS_GC_DBG("gc_check: bix:%d del:%d use:%d score:%d\n", cur_block, deleted_pages_in_block, used_pages_in_block, score);
while (cand_ix < max_candidates) { while (cand_ix < max_candidates) {
if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) { if (cand_blocks[cand_ix] == (spiffs_block_ix)-1) {
cand_blocks[cand_ix] = cur_block; cand_blocks[cand_ix] = cur_block;

166
cores/esp8266/spiffs/spiffs_hydrogen.c Executable file → Normal file
View File

@ -21,6 +21,36 @@ u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages) {
#endif #endif
#endif #endif
u8_t SPIFFS_mounted(spiffs *fs) {
return SPIFFS_CHECK_MOUNT(fs);
}
s32_t SPIFFS_format(spiffs *fs) {
SPIFFS_API_CHECK_CFG(fs);
if (SPIFFS_CHECK_MOUNT(fs)) {
fs->err_code = SPIFFS_ERR_MOUNTED;
return -1;
}
s32_t res;
SPIFFS_LOCK(fs);
spiffs_block_ix bix = 0;
while (bix < fs->block_count) {
fs->max_erase_count = 0;
res = spiffs_erase_block(fs, bix);
if (res != SPIFFS_OK) {
res = SPIFFS_ERR_ERASE_FAIL;
}
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
bix++;
}
SPIFFS_UNLOCK(fs);
return 0;
}
s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
u8_t *fd_space, u32_t fd_space_size, u8_t *fd_space, u32_t fd_space_size,
void *cache, u32_t cache_size, void *cache, u32_t cache_size,
@ -34,10 +64,10 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
c_memset(fd_space, 0, fd_space_size); c_memset(fd_space, 0, fd_space_size);
// align fd_space pointer to pointer size byte boundary, below is safe // align fd_space pointer to pointer size byte boundary, below is safe
u8_t ptr_size = sizeof(void*); u8_t ptr_size = sizeof(void*);
// #pragma GCC diagnostic push #pragma GCC diagnostic push
// #pragma GCC diagnostic ignored "-Wpointer-to-int-cast" #pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
u8_t addr_lsb = (u8_t)(((u32_t)fd_space) & (ptr_size-1)); u8_t addr_lsb = ((u8_t)fd_space) & (ptr_size-1);
// #pragma GCC diagnostic pop #pragma GCC diagnostic pop
if (addr_lsb) { if (addr_lsb) {
fd_space += (ptr_size-addr_lsb); fd_space += (ptr_size-addr_lsb);
fd_space_size -= (ptr_size-addr_lsb); fd_space_size -= (ptr_size-addr_lsb);
@ -46,10 +76,10 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
fs->fd_count = (fd_space_size/sizeof(spiffs_fd)); fs->fd_count = (fd_space_size/sizeof(spiffs_fd));
// align cache pointer to 4 byte boundary, below is safe // align cache pointer to 4 byte boundary, below is safe
// #pragma GCC diagnostic push #pragma GCC diagnostic push
// #pragma GCC diagnostic ignored "-Wpointer-to-int-cast" #pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
addr_lsb = (u8_t)(((u32_t)cache) & (ptr_size-1)); addr_lsb = ((u8_t)cache) & (ptr_size-1);
// #pragma GCC diagnostic pop #pragma GCC diagnostic pop
if (addr_lsb) { if (addr_lsb) {
u8_t *cache_8 = (u8_t *)cache; u8_t *cache_8 = (u8_t *)cache;
cache_8 += (ptr_size-addr_lsb); cache_8 += (ptr_size-addr_lsb);
@ -65,7 +95,16 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
spiffs_cache_init(fs); spiffs_cache_init(fs);
#endif #endif
s32_t res = spiffs_obj_lu_scan(fs); s32_t res;
#if SPIFFS_USE_MAGIC
res = SPIFFS_CHECK_MAGIC_POSSIBLE(fs) ? SPIFFS_OK : SPIFFS_ERR_MAGIC_NOT_POSSIBLE;
SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
#endif
fs->config_magic = SPIFFS_CONFIG_MAGIC;
res = spiffs_obj_lu_scan(fs);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res); SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
SPIFFS_DBG("page index byte len: %d\n", SPIFFS_CFG_LOG_PAGE_SZ(fs)); SPIFFS_DBG("page index byte len: %d\n", SPIFFS_CFG_LOG_PAGE_SZ(fs));
@ -79,13 +118,15 @@ s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work,
fs->check_cb_f = check_cb_f; fs->check_cb_f = check_cb_f;
fs->mounted = 1;
SPIFFS_UNLOCK(fs); SPIFFS_UNLOCK(fs);
return 0; return 0;
} }
void SPIFFS_unmount(spiffs *fs) { void SPIFFS_unmount(spiffs *fs) {
if (!SPIFFS_CHECK_MOUNT(fs)) return; if (!SPIFFS_CHECK_CFG(fs) || !SPIFFS_CHECK_MOUNT(fs)) return;
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
u32_t i; u32_t i;
spiffs_fd *fds = (spiffs_fd *)fs->fd_space; spiffs_fd *fds = (spiffs_fd *)fs->fd_space;
@ -98,16 +139,22 @@ void SPIFFS_unmount(spiffs *fs) {
spiffs_fd_return(fs, cur_fd->file_nbr); spiffs_fd_return(fs, cur_fd->file_nbr);
} }
} }
fs->block_count = 0; fs->mounted = 0;
SPIFFS_UNLOCK(fs); SPIFFS_UNLOCK(fs);
} }
s32_t SPIFFS_errno(spiffs *fs) { s32_t SPIFFS_errno(spiffs *fs) {
return fs->errno; return fs->err_code;
} }
s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode) { void SPIFFS_clearerr(spiffs *fs) {
fs->err_code = SPIFFS_OK;
}
s32_t SPIFFS_creat(spiffs *fs, char *path, spiffs_mode mode) {
(void)mode; (void)mode;
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
spiffs_obj_id obj_id; spiffs_obj_id obj_id;
@ -121,8 +168,9 @@ s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode) {
return 0; return 0;
} }
spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode) { spiffs_file SPIFFS_open(spiffs *fs, char *path, spiffs_flags flags, spiffs_mode mode) {
(void)mode; (void)mode;
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -181,6 +229,7 @@ spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs
} }
spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode) { spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode) {
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -209,7 +258,8 @@ spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_fl
return fd->file_nbr; return fd->file_nbr;
} }
s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, u32_t len) { s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -277,7 +327,8 @@ static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offs
} }
s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len) { s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) {
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -314,8 +365,6 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len) {
#endif #endif
} }
SPIFFS_DBG("SPIFFS_write %d %04x offs:%d len %d\n", fh, fd->obj_id, offset, len);
#if SPIFFS_CACHE_WR #if SPIFFS_CACHE_WR
if ((fd->flags & SPIFFS_DIRECT) == 0) { if ((fd->flags & SPIFFS_DIRECT) == 0) {
if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) { if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) {
@ -328,12 +377,13 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len) {
offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page offset + len > fd->cache_page->offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) // writing beyond cache page
{ {
// boundary violation, write back cache first and allocate new // boundary violation, write back cache first and allocate new
SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %d for fd %d:&04x, boundary viol, offs:%d size:%d\n", SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page %d for fd %d:%04x, boundary viol, offs:%d size:%d\n",
fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size); fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->offset, fd->cache_page->size);
res = spiffs_hydro_write(fs, fd, res = spiffs_hydro_write(fs, fd,
spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
fd->cache_page->offset, fd->cache_page->size); fd->cache_page->offset, fd->cache_page->size);
spiffs_cache_fd_release(fs, fd->cache_page); spiffs_cache_fd_release(fs, fd->cache_page);
SPIFFS_API_CHECK_RES(fs, res);
} else { } else {
// writing within cache // writing within cache
alloc_cpage = 0; alloc_cpage = 0;
@ -379,6 +429,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len) {
spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
fd->cache_page->offset, fd->cache_page->size); fd->cache_page->offset, fd->cache_page->size);
spiffs_cache_fd_release(fs, fd->cache_page); spiffs_cache_fd_release(fs, fd->cache_page);
SPIFFS_API_CHECK_RES(fs, res);
res = spiffs_hydro_write(fs, fd, buf, offset, len); res = spiffs_hydro_write(fs, fd, buf, offset, len);
SPIFFS_API_CHECK_RES(fs, res); SPIFFS_API_CHECK_RES(fs, res);
} }
@ -396,6 +447,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, u32_t len) {
} }
s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) { s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) {
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -439,7 +491,8 @@ s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) {
return 0; return 0;
} }
s32_t SPIFFS_remove(spiffs *fs, const char *path) { s32_t SPIFFS_remove(spiffs *fs, char *path) {
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -473,6 +526,7 @@ s32_t SPIFFS_remove(spiffs *fs, const char *path) {
} }
s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) { s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) {
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -520,7 +574,8 @@ static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spi
return res; return res;
} }
s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s) { s32_t SPIFFS_stat(spiffs *fs, char *path, spiffs_stat *s) {
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -538,6 +593,7 @@ s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s) {
} }
s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) { s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) {
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -580,7 +636,7 @@ static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) {
spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix),
fd->cache_page->offset, fd->cache_page->size); fd->cache_page->offset, fd->cache_page->size);
if (res < SPIFFS_OK) { if (res < SPIFFS_OK) {
fs->errno = res; fs->err_code = res;
} }
spiffs_cache_fd_release(fs, fd->cache_page); spiffs_cache_fd_release(fs, fd->cache_page);
} }
@ -591,6 +647,7 @@ static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) {
} }
s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) { s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) {
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_API_CHECK_MOUNT(fs);
s32_t res = SPIFFS_OK; s32_t res = SPIFFS_OK;
#if SPIFFS_CACHE_WR #if SPIFFS_CACHE_WR
@ -604,8 +661,13 @@ s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) {
} }
void SPIFFS_close(spiffs *fs, spiffs_file fh) { void SPIFFS_close(spiffs *fs, spiffs_file fh) {
if (!SPIFFS_CHECK_CFG((fs))) {
(fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED;
return;
}
if (!SPIFFS_CHECK_MOUNT(fs)) { if (!SPIFFS_CHECK_MOUNT(fs)) {
fs->errno = SPIFFS_ERR_NOT_MOUNTED; fs->err_code = SPIFFS_ERR_NOT_MOUNTED;
return; return;
} }
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -618,7 +680,8 @@ void SPIFFS_close(spiffs *fs, spiffs_file fh) {
SPIFFS_UNLOCK(fs); SPIFFS_UNLOCK(fs);
} }
s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newname) { s32_t SPIFFS_rename(spiffs *fs, char *old, char *new) {
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -628,7 +691,7 @@ s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newname) {
s32_t res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)old, &pix_old); s32_t res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)old, &pix_old);
SPIFFS_API_CHECK_RES_UNLOCK(fs, res); SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)newname, &pix_dummy); res = spiffs_object_find_object_index_header_by_name(fs, (u8_t*)new, &pix_dummy);
if (res == SPIFFS_ERR_NOT_FOUND) { if (res == SPIFFS_ERR_NOT_FOUND) {
res = SPIFFS_OK; res = SPIFFS_OK;
} else if (res == SPIFFS_OK) { } else if (res == SPIFFS_OK) {
@ -645,7 +708,7 @@ s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newname) {
} }
SPIFFS_API_CHECK_RES_UNLOCK(fs, res); SPIFFS_API_CHECK_RES_UNLOCK(fs, res);
res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (u8_t*)newname, res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (u8_t*)new,
0, &pix_dummy); 0, &pix_dummy);
if (res != SPIFFS_OK) { if (res != SPIFFS_OK) {
@ -658,12 +721,19 @@ s32_t SPIFFS_rename(spiffs *fs, const char *old, const char *newname) {
return res; return res;
} }
spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) { spiffs_DIR *SPIFFS_opendir(spiffs *fs, char *name, spiffs_DIR *d) {
(void)name; (void)name;
if (!SPIFFS_CHECK_MOUNT(fs)) {
fs->errno = SPIFFS_ERR_NOT_MOUNTED; if (!SPIFFS_CHECK_CFG((fs))) {
(fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED;
return 0; return 0;
} }
if (!SPIFFS_CHECK_MOUNT(fs)) {
fs->err_code = SPIFFS_ERR_NOT_MOUNTED;
return 0;
}
d->fs = fs; d->fs = fs;
d->block = 0; d->block = 0;
d->entry = 0; d->entry = 0;
@ -707,7 +777,7 @@ static s32_t spiffs_read_dir_v(
struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) {
if (!SPIFFS_CHECK_MOUNT(d->fs)) { if (!SPIFFS_CHECK_MOUNT(d->fs)) {
d->fs->errno = SPIFFS_ERR_NOT_MOUNTED; d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED;
return 0; return 0;
} }
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -732,19 +802,21 @@ struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) {
d->entry = entry + 1; d->entry = entry + 1;
ret = e; ret = e;
} else { } else {
d->fs->errno = res; d->fs->err_code = res;
} }
SPIFFS_UNLOCK(fs); SPIFFS_UNLOCK(fs);
return ret; return ret;
} }
s32_t SPIFFS_closedir(spiffs_DIR *d) { s32_t SPIFFS_closedir(spiffs_DIR *d) {
SPIFFS_API_CHECK_CFG(d->fs);
SPIFFS_API_CHECK_MOUNT(d->fs); SPIFFS_API_CHECK_MOUNT(d->fs);
return 0; return 0;
} }
s32_t SPIFFS_check(spiffs *fs) { s32_t SPIFFS_check(spiffs *fs) {
s32_t res; s32_t res;
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -760,7 +832,32 @@ s32_t SPIFFS_check(spiffs *fs) {
return res; return res;
} }
s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) {
s32_t res = SPIFFS_OK;
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs);
u32_t pages_per_block = SPIFFS_PAGES_PER_BLOCK(fs);
u32_t blocks = fs->block_count;
u32_t obj_lu_pages = SPIFFS_OBJ_LOOKUP_PAGES(fs);
u32_t data_page_size = SPIFFS_DATA_PAGE_SIZE(fs);
u32_t total_data_pages = (blocks - 2) * (pages_per_block - obj_lu_pages) + 1; // -2 for spare blocks, +1 for emergency page
if (total) {
*total = total_data_pages * data_page_size;
}
if (used) {
*used = fs->stats_p_allocated * data_page_size;
}
SPIFFS_UNLOCK(fs);
return res;
}
s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) { s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) {
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -780,6 +877,7 @@ s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) {
} }
s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) { s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) {
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -801,6 +899,7 @@ s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) {
#if SPIFFS_TEST_VISUALISATION #if SPIFFS_TEST_VISUALISATION
s32_t SPIFFS_vis(spiffs *fs) { s32_t SPIFFS_vis(spiffs *fs) {
s32_t res = SPIFFS_OK; s32_t res = SPIFFS_OK;
SPIFFS_API_CHECK_CFG(fs);
SPIFFS_API_CHECK_MOUNT(fs); SPIFFS_API_CHECK_MOUNT(fs);
SPIFFS_LOCK(fs); SPIFFS_LOCK(fs);
@ -859,11 +958,14 @@ s32_t SPIFFS_vis(spiffs *fs) {
} // per block } // per block
spiffs_printf("era_cnt_max: %d\n", fs->max_erase_count); spiffs_printf("era_cnt_max: %d\n", fs->max_erase_count);
spiffs_printf("last_errno: %d\n", fs->errno); spiffs_printf("last_errno: %d\n", fs->err_code);
spiffs_printf("blocks: %d\n", fs->block_count); spiffs_printf("blocks: %d\n", fs->block_count);
spiffs_printf("free_blocks: %d\n", fs->free_blocks); spiffs_printf("free_blocks: %d\n", fs->free_blocks);
spiffs_printf("page_alloc: %d\n", fs->stats_p_allocated); spiffs_printf("page_alloc: %d\n", fs->stats_p_allocated);
spiffs_printf("page_delet: %d\n", fs->stats_p_deleted); spiffs_printf("page_delet: %d\n", fs->stats_p_deleted);
u32_t total, used;
SPIFFS_info(fs, &total, &used);
spiffs_printf("used: %d of %d\n", used, total);
SPIFFS_UNLOCK(fs); SPIFFS_UNLOCK(fs);
return res; return res;

162
cores/esp8266/spiffs/spiffs_nucleus.c Executable file → Normal file
View File

@ -142,11 +142,15 @@ s32_t spiffs_obj_lu_find_entry_visitor(
cur_block++; cur_block++;
cur_block_addr = cur_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); cur_block_addr = cur_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs);
if (cur_block >= fs->block_count) { if (cur_block >= fs->block_count) {
if (flags & SPIFFS_VIS_NO_WRAP) {
return SPIFFS_VIS_END;
} else {
// block wrap // block wrap
cur_block = 0; cur_block = 0;
cur_block_addr = 0; cur_block_addr = 0;
} }
} }
}
// check each block // check each block
while (res == SPIFFS_OK && entry_count > 0) { while (res == SPIFFS_OK && entry_count > 0) {
@ -213,6 +217,45 @@ s32_t spiffs_obj_lu_find_entry_visitor(
return SPIFFS_VIS_END; return SPIFFS_VIS_END;
} }
s32_t spiffs_erase_block(
spiffs *fs,
spiffs_block_ix bix) {
s32_t res;
u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, bix);
s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs);
// here we ignore res, just try erasing the block
while (size > 0) {
SPIFFS_DBG("erase %08x:%08x\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs));
(void)fs->cfg.hal_erase_f(addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs));
addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs);
size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs);
}
fs->free_blocks++;
// register erase count for this block
res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0,
SPIFFS_ERASE_COUNT_PADDR(fs, bix),
sizeof(spiffs_obj_id), (u8_t *)&fs->max_erase_count);
SPIFFS_CHECK_RES(res);
#if SPIFFS_USE_MAGIC
// finally, write magic
spiffs_obj_id magic = SPIFFS_MAGIC(fs);
res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0,
SPIFFS_MAGIC_PADDR(fs, bix),
sizeof(spiffs_obj_id), (u8_t *)&magic);
SPIFFS_CHECK_RES(res);
#endif
fs->max_erase_count++;
if (fs->max_erase_count == SPIFFS_OBJ_ID_IX_FLAG) {
fs->max_erase_count = 0;
}
return res;
}
static s32_t spiffs_obj_lu_scan_v( static s32_t spiffs_obj_lu_scan_v(
spiffs *fs, spiffs *fs,
@ -238,40 +281,44 @@ static s32_t spiffs_obj_lu_scan_v(
return SPIFFS_VIS_COUNTINUE; return SPIFFS_VIS_COUNTINUE;
} }
// Scans thru all obj lu and counts free, deleted and used pages // Scans thru all obj lu and counts free, deleted and used pages
// Find the maximum block erase count // Find the maximum block erase count
// Checks magic if enabled
s32_t spiffs_obj_lu_scan( s32_t spiffs_obj_lu_scan(
spiffs *fs) { spiffs *fs) {
s32_t res; s32_t res;
spiffs_block_ix bix; spiffs_block_ix bix;
int entry; int entry;
#if SPIFFS_USE_MAGIC
spiffs_block_ix unerased_bix = (spiffs_block_ix)-1;
#endif
fs->free_blocks = 0; // find out erase count
fs->stats_p_allocated = 0; // if enabled, check magic
fs->stats_p_deleted = 0;
res = spiffs_obj_lu_find_entry_visitor(fs,
0,
0,
0,
0,
spiffs_obj_lu_scan_v,
0,
0,
&bix,
&entry);
if (res == SPIFFS_VIS_END) {
res = SPIFFS_OK;
}
SPIFFS_CHECK_RES(res);
bix = 0; bix = 0;
spiffs_obj_id erase_count_final; spiffs_obj_id erase_count_final;
spiffs_obj_id erase_count_min = SPIFFS_OBJ_ID_FREE; spiffs_obj_id erase_count_min = SPIFFS_OBJ_ID_FREE;
spiffs_obj_id erase_count_max = 0; spiffs_obj_id erase_count_max = 0;
while (bix < fs->block_count) { while (bix < fs->block_count) {
#if SPIFFS_USE_MAGIC
spiffs_obj_id magic;
res = _spiffs_rd(fs,
SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
0, SPIFFS_MAGIC_PADDR(fs, bix) ,
sizeof(spiffs_obj_id), (u8_t *)&magic);
SPIFFS_CHECK_RES(res);
if (magic != SPIFFS_MAGIC(fs)) {
if (unerased_bix == (spiffs_block_ix)-1) {
// allow one unerased block as it might be powered down during an erase
unerased_bix = bix;
} else {
// more than one unerased block, bail out
SPIFFS_CHECK_RES(SPIFFS_ERR_NOT_A_FS);
}
}
#endif
spiffs_obj_id erase_count; spiffs_obj_id erase_count;
res = _spiffs_rd(fs, res = _spiffs_rd(fs,
SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ,
@ -297,6 +344,38 @@ s32_t spiffs_obj_lu_scan(
fs->max_erase_count = erase_count_final; fs->max_erase_count = erase_count_final;
#if SPIFFS_USE_MAGIC
if (unerased_bix != (spiffs_block_ix)-1) {
// found one unerased block, remedy
SPIFFS_DBG("mount: erase block %d\n", bix);
res = spiffs_erase_block(fs, unerased_bix);
SPIFFS_CHECK_RES(res);
}
#endif
// count blocks
fs->free_blocks = 0;
fs->stats_p_allocated = 0;
fs->stats_p_deleted = 0;
res = spiffs_obj_lu_find_entry_visitor(fs,
0,
0,
0,
0,
spiffs_obj_lu_scan_v,
0,
0,
&bix,
&entry);
if (res == SPIFFS_VIS_END) {
res = SPIFFS_OK;
}
SPIFFS_CHECK_RES(res);
return res; return res;
} }
@ -614,7 +693,7 @@ s32_t spiffs_object_create(
spiffs_page_object_ix_header oix_hdr; spiffs_page_object_ix_header oix_hdr;
int entry; int entry;
res = spiffs_gc_check(fs, 0); res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs));
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);
obj_id |= SPIFFS_OBJ_ID_IX_FLAG; obj_id |= SPIFFS_OBJ_ID_IX_FLAG;
@ -811,7 +890,17 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
s32_t res = SPIFFS_OK; s32_t res = SPIFFS_OK;
u32_t written = 0; u32_t written = 0;
res = spiffs_gc_check(fs, len); SPIFFS_DBG("append: %d bytes @ offs %d of size %d\n", len, offset, fd->size);
if (offset > fd->size) {
SPIFFS_DBG("append: offset reversed to size\n");
offset = fd->size;
}
res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); // add an extra page of data worth for meta
if (res != SPIFFS_OK) {
SPIFFS_DBG("append: gc check fail %d\n", res);
}
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
@ -912,7 +1001,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix);
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);
} }
SPIFFS_DBG("append: %04x found object index at page %04x\n", fd->obj_id, pix); SPIFFS_DBG("append: %04x found object index at page %04x [fd size %d]\n", fd->obj_id, pix, fd->size);
res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ,
fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work);
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);
@ -1003,8 +1092,8 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
// update size in object header index page // update size in object header index page
res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id,
fd->objix_hdr_pix, 0, 0, offset+written, &new_objix_hdr_page); fd->objix_hdr_pix, 0, 0, offset+written, &new_objix_hdr_page);
SPIFFS_DBG("append: %04x store new size II %d in objix_hdr, %04x:%04x, written %d\n", fd->obj_id SPIFFS_DBG("append: %04x store new size II %d in objix_hdr, %04x:%04x, written %d, res %d\n", fd->obj_id
, offset+written, new_objix_hdr_page, 0, written); , offset+written, new_objix_hdr_page, 0, written, res2);
SPIFFS_CHECK_RES(res2); SPIFFS_CHECK_RES(res2);
} else { } else {
// wrote within object index header page // wrote within object index header page
@ -1042,7 +1131,7 @@ s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) {
s32_t res = SPIFFS_OK; s32_t res = SPIFFS_OK;
u32_t written = 0; u32_t written = 0;
res = spiffs_gc_check(fs, len); res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs));
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);
spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work;
@ -1308,7 +1397,7 @@ s32_t spiffs_object_truncate(
s32_t res = SPIFFS_OK; s32_t res = SPIFFS_OK;
spiffs *fs = fd->fs; spiffs *fs = fd->fs;
res = spiffs_gc_check(fs, 0); res = spiffs_gc_check(fs, remove ? 0 : SPIFFS_DATA_PAGE_SIZE(fs));
SPIFFS_CHECK_RES(res); SPIFFS_CHECK_RES(res);
spiffs_page_ix objix_pix = fd->objix_hdr_pix; spiffs_page_ix objix_pix = fd->objix_hdr_pix;
@ -1386,13 +1475,26 @@ s32_t spiffs_object_truncate(
((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE; ((spiffs_page_ix*)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE;
} }
SPIFFS_DBG("truncate: got data pix %04x\n", data_pix);
if (cur_size - SPIFFS_DATA_PAGE_SIZE(fs) >= new_size) { if (cur_size - SPIFFS_DATA_PAGE_SIZE(fs) >= new_size) {
// delete full data page // delete full data page
res = spiffs_page_data_check(fs, fd, data_pix, data_spix); res = spiffs_page_data_check(fs, fd, data_pix, data_spix);
if (res != SPIFFS_OK) break; if (res != SPIFFS_ERR_DELETED && res != SPIFFS_OK && res != SPIFFS_ERR_INDEX_REF_FREE) {
SPIFFS_DBG("truncate: err validating data pix %d\n", res);
break;
}
if (res == SPIFFS_OK) {
res = spiffs_page_delete(fs, data_pix); res = spiffs_page_delete(fs, data_pix);
if (res != SPIFFS_OK) break; if (res != SPIFFS_OK) {
SPIFFS_DBG("truncate: err deleting data pix %d\n", res);
break;
}
} else if (res == SPIFFS_ERR_DELETED || res == SPIFFS_ERR_INDEX_REF_FREE) {
res = SPIFFS_OK;
}
// update current size // update current size
if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) { if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) {
cur_size -= SPIFFS_DATA_PAGE_SIZE(fs); cur_size -= SPIFFS_DATA_PAGE_SIZE(fs);

49
cores/esp8266/spiffs/spiffs_nucleus.h Executable file → Normal file
View File

@ -131,6 +131,10 @@
#define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0) #define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0)
#define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1) #define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1)
#define SPIFFS_MAGIC(fs) ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs)))
#define SPIFFS_CONFIG_MAGIC (0x20090315)
#if SPIFFS_SINGLETON == 0 #if SPIFFS_SINGLETON == 0
#define SPIFFS_CFG_LOG_PAGE_SZ(fs) \ #define SPIFFS_CFG_LOG_PAGE_SZ(fs) \
((fs)->cfg.log_page_size) ((fs)->cfg.log_page_size)
@ -189,9 +193,18 @@
// returns data size in a data page // returns data size in a data page
#define SPIFFS_DATA_PAGE_SIZE(fs) \ #define SPIFFS_DATA_PAGE_SIZE(fs) \
( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) ) ( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) )
// returns physical address for block's erase count // returns physical address for block's erase count,
// always in the physical last entry of the last object lookup page
#define SPIFFS_ERASE_COUNT_PADDR(fs, bix) \ #define SPIFFS_ERASE_COUNT_PADDR(fs, bix) \
( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) ) ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) )
// returns physical address for block's magic,
// always in the physical second last entry of the last object lookup page
#define SPIFFS_MAGIC_PADDR(fs, bix) \
( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id)*2 )
// checks if there is any room for magic in the object luts
#define SPIFFS_CHECK_MAGIC_POSSIBLE(fs) \
( (SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) % (SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(spiffs_obj_id))) * sizeof(spiffs_obj_id) \
<= (SPIFFS_CFG_LOG_PAGE_SZ(fs)-sizeof(spiffs_obj_id)*2) )
// define helpers object // define helpers object
@ -238,7 +251,10 @@
#define SPIFFS_CHECK_MOUNT(fs) \ #define SPIFFS_CHECK_MOUNT(fs) \
((fs)->block_count > 0) ((fs)->mounted != 0)
#define SPIFFS_CHECK_CFG(fs) \
((fs)->config_magic == SPIFFS_CONFIG_MAGIC)
#define SPIFFS_CHECK_RES(res) \ #define SPIFFS_CHECK_RES(res) \
do { \ do { \
@ -247,19 +263,25 @@
#define SPIFFS_API_CHECK_MOUNT(fs) \ #define SPIFFS_API_CHECK_MOUNT(fs) \
if (!SPIFFS_CHECK_MOUNT((fs))) { \ if (!SPIFFS_CHECK_MOUNT((fs))) { \
(fs)->errno = SPIFFS_ERR_NOT_MOUNTED; \ (fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \
return -1; \
}
#define SPIFFS_API_CHECK_CFG(fs) \
if (!SPIFFS_CHECK_CFG((fs))) { \
(fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \
return -1; \ return -1; \
} }
#define SPIFFS_API_CHECK_RES(fs, res) \ #define SPIFFS_API_CHECK_RES(fs, res) \
if ((res) < SPIFFS_OK) { \ if ((res) < SPIFFS_OK) { \
(fs)->errno = (res); \ (fs)->err_code = (res); \
return -1; \ return -1; \
} }
#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \ #define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \
if ((res) < SPIFFS_OK) { \ if ((res) < SPIFFS_OK) { \
(fs)->errno = (res); \ (fs)->err_code = (res); \
SPIFFS_UNLOCK(fs); \ SPIFFS_UNLOCK(fs); \
return -1; \ return -1; \
} }
@ -381,6 +403,8 @@ typedef struct {
// object structs // object structs
// page header, part of each page except object lookup pages // page header, part of each page except object lookup pages
// NB: this is always aligned when the data page is an object index,
// as in this case struct spiffs_page_object_ix is used
typedef struct __attribute(( packed )) { typedef struct __attribute(( packed )) {
// object id // object id
spiffs_obj_id obj_id; spiffs_obj_id obj_id;
@ -391,7 +415,11 @@ typedef struct __attribute(( packed )) {
} spiffs_page_header; } spiffs_page_header;
// object index header page header // object index header page header
typedef struct __attribute(( packed )) { typedef struct __attribute(( packed ))
#if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES
__attribute(( aligned(sizeof(spiffs_page_ix)) ))
#endif
{
// common page header // common page header
spiffs_page_header p_hdr; spiffs_page_header p_hdr;
// alignment // alignment
@ -400,8 +428,6 @@ typedef struct __attribute(( packed )) {
u32_t size; u32_t size;
// type of object // type of object
spiffs_obj_type type; spiffs_obj_type type;
// alignment2
u8_t _align2[4 - (sizeof(spiffs_obj_type)&3)==0 ? 4 : (sizeof(spiffs_obj_type)&3)];
// name of object // name of object
u8_t name[SPIFFS_OBJ_NAME_LEN]; u8_t name[SPIFFS_OBJ_NAME_LEN];
} spiffs_page_object_ix_header; } spiffs_page_object_ix_header;
@ -480,6 +506,10 @@ s32_t spiffs_obj_lu_find_entry_visitor(
spiffs_block_ix *block_ix, spiffs_block_ix *block_ix,
int *lu_entry); int *lu_entry);
s32_t spiffs_erase_block(
spiffs *fs,
spiffs_block_ix bix);
// --------------- // ---------------
s32_t spiffs_obj_lu_scan( s32_t spiffs_obj_lu_scan(
@ -627,7 +657,8 @@ s32_t spiffs_gc_erase_page_stats(
s32_t spiffs_gc_find_candidate( s32_t spiffs_gc_find_candidate(
spiffs *fs, spiffs *fs,
spiffs_block_ix **block_candidate, spiffs_block_ix **block_candidate,
int *candidate_count); int *candidate_count,
char fs_crammed);
s32_t spiffs_gc_clean( s32_t spiffs_gc_clean(
spiffs *fs, spiffs *fs,

38
doc/exception_causes.md Normal file
View File

@ -0,0 +1,38 @@
Exception Causes (EXCCAUSE)
===========================================
| EXC-CAUSE Code | Cause Name | Cause Description | Required Option | EXC-VADDR Loaded |
|:--------------:|:---------------------------|:------------------------------------------------------------------------------------------------------------|:-------------------------|:----------------:|
| 0 | IllegalInstructionCause | Illegal instruction | Exception | No |
| 1 | SyscallCause | SYSCALL instruction | Exception | No |
| 2 | InstructionFetchErrorCause | Processor internal physical address or data error during instruction fetch | Exception | Yes |
| 3 | LoadStoreErrorCause | Processor internal physical address or data error during load or store | Exception | Yes |
| 4 | Level1InterruptCause | Level-1 interrupt as indicated by set level-1 bits in the INTERRUPT register | Interrupt | No |
| 5 | AllocaCause | MOVSP instruction, if callers registers are not in the register file | Windowed Register | No |
| 6 | IntegerDivideByZeroCause | QUOS, QUOU, REMS, or REMU divisor operand is zero | 32-bit Integer Divide | No |
| 7 | Reserved for Tensilica | | | |
| 8 | PrivilegedCause | Attempt to execute a privileged operation when CRING ? 0 | MMU | No |
| 9 | LoadStoreAlignmentCause | Load or store to an unaligned address | Unaligned Exception | Yes |
| 10..11 | Reserved for Tensilica | | | |
| 12 | InstrPIFDataErrorCause | PIF data error during instruction fetch | Processor Interface | Yes |
| 13 | LoadStorePIFDataErrorCause | Synchronous PIF data error during LoadStore access | Processor Interface | Yes |
| 14 | InstrPIFAddrErrorCause | PIF address error during instruction fetch | Processor Interface | Yes |
| 15 | LoadStorePIFAddrErrorCause | Synchronous PIF address error during LoadStore access | Processor Interface | Yes |
| 16 | InstTLBMissCause | Error during Instruction TLB refill | MMU | Yes |
| 17 | InstTLBMultiHitCause | Multiple instruction TLB entries matched | MMU | Yes |
| 18 | InstFetchPrivilegeCause | An instruction fetch referenced a virtual address at a ring level less than CRING | MMU | Yes |
| 19 | Reserved for Tensilica | | | |
| 20 | InstFetchProhibitedCause | An instruction fetch referenced a page mapped with an attribute that does not permit instruction fetch | Region Protection or MMU | Yes |
| 21..23 | Reserved for Tensilica | | | |
| 24 | LoadStoreTLBMissCause | Error during TLB refill for a load or store | MMU | Yes |
| 25 | LoadStoreTLBMultiHitCause | Multiple TLB entries matched for a load or store | MMU | Yes |
| 26 | LoadStorePrivilegeCause | A load or store referenced a virtual address at a ring level less than CRING | MMU | Yes |
| 27 | Reserved for Tensilica | | | |
| 28 | LoadProhibitedCause | A load referenced a page mapped with an attribute that does not permit loads | Region Protection or MMU | Yes |
| 29 | StoreProhibitedCause | A store referenced a page mapped with an attribute that does not permit stores | Region Protection or MMU | Yes |
| 30..31 | Reserved for Tensilica | | | |
| 32..39 | CoprocessornDisabled | Coprocessor n instruction when cpn disabled. n varies 0..7 as the cause varies 32..39 | Coprocessor | No |
| 40..63 | Reserved | | | |
Infos from Xtensa Instruction Set Architecture (ISA) Reference Manual

View File

@ -28,9 +28,10 @@
#include "os_type.h" #include "os_type.h"
#include "osapi.h" #include "osapi.h"
#include "spi_flash.h" #include "spi_flash.h"
extern uint32_t _SPIFFS_end;
} }
#define CONFIG_START_SECTOR 0x7b #define CONFIG_START_SECTOR (((uint32_t)_SPIFFS_end - 0x40200000) / 4096)
#define CONFIG_SECTOR (CONFIG_START_SECTOR + 0) #define CONFIG_SECTOR (CONFIG_START_SECTOR + 0)
#define CONFIG_ADDR (SPI_FLASH_SEC_SIZE * CONFIG_SECTOR) #define CONFIG_ADDR (SPI_FLASH_SEC_SIZE * CONFIG_SECTOR)
@ -41,7 +42,7 @@ EEPROMClass::EEPROMClass()
void EEPROMClass::begin(size_t size) void EEPROMClass::begin(size_t size)
{ {
if (size < 0) if (size <= 0)
return; return;
if (size > SPI_FLASH_SEC_SIZE) if (size > SPI_FLASH_SEC_SIZE)
size = SPI_FLASH_SEC_SIZE; size = SPI_FLASH_SEC_SIZE;
@ -49,7 +50,9 @@ void EEPROMClass::begin(size_t size)
_data = new uint8_t[size]; _data = new uint8_t[size];
_size = size; _size = size;
noInterrupts();
spi_flash_read(CONFIG_ADDR, reinterpret_cast<uint32_t*>(_data), _size); spi_flash_read(CONFIG_ADDR, reinterpret_cast<uint32_t*>(_data), _size);
interrupts();
} }
void EEPROMClass::end() void EEPROMClass::end()
@ -58,8 +61,9 @@ void EEPROMClass::end()
return; return;
commit(); commit();
if(_data) {
delete[] _data; delete[] _data;
}
_data = 0; _data = 0;
_size = 0; _size = 0;
} }
@ -69,6 +73,8 @@ uint8_t EEPROMClass::read(int address)
{ {
if (address < 0 || (size_t)address >= _size) if (address < 0 || (size_t)address >= _size)
return 0; return 0;
if(!_data)
return 0;
return _data[address]; return _data[address];
} }
@ -77,6 +83,8 @@ void EEPROMClass::write(int address, uint8_t value)
{ {
if (address < 0 || (size_t)address >= _size) if (address < 0 || (size_t)address >= _size)
return; return;
if(!_data)
return;
_data[address] = value; _data[address] = value;
_dirty = true; _dirty = true;
@ -89,20 +97,24 @@ bool EEPROMClass::commit()
return false; return false;
if(!_dirty) if(!_dirty)
return true; return true;
if(!_data)
return false;
ETS_UART_INTR_DISABLE(); noInterrupts();
if(spi_flash_erase_sector(CONFIG_SECTOR) == SPI_FLASH_RESULT_OK) { if(spi_flash_erase_sector(CONFIG_SECTOR) == SPI_FLASH_RESULT_OK) {
if(spi_flash_write(CONFIG_ADDR, reinterpret_cast<uint32_t*>(_data), _size) == SPI_FLASH_RESULT_OK) { if(spi_flash_write(CONFIG_ADDR, reinterpret_cast<uint32_t*>(_data), _size) == SPI_FLASH_RESULT_OK) {
_dirty = false; _dirty = false;
ret = true; ret = true;
} }
} }
ETS_UART_INTR_ENABLE(); interrupts();
return ret; return ret;
} }
uint8_t * EEPROMClass::getDataPtr() uint8_t * EEPROMClass::getDataPtr()
{ {
_dirty = true;
return &_data[0]; return &_data[0];
} }

View File

@ -76,6 +76,20 @@ public:
void sendHeader(String name, String value, bool first = false); void sendHeader(String name, String value, bool first = false);
void sendContent(String content); void sendContent(String content);
template<typename T> size_t streamFile(T &file, String contentType){
String head = "HTTP/1.1 200 OK\r\nContent-Type: ";
head += contentType;
head += "\r\nContent-Length: ";
head += file.size();
head += "\r\nConnection: close";
head += "\r\nAccess-Control-Allow-Origin: *";
head += "\r\n\r\n";
_currentClient.print(head);
head = String();
return _currentClient.write(file, HTTP_DOWNLOAD_UNIT_SIZE);
}
protected: protected:
void _handleRequest(); void _handleRequest();
bool _parseRequest(WiFiClient& client); bool _parseRequest(WiFiClient& client);

View File

@ -0,0 +1,33 @@
/*
* This sketch trys to Connect to the best AP based on a given list
*
*/
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
ESP8266WiFiMulti WiFiMulti = ESP8266WiFiMulti();
void setup() {
Serial.begin(115200);
delay(10);
WiFiMulti.addAP("ssid_from_AP_1", "your_password_for_AP_1");
WiFiMulti.addAP("ssid_from_AP_2", "your_password_for_AP_2");
WiFiMulti.addAP("ssid_from_AP_3", "your_password_for_AP_3");
Serial.println("Connecting Wifi...");
if(wifiMulti.run() == WL_CONNECTED) {
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
}
void loop() {
if(wifiMulti.run() != WL_CONNECTED) {
Serial.println("WiFi not connected!");
delay(1000);
}
}

View File

@ -81,6 +81,14 @@ int ESP8266WiFiClass::begin(const char* ssid, const char *passphrase)
return status(); return status();
} }
uint8_t ESP8266WiFiClass::waitForConnectResult(){
if ((wifi_get_opmode() & 1) == 0)//1 and 3 have STA enabled
return WL_DISCONNECTED;
while (status() == WL_DISCONNECTED)
delay(100);
return status();
}
void ESP8266WiFiClass::config(IPAddress local_ip, IPAddress gateway, IPAddress subnet) void ESP8266WiFiClass::config(IPAddress local_ip, IPAddress gateway, IPAddress subnet)
{ {
struct ip_info info; struct ip_info info;
@ -336,7 +344,7 @@ uint8_t ESP8266WiFiClass::encryptionType(uint8_t i)
return -1; return -1;
} }
uint8_t ESP8266WiFiClass::status() wl_status_t ESP8266WiFiClass::status()
{ {
int status = wifi_station_get_connect_status(); int status = wifi_station_get_connect_status();

View File

@ -58,6 +58,10 @@ public:
*/ */
int begin(const char* ssid, const char *passphrase); int begin(const char* ssid, const char *passphrase);
/* Wait for Wifi connection to reach a result
* returns the status reached or disconnect if STA is off
*/
uint8_t waitForConnectResult();
/* Set up an open access point /* Set up an open access point
* *
@ -195,7 +199,7 @@ public:
* *
* return: one of the value defined in wl_status_t * return: one of the value defined in wl_status_t
*/ */
uint8_t status(); wl_status_t status();
/* /*
* Resolve the given hostname to an IP address. * Resolve the given hostname to an IP address.

View File

@ -0,0 +1,171 @@
/**
*
* @file ESP8266WiFiMulti.cpp
* @date 16.05.2015
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the esp8266 core for Arduino environment.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "ESP8266WiFiMulti.h"
#include <limits.h>
ESP8266WiFiMulti::ESP8266WiFiMulti() {
}
ESP8266WiFiMulti::~ESP8266WiFiMulti() {
APlistClean();
}
bool ESP8266WiFiMulti::addAP(const char* ssid, const char *passphrase) {
return APlistAdd(ssid, passphrase);
}
wl_status_t ESP8266WiFiMulti::run(void) {
wl_status_t status = WiFi.status();
if(status == WL_DISCONNECTED || status == WL_NO_SSID_AVAIL || status == WL_IDLE_STATUS || status == WL_CONNECT_FAILED) {
WifiAPlist_t bestNetwork { NULL, NULL };
int bestNetworkDb = INT_MIN;
// WiFi.scanNetworks will return the number of networks found
int8_t n = WiFi.scanNetworks();
DEBUG_WIFI_MULTI("[WIFI] scan done\n");
delay(0);
if(n <= 0) {
DEBUG_WIFI_MULTI("[WIFI] no networks found\n");
} else {
DEBUG_WIFI_MULTI("[WIFI] %d networks found\n", n);
for(int8_t i = 0; i < n; ++i) {
const char * ssid_scan = WiFi.SSID(i);
int32_t rssi_scan = WiFi.RSSI(i);
uint8_t sec_scan = WiFi.encryptionType(i);
bool known = false;
for(uint32_t x = 0; x < APlist.size(); x++) {
WifiAPlist_t entry = APlist[x];
if(strcmp(entry.ssid, ssid_scan) == 0) { // SSID match
known = true;
if(rssi_scan > bestNetworkDb) { // best network
if(sec_scan == ENC_TYPE_NONE || entry.passphrase) { // check for passphrase if not open wlan
bestNetworkDb = rssi_scan;
memcpy((void*) &bestNetwork, (void*) &entry, sizeof(bestNetwork));
}
}
break;
}
}
if(known) {
DEBUG_WIFI_MULTI(" ---> ");
} else {
DEBUG_WIFI_MULTI(" ");
}
DEBUG_WIFI_MULTI(" %d: %s (%d) %c\n", i, ssid_scan, rssi_scan, (sec_scan == ENC_TYPE_NONE) ? ' ' : '*');
delay(0);
}
}
DEBUG_WIFI_MULTI("\n\n");
delay(0);
if(bestNetwork.ssid) {
DEBUG_WIFI_MULTI("[WIFI] Connecting SSID: %s (%d)\n", bestNetwork.ssid, bestNetworkDb);
WiFi.begin(bestNetwork.ssid, bestNetwork.passphrase);
status = WiFi.status();
// wait for connection or fail
while(status != WL_CONNECTED && status != WL_NO_SSID_AVAIL && status != WL_CONNECT_FAILED) {
delay(10);
status = WiFi.status();
}
IPAddress ip;
switch(status) {
case WL_CONNECTED:
ip = WiFi.localIP();
DEBUG_WIFI_MULTI("[WIFI] Connecting done.\n");
DEBUG_WIFI_MULTI("[WIFI] SSID: %s\n", WiFi.SSID());
DEBUG_WIFI_MULTI("[WIFI] IP: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
break;
case WL_NO_SSID_AVAIL:
DEBUG_WIFI_MULTI("[WIFI] Connecting Faild AP not found.\n");
break;
case WL_CONNECT_FAILED:
DEBUG_WIFI_MULTI("[WIFI] Connecting Faild.\n");
break;
default:
DEBUG_WIFI_MULTI("[WIFI] Connecting Faild (%d).\n", status);
break;
}
} else {
DEBUG_WIFI_MULTI("[WIFI] no matching wifi found!\n");
}
}
return status;
}
// ##################################################################################
bool ESP8266WiFiMulti::APlistAdd(const char* ssid, const char *passphrase) {
WifiAPlist_t newAP;
newAP.ssid = (char*) malloc(strlen(ssid));
if(!newAP.ssid) {
return false;
}
strcpy(newAP.ssid, ssid);
if(passphrase && *passphrase != 0x00) {
newAP.passphrase = (char*) malloc(strlen(passphrase));
}
if(!newAP.passphrase) {
free(newAP.ssid);
return false;
}
strcpy(newAP.passphrase, passphrase);
APlist.push_back(newAP);
return true;
}
void ESP8266WiFiMulti::APlistClean(void) {
for(uint32_t i = 0; i < APlist.size(); i++) {
WifiAPlist_t entry = APlist[i];
if(entry.ssid) {
free(entry.ssid);
}
if(entry.passphrase) {
free(entry.passphrase);
}
}
APlist.clear();
}

View File

@ -0,0 +1,60 @@
/**
*
* @file ESP8266WiFiMulti.h
* @date 16.05.2015
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the esp8266 core for Arduino environment.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef WIFICLIENTMULTI_H_
#define WIFICLIENTMULTI_H_
#include "ESP8266WiFi.h"
#include <vector>
//#define DEBUG_WIFI_MULTI(...) os_printf( __VA_ARGS__ )
#ifndef DEBUG_WIFI_MULTI
#define DEBUG_WIFI_MULTI(...)
#endif
typedef struct {
char * ssid;
char * passphrase;
} WifiAPlist_t;
class ESP8266WiFiMulti {
public:
ESP8266WiFiMulti();
~ESP8266WiFiMulti();
bool addAP(const char* ssid, const char *passphrase = NULL);
wl_status_t run(void);
private:
std::vector<WifiAPlist_t> APlist;
bool APlistAdd(const char* ssid, const char *passphrase = NULL);
void APlistClean(void);
};
#endif /* WIFICLIENTMULTI_H_ */

View File

@ -50,12 +50,12 @@
typedef enum { typedef enum {
WL_NO_SHIELD = 255, // for compatibility with WiFi Shield library WL_NO_SHIELD = 255, // for compatibility with WiFi Shield library
WL_IDLE_STATUS = 0, WL_IDLE_STATUS = 0,
WL_NO_SSID_AVAIL, WL_NO_SSID_AVAIL = 1,
WL_SCAN_COMPLETED, WL_SCAN_COMPLETED = 2,
WL_CONNECTED, WL_CONNECTED = 3,
WL_CONNECT_FAILED, WL_CONNECT_FAILED = 4,
WL_CONNECTION_LOST, WL_CONNECTION_LOST = 5,
WL_DISCONNECTED WL_DISCONNECTED = 6
} wl_status_t; } wl_status_t;
/* Encryption modes */ /* Encryption modes */

View File

@ -106,6 +106,14 @@ public:
boolean rmdir(char *filepath); boolean rmdir(char *filepath);
uint8_t type(){ return card.type(); }
uint8_t fatType(){ return volume.fatType(); }
size_t blocksPerCluster(){ return volume.blocksPerCluster(); }
size_t totalClusters(){ return volume.clusterCount(); }
size_t blockSize(){ return (size_t)0x200; }
size_t totalBlocks(){ return (totalClusters() / blocksPerCluster()); }
size_t clusterSize(){ return blocksPerCluster() * blockSize(); }
size_t size(){ return (clusterSize() * totalClusters()); }
private: private:
// This is used to determine the mode used to open a file // This is used to determine the mode used to open a file

View File

@ -121,14 +121,14 @@ void SPIClass::setBitOrder(uint8_t bitOrder) {
* @return * @return
*/ */
static uint32_t ClkRegToFreq(spiClk_t * reg) { static uint32_t ClkRegToFreq(spiClk_t * reg) {
return (F_CPU / ((reg->regPre + 1) * (reg->regN + 1))); return (SPI_MAX_SPEED / ((reg->regPre + 1) * (reg->regN + 1)));
} }
void SPIClass::setFrequency(uint32_t freq) { void SPIClass::setFrequency(uint32_t freq) {
static uint32_t lastSetFrequency = 0; static uint32_t lastSetFrequency = 0;
static uint32_t lastSetRegister = 0; static uint32_t lastSetRegister = 0;
if(freq >= F_CPU) { if(freq >= SPI_MAX_SPEED) {
setClockDivider(0x80000000); setClockDivider(0x80000000);
return; return;
} }
@ -164,7 +164,7 @@ void SPIClass::setFrequency(uint32_t freq) {
reg.regN = calN; reg.regN = calN;
while(calPreVari++ <= 1) { // test different variants for Pre (we calculate in int so we miss the decimals, testing is the easyest and fastest way) while(calPreVari++ <= 1) { // test different variants for Pre (we calculate in int so we miss the decimals, testing is the easyest and fastest way)
calPre = (((F_CPU / (reg.regN + 1)) / freq) - 1) + calPreVari; calPre = (((SPI_MAX_SPEED / (reg.regN + 1)) / freq) - 1) + calPreVari;
if(calPre > 0x1FFF) { if(calPre > 0x1FFF) {
reg.regPre = 0x1FFF; // 8191 reg.regPre = 0x1FFF; // 8191
} else if(calPre <= 0) { } else if(calPre <= 0) {

View File

@ -45,6 +45,8 @@
#define SPI_CLOCK_DIV64 0x04fc1001 //250 KHz #define SPI_CLOCK_DIV64 0x04fc1001 //250 KHz
#endif #endif
#define SPI_MAX_SPEED (80000000L)
const uint8_t SPI_MODE0 = 0x00; ///< CPOL: 0 CPHA: 0 const uint8_t SPI_MODE0 = 0x00; ///< CPOL: 0 CPHA: 0
const uint8_t SPI_MODE1 = 0x01; ///< CPOL: 0 CPHA: 1 const uint8_t SPI_MODE1 = 0x01; ///< CPOL: 0 CPHA: 1
const uint8_t SPI_MODE2 = 0x10; ///< CPOL: 1 CPHA: 0 const uint8_t SPI_MODE2 = 0x10; ///< CPOL: 1 CPHA: 0

View File

@ -15,7 +15,7 @@ compiler.sdk.path={compiler.tools.path}sdk/
compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ "-I{compiler.sdk.path}/include" compiler.cpreprocessor.flags=-D__ets__ -DICACHE_FLASH -U__STRICT_ANSI__ "-I{compiler.sdk.path}/include"
compiler.c.cmd=xtensa-lx106-elf-gcc compiler.c.cmd=xtensa-lx106-elf-gcc
compiler.c.flags=-c -Os -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -MMD -std=c99 compiler.c.flags=-c -Os -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -falign-functions=4 -MMD -std=c99
compiler.S.cmd=xtensa-lx106-elf-gcc compiler.S.cmd=xtensa-lx106-elf-gcc
compiler.S.flags=-c -g -x assembler-with-cpp -MMD compiler.S.flags=-c -g -x assembler-with-cpp -MMD
@ -25,7 +25,7 @@ compiler.c.elf.cmd=xtensa-lx106-elf-gcc
compiler.c.elf.libs=-lm -lgcc -lhal -lphy -lnet80211 -llwip -lwpa -lmain -lpp -lsmartconfig compiler.c.elf.libs=-lm -lgcc -lhal -lphy -lnet80211 -llwip -lwpa -lmain -lpp -lsmartconfig
compiler.cpp.cmd=xtensa-lx106-elf-g++ compiler.cpp.cmd=xtensa-lx106-elf-g++
compiler.cpp.flags=-c -Os -mlongcalls -mtext-section-literals -fno-exceptions -fno-rtti -std=c++11 -MMD compiler.cpp.flags=-c -Os -mlongcalls -mtext-section-literals -fno-exceptions -fno-rtti -falign-functions=4 -std=c++11 -MMD
compiler.as.cmd=xtensa-lx106-elf-as compiler.as.cmd=xtensa-lx106-elf-as