feat: added PicoW_Sensor code template
Credits to @ext-erich.styger that provided the template
This commit is contained in:
committed by
Sylvan Arnold
parent
b2e9eab44e
commit
6cd510e749
2173
TSM_PicoW_Sensor/McuLib/littleFS/DESIGN.md
Normal file
2173
TSM_PicoW_Sensor/McuLib/littleFS/DESIGN.md
Normal file
File diff suppressed because it is too large
Load Diff
25
TSM_PicoW_Sensor/McuLib/littleFS/LICENSE.md
Normal file
25
TSM_PicoW_Sensor/McuLib/littleFS/LICENSE.md
Normal file
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2022, The littlefs authors.
|
||||
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.
|
||||
953
TSM_PicoW_Sensor/McuLib/littleFS/McuLittleFS.c
Normal file
953
TSM_PicoW_Sensor/McuLib/littleFS/McuLittleFS.c
Normal file
@@ -0,0 +1,953 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Erich Styger
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "McuLittleFS.h"
|
||||
#include "McuLittleFSconfig.h"
|
||||
#include "McuLittleFSBlockDevice.h"
|
||||
#include "McuShell.h"
|
||||
#include "McuTimeDate.h"
|
||||
#include "littleFS/lfs.h"
|
||||
#if McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE==McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_WINBOND_W25Q128
|
||||
#include "McuW25Q128.h"
|
||||
#elif McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE==McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_MCU_FLASH
|
||||
#include "McuFlash.h"
|
||||
#endif
|
||||
|
||||
/* variables used by the file system */
|
||||
static bool McuLFS_isMounted = FALSE;
|
||||
static lfs_t McuLFS_lfs;
|
||||
|
||||
#if LITTLEFS_CONFIG_THREAD_SAFE
|
||||
static SemaphoreHandle_t fileSystemAccessMutex;
|
||||
#endif
|
||||
|
||||
bool McuLFS_IsMounted(void) {
|
||||
return McuLFS_isMounted;
|
||||
}
|
||||
|
||||
#if LITTLEFS_CONFIG_THREAD_SAFE
|
||||
static int McuLittleFS_lock(const struct lfs_config *c) {
|
||||
if (xSemaphoreTakeRecursive(fileSystemAccessMutex, portMAX_DELAY)== pdTRUE) {
|
||||
return 0; /* ok */
|
||||
}
|
||||
return -1; /* failed */
|
||||
}
|
||||
|
||||
static int McuLittleFS_unlock(const struct lfs_config *c) {
|
||||
if (xSemaphoreGiveRecursive(fileSystemAccessMutex) == pdTRUE) {
|
||||
return 0; /* ok */
|
||||
}
|
||||
return -1; /* failed */
|
||||
}
|
||||
#endif /* LITTLEFS_CONFIG_THREAD_SAFE */
|
||||
|
||||
/* configuration of the file system is provided by this struct */
|
||||
static const struct lfs_config McuLFS_cfg = {
|
||||
.context = NULL,
|
||||
/* block device operations */
|
||||
.read = McuLittleFS_block_device_read,
|
||||
.prog = McuLittleFS_block_device_prog,
|
||||
.erase = McuLittleFS_block_device_erase,
|
||||
.sync = McuLittleFS_block_device_sync,
|
||||
/* block device configuration */
|
||||
.read_size = McuLittleFS_CONFIG_FILESYSTEM_READ_BUFFER_SIZE,
|
||||
.prog_size = McuLittleFS_CONFIG_FILESYSTEM_PROG_BUFFER_SIZE,
|
||||
.block_size = McuLittleFS_CONFIG_BLOCK_SIZE,
|
||||
.block_count = McuLittleFS_CONFIG_BLOCK_COUNT,
|
||||
.cache_size = McuLittleFS_CONFIG_FILESYSTEM_CACHE_SIZE,
|
||||
.lookahead_size = McuLittleFS_CONFIG_FILESYSTEM_LOOKAHEAD_SIZE,
|
||||
.block_cycles = 500,
|
||||
#if LITTLEFS_CONFIG_THREAD_SAFE
|
||||
.lock = McuLittleFS_lock,
|
||||
.unlock = McuLittleFS_unlock,
|
||||
#endif
|
||||
};
|
||||
|
||||
const struct lfs_config *McuLFS_get_config(void) {
|
||||
return &McuLFS_cfg;
|
||||
}
|
||||
|
||||
lfs_t *McuLFS_get_lfs(void) {
|
||||
return &McuLFS_lfs;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Get a string from the file
|
||||
* (ported from FatFS function: f_gets())
|
||||
*-----------------------------------------------------------------------*/
|
||||
|
||||
char* McuLFS_gets (
|
||||
char* buff, /* Pointer to the string buffer to read */
|
||||
int len, /* Size of string buffer (characters) */
|
||||
lfs_file_t* fp /* Pointer to the file object */
|
||||
)
|
||||
{
|
||||
int n = 0;
|
||||
char c, *p = buff;
|
||||
unsigned char s[2];
|
||||
uint32_t rc;
|
||||
|
||||
while (n < len - 1) { /* Read characters until buffer gets filled */
|
||||
rc = lfs_file_read(&McuLFS_lfs,fp,s,1);
|
||||
if (rc != 1) break;
|
||||
c = s[0];
|
||||
|
||||
if (c == '\r') continue; /* Strip '\r' */
|
||||
*p++ = c;
|
||||
n++;
|
||||
if (c == '\n') break; /* Break on EOL */
|
||||
}
|
||||
*p = 0;
|
||||
return n ? buff : 0; /* When no data read (eof or error), return with error. */
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------*
|
||||
* Put a character to the file
|
||||
* (ported from FatFS)
|
||||
*-----------------------------------------------------------------------*/
|
||||
|
||||
typedef struct putbuff {
|
||||
lfs_file_t* fp ;
|
||||
int idx, nchr;
|
||||
unsigned char buf[64];
|
||||
} putbuff;
|
||||
|
||||
|
||||
static void putc_bfd (putbuff* pb, char c) {
|
||||
uint32_t bw;
|
||||
int32_t i;
|
||||
|
||||
if (c == '\n') { /* LF -> CRLF conversion */
|
||||
putc_bfd(pb, '\r');
|
||||
}
|
||||
|
||||
i = pb->idx; /* Buffer write index (-1:error) */
|
||||
if (i < 0) {
|
||||
return;
|
||||
}
|
||||
pb->buf[i++] = (unsigned char)c;
|
||||
|
||||
if (i >= (int)(sizeof pb->buf) - 3) { /* Write buffered characters to the file */
|
||||
bw = lfs_file_write(&McuLFS_lfs,pb->fp, pb->buf,(uint32_t)i);
|
||||
i = (bw == (uint32_t)i) ? 0 : -1;
|
||||
}
|
||||
pb->idx = i;
|
||||
pb->nchr++;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Put a string to the file
|
||||
* (ported from FatFS function: f_puts()) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
int McuLFS_puts (
|
||||
const char* str, /* Pointer to the string to be output */
|
||||
lfs_file_t* fp /* Pointer to the file object */
|
||||
)
|
||||
{
|
||||
putbuff pb;
|
||||
uint32_t nw;
|
||||
|
||||
pb.fp = fp; /* Initialize output buffer */
|
||||
pb.nchr = pb.idx = 0;
|
||||
|
||||
while (*str) { /* Put the string */
|
||||
putc_bfd(&pb, *str++);
|
||||
}
|
||||
nw = lfs_file_write(&McuLFS_lfs,pb.fp, pb.buf, (uint32_t)pb.idx);
|
||||
|
||||
if ( pb.idx >= 0 /* Flush buffered characters to the file */
|
||||
&& nw>=0
|
||||
&& (uint32_t)pb.idx == nw)
|
||||
{
|
||||
return pb.nchr;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t McuLFS_Format(McuShell_ConstStdIOType *io) {
|
||||
int res;
|
||||
|
||||
if (McuLFS_isMounted) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"File system is mounted, unmount it first.\r\n",io->stdErr);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"Formatting ...", io->stdOut);
|
||||
}
|
||||
res = lfs_format(&McuLFS_lfs, &McuLFS_cfg);
|
||||
if (res == LFS_ERR_OK) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)" done.\r\n", io->stdOut);
|
||||
}
|
||||
return ERR_OK;
|
||||
} else {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)" FAILED!\r\n", io->stdErr);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t McuLFS_Mount(McuShell_ConstStdIOType *io) {
|
||||
int res;
|
||||
|
||||
if (McuLFS_isMounted) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"File system is already mounted.\r\n", io->stdErr);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"Mounting ...", io->stdOut);
|
||||
}
|
||||
res = lfs_mount(&McuLFS_lfs, &McuLFS_cfg);
|
||||
if (res == LFS_ERR_OK) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)" done.\r\n", io->stdOut);
|
||||
}
|
||||
McuLFS_isMounted = TRUE;
|
||||
return ERR_OK;
|
||||
} else {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)" FAILED! Did you format the device?\r\n", io->stdErr);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t McuLFS_Unmount(McuShell_ConstStdIOType *io) {
|
||||
int res;
|
||||
|
||||
if (!McuLFS_isMounted) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"File system is already unmounted.\r\n", io->stdErr);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"Unmounting ...", io->stdOut);
|
||||
}
|
||||
res = lfs_unmount(&McuLFS_lfs);
|
||||
if (res == LFS_ERR_OK) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)" done.\r\n", io->stdOut);
|
||||
}
|
||||
McuLFS_isMounted = FALSE;
|
||||
return ERR_OK;
|
||||
} else {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)" FAILED!\r\n", io->stdErr);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t McuLFS_Dir(const char *path, McuShell_ConstStdIOType *io) {
|
||||
int res;
|
||||
lfs_dir_t dir;
|
||||
struct lfs_info info;
|
||||
|
||||
if (io == NULL) {
|
||||
return ERR_FAILED; /* listing a directory without an I/O channel does not make any sense */
|
||||
}
|
||||
|
||||
if (!McuLFS_isMounted) {
|
||||
McuShell_SendStr((const uint8_t *)"File system is not mounted, mount it first.\r\n", io->stdErr);
|
||||
return ERR_FAILED;
|
||||
}
|
||||
if (path == NULL) {
|
||||
path = "/"; /* default path */
|
||||
}
|
||||
res = lfs_dir_open(&McuLFS_lfs, &dir, path);
|
||||
if (res != LFS_ERR_OK) {
|
||||
McuShell_SendStr((const uint8_t *)"FAILED lfs_dir_open()!\r\n", io->stdErr);
|
||||
return ERR_FAILED;
|
||||
}
|
||||
for(;;) {
|
||||
res = lfs_dir_read(&McuLFS_lfs, &dir, &info);
|
||||
if (res < 0) {
|
||||
McuShell_SendStr((const uint8_t *)"FAILED lfs_dir_read()!\r\n", io->stdErr);
|
||||
return ERR_FAILED;
|
||||
}
|
||||
if (res == 0) { /* no more files */
|
||||
break;
|
||||
}
|
||||
switch (info.type) {
|
||||
case LFS_TYPE_REG:
|
||||
McuShell_SendStr((const uint8_t *)"reg ", io->stdOut);
|
||||
break;
|
||||
case LFS_TYPE_DIR:
|
||||
McuShell_SendStr((const uint8_t *)"dir ", io->stdOut);
|
||||
break;
|
||||
default:
|
||||
McuShell_SendStr((const uint8_t *)"? ", io->stdOut);
|
||||
break;
|
||||
}
|
||||
static const char *prefixes[] = { "", "K", "M", "G" }; /* prefixes for kilo, mega and giga */
|
||||
unsigned char buf[12];
|
||||
|
||||
for (int i = sizeof(prefixes) / sizeof(prefixes[0]) - 1; i >= 0; i--) {
|
||||
if (info.size >= (1 << 10 * i) - 1) {
|
||||
McuUtility_Num32sToStrFormatted(buf, sizeof(buf), (unsigned int)(info.size >> 10 * i), ' ', 4 - (i != 0));
|
||||
McuUtility_strcat(buf, sizeof(buf), (const unsigned char*)prefixes[i]);
|
||||
McuUtility_chcat(buf, sizeof(buf), ' ');
|
||||
McuShell_SendStr(buf, io->stdOut);
|
||||
break;
|
||||
}
|
||||
} /* for */
|
||||
McuShell_SendStr((const uint8_t *)info.name, io->stdOut);
|
||||
McuShell_SendStr((const uint8_t *)"\r\n", io->stdOut);
|
||||
} /* for */
|
||||
res = lfs_dir_close(&McuLFS_lfs, &dir);
|
||||
if (res != LFS_ERR_OK) {
|
||||
McuShell_SendStr((const uint8_t *)"FAILED lfs_dir_close()!\r\n", io->stdErr);
|
||||
return ERR_FAILED;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prints a list of Files and Directories of a given path
|
||||
* If path == NULL, the Files and Direcotries of the root-directory are printed
|
||||
* The First two characters of every line determin if its a File (F:) or a Directory (D:)
|
||||
*/
|
||||
uint8_t McuLFS_FileList(const char *path, McuShell_ConstStdIOType *io) {
|
||||
int res;
|
||||
lfs_dir_t dir;
|
||||
struct lfs_info info;
|
||||
|
||||
if (io == NULL) {
|
||||
return ERR_FAILED; /* listing a directory without an I/O channel does not make any sense */
|
||||
}
|
||||
if (!McuLFS_isMounted) {
|
||||
McuShell_SendStr((const uint8_t *)"File system is not mounted, mount it first.\r\n", io->stdErr);
|
||||
return ERR_FAILED;
|
||||
}
|
||||
if (path == NULL) {
|
||||
path = "/"; /* default path */
|
||||
}
|
||||
res = lfs_dir_open(&McuLFS_lfs, &dir, path);
|
||||
if (res != LFS_ERR_OK) {
|
||||
McuShell_SendStr((const uint8_t *)"FAILED lfs_dir_open()!\r\n", io->stdErr);
|
||||
return ERR_FAILED;
|
||||
}
|
||||
for(;;) {
|
||||
res = lfs_dir_read(&McuLFS_lfs, &dir, &info);
|
||||
if (res < 0) {
|
||||
McuShell_SendStr((const uint8_t *)"FAILED lfs_dir_read()!\r\n", io->stdErr);
|
||||
return ERR_FAILED;
|
||||
}
|
||||
if (res == 0) { /* no more files */
|
||||
break;
|
||||
}
|
||||
|
||||
if(!(McuUtility_strcmp(info.name,".") == 0 || McuUtility_strcmp(info.name,"..") == 0 )) {
|
||||
switch (info.type) {
|
||||
case LFS_TYPE_REG:
|
||||
McuShell_SendStr((const uint8_t *)"F:", io->stdOut);
|
||||
break;
|
||||
case LFS_TYPE_DIR:
|
||||
McuShell_SendStr((const uint8_t *)"D:", io->stdOut);
|
||||
break;
|
||||
default:
|
||||
McuShell_SendStr((const uint8_t *)"?:", io->stdOut);
|
||||
break;
|
||||
}
|
||||
McuShell_SendStr((const uint8_t *)info.name, io->stdOut);
|
||||
McuShell_SendStr((const uint8_t *)"\r\n", io->stdOut);
|
||||
}
|
||||
|
||||
} /* for */
|
||||
res = lfs_dir_close(&McuLFS_lfs, &dir);
|
||||
if (res != LFS_ERR_OK) {
|
||||
McuShell_SendStr((const uint8_t *)"FAILED lfs_dir_close()!\r\n", io->stdErr);
|
||||
return ERR_FAILED;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
uint8_t McuLFS_CopyFile(const char *srcPath, const char *dstPath,McuShell_ConstStdIOType *io) {
|
||||
lfs_file_t fsrc, fdst;
|
||||
int result, nofBytesRead;
|
||||
uint8_t buffer[32]; /* copy buffer */
|
||||
uint8_t res = ERR_OK;
|
||||
|
||||
if (!McuLFS_isMounted) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"ERROR: File system is not mounted.\r\n", io->stdErr);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
|
||||
/* open source file */
|
||||
result = lfs_file_open(&McuLFS_lfs, &fsrc, srcPath, LFS_O_RDONLY);
|
||||
if (result < 0) {
|
||||
McuShell_SendStr((const unsigned char*) "*** Failed opening source file!\r\n",io->stdErr);
|
||||
return ERR_FAILED;
|
||||
}
|
||||
/* create destination file */
|
||||
result = lfs_file_open(&McuLFS_lfs, &fdst, dstPath, LFS_O_WRONLY | LFS_O_CREAT);
|
||||
if (result < 0) {
|
||||
(void) lfs_file_close(&McuLFS_lfs, &fsrc);
|
||||
McuShell_SendStr((const unsigned char*) "*** Failed opening destination file!\r\n", io->stdErr);
|
||||
return ERR_FAILED;
|
||||
}
|
||||
/* now copy source to destination */
|
||||
for (;;) {
|
||||
nofBytesRead = lfs_file_read(&McuLFS_lfs, &fsrc, buffer, sizeof(buffer));
|
||||
if (nofBytesRead < 0) {
|
||||
McuShell_SendStr((const unsigned char*) "*** Failed reading source file!\r\n",io->stdErr);
|
||||
res = ERR_FAILED;
|
||||
break;
|
||||
}
|
||||
if (nofBytesRead == 0) { /* end of file */
|
||||
break;
|
||||
}
|
||||
result = lfs_file_write(&McuLFS_lfs, &fdst, buffer, nofBytesRead);
|
||||
if (result < 0) {
|
||||
McuShell_SendStr((const unsigned char*) "*** Failed writing destination file!\r\n", io->stdErr);
|
||||
res = ERR_FAILED;
|
||||
break;
|
||||
}
|
||||
} /* for */
|
||||
/* close all files */
|
||||
result = lfs_file_close(&McuLFS_lfs, &fsrc);
|
||||
if (result < 0) {
|
||||
McuShell_SendStr((const unsigned char*) "*** Failed closing source file!\r\n", io->stdErr);
|
||||
res = ERR_FAILED;
|
||||
}
|
||||
result = lfs_file_close(&McuLFS_lfs, &fdst);
|
||||
if (result < 0) {
|
||||
McuShell_SendStr((const unsigned char*) "*** Failed closing destination file!\r\n", io->stdErr);
|
||||
res = ERR_FAILED;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint8_t McuLFS_MoveFile(const char *srcPath, const char *dstPath,McuShell_ConstStdIOType *io) {
|
||||
if (!McuLFS_isMounted) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"ERROR: File system is not mounted.\r\n", io->stdErr);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
if (lfs_rename(&McuLFS_lfs, srcPath, dstPath) < 0) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"ERROR: failed renaming file or directory.\r\n",io->stdErr);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Used to read out data from Files for SDEP communication
|
||||
*/
|
||||
uint8_t McuLFS_ReadFile(lfs_file_t* file, bool readFromBeginning, size_t nofBytes, McuShell_ConstStdIOType *io) {
|
||||
static int32_t filePos;
|
||||
size_t fileSize;
|
||||
uint8_t buf[1024];
|
||||
|
||||
if( nofBytes > 1024) {
|
||||
nofBytes = 1024;
|
||||
}
|
||||
if(readFromBeginning) {
|
||||
lfs_file_rewind(&McuLFS_lfs,file);
|
||||
filePos = 0;
|
||||
} else {
|
||||
lfs_file_seek(&McuLFS_lfs,file, filePos,LFS_SEEK_SET);
|
||||
}
|
||||
|
||||
fileSize = lfs_file_size(&McuLFS_lfs, file);
|
||||
filePos = lfs_file_tell(&McuLFS_lfs, file);
|
||||
fileSize = fileSize - filePos;
|
||||
|
||||
if (fileSize < 0) {
|
||||
return ERR_FAILED;
|
||||
}
|
||||
|
||||
if(fileSize > nofBytes) {
|
||||
if (lfs_file_read(&McuLFS_lfs, file, buf, nofBytes) < 0) {
|
||||
return ERR_FAILED;
|
||||
}
|
||||
McuShell_SendData(buf,nofBytes,io->stdErr);
|
||||
filePos = filePos + nofBytes;
|
||||
return ERR_OK;
|
||||
} else {
|
||||
if (lfs_file_read(&McuLFS_lfs, file, buf, fileSize) < 0) {
|
||||
return ERR_FAILED;
|
||||
}
|
||||
McuShell_SendData(buf,fileSize,io->stdErr);
|
||||
filePos = filePos + fileSize;
|
||||
return ERR_PARAM_SIZE; //EOF
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t McuLFS_openFile(lfs_file_t* file, uint8_t* filename) {
|
||||
if (lfs_file_open(&McuLFS_lfs, file, (const char*)filename, LFS_O_RDWR | LFS_O_CREAT| LFS_O_APPEND) < 0) {
|
||||
return ERR_FAILED;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
uint8_t McuLFS_closeFile(lfs_file_t* file) {
|
||||
if(lfs_file_close(&McuLFS_lfs, file) == 0) {
|
||||
return ERR_OK;
|
||||
} else {
|
||||
return ERR_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t McuLFS_writeLine(lfs_file_t* file, uint8_t* line) {
|
||||
uint8_t lineBuf[200];
|
||||
McuUtility_strcpy(lineBuf, sizeof(lineBuf), line);
|
||||
McuUtility_strcat(lineBuf, sizeof(lineBuf), (unsigned char*)"\r\n");
|
||||
|
||||
if (lfs_file_write(&McuLFS_lfs, file, lineBuf, McuUtility_strlen((char*)lineBuf)) < 0) {
|
||||
lfs_file_close(&McuLFS_lfs, file);
|
||||
return ERR_FAILED;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
uint8_t McuLFS_readLine(lfs_file_t* file, uint8_t* lineBuf, size_t bufSize, uint8_t* nofReadChars) {
|
||||
lineBuf[0] = '\0';
|
||||
uint8_t ch;
|
||||
*nofReadChars = 0;
|
||||
|
||||
while(lfs_file_read(&McuLFS_lfs, file, &ch, 1) != 0 && ch != '\n') {
|
||||
(*nofReadChars)++;
|
||||
McuUtility_chcat(lineBuf,200,ch);
|
||||
}
|
||||
McuUtility_chcat(lineBuf,200,ch);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/* Function for the Shell PrintHex command */
|
||||
static uint8_t readFromFile(void *hndl, uint32_t addr, uint8_t *buf, size_t bufSize) {
|
||||
lfs_file_t *fp;
|
||||
|
||||
fp = (lfs_file_t*)hndl;
|
||||
if (lfs_file_read(&McuLFS_lfs, fp, buf, bufSize) < 0) {
|
||||
return ERR_FAILED;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
uint8_t McuLFS_PrintFile(const char *filePath, McuShell_ConstStdIOType *io, bool inHex) {
|
||||
lfs_file_t file;
|
||||
uint8_t res = ERR_OK;
|
||||
int32_t fileSize;
|
||||
int result;
|
||||
|
||||
if (io == NULL) {
|
||||
return ERR_FAILED; /* printing a file without an I/O channel does not make any sense */
|
||||
}
|
||||
if (!McuLFS_isMounted) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"ERROR: File system is not mounted.\r\n", io->stdErr);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
result = lfs_file_open(&McuLFS_lfs, &file, filePath, LFS_O_RDONLY);
|
||||
if (result < 0) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"ERROR: Failed opening file.\r\n", io->stdErr);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
fileSize = lfs_file_size(&McuLFS_lfs, &file);
|
||||
if (fileSize < 0) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"ERROR: getting file size\r\n", io->stdErr);
|
||||
(void) lfs_file_close(&McuLFS_lfs, &file);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
if (inHex) {
|
||||
res = McuShell_PrintMemory(&file, 0, fileSize-1, 4, 16, readFromFile, io);
|
||||
if (res != ERR_OK)
|
||||
{
|
||||
McuShell_SendStr((const uint8_t *)"ERROR while calling PrintMemory()\r\n", io->stdErr);
|
||||
}
|
||||
} else {
|
||||
uint8_t ch;
|
||||
|
||||
while(fileSize>0) {
|
||||
if (lfs_file_read(&McuLFS_lfs, &file, &ch, sizeof(ch)) < 0) {
|
||||
break; /* error case */
|
||||
}
|
||||
McuShell_SendCh(ch, io->stdOut); /* print character */
|
||||
fileSize--;
|
||||
}
|
||||
}
|
||||
(void) lfs_file_close(&McuLFS_lfs, &file);
|
||||
return res;
|
||||
}
|
||||
|
||||
static uint8_t McuLFS_CatBinaryTextDataToFile(const char *filePath, McuShell_ConstStdIOType *io, const unsigned char *p) {
|
||||
lfs_file_t file;
|
||||
int result;
|
||||
|
||||
if (io == NULL) {
|
||||
return ERR_FAILED; /* printing a file without an I/O channel does not make any sense */
|
||||
}
|
||||
if (!McuLFS_isMounted) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"ERROR: File system is not mounted.\r\n", io->stdErr);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
result = lfs_file_open(&McuLFS_lfs, &file, filePath, LFS_O_RDWR | LFS_O_CREAT| LFS_O_APPEND);
|
||||
if (result < 0) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"ERROR: Failed opening file.\r\n", io->stdErr);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
result = lfs_file_seek(&McuLFS_lfs, &file, 0, LFS_SEEK_END);
|
||||
if (result < 0) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"ERROR: Failed opening file.\r\n", io->stdErr);
|
||||
}
|
||||
(void)lfs_file_close(&McuLFS_lfs, &file);
|
||||
return ERR_FAILED;
|
||||
}
|
||||
for(;;) { /* breaks */
|
||||
int32_t v;
|
||||
uint8_t ch, res;
|
||||
|
||||
res = McuUtility_xatoi(&p, &v);
|
||||
if (res!=ERR_OK) {
|
||||
break;
|
||||
}
|
||||
ch = v; /* just one byte */
|
||||
if (lfs_file_write(&McuLFS_lfs, &file, &ch, sizeof(ch)) < 0) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"ERROR: Failed writing to file.\r\n", io->stdErr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
(void)lfs_file_close(&McuLFS_lfs, &file);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
uint8_t McuLFS_RemoveFile(const char *filePath, McuShell_ConstStdIOType *io) {
|
||||
int result;
|
||||
|
||||
if (!McuLFS_isMounted) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"ERROR: File system is not mounted.\r\n", io->stdErr);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
result = lfs_remove(&McuLFS_lfs, filePath);
|
||||
if (result < 0) {
|
||||
if (io != NULL) {
|
||||
McuShell_SendStr((const uint8_t *)"ERROR: Failed removing file.\r\n", io->stdErr);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
lfs_t* McuLFS_GetFileSystem(void) {
|
||||
return &McuLFS_lfs;
|
||||
}
|
||||
|
||||
uint8_t McuLFS_RunBenchmark(const McuShell_ConstStdIOType *io) {
|
||||
lfs_file_t file;
|
||||
uint32_t i;
|
||||
uint8_t read_buf[10];
|
||||
TIMEREC time, startTime;
|
||||
int32_t start_mseconds, mseconds;
|
||||
|
||||
if (!McuLFS_isMounted) {
|
||||
if (io!=NULL) {
|
||||
McuShell_SendStr((const unsigned char*)"ERROR: File system is not mounted.\r\n", io->stdErr);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
/* write benchmark */
|
||||
McuShell_SendStr((const unsigned char*)"Benchmark: write/copy/read a 100kB file:\r\n", io->stdOut);
|
||||
McuShell_SendStr((const unsigned char*)"Delete existing benchmark files...\r\n", io->stdOut);
|
||||
(void)McuLFS_RemoveFile((const char*)"./bench.txt", io);
|
||||
(void)McuLFS_RemoveFile((const char*)"./copy.txt", io);
|
||||
|
||||
McuShell_SendStr((const unsigned char*)"Create benchmark file...\r\n", io->stdOut);
|
||||
(void)McuTimeDate_GetTime(&startTime);
|
||||
if (lfs_file_open(&McuLFS_lfs, &file, "./bench.txt", LFS_O_WRONLY | LFS_O_CREAT)<0) {
|
||||
McuShell_SendStr((const unsigned char*)"*** Failed creating benchmark file!\r\n", io->stdErr);
|
||||
return ERR_FAILED;
|
||||
}
|
||||
for(i=0;i<10240;i++) {
|
||||
if (lfs_file_write(&McuLFS_lfs, &file, "benchmark ", sizeof("benchmark ")-1)<0) {
|
||||
McuShell_SendStr((const unsigned char*)"*** Failed writing file!\r\n", io->stdErr);
|
||||
(void)lfs_file_close(&McuLFS_lfs, &file);
|
||||
return ERR_FAILED;
|
||||
}
|
||||
}
|
||||
(void)lfs_file_close(&McuLFS_lfs, &file);
|
||||
(void)McuTimeDate_GetTime(&time);
|
||||
start_mseconds = startTime.Hour*60*60*1000 + startTime.Min*60*1000 + startTime.Sec*1000
|
||||
#if TmDt1_HAS_SEC100_IN_TIMEREC
|
||||
+ startTime.Sec100*10
|
||||
#endif
|
||||
;
|
||||
mseconds = time.Hour*60*60*1000 + time.Min*60*1000 + time.Sec*1000
|
||||
#if TmDt1_HAS_SEC100_IN_TIMEREC
|
||||
+ time.Sec100*10
|
||||
#endif
|
||||
- start_mseconds;
|
||||
McuShell_SendNum32s(mseconds, io->stdOut);
|
||||
McuShell_SendStr((const unsigned char*)" ms for writing (", io->stdOut);
|
||||
McuShell_SendNum32s((100*1000)/mseconds, io->stdOut);
|
||||
McuShell_SendStr((const unsigned char*)" kB/s)\r\n", io->stdOut);
|
||||
|
||||
/* read benchmark */
|
||||
McuShell_SendStr((const unsigned char*)"Read 100kB benchmark file...\r\n", io->stdOut);
|
||||
(void)McuTimeDate_GetTime(&startTime);
|
||||
if (lfs_file_open(&McuLFS_lfs, &file, "./bench.txt", LFS_O_RDONLY)<0) {
|
||||
McuShell_SendStr((const unsigned char*)"*** Failed opening benchmark file!\r\n", io->stdErr);
|
||||
return ERR_FAILED;
|
||||
}
|
||||
for(i=0;i<10240;i++) {
|
||||
if (lfs_file_read(&McuLFS_lfs, &file, &read_buf[0], sizeof(read_buf))<0) {
|
||||
McuShell_SendStr((const unsigned char*)"*** Failed reading file!\r\n", io->stdErr);
|
||||
(void)lfs_file_close(&McuLFS_lfs, &file);
|
||||
return ERR_FAILED;
|
||||
}
|
||||
}
|
||||
(void)lfs_file_close(&McuLFS_lfs, &file);
|
||||
(void)McuTimeDate_GetTime(&time);
|
||||
start_mseconds = startTime.Hour*60*60*1000 + startTime.Min*60*1000 + startTime.Sec*1000
|
||||
#if TmDt1_HAS_SEC100_IN_TIMEREC
|
||||
+ startTime.Sec100*10
|
||||
#endif
|
||||
;
|
||||
mseconds = time.Hour*60*60*1000 + time.Min*60*1000 + time.Sec*1000
|
||||
#if TmDt1_HAS_SEC100_IN_TIMEREC
|
||||
+ time.Sec100*10
|
||||
#endif
|
||||
- start_mseconds;
|
||||
McuShell_SendNum32s(mseconds, io->stdOut);
|
||||
McuShell_SendStr((const unsigned char*)" ms for reading (", io->stdOut);
|
||||
McuShell_SendNum32s((100*1000)/mseconds, io->stdOut);
|
||||
McuShell_SendStr((const unsigned char*)" kB/s)\r\n", io->stdOut);
|
||||
|
||||
/* copy benchmark */
|
||||
McuShell_SendStr((const unsigned char*)"Copy 100kB file...\r\n", io->stdOut);
|
||||
(void)McuTimeDate_GetTime(&startTime);
|
||||
(void)McuLFS_CopyFile((const char*)"./bench.txt", (const char*)"./copy.txt", io);
|
||||
(void)McuTimeDate_GetTime(&time);
|
||||
start_mseconds = startTime.Hour*60*60*1000 + startTime.Min*60*1000 + startTime.Sec*1000
|
||||
#if TmDt1_HAS_SEC100_IN_TIMEREC
|
||||
+ startTime.Sec100*10
|
||||
#endif
|
||||
;
|
||||
mseconds = time.Hour*60*60*1000 + time.Min*60*1000 + time.Sec*1000
|
||||
#if TmDt1_HAS_SEC100_IN_TIMEREC
|
||||
+ time.Sec100*10
|
||||
#endif
|
||||
- start_mseconds;
|
||||
McuShell_SendNum32s(mseconds, io->stdOut);
|
||||
McuShell_SendStr((const unsigned char*)" ms for copy (", io->stdOut);
|
||||
McuShell_SendNum32s((100*1000)/mseconds, io->stdOut);
|
||||
McuShell_SendStr((const unsigned char*)" kB/s)\r\n", io->stdOut);
|
||||
McuShell_SendStr((const unsigned char*)"done!\r\n", io->stdOut);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static uint8_t McuLFS_PrintStatus(McuShell_ConstStdIOType *io) {
|
||||
uint8_t buf[32];
|
||||
|
||||
McuShell_SendStatusStr((const unsigned char*) "McuLittleFS", (const unsigned char*) "McuLittleFS status\r\n", io->stdOut);
|
||||
|
||||
McuUtility_strcpy(buf, sizeof(buf), (const uint8_t *)"0x");
|
||||
McuUtility_strcatNum32Hex(buf, sizeof(buf), LFS_VERSION);
|
||||
McuUtility_strcat(buf, sizeof(buf), (const uint8_t *)", disk ");
|
||||
McuUtility_strcatNum32Hex(buf, sizeof(buf), LFS_DISK_VERSION);
|
||||
McuUtility_strcat(buf, sizeof(buf), (const uint8_t *)"\r\n");
|
||||
McuShell_SendStatusStr((const unsigned char*)" version", buf, io->stdOut);
|
||||
|
||||
#if McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE==McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_GENERIC
|
||||
McuShell_SendStatusStr((const unsigned char*)" memory", (const unsigned char*)"Generic\r\n", io->stdOut);
|
||||
#elif McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE==McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_WINBOND_W25Q128
|
||||
McuShell_SendStatusStr((const unsigned char*)" memory", (const unsigned char*)"Winbond W25Q128\r\n", io->stdOut);
|
||||
#elif McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE==McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_MCU_FLASH
|
||||
McuShell_SendStatusStr((const unsigned char*)" memory", (const unsigned char*)"MCU Flash\r\n", io->stdOut);
|
||||
#else
|
||||
#error "unknown type"
|
||||
#endif
|
||||
|
||||
McuShell_SendStatusStr((const unsigned char*) " mounted", McuLFS_isMounted ? (const uint8_t *)"yes\r\n" : (const uint8_t *)"no\r\n", io->stdOut);
|
||||
|
||||
McuUtility_Num32uToStr(buf, sizeof(buf), McuLFS_cfg.block_size);
|
||||
McuUtility_strcat(buf, sizeof(buf), (const uint8_t *)"\r\n");
|
||||
McuShell_SendStatusStr((const unsigned char*)" block_size", buf, io->stdOut);
|
||||
|
||||
McuUtility_Num32uToStr(buf, sizeof(buf), McuLFS_cfg.block_count);
|
||||
McuUtility_strcat(buf, sizeof(buf), (const uint8_t *)"\r\n");
|
||||
McuShell_SendStatusStr((const unsigned char*)" block_count", buf, io->stdOut);
|
||||
|
||||
McuUtility_Num32uToStr(buf, sizeof(buf), McuLittleFS_CONFIG_BLOCK_OFFSET);
|
||||
McuUtility_strcat(buf, sizeof(buf), (const uint8_t *)"\r\n");
|
||||
McuShell_SendStatusStr((const unsigned char*)" blockoffset", buf, io->stdOut);
|
||||
|
||||
McuUtility_Num32uToStr(buf, sizeof(buf), McuLFS_cfg.block_count * McuLFS_cfg.block_size);
|
||||
McuUtility_strcat(buf, sizeof(buf), (const uint8_t *)" bytes\r\n");
|
||||
McuShell_SendStatusStr((const unsigned char*)" space", buf, io->stdOut);
|
||||
|
||||
McuUtility_Num32uToStr(buf, sizeof(buf), McuLFS_cfg.read_size);
|
||||
McuUtility_strcat(buf, sizeof(buf), (const uint8_t *)"\r\n");
|
||||
McuShell_SendStatusStr((const unsigned char*)" read_size", buf, io->stdOut);
|
||||
|
||||
McuUtility_Num32uToStr(buf, sizeof(buf), McuLFS_cfg.prog_size);
|
||||
McuUtility_strcat(buf, sizeof(buf), (const uint8_t *)"\r\n");
|
||||
McuShell_SendStatusStr((const unsigned char*)" prog_size", buf, io->stdOut);
|
||||
|
||||
McuUtility_Num32uToStr(buf, sizeof(buf), McuLFS_cfg.lookahead_size);
|
||||
McuUtility_strcat(buf, sizeof(buf), (const uint8_t *)"\r\n");
|
||||
McuShell_SendStatusStr((const unsigned char*)" lookahead", buf, io->stdOut);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
uint8_t McuLFS_ParseCommand(const unsigned char* cmd, bool *handled,const McuShell_StdIOType *io) {
|
||||
unsigned char fileNameSrc[McuLittleFS_CONFIG_FILE_NAME_SIZE], fileNameDst[McuLittleFS_CONFIG_FILE_NAME_SIZE];
|
||||
size_t lenRead;
|
||||
const unsigned char *p;
|
||||
|
||||
if (McuUtility_strcmp((char*)cmd, McuShell_CMD_HELP) == 0|| McuUtility_strcmp((char*)cmd, "McuLittleFS help") == 0) {
|
||||
McuShell_SendHelpStr((unsigned char*) "McuLittleFS", (const unsigned char*) "Group of FileSystem (LittleFS) commands\r\n", io->stdOut);
|
||||
McuShell_SendHelpStr((unsigned char*) " help|status", (const unsigned char*) "Print help or status information\r\n", io->stdOut);
|
||||
McuShell_SendHelpStr((unsigned char*) " format",(const unsigned char*) "Format the file system\r\n",io->stdOut);
|
||||
McuShell_SendHelpStr((unsigned char*) " mount",(const unsigned char*) "Mount the file system\r\n", io->stdOut);
|
||||
McuShell_SendHelpStr((unsigned char*) " unmount",(const unsigned char*) "Unmount the file system\r\n", io->stdOut);
|
||||
McuShell_SendHelpStr((unsigned char*) " ls",(const unsigned char*) "List directory and files of root\r\n", io->stdOut);
|
||||
McuShell_SendHelpStr((unsigned char*) " ls <dir>",(const unsigned char*) "List directory and files of dir (ex. /Samples)\r\n", io->stdOut);
|
||||
McuShell_SendHelpStr((unsigned char*) " rm <file>",(const unsigned char*) "Remove a file\r\n", io->stdOut);
|
||||
McuShell_SendHelpStr((unsigned char*) " mv <src> <dst>",(const unsigned char*) "Rename a file\r\n", io->stdOut);
|
||||
McuShell_SendHelpStr((unsigned char*) " cp <src> <dst>",(const unsigned char*) "Copy a file\r\n", io->stdOut);
|
||||
McuShell_SendHelpStr((unsigned char*) " mkdir <dir>",(const unsigned char*) "Create a directory\r\n", io->stdOut);
|
||||
McuShell_SendHelpStr((unsigned char*) " bincat <file> <data>",(const unsigned char*) "Add hex numbers as binary data to a file. If file does not exist, it gets created\r\n",io->stdOut);
|
||||
McuShell_SendHelpStr((unsigned char*) " printhex <file>",(const unsigned char*) "Print the file data in hexadecimal format\r\n",io->stdOut);
|
||||
McuShell_SendHelpStr((unsigned char*) " printtxt <file>",(const unsigned char*) "Print the file data in text format\r\n",io->stdOut);
|
||||
McuShell_SendHelpStr((unsigned char*) " benchmark",(const unsigned char*) "Run a benchmark to measure performance\r\n",io->stdOut);
|
||||
*handled = TRUE;
|
||||
return ERR_OK;
|
||||
} else if (McuUtility_strcmp((char*)cmd, McuShell_CMD_STATUS) == 0|| McuUtility_strcmp((char*)cmd, "McuLittleFS status") == 0) {
|
||||
*handled = TRUE;
|
||||
return McuLFS_PrintStatus(io);
|
||||
} else if (McuUtility_strcmp((char*)cmd, "McuLittleFS format") == 0) {
|
||||
*handled = TRUE;
|
||||
return McuLFS_Format(io);
|
||||
} else if (McuUtility_strcmp((char*)cmd, "McuLittleFS mount") == 0) {
|
||||
*handled = TRUE;
|
||||
return McuLFS_Mount(io);
|
||||
} else if (McuUtility_strcmp((char*)cmd, "McuLittleFS unmount") == 0) {
|
||||
*handled = TRUE;
|
||||
return McuLFS_Unmount(io);
|
||||
} else if (McuUtility_strcmp((char*)cmd, "McuLittleFS ls") == 0) {
|
||||
*handled = TRUE;
|
||||
return McuLFS_Dir(NULL, io);
|
||||
} else if (McuUtility_strncmp((char* )cmd, "McuLittleFS ls ", sizeof("McuLittleFS ls ") - 1) == 0) {
|
||||
*handled = TRUE;
|
||||
if ((McuUtility_ReadEscapedName(cmd + sizeof("McuLittleFS ls ") - 1,fileNameSrc, sizeof(fileNameSrc), &lenRead, NULL, NULL) == ERR_OK)) {
|
||||
return McuLFS_Dir((const char*)fileNameSrc, io);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
} else if (McuUtility_strcmp((char*)cmd, "McuLittleFS benchmark") == 0) {
|
||||
*handled = TRUE;
|
||||
return McuLFS_RunBenchmark(io);
|
||||
} else if (McuUtility_strncmp((char* )cmd, "McuLittleFS printhex ", sizeof("McuLittleFS printhex ") - 1) == 0) {
|
||||
*handled = TRUE;
|
||||
if ((McuUtility_ReadEscapedName(cmd + sizeof("McuLittleFS printhex ") - 1,fileNameSrc, sizeof(fileNameSrc), &lenRead, NULL, NULL) == ERR_OK)) {
|
||||
return McuLFS_PrintFile((const char*)fileNameSrc, io, TRUE);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
} else if (McuUtility_strncmp((char* )cmd, "McuLittleFS printtxt ", sizeof("McuLittleFS printtxt ") - 1) == 0) {
|
||||
*handled = TRUE;
|
||||
if ((McuUtility_ReadEscapedName(cmd + sizeof("McuLittleFS printtxt ") - 1,fileNameSrc, sizeof(fileNameSrc), &lenRead, NULL, NULL) == ERR_OK)) {
|
||||
return McuLFS_PrintFile((const char*)fileNameSrc, io, FALSE);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
} else if (McuUtility_strncmp((char*)cmd, "McuLittleFS rm ", sizeof("McuLittleFS rm ")-1) == 0) {
|
||||
*handled = TRUE;
|
||||
if ((McuUtility_ReadEscapedName(cmd + sizeof("McuLittleFS rm ") - 1, fileNameSrc, sizeof(fileNameSrc), &lenRead, NULL, NULL) == ERR_OK)) {
|
||||
return McuLFS_RemoveFile((const char*)fileNameSrc, io);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
} else if (McuUtility_strncmp((char*)cmd, "McuLittleFS mv ", sizeof("McuLittleFS mv ")-1) == 0) {
|
||||
*handled = TRUE;
|
||||
if ((McuUtility_ReadEscapedName(cmd + sizeof("McuLittleFS mv ") - 1, fileNameSrc, sizeof(fileNameSrc), &lenRead, NULL, NULL) == ERR_OK)
|
||||
&& *(cmd + sizeof("McuLittleFS mv ") - 1 + lenRead) == ' '
|
||||
&& (McuUtility_ReadEscapedName(cmd + sizeof("McuLittleFS mv ") - 1 + lenRead + 1, fileNameDst,sizeof(fileNameDst), NULL, NULL, NULL) == ERR_OK))
|
||||
{
|
||||
return McuLFS_MoveFile((const char*)fileNameSrc, (const char*)fileNameDst, io);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
} else if (McuUtility_strncmp((char*)cmd, "McuLittleFS cp ", sizeof("McuLittleFS cp ")-1) == 0) {
|
||||
*handled = TRUE;
|
||||
if ((McuUtility_ReadEscapedName(cmd + sizeof("McuLittleFS cp ") - 1, fileNameSrc, sizeof(fileNameSrc), &lenRead, NULL, NULL) == ERR_OK)
|
||||
&& *(cmd + sizeof("McuLittleFS cp ") - 1 + lenRead) == ' '
|
||||
&& (McuUtility_ReadEscapedName(cmd + sizeof("McuLittleFS cp ") - 1 + lenRead + 1, fileNameDst,sizeof(fileNameDst), NULL, NULL, NULL) == ERR_OK))
|
||||
{
|
||||
return McuLFS_CopyFile((const char*)fileNameSrc, (const char*)fileNameDst, io);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
} else if (McuUtility_strncmp((char*)cmd, "McuLittleFS bincat ", sizeof("McuLittleFS bincat ")-1) == 0) {
|
||||
*handled = TRUE;
|
||||
p = cmd + sizeof("McuLittleFS bincat ") - 1;
|
||||
if ((McuUtility_ReadEscapedName(p, fileNameSrc, sizeof(fileNameSrc), &lenRead, NULL, NULL) == ERR_OK)) {
|
||||
p += lenRead;
|
||||
return McuLFS_CatBinaryTextDataToFile((const char*)fileNameSrc, io, p);
|
||||
}
|
||||
return ERR_FAILED;
|
||||
} else if (McuUtility_strncmp((char*)cmd, "McuLittleFS mkdir ", sizeof("McuLittleFS mkdir ")-1) == 0) {
|
||||
*handled = TRUE;
|
||||
p = cmd + sizeof("McuLittleFS mkdir ") - 1;
|
||||
if ((McuUtility_ReadEscapedName(p, fileNameSrc, sizeof(fileNameSrc), &lenRead, NULL, NULL) == ERR_OK)) {
|
||||
if (lfs_mkdir(&McuLFS_lfs, (const char*)fileNameSrc)!=0) {
|
||||
return ERR_FAILED;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
return ERR_FAILED;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
#if LITTLEFS_CONFIG_THREAD_SAFE
|
||||
void McuLFS_GetFileAccessSemaphore(SemaphoreHandle_t *mutex) {
|
||||
*mutex = fileSystemAccessMutex;
|
||||
}
|
||||
#endif
|
||||
|
||||
void McuLFS_Deinit(void) {
|
||||
#if LITTLEFS_CONFIG_THREAD_SAFE
|
||||
vSemaphoreDelete(fileSystemAccessMutex);
|
||||
fileSystemAccessMutex = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void McuLFS_Init(void) {
|
||||
#if LITTLEFS_CONFIG_THREAD_SAFE
|
||||
fileSystemAccessMutex = xSemaphoreCreateRecursiveMutex();
|
||||
if (fileSystemAccessMutex == NULL) {
|
||||
for(;;) {} /* Error */
|
||||
}
|
||||
vQueueAddToRegistry(fileSystemAccessMutex, "littleFSmutex");
|
||||
xSemaphoreGiveRecursive(fileSystemAccessMutex);
|
||||
if (McuLittleFS_block_device_init() != ERR_OK) {
|
||||
for(;;) {} /* Error */
|
||||
}
|
||||
#endif
|
||||
}
|
||||
48
TSM_PicoW_Sensor/McuLib/littleFS/McuLittleFS.h
Normal file
48
TSM_PicoW_Sensor/McuLib/littleFS/McuLittleFS.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Erich Styger
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef MCULITTLEFS_H_
|
||||
#define MCULITTLEFS_H_
|
||||
|
||||
#include "littleFS/McuLittleFSBlockDeviceconfig.h"
|
||||
#include "McuShell.h"
|
||||
#include "littleFS/lfs.h"
|
||||
#include "McuRTOS.h"
|
||||
|
||||
bool McuLFS_IsMounted(void);
|
||||
|
||||
lfs_t* McuLFS_GetFileSystem(void);
|
||||
uint8_t McuLFS_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io);
|
||||
#if LITTLEFS_CONFIG_THREAD_SAFE
|
||||
void McuLFS_GetFileAccessMutex(SemaphoreHandle_t* mutex);
|
||||
#endif
|
||||
|
||||
uint8_t McuLFS_ReadFile(lfs_file_t* file, bool readFromBeginning, size_t nofBytes, McuShell_ConstStdIOType *io);
|
||||
uint8_t McuLFS_FileList(const char *path, McuShell_ConstStdIOType *io);
|
||||
uint8_t McuLFS_RemoveFile(const char *filePath, McuShell_ConstStdIOType *io);
|
||||
uint8_t McuLFS_MoveFile(const char *srcPath, const char *dstPath,McuShell_ConstStdIOType *io);
|
||||
|
||||
uint8_t McuLFS_Mount(McuShell_ConstStdIOType *io);
|
||||
uint8_t McuLFS_Unmount(McuShell_ConstStdIOType *io);
|
||||
uint8_t McuLFS_Format(McuShell_ConstStdIOType *io);
|
||||
|
||||
uint8_t McuLFS_openFile(lfs_file_t* file,uint8_t* filename);
|
||||
uint8_t McuLFS_closeFile(lfs_file_t* file);
|
||||
uint8_t McuLFS_writeLine(lfs_file_t* file,uint8_t* line);
|
||||
uint8_t McuLFS_readLine(lfs_file_t* file,uint8_t* lineBuf,size_t bufSize,uint8_t* nofReadChars);
|
||||
|
||||
/* Functions ported from FatFS (Used by MiniIni) */
|
||||
char* McuLFS_gets (char* buff,int len, lfs_file_t* fp);
|
||||
int McuLFS_putc (char c, lfs_file_t* fp);
|
||||
int McuLFS_puts (const char* str, lfs_file_t* fp);
|
||||
|
||||
const struct lfs_config *McuLFS_get_config(void);
|
||||
lfs_t *McuLFS_get_lfs(void);
|
||||
|
||||
void McuLFS_Deinit(void);
|
||||
void McuLFS_Init(void);
|
||||
|
||||
#endif /* MCULITTLEFS_H_ */
|
||||
77
TSM_PicoW_Sensor/McuLib/littleFS/McuLittleFSBlockDevice.c
Normal file
77
TSM_PicoW_Sensor/McuLib/littleFS/McuLittleFSBlockDevice.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Erich Styger
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include "McuLib.h" /* for error codes */
|
||||
#include "McuLittleFSBlockDevice.h"
|
||||
#include "McuLittleFSconfig.h"
|
||||
#include "littleFS/lfs.h"
|
||||
|
||||
/* pre-configured memory devices */
|
||||
#if McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE==McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_WINBOND_W25Q128
|
||||
#include "McuW25Q128.h"
|
||||
#elif McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE==McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_MCU_FLASH
|
||||
#include "McuFlash.h"
|
||||
#endif
|
||||
|
||||
int McuLittleFS_block_device_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size) {
|
||||
uint8_t res;
|
||||
|
||||
#if McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE==McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_GENERIC
|
||||
res = ERR_FAILED; /* NYI */
|
||||
#elif McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE==McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_WINBOND_W25Q128
|
||||
res = McuW25_Read((block+McuLittleFS_CONFIG_BLOCK_OFFSET) * c->block_size + off, buffer, size);
|
||||
#elif McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE==McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_MCU_FLASH
|
||||
res = McuFlash_Read((void*)((block+McuLittleFS_CONFIG_BLOCK_OFFSET) * c->block_size + off), buffer, size);
|
||||
#endif
|
||||
if (res != ERR_OK) {
|
||||
return LFS_ERR_IO;
|
||||
}
|
||||
return LFS_ERR_OK;
|
||||
}
|
||||
|
||||
int McuLittleFS_block_device_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size) {
|
||||
uint8_t res;
|
||||
|
||||
#if McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE==McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_GENERIC
|
||||
res = ERR_FAILED; /* NYI */
|
||||
#elif McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE==McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_WINBOND_W25Q128
|
||||
res = McuW25_ProgramPage((block+McuLittleFS_CONFIG_BLOCK_OFFSET) * c->block_size + off, buffer, size);
|
||||
#elif McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE==McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_MCU_FLASH
|
||||
res = McuFlash_Program((void*)((block+McuLittleFS_CONFIG_BLOCK_OFFSET) * c->block_size + off), buffer, size);
|
||||
#endif
|
||||
if (res != ERR_OK) {
|
||||
return LFS_ERR_IO;
|
||||
}
|
||||
return LFS_ERR_OK;
|
||||
}
|
||||
|
||||
int McuLittleFS_block_device_erase(const struct lfs_config *c, lfs_block_t block) {
|
||||
uint8_t res;
|
||||
|
||||
#if McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE==McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_GENERIC
|
||||
res = ERR_FAILED; /* NYI */
|
||||
#elif McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE==McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_WINBOND_W25Q128
|
||||
res = McuW25_EraseSector4K((block+McuLittleFS_CONFIG_BLOCK_OFFSET) * c->block_size);
|
||||
#elif McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE==McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_MCU_FLASH
|
||||
res = McuFlash_Erase((void*)((block+McuLittleFS_CONFIG_BLOCK_OFFSET) * c->block_size), c->block_size);
|
||||
#endif
|
||||
if (res != ERR_OK) {
|
||||
return LFS_ERR_IO;
|
||||
}
|
||||
return LFS_ERR_OK;
|
||||
}
|
||||
|
||||
int McuLittleFS_block_device_sync(const struct lfs_config *c) {
|
||||
return LFS_ERR_OK;
|
||||
}
|
||||
|
||||
int McuLittleFS_block_device_deinit(void) {
|
||||
return LFS_ERR_OK;
|
||||
}
|
||||
|
||||
int McuLittleFS_block_device_init(void) {
|
||||
return LFS_ERR_OK;
|
||||
}
|
||||
27
TSM_PicoW_Sensor/McuLib/littleFS/McuLittleFSBlockDevice.h
Normal file
27
TSM_PicoW_Sensor/McuLib/littleFS/McuLittleFSBlockDevice.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Erich Styger
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
|
||||
#ifndef MCULITTLEFSBLOCKDEVICE_H_
|
||||
#define MCULITTLEFSBLOCKDEVICE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "littleFS/lfs.h"
|
||||
#include "littleFS/McuLittleFSBlockDeviceconfig.h"
|
||||
|
||||
int McuLittleFS_block_device_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, void *buffer, lfs_size_t size);
|
||||
|
||||
int McuLittleFS_block_device_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off, const void *buffer, lfs_size_t size);
|
||||
|
||||
int McuLittleFS_block_device_erase(const struct lfs_config *c, lfs_block_t block);
|
||||
|
||||
int McuLittleFS_block_device_sync(const struct lfs_config *c);
|
||||
|
||||
int McuLittleFS_block_device_deinit(void);
|
||||
|
||||
int McuLittleFS_block_device_init(void);
|
||||
|
||||
#endif /* MCULITTLEFSBLOCKDEVICE_H_ */
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Erich Styger
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LITTLEFS_MCULITTLEFSBLOCKDEVICECONFIG_H_
|
||||
#define LITTLEFS_MCULITTLEFSBLOCKDEVICECONFIG_H_
|
||||
|
||||
/* supported block device types */
|
||||
#define McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_GENERIC 0 /* dummy, generic implementation */
|
||||
#define McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_WINBOND_W25Q128 1 /* using WinBond W25Q128 external SPI FLASH */
|
||||
#define McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_MCU_FLASH 2 /* using McuFlash blocks */
|
||||
|
||||
#ifndef McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE
|
||||
#define McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE McuLittleFSBlockDevice_CONFIG_MEMORY_TYPE_GENERIC
|
||||
/*<! sets used block device */
|
||||
#endif
|
||||
|
||||
#endif /* LITTLEFS_MCULITTLEFSBLOCKDEVICECONFIG_H_ */
|
||||
46
TSM_PicoW_Sensor/McuLib/littleFS/McuLittleFSconfig.h
Normal file
46
TSM_PicoW_Sensor/McuLib/littleFS/McuLittleFSconfig.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Erich Styger
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef MCULITTLEFS_CONFIG_H_
|
||||
#define MCULITTLEFS_CONFIG_H_
|
||||
|
||||
#ifndef McuLittleFS_CONFIG_BLOCK_SIZE
|
||||
#define McuLittleFS_CONFIG_BLOCK_SIZE (4096)
|
||||
/*!< standard block size. Needs to match flash device block size*/
|
||||
#endif
|
||||
|
||||
#ifndef McuLittleFS_CONFIG_BLOCK_COUNT
|
||||
#define McuLittleFS_CONFIG_BLOCK_COUNT (16384) /* 16384 * 4K = 64 MByte */
|
||||
/*!< number of blocks used for the file system */
|
||||
#endif
|
||||
|
||||
#ifndef McuLittleFS_CONFIG_BLOCK_OFFSET
|
||||
#define McuLittleFS_CONFIG_BLOCK_OFFSET (0)
|
||||
/*!< block offset number for the block device */
|
||||
#endif
|
||||
|
||||
#ifndef McuLittleFS_CONFIG_FILE_NAME_SIZE
|
||||
#define McuLittleFS_CONFIG_FILE_NAME_SIZE (60)
|
||||
/*!< Length of file name, used in buffers */
|
||||
#endif
|
||||
|
||||
#ifndef McuLittleFS_CONFIG_FILESYSTEM_READ_BUFFER_SIZE
|
||||
#define McuLittleFS_CONFIG_FILESYSTEM_READ_BUFFER_SIZE (256)
|
||||
#endif
|
||||
|
||||
#ifndef McuLittleFS_CONFIG_FILESYSTEM_PROG_BUFFER_SIZE
|
||||
#define McuLittleFS_CONFIG_FILESYSTEM_PROG_BUFFER_SIZE (256)
|
||||
#endif
|
||||
|
||||
#ifndef McuLittleFS_CONFIG_FILESYSTEM_LOOKAHEAD_SIZE
|
||||
#define McuLittleFS_CONFIG_FILESYSTEM_LOOKAHEAD_SIZE (256)
|
||||
#endif
|
||||
|
||||
#ifndef McuLittleFS_CONFIG_FILESYSTEM_CACHE_SIZE
|
||||
#define McuLittleFS_CONFIG_FILESYSTEM_CACHE_SIZE (256)
|
||||
#endif
|
||||
|
||||
#endif /* MCULITTLEFS_CONFIG_H_ */
|
||||
288
TSM_PicoW_Sensor/McuLib/littleFS/README.md
Normal file
288
TSM_PicoW_Sensor/McuLib/littleFS/README.md
Normal file
@@ -0,0 +1,288 @@
|
||||
## littlefs
|
||||
|
||||
A little fail-safe filesystem designed for microcontrollers.
|
||||
|
||||
```
|
||||
| | | .---._____
|
||||
.-----. | |
|
||||
--|o |---| littlefs |
|
||||
--| |---| |
|
||||
'-----' '----------'
|
||||
| | |
|
||||
```
|
||||
|
||||
**Power-loss resilience** - littlefs is designed to handle random power
|
||||
failures. All file operations have strong copy-on-write guarantees and if
|
||||
power is lost the filesystem will fall back to the last known good state.
|
||||
|
||||
**Dynamic wear leveling** - littlefs is designed with flash in mind, and
|
||||
provides wear leveling over dynamic blocks. Additionally, littlefs can
|
||||
detect bad blocks and work around them.
|
||||
|
||||
**Bounded RAM/ROM** - littlefs is designed to work with a small amount of
|
||||
memory. RAM usage is strictly bounded, which means RAM consumption does not
|
||||
change as the filesystem grows. The filesystem contains no unbounded
|
||||
recursion and dynamic memory is limited to configurable buffers that can be
|
||||
provided statically.
|
||||
|
||||
## 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,
|
||||
.cache_size = 16,
|
||||
.lookahead_size = 16,
|
||||
.block_cycles = 500,
|
||||
};
|
||||
|
||||
// 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).
|
||||
|
||||
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, file updates are not actually committed to
|
||||
the filesystem until sync or close is called on the file.
|
||||
|
||||
## Other notes
|
||||
|
||||
Littlefs is written in C, and specifically should compile with any compiler
|
||||
that conforms to the `C99` standard.
|
||||
|
||||
All littlefs calls 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.
|
||||
|
||||
## Design
|
||||
|
||||
At a high level, littlefs is a block based filesystem that uses small logs to
|
||||
store metadata and larger copy-on-write (COW) structures to store file data.
|
||||
|
||||
In littlefs, these ingredients form a sort of two-layered cake, with the small
|
||||
logs (called metadata pairs) providing fast updates to metadata anywhere on
|
||||
storage, while the COW structures store file data compactly and without any
|
||||
wear amplification cost.
|
||||
|
||||
Both of these data structures are built out of blocks, which are fed by a
|
||||
common block allocator. By limiting the number of erases allowed on a block
|
||||
per allocation, the allocator provides dynamic wear leveling over the entire
|
||||
filesystem.
|
||||
|
||||
```
|
||||
root
|
||||
.--------.--------.
|
||||
| A'| B'| |
|
||||
| | |-> |
|
||||
| | | |
|
||||
'--------'--------'
|
||||
.----' '--------------.
|
||||
A v B v
|
||||
.--------.--------. .--------.--------.
|
||||
| C'| D'| | | E'|new| |
|
||||
| | |-> | | | E'|-> |
|
||||
| | | | | | | |
|
||||
'--------'--------' '--------'--------'
|
||||
.-' '--. | '------------------.
|
||||
v v .-' v
|
||||
.--------. .--------. v .--------.
|
||||
| C | | D | .--------. write | new E |
|
||||
| | | | | E | ==> | |
|
||||
| | | | | | | |
|
||||
'--------' '--------' | | '--------'
|
||||
'--------' .-' |
|
||||
.-' '-. .-------------|------'
|
||||
v v v v
|
||||
.--------. .--------. .--------.
|
||||
| F | | G | | new F |
|
||||
| | | | | |
|
||||
| | | | | |
|
||||
'--------' '--------' '--------'
|
||||
```
|
||||
|
||||
More details on how littlefs works can be found in [DESIGN.md](DESIGN.md) and
|
||||
[SPEC.md](SPEC.md).
|
||||
|
||||
- [DESIGN.md](DESIGN.md) - A fully detailed dive into how littlefs works.
|
||||
I would suggest reading it as the tradeoffs at work are quite interesting.
|
||||
|
||||
- [SPEC.md](SPEC.md) - The on-disk specification of littlefs with all the
|
||||
nitty-gritty details. May be useful for tooling development.
|
||||
|
||||
## Testing
|
||||
|
||||
The littlefs comes with a test suite designed to run on a PC using the
|
||||
[emulated block device](bd/lfs_testbd.h) found in the `bd` 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] 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
|
||||
|
||||
- [littlefs-fuse] - A [FUSE] 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] - 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][littlefs-js-demo].
|
||||
|
||||
- [littlefs-python] - A Python wrapper for littlefs. The project allows you
|
||||
to create images of the filesystem on your PC. Check if littlefs will fit
|
||||
your needs, create images for a later download to the target memory or
|
||||
inspect the content of a binary image of the target memory.
|
||||
|
||||
- [littlefs2-rust] - A Rust wrapper for littlefs. This project allows you
|
||||
to use littlefs in a Rust-friendly API, reaping the benefits of Rust's memory
|
||||
safety and other guarantees.
|
||||
|
||||
- [nim-littlefs] - A Nim wrapper and API for littlefs. Includes a fuse
|
||||
implementation based on [littlefs-fuse]
|
||||
|
||||
- [chamelon] - A pure-OCaml implementation of (most of) littlefs, designed for
|
||||
use with the MirageOS library operating system project. It is interoperable
|
||||
with the reference implementation, with some caveats.
|
||||
|
||||
- [littlefs-disk-img-viewer] - A memory-efficient web application for viewing
|
||||
littlefs disk images in your web browser.
|
||||
|
||||
- [mklfs] - A command line tool for creating littlefs images. Used in the Lua
|
||||
RTOS ecosystem.
|
||||
|
||||
- [mklittlefs] - A command line tool for creating littlefs images. Used in the
|
||||
ESP8266 and RP2040 ecosystem.
|
||||
|
||||
- [pico-littlefs-usb] - An interface for littlefs that emulates a FAT12
|
||||
filesystem over USB. Allows mounting littlefs on a host PC without additional
|
||||
drivers.
|
||||
|
||||
- [Mbed OS] - The easiest way to get started with littlefs is to jump into Mbed
|
||||
which already has block device drivers for most forms of embedded storage.
|
||||
littlefs is available in Mbed OS as the [LittleFileSystem] class.
|
||||
|
||||
- [SPIFFS] - Another excellent embedded filesystem for NOR flash. As a more
|
||||
traditional logging filesystem with full static wear-leveling, SPIFFS will
|
||||
likely outperform littlefs on small memories such as the internal flash on
|
||||
microcontrollers.
|
||||
|
||||
- [Dhara] - An interesting NAND flash translation layer designed for small
|
||||
MCUs. It offers static wear-leveling and power-resilience with only a fixed
|
||||
_O(|address|)_ pointer structure stored on each block and in RAM.
|
||||
|
||||
- [ChaN's FatFs] - A lightweight reimplementation of the infamous FAT filesystem
|
||||
for microcontroller-scale devices. Due to limitations of FAT it can't provide
|
||||
power-loss resilience, but it does allow easy interop with PCs.
|
||||
|
||||
[BSD-3-Clause]: https://spdx.org/licenses/BSD-3-Clause.html
|
||||
[littlefs-fuse]: https://github.com/geky/littlefs-fuse
|
||||
[FUSE]: https://github.com/libfuse/libfuse
|
||||
[littlefs-js]: https://github.com/geky/littlefs-js
|
||||
[littlefs-js-demo]:http://littlefs.geky.net/demo.html
|
||||
[littlefs-python]: https://pypi.org/project/littlefs-python/
|
||||
[littlefs2-rust]: https://crates.io/crates/littlefs2
|
||||
[nim-littlefs]: https://github.com/Graveflo/nim-littlefs
|
||||
[chamelon]: https://github.com/yomimono/chamelon
|
||||
[littlefs-disk-img-viewer]: https://github.com/tniessen/littlefs-disk-img-viewer
|
||||
[mklfs]: https://github.com/whitecatboard/Lua-RTOS-ESP32/tree/master/components/mklfs/src
|
||||
[mklittlefs]: https://github.com/earlephilhower/mklittlefs
|
||||
[pico-littlefs-usb]: https://github.com/oyama/pico-littlefs-usb
|
||||
[Mbed OS]: https://github.com/armmbed/mbed-os
|
||||
[LittleFileSystem]: https://os.mbed.com/docs/mbed-os/latest/apis/littlefilesystem.html
|
||||
[SPIFFS]: https://github.com/pellepl/spiffs
|
||||
[Dhara]: https://github.com/dlbeer/dhara
|
||||
[ChaN's FatFs]: http://elm-chan.org/fsw/ff/00index_e.html
|
||||
867
TSM_PicoW_Sensor/McuLib/littleFS/SPEC.md
Normal file
867
TSM_PicoW_Sensor/McuLib/littleFS/SPEC.md
Normal file
@@ -0,0 +1,867 @@
|
||||
## littlefs technical specification
|
||||
|
||||
This is the technical specification of the little filesystem with on-disk
|
||||
version lfs2.1. This document covers the technical details of how the littlefs
|
||||
is stored on disk for introspection and tooling. This document assumes you are
|
||||
familiar with the design of the littlefs, for more info on how littlefs works
|
||||
check out [DESIGN.md](DESIGN.md).
|
||||
|
||||
```
|
||||
| | | .---._____
|
||||
.-----. | |
|
||||
--|o |---| littlefs |
|
||||
--| |---| |
|
||||
'-----' '----------'
|
||||
| | |
|
||||
```
|
||||
|
||||
## Some quick notes
|
||||
|
||||
- littlefs is a block-based filesystem. The disk is divided into an array of
|
||||
evenly sized blocks that are used as the logical unit of storage.
|
||||
|
||||
- Block pointers are stored in 32 bits, with the special value `0xffffffff`
|
||||
representing a null block address.
|
||||
|
||||
- In addition to the logical block size (which usually matches the erase
|
||||
block size), littlefs also uses a program block size and read block size.
|
||||
These determine the alignment of block device operations, but don't need
|
||||
to be consistent for portability.
|
||||
|
||||
- By default, all values in littlefs are stored in little-endian byte order.
|
||||
|
||||
## Directories / Metadata pairs
|
||||
|
||||
Metadata pairs form the backbone of littlefs and provide a system for
|
||||
distributed atomic updates. Even the superblock is stored in a metadata pair.
|
||||
|
||||
As their name suggests, a metadata pair is stored in two blocks, with one block
|
||||
providing a backup during erase cycles in case power is lost. These two blocks
|
||||
are not necessarily sequential and may be anywhere on disk, so a "pointer" to a
|
||||
metadata pair is stored as two block pointers.
|
||||
|
||||
On top of this, each metadata block behaves as an appendable log, containing a
|
||||
variable number of commits. Commits can be appended to the metadata log in
|
||||
order to update the metadata without requiring an erase cycles. Note that
|
||||
successive commits may supersede the metadata in previous commits. Only the
|
||||
most recent metadata should be considered valid.
|
||||
|
||||
The high-level layout of a metadata block is fairly simple:
|
||||
|
||||
```
|
||||
.---------------------------------------.
|
||||
.-| revision count | entries | \
|
||||
| |-------------------+ | |
|
||||
| | | |
|
||||
| | | +-- 1st commit
|
||||
| | | |
|
||||
| | +-------------------| |
|
||||
| | | CRC | /
|
||||
| |-------------------+-------------------|
|
||||
| | entries | \
|
||||
| | | |
|
||||
| | | +-- 2nd commit
|
||||
| | +-------------------+--------------| |
|
||||
| | | CRC | padding | /
|
||||
| |----+-------------------+--------------|
|
||||
| | entries | \
|
||||
| | | |
|
||||
| | | +-- 3rd commit
|
||||
| | +-------------------+---------| |
|
||||
| | | CRC | | /
|
||||
| |---------+-------------------+ |
|
||||
| | unwritten storage | more commits
|
||||
| | | |
|
||||
| | | v
|
||||
| | |
|
||||
| | |
|
||||
| '---------------------------------------'
|
||||
'---------------------------------------'
|
||||
```
|
||||
|
||||
Each metadata block contains a 32-bit revision count followed by a number of
|
||||
commits. Each commit contains a variable number of metadata entries followed
|
||||
by a 32-bit CRC.
|
||||
|
||||
Note also that entries aren't necessarily word-aligned. This allows us to
|
||||
store metadata more compactly, however we can only write to addresses that are
|
||||
aligned to our program block size. This means each commit may have padding for
|
||||
alignment.
|
||||
|
||||
Metadata block fields:
|
||||
|
||||
1. **Revision count (32-bits)** - Incremented every erase cycle. If both blocks
|
||||
contain valid commits, only the block with the most recent revision count
|
||||
should be used. Sequence comparison must be used to avoid issues with
|
||||
integer overflow.
|
||||
|
||||
2. **CRC (32-bits)** - Detects corruption from power-loss or other write
|
||||
issues. Uses a CRC-32 with a polynomial of `0x04c11db7` initialized
|
||||
with `0xffffffff`.
|
||||
|
||||
Entries themselves are stored as a 32-bit tag followed by a variable length
|
||||
blob of data. But exactly how these tags are stored is a little bit tricky.
|
||||
|
||||
Metadata blocks support both forward and backward iteration. In order to do
|
||||
this without duplicating the space for each tag, neighboring entries have their
|
||||
tags XORed together, starting with `0xffffffff`.
|
||||
|
||||
```
|
||||
Forward iteration Backward iteration
|
||||
|
||||
.-------------------. 0xffffffff .-------------------.
|
||||
| revision count | | | revision count |
|
||||
|-------------------| v |-------------------|
|
||||
| tag ~A |---> xor -> tag A | tag ~A |---> xor -> 0xffffffff
|
||||
|-------------------| | |-------------------| ^
|
||||
| data A | | | data A | |
|
||||
| | | | | |
|
||||
| | | | | |
|
||||
|-------------------| v |-------------------| |
|
||||
| tag AxB |---> xor -> tag B | tag AxB |---> xor -> tag A
|
||||
|-------------------| | |-------------------| ^
|
||||
| data B | | | data B | |
|
||||
| | | | | |
|
||||
| | | | | |
|
||||
|-------------------| v |-------------------| |
|
||||
| tag BxC |---> xor -> tag C | tag BxC |---> xor -> tag B
|
||||
|-------------------| |-------------------| ^
|
||||
| data C | | data C | |
|
||||
| | | | tag C
|
||||
| | | |
|
||||
| | | |
|
||||
'-------------------' '-------------------'
|
||||
```
|
||||
|
||||
Here's a more complete example of metadata block containing 4 entries:
|
||||
|
||||
```
|
||||
.---------------------------------------.
|
||||
.-| revision count | tag ~A | \
|
||||
| |-------------------+-------------------| |
|
||||
| | data A | |
|
||||
| | | |
|
||||
| |-------------------+-------------------| |
|
||||
| | tag AxB | data B | <--. |
|
||||
| |-------------------+ | | |
|
||||
| | | | +-- 1st commit
|
||||
| | +-------------------+---------| | |
|
||||
| | | tag BxC | | <-.| |
|
||||
| |---------+-------------------+ | || |
|
||||
| | data C | || |
|
||||
| | | || |
|
||||
| |-------------------+-------------------| || |
|
||||
| | tag CxCRC | CRC | || /
|
||||
| |-------------------+-------------------| ||
|
||||
| | tag CRCxA' | data A' | || \
|
||||
| |-------------------+ | || |
|
||||
| | | || |
|
||||
| | +-------------------+----| || +-- 2nd commit
|
||||
| | | tag CRCxA' | | || |
|
||||
| |--------------+-------------------+----| || |
|
||||
| | CRC | padding | || /
|
||||
| |--------------+----+-------------------| ||
|
||||
| | tag CRCxA'' | data A'' | <---. \
|
||||
| |-------------------+ | ||| |
|
||||
| | | ||| |
|
||||
| | +-------------------+---------| ||| |
|
||||
| | | tag A''xD | | < ||| |
|
||||
| |---------+-------------------+ | |||| +-- 3rd commit
|
||||
| | data D | |||| |
|
||||
| | +---------| |||| |
|
||||
| | | tag Dx| |||| |
|
||||
| |---------+-------------------+---------| |||| |
|
||||
| |CRC | CRC | | |||| /
|
||||
| |---------+-------------------+ | ||||
|
||||
| | unwritten storage | |||| more commits
|
||||
| | | |||| |
|
||||
| | | |||| v
|
||||
| | | ||||
|
||||
| | | ||||
|
||||
| '---------------------------------------' ||||
|
||||
'---------------------------------------' |||'- most recent A
|
||||
||'-- most recent B
|
||||
|'--- most recent C
|
||||
'---- most recent D
|
||||
```
|
||||
|
||||
Two things to note before we get into the details around tag encoding:
|
||||
|
||||
1. Each tag contains a valid bit used to indicate if the tag and containing
|
||||
commit is valid. After XORing, this bit should always be zero.
|
||||
|
||||
At the end of each commit, the valid bit of the previous tag is XORed
|
||||
with the lowest bit in the type field of the CRC tag. This allows
|
||||
the CRC tag to force the next commit to fail the valid bit test if it
|
||||
has not yet been written to.
|
||||
|
||||
2. The valid bit alone is not enough info to know if the next commit has been
|
||||
erased. We don't know the order bits will be programmed in a program block,
|
||||
so it's possible that the next commit had an attempted program that left the
|
||||
valid bit unchanged.
|
||||
|
||||
To ensure we only ever program erased bytes, each commit can contain an
|
||||
optional forward-CRC (FCRC). An FCRC contains a checksum of some amount of
|
||||
bytes in the next commit at the time it was erased.
|
||||
|
||||
```
|
||||
.-------------------. \ \
|
||||
| revision count | | |
|
||||
|-------------------| | |
|
||||
| metadata | | |
|
||||
| | +---. +-- current commit
|
||||
| | | | |
|
||||
|-------------------| | | |
|
||||
| FCRC ---|-. | |
|
||||
|-------------------| / | | |
|
||||
| CRC -----|-' /
|
||||
|-------------------| |
|
||||
| padding | | padding (does't need CRC)
|
||||
| | |
|
||||
|-------------------| \ | \
|
||||
| erased? | +-' |
|
||||
| | | | +-- next commit
|
||||
| v | / |
|
||||
| | /
|
||||
| |
|
||||
'-------------------'
|
||||
```
|
||||
|
||||
If the FCRC is missing or the checksum does not match, we must assume a
|
||||
commit was attempted but failed due to power-loss.
|
||||
|
||||
Note that end-of-block commits do not need an FCRC.
|
||||
|
||||
## Metadata tags
|
||||
|
||||
So in littlefs, 32-bit tags describe every type of metadata. And this means
|
||||
_every_ type of metadata, including file entries, directory fields, and
|
||||
global state. Even the CRCs used to mark the end of commits get their own tag.
|
||||
|
||||
Because of this, the tag format contains some densely packed information. Note
|
||||
that there are multiple levels of types which break down into more info:
|
||||
|
||||
```
|
||||
[---- 32 ----]
|
||||
[1|-- 11 --|-- 10 --|-- 10 --]
|
||||
^. ^ . ^ ^- length
|
||||
|. | . '------------ id
|
||||
|. '-----.------------------ type (type3)
|
||||
'.-----------.------------------ valid bit
|
||||
[-3-|-- 8 --]
|
||||
^ ^- chunk
|
||||
'------- type (type1)
|
||||
```
|
||||
|
||||
|
||||
Before we go further, there's one important thing to note. These tags are
|
||||
**not** stored in little-endian. Tags stored in commits are actually stored
|
||||
in big-endian (and is the only thing in littlefs stored in big-endian). This
|
||||
little bit of craziness comes from the fact that the valid bit must be the
|
||||
first bit in a commit, and when converted to little-endian, the valid bit finds
|
||||
itself in byte 4. We could restructure the tag to store the valid bit lower,
|
||||
but, because none of the fields are byte-aligned, this would be more
|
||||
complicated than just storing the tag in big-endian.
|
||||
|
||||
Another thing to note is that both the tags `0x00000000` and `0xffffffff` are
|
||||
invalid and can be used for null values.
|
||||
|
||||
Metadata tag fields:
|
||||
|
||||
1. **Valid bit (1-bit)** - Indicates if the tag is valid.
|
||||
|
||||
2. **Type3 (11-bits)** - Type of the tag. This field is broken down further
|
||||
into a 3-bit abstract type and an 8-bit chunk field. Note that the value
|
||||
`0x000` is invalid and not assigned a type.
|
||||
|
||||
1. **Type1 (3-bits)** - Abstract type of the tag. Groups the tags into
|
||||
8 categories that facilitate bitmasked lookups.
|
||||
|
||||
2. **Chunk (8-bits)** - Chunk field used for various purposes by the different
|
||||
abstract types. type1+chunk+id form a unique identifier for each tag in the
|
||||
metadata block.
|
||||
|
||||
3. **Id (10-bits)** - File id associated with the tag. Each file in a metadata
|
||||
block gets a unique id which is used to associate tags with that file. The
|
||||
special value `0x3ff` is used for any tags that are not associated with a
|
||||
file, such as directory and global metadata.
|
||||
|
||||
4. **Length (10-bits)** - Length of the data in bytes. The special value
|
||||
`0x3ff` indicates that this tag has been deleted.
|
||||
|
||||
## Metadata types
|
||||
|
||||
What follows is an exhaustive list of metadata in littlefs.
|
||||
|
||||
---
|
||||
#### `0x401` LFS_TYPE_CREATE
|
||||
|
||||
Creates a new file with this id. Note that files in a metadata block
|
||||
don't necessarily need a create tag. All a create does is move over any
|
||||
files using this id. In this sense a create is similar to insertion into
|
||||
an imaginary array of files.
|
||||
|
||||
The create and delete tags allow littlefs to keep files in a directory
|
||||
ordered alphabetically by filename.
|
||||
|
||||
---
|
||||
#### `0x4ff` LFS_TYPE_DELETE
|
||||
|
||||
Deletes the file with this id. An inverse to create, this tag moves over
|
||||
any files neighboring this id similar to a deletion from an imaginary
|
||||
array of files.
|
||||
|
||||
---
|
||||
#### `0x0xx` LFS_TYPE_NAME
|
||||
|
||||
Associates the id with a file name and file type.
|
||||
|
||||
The data contains the file name stored as an ASCII string (may be expanded to
|
||||
UTF8 in the future).
|
||||
|
||||
The chunk field in this tag indicates an 8-bit file type which can be one of
|
||||
the following.
|
||||
|
||||
Currently, the name tag must precede any other tags associated with the id and
|
||||
can not be reassigned without deleting the file.
|
||||
|
||||
Layout of the name tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][--- variable length ---]
|
||||
[1| 3| 8 | 10 | 10 ][--- (size * 8) ---]
|
||||
^ ^ ^ ^ ^- size ^- file name
|
||||
| | | '------ id
|
||||
| | '----------- file type
|
||||
| '-------------- type1 (0x0)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
Name fields:
|
||||
|
||||
1. **file type (8-bits)** - Type of the file.
|
||||
|
||||
2. **file name** - File name stored as an ASCII string.
|
||||
|
||||
---
|
||||
#### `0x001` LFS_TYPE_REG
|
||||
|
||||
Initializes the id + name as a regular file.
|
||||
|
||||
How each file is stored depends on its struct tag, which is described below.
|
||||
|
||||
---
|
||||
#### `0x002` LFS_TYPE_DIR
|
||||
|
||||
Initializes the id + name as a directory.
|
||||
|
||||
Directories in littlefs are stored on disk as a linked-list of metadata pairs,
|
||||
each pair containing any number of files in alphabetical order. A pointer to
|
||||
the directory is stored in the struct tag, which is described below.
|
||||
|
||||
---
|
||||
#### `0x0ff` LFS_TYPE_SUPERBLOCK
|
||||
|
||||
Initializes the id as a superblock entry.
|
||||
|
||||
The superblock entry is a special entry used to store format-time configuration
|
||||
and identify the filesystem.
|
||||
|
||||
The name is a bit of a misnomer. While the superblock entry serves the same
|
||||
purpose as a superblock found in other filesystems, in littlefs the superblock
|
||||
does not get a dedicated block. Instead, the superblock entry is duplicated
|
||||
across a linked-list of metadata pairs rooted on the blocks 0 and 1. The last
|
||||
metadata pair doubles as the root directory of the filesystem.
|
||||
|
||||
```
|
||||
.--------. .--------. .--------. .--------. .--------.
|
||||
.| super |->| super |->| super |->| super |->| file B |
|
||||
|| block | || block | || block | || block | || file C |
|
||||
|| | || | || | || file A | || file D |
|
||||
|'--------' |'--------' |'--------' |'--------' |'--------'
|
||||
'--------' '--------' '--------' '--------' '--------'
|
||||
|
||||
\----------------+----------------/ \----------+----------/
|
||||
superblock pairs root directory
|
||||
```
|
||||
|
||||
The filesystem starts with only the root directory. The superblock metadata
|
||||
pairs grow every time the root pair is compacted in order to prolong the
|
||||
life of the device exponentially.
|
||||
|
||||
The contents of the superblock entry are stored in a name tag with the
|
||||
superblock type and an inline-struct tag. The name tag contains the magic
|
||||
string "littlefs", while the inline-struct tag contains version and
|
||||
configuration information.
|
||||
|
||||
Layout of the superblock name tag and inline-struct tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][-- 32 --|-- 32 --]
|
||||
[1|- 11 -| 10 | 10 ][--- 64 ---]
|
||||
^ ^ ^ ^- size (8) ^- magic string ("littlefs")
|
||||
| | '------ id (0)
|
||||
| '------------ type (0x0ff)
|
||||
'----------------- valid bit
|
||||
|
||||
tag data
|
||||
[-- 32 --][-- 32 --|-- 32 --|-- 32 --]
|
||||
[1|- 11 -| 10 | 10 ][-- 32 --|-- 32 --|-- 32 --]
|
||||
^ ^ ^ ^ ^- version ^- block size ^- block count
|
||||
| | | | [-- 32 --|-- 32 --|-- 32 --]
|
||||
| | | | [-- 32 --|-- 32 --|-- 32 --]
|
||||
| | | | ^- name max ^- file max ^- attr max
|
||||
| | | '- size (24)
|
||||
| | '------ id (0)
|
||||
| '------------ type (0x201)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
Superblock fields:
|
||||
|
||||
1. **Magic string (8-bytes)** - Magic string indicating the presence of
|
||||
littlefs on the device. Must be the string "littlefs".
|
||||
|
||||
2. **Version (32-bits)** - The version of littlefs at format time. The version
|
||||
is encoded in a 32-bit value with the upper 16-bits containing the major
|
||||
version, and the lower 16-bits containing the minor version.
|
||||
|
||||
This specification describes version 2.0 (`0x00020000`).
|
||||
|
||||
3. **Block size (32-bits)** - Size of the logical block size used by the
|
||||
filesystem in bytes.
|
||||
|
||||
4. **Block count (32-bits)** - Number of blocks in the filesystem.
|
||||
|
||||
5. **Name max (32-bits)** - Maximum size of file names in bytes.
|
||||
|
||||
6. **File max (32-bits)** - Maximum size of files in bytes.
|
||||
|
||||
7. **Attr max (32-bits)** - Maximum size of file attributes in bytes.
|
||||
|
||||
The superblock must always be the first entry (id 0) in the metadata pair, and
|
||||
the name tag must always be the first tag in the metadata pair. This makes it
|
||||
so that the magic string "littlefs" will always reside at offset=8 in a valid
|
||||
littlefs superblock.
|
||||
|
||||
---
|
||||
#### `0x2xx` LFS_TYPE_STRUCT
|
||||
|
||||
Associates the id with an on-disk data structure.
|
||||
|
||||
The exact layout of the data depends on the data structure type stored in the
|
||||
chunk field and can be one of the following.
|
||||
|
||||
Any type of struct supersedes all other structs associated with the id. For
|
||||
example, appending a ctz-struct replaces an inline-struct on the same file.
|
||||
|
||||
---
|
||||
#### `0x200` LFS_TYPE_DIRSTRUCT
|
||||
|
||||
Gives the id a directory data structure.
|
||||
|
||||
Directories in littlefs are stored on disk as a linked-list of metadata pairs,
|
||||
each pair containing any number of files in alphabetical order.
|
||||
|
||||
```
|
||||
|
|
||||
v
|
||||
.--------. .--------. .--------. .--------. .--------. .--------.
|
||||
.| file A |->| file D |->| file G |->| file I |->| file J |->| file M |
|
||||
|| file B | || file E | || file H | || | || file K | || file N |
|
||||
|| file C | || file F | || | || | || file L | || |
|
||||
|'--------' |'--------' |'--------' |'--------' |'--------' |'--------'
|
||||
'--------' '--------' '--------' '--------' '--------' '--------'
|
||||
```
|
||||
|
||||
The dir-struct tag contains only the pointer to the first metadata-pair in the
|
||||
directory. The directory size is not known without traversing the directory.
|
||||
|
||||
The pointer to the next metadata-pair in the directory is stored in a tail tag,
|
||||
which is described below.
|
||||
|
||||
Layout of the dir-struct tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][-- 32 --|-- 32 --]
|
||||
[1|- 11 -| 10 | 10 ][--- 64 ---]
|
||||
^ ^ ^ ^- size (8) ^- metadata pair
|
||||
| | '------ id
|
||||
| '------------ type (0x200)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
Dir-struct fields:
|
||||
|
||||
1. **Metadata pair (8-bytes)** - Pointer to the first metadata-pair
|
||||
in the directory.
|
||||
|
||||
---
|
||||
#### `0x201` LFS_TYPE_INLINESTRUCT
|
||||
|
||||
Gives the id an inline data structure.
|
||||
|
||||
Inline structs store small files that can fit in the metadata pair. In this
|
||||
case, the file data is stored directly in the tag's data area.
|
||||
|
||||
Layout of the inline-struct tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][--- variable length ---]
|
||||
[1|- 11 -| 10 | 10 ][--- (size * 8) ---]
|
||||
^ ^ ^ ^- size ^- inline data
|
||||
| | '------ id
|
||||
| '------------ type (0x201)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
Inline-struct fields:
|
||||
|
||||
1. **Inline data** - File data stored directly in the metadata-pair.
|
||||
|
||||
---
|
||||
#### `0x202` LFS_TYPE_CTZSTRUCT
|
||||
|
||||
Gives the id a CTZ skip-list data structure.
|
||||
|
||||
CTZ skip-lists store files that can not fit in the metadata pair. These files
|
||||
are stored in a skip-list in reverse, with a pointer to the head of the
|
||||
skip-list. Note that the head of the skip-list and the file size is enough
|
||||
information to read the file.
|
||||
|
||||
How exactly CTZ skip-lists work is a bit complicated. A full explanation can be
|
||||
found in the [DESIGN.md](DESIGN.md#ctz-skip-lists).
|
||||
|
||||
A quick summary: For every _n_‍th block where _n_ is divisible by
|
||||
2‍_ˣ_, that block contains a pointer to block _n_-2‍_ˣ_.
|
||||
These pointers are stored in increasing order of _x_ in each block of the file
|
||||
before the actual data.
|
||||
|
||||
```
|
||||
|
|
||||
v
|
||||
.--------. .--------. .--------. .--------. .--------. .--------.
|
||||
| A |<-| D |<-| G |<-| J |<-| M |<-| P |
|
||||
| B |<-| E |--| H |<-| K |--| N | | Q |
|
||||
| C |<-| F |--| I |--| L |--| O | | |
|
||||
'--------' '--------' '--------' '--------' '--------' '--------'
|
||||
block 0 block 1 block 2 block 3 block 4 block 5
|
||||
1 skip 2 skips 1 skip 3 skips 1 skip
|
||||
```
|
||||
|
||||
Note that the maximum number of pointers in a block is bounded by the maximum
|
||||
file size divided by the block size. With 32 bits for file size, this results
|
||||
in a minimum block size of 104 bytes.
|
||||
|
||||
Layout of the CTZ-struct tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][-- 32 --|-- 32 --]
|
||||
[1|- 11 -| 10 | 10 ][-- 32 --|-- 32 --]
|
||||
^ ^ ^ ^ ^ ^- file size
|
||||
| | | | '-------------------- file head
|
||||
| | | '- size (8)
|
||||
| | '------ id
|
||||
| '------------ type (0x202)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
CTZ-struct fields:
|
||||
|
||||
1. **File head (32-bits)** - Pointer to the block that is the head of the
|
||||
file's CTZ skip-list.
|
||||
|
||||
2. **File size (32-bits)** - Size of the file in bytes.
|
||||
|
||||
---
|
||||
#### `0x3xx` LFS_TYPE_USERATTR
|
||||
|
||||
Attaches a user attribute to an id.
|
||||
|
||||
littlefs has a concept of "user attributes". These are small user-provided
|
||||
attributes that can be used to store things like timestamps, hashes,
|
||||
permissions, etc.
|
||||
|
||||
Each user attribute is uniquely identified by an 8-bit type which is stored in
|
||||
the chunk field, and the user attribute itself can be found in the tag's data.
|
||||
|
||||
There are currently no standard user attributes and a portable littlefs
|
||||
implementation should work with any user attributes missing.
|
||||
|
||||
Layout of the user-attr tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][--- variable length ---]
|
||||
[1| 3| 8 | 10 | 10 ][--- (size * 8) ---]
|
||||
^ ^ ^ ^ ^- size ^- attr data
|
||||
| | | '------ id
|
||||
| | '----------- attr type
|
||||
| '-------------- type1 (0x3)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
User-attr fields:
|
||||
|
||||
1. **Attr type (8-bits)** - Type of the user attributes.
|
||||
|
||||
2. **Attr data** - The data associated with the user attribute.
|
||||
|
||||
---
|
||||
#### `0x6xx` LFS_TYPE_TAIL
|
||||
|
||||
Provides the tail pointer for the metadata pair itself.
|
||||
|
||||
The metadata pair's tail pointer is used in littlefs for a linked-list
|
||||
containing all metadata pairs. The chunk field contains the type of the tail,
|
||||
which indicates if the following metadata pair is a part of the directory
|
||||
(hard-tail) or only used to traverse the filesystem (soft-tail).
|
||||
|
||||
```
|
||||
.--------.
|
||||
.| dir A |-.
|
||||
||softtail| |
|
||||
.--------| |-'
|
||||
| |'--------'
|
||||
| '---|--|-'
|
||||
| .-' '-------------.
|
||||
| v v
|
||||
| .--------. .--------. .--------.
|
||||
'->| dir B |->| dir B |->| dir C |
|
||||
||hardtail| ||softtail| || |
|
||||
|| | || | || |
|
||||
|'--------' |'--------' |'--------'
|
||||
'--------' '--------' '--------'
|
||||
```
|
||||
|
||||
Currently any type supersedes any other preceding tails in the metadata pair,
|
||||
but this may change if additional metadata pair state is added.
|
||||
|
||||
A note about the metadata pair linked-list: Normally, this linked-list contains
|
||||
every metadata pair in the filesystem. However, there are some operations that
|
||||
can cause this linked-list to become out of sync if a power-loss were to occur.
|
||||
When this happens, littlefs sets the "sync" flag in the global state. How
|
||||
exactly this flag is stored is described below.
|
||||
|
||||
When the sync flag is set:
|
||||
|
||||
1. The linked-list may contain an orphaned directory that has been removed in
|
||||
the filesystem.
|
||||
2. The linked-list may contain a metadata pair with a bad block that has been
|
||||
replaced in the filesystem.
|
||||
|
||||
If the sync flag is set, the threaded linked-list must be checked for these
|
||||
errors before it can be used reliably. Note that the threaded linked-list can
|
||||
be ignored if littlefs is mounted read-only.
|
||||
|
||||
Layout of the tail tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][-- 32 --|-- 32 --]
|
||||
[1| 3| 8 | 10 | 10 ][--- 64 ---]
|
||||
^ ^ ^ ^ ^- size (8) ^- metadata pair
|
||||
| | | '------ id
|
||||
| | '---------- tail type
|
||||
| '------------- type1 (0x6)
|
||||
'---------------- valid bit
|
||||
```
|
||||
|
||||
Tail fields:
|
||||
|
||||
1. **Tail type (8-bits)** - Type of the tail pointer.
|
||||
|
||||
2. **Metadata pair (8-bytes)** - Pointer to the next metadata-pair.
|
||||
|
||||
---
|
||||
#### `0x600` LFS_TYPE_SOFTTAIL
|
||||
|
||||
Provides a tail pointer that points to the next metadata pair in the
|
||||
filesystem.
|
||||
|
||||
In this case, the next metadata pair is not a part of our current directory
|
||||
and should only be followed when traversing the entire filesystem.
|
||||
|
||||
---
|
||||
#### `0x601` LFS_TYPE_HARDTAIL
|
||||
|
||||
Provides a tail pointer that points to the next metadata pair in the
|
||||
directory.
|
||||
|
||||
In this case, the next metadata pair belongs to the current directory. Note
|
||||
that because directories in littlefs are sorted alphabetically, the next
|
||||
metadata pair should only contain filenames greater than any filename in the
|
||||
current pair.
|
||||
|
||||
---
|
||||
#### `0x7xx` LFS_TYPE_GSTATE
|
||||
|
||||
Provides delta bits for global state entries.
|
||||
|
||||
littlefs has a concept of "global state". This is a small set of state that
|
||||
can be updated by a commit to _any_ metadata pair in the filesystem.
|
||||
|
||||
The way this works is that the global state is stored as a set of deltas
|
||||
distributed across the filesystem such that the global state can be found by
|
||||
the xor-sum of these deltas.
|
||||
|
||||
```
|
||||
.--------. .--------. .--------. .--------. .--------.
|
||||
.| |->| gdelta |->| |->| gdelta |->| gdelta |
|
||||
|| | || 0x23 | || | || 0xff | || 0xce |
|
||||
|| | || | || | || | || |
|
||||
|'--------' |'--------' |'--------' |'--------' |'--------'
|
||||
'--------' '----|---' '--------' '----|---' '----|---'
|
||||
v v v
|
||||
0x00 --> xor ------------------> xor ------> xor --> gstate = 0x12
|
||||
```
|
||||
|
||||
Note that storing globals this way is very expensive in terms of storage usage,
|
||||
so any global state should be kept very small.
|
||||
|
||||
The size and format of each piece of global state depends on the type, which
|
||||
is stored in the chunk field. Currently, the only global state is move state,
|
||||
which is outlined below.
|
||||
|
||||
---
|
||||
#### `0x7ff` LFS_TYPE_MOVESTATE
|
||||
|
||||
Provides delta bits for the global move state.
|
||||
|
||||
The move state in littlefs is used to store info about operations that could
|
||||
cause to filesystem to go out of sync if the power is lost. The operations
|
||||
where this could occur is moves of files between metadata pairs and any
|
||||
operation that changes metadata pairs on the threaded linked-list.
|
||||
|
||||
In the case of moves, the move state contains a tag + metadata pair describing
|
||||
the source of the ongoing move. If this tag is non-zero, that means that power
|
||||
was lost during a move, and the file exists in two different locations. If this
|
||||
happens, the source of the move should be considered deleted, and the move
|
||||
should be completed (the source should be deleted) before any other write
|
||||
operations to the filesystem.
|
||||
|
||||
In the case of operations to the threaded linked-list, a single "sync" bit is
|
||||
used to indicate that a modification is ongoing. If this sync flag is set, the
|
||||
threaded linked-list will need to be checked for errors before it can be used
|
||||
reliably. The exact cases to check for are described above in the tail tag.
|
||||
|
||||
Layout of the move state:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][-- 32 --|-- 32 --|-- 32 --]
|
||||
[1|- 11 -| 10 | 10 ][1|- 11 -| 10 | 10 |--- 64 ---]
|
||||
^ ^ ^ ^ ^ ^ ^ ^- padding (0) ^- metadata pair
|
||||
| | | | | | '------ move id
|
||||
| | | | | '------------ move type
|
||||
| | | | '----------------- sync bit
|
||||
| | | |
|
||||
| | | '- size (12)
|
||||
| | '------ id (0x3ff)
|
||||
| '------------ type (0x7ff)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
Move state fields:
|
||||
|
||||
1. **Sync bit (1-bit)** - Indicates if the metadata pair threaded linked-list
|
||||
is in-sync. If set, the threaded linked-list should be checked for errors.
|
||||
|
||||
2. **Move type (11-bits)** - Type of move being performed. Must be either
|
||||
`0x000`, indicating no move, or `0x4ff` indicating the source file should
|
||||
be deleted.
|
||||
|
||||
3. **Move id (10-bits)** - The file id being moved.
|
||||
|
||||
4. **Metadata pair (8-bytes)** - Pointer to the metadata-pair containing
|
||||
the move.
|
||||
|
||||
---
|
||||
#### `0x5xx` LFS_TYPE_CRC
|
||||
|
||||
Last but not least, the CRC tag marks the end of a commit and provides a
|
||||
checksum for any commits to the metadata block.
|
||||
|
||||
The first 32-bits of the data contain a CRC-32 with a polynomial of
|
||||
`0x04c11db7` initialized with `0xffffffff`. This CRC provides a checksum for
|
||||
all metadata since the previous CRC tag, including the CRC tag itself. For
|
||||
the first commit, this includes the revision count for the metadata block.
|
||||
|
||||
However, the size of the data is not limited to 32-bits. The data field may
|
||||
larger to pad the commit to the next program-aligned boundary.
|
||||
|
||||
In addition, the CRC tag's chunk field contains a set of flags which can
|
||||
change the behaviour of commits. Currently the only flag in use is the lowest
|
||||
bit, which determines the expected state of the valid bit for any following
|
||||
tags. This is used to guarantee that unwritten storage in a metadata block
|
||||
will be detected as invalid.
|
||||
|
||||
Layout of the CRC tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][-- 32 --|--- variable length ---]
|
||||
[1| 3| 8 | 10 | 10 ][-- 32 --|--- (size * 8 - 32) ---]
|
||||
^ ^ ^ ^ ^ ^- crc ^- padding
|
||||
| | | | '- size
|
||||
| | | '------ id (0x3ff)
|
||||
| | '----------- valid state
|
||||
| '-------------- type1 (0x5)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
CRC fields:
|
||||
|
||||
1. **Valid state (1-bit)** - Indicates the expected value of the valid bit for
|
||||
any tags in the next commit.
|
||||
|
||||
2. **CRC (32-bits)** - CRC-32 with a polynomial of `0x04c11db7` initialized
|
||||
with `0xffffffff`.
|
||||
|
||||
3. **Padding** - Padding to the next program-aligned boundary. No guarantees
|
||||
are made about the contents.
|
||||
|
||||
---
|
||||
#### `0x5ff` LFS_TYPE_FCRC
|
||||
|
||||
Added in lfs2.1, the optional FCRC tag contains a checksum of some amount of
|
||||
bytes in the next commit at the time it was erased. This allows us to ensure
|
||||
that we only ever program erased bytes, even if a previous commit failed due
|
||||
to power-loss.
|
||||
|
||||
When programming a commit, the FCRC size must be at least as large as the
|
||||
program block size. However, the program block is not saved on disk, and can
|
||||
change between mounts, so the FCRC size on disk may be different than the
|
||||
current program block size.
|
||||
|
||||
If the FCRC is missing or the checksum does not match, we must assume a
|
||||
commit was attempted but failed due to power-loss.
|
||||
|
||||
Layout of the FCRC tag:
|
||||
|
||||
```
|
||||
tag data
|
||||
[-- 32 --][-- 32 --|-- 32 --]
|
||||
[1|- 11 -| 10 | 10 ][-- 32 --|-- 32 --]
|
||||
^ ^ ^ ^ ^- fcrc size ^- fcrc
|
||||
| | | '- size (8)
|
||||
| | '------ id (0x3ff)
|
||||
| '------------ type (0x5ff)
|
||||
'----------------- valid bit
|
||||
```
|
||||
|
||||
FCRC fields:
|
||||
|
||||
1. **FCRC size (32-bits)** - Number of bytes after this commit's CRC tag's
|
||||
padding to include in the FCRC.
|
||||
|
||||
2. **FCRC (32-bits)** - CRC of the bytes after this commit's CRC tag's padding
|
||||
when erased. Like the CRC tag, this uses a CRC-32 with a polynomial of
|
||||
`0x04c11db7` initialized with `0xffffffff`.
|
||||
|
||||
---
|
||||
6465
TSM_PicoW_Sensor/McuLib/littleFS/lfs.c
Normal file
6465
TSM_PicoW_Sensor/McuLib/littleFS/lfs.c
Normal file
File diff suppressed because it is too large
Load Diff
799
TSM_PicoW_Sensor/McuLib/littleFS/lfs.h
Normal file
799
TSM_PicoW_Sensor/McuLib/littleFS/lfs.h
Normal file
@@ -0,0 +1,799 @@
|
||||
/*
|
||||
* The little filesystem
|
||||
*
|
||||
* Copyright (c) 2022, The littlefs authors.
|
||||
* 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>
|
||||
#include "lfs_util.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 0x00020009
|
||||
#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 0x00020001
|
||||
#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;
|
||||
|
||||
// Maximum name size in bytes, may be redefined to reduce the size of the
|
||||
// info struct. Limited to <= 1022. Stored in superblock and must be
|
||||
// respected by other littlefs drivers.
|
||||
#ifndef LFS_NAME_MAX
|
||||
#define LFS_NAME_MAX 255
|
||||
#endif
|
||||
|
||||
// Maximum size of a file in bytes, may be redefined to limit to support other
|
||||
// drivers. Limited on disk to <= 2147483647. Stored in superblock and must be
|
||||
// respected by other littlefs drivers.
|
||||
#ifndef LFS_FILE_MAX
|
||||
#define LFS_FILE_MAX 2147483647
|
||||
#endif
|
||||
|
||||
// Maximum size of custom attributes in bytes, may be redefined, but there is
|
||||
// no real benefit to using a smaller LFS_ATTR_MAX. Limited to <= 1022. Stored
|
||||
// in superblock and must be respected by other littlefs drivers.
|
||||
#ifndef LFS_ATTR_MAX
|
||||
#define LFS_ATTR_MAX 1022
|
||||
#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 = -84, // 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
|
||||
LFS_ERR_NOATTR = -61, // No data/attr available
|
||||
LFS_ERR_NAMETOOLONG = -36, // File name too long
|
||||
};
|
||||
|
||||
// File types
|
||||
enum lfs_type {
|
||||
// file types
|
||||
LFS_TYPE_REG = 0x001,
|
||||
LFS_TYPE_DIR = 0x002,
|
||||
|
||||
// internally used types
|
||||
LFS_TYPE_SPLICE = 0x400,
|
||||
LFS_TYPE_NAME = 0x000,
|
||||
LFS_TYPE_STRUCT = 0x200,
|
||||
LFS_TYPE_USERATTR = 0x300,
|
||||
LFS_TYPE_FROM = 0x100,
|
||||
LFS_TYPE_TAIL = 0x600,
|
||||
LFS_TYPE_GLOBALS = 0x700,
|
||||
LFS_TYPE_CRC = 0x500,
|
||||
|
||||
// internally used type specializations
|
||||
LFS_TYPE_CREATE = 0x401,
|
||||
LFS_TYPE_DELETE = 0x4ff,
|
||||
LFS_TYPE_SUPERBLOCK = 0x0ff,
|
||||
LFS_TYPE_DIRSTRUCT = 0x200,
|
||||
LFS_TYPE_CTZSTRUCT = 0x202,
|
||||
LFS_TYPE_INLINESTRUCT = 0x201,
|
||||
LFS_TYPE_SOFTTAIL = 0x600,
|
||||
LFS_TYPE_HARDTAIL = 0x601,
|
||||
LFS_TYPE_MOVESTATE = 0x7ff,
|
||||
LFS_TYPE_CCRC = 0x500,
|
||||
LFS_TYPE_FCRC = 0x5ff,
|
||||
|
||||
// internal chip sources
|
||||
LFS_FROM_NOOP = 0x000,
|
||||
LFS_FROM_MOVE = 0x101,
|
||||
LFS_FROM_USERATTRS = 0x102,
|
||||
};
|
||||
|
||||
// File open flags
|
||||
enum lfs_open_flags {
|
||||
// open flags
|
||||
LFS_O_RDONLY = 1, // Open a file as read only
|
||||
#ifndef LFS_READONLY
|
||||
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
|
||||
#endif
|
||||
|
||||
// internally used flags
|
||||
#ifndef LFS_READONLY
|
||||
LFS_F_DIRTY = 0x010000, // File does not match storage
|
||||
LFS_F_WRITING = 0x020000, // File has been written since last flush
|
||||
#endif
|
||||
LFS_F_READING = 0x040000, // File has been read since last flush
|
||||
#ifndef LFS_READONLY
|
||||
LFS_F_ERRED = 0x080000, // An error occurred during write
|
||||
#endif
|
||||
LFS_F_INLINE = 0x100000, // Currently inlined in directory entry
|
||||
};
|
||||
|
||||
// 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 propagated
|
||||
// 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 propagated 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 propagated 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 propagated to the user.
|
||||
int (*sync)(const struct lfs_config *c);
|
||||
|
||||
#ifdef LFS_THREADSAFE
|
||||
// Lock the underlying block device. Negative error codes
|
||||
// are propagated to the user.
|
||||
int (*lock)(const struct lfs_config *c);
|
||||
|
||||
// Unlock the underlying block device. Negative error codes
|
||||
// are propagated to the user.
|
||||
int (*unlock)(const struct lfs_config *c);
|
||||
#endif
|
||||
|
||||
// Minimum size of a block read in bytes. All read operations will be a
|
||||
// multiple of this value.
|
||||
lfs_size_t read_size;
|
||||
|
||||
// Minimum size of a block program in bytes. All program operations will be
|
||||
// a multiple of this value.
|
||||
lfs_size_t prog_size;
|
||||
|
||||
// Size of an erasable block in bytes. This does not impact ram consumption
|
||||
// and may be larger than the physical erase size. However, non-inlined
|
||||
// files take up at minimum one block. Must be a multiple of the read and
|
||||
// program sizes.
|
||||
lfs_size_t block_size;
|
||||
|
||||
// Number of erasable blocks on the device. Defaults to block_count stored
|
||||
// on disk when zero.
|
||||
lfs_size_t block_count;
|
||||
|
||||
// Number of erase cycles before littlefs evicts metadata logs and moves
|
||||
// the metadata to another block. Suggested values are in the
|
||||
// range 100-1000, with large values having better performance at the cost
|
||||
// of less consistent wear distribution.
|
||||
//
|
||||
// Set to -1 to disable block-level wear-leveling.
|
||||
int32_t block_cycles;
|
||||
|
||||
// Size of block caches in bytes. Each cache buffers a portion of a block in
|
||||
// RAM. The littlefs needs a read cache, a program cache, and one additional
|
||||
// cache per file. Larger caches can improve performance by storing more
|
||||
// data and reducing the number of disk accesses. Must be a multiple of the
|
||||
// read and program sizes, and a factor of the block size.
|
||||
lfs_size_t cache_size;
|
||||
|
||||
// Size of the lookahead buffer in bytes. A larger lookahead buffer
|
||||
// increases the number of blocks found during an allocation pass. The
|
||||
// lookahead buffer is stored as a compact bitmap, so each byte of RAM
|
||||
// can track 8 blocks.
|
||||
lfs_size_t lookahead_size;
|
||||
|
||||
// Threshold for metadata compaction during lfs_fs_gc in bytes. Metadata
|
||||
// pairs that exceed this threshold will be compacted during lfs_fs_gc.
|
||||
// Defaults to ~88% block_size when zero, though the default may change
|
||||
// in the future.
|
||||
//
|
||||
// Note this only affects lfs_fs_gc. Normal compactions still only occur
|
||||
// when full.
|
||||
//
|
||||
// Set to -1 to disable metadata compaction during lfs_fs_gc.
|
||||
lfs_size_t compact_thresh;
|
||||
|
||||
// Optional statically allocated read buffer. Must be cache_size.
|
||||
// By default lfs_malloc is used to allocate this buffer.
|
||||
void *read_buffer;
|
||||
|
||||
// Optional statically allocated program buffer. Must be cache_size.
|
||||
// By default lfs_malloc is used to allocate this buffer.
|
||||
void *prog_buffer;
|
||||
|
||||
// Optional statically allocated lookahead buffer. Must be lookahead_size.
|
||||
// By default lfs_malloc is used to allocate this buffer.
|
||||
void *lookahead_buffer;
|
||||
|
||||
// Optional upper limit on length of file names in bytes. No downside for
|
||||
// larger names except the size of the info struct which is controlled by
|
||||
// the LFS_NAME_MAX define. Defaults to LFS_NAME_MAX or name_max stored on
|
||||
// disk when zero.
|
||||
lfs_size_t name_max;
|
||||
|
||||
// Optional upper limit on files in bytes. No downside for larger files
|
||||
// but must be <= LFS_FILE_MAX. Defaults to LFS_FILE_MAX or file_max stored
|
||||
// on disk when zero.
|
||||
lfs_size_t file_max;
|
||||
|
||||
// Optional upper limit on custom attributes in bytes. No downside for
|
||||
// larger attributes size but must be <= LFS_ATTR_MAX. Defaults to
|
||||
// LFS_ATTR_MAX or attr_max stored on disk when zero.
|
||||
lfs_size_t attr_max;
|
||||
|
||||
// Optional upper limit on total space given to metadata pairs in bytes. On
|
||||
// devices with large blocks (e.g. 128kB) setting this to a low size (2-8kB)
|
||||
// can help bound the metadata compaction time. Must be <= block_size.
|
||||
// Defaults to block_size when zero.
|
||||
lfs_size_t metadata_max;
|
||||
|
||||
// Optional upper limit on inlined files in bytes. Inlined files live in
|
||||
// metadata and decrease storage requirements, but may be limited to
|
||||
// improve metadata-related performance. Must be <= cache_size, <=
|
||||
// attr_max, and <= block_size/8. Defaults to the largest possible
|
||||
// inline_max when zero.
|
||||
//
|
||||
// Set to -1 to disable inlined files.
|
||||
lfs_size_t inline_max;
|
||||
|
||||
#ifdef LFS_MULTIVERSION
|
||||
// On-disk version to use when writing in the form of 16-bit major version
|
||||
// + 16-bit minor version. This limiting metadata to what is supported by
|
||||
// older minor versions. Note that some features will be lost. Defaults to
|
||||
// to the most recent minor version when zero.
|
||||
uint32_t disk_version;
|
||||
#endif
|
||||
};
|
||||
|
||||
// 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. Limited to 32-bits.
|
||||
lfs_size_t size;
|
||||
|
||||
// Name of the file stored as a null-terminated string. Limited to
|
||||
// LFS_NAME_MAX+1, which can be changed by redefining LFS_NAME_MAX to
|
||||
// reduce RAM. LFS_NAME_MAX is stored in superblock and must be
|
||||
// respected by other littlefs drivers.
|
||||
char name[LFS_NAME_MAX+1];
|
||||
};
|
||||
|
||||
// Filesystem info structure
|
||||
struct lfs_fsinfo {
|
||||
// On-disk version.
|
||||
uint32_t disk_version;
|
||||
|
||||
// Size of a logical block in bytes.
|
||||
lfs_size_t block_size;
|
||||
|
||||
// Number of logical blocks in filesystem.
|
||||
lfs_size_t block_count;
|
||||
|
||||
// Upper limit on the length of file names in bytes.
|
||||
lfs_size_t name_max;
|
||||
|
||||
// Upper limit on the size of files in bytes.
|
||||
lfs_size_t file_max;
|
||||
|
||||
// Upper limit on the size of custom attributes in bytes.
|
||||
lfs_size_t attr_max;
|
||||
};
|
||||
|
||||
// Custom attribute structure, used to describe custom attributes
|
||||
// committed atomically during file writes.
|
||||
struct lfs_attr {
|
||||
// 8-bit type of attribute, provided by user and used to
|
||||
// identify the attribute
|
||||
uint8_t type;
|
||||
|
||||
// Pointer to buffer containing the attribute
|
||||
void *buffer;
|
||||
|
||||
// Size of attribute in bytes, limited to LFS_ATTR_MAX
|
||||
lfs_size_t size;
|
||||
};
|
||||
|
||||
// Optional configuration provided during lfs_file_opencfg
|
||||
struct lfs_file_config {
|
||||
// Optional statically allocated file buffer. Must be cache_size.
|
||||
// By default lfs_malloc is used to allocate this buffer.
|
||||
void *buffer;
|
||||
|
||||
// Optional list of custom attributes related to the file. If the file
|
||||
// is opened with read access, these attributes will be read from disk
|
||||
// during the open call. If the file is opened with write access, the
|
||||
// attributes will be written to disk every file sync or close. This
|
||||
// write occurs atomically with update to the file's contents.
|
||||
//
|
||||
// Custom attributes are uniquely identified by an 8-bit type and limited
|
||||
// to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller
|
||||
// than the buffer, it will be padded with zeros. If the stored attribute
|
||||
// is larger, then it will be silently truncated. If the attribute is not
|
||||
// found, it will be created implicitly.
|
||||
struct lfs_attr *attrs;
|
||||
|
||||
// Number of custom attributes in the list
|
||||
lfs_size_t attr_count;
|
||||
};
|
||||
|
||||
|
||||
/// internal littlefs data structures ///
|
||||
typedef struct lfs_cache {
|
||||
lfs_block_t block;
|
||||
lfs_off_t off;
|
||||
lfs_size_t size;
|
||||
uint8_t *buffer;
|
||||
} lfs_cache_t;
|
||||
|
||||
typedef struct lfs_mdir {
|
||||
lfs_block_t pair[2];
|
||||
uint32_t rev;
|
||||
lfs_off_t off;
|
||||
uint32_t etag;
|
||||
uint16_t count;
|
||||
bool erased;
|
||||
bool split;
|
||||
lfs_block_t tail[2];
|
||||
} lfs_mdir_t;
|
||||
|
||||
// littlefs directory type
|
||||
typedef struct lfs_dir {
|
||||
struct lfs_dir *next;
|
||||
uint16_t id;
|
||||
uint8_t type;
|
||||
lfs_mdir_t m;
|
||||
|
||||
lfs_off_t pos;
|
||||
lfs_block_t head[2];
|
||||
} lfs_dir_t;
|
||||
|
||||
// littlefs file type
|
||||
typedef struct lfs_file {
|
||||
struct lfs_file *next;
|
||||
uint16_t id;
|
||||
uint8_t type;
|
||||
lfs_mdir_t m;
|
||||
|
||||
struct lfs_ctz {
|
||||
lfs_block_t head;
|
||||
lfs_size_t size;
|
||||
} ctz;
|
||||
|
||||
uint32_t flags;
|
||||
lfs_off_t pos;
|
||||
lfs_block_t block;
|
||||
lfs_off_t off;
|
||||
lfs_cache_t cache;
|
||||
|
||||
const struct lfs_file_config *cfg;
|
||||
} lfs_file_t;
|
||||
|
||||
typedef struct lfs_superblock {
|
||||
uint32_t version;
|
||||
lfs_size_t block_size;
|
||||
lfs_size_t block_count;
|
||||
lfs_size_t name_max;
|
||||
lfs_size_t file_max;
|
||||
lfs_size_t attr_max;
|
||||
} lfs_superblock_t;
|
||||
|
||||
typedef struct lfs_gstate {
|
||||
uint32_t tag;
|
||||
lfs_block_t pair[2];
|
||||
} lfs_gstate_t;
|
||||
|
||||
// The littlefs filesystem type
|
||||
typedef struct lfs {
|
||||
lfs_cache_t rcache;
|
||||
lfs_cache_t pcache;
|
||||
|
||||
lfs_block_t root[2];
|
||||
struct lfs_mlist {
|
||||
struct lfs_mlist *next;
|
||||
uint16_t id;
|
||||
uint8_t type;
|
||||
lfs_mdir_t m;
|
||||
} *mlist;
|
||||
uint32_t seed;
|
||||
|
||||
lfs_gstate_t gstate;
|
||||
lfs_gstate_t gdisk;
|
||||
lfs_gstate_t gdelta;
|
||||
|
||||
struct lfs_lookahead {
|
||||
lfs_block_t start;
|
||||
lfs_block_t size;
|
||||
lfs_block_t next;
|
||||
lfs_block_t ckpoint;
|
||||
uint8_t *buffer;
|
||||
} lookahead;
|
||||
|
||||
const struct lfs_config *cfg;
|
||||
lfs_size_t block_count;
|
||||
lfs_size_t name_max;
|
||||
lfs_size_t file_max;
|
||||
lfs_size_t attr_max;
|
||||
lfs_size_t inline_max;
|
||||
|
||||
#ifdef LFS_MIGRATE
|
||||
struct lfs1 *lfs1;
|
||||
#endif
|
||||
} lfs_t;
|
||||
|
||||
|
||||
/// Filesystem functions ///
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// 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);
|
||||
#endif
|
||||
|
||||
// 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 ///
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// 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);
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// 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);
|
||||
#endif
|
||||
|
||||
// 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);
|
||||
|
||||
// Get a custom attribute
|
||||
//
|
||||
// Custom attributes are uniquely identified by an 8-bit type and limited
|
||||
// to LFS_ATTR_MAX bytes. When read, if the stored attribute is smaller than
|
||||
// the buffer, it will be padded with zeros. If the stored attribute is larger,
|
||||
// then it will be silently truncated. If no attribute is found, the error
|
||||
// LFS_ERR_NOATTR is returned and the buffer is filled with zeros.
|
||||
//
|
||||
// Returns the size of the attribute, or a negative error code on failure.
|
||||
// Note, the returned size is the size of the attribute on disk, irrespective
|
||||
// of the size of the buffer. This can be used to dynamically allocate a buffer
|
||||
// or check for existence.
|
||||
lfs_ssize_t lfs_getattr(lfs_t *lfs, const char *path,
|
||||
uint8_t type, void *buffer, lfs_size_t size);
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Set custom attributes
|
||||
//
|
||||
// Custom attributes are uniquely identified by an 8-bit type and limited
|
||||
// to LFS_ATTR_MAX bytes. If an attribute is not found, it will be
|
||||
// implicitly created.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_setattr(lfs_t *lfs, const char *path,
|
||||
uint8_t type, const void *buffer, lfs_size_t size);
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Removes a custom attribute
|
||||
//
|
||||
// If an attribute is not found, nothing happens.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type);
|
||||
#endif
|
||||
|
||||
|
||||
/// File operations ///
|
||||
|
||||
#ifndef LFS_NO_MALLOC
|
||||
// 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);
|
||||
|
||||
// if LFS_NO_MALLOC is defined, lfs_file_open() will fail with LFS_ERR_NOMEM
|
||||
// thus use lfs_file_opencfg() with config.buffer set.
|
||||
#endif
|
||||
|
||||
// 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 remain 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);
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// 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);
|
||||
#endif
|
||||
|
||||
// Change the position of the file
|
||||
//
|
||||
// The change in position is determined by the offset and whence flag.
|
||||
// Returns the new 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);
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// 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);
|
||||
#endif
|
||||
|
||||
// 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_SET)
|
||||
// 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 ///
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Create a directory
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_mkdir(lfs_t *lfs, const char *path);
|
||||
#endif
|
||||
|
||||
// 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 positive value on success, 0 at the end of directory,
|
||||
// or 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);
|
||||
|
||||
|
||||
/// Filesystem-level filesystem operations
|
||||
|
||||
// Find on-disk info about the filesystem
|
||||
//
|
||||
// Fills out the fsinfo structure based on the filesystem found on-disk.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_fs_stat(lfs_t *lfs, struct lfs_fsinfo *fsinfo);
|
||||
|
||||
// Finds the current size of the filesystem
|
||||
//
|
||||
// Note: Result is best effort. If files share COW structures, the returned
|
||||
// size may be larger than the filesystem actually is.
|
||||
//
|
||||
// Returns the number of allocated blocks, or a negative error code on failure.
|
||||
lfs_ssize_t lfs_fs_size(lfs_t *lfs);
|
||||
|
||||
// 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_fs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Attempt to make the filesystem consistent and ready for writing
|
||||
//
|
||||
// Calling this function is not required, consistency will be implicitly
|
||||
// enforced on the first operation that writes to the filesystem, but this
|
||||
// function allows the work to be performed earlier and without other
|
||||
// filesystem changes.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_fs_mkconsistent(lfs_t *lfs);
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Attempt any janitorial work
|
||||
//
|
||||
// This currently:
|
||||
// 1. Calls mkconsistent if not already consistent
|
||||
// 2. Compacts metadata > compact_thresh
|
||||
// 3. Populates the block allocator
|
||||
//
|
||||
// Though additional janitorial work may be added in the future.
|
||||
//
|
||||
// Calling this function is not required, but may allow the offloading of
|
||||
// expensive janitorial work to a less time-critical code path.
|
||||
//
|
||||
// Returns a negative error code on failure. Accomplishing nothing is not
|
||||
// an error.
|
||||
int lfs_fs_gc(lfs_t *lfs);
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
// Grows the filesystem to a new size, updating the superblock with the new
|
||||
// block count.
|
||||
//
|
||||
// Note: This is irreversible.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_fs_grow(lfs_t *lfs, lfs_size_t block_count);
|
||||
#endif
|
||||
|
||||
#ifndef LFS_READONLY
|
||||
#ifdef LFS_MIGRATE
|
||||
// Attempts to migrate a previous version of littlefs
|
||||
//
|
||||
// Behaves similarly to the lfs_format function. Attempts to mount
|
||||
// the previous version of littlefs and update the filesystem so it can be
|
||||
// mounted with the current version of 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_migrate(lfs_t *lfs, const struct lfs_config *cfg);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
33
TSM_PicoW_Sensor/McuLib/littleFS/lfs_config.h
Normal file
33
TSM_PicoW_Sensor/McuLib/littleFS/lfs_config.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Erich Styger
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef LITTLEFS_LFSCONFIG_H_
|
||||
#define LITTLEFS_LFSCONFIG_H_
|
||||
|
||||
#include "McuLibconfig.h" /* to know if RTOS is used */
|
||||
#include "McuRTOS.h" /* for RTOS malloc()/free() */
|
||||
|
||||
#ifndef LITTLEFS_CONFIG_ENABLED
|
||||
#define LITTLEFS_CONFIG_ENABLED (0)
|
||||
/*!< 1: if LittleFS module is enabled; 0: no littleFS support */
|
||||
#endif
|
||||
|
||||
#ifndef LITTLEFS_CONFIG_THREAD_SAFE
|
||||
#define LITTLEFS_CONFIG_THREAD_SAFE (1 && McuLib_CONFIG_SDK_USE_FREERTOS)
|
||||
/*!< 1: make littleFS thread safe; 0: littleFS is not thread safe */
|
||||
#endif
|
||||
|
||||
#if LITTLEFS_CONFIG_THREAD_SAFE
|
||||
#define LFS_THREADSAFE
|
||||
/* define macro to tell FS to be thread safe */
|
||||
#endif
|
||||
|
||||
#ifndef LITTLEFS_CONFIG_USE_FREERTOS_HEAP
|
||||
#define LITTLEFS_CONFIG_USE_FREERTOS_HEAP (McuLib_CONFIG_SDK_USE_FREERTOS)
|
||||
/*!< 1: use FreeRTOS Heap (default), 0: use stdlib malloc() and free() */
|
||||
#endif
|
||||
|
||||
#endif /* LITTLEFS_LFSCONFIG_H_ */
|
||||
42
TSM_PicoW_Sensor/McuLib/littleFS/lfs_util.c
Normal file
42
TSM_PicoW_Sensor/McuLib/littleFS/lfs_util.c
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* lfs util functions
|
||||
*
|
||||
* Copyright (c) 2022, The littlefs authors.
|
||||
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#include "lfs_util.h"
|
||||
|
||||
#include "lfs_config.h" /* << EST */
|
||||
#if LITTLEFS_CONFIG_ENABLED /* << EST */
|
||||
|
||||
// Only compile if user does not provide custom config
|
||||
#ifndef LFS_CONFIG
|
||||
|
||||
|
||||
// If user provides their own CRC impl we don't need this
|
||||
#ifndef LFS_CRC
|
||||
// Software CRC implementation with small lookup table
|
||||
uint32_t lfs_crc(uint32_t 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];
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* << EST LITTLEFS_CONFIG_ENABLED */
|
||||
|
||||
261
TSM_PicoW_Sensor/McuLib/littleFS/lfs_util.h
Normal file
261
TSM_PicoW_Sensor/McuLib/littleFS/lfs_util.h
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* lfs utility functions
|
||||
*
|
||||
* Copyright (c) 2022, The littlefs authors.
|
||||
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#ifndef LFS_UTIL_H
|
||||
#define LFS_UTIL_H
|
||||
|
||||
#include "lfs_config.h" /* << EST */
|
||||
#if LITTLEFS_CONFIG_ENABLED /* << EST */
|
||||
|
||||
// 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>
|
||||
#include <inttypes.h> /* <<EST note: does not exist with RedLib: switch to NewLib or NewLib-nano! */
|
||||
|
||||
#ifndef LFS_NO_MALLOC
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifndef LFS_NO_ASSERT
|
||||
#include <assert.h>
|
||||
#endif
|
||||
#if !defined(LFS_NO_DEBUG) || \
|
||||
!defined(LFS_NO_WARN) || \
|
||||
!defined(LFS_NO_ERROR) || \
|
||||
defined(LFS_YES_TRACE)
|
||||
#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_TRACE
|
||||
#ifdef LFS_YES_TRACE
|
||||
#define LFS_TRACE_(fmt, ...) \
|
||||
printf("%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")
|
||||
#else
|
||||
#define LFS_TRACE(...)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef LFS_DEBUG
|
||||
#ifndef LFS_NO_DEBUG
|
||||
#define LFS_DEBUG_(fmt, ...) \
|
||||
printf("%s:%d:debug: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define LFS_DEBUG(...) LFS_DEBUG_(__VA_ARGS__, "")
|
||||
#else
|
||||
#define LFS_DEBUG(...)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef LFS_WARN
|
||||
#ifndef LFS_NO_WARN
|
||||
#define LFS_WARN_(fmt, ...) \
|
||||
printf("%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define LFS_WARN(...) LFS_WARN_(__VA_ARGS__, "")
|
||||
#else
|
||||
#define LFS_WARN(...)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef LFS_ERROR
|
||||
#ifndef LFS_NO_ERROR
|
||||
#define LFS_ERROR_(fmt, ...) \
|
||||
printf("%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
||||
#define LFS_ERROR(...) LFS_ERROR_(__VA_ARGS__, "")
|
||||
#else
|
||||
#define LFS_ERROR(...)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Runtime assertions
|
||||
#ifndef LFS_ASSERT
|
||||
#ifndef LFS_NO_ASSERT
|
||||
#define LFS_ASSERT(test) assert(test)
|
||||
#else
|
||||
#define LFS_ASSERT(test)
|
||||
#endif
|
||||
#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;
|
||||
}
|
||||
|
||||
// Align to nearest multiple of a size
|
||||
static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) {
|
||||
return a - (a % alignment);
|
||||
}
|
||||
|
||||
static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) {
|
||||
return lfs_aligndown(a + alignment-1, alignment);
|
||||
}
|
||||
|
||||
// Find the smallest power of 2 greater 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 between 32-bit little-endian and native order
|
||||
static inline uint32_t lfs_fromle32(uint32_t a) {
|
||||
#if (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
|
||||
(defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
|
||||
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
return a;
|
||||
#elif !defined(LFS_NO_INTRINSICS) && ( \
|
||||
(defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
|
||||
(defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
|
||||
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __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
|
||||
}
|
||||
|
||||
static inline uint32_t lfs_tole32(uint32_t a) {
|
||||
return lfs_fromle32(a);
|
||||
}
|
||||
|
||||
// Convert between 32-bit big-endian and native order
|
||||
static inline uint32_t lfs_frombe32(uint32_t a) {
|
||||
#if !defined(LFS_NO_INTRINSICS) && ( \
|
||||
(defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
|
||||
(defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
|
||||
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
|
||||
return __builtin_bswap32(a);
|
||||
#elif (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
|
||||
(defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
|
||||
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||
return a;
|
||||
#else
|
||||
return (((uint8_t*)&a)[0] << 24) |
|
||||
(((uint8_t*)&a)[1] << 16) |
|
||||
(((uint8_t*)&a)[2] << 8) |
|
||||
(((uint8_t*)&a)[3] << 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint32_t lfs_tobe32(uint32_t a) {
|
||||
return lfs_frombe32(a);
|
||||
}
|
||||
|
||||
// Calculate CRC-32 with polynomial = 0x04c11db7
|
||||
#ifdef LFS_CRC
|
||||
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size) {
|
||||
return LFS_CRC(crc, buffer, size)
|
||||
}
|
||||
#else
|
||||
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size);
|
||||
#endif
|
||||
|
||||
// Allocate memory, only used if buffers are not provided to littlefs
|
||||
//
|
||||
// littlefs current has no alignment requirements, as it only allocates
|
||||
// byte-level buffers.
|
||||
static inline void *lfs_malloc(size_t size) {
|
||||
#if defined(LFS_MALLOC)
|
||||
return LFS_MALLOC(size);
|
||||
#elif !defined(LFS_NO_MALLOC)
|
||||
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) {
|
||||
#if defined(LFS_FREE)
|
||||
LFS_FREE(p);
|
||||
#elif !defined(LFS_NO_MALLOC)
|
||||
free(p);
|
||||
#else
|
||||
(void)p;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* LITTLEFS_CONFIG_ENABLED */ /* << EST */
|
||||
|
||||
#endif
|
||||
5
TSM_PicoW_Sensor/McuLib/littleFS/readme.txt
Normal file
5
TSM_PicoW_Sensor/McuLib/littleFS/readme.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
readme.txt
|
||||
----------
|
||||
|
||||
Repository and more details: https://github.com/littlefs-project/littlefs
|
||||
|
||||
Reference in New Issue
Block a user