mirror of
https://github.com/meshcore-dev/MeshCore.git
synced 2026-04-20 22:13:47 +00:00
Merge branch 'dev' of https://github.com/ripplebiz/MeshCore into dev
This commit is contained in:
commit
0c3c162835
33 changed files with 4989 additions and 23 deletions
1
arch/stm32/Adafruit_LittleFS_stm32/README.md
Normal file
1
arch/stm32/Adafruit_LittleFS_stm32/README.md
Normal file
|
|
@ -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)
|
||||
10
arch/stm32/Adafruit_LittleFS_stm32/library.properties
Normal file
10
arch/stm32/Adafruit_LittleFS_stm32/library.properties
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
name=Adafruit Little File System Libraries
|
||||
version=0.11.0
|
||||
author=Adafruit
|
||||
maintainer=Adafruit <info@adafruit.com>
|
||||
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
|
||||
273
arch/stm32/Adafruit_LittleFS_stm32/src/Adafruit_LittleFS.cpp
Normal file
273
arch/stm32/Adafruit_LittleFS_stm32/src/Adafruit_LittleFS.cpp
Normal file
|
|
@ -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
|
||||
102
arch/stm32/Adafruit_LittleFS_stm32/src/Adafruit_LittleFS.h
Normal file
102
arch/stm32/Adafruit_LittleFS_stm32/src/Adafruit_LittleFS.h
Normal file
|
|
@ -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();
|
||||
}
|
||||
|
||||
108
arch/stm32/Adafruit_LittleFS_stm32/src/Adafruit_LittleFS_File.h
Normal file
108
arch/stm32/Adafruit_LittleFS_stm32/src/Adafruit_LittleFS_File.h
Normal file
|
|
@ -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_ */
|
||||
24
arch/stm32/Adafruit_LittleFS_stm32/src/littlefs/LICENSE.md
Normal file
24
arch/stm32/Adafruit_LittleFS_stm32/src/littlefs/LICENSE.md
Normal file
|
|
@ -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.
|
||||
177
arch/stm32/Adafruit_LittleFS_stm32/src/littlefs/README.md
Normal file
177
arch/stm32/Adafruit_LittleFS_stm32/src/littlefs/README.md
Normal file
|
|
@ -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).
|
||||
2585
arch/stm32/Adafruit_LittleFS_stm32/src/littlefs/lfs.c
Normal file
2585
arch/stm32/Adafruit_LittleFS_stm32/src/littlefs/lfs.c
Normal file
File diff suppressed because it is too large
Load diff
501
arch/stm32/Adafruit_LittleFS_stm32/src/littlefs/lfs.h
Normal file
501
arch/stm32/Adafruit_LittleFS_stm32/src/littlefs/lfs.h
Normal file
|
|
@ -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
|
||||
31
arch/stm32/Adafruit_LittleFS_stm32/src/littlefs/lfs_util.c
Normal file
31
arch/stm32/Adafruit_LittleFS_stm32/src/littlefs/lfs_util.c
Normal file
|
|
@ -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
|
||||
197
arch/stm32/Adafruit_LittleFS_stm32/src/littlefs/lfs_util.h
Normal file
197
arch/stm32/Adafruit_LittleFS_stm32/src/littlefs/lfs_util.h
Normal file
|
|
@ -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
|
||||
10
arch/stm32/build_hex.py
Normal file
10
arch/stm32/build_hex.py
Normal file
|
|
@ -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")
|
||||
)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#include <Arduino.h> // needed for PlatformIO
|
||||
#include <Mesh.h>
|
||||
|
||||
#if defined(NRF52_PLATFORM)
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
#include <InternalFileSystem.h>
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
#include <LittleFS.h>
|
||||
|
|
@ -291,7 +291,7 @@ class MyMesh : public BaseChatMesh {
|
|||
}
|
||||
|
||||
void saveContacts() {
|
||||
#if defined(NRF52_PLATFORM)
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
File file = _fs->open("/contacts3", FILE_O_WRITE);
|
||||
if (file) { file.seek(0); file.truncate(); }
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
|
|
@ -356,7 +356,7 @@ class MyMesh : public BaseChatMesh {
|
|||
}
|
||||
|
||||
void saveChannels() {
|
||||
#if defined(NRF52_PLATFORM)
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
File file = _fs->open("/channels2", FILE_O_WRITE);
|
||||
if (file) { file.seek(0); file.truncate(); }
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
|
|
@ -413,7 +413,7 @@ class MyMesh : public BaseChatMesh {
|
|||
mesh::Utils::toHex(fname, key, key_len);
|
||||
sprintf(path, "/bl/%s", fname);
|
||||
|
||||
#if defined(NRF52_PLATFORM)
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
File f = _fs->open(path, FILE_O_WRITE);
|
||||
if (f) { f.seek(0); f.truncate(); }
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
|
|
@ -866,7 +866,7 @@ public:
|
|||
|
||||
BaseChatMesh::begin();
|
||||
|
||||
#if defined(NRF52_PLATFORM)
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
_identity_store = new IdentityStore(fs, "");
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
_identity_store = new IdentityStore(fs, "/identity");
|
||||
|
|
@ -933,7 +933,7 @@ public:
|
|||
}
|
||||
|
||||
void savePrefs() {
|
||||
#if defined(NRF52_PLATFORM)
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
File file = _fs->open("/new_prefs", FILE_O_WRITE);
|
||||
if (file) { file.seek(0); file.truncate(); }
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
|
|
@ -1621,6 +1621,9 @@ public:
|
|||
#include <helpers/ArduinoSerialInterface.h>
|
||||
ArduinoSerialInterface serial_interface;
|
||||
#endif
|
||||
#elif defined(STM32_PLATFORM)
|
||||
#include <helpers/ArduinoSerialInterface.h>
|
||||
ArduinoSerialInterface serial_interface;
|
||||
#else
|
||||
#error "need to define a serial interface"
|
||||
#endif
|
||||
|
|
@ -1654,7 +1657,7 @@ void setup() {
|
|||
|
||||
fast_rng.begin(radio_get_rng_seed());
|
||||
|
||||
#if defined(NRF52_PLATFORM)
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
InternalFS.begin();
|
||||
the_mesh.begin(InternalFS,
|
||||
#ifdef HAS_UI
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#include <Arduino.h> // needed for PlatformIO
|
||||
#include <Mesh.h>
|
||||
|
||||
#if defined(NRF52_PLATFORM)
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
#include <InternalFileSystem.h>
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
#include <LittleFS.h>
|
||||
|
|
@ -230,7 +230,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
|
|||
}
|
||||
|
||||
File openAppend(const char* fname) {
|
||||
#if defined(NRF52_PLATFORM)
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
return _fs->open(fname, FILE_O_WRITE);
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
return _fs->open(fname, "a");
|
||||
|
|
@ -597,7 +597,7 @@ public:
|
|||
}
|
||||
|
||||
bool formatFileSystem() override {
|
||||
#if defined(NRF52_PLATFORM)
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
return InternalFS.format();
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
return LittleFS.format();
|
||||
|
|
@ -675,7 +675,8 @@ public:
|
|||
mesh::Utils::toHex(hex, neighbour->id.pub_key, 4);
|
||||
|
||||
// add next neighbour
|
||||
sprintf(dp, "%s:%d:%d", hex, neighbour->advert_timestamp, neighbour->snr);
|
||||
uint32_t secs_ago = getRTCClock()->getCurrentTime() - neighbour->heard_timestamp;
|
||||
sprintf(dp, "%s:%d:%d", hex, secs_ago, neighbour->snr);
|
||||
while (*dp) dp++; // find end of string
|
||||
}
|
||||
#endif
|
||||
|
|
@ -738,7 +739,7 @@ void setup() {
|
|||
fast_rng.begin(radio_get_rng_seed());
|
||||
|
||||
FILESYSTEM* fs;
|
||||
#if defined(NRF52_PLATFORM)
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
InternalFS.begin();
|
||||
fs = &InternalFS;
|
||||
IdentityStore store(InternalFS, "");
|
||||
|
|
|
|||
|
|
@ -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/ststm32@19.1.0
|
||||
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
|
||||
|
|
@ -73,7 +73,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
|
|||
}
|
||||
|
||||
void CommonCLI::savePrefs(FILESYSTEM* fs) {
|
||||
#if defined(NRF52_PLATFORM)
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
File file = fs->open("/com_prefs", FILE_O_WRITE);
|
||||
if (file) { file.seek(0); file.truncate(); }
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
|
|
|
|||
17
src/helpers/CustomSTM32WLx.h
Normal file
17
src/helpers/CustomSTM32WLx.h
Normal file
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
30
src/helpers/CustomSTM32WLxWrapper.h
Normal file
30
src/helpers/CustomSTM32WLxWrapper.h
Normal file
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
@ -46,7 +46,7 @@ bool IdentityStore::save(const char *name, const mesh::LocalIdentity& id) {
|
|||
char filename[40];
|
||||
sprintf(filename, "%s/%s.id", _dir, name);
|
||||
|
||||
#if defined(NRF52_PLATFORM)
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
File file = _fs->open(filename, FILE_O_WRITE);
|
||||
if (file) { file.seek(0); file.truncate(); }
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
|
|
@ -68,7 +68,7 @@ bool IdentityStore::save(const char *name, const mesh::LocalIdentity& id, const
|
|||
char filename[40];
|
||||
sprintf(filename, "%s/%s.id", _dir, name);
|
||||
|
||||
#if defined(NRF52_PLATFORM)
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
File file = _fs->open(filename, FILE_O_WRITE);
|
||||
if (file) { file.seek(0); file.truncate(); }
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#if defined(ESP32) || defined(RP2040_PLATFORM)
|
||||
#include <FS.h>
|
||||
#define FILESYSTEM fs::FS
|
||||
#elif defined(NRF52_PLATFORM)
|
||||
#elif defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
#include <Adafruit_LittleFS.h>
|
||||
#define FILESYSTEM Adafruit_LittleFS
|
||||
|
||||
|
|
|
|||
139
src/helpers/stm32/InternalFileSystem.cpp
Normal file
139
src/helpers/stm32/InternalFileSystem.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
48
src/helpers/stm32/InternalFileSystem.h
Normal file
48
src/helpers/stm32/InternalFileSystem.h
Normal file
|
|
@ -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_ */
|
||||
|
||||
29
src/helpers/stm32/STM32Board.h
Normal file
29
src/helpers/stm32/STM32Board.h
Normal file
|
|
@ -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; };
|
||||
};
|
||||
|
|
@ -58,7 +58,7 @@ extends = T_Beam_S3_Supreme_SX1262
|
|||
build_flags =
|
||||
${T_Beam_S3_Supreme_SX1262.build_flags}
|
||||
-D MAX_CONTACTS=100
|
||||
-D MAX_GROUP_CHANNELS=1
|
||||
-D MAX_GROUP_CHANNELS=8
|
||||
-D BLE_PIN_CODE=123456
|
||||
-D BLE_DEBUG_LOGGING=1
|
||||
; -D ENABLE_PRIVATE_KEY_IMPORT=1
|
||||
|
|
@ -70,4 +70,4 @@ build_src_filter = ${T_Beam_S3_Supreme_SX1262.build_src_filter}
|
|||
+<../examples/companion_radio>
|
||||
lib_deps =
|
||||
${T_Beam_S3_Supreme_SX1262.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ lib_deps =
|
|||
adafruit/Adafruit GFX Library @ ^1.12.1
|
||||
${Heltec_t114.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
stevemarple/MicroNMEA @ ^2.0.6
|
||||
|
||||
[env:Heltec_t114_companion_radio_usb]
|
||||
extends = Heltec_t114
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include <Arduino.h>
|
||||
#include "target.h"
|
||||
#include <helpers/ArduinoHelpers.h>
|
||||
#include <helpers/sensors/MicroNMEALocationProvider.h>
|
||||
|
||||
T114Board board;
|
||||
|
||||
|
|
@ -10,7 +11,8 @@ WRAPPER_CLASS radio_driver(radio, board);
|
|||
|
||||
VolatileRTCClock fallback_clock;
|
||||
AutoDiscoverRTCClock rtc_clock(fallback_clock);
|
||||
SensorManager sensors;
|
||||
MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1);
|
||||
T114SensorManager sensors = T114SensorManager(nmea);
|
||||
|
||||
#ifndef LORA_CR
|
||||
#define LORA_CR 5
|
||||
|
|
@ -68,3 +70,90 @@ mesh::LocalIdentity radio_new_identity() {
|
|||
RadioNoiseListener rng(radio);
|
||||
return mesh::LocalIdentity(&rng); // create new random identity
|
||||
}
|
||||
|
||||
void T114SensorManager::start_gps() {
|
||||
if (!gps_active) {
|
||||
gps_active = true;
|
||||
_location->begin();
|
||||
}
|
||||
}
|
||||
|
||||
void T114SensorManager::stop_gps() {
|
||||
if (gps_active) {
|
||||
gps_active = false;
|
||||
_location->stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool T114SensorManager::begin() {
|
||||
Serial1.begin(9600);
|
||||
|
||||
// Try to detect if GPS is physically connected to determine if we should expose the setting
|
||||
pinMode(GPS_EN, OUTPUT);
|
||||
digitalWrite(GPS_EN, HIGH); // Power on GPS
|
||||
|
||||
// Give GPS a moment to power up and send data
|
||||
delay(500);
|
||||
|
||||
// We'll consider GPS detected if we see any data on Serial1
|
||||
gps_detected = (Serial1.available() > 0);
|
||||
|
||||
if (gps_detected) {
|
||||
MESH_DEBUG_PRINTLN("GPS detected");
|
||||
digitalWrite(GPS_EN, LOW); // Power off GPS until the setting is changed
|
||||
} else {
|
||||
MESH_DEBUG_PRINTLN("No GPS detected");
|
||||
digitalWrite(GPS_EN, LOW);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool T114SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) {
|
||||
if (requester_permissions & TELEM_PERM_LOCATION) { // does requester have permission?
|
||||
telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, 0.0f);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void T114SensorManager::loop() {
|
||||
static long next_gps_update = 0;
|
||||
|
||||
_location->loop();
|
||||
|
||||
if (millis() > next_gps_update) {
|
||||
if (_location->isValid()) {
|
||||
node_lat = ((double)_location->getLatitude())/1000000.;
|
||||
node_lon = ((double)_location->getLongitude())/1000000.;
|
||||
MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon);
|
||||
}
|
||||
next_gps_update = millis() + 1000;
|
||||
}
|
||||
}
|
||||
|
||||
int T114SensorManager::getNumSettings() const {
|
||||
return gps_detected ? 1 : 0; // only show GPS setting if GPS is detected
|
||||
}
|
||||
|
||||
const char* T114SensorManager::getSettingName(int i) const {
|
||||
return (gps_detected && i == 0) ? "gps" : NULL;
|
||||
}
|
||||
|
||||
const char* T114SensorManager::getSettingValue(int i) const {
|
||||
if (gps_detected && i == 0) {
|
||||
return gps_active ? "1" : "0";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool T114SensorManager::setSettingValue(const char* name, const char* value) {
|
||||
if (gps_detected && strcmp(name, "gps") == 0) {
|
||||
if (strcmp(value, "0") == 0) {
|
||||
stop_gps();
|
||||
} else {
|
||||
start_gps();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false; // not supported
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,30 @@
|
|||
#include <helpers/CustomSX1262Wrapper.h>
|
||||
#include <helpers/AutoDiscoverRTCClock.h>
|
||||
#include <helpers/SensorManager.h>
|
||||
#include <helpers/sensors/LocationProvider.h>
|
||||
|
||||
class T114SensorManager : public SensorManager {
|
||||
bool gps_active = false;
|
||||
bool gps_detected = false;
|
||||
LocationProvider* _location;
|
||||
|
||||
void start_gps();
|
||||
void stop_gps();
|
||||
public:
|
||||
T114SensorManager(LocationProvider &location): _location(&location) { }
|
||||
bool begin() override;
|
||||
bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override;
|
||||
void loop() override;
|
||||
int getNumSettings() const override;
|
||||
const char* getSettingName(int i) const override;
|
||||
const char* getSettingValue(int i) const override;
|
||||
bool setSettingValue(const char* name, const char* value) override;
|
||||
};
|
||||
|
||||
extern T114Board board;
|
||||
extern WRAPPER_CLASS radio_driver;
|
||||
extern AutoDiscoverRTCClock rtc_clock;
|
||||
extern SensorManager sensors;
|
||||
extern T114SensorManager sensors;
|
||||
|
||||
bool radio_init();
|
||||
uint32_t radio_get_rng_seed();
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// UART pin definition
|
||||
|
||||
#define PIN_SERIAL1_RX (39)
|
||||
#define PIN_SERIAL1_TX (37)
|
||||
#define PIN_SERIAL1_RX (37)
|
||||
#define PIN_SERIAL1_TX (39)
|
||||
|
||||
#define PIN_SERIAL2_RX (9)
|
||||
#define PIN_SERIAL2_TX (10)
|
||||
|
|
@ -69,7 +69,7 @@
|
|||
#define LED_BUILTIN (35)
|
||||
#define PIN_LED LED_BUILTIN
|
||||
#define LED_RED LED_BUILTIN
|
||||
#define LED_BLUE LED_BUILTIN
|
||||
#define LED_BLUE (-1) // No blue led, prevents Bluefruit flashing the green LED during advertising
|
||||
#define LED_PIN LED_BUILTIN
|
||||
|
||||
#define LED_STATE_ON HIGH
|
||||
|
|
@ -112,6 +112,12 @@
|
|||
#define PIN_BUZZER (46)
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// GPS
|
||||
|
||||
#define GPS_EN (21)
|
||||
#define GPS_RESET (38)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TFT
|
||||
#define PIN_TFT_SCL (40)
|
||||
|
|
|
|||
31
variants/wio-e5/platformio.ini
Normal file
31
variants/wio-e5/platformio.ini
Normal file
|
|
@ -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
|
||||
69
variants/wio-e5/target.cpp
Normal file
69
variants/wio-e5/target.cpp
Normal file
|
|
@ -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
|
||||
}
|
||||
20
variants/wio-e5/target.h
Normal file
20
variants/wio-e5/target.h
Normal file
|
|
@ -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();
|
||||
10
variants/wio-e5/variant.h
Normal file
10
variants/wio-e5/variant.h
Normal file
|
|
@ -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…
Add table
Add a link
Reference in a new issue