1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-21 10:26:06 +03:00

Merge branch 'feature/fs' into esp8266

This commit is contained in:
Ivan Grokhotkov 2015-07-28 18:10:26 +03:00
commit f96e36ad02
9 changed files with 1058 additions and 9 deletions

230
cores/esp8266/FS.cpp Normal file
View File

@ -0,0 +1,230 @@
/*
FS.cpp - file system wrapper
Copyright (c) 2015 Ivan Grokhotkov. 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 "FS.h"
#include "FSImpl.h"
static bool sflags(const char* mode, OpenMode& om, AccessMode& am);
size_t File::write(uint8_t c) {
if (!_p)
return 0;
_p->write(&c, 1);
}
size_t File::write(const uint8_t *buf, size_t size) {
if (!_p)
return 0;
_p->write(buf, size);
}
int File::available() {
if (!_p)
return false;
return _p->position() < _p->size();
}
int File::read() {
if (!_p)
return -1;
uint8_t result;
if (_p->read(&result, 1) != 1) {
return -1;
}
return result;
}
size_t File::read(uint8_t* buf, size_t size) {
if (!_p)
return -1;
return _p->read(buf, size);
}
int File::peek() {
if (!_p)
return -1;
size_t curPos = _p->position();
int result = read();
seek(curPos, SeekSet);
return result;
}
void File::flush() {
if (!_p)
return;
_p->flush();
}
bool File::seek(uint32_t pos, SeekMode mode) {
if (!_p)
return false;
return _p->seek(pos, mode);
}
size_t File::position() const {
if (!_p)
return 0;
return _p->position();
}
size_t File::size() const {
if (!_p)
return 0;
return _p->size();
}
void File::close() {
if (_p) {
_p->close();
_p = nullptr;
}
}
File::operator bool() const {
return !!_p;
}
File Dir::openFile(const char* mode) {
if (!_impl) {
return File();
}
OpenMode om;
AccessMode am;
if (!sflags(mode, om, am)) {
DEBUGV("Dir::openFile: invalid mode `%s`\r\n", mode);
return File();
}
return File(_impl->openFile(om, am));
}
String Dir::fileName() {
if (!_impl) {
return String();
}
return _impl->fileName();
}
bool Dir::next() {
if (!_impl) {
return false;
}
return _impl->next();
}
File FS::open(const char* path, const char* mode) {
if (!_impl) {
return File();
}
OpenMode om;
AccessMode am;
if (!sflags(mode, om, am)) {
DEBUGV("FS::open: invalid mode `%s`\r\n", mode);
return File();
}
return File(_impl->open(path, om, am));
}
File FS::open(const String& path, const char* mode) {
return FS::open(path.c_str(), mode);
}
Dir FS::openDir(const char* path) {
if (!_impl) {
return Dir();
}
return Dir(_impl->openDir(path));
}
Dir FS::openDir(const String& path) {
return FS::openDir(path.c_str());
}
struct MountEntry {
FSImplPtr fs;
String path;
MountEntry* next;
};
static MountEntry* s_mounted = nullptr;
template<>
bool mount<FS>(FS& fs, const char* mountPoint) {
FSImplPtr p = fs._impl;
if (!p || !p->mount()) {
DEBUGV("FSImpl mount failed\r\n");
return false;
}
MountEntry* entry = new MountEntry;
entry->fs = p;
entry->path = mountPoint;
entry->next = s_mounted;
s_mounted = entry;
return true;
}
static bool sflags(const char* mode, OpenMode& om, AccessMode& am) {
switch (mode[0]) {
case 'r':
am = AM_READ;
om = OM_DEFAULT;
break;
case 'w':
am = AM_WRITE;
om = (OpenMode) (OM_CREATE | OM_TRUNCATE);
break;
case 'a':
am = AM_WRITE;
om = (OpenMode) (OM_CREATE | OM_APPEND);
break;
default:
return false;
}
switch(mode[1]) {
case '+':
am = (AccessMode) (AM_WRITE | AM_READ);
break;
case 0:
break;
default:
return false;
}
return true;
}

106
cores/esp8266/FS.h Normal file
View File

@ -0,0 +1,106 @@
/*
FS.h - file system wrapper
Copyright (c) 2015 Ivan Grokhotkov. 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 FS_H
#define FS_H
#include <Arduino.h>
#include <memory>
class FileImpl;
typedef std::shared_ptr<FileImpl> FileImplPtr;
class FSImpl;
typedef std::shared_ptr<FSImpl> FSImplPtr;
class DirImpl;
typedef std::shared_ptr<DirImpl> DirImplPtr;
template <typename Tfs>
bool mount(Tfs& fs, const char* mountPoint);
enum SeekMode {
SeekSet = 0,
SeekCur = 1,
SeekEnd = 2
};
class File : public Stream
{
public:
File(FileImplPtr p = FileImplPtr()) : _p(p) {}
// Print methods:
size_t write(uint8_t) override;
size_t write(const uint8_t *buf, size_t size) override;
// Stream methods:
int available() override;
int read() override;
int peek() override;
void flush() override;
size_t read(uint8_t* buf, size_t size);
bool seek(uint32_t pos, SeekMode mode);
size_t position() const;
size_t size() const;
void close();
operator bool() const;
protected:
FileImplPtr _p;
};
class Dir {
public:
Dir(DirImplPtr impl = DirImplPtr()): _impl(impl) { }
File openFile(const char* mode);
String fileName();
bool next();
protected:
DirImplPtr _impl;
};
class FS
{
public:
FS(FSImplPtr impl) : _impl(impl) { }
File open(const char* path, const char* mode);
File open(const String& path, const char* mode);
Dir openDir(const char* path);
Dir openDir(const String& path);
protected:
FSImplPtr _impl;
template <typename Tfs>
friend bool mount(Tfs& fs, const char* mountPoint);
};
extern FS SPIFFS;
template<>
bool mount<FS>(FS& fs, const char* mountPoint);
File open(const char* path, const char* mode);
Dir openDir(const char* path);
#endif //FS_H

67
cores/esp8266/FSImpl.h Normal file
View File

@ -0,0 +1,67 @@
/*
FSImpl.h - base file system interface
Copyright (c) 2015 Ivan Grokhotkov. 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 FSIMPL_H
#define FSIMPL_H
#include <stddef.h>
#include <stdint.h>
class FileImpl {
public:
virtual ~FileImpl() { }
virtual size_t write(const uint8_t *buf, size_t size) = 0;
virtual size_t read(uint8_t* buf, size_t size) = 0;
virtual void flush() = 0;
virtual bool seek(uint32_t pos, SeekMode mode) = 0;
virtual size_t position() const = 0;
virtual size_t size() const = 0;
virtual void close() = 0;
};
enum OpenMode {
OM_DEFAULT = 0,
OM_CREATE = 1,
OM_APPEND = 2,
OM_TRUNCATE = 4
};
enum AccessMode {
AM_READ = 1,
AM_WRITE = 2,
AM_RW = AM_READ | AM_WRITE
};
class DirImpl {
public:
virtual FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) = 0;
virtual const char* fileName() = 0;
virtual bool next() = 0;
};
class FSImpl {
public:
virtual FileImplPtr open(const char* path, OpenMode openMode, AccessMode accessMode) = 0;
virtual DirImplPtr openDir(const char* path) = 0;
virtual bool mount() = 0;
};
#endif //FSIMPL_H

View File

@ -0,0 +1,30 @@
#ifndef INTERRUPTS_H
#define INTERRUPTS_H
#include <stddef.h>
#include <stdint.h>
extern "C" {
#include "c_types.h"
#include "ets_sys.h"
}
#define xt_disable_interrupts(state, level) __asm__ __volatile__("rsil %0," __STRINGIFY(level) : "=a" (state))
#define xt_enable_interrupts(state) __asm__ __volatile__("wsr %0,ps; isync" :: "a" (state) : "memory")
class InterruptLock {
public:
InterruptLock() {
xt_disable_interrupts(_state, 15);
}
~InterruptLock() {
xt_enable_interrupts(_state);
}
protected:
uint32_t _state;
};
#endif //INTERRUPTS_H

View File

@ -8,15 +8,16 @@
#ifndef SPIFFS_CONFIG_H_ #ifndef SPIFFS_CONFIG_H_
#define SPIFFS_CONFIG_H_ #define SPIFFS_CONFIG_H_
#include "mem.h" #include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "c_types.h" #include "c_types.h"
#include "stddef.h"
#include "osapi.h"
#include "ets_sys.h" #include "ets_sys.h"
#define c_memcpy os_memcpy
#define c_printf os_printf #define c_memcpy memcpy
#define c_memset os_memset #define c_printf ets_printf
#define c_memset memset
typedef signed short file_t; typedef signed short file_t;
typedef int32_t s32_t; typedef int32_t s32_t;
@ -65,7 +66,7 @@ typedef uint8_t u8_t;
// for filedescriptor and cache buffers. Once decided for a configuration, // for filedescriptor and cache buffers. Once decided for a configuration,
// this can be disabled to reduce flash. // this can be disabled to reduce flash.
#ifndef SPIFFS_BUFFER_HELP #ifndef SPIFFS_BUFFER_HELP
#define SPIFFS_BUFFER_HELP 0 #define SPIFFS_BUFFER_HELP 1
#endif #endif
// Enables/disable memory read caching of nucleus file system operations. // Enables/disable memory read caching of nucleus file system operations.
@ -140,7 +141,7 @@ typedef uint8_t u8_t;
// not on mount point. If not, SPIFFS_format must be called prior to mounting // not on mount point. If not, SPIFFS_format must be called prior to mounting
// again. // again.
#ifndef SPIFFS_USE_MAGIC #ifndef SPIFFS_USE_MAGIC
#define SPIFFS_USE_MAGIC (0) #define SPIFFS_USE_MAGIC (1)
#endif #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

View File

@ -556,4 +556,3 @@ s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) {
return res; return res;
} }

View File

@ -0,0 +1,348 @@
/*
spiffs_api.cpp - file system wrapper for SPIFFS
Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
This code was influenced by NodeMCU and Sming libraries, and first version of
Arduino wrapper written by Hristo Gochkov.
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 "FS.h"
#include "FSImpl.h"
#include "spiffs/spiffs.h"
#include "debug.h"
extern "C" {
#include "c_types.h"
#include "spi_flash.h"
}
extern int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src);
extern int32_t spiffs_hal_erase(uint32_t addr, uint32_t size);
extern int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst);
int getSpiffsMode(OpenMode openMode, AccessMode accessMode);
class SPIFFSFileImpl;
class SPIFFSDirImpl;
class SPIFFSImpl : public FSImpl {
public:
SPIFFSImpl(uint32_t start, uint32_t size, uint32_t pageSize, uint32_t blockSize, uint32_t maxOpenFds)
: _fs({0})
, _start(start)
, _size(size)
, _pageSize(pageSize)
, _blockSize(blockSize)
, _maxOpenFds(maxOpenFds)
{
}
FileImplPtr open(const char* path, OpenMode openMode, AccessMode accessMode) override;
DirImplPtr openDir(const char* path) override;
bool mount() override {
if (SPIFFS_mounted(&_fs) != 0) {
return true;
}
if (_tryMount()) {
return true;
}
auto rc = SPIFFS_format(&_fs);
if (rc != SPIFFS_OK) {
DEBUGV("SPIFFS_format: rc=%d, err=%d\r\n", rc, _fs.err_code);
return false;
}
return _tryMount();
}
protected:
friend class SPIFFSFileImpl;
friend class SPIFFSDirImpl;
spiffs* getFs() {
return &_fs;
}
bool _tryMount() {
spiffs_config config = {0};
config.hal_read_f = &spiffs_hal_read;
config.hal_write_f = &spiffs_hal_write;
config.hal_erase_f = &spiffs_hal_erase;
config.phys_size = _size;
config.phys_addr = _start;
config.phys_erase_block = SPI_FLASH_SEC_SIZE;
config.log_block_size = _blockSize;
config.log_page_size = _pageSize;
// hack: even though fs is not initialized at this point,
// SPIFFS_buffer_bytes_for_cache uses only fs->config.log_page_size
// suggestion: change SPIFFS_buffer_bytes_for_cache to take
// spiffs_config* instead of spiffs* as an argument
_fs.cfg.log_page_size = config.log_page_size;
size_t workBufSize = 2 * _pageSize;
size_t fdsBufSize = SPIFFS_buffer_bytes_for_filedescs(&_fs, _maxOpenFds);
size_t cacheBufSize = SPIFFS_buffer_bytes_for_cache(&_fs, _maxOpenFds);
if (!_workBuf) {
DEBUGV("SPIFFSImpl: allocating %d+%d+%d=%d bytes\r\n",
workBufSize, fdsBufSize, cacheBufSize,
workBufSize + fdsBufSize + cacheBufSize);
_workBuf.reset(new uint8_t[workBufSize]);
_fdsBuf.reset(new uint8_t[fdsBufSize]);
_cacheBuf.reset(new uint8_t[cacheBufSize]);
}
DEBUGV("SPIFFSImpl: mounting fs @%x, size=%x, block=%x, page=%x\r\n",
_start, _size, _blockSize, _pageSize);
auto err = SPIFFS_mount(&_fs, &config, _workBuf.get(),
_fdsBuf.get(), fdsBufSize, _cacheBuf.get(), cacheBufSize,
&SPIFFSImpl::_check_cb);
DEBUGV("SPIFFSImpl: mount rc=%d\r\n", err);
return err == SPIFFS_OK;
}
static void _check_cb(spiffs_check_type type, spiffs_check_report report,
uint32_t arg1, uint32_t arg2) {
// TODO: spiffs doesn't pass any context pointer along with _check_cb,
// so we can't do anything useful here other than perhaps
// feeding the watchdog
}
spiffs _fs;
uint32_t _start;
uint32_t _size;
uint32_t _pageSize;
uint32_t _blockSize;
uint32_t _maxOpenFds;
std::unique_ptr<uint8_t[]> _workBuf;
std::unique_ptr<uint8_t[]> _fdsBuf;
std::unique_ptr<uint8_t[]> _cacheBuf;
};
#define CHECKFD() while (_fd == 0) { DEBUGV("SPIFFSFileImpl(%d) _fd == 0\r\n", __LINE__); abort(); }
class SPIFFSFileImpl : public FileImpl {
public:
SPIFFSFileImpl(SPIFFSImpl* fs, spiffs_file fd)
: _fs(fs)
, _fd(fd)
, _stat({0})
{
CHECKFD();
auto rc = SPIFFS_fstat(_fs->getFs(), _fd, &_stat);
if (rc != SPIFFS_OK) {
DEBUGV("SPIFFS_fstat rc=%d\r\n", rc);
_stat = {0};
}
}
~SPIFFSFileImpl() override {
close();
}
size_t write(const uint8_t *buf, size_t size) override {
CHECKFD();
auto result = SPIFFS_write(_fs->getFs(), _fd, (void*) buf, size);
if (result < 0) {
DEBUGV("SPIFFS_write rc=%d\r\n", result);
return 0;
}
return result;
}
size_t read(uint8_t* buf, size_t size) override {
CHECKFD();
auto result = SPIFFS_read(_fs->getFs(), _fd, (void*) buf, size);
if (result < 0) {
DEBUGV("SPIFFS_read rc=%d\r\n", result);
return 0;
}
return result;
}
void flush() override {
CHECKFD();
auto rc = SPIFFS_fflush(_fs->getFs(), _fd);
if (rc < 0) {
DEBUGV("SPIFFS_fflush rc=%d\r\n", rc);
}
}
bool seek(uint32_t pos, SeekMode mode) override {
CHECKFD();
auto rc = SPIFFS_lseek(_fs->getFs(), _fd, pos, (int) mode);
if (rc < 0) {
DEBUGV("SPIFFS_lseek rc=%d\r\n", rc);
return false;
}
return true;
}
size_t position() const override {
CHECKFD();
auto result = SPIFFS_tell(_fs->getFs(), _fd);
if (result < 0) {
DEBUGV("SPIFFS_tell rc=%d\r\n", result);
return 0;
}
return result;
}
size_t size() const override {
CHECKFD();
return _stat.size;
}
void close() override {
CHECKFD();
SPIFFS_close(_fs->getFs(), _fd);
DEBUGV("SPIFFS_close: fd=%d\r\n", _fd);
}
protected:
SPIFFSImpl* _fs;
spiffs_file _fd;
spiffs_stat _stat;
};
class SPIFFSDirImpl : public DirImpl {
public:
SPIFFSDirImpl(SPIFFSImpl* fs, spiffs_DIR& dir)
: _fs(fs)
, _dir(dir)
, _valid(false)
{
}
FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) override {
if (!_valid) {
return FileImplPtr();
}
int mode = getSpiffsMode(openMode, accessMode);
spiffs_file fd = SPIFFS_open_by_dirent(_fs->getFs(), &_dirent, mode, 0);
if (fd < 0) {
DEBUGV("SPIFFSDirImpl::openFile: fd=%d path=`%s` openMode=%d accessMode=%d err=%d\r\n",
fd, _dirent.name, openMode, accessMode, _fs.err_code);
return FileImplPtr();
}
return std::make_shared<SPIFFSFileImpl>(_fs, fd);
}
const char* fileName() override {
if (!_valid)
return nullptr;
return (const char*) _dirent.name;
}
bool next() override {
spiffs_dirent* result = SPIFFS_readdir(&_dir, &_dirent);
_valid = (result != nullptr);
return _valid;
}
protected:
SPIFFSImpl* _fs;
spiffs_DIR _dir;
spiffs_dirent _dirent;
bool _valid;
};
FileImplPtr SPIFFSImpl::open(const char* path, OpenMode openMode, AccessMode accessMode) {
int mode = getSpiffsMode(openMode, accessMode);
char tmpName[SPIFFS_OBJ_NAME_LEN];
strlcpy(tmpName, path, sizeof(tmpName));
int fd = SPIFFS_open(&_fs, tmpName, mode, 0);
if (fd < 0) {
DEBUGV("SPIFFSImpl::open: fd=%d path=`%s` openMode=%d accessMode=%d err=%d\r\n",
fd, path, openMode, accessMode, _fs.err_code);
return FileImplPtr();
}
return std::make_shared<SPIFFSFileImpl>(this, fd);
}
DirImplPtr SPIFFSImpl::openDir(const char* path) {
spiffs_DIR dir;
char tmpName[SPIFFS_OBJ_NAME_LEN];
strlcpy(tmpName, path, sizeof(tmpName));
spiffs_DIR* result = SPIFFS_opendir(&_fs, tmpName, &dir);
if (!result) {
DEBUGV("SPIFFSImpl::openDir: path=`%s` err=%d\r\n", path, _fs.err_code);
return DirImplPtr();
}
return std::make_shared<SPIFFSDirImpl>(this, dir);
}
int getSpiffsMode(OpenMode openMode, AccessMode accessMode) {
int mode = 0;
if (openMode & OM_CREATE) {
mode |= SPIFFS_CREAT;
}
if (openMode & OM_APPEND) {
mode |= SPIFFS_APPEND;
}
if (openMode & OM_TRUNCATE) {
mode |= SPIFFS_TRUNC;
}
if (accessMode & AM_READ) {
mode |= SPIFFS_RDONLY;
}
if (accessMode & AM_WRITE) {
mode |= SPIFFS_WRONLY;
}
return mode;
}
// these symbols should be defined in the linker script for each flash layout
extern "C" uint32_t _SPIFFS_start;
extern "C" uint32_t _SPIFFS_end;
extern "C" uint32_t _SPIFFS_page;
extern "C" uint32_t _SPIFFS_block;
static SPIFFSImpl s_defaultFs(
(uint32_t) &_SPIFFS_start - 0x40200000,
(uint32_t) (&_SPIFFS_end - &_SPIFFS_start),
(uint32_t) &_SPIFFS_page,
(uint32_t) &_SPIFFS_block,
5);
FS SPIFFS = FS(FSImplPtr(&s_defaultFs));

View File

@ -0,0 +1,192 @@
/*
spiffs_hal.cpp - SPI read/write/erase functions for SPIFFS.
Copyright (c) 2015 Ivan Grokhotkov. 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 <Arduino.h>
#include <stdlib.h>
#include <algorithm>
#include "spiffs/spiffs.h"
#include "debug.h"
#include "interrupts.h"
extern "C" {
#include "c_types.h"
#include "spi_flash.h"
}
static int spi_flash_read_locked(uint32_t addr, uint32_t* dst, uint32_t size) {
InterruptLock lock;
return spi_flash_read(addr, dst, size);
}
static int spi_flash_write_locked(uint32_t addr, const uint32_t* src, uint32_t size) {
InterruptLock lock;
return spi_flash_write(addr, (uint32_t*) src, size);
}
static int spi_flash_erase_sector_locked(uint32_t sector) {
optimistic_yield(10000);
InterruptLock lock;
return spi_flash_erase_sector(sector);
}
/*
spi_flash_read function requires flash address to be aligned on word boundary.
We take care of this by reading first and last words separately and memcpy
relevant bytes into result buffer.
alignment: 012301230123012301230123
bytes requested: -------***********------
read directly: --------xxxxxxxx--------
read pre: ----aaaa----------------
read post: ----------------bbbb----
alignedBegin: ^
alignedEnd: ^
*/
int32_t spiffs_hal_read(uint32_t addr, uint32_t size, uint8_t *dst) {
uint32_t result = SPIFFS_OK;
uint32_t alignedBegin = (addr + 3) & (~3);
uint32_t alignedEnd = (addr + size) & (~3);
if (addr < alignedBegin) {
uint32_t nb = alignedBegin - addr;
uint32_t tmp;
if (spi_flash_read_locked(alignedBegin - 4, &tmp, 4) != SPI_FLASH_RESULT_OK) {
DEBUGV("_spif_read(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return SPIFFS_ERR_INTERNAL;
}
memcpy(dst, &tmp + 4 - nb, nb);
}
if (alignedEnd != alignedBegin) {
if (spi_flash_read_locked(alignedBegin, (uint32_t*) (dst + alignedBegin - addr),
alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) {
DEBUGV("_spif_read(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return SPIFFS_ERR_INTERNAL;
}
}
if (addr + size > alignedEnd) {
uint32_t nb = addr + size - alignedEnd;
uint32_t tmp;
if (spi_flash_read_locked(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) {
DEBUGV("_spif_read(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return SPIFFS_ERR_INTERNAL;
}
memcpy(dst + size - nb, &tmp, nb);
}
return result;
}
/*
Like spi_flash_read, spi_flash_write has a requirement for flash address to be
aligned. However it also requires RAM address to be aligned as it reads data
in 32-bit words. Flash address (mis-)alignment is handled much the same way
as for reads, but for RAM alignment we have to copy data into a temporary
buffer. The size of this buffer is a tradeoff between number of writes required
and amount of stack required. This is chosen to be 512 bytes here, but might
be adjusted in the future if there are good reasons to do so.
*/
static const int UNALIGNED_WRITE_BUFFER_SIZE = 512;
int32_t spiffs_hal_write(uint32_t addr, uint32_t size, uint8_t *src) {
uint32_t alignedBegin = (addr + 3) & (~3);
uint32_t alignedEnd = (addr + size) & (~3);
if (addr < alignedBegin) {
uint32_t nb = alignedBegin - addr;
uint32_t tmp = 0xffffffff;
memcpy(((uint8_t* )&tmp) + 4 - nb, src, nb);
if (spi_flash_write_locked(alignedBegin - 4, &tmp, 4) != SPI_FLASH_RESULT_OK) {
DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return SPIFFS_ERR_INTERNAL;
}
}
if (alignedEnd != alignedBegin) {
uint32_t* srcLeftover = (uint32_t*) (src + alignedBegin - addr);
uint32_t srcAlign = ((uint32_t) srcLeftover) & 3;
if (!srcAlign) {
if (spi_flash_write_locked(alignedBegin, (uint32_t*) srcLeftover,
alignedEnd - alignedBegin) != SPI_FLASH_RESULT_OK) {
DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return SPIFFS_ERR_INTERNAL;
}
}
else {
uint8_t buf[UNALIGNED_WRITE_BUFFER_SIZE];
for (uint32_t sizeLeft = alignedEnd - alignedBegin; sizeLeft; ) {
size_t willCopy = std::min(sizeLeft, sizeof(buf));
memcpy(buf, srcLeftover, willCopy);
if (spi_flash_write_locked(alignedBegin, (uint32_t*) buf,
willCopy) != SPI_FLASH_RESULT_OK) {
DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return SPIFFS_ERR_INTERNAL;
}
sizeLeft -= willCopy;
srcLeftover += willCopy;
alignedBegin += willCopy;
}
}
}
if (addr + size > alignedEnd) {
uint32_t nb = addr + size - alignedEnd;
uint32_t tmp = 0xffffffff;
memcpy(&tmp, src + size - nb, nb);
if (spi_flash_write_locked(alignedEnd, &tmp, 4) != SPI_FLASH_RESULT_OK) {
DEBUGV("_spif_write(%d) addr=%x size=%x ab=%x ae=%x\r\n",
__LINE__, addr, size, alignedBegin, alignedEnd);
return SPIFFS_ERR_INTERNAL;
}
}
return SPIFFS_OK;
}
int32_t spiffs_hal_erase(uint32_t addr, uint32_t size) {
if ((size & (SPI_FLASH_SEC_SIZE - 1)) != 0 ||
(addr & (SPI_FLASH_SEC_SIZE - 1)) != 0) {
DEBUGV("_spif_erase called with addr=%x, size=%d\r\n", addr, size);
abort();
}
const uint32_t sector = addr / SPI_FLASH_SEC_SIZE;
const uint32_t sectorCount = size / SPI_FLASH_SEC_SIZE;
for (uint32_t i = 0; i < sectorCount; ++i) {
if (spi_flash_erase_sector_locked(sector + i) != 0) {
DEBUGV("_spif_erase addr=%x size=%d i=%d\r\n", addr, size, i);
return SPIFFS_ERR_INTERNAL;
}
}
return SPIFFS_OK;
}

View File

@ -0,0 +1,76 @@
#include <ESP8266WiFi.h>
#include "FS.h"
void fail(const char* msg) {
Serial.println(msg);
while(true) {
yield();
}
}
void setup() {
Serial.begin(115200);
Serial.setDebugOutput(true);
WiFi.mode(WIFI_OFF);
Serial.println("\n\nFS test\n");
if (!mount(SPIFFS, "/")) {
fail("mount failed");
}
String text = "write test";
{
File out = SPIFFS.open("/tmp.txt", "w");
if (!out) {
fail("failed to open tmp.txt for writing");
}
out.print(text);
}
{
File in = SPIFFS.open("/tmp.txt", "r");
if (!in) {
fail("failed to open tmp.txt for reading");
}
Serial.printf("size=%d\r\n", in.size());
if (in.size() != text.length()) {
fail("invalid size of tmp.txt");
}
Serial.print("Reading data: ");
in.setTimeout(0);
String result = in.readString();
Serial.println(result);
if (result != text) {
fail("invalid data in tmp.txt");
}
}
{
for (int i = 0; i < 10; ++i) {
String name = "seq_";
name += i;
name += ".txt";
File out = SPIFFS.open(name, "w");
if (!out) {
fail("can't open seq_ file");
}
out.println(i);
}
}
{
Dir root = SPIFFS.openDir("/");
while (root.next()) {
String fileName = root.fileName();
File f = root.openFile("r");
Serial.printf("%s: %d\r\n", fileName.c_str(), f.size());
}
}
Serial.println("success");
}
void loop() {
}