mirror of https://github.com/meshcore-dev/MeshCore
committed by
GitHub
79 changed files with 6144 additions and 256 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 |
extends = arduino_base |
||||
build_flags = ${arduino_base.build_flags} |
build_flags = ${arduino_base.build_flags} |
||||
-D RP2040_PLATFORM |
-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,91 @@ |
|||||
|
#include "SH1106Display.h" |
||||
|
#include <Adafruit_GrayOLED.h> |
||||
|
#include "Adafruit_SH110X.h" |
||||
|
|
||||
|
bool SH1106Display::i2c_probe(TwoWire &wire, uint8_t addr) |
||||
|
{ |
||||
|
wire.beginTransmission(addr); |
||||
|
uint8_t error = wire.endTransmission(); |
||||
|
return (error == 0); |
||||
|
} |
||||
|
|
||||
|
bool SH1106Display::begin() |
||||
|
{ |
||||
|
return display.begin(DISPLAY_ADDRESS, true) && i2c_probe(Wire, DISPLAY_ADDRESS); |
||||
|
} |
||||
|
|
||||
|
void SH1106Display::turnOn() |
||||
|
{ |
||||
|
display.oled_command(SH110X_DISPLAYON); |
||||
|
_isOn = true; |
||||
|
} |
||||
|
|
||||
|
void SH1106Display::turnOff() |
||||
|
{ |
||||
|
display.oled_command(SH110X_DISPLAYOFF); |
||||
|
_isOn = false; |
||||
|
} |
||||
|
|
||||
|
void SH1106Display::clear() |
||||
|
{ |
||||
|
display.clearDisplay(); |
||||
|
display.display(); |
||||
|
} |
||||
|
|
||||
|
void SH1106Display::startFrame(Color bkg) |
||||
|
{ |
||||
|
display.clearDisplay(); // TODO: apply 'bkg'
|
||||
|
_color = SH110X_WHITE; |
||||
|
display.setTextColor(_color); |
||||
|
display.setTextSize(1); |
||||
|
display.cp437(true); // Use full 256 char 'Code Page 437' font
|
||||
|
} |
||||
|
|
||||
|
void SH1106Display::setTextSize(int sz) |
||||
|
{ |
||||
|
display.setTextSize(sz); |
||||
|
} |
||||
|
|
||||
|
void SH1106Display::setColor(Color c) |
||||
|
{ |
||||
|
_color = (c != 0) ? SH110X_WHITE : SH110X_BLACK; |
||||
|
display.setTextColor(_color); |
||||
|
} |
||||
|
|
||||
|
void SH1106Display::setCursor(int x, int y) |
||||
|
{ |
||||
|
display.setCursor(x, y); |
||||
|
} |
||||
|
|
||||
|
void SH1106Display::print(const char *str) |
||||
|
{ |
||||
|
display.print(str); |
||||
|
} |
||||
|
|
||||
|
void SH1106Display::fillRect(int x, int y, int w, int h) |
||||
|
{ |
||||
|
display.fillRect(x, y, w, h, _color); |
||||
|
} |
||||
|
|
||||
|
void SH1106Display::drawRect(int x, int y, int w, int h) |
||||
|
{ |
||||
|
display.drawRect(x, y, w, h, _color); |
||||
|
} |
||||
|
|
||||
|
void SH1106Display::drawXbm(int x, int y, const uint8_t *bits, int w, int h) |
||||
|
{ |
||||
|
display.drawBitmap(x, y, bits, w, h, SH110X_WHITE); |
||||
|
} |
||||
|
|
||||
|
uint16_t SH1106Display::getTextWidth(const char *str) |
||||
|
{ |
||||
|
int16_t x1, y1; |
||||
|
uint16_t w, h; |
||||
|
display.getTextBounds(str, 0, 0, &x1, &y1, &w, &h); |
||||
|
return w; |
||||
|
} |
||||
|
|
||||
|
void SH1106Display::endFrame() |
||||
|
{ |
||||
|
display.display(); |
||||
|
} |
||||
@ -0,0 +1,43 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include "DisplayDriver.h" |
||||
|
#include <Wire.h> |
||||
|
#include <Adafruit_GFX.h> |
||||
|
#define SH110X_NO_SPLASH |
||||
|
#include <Adafruit_SH110X.h> |
||||
|
|
||||
|
#ifndef PIN_OLED_RESET |
||||
|
#define PIN_OLED_RESET -1 |
||||
|
#endif |
||||
|
|
||||
|
#ifndef DISPLAY_ADDRESS |
||||
|
#define DISPLAY_ADDRESS 0x3C |
||||
|
#endif |
||||
|
|
||||
|
class SH1106Display : public DisplayDriver |
||||
|
{ |
||||
|
Adafruit_SH1106G display; |
||||
|
bool _isOn; |
||||
|
uint8_t _color; |
||||
|
|
||||
|
bool i2c_probe(TwoWire &wire, uint8_t addr); |
||||
|
|
||||
|
public: |
||||
|
SH1106Display() : DisplayDriver(128, 64), display(128, 64, &Wire, PIN_OLED_RESET) { _isOn = false; } |
||||
|
bool begin(); |
||||
|
|
||||
|
bool isOn() override { return _isOn; } |
||||
|
void turnOn() override; |
||||
|
void turnOff() override; |
||||
|
void clear() override; |
||||
|
void startFrame(Color bkg = DARK) override; |
||||
|
void setTextSize(int sz) override; |
||||
|
void setColor(Color c) override; |
||||
|
void setCursor(int x, int y) override; |
||||
|
void print(const char *str) override; |
||||
|
void fillRect(int x, int y, int w, int h) override; |
||||
|
void drawRect(int x, int y, int w, int h) override; |
||||
|
void drawXbm(int x, int y, const uint8_t *bits, int w, int h) override; |
||||
|
uint16_t getTextWidth(const char *str) override; |
||||
|
void endFrame() override; |
||||
|
}; |
||||
@ -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