From 45a8af251acc442740a46c3858a1a1d25f63225c Mon Sep 17 00:00:00 2001 From: GoT Date: Sat, 24 Feb 2024 16:10:02 +0100 Subject: [PATCH] feat(fs): implement littlefs lfs.h driver support (#5562) --- Kconfig | 7 + docs/libs/fs.rst | 1 + docs/libs/index.rst | 1 + docs/libs/lfs.rst | 59 ++++++++ examples/porting/lv_port_fs_template.c | 5 +- lv_conf_template.h | 6 + src/core/lv_global.h | 4 + src/libs/fsdrv/lv_fs_fatfs.c | 5 +- src/libs/fsdrv/lv_fs_littlefs.c | 188 +++++++++++++++++++++++++ src/libs/fsdrv/lv_fs_memfs.c | 5 +- src/libs/fsdrv/lv_fs_posix.c | 5 +- src/libs/fsdrv/lv_fs_stdio.c | 5 +- src/libs/fsdrv/lv_fs_win32.c | 4 +- src/lv_conf_internal.h | 18 +++ src/lv_init.c | 4 + 15 files changed, 300 insertions(+), 17 deletions(-) create mode 100644 docs/libs/lfs.rst create mode 100644 src/libs/fsdrv/lv_fs_littlefs.c diff --git a/Kconfig b/Kconfig index 126e1ff82..b3a577c7d 100644 --- a/Kconfig +++ b/Kconfig @@ -1057,6 +1057,13 @@ menu "LVGL configuration" default 0 depends on LV_USE_FS_MEMFS + config LV_USE_FS_LITTLEFS + bool "File system on top of littlefs API" + config LV_FS_LITTLEFS_LETTER + int "Set an upper cased letter on which the drive will accessible (e.g. 'A' i.e. 65)" + default 0 + depends on LV_USE_FS_LITTLEFS + config LV_USE_LODEPNG bool "PNG decoder library" diff --git a/docs/libs/fs.rst b/docs/libs/fs.rst index becc142df..f71c07a3d 100644 --- a/docs/libs/fs.rst +++ b/docs/libs/fs.rst @@ -14,6 +14,7 @@ LVG has built in support for: - POSIX (Linux and Windows using POSIX function .e.g ``open``, ``read``) - WIN32 (Windows using Win32 API function .e.g ``CreateFileA``, ``ReadFile``) - MEMFS (read a file from a memory buffer) +- LITTLEFS (a little fail-safe filesystem designed for microcontrollers) You still need to provide the drivers and libraries, this extension provides only the bridge between FATFS, STDIO, POSIX, WIN32 and LVGL. diff --git a/docs/libs/index.rst b/docs/libs/index.rst index 8e983dcb2..ef6f17103 100644 --- a/docs/libs/index.rst +++ b/docs/libs/index.rst @@ -22,3 +22,4 @@ rlottie ffmpeg rle + lfs diff --git a/docs/libs/lfs.rst b/docs/libs/lfs.rst new file mode 100644 index 000000000..89234aaba --- /dev/null +++ b/docs/libs/lfs.rst @@ -0,0 +1,59 @@ +.. _lfs: + +============== +littlefs +============== + +littlefs is a little fail-safe filesystem designed for microcontrollers. + +Detailed introduction: https://github.com/littlefs-project/littlefs + + +Usage +----- + +Enable :c:macro:`LV_USE_FS_LITTLEFS` and define a :c:macro`LV_FS_LITTLEFS_LETTER` in ``lv_conf.h``. + +When enabled :c:macro:`lv_littlefs_set_handler` can be used to set up a mount point. + +Example +------- + +.. code:: c + #include "lfs.h" + + // 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, + }; + + // 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); + } + + lv_littlefs_set_handler(&lfs); + + +API +--- + + diff --git a/examples/porting/lv_port_fs_template.c b/examples/porting/lv_port_fs_template.c index a2d41e1d1..fab2cb39f 100644 --- a/examples/porting/lv_port_fs_template.c +++ b/examples/porting/lv_port_fs_template.c @@ -64,7 +64,6 @@ void lv_port_fs_init(void) * Register the file system interface in LVGL *--------------------------------------------------*/ - /*Add a simple drive to open images*/ static lv_fs_drv_t fs_drv; lv_fs_drv_init(&fs_drv); @@ -195,8 +194,8 @@ static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs /** * Give the position of the read write pointer * @param drv pointer to a driver where this function belongs - * @param file_p pointer to a file_t variable. - * @param pos_p pointer to to store the result + * @param file_p pointer to a file_t variable + * @param pos_p pointer to store the result * @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum */ static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p) diff --git a/lv_conf_template.h b/lv_conf_template.h index 8c49a1795..988897c0e 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -641,6 +641,12 @@ #define LV_FS_MEMFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ #endif +/*API for LittleFs. */ +#define LV_USE_FS_LITTLEFS 0 +#if LV_USE_FS_LITTLEFS + #define LV_FS_LITTLEFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ +#endif + /*LODEPNG decoder library*/ #define LV_USE_LODEPNG 0 diff --git a/src/core/lv_global.h b/src/core/lv_global.h index a827459c2..bc8a6e15b 100644 --- a/src/core/lv_global.h +++ b/src/core/lv_global.h @@ -160,6 +160,10 @@ typedef struct _lv_global_t { lv_fs_drv_t win32_fs_drv; #endif +#if LV_USE_FS_LITTLEFS + lv_fs_drv_t littlefs_fs_drv; +#endif + #if LV_USE_FREETYPE struct _lv_freetype_context_t * ft_context; #endif diff --git a/src/libs/fsdrv/lv_fs_fatfs.c b/src/libs/fsdrv/lv_fs_fatfs.c index 44971733b..adee2e322 100644 --- a/src/libs/fsdrv/lv_fs_fatfs.c +++ b/src/libs/fsdrv/lv_fs_fatfs.c @@ -62,7 +62,6 @@ void lv_fs_fatfs_init(void) * Register the file system interface in LVGL *--------------------------------------------------*/ - /*Add a simple drive to open images*/ lv_fs_drv_t * fs_drv_p = &(LV_GLOBAL_DEFAULT()->fatfs_fs_drv); lv_fs_drv_init(fs_drv_p); @@ -205,8 +204,8 @@ static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs /** * Give the position of the read write pointer * @param drv pointer to a driver where this function belongs - * @param file_p pointer to a FIL variable. - * @param pos_p pointer to to store the result + * @param file_p pointer to a FIL variable + * @param pos_p pointer to store the result * @return LV_FS_RES_OK: no error, the file is read * any error from lv_fs_res_t enum */ diff --git a/src/libs/fsdrv/lv_fs_littlefs.c b/src/libs/fsdrv/lv_fs_littlefs.c new file mode 100644 index 000000000..3ac523361 --- /dev/null +++ b/src/libs/fsdrv/lv_fs_littlefs.c @@ -0,0 +1,188 @@ +#include "../../../lvgl.h" +#if LV_USE_FS_LITTLEFS + +#include "lfs.h" +#include "../../core/lv_global.h" + +typedef struct LittleFile { + lfs_file_t file; +} LittleFile; + +/********************** + * STATIC PROTOTYPES + **********************/ +static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode); +static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p); +static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br); +static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw); +static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence); +static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p); + +void lv_littlefs_set_handler(lfs_t * lfs) +{ + lv_fs_drv_t * drv = lv_fs_get_drv(LV_FS_LITTLEFS_LETTER); + drv->user_data = lfs; +} + +/** + * Register a driver for the LittleFS File System interface + */ +void lv_fs_littlefs_init(void) +{ + lv_fs_drv_t * fs_drv = &(LV_GLOBAL_DEFAULT()->littlefs_fs_drv); + lv_fs_drv_init(fs_drv); + + fs_drv->letter = LV_FS_LITTLEFS_LETTER; + fs_drv->open_cb = fs_open; + fs_drv->close_cb = fs_close; + fs_drv->read_cb = fs_read; + fs_drv->write_cb = fs_write; + fs_drv->seek_cb = fs_seek; + fs_drv->tell_cb = fs_tell; + + fs_drv->dir_close_cb = NULL; + fs_drv->dir_open_cb = NULL; + fs_drv->dir_read_cb = NULL; + + lv_fs_drv_register(fs_drv); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Open a file + * @param drv pointer to a driver where this function belongs + * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt) + * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR + * @return a file descriptor or NULL on error + */ +static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode) +{ + LV_UNUSED(drv); + + int flags; + if(mode == LV_FS_MODE_WR) + flags = LFS_O_WRONLY; + else if(mode == LV_FS_MODE_RD) + flags = LFS_O_RDONLY; + else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) + flags = LFS_O_RDWR; + + LittleFile * lf = (LittleFile *)lv_malloc(sizeof(LittleFile)); + LV_ASSERT_NULL(lf); + + lfs_t * lfs = (lfs_t *)drv->user_data; + int err = lfs_file_open(lfs, &lf->file, path, flags); + if(err) { + return NULL; + } + + return (void *)lf; +} + +/** + * Close an opened file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. (opened with fs_open) + * @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum + */ +static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p) +{ + LV_UNUSED(drv); + LittleFile * lf = (LittleFile *)file_p; + + lfs_t * lfs = (lfs_t *)drv->user_data; + lfs_file_close(lfs, &lf->file); + lv_free(lf); + + return LV_FS_RES_OK; +} + +/** + * Read data from an opened file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. + * @param buf pointer to a memory block where to store the read data + * @param btr number of Bytes To Read + * @param br the real number of read bytes (Byte Read) + * @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum + */ +static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br) +{ + LV_UNUSED(drv); + LittleFile * lf = (LittleFile *)file_p; + + lfs_t * lfs = (lfs_t *)drv->user_data; + *br = lfs_file_read(lfs, &lf->file, (uint8_t *)buf, btr); + + return (int32_t)(*br) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; +} + +/** + * Write into a file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable + * @param buf pointer to a buffer with the bytes to write + * @param btw Bytes To Write + * @param bw the number of real written bytes (Bytes Written) + * @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum + */ +static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw) +{ + LV_UNUSED(drv); + LittleFile * lf = (LittleFile *)file_p; + + lfs_t * lfs = (lfs_t *)drv->user_data; + *bw = lfs_file_write(lfs, &lf->file, (uint8_t *)buf, btw); + + return (int32_t)(*bw) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; +} + +/** + * Set the read write pointer. Also expand the file size if necessary. + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. (opened with fs_open ) + * @param pos the new position of read write pointer + * @param whence tells from where to interpret the `pos`. See @lv_fs_whence_t + * @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum + */ +static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence) +{ + LV_UNUSED(drv); + int mode; + if(whence == LV_FS_SEEK_SET) + mode = LFS_SEEK_SET; + else if(whence == LV_FS_SEEK_CUR) + mode = LFS_SEEK_CUR; + else if(whence == LV_FS_SEEK_END) + mode = LFS_SEEK_END; + + LittleFile * lf = (LittleFile *)file_p; + + lfs_t * lfs = (lfs_t *)drv->user_data; + int rc = lfs_file_seek(lfs, &lf->file, pos, mode); + + return rc < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; +} + +/** + * Give the position of the read write pointer + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_p variable + * @param pos_p pointer to store the result + * @return LV_FS_RES_OK: no error or any error from @lv_fs_res_t enum + */ +static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p) +{ + LV_UNUSED(drv); + LittleFile * lf = (LittleFile *)file_p; + + lfs_t * lfs = (lfs_t *)drv->user_data; + *pos_p = lfs_file_tell(lfs, &lf->file); + + return (int32_t)(*pos_p) < 0 ? LV_FS_RES_UNKNOWN : LV_FS_RES_OK; +} + +#endif diff --git a/src/libs/fsdrv/lv_fs_memfs.c b/src/libs/fsdrv/lv_fs_memfs.c index 746f62ea1..52379beb7 100644 --- a/src/libs/fsdrv/lv_fs_memfs.c +++ b/src/libs/fsdrv/lv_fs_memfs.c @@ -84,7 +84,6 @@ void lv_fs_memfs_init(void) * Register the file system interface in LVGL *--------------------------------------------------*/ - /*Add a simple drive to open images*/ lv_fs_drv_init(&fs_drv); /*Set up fields...*/ @@ -194,8 +193,8 @@ static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs /** * Give the position of the read write pointer * @param drv pointer to a driver where this function belongs - * @param file_p pointer to a FILE variable. - * @param pos_p pointer to to store the result + * @param file_p pointer to a FILE variable + * @param pos_p pointer to store the result * @return LV_FS_RES_OK: no error, the file is read * any error from lv_fs_res_t enum */ diff --git a/src/libs/fsdrv/lv_fs_posix.c b/src/libs/fsdrv/lv_fs_posix.c index b345ea969..839742fc5 100644 --- a/src/libs/fsdrv/lv_fs_posix.c +++ b/src/libs/fsdrv/lv_fs_posix.c @@ -68,7 +68,6 @@ void lv_fs_posix_init(void) * Register the file system interface in LVGL *--------------------------------------------------*/ - /*Add a simple drive to open images*/ lv_fs_drv_t * fs_drv_p = &(LV_GLOBAL_DEFAULT()->posix_fs_drv); lv_fs_drv_init(fs_drv_p); @@ -200,8 +199,8 @@ static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs /** * Give the position of the read write pointer * @param drv pointer to a driver where this function belongs - * @param file_p a file handle variable. - * @param pos_p pointer to to store the result + * @param file_p a file handle variable + * @param pos_p pointer to store the result * @return LV_FS_RES_OK: no error, the file is read * any error from lv_fs_res_t enum */ diff --git a/src/libs/fsdrv/lv_fs_stdio.c b/src/libs/fsdrv/lv_fs_stdio.c index fd5c2d183..020749d65 100644 --- a/src/libs/fsdrv/lv_fs_stdio.c +++ b/src/libs/fsdrv/lv_fs_stdio.c @@ -69,7 +69,6 @@ void lv_fs_stdio_init(void) * Register the file system interface in LVGL *--------------------------------------------------*/ - /*Add a simple drive to open images*/ lv_fs_drv_t * fs_drv_p = &(LV_GLOBAL_DEFAULT()->stdio_fs_drv); lv_fs_drv_init(fs_drv_p); @@ -200,8 +199,8 @@ static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs /** * Give the position of the read write pointer * @param drv pointer to a driver where this function belongs - * @param file_p pointer to a FILE variable. - * @param pos_p pointer to to store the result + * @param file_p pointer to a FILE variable + * @param pos_p pointer to store the result * @return LV_FS_RES_OK: no error, the file is read * any error from lv_fs_res_t enum */ diff --git a/src/libs/fsdrv/lv_fs_win32.c b/src/libs/fsdrv/lv_fs_win32.c index 4a8ac0a81..e021219ed 100644 --- a/src/libs/fsdrv/lv_fs_win32.c +++ b/src/libs/fsdrv/lv_fs_win32.c @@ -315,8 +315,8 @@ static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs /** * Give the position of the read write pointer * @param drv pointer to a driver where this function belongs - * @param file_p pointer to a FILE variable. - * @param pos_p pointer to to store the result + * @param file_p pointer to a FILE variable + * @param pos_p pointer to store the result * @return LV_FS_RES_OK: no error, the file is read * any error from lv_fs_res_t enum */ diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 28b3d36fb..c6e3eb8e8 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -2139,6 +2139,24 @@ #endif #endif +/*API for LittleFs. */ +#ifndef LV_USE_FS_LITTLEFS + #ifdef CONFIG_LV_USE_FS_LITTLEFS + #define LV_USE_FS_LITTLEFS CONFIG_LV_USE_FS_LITTLEFS + #else + #define LV_USE_FS_LITTLEFS 0 + #endif +#endif +#if LV_USE_FS_LITTLEFS + #ifndef LV_FS_LITTLEFS_LETTER + #ifdef CONFIG_LV_FS_LITTLEFS_LETTER + #define LV_FS_LITTLEFS_LETTER CONFIG_LV_FS_LITTLEFS_LETTER + #else + #define LV_FS_LITTLEFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #endif + #endif +#endif + /*LODEPNG decoder library*/ #ifndef LV_USE_LODEPNG #ifdef CONFIG_LV_USE_LODEPNG diff --git a/src/lv_init.c b/src/lv_init.c index 991f11e46..f3eec701c 100644 --- a/src/lv_init.c +++ b/src/lv_init.c @@ -265,6 +265,10 @@ void lv_init(void) lv_fs_memfs_init(); #endif +#if LV_USE_FS_LITTLEFS + lv_fs_littlefs_init(); +#endif + #if LV_USE_LODEPNG lv_lodepng_init(); #endif