Compare commits
2 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec6c8dcda1 | ||
|
|
24bfb00571 |
12 changed files with 2028 additions and 0 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -7,3 +7,6 @@
|
|||
[submodule "system/pyserial"]
|
||||
path = tools/pyserial
|
||||
url = https://github.com/pyserial/pyserial.git
|
||||
[submodule "libraries/LittleFS/lib/littlefs"]
|
||||
path = libraries/LittleFS/lib/littlefs
|
||||
url = https://github.com/littlefs-project/littlefs.git
|
||||
|
|
|
|||
563
cores/rp2040/FS.cpp
Normal file
563
cores/rp2040/FS.cpp
Normal file
|
|
@ -0,0 +1,563 @@
|
|||
/*
|
||||
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"
|
||||
|
||||
using namespace fs;
|
||||
|
||||
static bool sflags(const char* mode, OpenMode& om, AccessMode& am);
|
||||
|
||||
size_t File::write(uint8_t c) {
|
||||
if (!_p)
|
||||
return 0;
|
||||
|
||||
return _p->write(&c, 1);
|
||||
}
|
||||
|
||||
size_t File::write(const uint8_t *buf, size_t size) {
|
||||
if (!_p)
|
||||
return 0;
|
||||
|
||||
return _p->write(buf, size);
|
||||
}
|
||||
|
||||
int File::available() {
|
||||
if (!_p)
|
||||
return false;
|
||||
|
||||
return _p->size() - _p->position();
|
||||
}
|
||||
|
||||
int File::availableForWrite() {
|
||||
if (!_p)
|
||||
return false;
|
||||
|
||||
return _p->availableForWrite();
|
||||
}
|
||||
|
||||
|
||||
int File::read() {
|
||||
if (!_p)
|
||||
return -1;
|
||||
|
||||
uint8_t result;
|
||||
if (_p->read(&result, 1) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int File::read(uint8_t* buf, size_t size) {
|
||||
if (!_p)
|
||||
return 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool File::truncate(uint32_t size) {
|
||||
if (!_p)
|
||||
return false;
|
||||
|
||||
return _p->truncate(size);
|
||||
}
|
||||
|
||||
const char* File::name() const {
|
||||
if (!_p)
|
||||
return nullptr;
|
||||
|
||||
return _p->name();
|
||||
}
|
||||
|
||||
const char* File::fullName() const {
|
||||
if (!_p)
|
||||
return nullptr;
|
||||
|
||||
return _p->fullName();
|
||||
}
|
||||
|
||||
bool File::isFile() const {
|
||||
if (!_p)
|
||||
return false;
|
||||
|
||||
return _p->isFile();
|
||||
}
|
||||
|
||||
bool File::isDirectory() const {
|
||||
if (!_p)
|
||||
return false;
|
||||
|
||||
return _p->isDirectory();
|
||||
}
|
||||
|
||||
void File::rewindDirectory() {
|
||||
if (!_fakeDir) {
|
||||
_fakeDir = std::make_shared<Dir>(_baseFS->openDir(fullName()));
|
||||
} else {
|
||||
_fakeDir->rewind();
|
||||
}
|
||||
}
|
||||
|
||||
File File::openNextFile() {
|
||||
if (!_fakeDir) {
|
||||
_fakeDir = std::make_shared<Dir>(_baseFS->openDir(fullName()));
|
||||
}
|
||||
_fakeDir->next();
|
||||
return _fakeDir->openFile("r");
|
||||
}
|
||||
|
||||
String File::readString()
|
||||
{
|
||||
String ret;
|
||||
ret.reserve(size() - position());
|
||||
char temp[256+1];
|
||||
int countRead = readBytes(temp, sizeof(temp)-1);
|
||||
while (countRead > 0)
|
||||
{
|
||||
temp[countRead] = 0;
|
||||
ret += temp;
|
||||
countRead = readBytes(temp, sizeof(temp)-1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
time_t File::getLastWrite() {
|
||||
if (!_p)
|
||||
return 0;
|
||||
|
||||
return _p->getLastWrite();
|
||||
}
|
||||
|
||||
time_t File::getCreationTime() {
|
||||
if (!_p)
|
||||
return 0;
|
||||
|
||||
return _p->getCreationTime();
|
||||
}
|
||||
|
||||
void File::setTimeCallback(time_t (*cb)(void)) {
|
||||
if (!_p)
|
||||
return;
|
||||
_p->setTimeCallback(cb);
|
||||
_timeCallback = cb;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
File f(_impl->openFile(om, am), _baseFS);
|
||||
f.setTimeCallback(_timeCallback);
|
||||
return f;
|
||||
}
|
||||
|
||||
String Dir::fileName() {
|
||||
if (!_impl) {
|
||||
return String();
|
||||
}
|
||||
|
||||
return _impl->fileName();
|
||||
}
|
||||
|
||||
time_t Dir::fileTime() {
|
||||
if (!_impl)
|
||||
return 0;
|
||||
return _impl->fileTime();
|
||||
}
|
||||
|
||||
time_t Dir::fileCreationTime() {
|
||||
if (!_impl)
|
||||
return 0;
|
||||
return _impl->fileCreationTime();
|
||||
}
|
||||
|
||||
size_t Dir::fileSize() {
|
||||
if (!_impl) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _impl->fileSize();
|
||||
}
|
||||
|
||||
bool Dir::isFile() const {
|
||||
if (!_impl)
|
||||
return false;
|
||||
|
||||
return _impl->isFile();
|
||||
}
|
||||
|
||||
bool Dir::isDirectory() const {
|
||||
if (!_impl)
|
||||
return false;
|
||||
|
||||
return _impl->isDirectory();
|
||||
}
|
||||
|
||||
bool Dir::next() {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _impl->next();
|
||||
}
|
||||
|
||||
bool Dir::rewind() {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _impl->rewind();
|
||||
}
|
||||
|
||||
void Dir::setTimeCallback(time_t (*cb)(void)) {
|
||||
if (!_impl)
|
||||
return;
|
||||
_impl->setTimeCallback(cb);
|
||||
_timeCallback = cb;
|
||||
}
|
||||
|
||||
|
||||
bool FS::setConfig(const FSConfig &cfg) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _impl->setConfig(cfg);
|
||||
}
|
||||
|
||||
bool FS::begin() {
|
||||
if (!_impl) {
|
||||
DEBUGV("#error: FS: no implementation");
|
||||
return false;
|
||||
}
|
||||
_impl->setTimeCallback(_timeCallback);
|
||||
bool ret = _impl->begin();
|
||||
DEBUGV("%s\n", ret? "": "#error: FS could not start");
|
||||
return ret;
|
||||
}
|
||||
|
||||
void FS::end() {
|
||||
if (_impl) {
|
||||
_impl->end();
|
||||
}
|
||||
}
|
||||
|
||||
bool FS::gc() {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->gc();
|
||||
}
|
||||
|
||||
bool FS::check() {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->check();
|
||||
}
|
||||
|
||||
bool FS::format() {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->format();
|
||||
}
|
||||
|
||||
bool FS::info(FSInfo& info){
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->info(info);
|
||||
}
|
||||
|
||||
bool FS::info64(FSInfo64& info){
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->info64(info);
|
||||
}
|
||||
|
||||
File FS::open(const String& path, const char* mode) {
|
||||
return open(path.c_str(), mode);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
File f(_impl->open(path, om, am), this);
|
||||
f.setTimeCallback(_timeCallback);
|
||||
return f;
|
||||
}
|
||||
|
||||
bool FS::exists(const char* path) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->exists(path);
|
||||
}
|
||||
|
||||
bool FS::exists(const String& path) {
|
||||
return exists(path.c_str());
|
||||
}
|
||||
|
||||
Dir FS::openDir(const char* path) {
|
||||
if (!_impl) {
|
||||
return Dir();
|
||||
}
|
||||
DirImplPtr p = _impl->openDir(path);
|
||||
Dir d(p, this);
|
||||
d.setTimeCallback(_timeCallback);
|
||||
return d;
|
||||
}
|
||||
|
||||
Dir FS::openDir(const String& path) {
|
||||
return openDir(path.c_str());
|
||||
}
|
||||
|
||||
bool FS::remove(const char* path) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->remove(path);
|
||||
}
|
||||
|
||||
bool FS::remove(const String& path) {
|
||||
return remove(path.c_str());
|
||||
}
|
||||
|
||||
bool FS::rmdir(const char* path) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->rmdir(path);
|
||||
}
|
||||
|
||||
bool FS::rmdir(const String& path) {
|
||||
return rmdir(path.c_str());
|
||||
}
|
||||
|
||||
bool FS::mkdir(const char* path) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->mkdir(path);
|
||||
}
|
||||
|
||||
bool FS::mkdir(const String& path) {
|
||||
return mkdir(path.c_str());
|
||||
}
|
||||
|
||||
bool FS::rename(const char* pathFrom, const char* pathTo) {
|
||||
if (!_impl) {
|
||||
return false;
|
||||
}
|
||||
return _impl->rename(pathFrom, pathTo);
|
||||
}
|
||||
|
||||
bool FS::rename(const String& pathFrom, const String& pathTo) {
|
||||
return rename(pathFrom.c_str(), pathTo.c_str());
|
||||
}
|
||||
|
||||
time_t FS::getCreationTime() {
|
||||
if (!_impl) {
|
||||
return 0;
|
||||
}
|
||||
return _impl->getCreationTime();
|
||||
}
|
||||
|
||||
void FS::setTimeCallback(time_t (*cb)(void)) {
|
||||
if (!_impl)
|
||||
return;
|
||||
_impl->setTimeCallback(cb);
|
||||
_timeCallback = cb;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
#if defined(FS_FREESTANDING_FUNCTIONS)
|
||||
|
||||
/*
|
||||
TODO: move these functions to public API:
|
||||
*/
|
||||
File open(const char* path, const char* mode);
|
||||
File open(String& path, const char* mode);
|
||||
|
||||
Dir openDir(const char* path);
|
||||
Dir openDir(String& path);
|
||||
|
||||
template<>
|
||||
bool mount<FS>(FS& fs, const char* mountPoint);
|
||||
/*
|
||||
*/
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
!make sure mountPoint has trailing '/' here
|
||||
|
||||
MountEntry* entry = new MountEntry;
|
||||
entry->fs = p;
|
||||
entry->path = mountPoint;
|
||||
entry->next = s_mounted;
|
||||
s_mounted = entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
iterate over MountEntries and look for the ones which match the path
|
||||
*/
|
||||
File open(const char* path, const char* mode) {
|
||||
OpenMode om;
|
||||
AccessMode am;
|
||||
if (!sflags(mode, om, am)) {
|
||||
DEBUGV("open: invalid mode `%s`\r\n", mode);
|
||||
return File();
|
||||
}
|
||||
|
||||
for (MountEntry* entry = s_mounted; entry; entry = entry->next) {
|
||||
size_t offset = entry->path.length();
|
||||
if (strstr(path, entry->path.c_str())) {
|
||||
File result = entry->fs->open(path + offset);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return File();
|
||||
}
|
||||
|
||||
File open(const String& path, const char* mode) {
|
||||
return FS::open(path.c_str(), mode);
|
||||
}
|
||||
|
||||
Dir openDir(const String& path) {
|
||||
return openDir(path.c_str());
|
||||
}
|
||||
#endif
|
||||
278
cores/rp2040/FS.h
Normal file
278
cores/rp2040/FS.h
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
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 <memory>
|
||||
#include <Arduino.h>
|
||||
#include <../include/time.h> // See issue #6714
|
||||
|
||||
class SDClass;
|
||||
|
||||
namespace fs {
|
||||
|
||||
class File;
|
||||
class Dir;
|
||||
class FS;
|
||||
|
||||
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(), FS *baseFS = nullptr) : _p(p), _fakeDir(nullptr), _baseFS(baseFS) { }
|
||||
|
||||
// Print methods:
|
||||
size_t write(uint8_t) override;
|
||||
size_t write(const uint8_t *buf, size_t size) override;
|
||||
int availableForWrite() override;
|
||||
|
||||
// Stream methods:
|
||||
int available() override;
|
||||
int read() override;
|
||||
int peek() override;
|
||||
void flush() override;
|
||||
size_t readBytes(char *buffer, size_t length) {
|
||||
return read((uint8_t*)buffer, length);
|
||||
}
|
||||
int read(uint8_t* buf, size_t size);
|
||||
bool seek(uint32_t pos, SeekMode mode);
|
||||
bool seek(uint32_t pos) {
|
||||
return seek(pos, SeekSet);
|
||||
}
|
||||
size_t position() const;
|
||||
size_t size() const;
|
||||
virtual ssize_t streamRemaining() { return (ssize_t)size() - (ssize_t)position(); }
|
||||
void close();
|
||||
operator bool() const;
|
||||
const char* name() const;
|
||||
const char* fullName() const; // Includes path
|
||||
bool truncate(uint32_t size);
|
||||
|
||||
bool isFile() const;
|
||||
bool isDirectory() const;
|
||||
|
||||
// Arduino "class SD" methods for compatibility
|
||||
//TODO use stream::send / check read(buf,size) result
|
||||
template<typename T> size_t write(T &src){
|
||||
uint8_t obuf[256];
|
||||
size_t doneLen = 0;
|
||||
size_t sentLen;
|
||||
int i;
|
||||
|
||||
while (src.available() > sizeof(obuf)){
|
||||
src.read(obuf, sizeof(obuf));
|
||||
sentLen = write(obuf, sizeof(obuf));
|
||||
doneLen = doneLen + sentLen;
|
||||
if(sentLen != sizeof(obuf)){
|
||||
return doneLen;
|
||||
}
|
||||
}
|
||||
|
||||
size_t leftLen = src.available();
|
||||
src.read(obuf, leftLen);
|
||||
sentLen = write(obuf, leftLen);
|
||||
doneLen = doneLen + sentLen;
|
||||
return doneLen;
|
||||
}
|
||||
using Print::write;
|
||||
|
||||
void rewindDirectory();
|
||||
File openNextFile();
|
||||
|
||||
String readString();
|
||||
|
||||
time_t getLastWrite();
|
||||
time_t getCreationTime();
|
||||
void setTimeCallback(time_t (*cb)(void));
|
||||
|
||||
protected:
|
||||
FileImplPtr _p;
|
||||
time_t (*_timeCallback)(void) = nullptr;
|
||||
|
||||
// Arduino SD class emulation
|
||||
std::shared_ptr<Dir> _fakeDir;
|
||||
FS *_baseFS;
|
||||
};
|
||||
|
||||
class Dir {
|
||||
public:
|
||||
Dir(DirImplPtr impl = DirImplPtr(), FS *baseFS = nullptr): _impl(impl), _baseFS(baseFS) { }
|
||||
|
||||
File openFile(const char* mode);
|
||||
|
||||
String fileName();
|
||||
size_t fileSize();
|
||||
time_t fileTime();
|
||||
time_t fileCreationTime();
|
||||
bool isFile() const;
|
||||
bool isDirectory() const;
|
||||
|
||||
bool next();
|
||||
bool rewind();
|
||||
|
||||
void setTimeCallback(time_t (*cb)(void));
|
||||
|
||||
protected:
|
||||
DirImplPtr _impl;
|
||||
FS *_baseFS;
|
||||
time_t (*_timeCallback)(void) = nullptr;
|
||||
};
|
||||
|
||||
// Backwards compatible, <4GB filesystem usage
|
||||
struct FSInfo {
|
||||
size_t totalBytes;
|
||||
size_t usedBytes;
|
||||
size_t blockSize;
|
||||
size_t pageSize;
|
||||
size_t maxOpenFiles;
|
||||
size_t maxPathLength;
|
||||
};
|
||||
|
||||
// Support > 4GB filesystems (SD, etc.)
|
||||
struct FSInfo64 {
|
||||
uint64_t totalBytes;
|
||||
uint64_t usedBytes;
|
||||
size_t blockSize;
|
||||
size_t pageSize;
|
||||
size_t maxOpenFiles;
|
||||
size_t maxPathLength;
|
||||
};
|
||||
|
||||
|
||||
class FSConfig
|
||||
{
|
||||
public:
|
||||
static constexpr uint32_t FSId = 0x00000000;
|
||||
|
||||
FSConfig(uint32_t type = FSId, bool autoFormat = true) : _type(type), _autoFormat(autoFormat) { }
|
||||
|
||||
FSConfig setAutoFormat(bool val = true) {
|
||||
_autoFormat = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint32_t _type;
|
||||
bool _autoFormat;
|
||||
};
|
||||
|
||||
class SPIFFSConfig : public FSConfig
|
||||
{
|
||||
public:
|
||||
static constexpr uint32_t FSId = 0x53504946;
|
||||
SPIFFSConfig(bool autoFormat = true) : FSConfig(FSId, autoFormat) { }
|
||||
|
||||
// Inherit _type and _autoFormat
|
||||
// nothing yet, enableTime TBD when SPIFFS has metadate
|
||||
};
|
||||
|
||||
class FS
|
||||
{
|
||||
public:
|
||||
FS(FSImplPtr impl) : _impl(impl) { _timeCallback = _defaultTimeCB; }
|
||||
|
||||
bool setConfig(const FSConfig &cfg);
|
||||
|
||||
bool begin();
|
||||
void end();
|
||||
|
||||
bool format();
|
||||
bool info(FSInfo& info);
|
||||
bool info64(FSInfo64& info);
|
||||
|
||||
File open(const char* path, const char* mode);
|
||||
File open(const String& path, const char* mode);
|
||||
|
||||
bool exists(const char* path);
|
||||
bool exists(const String& path);
|
||||
|
||||
Dir openDir(const char* path);
|
||||
Dir openDir(const String& path);
|
||||
|
||||
bool remove(const char* path);
|
||||
bool remove(const String& path);
|
||||
|
||||
bool rename(const char* pathFrom, const char* pathTo);
|
||||
bool rename(const String& pathFrom, const String& pathTo);
|
||||
|
||||
bool mkdir(const char* path);
|
||||
bool mkdir(const String& path);
|
||||
|
||||
bool rmdir(const char* path);
|
||||
bool rmdir(const String& path);
|
||||
|
||||
// Low-level FS routines, not needed by most applications
|
||||
bool gc();
|
||||
bool check();
|
||||
|
||||
time_t getCreationTime();
|
||||
|
||||
void setTimeCallback(time_t (*cb)(void));
|
||||
|
||||
friend class ::SDClass; // More of a frenemy, but SD needs internal implementation to get private FAT bits
|
||||
protected:
|
||||
FSImplPtr _impl;
|
||||
FSImplPtr getImpl() { return _impl; }
|
||||
time_t (*_timeCallback)(void) = nullptr;
|
||||
static time_t _defaultTimeCB(void) { return time(NULL); }
|
||||
};
|
||||
|
||||
} // namespace fs
|
||||
|
||||
extern "C"
|
||||
{
|
||||
void close_all_fs(void);
|
||||
void littlefs_request_end(void);
|
||||
void spiffs_request_end(void);
|
||||
}
|
||||
|
||||
#ifndef FS_NO_GLOBALS
|
||||
using fs::FS;
|
||||
using fs::File;
|
||||
using fs::Dir;
|
||||
using fs::SeekMode;
|
||||
using fs::SeekSet;
|
||||
using fs::SeekCur;
|
||||
using fs::SeekEnd;
|
||||
using fs::FSInfo;
|
||||
using fs::FSConfig;
|
||||
using fs::SPIFFSConfig;
|
||||
#endif //FS_NO_GLOBALS
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SPIFFS)
|
||||
extern fs::FS SPIFFS __attribute__((deprecated("SPIFFS has been deprecated. Please consider moving to LittleFS or other filesystems.")));
|
||||
#endif
|
||||
|
||||
#endif //FS_H
|
||||
131
cores/rp2040/FSImpl.h
Normal file
131
cores/rp2040/FSImpl.h
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
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>
|
||||
#include <FS.h>
|
||||
|
||||
namespace fs {
|
||||
|
||||
class FileImpl {
|
||||
public:
|
||||
virtual ~FileImpl() { }
|
||||
virtual size_t write(const uint8_t *buf, size_t size) = 0;
|
||||
virtual int 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 int availableForWrite() { return 0; }
|
||||
virtual bool truncate(uint32_t size) = 0;
|
||||
virtual void close() = 0;
|
||||
virtual const char* name() const = 0;
|
||||
virtual const char* fullName() const = 0;
|
||||
virtual bool isFile() const = 0;
|
||||
virtual bool isDirectory() const = 0;
|
||||
|
||||
// Filesystems *may* support a timestamp per-file, so allow the user to override with
|
||||
// their own callback for *this specific* file (as opposed to the FSImpl call of the
|
||||
// same name. The default implementation simply returns time(null)
|
||||
virtual void setTimeCallback(time_t (*cb)(void)) { _timeCallback = cb; }
|
||||
|
||||
// Return the last written time for a file. Undefined when called on a writable file
|
||||
// as the FS is allowed to return either the time of the last write() operation or the
|
||||
// time present in the filesystem metadata (often the last time the file was closed)
|
||||
virtual time_t getLastWrite() { return 0; } // Default is to not support timestamps
|
||||
// Same for creation time.
|
||||
virtual time_t getCreationTime() { return 0; } // Default is to not support timestamps
|
||||
|
||||
protected:
|
||||
time_t (*_timeCallback)(void) = nullptr;
|
||||
};
|
||||
|
||||
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 ~DirImpl() { }
|
||||
virtual FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) = 0;
|
||||
virtual const char* fileName() = 0;
|
||||
virtual size_t fileSize() = 0;
|
||||
// Return the last written time for a file. Undefined when called on a writable file
|
||||
// as the FS is allowed to return either the time of the last write() operation or the
|
||||
// time present in the filesystem metadata (often the last time the file was closed)
|
||||
virtual time_t fileTime() { return 0; } // By default, FS doesn't report file times
|
||||
virtual time_t fileCreationTime() { return 0; } // By default, FS doesn't report file times
|
||||
virtual bool isFile() const = 0;
|
||||
virtual bool isDirectory() const = 0;
|
||||
virtual bool next() = 0;
|
||||
virtual bool rewind() = 0;
|
||||
|
||||
// Filesystems *may* support a timestamp per-file, so allow the user to override with
|
||||
// their own callback for *this specific* file (as opposed to the FSImpl call of the
|
||||
// same name. The default implementation simply returns time(null)
|
||||
virtual void setTimeCallback(time_t (*cb)(void)) { _timeCallback = cb; }
|
||||
|
||||
protected:
|
||||
time_t (*_timeCallback)(void) = nullptr;
|
||||
};
|
||||
|
||||
class FSImpl {
|
||||
public:
|
||||
virtual ~FSImpl () { }
|
||||
virtual bool setConfig(const FSConfig &cfg) = 0;
|
||||
virtual bool begin() = 0;
|
||||
virtual void end() = 0;
|
||||
virtual bool format() = 0;
|
||||
virtual bool info(FSInfo& info) = 0;
|
||||
virtual bool info64(FSInfo64& info) = 0;
|
||||
virtual FileImplPtr open(const char* path, OpenMode openMode, AccessMode accessMode) = 0;
|
||||
virtual bool exists(const char* path) = 0;
|
||||
virtual DirImplPtr openDir(const char* path) = 0;
|
||||
virtual bool rename(const char* pathFrom, const char* pathTo) = 0;
|
||||
virtual bool remove(const char* path) = 0;
|
||||
virtual bool mkdir(const char* path) = 0;
|
||||
virtual bool rmdir(const char* path) = 0;
|
||||
virtual bool gc() { return true; } // May not be implemented in all file systems.
|
||||
virtual bool check() { return true; } // May not be implemented in all file systems.
|
||||
virtual time_t getCreationTime() { return 0; } // May not be implemented in all file systems.
|
||||
|
||||
// Filesystems *may* support a timestamp per-file, so allow the user to override with
|
||||
// their own callback for all files on this FS. The default implementation simply
|
||||
// returns the present time as reported by time(null)
|
||||
virtual void setTimeCallback(time_t (*cb)(void)) { _timeCallback = cb; }
|
||||
|
||||
protected:
|
||||
time_t (*_timeCallback)(void) = nullptr;
|
||||
};
|
||||
|
||||
} // namespace fs
|
||||
|
||||
#endif //FSIMPL_H
|
||||
|
|
@ -28,6 +28,12 @@ extern "C" int main() {
|
|||
#if F_CPU != 125000000
|
||||
set_sys_clock_khz(F_CPU / 1000, true);
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_USB_SERIAL
|
||||
// Enable serial port for reset/upload always
|
||||
Serial.begin();
|
||||
#endif
|
||||
|
||||
#if defined DEBUG_RP2040_PORT
|
||||
DEBUG_RP2040_PORT.begin();
|
||||
#endif
|
||||
|
|
|
|||
131
libraries/LittleFS/examples/SpeedTest/SpeedTest.ino
Normal file
131
libraries/LittleFS/examples/SpeedTest/SpeedTest.ino
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
// Simple speed test for filesystem objects
|
||||
// Released to the public domain by Earle F. Philhower, III
|
||||
|
||||
#include <FS.h>
|
||||
#include <LittleFS.h>
|
||||
|
||||
// Choose the filesystem to test
|
||||
// WARNING: The filesystem will be formatted at the start of the test!
|
||||
|
||||
#define TESTFS LittleFS
|
||||
//#define TESTFS SDFS
|
||||
|
||||
// How large of a file to test
|
||||
#define TESTSIZEKB 256
|
||||
|
||||
void DoTest(FS *fs) {
|
||||
if (!fs->format()) {
|
||||
Serial.printf("Unable to format(), aborting\n");
|
||||
return;
|
||||
}
|
||||
if (!fs->begin()) {
|
||||
Serial.printf("Unable to begin(), aborting\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t data[256];
|
||||
for (int i = 0; i < 256; i++) {
|
||||
data[i] = (uint8_t) i;
|
||||
}
|
||||
|
||||
Serial.printf("Creating %dKB file, may take a while...\n", TESTSIZEKB);
|
||||
long start = millis();
|
||||
File f = fs->open("/testwrite.bin", "w");
|
||||
if (!f) {
|
||||
Serial.printf("Unable to open file for writing, aborting\n");
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < TESTSIZEKB; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
f.write(data, 256);
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
long stop = millis();
|
||||
Serial.printf("==> Time to write %dKB in 256b chunks = %ld milliseconds\n", TESTSIZEKB, stop - start);
|
||||
|
||||
f = fs->open("/testwrite.bin", "r");
|
||||
Serial.printf("==> Created file size = %d\n", f.size());
|
||||
f.close();
|
||||
|
||||
Serial.printf("Reading %dKB file sequentially in 256b chunks\n", TESTSIZEKB);
|
||||
start = millis();
|
||||
f = fs->open("/testwrite.bin", "r");
|
||||
for (int i = 0; i < TESTSIZEKB; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
f.read(data, 256);
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
stop = millis();
|
||||
Serial.printf("==> Time to read %dKB sequentially in 256b chunks = %ld milliseconds = %ld bytes/s\n", TESTSIZEKB, stop - start, TESTSIZEKB * 1024 / (stop - start) * 1000);
|
||||
|
||||
Serial.printf("Reading %dKB file MISALIGNED in flash and RAM sequentially in 256b chunks\n", TESTSIZEKB);
|
||||
start = millis();
|
||||
f = fs->open("/testwrite.bin", "r");
|
||||
f.read();
|
||||
for (int i = 0; i < TESTSIZEKB; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
f.read(data + 1, 256);
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
stop = millis();
|
||||
Serial.printf("==> Time to read %dKB sequentially MISALIGNED in flash and RAM in 256b chunks = %ld milliseconds = %ld bytes/s\n", TESTSIZEKB, stop - start, TESTSIZEKB * 1024 / (stop - start) * 1000);
|
||||
|
||||
|
||||
Serial.printf("Reading %dKB file in reverse by 256b chunks\n", TESTSIZEKB);
|
||||
start = millis();
|
||||
f = fs->open("/testwrite.bin", "r");
|
||||
for (int i = 0; i < TESTSIZEKB; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
if (!f.seek(256 + 256 * j * i, SeekEnd)) {
|
||||
Serial.printf("Unable to seek to %d, aborting\n", -256 - 256 * j * i);
|
||||
return;
|
||||
}
|
||||
if (256 != f.read(data, 256)) {
|
||||
Serial.printf("Unable to read 256 bytes, aborting\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
stop = millis();
|
||||
Serial.printf("==> Time to read %dKB in reverse in 256b chunks = %ld milliseconds = %ld bytes/s\n", TESTSIZEKB, stop - start, TESTSIZEKB * 1024 / (stop - start) * 1000);
|
||||
|
||||
|
||||
Serial.printf("Writing 64K file in 1-byte chunks\n");
|
||||
start = millis();
|
||||
f = fs->open("/test1b.bin", "w");
|
||||
for (int i = 0; i < 65536; i++) {
|
||||
f.write((uint8_t*)&i, 1);
|
||||
}
|
||||
f.close();
|
||||
stop = millis();
|
||||
Serial.printf("==> Time to write 64KB in 1b chunks = %ld milliseconds = %ld bytes/s\n", stop - start, 65536 / (stop - start) * 1000);
|
||||
|
||||
Serial.printf("Reading 64K file in 1-byte chunks\n");
|
||||
start = millis();
|
||||
f = fs->open("/test1b.bin", "r");
|
||||
for (int i = 0; i < 65536; i++) {
|
||||
char c;
|
||||
f.read((uint8_t*)&c, 1);
|
||||
}
|
||||
f.close();
|
||||
stop = millis();
|
||||
Serial.printf("==> Time to read 64KB in 1b chunks = %ld milliseconds = %ld bytes/s\n", stop - start, 65536 / (stop - start) * 1000);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(5000);
|
||||
Serial.printf("Beginning test\n");
|
||||
Serial.flush();
|
||||
DoTest(&TESTFS);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(10000);
|
||||
}
|
||||
1
libraries/LittleFS/lib/littlefs
Submodule
1
libraries/LittleFS/lib/littlefs
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 1863dc7883d82bd6ca79faa164b65341064d1c16
|
||||
10
libraries/LittleFS/library.properties
Normal file
10
libraries/LittleFS/library.properties
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
name=LittleFS
|
||||
version=0.1.0
|
||||
author=Earle F. Philhower, III
|
||||
maintainer=Earle F. Philhower, III <earlephilhower@yahoo.com>
|
||||
sentence=Port of LittleFS to ESP8266 Arduino
|
||||
paragraph=Replacement for SPIFFS to manage a filesystem in the onboard flash, supporting power fail safety and higher performance than SPIFFS at the cost of a lower maximum number of files.
|
||||
category=Data Storage
|
||||
url=https://github.com/esp8266/Arduino/libraries/LittleFS
|
||||
architectures=rp2040
|
||||
dot_a_linkage=true
|
||||
203
libraries/LittleFS/src/LittleFS.cpp
Normal file
203
libraries/LittleFS/src/LittleFS.cpp
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
LittleFS.cpp - Wrapper for LittleFS for RP2040
|
||||
Copyright (c) 2021 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
Based extensively off of the ESP8266 SPIFFS code, which is
|
||||
Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
|
||||
|
||||
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 "LittleFS.h"
|
||||
#include <hardware/flash.h>
|
||||
#include <hardware/sync.h>
|
||||
|
||||
extern uint8_t _FS_start;
|
||||
extern uint8_t _FS_end;
|
||||
|
||||
namespace littlefs_impl {
|
||||
|
||||
FileImplPtr LittleFSImpl::open(const char* path, OpenMode openMode, AccessMode accessMode) {
|
||||
if (!_mounted) {
|
||||
DEBUGV("LittleFSImpl::open() called on unmounted FS\n");
|
||||
return FileImplPtr();
|
||||
}
|
||||
if (!path || !path[0]) {
|
||||
DEBUGV("LittleFSImpl::open() called with invalid filename\n");
|
||||
return FileImplPtr();
|
||||
}
|
||||
if (!LittleFSImpl::pathValid(path)) {
|
||||
DEBUGV("LittleFSImpl::open() called with too long filename\n");
|
||||
return FileImplPtr();
|
||||
}
|
||||
|
||||
int flags = _getFlags(openMode, accessMode);
|
||||
auto fd = std::make_shared<lfs_file_t>();
|
||||
|
||||
if ((openMode & OM_CREATE) && strchr(path, '/')) {
|
||||
// For file creation, silently make subdirs as needed. If any fail,
|
||||
// it will be caught by the real file open later on
|
||||
char *pathStr = strdup(path);
|
||||
if (pathStr) {
|
||||
// Make dirs up to the final fnamepart
|
||||
char *ptr = strchr(pathStr, '/');
|
||||
while (ptr) {
|
||||
*ptr = 0;
|
||||
lfs_mkdir(&_lfs, pathStr);
|
||||
*ptr = '/';
|
||||
ptr = strchr(ptr+1, '/');
|
||||
}
|
||||
}
|
||||
free(pathStr);
|
||||
}
|
||||
|
||||
time_t creation = 0;
|
||||
if (_timeCallback && (openMode & OM_CREATE)) {
|
||||
// O_CREATE means we *may* make the file, but not if it already exists.
|
||||
// See if it exists, and only if not update the creation time
|
||||
int rc = lfs_file_open(&_lfs, fd.get(), path, LFS_O_RDONLY);
|
||||
if (rc == 0) {
|
||||
lfs_file_close(&_lfs, fd.get()); // It exists, don't update create time
|
||||
} else {
|
||||
creation = _timeCallback(); // File didn't exist or otherwise, so we're going to create this time
|
||||
}
|
||||
}
|
||||
|
||||
int rc = lfs_file_open(&_lfs, fd.get(), path, flags);
|
||||
if (rc == LFS_ERR_ISDIR) {
|
||||
// To support the SD.openNextFile, a null FD indicates to the LittleFSFile this is just
|
||||
// a directory whose name we are carrying around but which cannot be read or written
|
||||
return std::make_shared<LittleFSFileImpl>(this, path, nullptr, flags, creation);
|
||||
} else if (rc == 0) {
|
||||
lfs_file_sync(&_lfs, fd.get());
|
||||
return std::make_shared<LittleFSFileImpl>(this, path, fd, flags, creation);
|
||||
} else {
|
||||
DEBUGV("LittleFSDirImpl::openFile: rc=%d fd=%p path=`%s` openMode=%d accessMode=%d err=%d\n",
|
||||
rc, fd.get(), path, openMode, accessMode, rc);
|
||||
return FileImplPtr();
|
||||
}
|
||||
}
|
||||
|
||||
DirImplPtr LittleFSImpl::openDir(const char *path) {
|
||||
if (!_mounted || !path) {
|
||||
return DirImplPtr();
|
||||
}
|
||||
char *pathStr = strdup(path); // Allow edits on our scratch copy
|
||||
// Get rid of any trailing slashes
|
||||
while (strlen(pathStr) && (pathStr[strlen(pathStr)-1]=='/')) {
|
||||
pathStr[strlen(pathStr)-1] = 0;
|
||||
}
|
||||
// At this point we have a name of "blah/blah/blah" or "blah" or ""
|
||||
// If that references a directory, just open it and we're done.
|
||||
lfs_info info;
|
||||
auto dir = std::make_shared<lfs_dir_t>();
|
||||
int rc;
|
||||
const char *filter = "";
|
||||
if (!pathStr[0]) {
|
||||
// openDir("") === openDir("/")
|
||||
rc = lfs_dir_open(&_lfs, dir.get(), "/");
|
||||
filter = "";
|
||||
} else if (lfs_stat(&_lfs, pathStr, &info) >= 0) {
|
||||
if (info.type == LFS_TYPE_DIR) {
|
||||
// Easy peasy, path specifies an existing dir!
|
||||
rc = lfs_dir_open(&_lfs, dir.get(), pathStr);
|
||||
filter = "";
|
||||
} else {
|
||||
// This is a file, so open the containing dir
|
||||
char *ptr = strrchr(pathStr, '/');
|
||||
if (!ptr) {
|
||||
// No slashes, open the root dir
|
||||
rc = lfs_dir_open(&_lfs, dir.get(), "/");
|
||||
filter = pathStr;
|
||||
} else {
|
||||
// We've got slashes, open the dir one up
|
||||
*ptr = 0; // Remove slash, truncate string
|
||||
rc = lfs_dir_open(&_lfs, dir.get(), pathStr);
|
||||
filter = ptr + 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Name doesn't exist, so use the parent dir of whatever was sent in
|
||||
// This is a file, so open the containing dir
|
||||
char *ptr = strrchr(pathStr, '/');
|
||||
if (!ptr) {
|
||||
// No slashes, open the root dir
|
||||
rc = lfs_dir_open(&_lfs, dir.get(), "/");
|
||||
filter = pathStr;
|
||||
} else {
|
||||
// We've got slashes, open the dir one up
|
||||
*ptr = 0; // Remove slash, truncate string
|
||||
rc = lfs_dir_open(&_lfs, dir.get(), pathStr);
|
||||
filter = ptr + 1;
|
||||
}
|
||||
}
|
||||
if (rc < 0) {
|
||||
DEBUGV("LittleFSImpl::openDir: path=`%s` err=%d\n", path, rc);
|
||||
free(pathStr);
|
||||
return DirImplPtr();
|
||||
}
|
||||
// Skip the . and .. entries
|
||||
lfs_info dirent;
|
||||
lfs_dir_read(&_lfs, dir.get(), &dirent);
|
||||
lfs_dir_read(&_lfs, dir.get(), &dirent);
|
||||
|
||||
auto ret = std::make_shared<LittleFSDirImpl>(filter, this, dir, pathStr);
|
||||
free(pathStr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int LittleFSImpl::lfs_flash_read(const struct lfs_config *c,
|
||||
lfs_block_t block, lfs_off_t off, void *dst, lfs_size_t size) {
|
||||
LittleFSImpl *me = reinterpret_cast<LittleFSImpl*>(c->context);
|
||||
// Serial.printf(" READ: %p, %d\n", me->_start + (block * me->_blockSize) + off, size);
|
||||
memcpy(dst, me->_start + (block * me->_blockSize) + off, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LittleFSImpl::lfs_flash_prog(const struct lfs_config *c,
|
||||
lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) {
|
||||
LittleFSImpl *me = reinterpret_cast<LittleFSImpl*>(c->context);
|
||||
uint8_t *addr = me->_start + (block * me->_blockSize) + off;
|
||||
uint32_t save = save_and_disable_interrupts();
|
||||
// Serial.printf("WRITE: %p, $d\n", (intptr_t)addr - (intptr_t)XIP_BASE, size);
|
||||
flash_range_program((intptr_t)addr - (intptr_t)XIP_BASE, (const uint8_t *)buffer, size);
|
||||
restore_interrupts(save);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LittleFSImpl::lfs_flash_erase(const struct lfs_config *c, lfs_block_t block) {
|
||||
LittleFSImpl *me = reinterpret_cast<LittleFSImpl*>(c->context);
|
||||
uint8_t *addr = me->_start + (block * me->_blockSize);
|
||||
// Serial.printf("ERASE: %p, %d\n", (intptr_t)addr - (intptr_t)XIP_BASE, me->_blockSize);
|
||||
uint32_t save = save_and_disable_interrupts();
|
||||
flash_range_erase((intptr_t)addr - (intptr_t)XIP_BASE, me->_blockSize);
|
||||
restore_interrupts(save);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LittleFSImpl::lfs_flash_sync(const struct lfs_config *c) {
|
||||
/* NOOP */
|
||||
(void) c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
}; // namespace
|
||||
|
||||
FS LittleFS = FS(FSImplPtr(new littlefs_impl::LittleFSImpl(&_FS_start, &_FS_end - &_FS_start, 256, 4096, 16)));
|
||||
|
||||
686
libraries/LittleFS/src/LittleFS.h
Normal file
686
libraries/LittleFS/src/LittleFS.h
Normal file
|
|
@ -0,0 +1,686 @@
|
|||
/*
|
||||
LittleFS.h - Filesystem wrapper for LittleFS on the RP2040
|
||||
Copyright (c) 2019 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
Based heavily off of the SPIFFS equivalent code in the ESP8266 core
|
||||
"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 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 __LITTLEFS_H
|
||||
#define __LITTLEFS_H
|
||||
|
||||
#include <limits>
|
||||
#include <FS.h>
|
||||
#include <FSImpl.h>
|
||||
|
||||
#define LFS_NAME_MAX 32
|
||||
#include "../lib/littlefs/lfs.h"
|
||||
|
||||
using namespace fs;
|
||||
|
||||
namespace littlefs_impl {
|
||||
|
||||
class LittleFSFileImpl;
|
||||
class LittleFSDirImpl;
|
||||
|
||||
class LittleFSConfig : public FSConfig
|
||||
{
|
||||
public:
|
||||
static constexpr uint32_t FSId = 0x4c495454;
|
||||
LittleFSConfig(bool autoFormat = true) : FSConfig(FSId, autoFormat) { }
|
||||
};
|
||||
|
||||
class LittleFSImpl : public FSImpl
|
||||
{
|
||||
public:
|
||||
LittleFSImpl(uint8_t *start, uint32_t size, uint32_t pageSize, uint32_t blockSize, uint32_t maxOpenFds)
|
||||
: _start(start) , _size(size) , _pageSize(pageSize) , _blockSize(blockSize) , _maxOpenFds(maxOpenFds),
|
||||
_mounted(false) {
|
||||
memset(&_lfs, 0, sizeof(_lfs));
|
||||
memset(&_lfs_cfg, 0, sizeof(_lfs_cfg));
|
||||
_lfs_cfg.context = (void*) this;
|
||||
_lfs_cfg.read = lfs_flash_read;
|
||||
_lfs_cfg.prog = lfs_flash_prog;
|
||||
_lfs_cfg.erase = lfs_flash_erase;
|
||||
_lfs_cfg.sync = lfs_flash_sync;
|
||||
_lfs_cfg.read_size = 256;
|
||||
_lfs_cfg.prog_size = 256;
|
||||
_lfs_cfg.block_size = _blockSize;
|
||||
_lfs_cfg.block_count =_blockSize? _size / _blockSize: 0;
|
||||
_lfs_cfg.block_cycles = 16; // TODO - need better explanation
|
||||
_lfs_cfg.cache_size = 256;
|
||||
_lfs_cfg.lookahead_size = 256;
|
||||
_lfs_cfg.read_buffer = nullptr;
|
||||
_lfs_cfg.prog_buffer = nullptr;
|
||||
_lfs_cfg.lookahead_buffer = nullptr;
|
||||
_lfs_cfg.name_max = 0;
|
||||
_lfs_cfg.file_max = 0;
|
||||
_lfs_cfg.attr_max = 0;
|
||||
}
|
||||
|
||||
~LittleFSImpl() {
|
||||
if (_mounted) {
|
||||
lfs_unmount(&_lfs);
|
||||
}
|
||||
}
|
||||
|
||||
FileImplPtr open(const char* path, OpenMode openMode, AccessMode accessMode) override;
|
||||
DirImplPtr openDir(const char *path) override;
|
||||
|
||||
bool exists(const char* path) override {
|
||||
if (!_mounted || !path || !path[0]) {
|
||||
return false;
|
||||
}
|
||||
lfs_info info;
|
||||
int rc = lfs_stat(&_lfs, path, &info);
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
bool rename(const char* pathFrom, const char* pathTo) override {
|
||||
if (!_mounted || !pathFrom || !pathFrom[0] || !pathTo || !pathTo[0]) {
|
||||
return false;
|
||||
}
|
||||
int rc = lfs_rename(&_lfs, pathFrom, pathTo);
|
||||
if (rc != 0) {
|
||||
DEBUGV("lfs_rename: rc=%d, from=`%s`, to=`%s`\n", rc, pathFrom, pathTo);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool info(FSInfo& info) override {
|
||||
if (!_mounted) {
|
||||
return false;
|
||||
}
|
||||
info.maxOpenFiles = _maxOpenFds;
|
||||
info.blockSize = _blockSize;
|
||||
info.pageSize = _pageSize;
|
||||
info.maxOpenFiles = _maxOpenFds;
|
||||
info.maxPathLength = LFS_NAME_MAX;
|
||||
info.totalBytes = _size;
|
||||
info.usedBytes = _getUsedBlocks() * _blockSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool info64(FSInfo64& info64) {
|
||||
FSInfo i;
|
||||
if (!info(i)) {
|
||||
return false;
|
||||
}
|
||||
info64.blockSize = i.blockSize;
|
||||
info64.pageSize = i.pageSize;
|
||||
info64.maxOpenFiles = i.maxOpenFiles;
|
||||
info64.maxPathLength = i.maxPathLength;
|
||||
info64.totalBytes = i.totalBytes;
|
||||
info64.usedBytes = i.usedBytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool remove(const char* path) override {
|
||||
if (!_mounted || !path || !path[0]) {
|
||||
return false;
|
||||
}
|
||||
int rc = lfs_remove(&_lfs, path);
|
||||
if (rc != 0) {
|
||||
DEBUGV("lfs_remove: rc=%d path=`%s`\n", rc, path);
|
||||
return false;
|
||||
}
|
||||
// Now try and remove any empty subdirs this makes, silently
|
||||
char *pathStr = strdup(path);
|
||||
if (pathStr) {
|
||||
char *ptr = strrchr(pathStr, '/');
|
||||
while (ptr) {
|
||||
*ptr = 0;
|
||||
lfs_remove(&_lfs, pathStr); // Don't care if fails if there are files left
|
||||
ptr = strrchr(pathStr, '/');
|
||||
}
|
||||
free(pathStr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mkdir(const char* path) override {
|
||||
if (!_mounted || !path || !path[0]) {
|
||||
return false;
|
||||
}
|
||||
int rc = lfs_mkdir(&_lfs, path);
|
||||
return (rc==0);
|
||||
}
|
||||
|
||||
bool rmdir(const char* path) override {
|
||||
return remove(path); // Same call on LittleFS
|
||||
}
|
||||
|
||||
bool setConfig(const FSConfig &cfg) override {
|
||||
if ((cfg._type != LittleFSConfig::FSId) || _mounted) {
|
||||
return false;
|
||||
}
|
||||
_cfg = *static_cast<const LittleFSConfig *>(&cfg);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool begin() override {
|
||||
if (_size <= 0) {
|
||||
DEBUGV("LittleFS size is <= zero");
|
||||
return false;
|
||||
}
|
||||
if (_tryMount()) {
|
||||
return true;
|
||||
}
|
||||
if (!_cfg._autoFormat || !format()) {
|
||||
return false;
|
||||
}
|
||||
return _tryMount();
|
||||
}
|
||||
|
||||
void end() override {
|
||||
if (!_mounted) {
|
||||
return;
|
||||
}
|
||||
lfs_unmount(&_lfs);
|
||||
_mounted = false;
|
||||
}
|
||||
|
||||
bool format() override {
|
||||
if (_size == 0) {
|
||||
DEBUGV("lfs size is zero\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wasMounted = _mounted;
|
||||
if (_mounted) {
|
||||
lfs_unmount(&_lfs);
|
||||
_mounted = false;
|
||||
}
|
||||
|
||||
memset(&_lfs, 0, sizeof(_lfs));
|
||||
int rc = lfs_format(&_lfs, &_lfs_cfg);
|
||||
if (rc != 0) {
|
||||
DEBUGV("lfs_format: rc=%d\n", rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(_timeCallback && _tryMount()) {
|
||||
// Mounting is required to set attributes
|
||||
|
||||
time_t t = _timeCallback();
|
||||
rc = lfs_setattr(&_lfs, "/", 'c', &t, 8);
|
||||
if (rc != 0) {
|
||||
DEBUGV("lfs_format, lfs_setattr 'c': rc=%d\n", rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
rc = lfs_setattr(&_lfs, "/", 't', &t, 8);
|
||||
if (rc != 0) {
|
||||
DEBUGV("lfs_format, lfs_setattr 't': rc=%d\n", rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
lfs_unmount(&_lfs);
|
||||
_mounted = false;
|
||||
}
|
||||
|
||||
if (wasMounted) {
|
||||
return _tryMount();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
time_t getCreationTime() override {
|
||||
time_t t;
|
||||
uint32_t t32b;
|
||||
|
||||
if (lfs_getattr(&_lfs, "/", 'c', &t, 8) == 8) {
|
||||
return t;
|
||||
} else if (lfs_getattr(&_lfs, "/", 'c', &t32b, 4) == 4) {
|
||||
return (time_t)t32b;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
friend class LittleFSFileImpl;
|
||||
friend class LittleFSDirImpl;
|
||||
|
||||
lfs_t* getFS() {
|
||||
return &_lfs;
|
||||
}
|
||||
|
||||
bool _tryMount() {
|
||||
if (_mounted) {
|
||||
lfs_unmount(&_lfs);
|
||||
_mounted = false;
|
||||
}
|
||||
memset(&_lfs, 0, sizeof(_lfs));
|
||||
int rc = lfs_mount(&_lfs, &_lfs_cfg);
|
||||
if (rc==0) {
|
||||
_mounted = true;
|
||||
}
|
||||
return _mounted;
|
||||
}
|
||||
|
||||
int _getUsedBlocks() {
|
||||
if (!_mounted) {
|
||||
return 0;
|
||||
}
|
||||
return lfs_fs_size(&_lfs);
|
||||
}
|
||||
|
||||
static int _getFlags(OpenMode openMode, AccessMode accessMode) {
|
||||
int mode = 0;
|
||||
if (openMode & OM_CREATE) {
|
||||
mode |= LFS_O_CREAT;
|
||||
}
|
||||
if (openMode & OM_APPEND) {
|
||||
mode |= LFS_O_APPEND;
|
||||
}
|
||||
if (openMode & OM_TRUNCATE) {
|
||||
mode |= LFS_O_TRUNC;
|
||||
}
|
||||
if (accessMode & AM_READ) {
|
||||
mode |= LFS_O_RDONLY;
|
||||
}
|
||||
if (accessMode & AM_WRITE) {
|
||||
mode |= LFS_O_WRONLY;
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
// Check that no components of path beyond max len
|
||||
static bool pathValid(const char *path) {
|
||||
while (*path) {
|
||||
const char *slash = strchr(path, '/');
|
||||
if (!slash) {
|
||||
if (strlen(path) >= LFS_NAME_MAX) {
|
||||
// Terminal filename is too long
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ((slash - path) >= LFS_NAME_MAX) {
|
||||
// This subdir name too long
|
||||
return false;
|
||||
}
|
||||
path = slash + 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// The actual flash accessing routines
|
||||
static int lfs_flash_read(const struct lfs_config *c, lfs_block_t block,
|
||||
lfs_off_t off, void *buffer, lfs_size_t size);
|
||||
static int lfs_flash_prog(const struct lfs_config *c, lfs_block_t block,
|
||||
lfs_off_t off, const void *buffer, lfs_size_t size);
|
||||
static int lfs_flash_erase(const struct lfs_config *c, lfs_block_t block);
|
||||
static int lfs_flash_sync(const struct lfs_config *c);
|
||||
|
||||
lfs_t _lfs;
|
||||
lfs_config _lfs_cfg;
|
||||
|
||||
LittleFSConfig _cfg;
|
||||
|
||||
uint8_t *_start;
|
||||
uint32_t _size;
|
||||
uint32_t _pageSize;
|
||||
uint32_t _blockSize;
|
||||
uint32_t _maxOpenFds;
|
||||
|
||||
bool _mounted;
|
||||
};
|
||||
|
||||
|
||||
class LittleFSFileImpl : public FileImpl
|
||||
{
|
||||
public:
|
||||
LittleFSFileImpl(LittleFSImpl* fs, const char *name, std::shared_ptr<lfs_file_t> fd, int flags, time_t creation) : _fs(fs), _fd(fd), _opened(true), _flags(flags), _creation(creation) {
|
||||
_name = std::shared_ptr<char>(new char[strlen(name) + 1], std::default_delete<char[]>());
|
||||
strcpy(_name.get(), name);
|
||||
}
|
||||
|
||||
~LittleFSFileImpl() override {
|
||||
if (_opened) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
size_t write(const uint8_t *buf, size_t size) override {
|
||||
if (!_opened || !_fd || !buf) {
|
||||
return 0;
|
||||
}
|
||||
int result = lfs_file_write(_fs->getFS(), _getFD(), (void*) buf, size);
|
||||
if (result < 0) {
|
||||
DEBUGV("lfs_write rc=%d\n", result);
|
||||
return 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int read(uint8_t* buf, size_t size) override {
|
||||
if (!_opened || !_fd | !buf) {
|
||||
return 0;
|
||||
}
|
||||
int result = lfs_file_read(_fs->getFS(), _getFD(), (void*) buf, size);
|
||||
if (result < 0) {
|
||||
DEBUGV("lfs_read rc=%d\n", result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void flush() override {
|
||||
if (!_opened || !_fd) {
|
||||
return;
|
||||
}
|
||||
int rc = lfs_file_sync(_fs->getFS(), _getFD());
|
||||
if (rc < 0) {
|
||||
DEBUGV("lfs_file_sync rc=%d\n", rc);
|
||||
}
|
||||
}
|
||||
|
||||
bool seek(uint32_t pos, SeekMode mode) override {
|
||||
if (!_opened || !_fd) {
|
||||
return false;
|
||||
}
|
||||
int32_t offset = static_cast<int32_t>(pos);
|
||||
if (mode == SeekEnd) {
|
||||
offset = -offset; // TODO - this seems like its plain wrong vs. POSIX
|
||||
}
|
||||
auto lastPos = position();
|
||||
int rc = lfs_file_seek(_fs->getFS(), _getFD(), offset, (int)mode); // NB. SeekMode === LFS_SEEK_TYPES
|
||||
if (rc < 0) {
|
||||
DEBUGV("lfs_file_seek rc=%d\n", rc);
|
||||
return false;
|
||||
}
|
||||
if (position() > size()) {
|
||||
seek(lastPos, SeekSet); // Pretend the seek() never happened
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t position() const override {
|
||||
if (!_opened || !_fd) {
|
||||
return 0;
|
||||
}
|
||||
int result = lfs_file_tell(_fs->getFS(), _getFD());
|
||||
if (result < 0) {
|
||||
DEBUGV("lfs_file_tell rc=%d\n", result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t size() const override {
|
||||
return (_opened && _fd)? lfs_file_size(_fs->getFS(), _getFD()) : 0;
|
||||
}
|
||||
|
||||
bool truncate(uint32_t size) override {
|
||||
if (!_opened || !_fd) {
|
||||
return false;
|
||||
}
|
||||
int rc = lfs_file_truncate(_fs->getFS(), _getFD(), size);
|
||||
if (rc < 0) {
|
||||
DEBUGV("lfs_file_truncate rc=%d\n", rc);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void close() override {
|
||||
if (_opened && _fd) {
|
||||
lfs_file_close(_fs->getFS(), _getFD());
|
||||
_opened = false;
|
||||
DEBUGV("lfs_file_close: fd=%p\n", _getFD());
|
||||
if (_timeCallback && (_flags & LFS_O_WRONLY)) {
|
||||
// If the file opened with O_CREAT, write the creation time attribute
|
||||
if (_creation) {
|
||||
int rc = lfs_setattr(_fs->getFS(), _name.get(), 'c', (const void *)&_creation, sizeof(_creation));
|
||||
if (rc < 0) {
|
||||
DEBUGV("Unable to set creation time on '%s' to %d\n", _name.get(), _creation);
|
||||
}
|
||||
}
|
||||
// Add metadata with last write time
|
||||
time_t now = _timeCallback();
|
||||
int rc = lfs_setattr(_fs->getFS(), _name.get(), 't', (const void *)&now, sizeof(now));
|
||||
if (rc < 0) {
|
||||
DEBUGV("Unable to set last write time on '%s' to %d\n", _name.get(), now);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time_t getLastWrite() override {
|
||||
time_t ftime = 0;
|
||||
if (_opened && _fd) {
|
||||
int rc = lfs_getattr(_fs->getFS(), _name.get(), 't', (void *)&ftime, sizeof(ftime));
|
||||
if (rc != sizeof(ftime))
|
||||
ftime = 0; // Error, so clear read value
|
||||
}
|
||||
return ftime;
|
||||
}
|
||||
|
||||
time_t getCreationTime() override {
|
||||
time_t ftime = 0;
|
||||
if (_opened && _fd) {
|
||||
int rc = lfs_getattr(_fs->getFS(), _name.get(), 'c', (void *)&ftime, sizeof(ftime));
|
||||
if (rc != sizeof(ftime))
|
||||
ftime = 0; // Error, so clear read value
|
||||
}
|
||||
return ftime;
|
||||
}
|
||||
|
||||
const char* name() const override {
|
||||
if (!_opened) {
|
||||
return nullptr;
|
||||
} else {
|
||||
const char *p = _name.get();
|
||||
const char *slash = strrchr(p, '/');
|
||||
return (slash && slash[1]) ? slash + 1 : p;
|
||||
}
|
||||
}
|
||||
|
||||
const char* fullName() const override {
|
||||
return _opened ? _name.get() : nullptr;
|
||||
}
|
||||
|
||||
bool isFile() const override {
|
||||
if (!_opened || !_fd) {
|
||||
return false;
|
||||
}
|
||||
lfs_info info;
|
||||
int rc = lfs_stat(_fs->getFS(), fullName(), &info);
|
||||
return (rc == 0) && (info.type == LFS_TYPE_REG);
|
||||
}
|
||||
|
||||
bool isDirectory() const override {
|
||||
if (!_opened) {
|
||||
return false;
|
||||
} else if (!_fd) {
|
||||
return true;
|
||||
}
|
||||
lfs_info info;
|
||||
int rc = lfs_stat(_fs->getFS(), fullName(), &info);
|
||||
return (rc == 0) && (info.type == LFS_TYPE_DIR);
|
||||
}
|
||||
|
||||
protected:
|
||||
lfs_file_t *_getFD() const {
|
||||
return _fd.get();
|
||||
}
|
||||
|
||||
LittleFSImpl *_fs;
|
||||
std::shared_ptr<lfs_file_t> _fd;
|
||||
std::shared_ptr<char> _name;
|
||||
bool _opened;
|
||||
int _flags;
|
||||
time_t _creation;
|
||||
};
|
||||
|
||||
class LittleFSDirImpl : public DirImpl
|
||||
{
|
||||
public:
|
||||
LittleFSDirImpl(const String& pattern, LittleFSImpl* fs, std::shared_ptr<lfs_dir_t> dir, const char *dirPath = nullptr)
|
||||
: _pattern(pattern) , _fs(fs) , _dir(dir) , _dirPath(nullptr), _valid(false), _opened(true)
|
||||
{
|
||||
memset(&_dirent, 0, sizeof(_dirent));
|
||||
if (dirPath) {
|
||||
_dirPath = std::shared_ptr<char>(new char[strlen(dirPath) + 1], std::default_delete<char[]>());
|
||||
strcpy(_dirPath.get(), dirPath);
|
||||
}
|
||||
}
|
||||
|
||||
~LittleFSDirImpl() override {
|
||||
if (_opened) {
|
||||
lfs_dir_close(_fs->getFS(), _getDir());
|
||||
}
|
||||
}
|
||||
|
||||
FileImplPtr openFile(OpenMode openMode, AccessMode accessMode) override {
|
||||
if (!_valid) {
|
||||
return FileImplPtr();
|
||||
}
|
||||
int nameLen = 3; // Slashes, terminator
|
||||
nameLen += _dirPath.get() ? strlen(_dirPath.get()) : 0;
|
||||
nameLen += strlen(_dirent.name);
|
||||
char tmpName[nameLen];
|
||||
snprintf(tmpName, nameLen, "%s%s%s", _dirPath.get() ? _dirPath.get() : "", _dirPath.get()&&_dirPath.get()[0]?"/":"", _dirent.name);
|
||||
auto ret = _fs->open((const char *)tmpName, openMode, accessMode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char* fileName() override {
|
||||
if (!_valid) {
|
||||
return nullptr;
|
||||
}
|
||||
return (const char*) _dirent.name;
|
||||
}
|
||||
|
||||
size_t fileSize() override {
|
||||
if (!_valid) {
|
||||
return 0;
|
||||
}
|
||||
return _dirent.size;
|
||||
}
|
||||
|
||||
time_t fileTime() override {
|
||||
time_t t;
|
||||
int32_t t32b;
|
||||
|
||||
// If the attribute is 8-bytes, we're all set
|
||||
if (_getAttr('t', 8, &t)) {
|
||||
return t;
|
||||
} else if (_getAttr('t', 4, &t32b)) {
|
||||
// If it's 4 bytes silently promote to 64b
|
||||
return (time_t)t32b;
|
||||
} else {
|
||||
// OTW, none present
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
time_t fileCreationTime() override {
|
||||
time_t t;
|
||||
int32_t t32b;
|
||||
|
||||
// If the attribute is 8-bytes, we're all set
|
||||
if (_getAttr('c', 8, &t)) {
|
||||
return t;
|
||||
} else if (_getAttr('c', 4, &t32b)) {
|
||||
// If it's 4 bytes silently promote to 64b
|
||||
return (time_t)t32b;
|
||||
} else {
|
||||
// OTW, none present
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool isFile() const override {
|
||||
return _valid && (_dirent.type == LFS_TYPE_REG);
|
||||
}
|
||||
|
||||
bool isDirectory() const override {
|
||||
return _valid && (_dirent.type == LFS_TYPE_DIR);
|
||||
}
|
||||
|
||||
bool rewind() override {
|
||||
_valid = false;
|
||||
int rc = lfs_dir_rewind(_fs->getFS(), _getDir());
|
||||
// Skip the . and .. entries
|
||||
lfs_info dirent;
|
||||
lfs_dir_read(_fs->getFS(), _getDir(), &dirent);
|
||||
lfs_dir_read(_fs->getFS(), _getDir(), &dirent);
|
||||
return (rc == 0);
|
||||
}
|
||||
|
||||
bool next() override {
|
||||
const int n = _pattern.length();
|
||||
bool match;
|
||||
do {
|
||||
_dirent.name[0] = 0;
|
||||
int rc = lfs_dir_read(_fs->getFS(), _getDir(), &_dirent);
|
||||
_valid = (rc == 1);
|
||||
match = (!n || !strncmp((const char*) _dirent.name, _pattern.c_str(), n));
|
||||
} while (_valid && !match);
|
||||
return _valid;
|
||||
}
|
||||
|
||||
protected:
|
||||
lfs_dir_t *_getDir() const {
|
||||
return _dir.get();
|
||||
}
|
||||
|
||||
bool _getAttr(char attr, int len, void *dest) {
|
||||
if (!_valid || !len || !dest) {
|
||||
return false;
|
||||
}
|
||||
int nameLen = 3; // Slashes, terminator
|
||||
nameLen += _dirPath.get() ? strlen(_dirPath.get()) : 0;
|
||||
nameLen += strlen(_dirent.name);
|
||||
char tmpName[nameLen];
|
||||
snprintf(tmpName, nameLen, "%s%s%s", _dirPath.get() ? _dirPath.get() : "", _dirPath.get()&&_dirPath.get()[0]?"/":"", _dirent.name);
|
||||
int rc = lfs_getattr(_fs->getFS(), tmpName, attr, dest, len);
|
||||
return (rc == len);
|
||||
}
|
||||
|
||||
String _pattern;
|
||||
LittleFSImpl *_fs;
|
||||
std::shared_ptr<lfs_dir_t> _dir;
|
||||
std::shared_ptr<char> _dirPath;
|
||||
lfs_info _dirent;
|
||||
bool _valid;
|
||||
bool _opened;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_LITTLEFS)
|
||||
extern FS LittleFS;
|
||||
using littlefs_impl::LittleFSConfig;
|
||||
#endif // ARDUINO
|
||||
|
||||
|
||||
#endif // !defined(__LITTLEFS_H)
|
||||
10
libraries/LittleFS/src/lfs.c
Normal file
10
libraries/LittleFS/src/lfs.c
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// Can't place library in ths src/ directory, Arduino will attempt to build the tests/etc.
|
||||
// Just have a stub here that redirects to the actual source file
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
#define LFS_NAME_MAX 32
|
||||
#define LFS_NO_DEBUG
|
||||
#define LFS_NO_WARN
|
||||
#define LFS_NO_ERROR
|
||||
|
||||
#include "../lib/littlefs/lfs.c"
|
||||
6
libraries/LittleFS/src/lfs_util.c
Normal file
6
libraries/LittleFS/src/lfs_util.c
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#define LFS_NAME_MAX 32
|
||||
#define LFS_NO_DEBUG
|
||||
#define LFS_NO_WARN
|
||||
#define LFS_NO_ERROR
|
||||
|
||||
#include "../lib/littlefs/lfs_util.c"
|
||||
Loading…
Reference in a new issue