From abba1c351a9736bf2703d4a97cf5c665e8249e6a Mon Sep 17 00:00:00 2001 From: Adrian Schnyder Date: Mon, 24 Jan 2022 20:52:25 +0100 Subject: [PATCH] feat(fs): add caching option for lv_fs-read (#2979) BREAKING CHANGE: The `LV_FS_...` related configs needs to be updated. Co-authored-by: Gabor Kiss-Vamosi --- Kconfig | 36 +++-- docs/overview/file-system.md | 2 + examples/libs/bmp/lv_example_bmp_1.c | 4 + lv_conf_template.h | 38 +++-- src/extra/libs/bmp/lv_bmp.c | 21 +++ src/extra/libs/fsdrv/lv_fs_fatfs.c | 19 ++- src/extra/libs/fsdrv/lv_fs_posix.c | 28 ++-- src/extra/libs/fsdrv/lv_fs_stdio.c | 25 ++-- src/extra/libs/fsdrv/lv_fs_win32.c | 18 +-- src/lv_conf_internal.h | 104 ++++++++++++-- src/misc/lv_fs.c | 200 +++++++++++++++++++++++---- src/misc/lv_fs.h | 9 ++ tests/CMakeLists.txt | 6 +- 13 files changed, 414 insertions(+), 96 deletions(-) diff --git a/Kconfig b/Kconfig index ca0d8da2c..e86058527 100644 --- a/Kconfig +++ b/Kconfig @@ -869,29 +869,49 @@ menu "LVGL configuration" menu "3rd Party Libraries" config LV_USE_FS_STDIO - int "File system on top of stdio API" - default 0 + bool "File system on top of stdio API" + config LV_FS_STDIO_LETTER + string "Set an upper cased letter on which the drive will accessible (e.g. 'A' i.e. 65 )" + depends on LV_USE_FS_STDIO != 0 config LV_FS_STDIO_PATH string "Set the working directory" depends on LV_USE_FS_STDIO != 0 + config LV_FS_STDIO_CACHE_SIZE + string ">0 to cache this number of bytes in lv_fs_read()" + depends on LV_USE_FS_STDIO != 0 config LV_USE_FS_POSIX - int "File system on top of posix API" - default 0 + bool "File system on top of posix API" + config LV_FS_POSIX_LETTER + int "Set an upper cased letter on which the drive will accessible (e.g. 'A' i.e. 65)" + depends on LV_USE_FS_POSIX != 0 config LV_FS_POSIX_PATH string "Set the working directory" depends on LV_USE_FS_POSIX != 0 + config LV_FS_POSIX_CACHE_SIZE + int ">0 to cache this number of bytes in lv_fs_read()" + depends on LV_USE_FS_POSIX != 0 config LV_USE_FS_WIN32 - int "File system on top of Win32 API" - default 0 + bool "File system on top of Win32 API" + config LV_FS_WIN32_LETTER + int "Set an upper cased letter on which the drive will accessible (e.g. 'A' i.e. 65)" + depends on LV_USE_FS_WIN32 != 0 config LV_FS_WIN32_PATH string "Set the working directory" depends on LV_USE_FS_WIN32 != 0 + config LV_FS_WIN32_CACHE_SIZE + int ">0 to cache this number of bytes in lv_fs_read()" + depends on LV_USE_FS_WIN32 != 0 config LV_USE_FS_FATFS - int "File system on top of FatFS" - default 0 + bool "File system on top of FatFS" + config LV_FS_FATFS_LETTER + int "Set an upper cased letter on which the drive will accessible (e.g. 'A' i.e. 65)" + depends on LV_USE_FS_FATFS != 0 + config LV_FS_FATFS_CACHE_SIZE + int ">0 to cache this number of bytes in lv_fs_read()" + depends on LV_USE_FS_FATFS != 0 config LV_USE_PNG bool "PNG decoder library" diff --git a/docs/overview/file-system.md b/docs/overview/file-system.md index ae8c2f778..5ef5bc505 100644 --- a/docs/overview/file-system.md +++ b/docs/overview/file-system.md @@ -21,6 +21,8 @@ static lv_fs_drv_t drv; /*Needs to be static or global*/ lv_fs_drv_init(&drv); /*Basic initialization*/ drv.letter = 'S'; /*An uppercase letter to identify the drive */ +drv.cache_size = my_cahce_size; /*Cache size for reading in bytes. 0 to not cache.*/ + drv.ready_cb = my_ready_cb; /*Callback to tell if the drive is ready to use */ drv.open_cb = my_open_cb; /*Callback to open a file */ drv.close_cb = my_close_cb; /*Callback to close a file */ diff --git a/examples/libs/bmp/lv_example_bmp_1.c b/examples/libs/bmp/lv_example_bmp_1.c index 9bead1d0f..233c5e0e2 100644 --- a/examples/libs/bmp/lv_example_bmp_1.c +++ b/examples/libs/bmp/lv_example_bmp_1.c @@ -9,7 +9,11 @@ void lv_example_bmp_1(void) lv_obj_t * img = lv_img_create(lv_scr_act()); /* Assuming a File system is attached to letter 'A' * E.g. set LV_USE_FS_STDIO 'A' in lv_conf.h */ +#if LV_COLOR_DEPTH == 32 lv_img_set_src(img, "A:lvgl/examples/libs/bmp/example_32bit.bmp"); +#elif LV_COLOR_DEPTH == 16 + lv_img_set_src(img, "A:lvgl/examples/libs/bmp/example_16bit.bmp"); +#endif lv_obj_center(img); } diff --git a/lv_conf_template.h b/lv_conf_template.h index 95707e55e..0b2a36a82 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -567,18 +567,38 @@ * 3rd party libraries *--------------------*/ -/*File system interfaces for common APIs - *To enable set a driver letter for that API*/ -#define LV_USE_FS_STDIO '\0' /*Uses fopen, fread, etc*/ -//#define LV_FS_STDIO_PATH "/home/john/" /*Set the working directory. If commented it will be "./" */ +/*File system interfaces for common APIs */ -#define LV_USE_FS_POSIX '\0' /*Uses open, read, etc*/ -//#define LV_FS_POSIX_PATH "/home/john/" /*Set the working directory. If commented it will be "./" */ +/*API for fopen, fread, etc*/ +#define LV_USE_FS_STDIO 0 +#if LV_USE_FS_STDIO + #define LV_FS_STDIO_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths ill be appended to it.*/ + #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif -#define LV_USE_FS_WIN32 '\0' /*Uses CreateFile, ReadFile, etc*/ -//#define LV_FS_WIN32_PATH "C:\\Users\\john\\" /*Set the working directory. If commented it will be ".\\" */ +/*API for open, read, etc*/ +#define LV_USE_FS_POSIX 0 +#if LV_USE_FS_POSIX + #define LV_FS_POSIX_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_POSIX_PATH "" /*Set the working directory. File/directory paths ill be appended to it.*/ + #define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif -#define LV_USE_FS_FATFS '\0' /*Uses f_open, f_read, etc*/ +/*API for CreateFile, ReadFile, etc*/ +#define LV_USE_FS_WIN32 0 +#if LV_USE_FS_WIN32 + #define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory path ill be appended to it.*/ + #define LV_FS_WIN32_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + +/*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/ +#define LV_USE_FS_FATFS 0 +#if LV_USE_FS_FATFS + #define LV_FS_FATFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_FATSF_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif /*PNG decoder library*/ #define LV_USE_PNG 0 diff --git a/src/extra/libs/bmp/lv_bmp.c b/src/extra/libs/bmp/lv_bmp.c index 969c4e1b2..73e803025 100644 --- a/src/extra/libs/bmp/lv_bmp.c +++ b/src/extra/libs/bmp/lv_bmp.c @@ -141,6 +141,7 @@ static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * lv_fs_read(&b.f, header, 54, NULL); if(0x42 != header[0] || 0x4d != header[1]) { + lv_fs_close(&b.f); return LV_RES_INV; } @@ -150,6 +151,26 @@ static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * memcpy(&b.bpp, header + 28, 2); b.row_size_bytes = ((b.bpp * b.px_width + 31) / 32) * 4; + bool color_depth_error = false; + if(LV_COLOR_DEPTH == 32 && (b.bpp != 32 || b.bpp != 24)) { + LV_LOG_WARN("LV_COLOR_DEPTH == 32 but bpp is %d (should be 32 or 24)", b.bpp); + color_depth_error = true; + } + else if(LV_COLOR_DEPTH == 16 && b.bpp != 16) { + LV_LOG_WARN("LV_COLOR_DEPTH == 16 but bpp is %d (should be 16)", b.bpp); + color_depth_error = true; + } + else if(LV_COLOR_DEPTH == 8 && b.bpp != 8) { + LV_LOG_WARN("LV_COLOR_DEPTH == 8 but bpp is %d (should be 8)", b.bpp); + color_depth_error = true; + } + + if(color_depth_error) { + dsc->error_msg = "Color depth mismatch"; + lv_fs_close(&b.f); + return LV_RES_INV; + } + dsc->user_data = lv_mem_alloc(sizeof(bmp_dsc_t)); LV_ASSERT_MALLOC(dsc->user_data); if(dsc->user_data == NULL) return LV_RES_INV; diff --git a/src/extra/libs/fsdrv/lv_fs_fatfs.c b/src/extra/libs/fsdrv/lv_fs_fatfs.c index 04996226c..5f2d868de 100644 --- a/src/extra/libs/fsdrv/lv_fs_fatfs.c +++ b/src/extra/libs/fsdrv/lv_fs_fatfs.c @@ -8,13 +8,17 @@ *********************/ #include "../../../lvgl.h" -#if LV_USE_FS_FATFS != '\0' +#if LV_USE_FS_FATFS #include "ff.h" /********************* * DEFINES *********************/ +#if LV_FS_FATFS_LETTER == '\0' + #error "LV_FS_FATFS_LETTER must be an upper case ASCII letter" +#endif + /********************** * TYPEDEFS **********************/ @@ -62,7 +66,9 @@ void lv_fs_fatfs_init(void) lv_fs_drv_init(&fs_drv); /*Set up fields...*/ - fs_drv.letter = LV_USE_FS_FATFS; + fs_drv.letter = LV_FS_FATFS_LETTER; + fs_drv.cache_size = LV_FS_FSTFS_CACHE_SIZE; + fs_drv.open_cb = fs_open; fs_drv.close_cb = fs_close; fs_drv.read_cb = fs_read; @@ -274,4 +280,11 @@ static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p) return LV_FS_RES_OK; } -#endif /*LV_USE_FS_FATFS*/ +#else /*LV_USE_FS_FATFS == 0*/ + +#if defined(LV_FS_FATFS_LETTER) && LV_FS_FATFS_LETTER != '\0' + #warning "LV_USE_FS_FATFS is not enabled but LV_FS_FATFS_LETTER is set" +#endif + +#endif /*LV_USE_FS_POSIX*/ + diff --git a/src/extra/libs/fsdrv/lv_fs_posix.c b/src/extra/libs/fsdrv/lv_fs_posix.c index 3c21a2ee1..fb5fcd306 100644 --- a/src/extra/libs/fsdrv/lv_fs_posix.c +++ b/src/extra/libs/fsdrv/lv_fs_posix.c @@ -8,7 +8,8 @@ * INCLUDES *********************/ #include "../../../lvgl.h" -#if LV_USE_FS_POSIX != '\0' + +#if LV_USE_FS_POSIX #include #include @@ -23,6 +24,10 @@ * DEFINES *********************/ +#if LV_FS_POSIX_LETTER == '\0' + #error "LV_FS_POSIX_LETTER must be an upper case ASCII letter" +#endif + /********************** * TYPEDEFS **********************/ @@ -66,7 +71,9 @@ void lv_fs_posix_init(void) lv_fs_drv_init(&fs_drv); /*Set up fields...*/ - fs_drv.letter = LV_USE_FS_POSIX; + fs_drv.letter = LV_FS_POSIX_LETTER; + fs_drv.cache_size = LV_FS_POSIX_CACHE_SIZE; + fs_drv.open_cb = fs_open; fs_drv.close_cb = fs_close; fs_drv.read_cb = fs_read; @@ -101,15 +108,11 @@ static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode) else if(mode == LV_FS_MODE_RD) flags = O_RDONLY; else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = O_RDWR; -#ifdef LV_FS_POSIX_PATH /*Make the path relative to the current directory (the projects root folder)*/ char buf[256]; sprintf(buf, LV_FS_POSIX_PATH "%s", path); int f = open(buf, flags); -#else - int f = open(path, flags); -#endif if(f < 0) return NULL; return (void *)(lv_uintptr_t)f; @@ -207,25 +210,17 @@ static void * fs_dir_open(lv_fs_drv_t * drv, const char * path) LV_UNUSED(drv); #ifndef WIN32 -# ifdef LV_FS_POSIX_PATH /*Make the path relative to the current directory (the projects root folder)*/ char buf[256]; sprintf(buf, LV_FS_POSIX_PATH "%s", path); return opendir(buf); -# else - return opendir(path); -# endif #else HANDLE d = INVALID_HANDLE_VALUE; WIN32_FIND_DATA fdata; /*Make the path relative to the current directory (the projects root folder)*/ char buf[256]; -# ifdef LV_FS_POSIX_PATH sprintf(buf, LV_FS_POSIX_PATH "%s\\*", path); -# else - sprintf(buf, "%s\\*", path); -# endif strcpy(next_fn, ""); d = FindFirstFile(buf, &fdata); @@ -314,5 +309,10 @@ static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p) #endif return LV_FS_RES_OK; } +#else /*LV_USE_FS_POSIX == 0*/ + +#if defined(LV_FS_POSIX_LETTER) && LV_FS_POSIX_LETTER != '\0' + #warning "LV_USE_FS_POSIX is not enabled but LV_FS_POSIX_LETTER is set" +#endif #endif /*LV_USE_FS_POSIX*/ diff --git a/src/extra/libs/fsdrv/lv_fs_stdio.c b/src/extra/libs/fsdrv/lv_fs_stdio.c index 1fcf491f0..e4bf07783 100644 --- a/src/extra/libs/fsdrv/lv_fs_stdio.c +++ b/src/extra/libs/fsdrv/lv_fs_stdio.c @@ -65,7 +65,9 @@ void lv_fs_stdio_init(void) lv_fs_drv_init(&fs_drv); /*Set up fields...*/ - fs_drv.letter = LV_USE_FS_STDIO; + fs_drv.letter = LV_FS_STDIO_LETTER; + fs_drv.cache_size = LV_FS_STDIO_CACHE_SIZE; + fs_drv.open_cb = fs_open; fs_drv.close_cb = fs_close; fs_drv.read_cb = fs_read; @@ -101,16 +103,12 @@ static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode) else if(mode == LV_FS_MODE_RD) flags = "rb"; else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = "rb+"; -#ifdef LV_FS_STDIO_PATH /*Make the path relative to the current directory (the projects root folder)*/ char buf[256]; sprintf(buf, LV_FS_STDIO_PATH "%s", path); return fopen(buf, flags); -#else - return fopen(path, flags); -#endif } /** @@ -204,25 +202,17 @@ static void * fs_dir_open(lv_fs_drv_t * drv, const char * path) { LV_UNUSED(drv); #ifndef WIN32 -# ifdef LV_FS_STDIO_PATH /*Make the path relative to the current directory (the projects root folder)*/ char buf[256]; sprintf(buf, LV_FS_STDIO_PATH "%s", path); return opendir(buf); -# else - return opendir(path); -# endif #else HANDLE d = INVALID_HANDLE_VALUE; WIN32_FIND_DATA fdata; /*Make the path relative to the current directory (the projects root folder)*/ char buf[256]; -# ifdef LV_FS_STDIO_PATH sprintf(buf, LV_FS_STDIO_PATH "%s\\*", path); -# else - sprintf(buf, "%s\\*", path); -# endif strcpy(next_fn, ""); d = FindFirstFile(buf, &fdata); @@ -312,4 +302,11 @@ static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p) return LV_FS_RES_OK; } -#endif /*LV_USE_FS_STDIO*/ +#else /*LV_USE_FS_STDIO == 0*/ + +#if defined(LV_FS_STDIO_LETTER) && LV_FS_STDIO_LETTER != '\0' + #warning "LV_USE_FS_STDIO is not enabled but LV_FS_STDIO_LETTER is set" +#endif + +#endif /*LV_USE_FS_POSIX*/ + diff --git a/src/extra/libs/fsdrv/lv_fs_win32.c b/src/extra/libs/fsdrv/lv_fs_win32.c index e7cb46370..ad4536d40 100644 --- a/src/extra/libs/fsdrv/lv_fs_win32.c +++ b/src/extra/libs/fsdrv/lv_fs_win32.c @@ -63,7 +63,9 @@ void lv_fs_win32_init(void) lv_fs_drv_init(&fs_drv); /*Set up fields...*/ - fs_drv.letter = LV_USE_FS_WIN32; + fs_drv.letter = LV_FS_WIN32_LETTER; + fs_drv.cache_size = LV_FS_WIN32_CACHE_SIZE; + fs_drv.open_cb = fs_open; fs_drv.close_cb = fs_close; fs_drv.read_cb = fs_read; @@ -208,19 +210,13 @@ static void * fs_open(lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode) desired_access |= GENERIC_WRITE; } -#ifdef LV_FS_WIN32_PATH /*Make the path relative to the current directory (the projects root folder)*/ char buf[MAX_PATH]; sprintf(buf, LV_FS_WIN32_PATH "%s", path); -#endif return (void *)CreateFileA( -#ifdef LV_FS_WIN32_PATH buf, -#else - path, -#endif desired_access, FILE_SHARE_READ, NULL, @@ -449,4 +445,10 @@ static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p) : fs_error_from_win32(GetLastError()); } -#endif /*LV_USE_FS_WIN32*/ +#else /*LV_USE_FS_WIN32 == 0*/ + +#if defined(LV_FS_WIN32_LETTER) && LV_FS_WIN32_LETTER != '\0' + #warning "LV_USE_FS_WIN32 is not enabled but LV_FS_WIN32_LETTER is set" +#endif + +#endif diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index ccfab5497..a438c370a 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -1832,40 +1832,126 @@ * 3rd party libraries *--------------------*/ -/*File system interfaces for common APIs - *To enable set a driver letter for that API*/ +/*File system interfaces for common APIs */ + +/*API for fopen, fread, etc*/ #ifndef LV_USE_FS_STDIO #ifdef CONFIG_LV_USE_FS_STDIO #define LV_USE_FS_STDIO CONFIG_LV_USE_FS_STDIO #else - #define LV_USE_FS_STDIO '\0' /*Uses fopen, fread, etc*/ + #define LV_USE_FS_STDIO 0 + #endif +#endif +#if LV_USE_FS_STDIO + #ifndef LV_FS_STDIO_LETTER + #ifdef CONFIG_LV_FS_STDIO_LETTER + #define LV_FS_STDIO_LETTER CONFIG_LV_FS_STDIO_LETTER + #else + #define LV_FS_STDIO_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #endif + #endif + #ifndef LV_FS_STDIO_PATH + #ifdef CONFIG_LV_FS_STDIO_PATH + #define LV_FS_STDIO_PATH CONFIG_LV_FS_STDIO_PATH + #else + #define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths ill be appended to it.*/ + #endif + #endif + #ifndef LV_FS_STDIO_CACHE_SIZE + #ifdef CONFIG_LV_FS_STDIO_CACHE_SIZE + #define LV_FS_STDIO_CACHE_SIZE CONFIG_LV_FS_STDIO_CACHE_SIZE + #else + #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ + #endif #endif #endif -//#define LV_FS_STDIO_PATH "/home/john/" /*Set the working directory. If commented it will be "./" */ +/*API for open, read, etc*/ #ifndef LV_USE_FS_POSIX #ifdef CONFIG_LV_USE_FS_POSIX #define LV_USE_FS_POSIX CONFIG_LV_USE_FS_POSIX #else - #define LV_USE_FS_POSIX '\0' /*Uses open, read, etc*/ + #define LV_USE_FS_POSIX 0 + #endif +#endif +#if LV_USE_FS_POSIX + #ifndef LV_FS_POSIX_LETTER + #ifdef CONFIG_LV_FS_POSIX_LETTER + #define LV_FS_POSIX_LETTER CONFIG_LV_FS_POSIX_LETTER + #else + #define LV_FS_POSIX_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #endif + #endif + #ifndef LV_FS_POSIX_PATH + #ifdef CONFIG_LV_FS_POSIX_PATH + #define LV_FS_POSIX_PATH CONFIG_LV_FS_POSIX_PATH + #else + #define LV_FS_POSIX_PATH "" /*Set the working directory. File/directory paths ill be appended to it.*/ + #endif + #endif + #ifndef LV_FS_POSIX_CACHE_SIZE + #ifdef CONFIG_LV_FS_POSIX_CACHE_SIZE + #define LV_FS_POSIX_CACHE_SIZE CONFIG_LV_FS_POSIX_CACHE_SIZE + #else + #define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ + #endif #endif #endif -//#define LV_FS_POSIX_PATH "/home/john/" /*Set the working directory. If commented it will be "./" */ +/*API for CreateFile, ReadFile, etc*/ #ifndef LV_USE_FS_WIN32 #ifdef CONFIG_LV_USE_FS_WIN32 #define LV_USE_FS_WIN32 CONFIG_LV_USE_FS_WIN32 #else - #define LV_USE_FS_WIN32 '\0' /*Uses CreateFile, ReadFile, etc*/ + #define LV_USE_FS_WIN32 0 + #endif +#endif +#if LV_USE_FS_WIN32 + #ifndef LV_FS_WIN32_LETTER + #ifdef CONFIG_LV_FS_WIN32_LETTER + #define LV_FS_WIN32_LETTER CONFIG_LV_FS_WIN32_LETTER + #else + #define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #endif + #endif + #ifndef LV_FS_WIN32_PATH + #ifdef CONFIG_LV_FS_WIN32_PATH + #define LV_FS_WIN32_PATH CONFIG_LV_FS_WIN32_PATH + #else + #define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory path ill be appended to it.*/ + #endif + #endif + #ifndef LV_FS_WIN32_CACHE_SIZE + #ifdef CONFIG_LV_FS_WIN32_CACHE_SIZE + #define LV_FS_WIN32_CACHE_SIZE CONFIG_LV_FS_WIN32_CACHE_SIZE + #else + #define LV_FS_WIN32_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ + #endif #endif #endif -//#define LV_FS_WIN32_PATH "C:\\Users\\john\\" /*Set the working directory. If commented it will be ".\\" */ +/*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/ #ifndef LV_USE_FS_FATFS #ifdef CONFIG_LV_USE_FS_FATFS #define LV_USE_FS_FATFS CONFIG_LV_USE_FS_FATFS #else - #define LV_USE_FS_FATFS '\0' /*Uses f_open, f_read, etc*/ + #define LV_USE_FS_FATFS 0 + #endif +#endif +#if LV_USE_FS_FATFS + #ifndef LV_FS_FATFS_LETTER + #ifdef CONFIG_LV_FS_FATFS_LETTER + #define LV_FS_FATFS_LETTER CONFIG_LV_FS_FATFS_LETTER + #else + #define LV_FS_FATFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #endif + #endif + #ifndef LV_FS_FATSF_CACHE_SIZE + #ifdef CONFIG_LV_FS_FATSF_CACHE_SIZE + #define LV_FS_FATSF_CACHE_SIZE CONFIG_LV_FS_FATSF_CACHE_SIZE + #else + #define LV_FS_FATSF_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ + #endif #endif #endif diff --git a/src/misc/lv_fs.c b/src/misc/lv_fs.c index 8011b4cf4..5a7afc59a 100644 --- a/src/misc/lv_fs.c +++ b/src/misc/lv_fs.c @@ -8,24 +8,15 @@ *********************/ #include "lv_fs.h" -#include -#include "lv_assert.h" +#include "../misc/lv_assert.h" #include "lv_ll.h" +#include #include "lv_gc.h" /********************* * DEFINES *********************/ -/** - * "free" is used as a function pointer (in lv_fs_drv_t). - * We must make sure "free" was not defined to a platform specific - * free function, otherwise compilation would fail. - */ -#ifdef free - #undef free -#endif - /********************** * TYPEDEFS **********************/ @@ -100,6 +91,12 @@ lv_fs_res_t lv_fs_open(lv_fs_file_t * file_p, const char * path, lv_fs_mode_t mo file_p->drv = drv; file_p->file_d = file_d; + if(drv->cache_size) { + file_p->cache = lv_mem_alloc(sizeof(lv_fs_file_cache_t)); + LV_ASSERT_MALLOC(file_p->cache); + lv_memset_00(file_p->cache, sizeof(lv_fs_file_cache_t)); + } + return LV_FS_RES_OK; } @@ -115,8 +112,91 @@ lv_fs_res_t lv_fs_close(lv_fs_file_t * file_p) lv_fs_res_t res = file_p->drv->close_cb(file_p->drv, file_p->file_d); + if(file_p->drv->cache_size && file_p->cache) { + if(file_p->cache->buffer) { + lv_mem_free(file_p->cache->buffer); + } + + lv_mem_free(file_p->cache); + } + file_p->file_d = NULL; file_p->drv = NULL; + file_p->cache = NULL; + + return res; +} + +static lv_fs_res_t lv_fs_read_cached(lv_fs_file_t * file_p, char * buf, uint32_t btr, uint32_t * br) +{ + lv_fs_res_t res = LV_FS_RES_OK; + uint32_t file_position = file_p->cache->file_position; + uint32_t start = file_p->cache->start; + uint32_t end = file_p->cache->end; + char * buffer = file_p->cache->buffer; + uint16_t buffer_size = file_p->drv->cache_size; + + if(start <= file_position && file_position < end) { + /* Data can be read from cache buffer */ + + uint16_t buffer_offset = file_position - start; + uint16_t buffer_remaining_length = buffer_size - buffer_offset; + + if(btr <= buffer_remaining_length) { + /*Data is in cache buffer, and buffer end not reached, no need to read from FS*/ + lv_memcpy(buf, buffer + buffer_offset, btr); + } + else { + /*First part of data is in cache buffer, but we need to read rest of data from FS*/ + lv_memcpy(buf, buffer + buffer_offset, buffer_remaining_length); + + if(btr > buffer_size) { + /*If remaining data chuck is bigger than buffer size, then do not use cache, instead read it directly from FS*/ + res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)(buf + buffer_remaining_length), + btr - buffer_remaining_length, br); + } + else { + /*If remaining data chunk is smaller than buffer size, then read into cache buffer*/ + uint32_t bytes_read_to_buffer = 0; + + /*Read into cache buffer:*/ + res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buffer, buffer_size, &bytes_read_to_buffer); + file_p->cache->start = file_p->cache->end + 1; + file_p->cache->end = file_p->cache->start + bytes_read_to_buffer; + + uint16_t data_chunk_remaining = btr - buffer_remaining_length; + memcpy(buf + buffer_remaining_length, buffer, data_chunk_remaining); + } + } + } + else { + /*Data is not in cache buffer*/ + + if(btr > buffer_size) { + /*If bigger data is requested, then do not use cache, instead read it directly*/ + res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buf, btr, br); + } + else { + /*If small data is requested, then read from FS into cache buffer*/ + if(buffer == NULL) { + file_p->cache->buffer = lv_mem_alloc(buffer_size); + LV_ASSERT_MALLOC(file_p->cache->buffer); + buffer = file_p->cache->buffer; + } + + uint32_t bytes_read_to_buffer = 0; + res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buffer, buffer_size, &bytes_read_to_buffer); + file_p->cache->start = file_position; + file_p->cache->end = file_p->cache->start + bytes_read_to_buffer; + + memcpy(buf, buffer, btr); + } + } + + if(res == LV_FS_RES_OK) { + *br = btr; + file_p->cache->file_position += btr; + } return res; } @@ -128,7 +208,15 @@ lv_fs_res_t lv_fs_read(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t if(file_p->drv->read_cb == NULL) return LV_FS_RES_NOT_IMP; uint32_t br_tmp = 0; - lv_fs_res_t res = file_p->drv->read_cb(file_p->drv, file_p->file_d, buf, btr, &br_tmp); + lv_fs_res_t res; + + if(file_p->drv->cache_size) { + res = lv_fs_read_cached(file_p, (char *)buf, btr, &br_tmp); + } + else { + res = file_p->drv->read_cb(file_p->drv, file_p->file_d, buf, btr, &br_tmp); + } + if(br != NULL) *br = br_tmp; return res; @@ -163,22 +251,73 @@ lv_fs_res_t lv_fs_seek(lv_fs_file_t * file_p, uint32_t pos, lv_fs_whence_t whenc return LV_FS_RES_NOT_IMP; } - return file_p->drv->seek_cb(file_p->drv, file_p->file_d, pos, whence); + lv_fs_res_t res = LV_FS_RES_OK; + if(file_p->drv->cache_size) { + switch(whence) { + case LV_FS_SEEK_SET: { + file_p->cache->file_position = pos; + + /*FS seek if new position is outside cache buffer*/ + if(file_p->cache->file_position < file_p->cache->start || file_p->cache->file_position > file_p->cache->end) { + res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position, LV_FS_SEEK_SET); + } + + break; + } + case LV_FS_SEEK_CUR: { + file_p->cache->file_position += pos; + + /*FS seek if new position is outside cache buffer*/ + if(file_p->cache->file_position < file_p->cache->start || file_p->cache->file_position > file_p->cache->end) { + res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position, LV_FS_SEEK_SET); + } + + break; + } + case LV_FS_SEEK_END: { + /*Because we don't know the file size, we do a little trick: do a FS seek, then get new file position from FS*/ + res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, pos, whence); + if(res == LV_FS_RES_OK) { + uint32_t tmp_position; + res = file_p->drv->tell_cb(file_p->drv, file_p->file_d, &tmp_position); + + if(res == LV_FS_RES_OK) { + file_p->cache->file_position = tmp_position; + } + } + break; + } + } + } + else { + res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, pos, whence); + } + + return res; } lv_fs_res_t lv_fs_tell(lv_fs_file_t * file_p, uint32_t * pos) { - *pos = 0; - if(file_p->drv == NULL) { + *pos = 0; return LV_FS_RES_INV_PARAM; } if(file_p->drv->tell_cb == NULL) { + *pos = 0; return LV_FS_RES_NOT_IMP; } - return file_p->drv->tell_cb(file_p->drv, file_p->file_d, pos); + lv_fs_res_t res; + if(file_p->drv->cache_size) { + *pos = file_p->cache->file_position; + res = LV_FS_RES_OK; + } + else { + res = file_p->drv->tell_cb(file_p->drv, file_p->file_d, pos); + } + + return res; } lv_fs_res_t lv_fs_dir_open(lv_fs_dir_t * rddir_p, const char * path) @@ -217,17 +356,19 @@ lv_fs_res_t lv_fs_dir_open(lv_fs_dir_t * rddir_p, const char * path) lv_fs_res_t lv_fs_dir_read(lv_fs_dir_t * rddir_p, char * fn) { - fn[0] = '\0'; - if(rddir_p->drv == NULL || rddir_p->dir_d == NULL) { + fn[0] = '\0'; return LV_FS_RES_INV_PARAM; } if(rddir_p->drv->dir_read_cb == NULL) { + fn[0] = '\0'; return LV_FS_RES_NOT_IMP; } - return rddir_p->drv->dir_read_cb(rddir_p->drv, rddir_p->dir_d, fn); + lv_fs_res_t res = rddir_p->drv->dir_read_cb(rddir_p->drv, rddir_p->dir_d, fn); + + return res; } lv_fs_res_t lv_fs_dir_close(lv_fs_dir_t * rddir_p) @@ -296,10 +437,10 @@ const char * lv_fs_get_ext(const char * fn) { size_t i; for(i = strlen(fn); i > 0; i--) { - if(fn[i - 1] == '.') { - return &fn[i]; + if(fn[i] == '.') { + return &fn[i + 1]; } - else if(fn[i - 1] == '/' || fn[i - 1] == '\\') { + else if(fn[i] == '/' || fn[i] == '\\') { return ""; /*No extension if a '\' or '/' found*/ } } @@ -325,12 +466,11 @@ char * lv_fs_up(char * path) size_t i; for(i = len; i > 0; i--) { - if(path[i - 1] == '/' || path[i - 1] == '\\') { - path[i - 1] = '\0'; - break; - } + if(path[i] == '/' || path[i] == '\\') break; } + if(i > 0) path[i] = '\0'; + return path; } @@ -351,12 +491,14 @@ const char * lv_fs_get_last(const char * path) size_t i; for(i = len; i > 0; i--) { - if(path[i - 1] == '/' || path[i - 1] == '\\') break; + if(path[i] == '/' || path[i] == '\\') break; } - return &path[i]; -} + /*No '/' or '\' in the path so return with path itself*/ + if(i == 0) return path; + return &path[i + 1]; +} /********************** * STATIC FUNCTIONS **********************/ diff --git a/src/misc/lv_fs.h b/src/misc/lv_fs.h index 9afc2bf57..0a9b24104 100644 --- a/src/misc/lv_fs.h +++ b/src/misc/lv_fs.h @@ -69,6 +69,7 @@ typedef enum { typedef struct _lv_fs_drv_t { char letter; + uint16_t cache_size; bool (*ready_cb)(struct _lv_fs_drv_t * drv); void * (*open_cb)(struct _lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode); @@ -87,9 +88,17 @@ typedef struct _lv_fs_drv_t { #endif } lv_fs_drv_t; +typedef struct { + uint32_t start; + uint32_t end; + uint32_t file_position; + void * buffer; +} lv_fs_file_cache_t; + typedef struct { void * file_d; lv_fs_drv_t * drv; + lv_fs_file_cache_t * cache; } lv_fs_file_t; typedef struct { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3406df8fa..63e21b1d3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -180,8 +180,10 @@ set(LVGL_TEST_OPTIONS_FULL_32BIT -DLV_LABEL_TEXT_SELECTION=1 ${LVGL_TEST_COMMON_EXAMPLE_OPTIONS} -DLV_FONT_DEFAULT=&lv_font_montserrat_24 - -DLV_USE_FS_STDIO='A' - -DLV_USE_FS_POSIX='B' + -DLV_USE_FS_STDIO=1 + -DLV_FS_STDIO_LETTER='A' + -DLV_USE_FS_POSIX=1 + -DLV_FS_POSIX_LETTER='B' -DLV_USE_PNG=1 -DLV_USE_BMP=1 -DLV_USE_SJPG=1