mirror of https://github.com/meshcore-dev/MeshCore
28 changed files with 4865 additions and 15 deletions
@ -0,0 +1 @@ |
|||
This is LittleFS from Adafruit, stripped from things that makes it not compile with stm32 (refs to TinyUSB and free_rtos, mostly) |
|||
@ -0,0 +1,10 @@ |
|||
name=Adafruit Little File System Libraries |
|||
version=0.11.0 |
|||
author=Adafruit |
|||
maintainer=Adafruit <[email protected]> |
|||
sentence=Arduino library for ARM Little File System |
|||
paragraph=Arduino library for ARM Little File System |
|||
category=Data Storage |
|||
url=https://github.com/adafruit/Adafruit_nRF52_Arduino |
|||
architectures=* |
|||
includes=Adafruit_LittleFS.h |
|||
@ -0,0 +1,273 @@ |
|||
/*
|
|||
* The MIT License (MIT) |
|||
* |
|||
* Copyright (c) 2019 Ha Thach for Adafruit Industries |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to deal |
|||
* in the Software without restriction, including without limitation the rights |
|||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
* copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
* THE SOFTWARE. |
|||
*/ |
|||
|
|||
#include <Arduino.h> |
|||
#include <string.h> |
|||
#include "Adafruit_LittleFS.h" |
|||
|
|||
//#include <Adafruit_TinyUSB.h> // for Serial
|
|||
|
|||
using namespace Adafruit_LittleFS_Namespace; |
|||
|
|||
#define memclr(buffer, size) memset(buffer, 0, size) |
|||
#define varclr(_var) memclr(_var, sizeof(*(_var))) |
|||
|
|||
//--------------------------------------------------------------------+
|
|||
// Implementation
|
|||
//--------------------------------------------------------------------+
|
|||
|
|||
Adafruit_LittleFS::Adafruit_LittleFS (void) |
|||
: Adafruit_LittleFS(NULL) |
|||
{ |
|||
|
|||
} |
|||
|
|||
Adafruit_LittleFS::Adafruit_LittleFS (struct lfs_config* cfg) |
|||
{ |
|||
varclr(&_lfs); |
|||
_lfs_cfg = cfg; |
|||
_mounted = false; |
|||
// _mutex = xSemaphoreCreateMutexStatic(&this->_MutexStorageSpace);
|
|||
} |
|||
|
|||
Adafruit_LittleFS::~Adafruit_LittleFS () |
|||
{ |
|||
|
|||
} |
|||
|
|||
// Initialize and mount the file system
|
|||
// Return true if mounted successfully else probably corrupted.
|
|||
// User should format the disk and try again
|
|||
bool Adafruit_LittleFS::begin (struct lfs_config * cfg) |
|||
{ |
|||
_lockFS(); |
|||
|
|||
bool ret; |
|||
// not a loop, just an quick way to short-circuit on error
|
|||
do { |
|||
if (_mounted) { ret = true; break; } |
|||
if (cfg) { _lfs_cfg = cfg; } |
|||
if (nullptr == _lfs_cfg) { ret = false; break; } |
|||
// actually attempt to mount, and log error if one occurs
|
|||
int err = lfs_mount(&_lfs, _lfs_cfg); |
|||
PRINT_LFS_ERR(err); |
|||
_mounted = (err == LFS_ERR_OK); |
|||
ret = _mounted; |
|||
} while(0); |
|||
|
|||
_unlockFS(); |
|||
return ret; |
|||
} |
|||
|
|||
// Tear down and unmount file system
|
|||
void Adafruit_LittleFS::end(void) |
|||
{ |
|||
_lockFS(); |
|||
|
|||
if (_mounted) |
|||
{ |
|||
_mounted = false; |
|||
int err = lfs_unmount(&_lfs); |
|||
PRINT_LFS_ERR(err); |
|||
(void)err; |
|||
} |
|||
|
|||
_unlockFS(); |
|||
} |
|||
|
|||
bool Adafruit_LittleFS::format (void) |
|||
{ |
|||
_lockFS(); |
|||
|
|||
int err = LFS_ERR_OK; |
|||
bool attemptMount = _mounted; |
|||
// not a loop, just an quick way to short-circuit on error
|
|||
do |
|||
{ |
|||
// if already mounted: umount first -> format -> remount
|
|||
if (_mounted) |
|||
{ |
|||
_mounted = false; |
|||
err = lfs_unmount(&_lfs); |
|||
if ( LFS_ERR_OK != err) { PRINT_LFS_ERR(err); break; } |
|||
} |
|||
err = lfs_format(&_lfs, _lfs_cfg); |
|||
if ( LFS_ERR_OK != err ) { PRINT_LFS_ERR(err); break; } |
|||
|
|||
if (attemptMount) |
|||
{ |
|||
err = lfs_mount(&_lfs, _lfs_cfg); |
|||
if ( LFS_ERR_OK != err ) { PRINT_LFS_ERR(err); break; } |
|||
_mounted = true; |
|||
} |
|||
// success!
|
|||
} while(0); |
|||
|
|||
_unlockFS(); |
|||
return LFS_ERR_OK == err; |
|||
} |
|||
|
|||
// Open a file or folder
|
|||
Adafruit_LittleFS_Namespace::File Adafruit_LittleFS::open (char const *filepath, uint8_t mode) |
|||
{ |
|||
// No lock is required here ... the File() object will synchronize with the mutex provided
|
|||
return Adafruit_LittleFS_Namespace::File(filepath, mode, *this); |
|||
} |
|||
|
|||
// Check if file or folder exists
|
|||
bool Adafruit_LittleFS::exists (char const *filepath) |
|||
{ |
|||
struct lfs_info info; |
|||
_lockFS(); |
|||
|
|||
bool ret = (0 == lfs_stat(&_lfs, filepath, &info)); |
|||
|
|||
_unlockFS(); |
|||
return ret; |
|||
} |
|||
|
|||
|
|||
// Create a directory, create intermediate parent if needed
|
|||
bool Adafruit_LittleFS::mkdir (char const *filepath) |
|||
{ |
|||
bool ret = true; |
|||
const char* slash = filepath; |
|||
if ( slash[0] == '/' ) slash++; // skip root '/'
|
|||
|
|||
_lockFS(); |
|||
|
|||
// make intermediate parent directory(ies)
|
|||
while ( NULL != (slash = strchr(slash, '/')) ) |
|||
{ |
|||
char parent[slash - filepath + 1] = { 0 }; |
|||
memcpy(parent, filepath, slash - filepath); |
|||
|
|||
int rc = lfs_mkdir(&_lfs, parent); |
|||
if ( rc != LFS_ERR_OK && rc != LFS_ERR_EXIST ) |
|||
{ |
|||
PRINT_LFS_ERR(rc); |
|||
ret = false; |
|||
break; |
|||
} |
|||
slash++; |
|||
} |
|||
// make the final requested directory
|
|||
if (ret) |
|||
{ |
|||
int rc = lfs_mkdir(&_lfs, filepath); |
|||
if ( rc != LFS_ERR_OK && rc != LFS_ERR_EXIST ) |
|||
{ |
|||
PRINT_LFS_ERR(rc); |
|||
ret = false; |
|||
} |
|||
} |
|||
|
|||
_unlockFS(); |
|||
return ret; |
|||
} |
|||
|
|||
// Remove a file
|
|||
bool Adafruit_LittleFS::remove (char const *filepath) |
|||
{ |
|||
_lockFS(); |
|||
|
|||
int err = lfs_remove(&_lfs, filepath); |
|||
PRINT_LFS_ERR(err); |
|||
|
|||
_unlockFS(); |
|||
return LFS_ERR_OK == err; |
|||
} |
|||
|
|||
// Rename a file
|
|||
bool Adafruit_LittleFS::rename (char const *oldfilepath, char const *newfilepath) |
|||
{ |
|||
_lockFS(); |
|||
|
|||
int err = lfs_rename(&_lfs, oldfilepath, newfilepath); |
|||
PRINT_LFS_ERR(err); |
|||
|
|||
_unlockFS(); |
|||
return LFS_ERR_OK == err; |
|||
} |
|||
|
|||
// Remove a folder
|
|||
bool Adafruit_LittleFS::rmdir (char const *filepath) |
|||
{ |
|||
_lockFS(); |
|||
|
|||
int err = lfs_remove(&_lfs, filepath); |
|||
PRINT_LFS_ERR(err); |
|||
|
|||
_unlockFS(); |
|||
return LFS_ERR_OK == err; |
|||
} |
|||
|
|||
// Remove a folder recursively
|
|||
bool Adafruit_LittleFS::rmdir_r (char const *filepath) |
|||
{ |
|||
/* adafruit: lfs is modified to remove non-empty folder,
|
|||
According to below issue, comment these 2 line won't corrupt filesystem |
|||
at least when using LFS v1. If moving to LFS v2, see tracked issue |
|||
to see if issues (such as the orphans in threaded linked list) are resolved. |
|||
https://github.com/ARMmbed/littlefs/issues/43
|
|||
*/ |
|||
_lockFS(); |
|||
|
|||
int err = lfs_remove(&_lfs, filepath); |
|||
PRINT_LFS_ERR(err); |
|||
|
|||
_unlockFS(); |
|||
return LFS_ERR_OK == err; |
|||
} |
|||
|
|||
//------------- Debug -------------//
|
|||
#if CFG_DEBUG |
|||
|
|||
const char* dbg_strerr_lfs (int32_t err) |
|||
{ |
|||
switch ( err ) |
|||
{ |
|||
case LFS_ERR_OK : return "LFS_ERR_OK"; |
|||
case LFS_ERR_IO : return "LFS_ERR_IO"; |
|||
case LFS_ERR_CORRUPT : return "LFS_ERR_CORRUPT"; |
|||
case LFS_ERR_NOENT : return "LFS_ERR_NOENT"; |
|||
case LFS_ERR_EXIST : return "LFS_ERR_EXIST"; |
|||
case LFS_ERR_NOTDIR : return "LFS_ERR_NOTDIR"; |
|||
case LFS_ERR_ISDIR : return "LFS_ERR_ISDIR"; |
|||
case LFS_ERR_NOTEMPTY : return "LFS_ERR_NOTEMPTY"; |
|||
case LFS_ERR_BADF : return "LFS_ERR_BADF"; |
|||
case LFS_ERR_INVAL : return "LFS_ERR_INVAL"; |
|||
case LFS_ERR_NOSPC : return "LFS_ERR_NOSPC"; |
|||
case LFS_ERR_NOMEM : return "LFS_ERR_NOMEM"; |
|||
|
|||
default: |
|||
static char errcode[10]; |
|||
sprintf(errcode, "%ld", err); |
|||
return errcode; |
|||
} |
|||
|
|||
return NULL; |
|||
} |
|||
|
|||
#endif |
|||
@ -0,0 +1,102 @@ |
|||
/*
|
|||
* The MIT License (MIT) |
|||
* |
|||
* Copyright (c) 2019 Ha Thach for Adafruit Industries |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to deal |
|||
* in the Software without restriction, including without limitation the rights |
|||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
* copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
* THE SOFTWARE. |
|||
*/ |
|||
|
|||
#ifndef ADAFRUIT_LITTLEFS_H_ |
|||
#define ADAFRUIT_LITTLEFS_H_ |
|||
|
|||
#include <Stream.h> |
|||
|
|||
// Internal Flash uses ARM Little FileSystem
|
|||
// https://github.com/ARMmbed/littlefs
|
|||
#include "littlefs/lfs.h" |
|||
#include "Adafruit_LittleFS_File.h" |
|||
//#include "rtos.h" // tied to FreeRTOS for serialization
|
|||
|
|||
class Adafruit_LittleFS |
|||
{ |
|||
public: |
|||
Adafruit_LittleFS (void); |
|||
Adafruit_LittleFS (struct lfs_config* cfg); |
|||
virtual ~Adafruit_LittleFS (); |
|||
|
|||
bool begin(struct lfs_config * cfg = NULL); |
|||
void end(void); |
|||
|
|||
// Open the specified file/directory with the supplied mode (e.g. read or
|
|||
// write, etc). Returns a File object for interacting with the file.
|
|||
// Note that currently only one file can be open at a time.
|
|||
Adafruit_LittleFS_Namespace::File open (char const *filename, uint8_t mode = Adafruit_LittleFS_Namespace::FILE_O_READ); |
|||
|
|||
// Methods to determine if the requested file path exists.
|
|||
bool exists (char const *filepath); |
|||
|
|||
// Create the requested directory hierarchy--if intermediate directories
|
|||
// do not exist they will be created.
|
|||
bool mkdir (char const *filepath); |
|||
|
|||
// Delete the file.
|
|||
bool remove (char const *filepath); |
|||
|
|||
// Rename the file.
|
|||
bool rename (char const *oldfilepath, char const *newfilepath); |
|||
|
|||
// Delete a folder (must be empty)
|
|||
bool rmdir (char const *filepath); |
|||
|
|||
// Delete a folder (recursively)
|
|||
bool rmdir_r (char const *filepath); |
|||
|
|||
// format file system
|
|||
bool format (void); |
|||
|
|||
/*------------------------------------------------------------------*/ |
|||
/* INTERNAL USAGE ONLY
|
|||
* Although declare as public, it is meant to be invoked by internal |
|||
* code. User should not call these directly |
|||
*------------------------------------------------------------------*/ |
|||
lfs_t* _getFS (void) { return &_lfs; } |
|||
void _lockFS (void) { }//xSemaphoreTake(_mutex, portMAX_DELAY); }
|
|||
void _unlockFS(void) { }//xSemaphoreGive(_mutex); }
|
|||
|
|||
protected: |
|||
bool _mounted; |
|||
struct lfs_config* _lfs_cfg; |
|||
lfs_t _lfs; |
|||
// SemaphoreHandle_t _mutex;
|
|||
|
|||
private: |
|||
// StaticSemaphore_t _MutexStorageSpace;
|
|||
}; |
|||
|
|||
#if !CFG_DEBUG |
|||
#define VERIFY_LFS(...) _GET_3RD_ARG(__VA_ARGS__, VERIFY_ERR_2ARGS, VERIFY_ERR_1ARGS)(__VA_ARGS__, NULL) |
|||
#define PRINT_LFS_ERR(_err) |
|||
#else |
|||
#define VERIFY_LFS(...) _GET_3RD_ARG(__VA_ARGS__, VERIFY_ERR_2ARGS, VERIFY_ERR_1ARGS)(__VA_ARGS__, dbg_strerr_lfs) |
|||
#define PRINT_LFS_ERR(_err) do { if (_err) { VERIFY_MESS((long int)_err, dbg_strerr_lfs); } } while(0) // LFS_ERR are of type int, VERIFY_MESS expects long_int
|
|||
|
|||
const char* dbg_strerr_lfs (int32_t err); |
|||
#endif |
|||
|
|||
#endif /* ADAFRUIT_LITTLEFS_H_ */ |
|||
@ -0,0 +1,420 @@ |
|||
/*
|
|||
* The MIT License (MIT) |
|||
* |
|||
* Copyright (c) 2019 Ha Thach for Adafruit Industries |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to deal |
|||
* in the Software without restriction, including without limitation the rights |
|||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
* copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
* THE SOFTWARE. |
|||
*/ |
|||
|
|||
#include <Arduino.h> |
|||
#include "Adafruit_LittleFS.h" |
|||
#include "littlefs/lfs.h" |
|||
|
|||
//--------------------------------------------------------------------+
|
|||
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
|
|||
//--------------------------------------------------------------------+
|
|||
|
|||
using namespace Adafruit_LittleFS_Namespace; |
|||
|
|||
File::File (Adafruit_LittleFS &fs) |
|||
{ |
|||
_fs = &fs; |
|||
_is_dir = false; |
|||
_name[0] = 0; |
|||
_name[LFS_NAME_MAX] = 0; |
|||
_dir_path = NULL; |
|||
|
|||
_dir = NULL; |
|||
_file = NULL; |
|||
} |
|||
|
|||
File::File (char const *filename, uint8_t mode, Adafruit_LittleFS &fs) |
|||
: File(fs) |
|||
{ |
|||
// public constructor calls public API open(), which will obtain the mutex
|
|||
this->open(filename, mode); |
|||
} |
|||
|
|||
bool File::_open_file (char const *filepath, uint8_t mode) |
|||
{ |
|||
int flags = (mode == FILE_O_READ) ? LFS_O_RDONLY : |
|||
(mode == FILE_O_WRITE) ? (LFS_O_RDWR | LFS_O_CREAT) : 0; |
|||
|
|||
if ( flags ) |
|||
{ |
|||
_file = (lfs_file_t*) malloc(sizeof(lfs_file_t)); |
|||
if (!_file) return false; |
|||
|
|||
int rc = lfs_file_open(_fs->_getFS(), _file, filepath, flags); |
|||
|
|||
if ( rc ) |
|||
{ |
|||
// failed to open
|
|||
PRINT_LFS_ERR(rc); |
|||
// free memory
|
|||
free(_file); |
|||
_file = NULL; |
|||
return false; |
|||
} |
|||
|
|||
// move to end of file
|
|||
if ( mode == FILE_O_WRITE ) lfs_file_seek(_fs->_getFS(), _file, 0, LFS_SEEK_END); |
|||
|
|||
_is_dir = false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
bool File::_open_dir (char const *filepath) |
|||
{ |
|||
_dir = (lfs_dir_t*) malloc(sizeof(lfs_dir_t)); |
|||
if (!_dir) return false; |
|||
|
|||
int rc = lfs_dir_open(_fs->_getFS(), _dir, filepath); |
|||
|
|||
if ( rc ) |
|||
{ |
|||
// failed to open
|
|||
PRINT_LFS_ERR(rc); |
|||
// free memory
|
|||
free(_dir); |
|||
_dir = NULL; |
|||
return false; |
|||
} |
|||
|
|||
_is_dir = true; |
|||
|
|||
_dir_path = (char*) malloc(strlen(filepath) + 1); |
|||
strcpy(_dir_path, filepath); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
bool File::open (char const *filepath, uint8_t mode) |
|||
{ |
|||
bool ret = false; |
|||
_fs->_lockFS(); |
|||
|
|||
ret = this->_open(filepath, mode); |
|||
|
|||
_fs->_unlockFS(); |
|||
return ret; |
|||
} |
|||
|
|||
bool File::_open (char const *filepath, uint8_t mode) |
|||
{ |
|||
bool ret = false; |
|||
|
|||
// close if currently opened
|
|||
if ( this->isOpen() ) _close(); |
|||
|
|||
struct lfs_info info; |
|||
int rc = lfs_stat(_fs->_getFS(), filepath, &info); |
|||
|
|||
if ( LFS_ERR_OK == rc ) |
|||
{ |
|||
// file existed, open file or directory accordingly
|
|||
ret = (info.type == LFS_TYPE_REG) ? _open_file(filepath, mode) : _open_dir(filepath); |
|||
} |
|||
else if ( LFS_ERR_NOENT == rc ) |
|||
{ |
|||
// file not existed, only proceed with FILE_O_WRITE mode
|
|||
if ( mode == FILE_O_WRITE ) ret = _open_file(filepath, mode); |
|||
} |
|||
else |
|||
{ |
|||
PRINT_LFS_ERR(rc); |
|||
} |
|||
|
|||
// save bare file name
|
|||
if (ret) |
|||
{ |
|||
char const* splash = strrchr(filepath, '/'); |
|||
strncpy(_name, splash ? (splash + 1) : filepath, LFS_NAME_MAX); |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
size_t File::write (uint8_t ch) |
|||
{ |
|||
return write(&ch, 1); |
|||
} |
|||
|
|||
size_t File::write (uint8_t const *buf, size_t size) |
|||
{ |
|||
lfs_ssize_t wrcount = 0; |
|||
_fs->_lockFS(); |
|||
|
|||
if (!this->_is_dir) |
|||
{ |
|||
wrcount = lfs_file_write(_fs->_getFS(), _file, buf, size); |
|||
if (wrcount < 0) |
|||
{ |
|||
wrcount = 0; |
|||
} |
|||
} |
|||
|
|||
_fs->_unlockFS(); |
|||
return wrcount; |
|||
} |
|||
|
|||
int File::read (void) |
|||
{ |
|||
// this thin wrapper relies on called function to synchronize
|
|||
int ret = -1; |
|||
uint8_t ch; |
|||
if (read(&ch, 1) > 0) |
|||
{ |
|||
ret = static_cast<int>(ch); |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
int File::read (void *buf, uint16_t nbyte) |
|||
{ |
|||
int ret = 0; |
|||
_fs->_lockFS(); |
|||
|
|||
if (!this->_is_dir) |
|||
{ |
|||
ret = lfs_file_read(_fs->_getFS(), _file, buf, nbyte); |
|||
} |
|||
|
|||
_fs->_unlockFS(); |
|||
return ret; |
|||
} |
|||
|
|||
int File::peek (void) |
|||
{ |
|||
int ret = -1; |
|||
_fs->_lockFS(); |
|||
|
|||
if (!this->_is_dir) |
|||
{ |
|||
uint32_t pos = lfs_file_tell(_fs->_getFS(), _file); |
|||
uint8_t ch = 0; |
|||
if (lfs_file_read(_fs->_getFS(), _file, &ch, 1) > 0) |
|||
{ |
|||
ret = static_cast<int>(ch); |
|||
} |
|||
(void) lfs_file_seek(_fs->_getFS(), _file, pos, LFS_SEEK_SET); |
|||
} |
|||
|
|||
_fs->_unlockFS(); |
|||
return ret; |
|||
} |
|||
|
|||
int File::available (void) |
|||
{ |
|||
int ret = 0; |
|||
_fs->_lockFS(); |
|||
|
|||
if (!this->_is_dir) |
|||
{ |
|||
uint32_t size = lfs_file_size(_fs->_getFS(), _file); |
|||
uint32_t pos = lfs_file_tell(_fs->_getFS(), _file); |
|||
ret = size - pos; |
|||
} |
|||
|
|||
_fs->_unlockFS(); |
|||
return ret; |
|||
} |
|||
|
|||
bool File::seek (uint32_t pos) |
|||
{ |
|||
bool ret = false; |
|||
_fs->_lockFS(); |
|||
|
|||
if (!this->_is_dir) |
|||
{ |
|||
ret = lfs_file_seek(_fs->_getFS(), _file, pos, LFS_SEEK_SET) >= 0; |
|||
} |
|||
|
|||
_fs->_unlockFS(); |
|||
return ret; |
|||
} |
|||
|
|||
uint32_t File::position (void) |
|||
{ |
|||
uint32_t ret = 0; |
|||
_fs->_lockFS(); |
|||
|
|||
if (!this->_is_dir) |
|||
{ |
|||
ret = lfs_file_tell(_fs->_getFS(), _file); |
|||
} |
|||
|
|||
_fs->_unlockFS(); |
|||
return ret; |
|||
} |
|||
|
|||
uint32_t File::size (void) |
|||
{ |
|||
uint32_t ret = 0; |
|||
_fs->_lockFS(); |
|||
|
|||
if (!this->_is_dir) |
|||
{ |
|||
ret = lfs_file_size(_fs->_getFS(), _file); |
|||
} |
|||
|
|||
_fs->_unlockFS(); |
|||
return ret; |
|||
} |
|||
|
|||
bool File::truncate (uint32_t pos) |
|||
{ |
|||
int32_t ret=LFS_ERR_ISDIR; |
|||
_fs->_lockFS(); |
|||
if (!this->_is_dir) |
|||
{ |
|||
ret = lfs_file_truncate(_fs->_getFS(), _file, pos); |
|||
} |
|||
_fs->_unlockFS(); |
|||
return ( ret == 0 ); |
|||
} |
|||
|
|||
bool File::truncate (void) |
|||
{ |
|||
int32_t ret=LFS_ERR_ISDIR; |
|||
uint32_t pos; |
|||
_fs->_lockFS(); |
|||
if (!this->_is_dir) |
|||
{ |
|||
pos = lfs_file_tell(_fs->_getFS(), _file); |
|||
ret = lfs_file_truncate(_fs->_getFS(), _file, pos); |
|||
} |
|||
_fs->_unlockFS(); |
|||
return ( ret == 0 ); |
|||
} |
|||
|
|||
void File::flush (void) |
|||
{ |
|||
_fs->_lockFS(); |
|||
|
|||
if (!this->_is_dir) |
|||
{ |
|||
lfs_file_sync(_fs->_getFS(), _file); |
|||
} |
|||
|
|||
_fs->_unlockFS(); |
|||
return; |
|||
} |
|||
|
|||
void File::close (void) |
|||
{ |
|||
_fs->_lockFS(); |
|||
this->_close(); |
|||
_fs->_unlockFS(); |
|||
} |
|||
|
|||
void File::_close(void) |
|||
{ |
|||
if ( this->isOpen() ) |
|||
{ |
|||
if ( this->_is_dir ) |
|||
{ |
|||
lfs_dir_close(_fs->_getFS(), _dir); |
|||
free(_dir); |
|||
_dir = NULL; |
|||
|
|||
if ( this->_dir_path ) free(_dir_path); |
|||
_dir_path = NULL; |
|||
} |
|||
else |
|||
{ |
|||
lfs_file_close(this->_fs->_getFS(), _file); |
|||
free(_file); |
|||
_file = NULL; |
|||
} |
|||
} |
|||
} |
|||
|
|||
File::operator bool (void) |
|||
{ |
|||
return isOpen(); |
|||
} |
|||
|
|||
bool File::isOpen(void) |
|||
{ |
|||
return (_file != NULL) || (_dir != NULL); |
|||
} |
|||
|
|||
// WARNING -- although marked as `const`, the values pointed
|
|||
// to may change. For example, if the same File
|
|||
// object has `open()` called with a different
|
|||
// file or directory name, this same pointer will
|
|||
// suddenly (unexpectedly?) have different values.
|
|||
char const* File::name (void) |
|||
{ |
|||
return this->_name; |
|||
} |
|||
|
|||
bool File::isDirectory (void) |
|||
{ |
|||
return this->_is_dir; |
|||
} |
|||
|
|||
File File::openNextFile (uint8_t mode) |
|||
{ |
|||
_fs->_lockFS(); |
|||
|
|||
File ret(*_fs); |
|||
if (this->_is_dir) |
|||
{ |
|||
struct lfs_info info; |
|||
int rc; |
|||
|
|||
// lfs_dir_read returns 0 when reaching end of directory, 1 if found an entry
|
|||
// Skip the "." and ".." entries ...
|
|||
do |
|||
{ |
|||
rc = lfs_dir_read(_fs->_getFS(), _dir, &info); |
|||
} while ( rc == 1 && (!strcmp(".", info.name) || !strcmp("..", info.name)) ); |
|||
|
|||
if ( rc == 1 ) |
|||
{ |
|||
// string cat name with current folder
|
|||
char filepath[strlen(_dir_path) + 1 + strlen(info.name) + 1]; // potential for significant stack usage
|
|||
strcpy(filepath, _dir_path); |
|||
if ( !(_dir_path[0] == '/' && _dir_path[1] == 0) ) strcat(filepath, "/"); // only add '/' if cwd is not root
|
|||
strcat(filepath, info.name); |
|||
|
|||
(void)ret._open(filepath, mode); // return value is ignored ... caller is expected to check isOpened()
|
|||
} |
|||
else if ( rc < 0 ) |
|||
{ |
|||
PRINT_LFS_ERR(rc); |
|||
} |
|||
} |
|||
_fs->_unlockFS(); |
|||
return ret; |
|||
} |
|||
|
|||
void File::rewindDirectory (void) |
|||
{ |
|||
_fs->_lockFS(); |
|||
if (this->_is_dir) |
|||
{ |
|||
lfs_dir_rewind(_fs->_getFS(), _dir); |
|||
} |
|||
_fs->_unlockFS(); |
|||
} |
|||
|
|||
@ -0,0 +1,108 @@ |
|||
/*
|
|||
* The MIT License (MIT) |
|||
* |
|||
* Copyright (c) 2019 Ha Thach for Adafruit Industries |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to deal |
|||
* in the Software without restriction, including without limitation the rights |
|||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
* copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
* THE SOFTWARE. |
|||
*/ |
|||
|
|||
#ifndef ADAFRUIT_LITTLEFS_FILE_H_ |
|||
#define ADAFRUIT_LITTLEFS_FILE_H_ |
|||
|
|||
// Forward declaration
|
|||
class Adafruit_LittleFS; |
|||
|
|||
namespace Adafruit_LittleFS_Namespace |
|||
{ |
|||
|
|||
// avoid conflict with other FileSystem FILE_READ/FILE_WRITE
|
|||
enum |
|||
{ |
|||
FILE_O_READ = 0, |
|||
FILE_O_WRITE = 1, |
|||
}; |
|||
|
|||
class File : public Stream |
|||
{ |
|||
public: |
|||
File (Adafruit_LittleFS &fs); |
|||
File (char const *filename, uint8_t mode, Adafruit_LittleFS &fs); |
|||
|
|||
public: |
|||
|
|||
bool open (char const *filename, uint8_t mode); |
|||
|
|||
//------------- Stream API -------------//
|
|||
virtual size_t write (uint8_t ch); |
|||
virtual size_t write (uint8_t const *buf, size_t size); |
|||
size_t write(const char *str) { |
|||
if (str == NULL) return 0; |
|||
return write((const uint8_t *)str, strlen(str)); |
|||
} |
|||
size_t write(const char *buffer, size_t size) { |
|||
return write((const uint8_t *)buffer, size); |
|||
} |
|||
|
|||
virtual int read (void); |
|||
int read (void *buf, uint16_t nbyte); |
|||
|
|||
virtual int peek (void); |
|||
virtual int available (void); |
|||
virtual void flush (void); |
|||
|
|||
bool seek (uint32_t pos); |
|||
uint32_t position (void); |
|||
uint32_t size (void); |
|||
|
|||
bool truncate (uint32_t pos); |
|||
bool truncate (void); |
|||
|
|||
void close (void); |
|||
|
|||
operator bool (void); |
|||
|
|||
bool isOpen(void); |
|||
char const* name (void); |
|||
|
|||
bool isDirectory (void); |
|||
File openNextFile (uint8_t mode = FILE_O_READ); |
|||
void rewindDirectory (void); |
|||
|
|||
private: |
|||
Adafruit_LittleFS* _fs; |
|||
|
|||
bool _is_dir; |
|||
|
|||
union { |
|||
lfs_file_t* _file; |
|||
lfs_dir_t* _dir; |
|||
}; |
|||
|
|||
char* _dir_path; |
|||
char _name[LFS_NAME_MAX+1]; |
|||
|
|||
bool _open(char const *filepath, uint8_t mode); |
|||
bool _open_file(char const *filepath, uint8_t mode); |
|||
bool _open_dir (char const *filepath); |
|||
void _close(void); |
|||
}; |
|||
|
|||
} |
|||
|
|||
#endif /* ADAFRUIT_LITTLEFS_FILE_H_ */ |
|||
@ -0,0 +1,24 @@ |
|||
Copyright (c) 2017, Arm Limited. All rights reserved. |
|||
|
|||
Redistribution and use in source and binary forms, with or without modification, |
|||
are permitted provided that the following conditions are met: |
|||
|
|||
- Redistributions of source code must retain the above copyright notice, this |
|||
list of conditions and the following disclaimer. |
|||
- Redistributions in binary form must reproduce the above copyright notice, this |
|||
list of conditions and the following disclaimer in the documentation and/or |
|||
other materials provided with the distribution. |
|||
- Neither the name of ARM nor the names of its contributors may be used to |
|||
endorse or promote products derived from this software without specific prior |
|||
written permission. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
|||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR |
|||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
|||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
@ -0,0 +1,177 @@ |
|||
## The little filesystem |
|||
|
|||
A little fail-safe filesystem designed for embedded systems. |
|||
|
|||
``` |
|||
| | | .---._____ |
|||
.-----. | | |
|||
--|o |---| littlefs | |
|||
--| |---| | |
|||
'-----' '----------' |
|||
| | | |
|||
``` |
|||
|
|||
**Bounded RAM/ROM** - The littlefs is designed to work with a limited amount |
|||
of memory. Recursion is avoided and dynamic memory is limited to configurable |
|||
buffers that can be provided statically. |
|||
|
|||
**Power-loss resilient** - The littlefs is designed for systems that may have |
|||
random power failures. The littlefs has strong copy-on-write guarantees and |
|||
storage on disk is always kept in a valid state. |
|||
|
|||
**Wear leveling** - Since the most common form of embedded storage is erodible |
|||
flash memories, littlefs provides a form of dynamic wear leveling for systems |
|||
that can not fit a full flash translation layer. |
|||
|
|||
## Example |
|||
|
|||
Here's a simple example that updates a file named `boot_count` every time |
|||
main runs. The program can be interrupted at any time without losing track |
|||
of how many times it has been booted and without corrupting the filesystem: |
|||
|
|||
``` c |
|||
#include "lfs.h" |
|||
|
|||
// variables used by the filesystem |
|||
lfs_t lfs; |
|||
lfs_file_t file; |
|||
|
|||
// configuration of the filesystem is provided by this struct |
|||
const struct lfs_config cfg = { |
|||
// block device operations |
|||
.read = user_provided_block_device_read, |
|||
.prog = user_provided_block_device_prog, |
|||
.erase = user_provided_block_device_erase, |
|||
.sync = user_provided_block_device_sync, |
|||
|
|||
// block device configuration |
|||
.read_size = 16, |
|||
.prog_size = 16, |
|||
.block_size = 4096, |
|||
.block_count = 128, |
|||
.lookahead = 128, |
|||
}; |
|||
|
|||
// entry point |
|||
int main(void) { |
|||
// mount the filesystem |
|||
int err = lfs_mount(&lfs, &cfg); |
|||
|
|||
// reformat if we can't mount the filesystem |
|||
// this should only happen on the first boot |
|||
if (err) { |
|||
lfs_format(&lfs, &cfg); |
|||
lfs_mount(&lfs, &cfg); |
|||
} |
|||
|
|||
// read current count |
|||
uint32_t boot_count = 0; |
|||
lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT); |
|||
lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count)); |
|||
|
|||
// update boot count |
|||
boot_count += 1; |
|||
lfs_file_rewind(&lfs, &file); |
|||
lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count)); |
|||
|
|||
// remember the storage is not updated until the file is closed successfully |
|||
lfs_file_close(&lfs, &file); |
|||
|
|||
// release any resources we were using |
|||
lfs_unmount(&lfs); |
|||
|
|||
// print the boot count |
|||
printf("boot_count: %d\n", boot_count); |
|||
} |
|||
``` |
|||
|
|||
## Usage |
|||
|
|||
Detailed documentation (or at least as much detail as is currently available) |
|||
can be found in the comments in [lfs.h](lfs.h). |
|||
|
|||
As you may have noticed, littlefs takes in a configuration structure that |
|||
defines how the filesystem operates. The configuration struct provides the |
|||
filesystem with the block device operations and dimensions, tweakable |
|||
parameters that tradeoff memory usage for performance, and optional |
|||
static buffers if the user wants to avoid dynamic memory. |
|||
|
|||
The state of the littlefs is stored in the `lfs_t` type which is left up |
|||
to the user to allocate, allowing multiple filesystems to be in use |
|||
simultaneously. With the `lfs_t` and configuration struct, a user can |
|||
format a block device or mount the filesystem. |
|||
|
|||
Once mounted, the littlefs provides a full set of POSIX-like file and |
|||
directory functions, with the deviation that the allocation of filesystem |
|||
structures must be provided by the user. |
|||
|
|||
All POSIX operations, such as remove and rename, are atomic, even in event |
|||
of power-loss. Additionally, no file updates are actually committed to the |
|||
filesystem until sync or close is called on the file. |
|||
|
|||
## Other notes |
|||
|
|||
All littlefs have the potential to return a negative error code. The errors |
|||
can be either one of those found in the `enum lfs_error` in [lfs.h](lfs.h), |
|||
or an error returned by the user's block device operations. |
|||
|
|||
In the configuration struct, the `prog` and `erase` function provided by the |
|||
user may return a `LFS_ERR_CORRUPT` error if the implementation already can |
|||
detect corrupt blocks. However, the wear leveling does not depend on the return |
|||
code of these functions, instead all data is read back and checked for |
|||
integrity. |
|||
|
|||
If your storage caches writes, make sure that the provided `sync` function |
|||
flushes all the data to memory and ensures that the next read fetches the data |
|||
from memory, otherwise data integrity can not be guaranteed. If the `write` |
|||
function does not perform caching, and therefore each `read` or `write` call |
|||
hits the memory, the `sync` function can simply return 0. |
|||
|
|||
## Reference material |
|||
|
|||
[DESIGN.md](DESIGN.md) - DESIGN.md contains a fully detailed dive into how |
|||
littlefs actually works. I would encourage you to read it since the |
|||
solutions and tradeoffs at work here are quite interesting. |
|||
|
|||
[SPEC.md](SPEC.md) - SPEC.md contains the on-disk specification of littlefs |
|||
with all the nitty-gritty details. Can be useful for developing tooling. |
|||
|
|||
## Testing |
|||
|
|||
The littlefs comes with a test suite designed to run on a PC using the |
|||
[emulated block device](emubd/lfs_emubd.h) found in the emubd directory. |
|||
The tests assume a Linux environment and can be started with make: |
|||
|
|||
``` bash |
|||
make test |
|||
``` |
|||
|
|||
## License |
|||
|
|||
The littlefs is provided under the [BSD-3-Clause](https://spdx.org/licenses/BSD-3-Clause.html) |
|||
license. See [LICENSE.md](LICENSE.md) for more information. Contributions to |
|||
this project are accepted under the same license. |
|||
|
|||
Individual files contain the following tag instead of the full license text. |
|||
|
|||
SPDX-License-Identifier: BSD-3-Clause |
|||
|
|||
This enables machine processing of license information based on the SPDX |
|||
License Identifiers that are here available: http://spdx.org/licenses/ |
|||
|
|||
## Related projects |
|||
|
|||
[Mbed OS](https://github.com/ARMmbed/mbed-os/tree/master/features/filesystem/littlefs) - |
|||
The easiest way to get started with littlefs is to jump into [Mbed](https://os.mbed.com/), |
|||
which already has block device drivers for most forms of embedded storage. The |
|||
littlefs is available in Mbed OS as the [LittleFileSystem](https://os.mbed.com/docs/latest/reference/littlefilesystem.html) |
|||
class. |
|||
|
|||
[littlefs-fuse](https://github.com/geky/littlefs-fuse) - A [FUSE](https://github.com/libfuse/libfuse) |
|||
wrapper for littlefs. The project allows you to mount littlefs directly on a |
|||
Linux machine. Can be useful for debugging littlefs if you have an SD card |
|||
handy. |
|||
|
|||
[littlefs-js](https://github.com/geky/littlefs-js) - A javascript wrapper for |
|||
littlefs. I'm not sure why you would want this, but it is handy for demos. |
|||
You can see it in action [here](http://littlefs.geky.net/demo.html). |
|||
File diff suppressed because it is too large
@ -0,0 +1,501 @@ |
|||
/*
|
|||
* The little filesystem |
|||
* |
|||
* Copyright (c) 2017, Arm Limited. All rights reserved. |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
#ifndef LFS_H |
|||
#define LFS_H |
|||
|
|||
#include <stdint.h> |
|||
#include <stdbool.h> |
|||
|
|||
#ifdef __cplusplus |
|||
extern "C" |
|||
{ |
|||
#endif |
|||
|
|||
|
|||
/// Version info ///
|
|||
|
|||
// Software library version
|
|||
// Major (top-nibble), incremented on backwards incompatible changes
|
|||
// Minor (bottom-nibble), incremented on feature additions
|
|||
#define LFS_VERSION 0x00010007 |
|||
#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16)) |
|||
#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0)) |
|||
|
|||
// Version of On-disk data structures
|
|||
// Major (top-nibble), incremented on backwards incompatible changes
|
|||
// Minor (bottom-nibble), incremented on feature additions
|
|||
#define LFS_DISK_VERSION 0x00010001 |
|||
#define LFS_DISK_VERSION_MAJOR (0xffff & (LFS_DISK_VERSION >> 16)) |
|||
#define LFS_DISK_VERSION_MINOR (0xffff & (LFS_DISK_VERSION >> 0)) |
|||
|
|||
|
|||
/// Definitions ///
|
|||
|
|||
// Type definitions
|
|||
typedef uint32_t lfs_size_t; |
|||
typedef uint32_t lfs_off_t; |
|||
|
|||
typedef int32_t lfs_ssize_t; |
|||
typedef int32_t lfs_soff_t; |
|||
|
|||
typedef uint32_t lfs_block_t; |
|||
|
|||
// Max name size in bytes
|
|||
#ifndef LFS_NAME_MAX |
|||
#define LFS_NAME_MAX 255 |
|||
#endif |
|||
|
|||
// Max file size in bytes
|
|||
#ifndef LFS_FILE_MAX |
|||
#define LFS_FILE_MAX 2147483647 |
|||
#endif |
|||
|
|||
// Possible error codes, these are negative to allow
|
|||
// valid positive return values
|
|||
enum lfs_error { |
|||
LFS_ERR_OK = 0, // No error
|
|||
LFS_ERR_IO = -5, // Error during device operation
|
|||
LFS_ERR_CORRUPT = -52, // Corrupted
|
|||
LFS_ERR_NOENT = -2, // No directory entry
|
|||
LFS_ERR_EXIST = -17, // Entry already exists
|
|||
LFS_ERR_NOTDIR = -20, // Entry is not a dir
|
|||
LFS_ERR_ISDIR = -21, // Entry is a dir
|
|||
LFS_ERR_NOTEMPTY = -39, // Dir is not empty
|
|||
LFS_ERR_BADF = -9, // Bad file number
|
|||
LFS_ERR_FBIG = -27, // File too large
|
|||
LFS_ERR_INVAL = -22, // Invalid parameter
|
|||
LFS_ERR_NOSPC = -28, // No space left on device
|
|||
LFS_ERR_NOMEM = -12, // No more memory available
|
|||
}; |
|||
|
|||
// File types
|
|||
enum lfs_type { |
|||
LFS_TYPE_REG = 0x11, |
|||
LFS_TYPE_DIR = 0x22, |
|||
LFS_TYPE_SUPERBLOCK = 0x2e, |
|||
}; |
|||
|
|||
// File open flags
|
|||
enum lfs_open_flags { |
|||
// open flags
|
|||
LFS_O_RDONLY = 1, // Open a file as read only
|
|||
LFS_O_WRONLY = 2, // Open a file as write only
|
|||
LFS_O_RDWR = 3, // Open a file as read and write
|
|||
LFS_O_CREAT = 0x0100, // Create a file if it does not exist
|
|||
LFS_O_EXCL = 0x0200, // Fail if a file already exists
|
|||
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
|
|||
LFS_O_APPEND = 0x0800, // Move to end of file on every write
|
|||
|
|||
// internally used flags
|
|||
LFS_F_DIRTY = 0x10000, // File does not match storage
|
|||
LFS_F_WRITING = 0x20000, // File has been written since last flush
|
|||
LFS_F_READING = 0x40000, // File has been read since last flush
|
|||
LFS_F_ERRED = 0x80000, // An error occured during write
|
|||
}; |
|||
|
|||
// File seek flags
|
|||
enum lfs_whence_flags { |
|||
LFS_SEEK_SET = 0, // Seek relative to an absolute position
|
|||
LFS_SEEK_CUR = 1, // Seek relative to the current file position
|
|||
LFS_SEEK_END = 2, // Seek relative to the end of the file
|
|||
}; |
|||
|
|||
|
|||
// Configuration provided during initialization of the littlefs
|
|||
struct lfs_config { |
|||
// Opaque user provided context that can be used to pass
|
|||
// information to the block device operations
|
|||
void *context; |
|||
|
|||
// Read a region in a block. Negative error codes are propogated
|
|||
// to the user.
|
|||
int (*read)(const struct lfs_config *c, lfs_block_t block, |
|||
lfs_off_t off, void *buffer, lfs_size_t size); |
|||
|
|||
// Program a region in a block. The block must have previously
|
|||
// been erased. Negative error codes are propogated to the user.
|
|||
// May return LFS_ERR_CORRUPT if the block should be considered bad.
|
|||
int (*prog)(const struct lfs_config *c, lfs_block_t block, |
|||
lfs_off_t off, const void *buffer, lfs_size_t size); |
|||
|
|||
// Erase a block. A block must be erased before being programmed.
|
|||
// The state of an erased block is undefined. Negative error codes
|
|||
// are propogated to the user.
|
|||
// May return LFS_ERR_CORRUPT if the block should be considered bad.
|
|||
int (*erase)(const struct lfs_config *c, lfs_block_t block); |
|||
|
|||
// Sync the state of the underlying block device. Negative error codes
|
|||
// are propogated to the user.
|
|||
int (*sync)(const struct lfs_config *c); |
|||
|
|||
// Minimum size of a block read. This determines the size of read buffers.
|
|||
// This may be larger than the physical read size to improve performance
|
|||
// by caching more of the block device.
|
|||
lfs_size_t read_size; |
|||
|
|||
// Minimum size of a block program. This determines the size of program
|
|||
// buffers. This may be larger than the physical program size to improve
|
|||
// performance by caching more of the block device.
|
|||
// Must be a multiple of the read size.
|
|||
lfs_size_t prog_size; |
|||
|
|||
// Size of an erasable block. This does not impact ram consumption and
|
|||
// may be larger than the physical erase size. However, this should be
|
|||
// kept small as each file currently takes up an entire block.
|
|||
// Must be a multiple of the program size.
|
|||
lfs_size_t block_size; |
|||
|
|||
// Number of erasable blocks on the device.
|
|||
lfs_size_t block_count; |
|||
|
|||
// Number of blocks to lookahead during block allocation. A larger
|
|||
// lookahead reduces the number of passes required to allocate a block.
|
|||
// The lookahead buffer requires only 1 bit per block so it can be quite
|
|||
// large with little ram impact. Should be a multiple of 32.
|
|||
lfs_size_t lookahead; |
|||
|
|||
// Optional, statically allocated read buffer. Must be read sized.
|
|||
void *read_buffer; |
|||
|
|||
// Optional, statically allocated program buffer. Must be program sized.
|
|||
void *prog_buffer; |
|||
|
|||
// Optional, statically allocated lookahead buffer. Must be 1 bit per
|
|||
// lookahead block.
|
|||
void *lookahead_buffer; |
|||
|
|||
// Optional, statically allocated buffer for files. Must be program sized.
|
|||
// If enabled, only one file may be opened at a time.
|
|||
void *file_buffer; |
|||
}; |
|||
|
|||
// Optional configuration provided during lfs_file_opencfg
|
|||
struct lfs_file_config { |
|||
// Optional, statically allocated buffer for files. Must be program sized.
|
|||
// If NULL, malloc will be used by default.
|
|||
void *buffer; |
|||
}; |
|||
|
|||
// File info structure
|
|||
struct lfs_info { |
|||
// Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR
|
|||
uint8_t type; |
|||
|
|||
// Size of the file, only valid for REG files
|
|||
lfs_size_t size; |
|||
|
|||
// Name of the file stored as a null-terminated string
|
|||
char name[LFS_NAME_MAX+1]; |
|||
}; |
|||
|
|||
|
|||
/// littlefs data structures ///
|
|||
typedef struct lfs_entry { |
|||
lfs_off_t off; |
|||
|
|||
struct lfs_disk_entry { |
|||
uint8_t type; |
|||
uint8_t elen; |
|||
uint8_t alen; |
|||
uint8_t nlen; |
|||
union { |
|||
struct { |
|||
lfs_block_t head; |
|||
lfs_size_t size; |
|||
} file; |
|||
lfs_block_t dir[2]; |
|||
} u; |
|||
} d; |
|||
} lfs_entry_t; |
|||
|
|||
typedef struct lfs_cache { |
|||
lfs_block_t block; |
|||
lfs_off_t off; |
|||
uint8_t *buffer; |
|||
} lfs_cache_t; |
|||
|
|||
typedef struct lfs_file { |
|||
struct lfs_file *next; |
|||
lfs_block_t pair[2]; |
|||
lfs_off_t poff; |
|||
|
|||
lfs_block_t head; |
|||
lfs_size_t size; |
|||
|
|||
const struct lfs_file_config *cfg; |
|||
uint32_t flags; |
|||
lfs_off_t pos; |
|||
lfs_block_t block; |
|||
lfs_off_t off; |
|||
lfs_cache_t cache; |
|||
} lfs_file_t; |
|||
|
|||
typedef struct lfs_dir { |
|||
struct lfs_dir *next; |
|||
lfs_block_t pair[2]; |
|||
lfs_off_t off; |
|||
|
|||
lfs_block_t head[2]; |
|||
lfs_off_t pos; |
|||
|
|||
struct lfs_disk_dir { |
|||
uint32_t rev; |
|||
lfs_size_t size; |
|||
lfs_block_t tail[2]; |
|||
} d; |
|||
} lfs_dir_t; |
|||
|
|||
typedef struct lfs_superblock { |
|||
lfs_off_t off; |
|||
|
|||
struct lfs_disk_superblock { |
|||
uint8_t type; |
|||
uint8_t elen; |
|||
uint8_t alen; |
|||
uint8_t nlen; |
|||
lfs_block_t root[2]; |
|||
uint32_t block_size; |
|||
uint32_t block_count; |
|||
uint32_t version; |
|||
char magic[8]; |
|||
} d; |
|||
} lfs_superblock_t; |
|||
|
|||
typedef struct lfs_free { |
|||
lfs_block_t off; |
|||
lfs_block_t size; |
|||
lfs_block_t i; |
|||
lfs_block_t ack; |
|||
uint32_t *buffer; |
|||
} lfs_free_t; |
|||
|
|||
// The littlefs type
|
|||
typedef struct lfs { |
|||
const struct lfs_config *cfg; |
|||
|
|||
lfs_block_t root[2]; |
|||
lfs_file_t *files; |
|||
lfs_dir_t *dirs; |
|||
|
|||
lfs_cache_t rcache; |
|||
lfs_cache_t pcache; |
|||
|
|||
lfs_free_t free; |
|||
bool deorphaned; |
|||
bool moving; |
|||
} lfs_t; |
|||
|
|||
|
|||
/// Filesystem functions ///
|
|||
|
|||
// Format a block device with the littlefs
|
|||
//
|
|||
// Requires a littlefs object and config struct. This clobbers the littlefs
|
|||
// object, and does not leave the filesystem mounted. The config struct must
|
|||
// be zeroed for defaults and backwards compatibility.
|
|||
//
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_format(lfs_t *lfs, const struct lfs_config *config); |
|||
|
|||
// Mounts a littlefs
|
|||
//
|
|||
// Requires a littlefs object and config struct. Multiple filesystems
|
|||
// may be mounted simultaneously with multiple littlefs objects. Both
|
|||
// lfs and config must be allocated while mounted. The config struct must
|
|||
// be zeroed for defaults and backwards compatibility.
|
|||
//
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_mount(lfs_t *lfs, const struct lfs_config *config); |
|||
|
|||
// Unmounts a littlefs
|
|||
//
|
|||
// Does nothing besides releasing any allocated resources.
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_unmount(lfs_t *lfs); |
|||
|
|||
/// General operations ///
|
|||
|
|||
// Removes a file or directory
|
|||
//
|
|||
// If removing a directory, the directory must be empty.
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_remove(lfs_t *lfs, const char *path); |
|||
|
|||
// Rename or move a file or directory
|
|||
//
|
|||
// If the destination exists, it must match the source in type.
|
|||
// If the destination is a directory, the directory must be empty.
|
|||
//
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath); |
|||
|
|||
// Find info about a file or directory
|
|||
//
|
|||
// Fills out the info structure, based on the specified file or directory.
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info); |
|||
|
|||
|
|||
/// File operations ///
|
|||
|
|||
// Open a file
|
|||
//
|
|||
// The mode that the file is opened in is determined by the flags, which
|
|||
// are values from the enum lfs_open_flags that are bitwise-ored together.
|
|||
//
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_file_open(lfs_t *lfs, lfs_file_t *file, |
|||
const char *path, int flags); |
|||
|
|||
// Open a file with extra configuration
|
|||
//
|
|||
// The mode that the file is opened in is determined by the flags, which
|
|||
// are values from the enum lfs_open_flags that are bitwise-ored together.
|
|||
//
|
|||
// The config struct provides additional config options per file as described
|
|||
// above. The config struct must be allocated while the file is open, and the
|
|||
// config struct must be zeroed for defaults and backwards compatibility.
|
|||
//
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_file_opencfg(lfs_t *lfs, lfs_file_t *file, |
|||
const char *path, int flags, |
|||
const struct lfs_file_config *config); |
|||
|
|||
// Close a file
|
|||
//
|
|||
// Any pending writes are written out to storage as though
|
|||
// sync had been called and releases any allocated resources.
|
|||
//
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_file_close(lfs_t *lfs, lfs_file_t *file); |
|||
|
|||
// Synchronize a file on storage
|
|||
//
|
|||
// Any pending writes are written out to storage.
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_file_sync(lfs_t *lfs, lfs_file_t *file); |
|||
|
|||
// Read data from file
|
|||
//
|
|||
// Takes a buffer and size indicating where to store the read data.
|
|||
// Returns the number of bytes read, or a negative error code on failure.
|
|||
lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, |
|||
void *buffer, lfs_size_t size); |
|||
|
|||
// Write data to file
|
|||
//
|
|||
// Takes a buffer and size indicating the data to write. The file will not
|
|||
// actually be updated on the storage until either sync or close is called.
|
|||
//
|
|||
// Returns the number of bytes written, or a negative error code on failure.
|
|||
lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, |
|||
const void *buffer, lfs_size_t size); |
|||
|
|||
// Change the position of the file
|
|||
//
|
|||
// The change in position is determined by the offset and whence flag.
|
|||
// Returns the old position of the file, or a negative error code on failure.
|
|||
lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, |
|||
lfs_soff_t off, int whence); |
|||
|
|||
// Truncates the size of the file to the specified size
|
|||
//
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size); |
|||
|
|||
// Return the position of the file
|
|||
//
|
|||
// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR)
|
|||
// Returns the position of the file, or a negative error code on failure.
|
|||
lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file); |
|||
|
|||
// Change the position of the file to the beginning of the file
|
|||
//
|
|||
// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR)
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file); |
|||
|
|||
// Return the size of the file
|
|||
//
|
|||
// Similar to lfs_file_seek(lfs, file, 0, LFS_SEEK_END)
|
|||
// Returns the size of the file, or a negative error code on failure.
|
|||
lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file); |
|||
|
|||
|
|||
/// Directory operations ///
|
|||
|
|||
// Create a directory
|
|||
//
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_mkdir(lfs_t *lfs, const char *path); |
|||
|
|||
// Open a directory
|
|||
//
|
|||
// Once open a directory can be used with read to iterate over files.
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path); |
|||
|
|||
// Close a directory
|
|||
//
|
|||
// Releases any allocated resources.
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir); |
|||
|
|||
// Read an entry in the directory
|
|||
//
|
|||
// Fills out the info structure, based on the specified file or directory.
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info); |
|||
|
|||
// Change the position of the directory
|
|||
//
|
|||
// The new off must be a value previous returned from tell and specifies
|
|||
// an absolute offset in the directory seek.
|
|||
//
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off); |
|||
|
|||
// Return the position of the directory
|
|||
//
|
|||
// The returned offset is only meant to be consumed by seek and may not make
|
|||
// sense, but does indicate the current position in the directory iteration.
|
|||
//
|
|||
// Returns the position of the directory, or a negative error code on failure.
|
|||
lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir); |
|||
|
|||
// Change the position of the directory to the beginning of the directory
|
|||
//
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); |
|||
|
|||
|
|||
/// Miscellaneous littlefs specific operations ///
|
|||
|
|||
// Traverse through all blocks in use by the filesystem
|
|||
//
|
|||
// The provided callback will be called with each block address that is
|
|||
// currently in use by the filesystem. This can be used to determine which
|
|||
// blocks are in use or how much of the storage is available.
|
|||
//
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); |
|||
|
|||
// Prunes any recoverable errors that may have occured in the filesystem
|
|||
//
|
|||
// Not needed to be called by user unless an operation is interrupted
|
|||
// but the filesystem is still mounted. This is already called on first
|
|||
// allocation.
|
|||
//
|
|||
// Returns a negative error code on failure.
|
|||
int lfs_deorphan(lfs_t *lfs); |
|||
|
|||
|
|||
#ifdef __cplusplus |
|||
} /* extern "C" */ |
|||
#endif |
|||
|
|||
#endif |
|||
@ -0,0 +1,31 @@ |
|||
/*
|
|||
* lfs util functions |
|||
* |
|||
* Copyright (c) 2017, Arm Limited. All rights reserved. |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
#include "lfs_util.h" |
|||
|
|||
// Only compile if user does not provide custom config
|
|||
#ifndef LFS_CONFIG |
|||
|
|||
|
|||
// Software CRC implementation with small lookup table
|
|||
void lfs_crc(uint32_t *restrict crc, const void *buffer, size_t size) { |
|||
static const uint32_t rtable[16] = { |
|||
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, |
|||
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, |
|||
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, |
|||
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c, |
|||
}; |
|||
|
|||
const uint8_t *data = buffer; |
|||
|
|||
for (size_t i = 0; i < size; i++) { |
|||
*crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 0)) & 0xf]; |
|||
*crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 4)) & 0xf]; |
|||
} |
|||
} |
|||
|
|||
|
|||
#endif |
|||
@ -0,0 +1,197 @@ |
|||
/*
|
|||
* lfs utility functions |
|||
* |
|||
* Copyright (c) 2017, Arm Limited. All rights reserved. |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
#ifndef LFS_UTIL_H |
|||
#define LFS_UTIL_H |
|||
|
|||
// Users can override lfs_util.h with their own configuration by defining
|
|||
// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h).
|
|||
//
|
|||
// If LFS_CONFIG is used, none of the default utils will be emitted and must be
|
|||
// provided by the config file. To start I would suggest copying lfs_util.h and
|
|||
// modifying as needed.
|
|||
#ifdef LFS_CONFIG |
|||
#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x) |
|||
#define LFS_STRINGIZE2(x) #x |
|||
#include LFS_STRINGIZE(LFS_CONFIG) |
|||
#else |
|||
|
|||
// System includes
|
|||
#include <stdint.h> |
|||
#include <stdbool.h> |
|||
#include <string.h> |
|||
|
|||
#ifndef LFS_NO_MALLOC |
|||
#include <stdlib.h> |
|||
#endif |
|||
#ifndef LFS_NO_ASSERT |
|||
#include <assert.h> |
|||
#endif |
|||
|
|||
#if !CFG_DEBUG |
|||
#define LFS_NO_DEBUG |
|||
#define LFS_NO_WARN |
|||
#define LFS_NO_ERROR |
|||
#endif |
|||
|
|||
#if !defined(LFS_NO_DEBUG) || !defined(LFS_NO_WARN) || !defined(LFS_NO_ERROR) |
|||
#include <stdio.h> |
|||
#endif |
|||
|
|||
#ifdef __cplusplus |
|||
extern "C" |
|||
{ |
|||
#endif |
|||
|
|||
|
|||
// Macros, may be replaced by system specific wrappers. Arguments to these
|
|||
// macros must not have side-effects as the macros can be removed for a smaller
|
|||
// code footprint
|
|||
|
|||
// Logging functions
|
|||
#ifndef LFS_NO_DEBUG |
|||
#define LFS_DEBUG(fmt, ...) \ |
|||
printf("lfs debug:%d: " fmt "\n", __LINE__, __VA_ARGS__) |
|||
#else |
|||
#define LFS_DEBUG(fmt, ...) |
|||
#endif |
|||
|
|||
#ifndef LFS_NO_WARN |
|||
#define LFS_WARN(fmt, ...) \ |
|||
printf("lfs warn:%d: " fmt "\n", __LINE__, __VA_ARGS__) |
|||
#else |
|||
#define LFS_WARN(fmt, ...) |
|||
#endif |
|||
|
|||
#ifndef LFS_NO_ERROR |
|||
#define LFS_ERROR(fmt, ...) \ |
|||
printf("lfs error:%d: " fmt "\n", __LINE__, __VA_ARGS__) |
|||
#else |
|||
#define LFS_ERROR(fmt, ...) |
|||
#endif |
|||
|
|||
// Runtime assertions
|
|||
#ifndef LFS_NO_ASSERT |
|||
#define LFS_ASSERT(test) assert(test) |
|||
#else |
|||
#define LFS_ASSERT(test) |
|||
#endif |
|||
|
|||
|
|||
// Builtin functions, these may be replaced by more efficient
|
|||
// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more
|
|||
// expensive basic C implementation for debugging purposes
|
|||
|
|||
// Min/max functions for unsigned 32-bit numbers
|
|||
static inline uint32_t lfs_max(uint32_t a, uint32_t b) { |
|||
return (a > b) ? a : b; |
|||
} |
|||
|
|||
static inline uint32_t lfs_min(uint32_t a, uint32_t b) { |
|||
return (a < b) ? a : b; |
|||
} |
|||
|
|||
// Find the next smallest power of 2 less than or equal to a
|
|||
static inline uint32_t lfs_npw2(uint32_t a) { |
|||
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) |
|||
return 32 - __builtin_clz(a-1); |
|||
#else |
|||
uint32_t r = 0; |
|||
uint32_t s; |
|||
a -= 1; |
|||
s = (a > 0xffff) << 4; a >>= s; r |= s; |
|||
s = (a > 0xff ) << 3; a >>= s; r |= s; |
|||
s = (a > 0xf ) << 2; a >>= s; r |= s; |
|||
s = (a > 0x3 ) << 1; a >>= s; r |= s; |
|||
return (r | (a >> 1)) + 1; |
|||
#endif |
|||
} |
|||
|
|||
// Count the number of trailing binary zeros in a
|
|||
// lfs_ctz(0) may be undefined
|
|||
static inline uint32_t lfs_ctz(uint32_t a) { |
|||
#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__) |
|||
return __builtin_ctz(a); |
|||
#else |
|||
return lfs_npw2((a & -a) + 1) - 1; |
|||
#endif |
|||
} |
|||
|
|||
// Count the number of binary ones in a
|
|||
static inline uint32_t lfs_popc(uint32_t a) { |
|||
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) |
|||
return __builtin_popcount(a); |
|||
#else |
|||
a = a - ((a >> 1) & 0x55555555); |
|||
a = (a & 0x33333333) + ((a >> 2) & 0x33333333); |
|||
return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24; |
|||
#endif |
|||
} |
|||
|
|||
// Find the sequence comparison of a and b, this is the distance
|
|||
// between a and b ignoring overflow
|
|||
static inline int lfs_scmp(uint32_t a, uint32_t b) { |
|||
return (int)(unsigned)(a - b); |
|||
} |
|||
|
|||
// Convert from 32-bit little-endian to native order
|
|||
static inline uint32_t lfs_fromle32(uint32_t a) { |
|||
#if !defined(LFS_NO_INTRINSICS) && ( \ |
|||
(defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ |
|||
(defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ |
|||
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) |
|||
return a; |
|||
#elif !defined(LFS_NO_INTRINSICS) && ( \ |
|||
(defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ |
|||
(defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ |
|||
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) |
|||
return __builtin_bswap32(a); |
|||
#else |
|||
return (((uint8_t*)&a)[0] << 0) | |
|||
(((uint8_t*)&a)[1] << 8) | |
|||
(((uint8_t*)&a)[2] << 16) | |
|||
(((uint8_t*)&a)[3] << 24); |
|||
#endif |
|||
} |
|||
|
|||
// Convert to 32-bit little-endian from native order
|
|||
static inline uint32_t lfs_tole32(uint32_t a) { |
|||
return lfs_fromle32(a); |
|||
} |
|||
|
|||
// Calculate CRC-32 with polynomial = 0x04c11db7
|
|||
void lfs_crc(uint32_t *crc, const void *buffer, size_t size); |
|||
|
|||
// Allocate memory, only used if buffers are not provided to littlefs
|
|||
static inline void *lfs_malloc(size_t size) { |
|||
#ifndef LFS_NO_MALLOC |
|||
//extern void *pvPortMalloc( size_t xWantedSize );
|
|||
//return pvPortMalloc(size);
|
|||
return malloc(size); |
|||
#else |
|||
(void)size; |
|||
return NULL; |
|||
#endif |
|||
} |
|||
|
|||
// Deallocate memory, only used if buffers are not provided to littlefs
|
|||
static inline void lfs_free(void *p) { |
|||
#ifndef LFS_NO_MALLOC |
|||
//extern void vPortFree( void *pv );
|
|||
//vPortFree(p);
|
|||
free(p); |
|||
#else |
|||
(void)p; |
|||
#endif |
|||
} |
|||
|
|||
|
|||
#ifdef __cplusplus |
|||
} /* extern "C" */ |
|||
#endif |
|||
|
|||
#endif |
|||
#endif |
|||
@ -0,0 +1,10 @@ |
|||
Import("env") |
|||
|
|||
# Make custom HEX from ELF |
|||
env.AddPostAction( |
|||
"$BUILD_DIR/${PROGNAME}.elf", |
|||
env.VerboseAction(" ".join([ |
|||
"$OBJCOPY", "-O", "ihex", "-R", ".eeprom", |
|||
'"$BUILD_DIR/${PROGNAME}.elf"', '"$BUILD_DIR/${PROGNAME}.hex"' |
|||
]), "Building $BUILD_DIR/${PROGNAME}.hex") |
|||
) |
|||
@ -69,3 +69,18 @@ lib_deps = |
|||
extends = arduino_base |
|||
build_flags = ${arduino_base.build_flags} |
|||
-D RP2040_PLATFORM |
|||
|
|||
; ----------------- STM32 ---------------------- |
|||
|
|||
[stm32_base] |
|||
extends = arduino_base |
|||
platform = platformio/[email protected] |
|||
platform_packages = platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32/archive/2.10.1.zip |
|||
extra_scripts = post:arch/stm32/build_hex.py |
|||
build_flags = ${arduino_base.build_flags} |
|||
-D STM32_PLATFORM |
|||
-I src/helpers/stm32 |
|||
build_src_filter = ${arduino_base.build_src_filter} |
|||
+<helpers/stm32> |
|||
lib_deps = ${arduino_base.lib_deps} |
|||
file://arch/stm32/Adafruit_LittleFS_stm32 |
|||
@ -0,0 +1,17 @@ |
|||
#pragma once |
|||
|
|||
#include <RadioLib.h> |
|||
|
|||
#define SX126X_IRQ_HEADER_VALID 0b0000010000 // 4 4 valid LoRa header received
|
|||
#define SX126X_IRQ_PREAMBLE_DETECTED 0x04 |
|||
|
|||
class CustomSTM32WLx : public STM32WLx { |
|||
public: |
|||
CustomSTM32WLx(STM32WLx_Module *mod) : STM32WLx(mod) { } |
|||
|
|||
bool isReceiving() { |
|||
uint16_t irq = getIrqFlags(); |
|||
bool detected = (irq & SX126X_IRQ_HEADER_VALID) || (irq & SX126X_IRQ_PREAMBLE_DETECTED); |
|||
return detected; |
|||
} |
|||
}; |
|||
@ -0,0 +1,30 @@ |
|||
#pragma once |
|||
|
|||
#include "CustomSTM32WLx.h" |
|||
#include "RadioLibWrappers.h" |
|||
#include <math.h> |
|||
|
|||
class CustomSTM32WLxWrapper : public RadioLibWrapper { |
|||
public: |
|||
CustomSTM32WLxWrapper(CustomSTM32WLx& radio, mesh::MainBoard& board) : RadioLibWrapper(radio, board) { } |
|||
bool isReceiving() override { |
|||
if (((CustomSTM32WLx *)_radio)->isReceiving()) return true; |
|||
|
|||
idle(); // put sx126x into standby
|
|||
// do some basic CAD (blocks for ~12780 micros (on SF 10)!)
|
|||
bool activity = (((CustomSTM32WLx *)_radio)->scanChannel() == RADIOLIB_LORA_DETECTED); |
|||
if (activity) { |
|||
startRecv(); |
|||
} else { |
|||
idle(); |
|||
} |
|||
return activity; |
|||
} |
|||
float getLastRSSI() const override { return ((CustomSTM32WLx *)_radio)->getRSSI(); } |
|||
float getLastSNR() const override { return ((CustomSTM32WLx *)_radio)->getSNR(); } |
|||
|
|||
float packetScore(float snr, int packet_len) override { |
|||
int sf = ((CustomSTM32WLx *)_radio)->spreadingFactor; |
|||
return packetScoreInt(snr, sf, packet_len); |
|||
} |
|||
}; |
|||
@ -0,0 +1,139 @@ |
|||
/*
|
|||
* The MIT License (MIT) |
|||
* |
|||
* Copyright (c) 2019 hathach for Adafruit Industries |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to deal |
|||
* in the Software without restriction, including without limitation the rights |
|||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
* copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
* THE SOFTWARE. |
|||
*/ |
|||
|
|||
#include <Arduino.h> |
|||
#include "InternalFileSystem.h" |
|||
|
|||
//--------------------------------------------------------------------+
|
|||
// LFS Disk IO
|
|||
//--------------------------------------------------------------------+
|
|||
static int _internal_flash_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) |
|||
{ |
|||
if (!buffer || !size) return LFS_ERR_INVAL; |
|||
lfs_block_t address = LFS_FLASH_ADDR_BASE + (block * FLASH_PAGE_SIZE + off); |
|||
memcpy(buffer, (void *)address, size); |
|||
return LFS_ERR_OK; |
|||
} |
|||
|
|||
// Program a region in a block. The block must have previously
|
|||
// been erased. Negative error codes are propogated to the user.
|
|||
// May return LFS_ERR_CORRUPT if the block should be considered bad.
|
|||
static int _internal_flash_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) |
|||
{ |
|||
HAL_StatusTypeDef hal_rc = HAL_OK; |
|||
lfs_block_t addr = LFS_FLASH_ADDR_BASE + (block * FLASH_PAGE_SIZE + off); |
|||
uint64_t *bufp = (uint64_t *) buffer; |
|||
|
|||
if (HAL_FLASH_Unlock() != HAL_OK) return LFS_ERR_IO; |
|||
for (uint32_t i = 0; i < size/8; i++) { |
|||
if ((addr < LFS_FLASH_ADDR_BASE) || (addr > FLASH_END_ADDR)) { |
|||
HAL_FLASH_Lock(); |
|||
return LFS_ERR_INVAL; |
|||
} |
|||
hal_rc = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr, *bufp); |
|||
addr += 8; |
|||
bufp += 1; |
|||
} |
|||
if (HAL_FLASH_Lock() != HAL_OK) return LFS_ERR_IO; |
|||
|
|||
return hal_rc == HAL_OK ? LFS_ERR_OK : LFS_ERR_IO; |
|||
} |
|||
|
|||
// Erase a block. A block must be erased before being programmed.
|
|||
// The state of an erased block is undefined. Negative error codes
|
|||
// are propogated to the user.
|
|||
// May return LFS_ERR_CORRUPT if the block should be considered bad.
|
|||
static int _internal_flash_erase(const struct lfs_config *c, lfs_block_t block) |
|||
{ |
|||
HAL_StatusTypeDef hal_rc; |
|||
lfs_block_t address = LFS_FLASH_ADDR_BASE + (block * FLASH_PAGE_SIZE); |
|||
uint32_t pageError = 0; |
|||
FLASH_EraseInitTypeDef EraseInitStruct = { |
|||
.TypeErase = FLASH_TYPEERASE_PAGES, |
|||
.Page = 0, |
|||
.NbPages = 1 |
|||
}; |
|||
|
|||
if ((address < LFS_FLASH_ADDR_BASE) || (address > FLASH_END_ADDR)) { |
|||
return LFS_ERR_INVAL; |
|||
} |
|||
EraseInitStruct.Page = (address - FLASH_BASE) / FLASH_PAGE_SIZE; |
|||
HAL_FLASH_Unlock(); |
|||
hal_rc = HAL_FLASHEx_Erase(&EraseInitStruct, &pageError); |
|||
HAL_FLASH_Lock(); |
|||
|
|||
return hal_rc == HAL_OK ? LFS_ERR_OK : LFS_ERR_IO; |
|||
} |
|||
|
|||
// Sync the state of the underlying block device. Negative error codes
|
|||
// are propogated to the user.
|
|||
static int _internal_flash_sync(const struct lfs_config *c) |
|||
{ |
|||
return LFS_ERR_OK; // don't need sync
|
|||
} |
|||
|
|||
struct lfs_config _InternalFSConfig = { |
|||
.context = NULL, |
|||
.read = _internal_flash_read, |
|||
.prog = _internal_flash_prog, |
|||
.erase = _internal_flash_erase, |
|||
.sync = _internal_flash_sync, |
|||
|
|||
.read_size = LFS_BLOCK_SIZE, |
|||
.prog_size = LFS_BLOCK_SIZE, |
|||
.block_size = LFS_BLOCK_SIZE, |
|||
.block_count = LFS_FLASH_TOTAL_SIZE / LFS_BLOCK_SIZE, |
|||
.lookahead = 128, |
|||
|
|||
.read_buffer = NULL, |
|||
.prog_buffer = NULL, |
|||
.lookahead_buffer = NULL, |
|||
.file_buffer = NULL |
|||
}; |
|||
|
|||
InternalFileSystem InternalFS; |
|||
|
|||
//--------------------------------------------------------------------+
|
|||
//
|
|||
//--------------------------------------------------------------------+
|
|||
|
|||
InternalFileSystem::InternalFileSystem(void) |
|||
: Adafruit_LittleFS(&_InternalFSConfig) |
|||
{ |
|||
|
|||
} |
|||
|
|||
bool InternalFileSystem::begin(void) |
|||
{ |
|||
// failed to mount, erase all sector then format and mount again
|
|||
if ( !Adafruit_LittleFS::begin() ) |
|||
{ |
|||
// lfs format
|
|||
this->format(); |
|||
// mount again if still failed, give up
|
|||
if ( !Adafruit_LittleFS::begin() ) return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
/*
|
|||
* The MIT License (MIT) |
|||
* |
|||
* Copyright (c) 2019 hathach for Adafruit Industries |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to deal |
|||
* in the Software without restriction, including without limitation the rights |
|||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
* copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
* THE SOFTWARE. |
|||
*/ |
|||
|
|||
#ifndef INTERNALFILESYSTEM_H_ |
|||
#define INTERNALFILESYSTEM_H_ |
|||
|
|||
#include "Adafruit_LittleFS.h" |
|||
|
|||
#ifndef LFS_FLASH_TOTAL_SIZE /* Flash size can be configured in platformio.ini */ |
|||
#define LFS_FLASH_TOTAL_SIZE (16 * 2048) /* defaults to 32k flash */ |
|||
#endif |
|||
#define LFS_BLOCK_SIZE (2048) |
|||
#define LFS_FLASH_ADDR_BASE (FLASH_END_ADDR - LFS_FLASH_TOTAL_SIZE + 1) |
|||
|
|||
class InternalFileSystem : public Adafruit_LittleFS |
|||
{ |
|||
public: |
|||
InternalFileSystem(void); |
|||
|
|||
// overwrite to also perform low level format (sector erase of whole flash region)
|
|||
bool begin(void); |
|||
}; |
|||
|
|||
extern InternalFileSystem InternalFS; |
|||
|
|||
#endif /* INTERNALFILESYSTEM_H_ */ |
|||
|
|||
@ -0,0 +1,29 @@ |
|||
#pragma once |
|||
|
|||
#include <MeshCore.h> |
|||
#include <Arduino.h> |
|||
|
|||
class STM32Board : public mesh::MainBoard { |
|||
protected: |
|||
uint8_t startup_reason; |
|||
|
|||
public: |
|||
void begin() { |
|||
startup_reason = BD_STARTUP_NORMAL; |
|||
} |
|||
|
|||
uint8_t getStartupReason() const override { return startup_reason; } |
|||
|
|||
uint16_t getBattMilliVolts() override { |
|||
return 0; // not supported
|
|||
} |
|||
|
|||
const char* getManufacturerName() const override { |
|||
return "Generic STM32"; |
|||
} |
|||
|
|||
void reboot() override { |
|||
} |
|||
|
|||
bool startOTAUpdate(const char* id, char reply[]) override { return false; }; |
|||
}; |
|||
@ -0,0 +1,31 @@ |
|||
[lora_e5] |
|||
extends = stm32_base |
|||
board = lora_e5_dev_board |
|||
board_upload.maximum_size = 229376 ; 32kb for FS |
|||
build_flags = ${stm32_base.build_flags} |
|||
-D RADIO_CLASS=CustomSTM32WLx |
|||
-D WRAPPER_CLASS=CustomSTM32WLxWrapper |
|||
-D SPI_INTERFACES_COUNT=0 |
|||
-I variants/wio-e5 |
|||
build_src_filter = ${stm32_base.build_src_filter} |
|||
+<../variants/wio-e5> |
|||
|
|||
[env:wio-e5-repeater] |
|||
extends = lora_e5 |
|||
build_flags = ${lora_e5.build_flags} |
|||
-D LORA_TX_POWER=20 |
|||
-D ADVERT_NAME='"WIO-E5 Repeater"' |
|||
-D ADMIN_PASSWORD='"password"' |
|||
build_src_filter = ${lora_e5.build_src_filter} |
|||
+<../examples/simple_repeater/main.cpp> |
|||
|
|||
[env:wio-e5_companion_radio_usb] |
|||
extends = lora_e5 |
|||
build_flags = ${lora_e5.build_flags} |
|||
-D LORA_TX_POWER=20 |
|||
-D MAX_CONTACTS=100 |
|||
-D MAX_GROUP_CHANNELS=8 |
|||
build_src_filter = ${lora_e5.build_src_filter} |
|||
+<../examples/companion_radio/*.cpp> |
|||
lib_deps = ${lora_e5.lib_deps} |
|||
densaugeo/base64 @ ~1.4.0 |
|||
@ -0,0 +1,69 @@ |
|||
#include <Arduino.h> |
|||
#include "target.h" |
|||
#include <helpers/ArduinoHelpers.h> |
|||
|
|||
STM32Board board; |
|||
|
|||
RADIO_CLASS radio = new STM32WLx_Module(); |
|||
|
|||
WRAPPER_CLASS radio_driver(radio, board); |
|||
|
|||
static const uint32_t rfswitch_pins[] = {PA4, PA5, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC}; |
|||
static const Module::RfSwitchMode_t rfswitch_table[] = { |
|||
{STM32WLx::MODE_IDLE, {LOW, LOW}}, |
|||
{STM32WLx::MODE_RX, {HIGH, LOW}}, |
|||
{STM32WLx::MODE_TX_HP, {LOW, HIGH}}, // for LoRa-E5 mini
|
|||
// {STM32WLx::MODE_TX_LP, {HIGH, HIGH}}, // for LoRa-E5-LE mini
|
|||
END_OF_MODE_TABLE, |
|||
}; |
|||
|
|||
VolatileRTCClock rtc_clock; |
|||
SensorManager sensors; |
|||
|
|||
#ifndef LORA_CR |
|||
#define LORA_CR 5 |
|||
#endif |
|||
|
|||
bool radio_init() { |
|||
// rtc_clock.begin(Wire);
|
|||
|
|||
// #ifdef SX126X_DIO3_TCXO_VOLTAGE
|
|||
// float tcxo = SX126X_DIO3_TCXO_VOLTAGE;
|
|||
// #else
|
|||
// float tcxo = 1.6f;
|
|||
// #endif
|
|||
|
|||
radio.setRfSwitchTable(rfswitch_pins, rfswitch_table); |
|||
|
|||
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, 1.7, 0); |
|||
|
|||
if (status != RADIOLIB_ERR_NONE) { |
|||
Serial.print("ERROR: radio init failed: "); |
|||
Serial.println(status); |
|||
return false; // fail
|
|||
} |
|||
|
|||
radio.setCRC(1); |
|||
|
|||
return true; // success
|
|||
} |
|||
|
|||
uint32_t radio_get_rng_seed() { |
|||
return radio.random(0x7FFFFFFF); |
|||
} |
|||
|
|||
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr) { |
|||
radio.setFrequency(freq); |
|||
radio.setSpreadingFactor(sf); |
|||
radio.setBandwidth(bw); |
|||
radio.setCodingRate(cr); |
|||
} |
|||
|
|||
void radio_set_tx_power(uint8_t dbm) { |
|||
radio.setOutputPower(dbm); |
|||
} |
|||
|
|||
mesh::LocalIdentity radio_new_identity() { |
|||
RadioNoiseListener rng(radio); |
|||
return mesh::LocalIdentity(&rng); // create new random identity
|
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
#pragma once |
|||
|
|||
#define RADIOLIB_STATIC_ONLY 1 |
|||
#include <RadioLib.h> |
|||
#include <helpers/RadioLibWrappers.h> |
|||
#include <helpers/stm32/STM32Board.h> |
|||
#include <helpers/CustomSTM32WLxWrapper.h> |
|||
#include <helpers/ArduinoHelpers.h> |
|||
#include <helpers/SensorManager.h> |
|||
|
|||
extern STM32Board board; |
|||
extern WRAPPER_CLASS radio_driver; |
|||
extern VolatileRTCClock rtc_clock; |
|||
extern SensorManager sensors; |
|||
|
|||
bool radio_init(); |
|||
uint32_t radio_get_rng_seed(); |
|||
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr); |
|||
void radio_set_tx_power(uint8_t dbm); |
|||
mesh::LocalIdentity radio_new_identity(); |
|||
@ -0,0 +1,10 @@ |
|||
#pragma once |
|||
|
|||
// UART Definitions
|
|||
#ifndef SERIAL_UART_INSTANCE |
|||
#define SERIAL_UART_INSTANCE 101 |
|||
#endif |
|||
|
|||
#include <variant_LORA_E5_MINI.h> |
|||
|
|||
#undef RNG |
|||
Loading…
Reference in new issue