mirror of
https://github.com/meshtastic/firmware.git
synced 2025-09-09 21:01:39 +00:00
try implementing littlefs for stm32
This commit is contained in:
parent
447533aae5
commit
486c56e651
@ -1,7 +1,7 @@
|
||||
[stm32_base]
|
||||
extends = arduino_base
|
||||
platform = ststm32
|
||||
platform_packages = platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32.git#ea74156acd823b6d14739f389e6cdc648f8ee36e
|
||||
platform_packages = platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32.git#2.9.0
|
||||
|
||||
build_type = release
|
||||
|
||||
@ -9,11 +9,12 @@ build_type = release
|
||||
|
||||
build_flags =
|
||||
${arduino_base.build_flags}
|
||||
-Wl,--undefined=vTaskSwitchContext
|
||||
-flto
|
||||
-Isrc/platform/stm32wl -g
|
||||
-DMESHTASTIC_MINIMIZE_BUILD
|
||||
-DMESHTASTIC_EXCLUDE_GPS
|
||||
-DDEBUG_MUTE
|
||||
; -DDEBUG_MUTE
|
||||
; -DVECT_TAB_OFFSET=0x08000000
|
||||
-DconfigUSE_CMSIS_RTOS_V2=1
|
||||
; -DSPI_MODE_0=SPI_MODE0
|
||||
@ -29,8 +30,8 @@ upload_protocol = stlink
|
||||
|
||||
lib_deps =
|
||||
${env.lib_deps}
|
||||
charlesbaynham/OSFS@^1.2.3
|
||||
jgromes/RadioLib@7.0.2
|
||||
${radiolib_base.lib_deps}
|
||||
stm32duino/STM32duino FreeRTOS@^10.3.2
|
||||
https://github.com/caveman99/Crypto.git#f61ae26a53f7a2d0ba5511625b8bf8eff3a35d5e
|
||||
|
||||
lib_ignore =
|
||||
|
@ -25,30 +25,6 @@ SPIClass SPI1(HSPI);
|
||||
|
||||
#endif // HAS_SDCARD
|
||||
|
||||
#if defined(ARCH_STM32WL)
|
||||
|
||||
uint16_t OSFS::startOfEEPROM = 1;
|
||||
uint16_t OSFS::endOfEEPROM = 2048;
|
||||
|
||||
// 3) How do I read from the medium?
|
||||
void OSFS::readNBytes(uint16_t address, unsigned int num, byte *output)
|
||||
{
|
||||
for (uint16_t i = address; i < address + num; i++) {
|
||||
*output = EEPROM.read(i);
|
||||
output++;
|
||||
}
|
||||
}
|
||||
|
||||
// 4) How to I write to the medium?
|
||||
void OSFS::writeNBytes(uint16_t address, unsigned int num, const byte *input)
|
||||
{
|
||||
for (uint16_t i = address; i < address + num; i++) {
|
||||
EEPROM.update(i, *input);
|
||||
input++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Copies a file from one location to another.
|
||||
*
|
||||
@ -58,33 +34,7 @@ void OSFS::writeNBytes(uint16_t address, unsigned int num, const byte *input)
|
||||
*/
|
||||
bool copyFile(const char *from, const char *to)
|
||||
{
|
||||
#ifdef ARCH_STM32WL
|
||||
unsigned char cbuffer[2048];
|
||||
|
||||
// Var to hold the result of actions
|
||||
OSFS::result r;
|
||||
|
||||
r = OSFS::getFile(from, cbuffer);
|
||||
|
||||
if (r == notfound) {
|
||||
LOG_ERROR("Failed to open source file %s", from);
|
||||
return false;
|
||||
} else if (r == noerr) {
|
||||
r = OSFS::newFile(to, cbuffer, true);
|
||||
if (r == noerr) {
|
||||
return true;
|
||||
} else {
|
||||
LOG_ERROR("OSFS Error %d", r);
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
LOG_ERROR("OSFS Error %d", r);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
#elif defined(FSCom)
|
||||
#ifdef FSCom
|
||||
// take SPI Lock
|
||||
concurrency::LockGuard g(spiLock);
|
||||
unsigned char cbuffer[16];
|
||||
@ -123,13 +73,7 @@ bool copyFile(const char *from, const char *to)
|
||||
*/
|
||||
bool renameFile(const char *pathFrom, const char *pathTo)
|
||||
{
|
||||
#ifdef ARCH_STM32WL
|
||||
if (copyFile(pathFrom, pathTo) && (OSFS::deleteFile(pathFrom) == OSFS::result::NO_ERROR)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
#elif defined(FSCom)
|
||||
#ifdef FSCom
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
// take SPI Lock
|
||||
|
@ -15,13 +15,11 @@
|
||||
#endif
|
||||
|
||||
#if defined(ARCH_STM32WL)
|
||||
// STM32WL series 2 Kbytes (8 rows of 256 bytes)
|
||||
#include <EEPROM.h>
|
||||
#include <OSFS.h>
|
||||
|
||||
// Useful consts
|
||||
const OSFS::result noerr = OSFS::result::NO_ERROR;
|
||||
const OSFS::result notfound = OSFS::result::FILE_NOT_FOUND;
|
||||
// STM32WL
|
||||
#include "LittleFS.h"
|
||||
#define FSCom InternalFS
|
||||
#define FSBegin() FSCom.begin()
|
||||
using namespace STM32_LittleFS_Namespace;
|
||||
#endif
|
||||
|
||||
#if defined(ARCH_RP2040)
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include <freertos/task.h>
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINO_NRF52_ADAFRUIT) || defined(ARDUINO_ARCH_RP2040)
|
||||
#if defined(ARDUINO_NRF52_ADAFRUIT) || defined(ARDUINO_ARCH_RP2040) || defined(STM32WLE5xx)
|
||||
#define HAS_FREE_RTOS
|
||||
|
||||
#include <FreeRTOS.h>
|
||||
|
207
src/platform/stm32wl/LittleFS.cpp
Normal file
207
src/platform/stm32wl/LittleFS.cpp
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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 "LittleFS.h"
|
||||
|
||||
/**********************************************************************************************************************
|
||||
* Macro definitions
|
||||
**********************************************************************************************************************/
|
||||
/** This macro is used to suppress compiler messages about a parameter not being used in a function. */
|
||||
#define PARAMETER_NOT_USED(p) (void)((p))
|
||||
|
||||
#define STM32WL_SECTOR_SIZE 0x800 /* 2K */
|
||||
#define STM32WL_SECTOR_COUNT 14
|
||||
|
||||
#define LFS_FLASH_TOTAL_SIZE (STM32WL_SECTOR_COUNT * STM32WL_SECTOR_SIZE)
|
||||
#define LFS_BLOCK_SIZE 128
|
||||
|
||||
#define LFS_FLASH_ADDR (262144 - LFS_FLASH_TOTAL_SIZE)
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// LFS Disk IO
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static inline uint32_t lba2addr(uint32_t block)
|
||||
{
|
||||
return ((uint32_t)LFS_FLASH_ADDR) + block * LFS_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
static int _internal_flash_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size)
|
||||
{
|
||||
PARAMETER_NOT_USED(c);
|
||||
if (!buffer || !size) {
|
||||
printf("%s Invalid parameter!\r\n", __func__);
|
||||
return LFS_ERR_INVAL;
|
||||
}
|
||||
|
||||
lfs_block_t address = LFS_FLASH_ADDR + (block * STM32WL_SECTOR_SIZE + off);
|
||||
// printf("+%s(Addr 0x%06lX, Len 0x%04lX)\r\n",__func__,address,size);
|
||||
// hexdump((void *)address,size);
|
||||
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)
|
||||
{
|
||||
// (void) c;
|
||||
|
||||
// uint32_t addr = lba2addr(block) + off;
|
||||
// VERIFY( flash_nrf5x_write(addr, buffer, size), -1)
|
||||
|
||||
// return 0;
|
||||
PARAMETER_NOT_USED(c);
|
||||
lfs_block_t address = LFS_FLASH_ADDR + (block * STM32WL_SECTOR_SIZE + off);
|
||||
HAL_StatusTypeDef hal_rc = HAL_OK;
|
||||
uint32_t block_count = size / 8;
|
||||
|
||||
// printf("+%s(Addr 0x%06lX, Len 0x%04lX)\r\n",__func__,address,size);
|
||||
// hexdump((void *)address,size);
|
||||
/* Program the user Flash area word by word
|
||||
(area defined by FLASH_USER_START_ADDR and FLASH_USER_END_ADDR) ***********/
|
||||
|
||||
uint64_t data_source;
|
||||
|
||||
for (uint32_t i = 0; i < block_count; i++) {
|
||||
memcpy(&data_source, buffer, 8); // load the 64-bit source from the buffer
|
||||
hal_rc = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, data_source);
|
||||
if (hal_rc == HAL_OK) {
|
||||
address += 8;
|
||||
buffer = (uint8_t *)buffer + 8;
|
||||
} else {
|
||||
/* Error occurred while writing data in Flash memory.
|
||||
User can add here some code to deal with this error */
|
||||
printf("Program Error, 0x%X\n", hal_rc);
|
||||
|
||||
} // else
|
||||
} // for
|
||||
// printf("-%s\n",__func__);
|
||||
|
||||
return hal_rc == HAL_OK ? LFS_ERR_OK : LFS_ERR_IO; // If HAL_OK, return LFS_ERR_OK, else return 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)
|
||||
{
|
||||
// (void) c;
|
||||
|
||||
// uint32_t addr = lba2addr(block);
|
||||
|
||||
// // implement as write 0xff to whole block address
|
||||
// for(int i=0; i <LFS_BLOCK_SIZE; i++)
|
||||
// {
|
||||
// flash_nrf5x_write8(addr + i, 0xFF);
|
||||
// }
|
||||
|
||||
// // flash_nrf5x_flush();
|
||||
|
||||
// return 0;
|
||||
PARAMETER_NOT_USED(c);
|
||||
lfs_block_t address = LFS_FLASH_ADDR + (block * STM32WL_SECTOR_SIZE);
|
||||
// printf("+%s(Addr 0x%06lX)\r\n",__func__,address);
|
||||
|
||||
HAL_StatusTypeDef hal_rc;
|
||||
FLASH_EraseInitTypeDef EraseInitStruct;
|
||||
uint32_t PAGEError = 0;
|
||||
|
||||
/* Fill EraseInit structure*/
|
||||
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
|
||||
EraseInitStruct.Page = (address - FLASH_BASE) / STM32WL_SECTOR_SIZE;
|
||||
EraseInitStruct.NbPages = 1;
|
||||
hal_rc = HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError);
|
||||
// if (hal_rc != HAL_OK)
|
||||
// {
|
||||
// printf("%s ERROR 0x%X\n",__func__,hal_rc);
|
||||
// }
|
||||
// else
|
||||
// printf("%s SUCCESS\n",__func__);
|
||||
|
||||
return hal_rc == HAL_OK ? LFS_ERR_OK : LFS_ERR_IO; // If HAL_OK, return LFS_ERR_OK, else return 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)
|
||||
{
|
||||
// (void) c;
|
||||
// flash_nrf5x_flush();
|
||||
// return 0;
|
||||
PARAMETER_NOT_USED(c);
|
||||
// write function performs no caching. No need for sync.
|
||||
// printf("+%s()\r\n",__func__);
|
||||
return LFS_ERR_OK;
|
||||
// return LFS_ERR_IO;
|
||||
}
|
||||
|
||||
static 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};
|
||||
|
||||
LittleFS InternalFS;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
//
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
LittleFS::LittleFS(void) : STM32_LittleFS(&_InternalFSConfig) {}
|
||||
|
||||
bool LittleFS::begin(void)
|
||||
{
|
||||
// failed to mount, erase all sector then format and mount again
|
||||
if (!STM32_LittleFS::begin()) {
|
||||
// Erase all sectors of internal flash region for Filesystem.
|
||||
for (uint32_t addr = LFS_FLASH_ADDR; addr < LFS_FLASH_ADDR + LFS_FLASH_TOTAL_SIZE; addr += STM32WL_SECTOR_SIZE) {
|
||||
_internal_flash_erase(&_InternalFSConfig, (addr - LFS_FLASH_ADDR) / STM32WL_SECTOR_SIZE);
|
||||
}
|
||||
|
||||
// lfs format
|
||||
this->format();
|
||||
|
||||
// mount again if still failed, give up
|
||||
if (!STM32_LittleFS::begin())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
41
src/platform/stm32wl/LittleFS.h
Normal file
41
src/platform/stm32wl/LittleFS.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 "STM32_LittleFS.h"
|
||||
|
||||
class LittleFS : public STM32_LittleFS
|
||||
{
|
||||
public:
|
||||
LittleFS(void);
|
||||
|
||||
// overwrite to also perform low level format (sector erase of whole flash region)
|
||||
bool begin(void);
|
||||
};
|
||||
|
||||
extern LittleFS InternalFS;
|
||||
|
||||
#endif /* INTERNALFILESYSTEM_H_ */
|
284
src/platform/stm32wl/STM32_LittleFS.cpp
Normal file
284
src/platform/stm32wl/STM32_LittleFS.cpp
Normal file
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* 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 "STM32_LittleFS.h"
|
||||
#include <Arduino.h>
|
||||
#include <string.h>
|
||||
|
||||
#define memclr(buffer, size) memset(buffer, 0, size)
|
||||
#define varclr(_var) memclr(_var, sizeof(*(_var)))
|
||||
|
||||
using namespace STM32_LittleFS_Namespace;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Implementation
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
STM32_LittleFS::STM32_LittleFS(void) : STM32_LittleFS(NULL) {}
|
||||
|
||||
STM32_LittleFS::STM32_LittleFS(struct lfs_config *cfg)
|
||||
{
|
||||
varclr(&_lfs);
|
||||
_lfs_cfg = cfg;
|
||||
_mounted = false;
|
||||
_mutex = xSemaphoreCreateMutexStatic(&this->_MutexStorageSpace);
|
||||
}
|
||||
|
||||
STM32_LittleFS::~STM32_LittleFS() {}
|
||||
|
||||
// Initialize and mount the file system
|
||||
// Return true if mounted successfully else probably corrupted.
|
||||
// User should format the disk and try again
|
||||
bool STM32_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 STM32_LittleFS::end(void)
|
||||
{
|
||||
_lockFS();
|
||||
|
||||
if (_mounted) {
|
||||
_mounted = false;
|
||||
int err = lfs_unmount(&_lfs);
|
||||
PRINT_LFS_ERR(err);
|
||||
(void)err;
|
||||
}
|
||||
|
||||
_unlockFS();
|
||||
}
|
||||
|
||||
bool STM32_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
|
||||
STM32_LittleFS_Namespace::File STM32_LittleFS::open(char const *filepath, uint8_t mode)
|
||||
{
|
||||
// No lock is required here ... the File() object will synchronize with the mutex provided
|
||||
return STM32_LittleFS_Namespace::File(filepath, mode, *this);
|
||||
}
|
||||
|
||||
// Check if file or folder exists
|
||||
bool STM32_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 STM32_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 STM32_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 STM32_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 STM32_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 STM32_LittleFS::rmdir_r(char const *filepath)
|
||||
{
|
||||
/* 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
|
107
src/platform/stm32wl/STM32_LittleFS.h
Normal file
107
src/platform/stm32wl/STM32_LittleFS.h
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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 STM32_LITTLEFS_H_
|
||||
#define STM32_LITTLEFS_H_
|
||||
|
||||
#include <Stream.h>
|
||||
|
||||
// Internal Flash uses ARM Little FileSystem
|
||||
// https://github.com/ARMmbed/littlefs
|
||||
#include "../../freertosinc.h" // tied to FreeRTOS for serialization
|
||||
#include "STM32_LittleFS_File.h"
|
||||
#include "littlefs/lfs.h"
|
||||
|
||||
class STM32_LittleFS
|
||||
{
|
||||
public:
|
||||
STM32_LittleFS(void);
|
||||
STM32_LittleFS(struct lfs_config *cfg);
|
||||
virtual ~STM32_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.
|
||||
STM32_LittleFS_Namespace::File open(char const *filename, uint8_t mode = STM32_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 /* STM32_LITTLEFS_H_ */
|
395
src/platform/stm32wl/STM32_LittleFS_File.cpp
Normal file
395
src/platform/stm32wl/STM32_LittleFS_File.cpp
Normal file
@ -0,0 +1,395 @@
|
||||
/*
|
||||
* 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 "STM32_LittleFS.h"
|
||||
#include "littlefs/lfs.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
#define rtos_malloc malloc
|
||||
#define rtos_free free
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
using namespace STM32_LittleFS_Namespace;
|
||||
|
||||
File::File(STM32_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, STM32_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 *)rtos_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
|
||||
rtos_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 *)rtos_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
|
||||
rtos_free(_dir);
|
||||
_dir = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
_is_dir = true;
|
||||
|
||||
_dir_path = (char *)rtos_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);
|
||||
rtos_free(_dir);
|
||||
_dir = NULL;
|
||||
|
||||
if (this->_dir_path)
|
||||
rtos_free(_dir_path);
|
||||
_dir_path = NULL;
|
||||
} else {
|
||||
lfs_file_close(this->_fs->_getFS(), _file);
|
||||
rtos_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();
|
||||
}
|
106
src/platform/stm32wl/STM32_LittleFS_File.h
Normal file
106
src/platform/stm32wl/STM32_LittleFS_File.h
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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 STM32_LITTLEFS_FILE_H_
|
||||
#define STM32_LITTLEFS_FILE_H_
|
||||
|
||||
// Forward declaration
|
||||
class STM32_LittleFS;
|
||||
|
||||
namespace STM32_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(STM32_LittleFS &fs);
|
||||
File(char const *filename, uint8_t mode, STM32_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:
|
||||
STM32_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);
|
||||
};
|
||||
|
||||
} // namespace STM32_LittleFS_Namespace
|
||||
|
||||
#endif /* STM32_LITTLEFS_FILE_H_ */
|
2532
src/platform/stm32wl/littlefs/lfs.c
Normal file
2532
src/platform/stm32wl/littlefs/lfs.c
Normal file
File diff suppressed because it is too large
Load Diff
476
src/platform/stm32wl/littlefs/lfs.h
Normal file
476
src/platform/stm32wl/littlefs/lfs.h
Normal file
@ -0,0 +1,476 @@
|
||||
/*
|
||||
* The little filesystem
|
||||
*
|
||||
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#ifndef LFS_H
|
||||
#define LFS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.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 0x00010006
|
||||
#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
|
||||
|
||||
// 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_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;
|
||||
} 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
|
28
src/platform/stm32wl/littlefs/lfs_util.c
Normal file
28
src/platform/stm32wl/littlefs/lfs_util.c
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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
|
201
src/platform/stm32wl/littlefs/lfs_util.h
Normal file
201
src/platform/stm32wl/littlefs/lfs_util.h
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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 <stdbool.h>
|
||||
#include <stdint.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);
|
||||
#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);
|
||||
#else
|
||||
(void)p;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
@ -1,6 +1,7 @@
|
||||
[env:rak3172]
|
||||
extends = stm32_base
|
||||
board = wiscore_rak3172
|
||||
board_upload.maximum_size = 233472 ; reserve the last 28KB for filesystem
|
||||
build_flags =
|
||||
${stm32_base.build_flags}
|
||||
-Ivariants/rak3172
|
||||
|
@ -1,6 +1,7 @@
|
||||
[env:wio-e5]
|
||||
extends = stm32_base
|
||||
board = lora_e5_dev_board
|
||||
board_upload.maximum_size = 233472 ; reserve the last 28KB for filesystem
|
||||
build_flags =
|
||||
${stm32_base.build_flags}
|
||||
-Ivariants/wio-e5
|
||||
|
Loading…
Reference in New Issue
Block a user