1 /**
2  * @file lv_fs.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_fs_private.h"
10 
11 #include "../misc/lv_assert.h"
12 #include "../misc/lv_profiler.h"
13 #include "../stdlib/lv_string.h"
14 #include "lv_ll.h"
15 #include "../core/lv_global.h"
16 
17 /*********************
18  *      DEFINES
19  *********************/
20 
21 #if LV_FS_DEFAULT_DRIVER_LETTER != '\0' && (LV_FS_DEFAULT_DRIVER_LETTER < 'A' || 'Z' < LV_FS_DEFAULT_DRIVER_LETTER)
22     #error "When enabled, LV_FS_DEFAULT_DRIVER_LETTER needs to be a capital ASCII letter (A-Z)"
23 #endif
24 
25 #define fsdrv_ll_p &(LV_GLOBAL_DEFAULT()->fsdrv_ll)
26 
27 /**********************
28  *      TYPEDEFS
29  **********************/
30 typedef struct {
31     char driver_letter;
32     const char * real_path;
33 } resolved_path_t;
34 
35 /**********************
36  *  STATIC PROTOTYPES
37  **********************/
38 static resolved_path_t lv_fs_resolve_path(const char * path);
39 static lv_fs_res_t lv_fs_read_cached(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br);
40 static lv_fs_res_t lv_fs_write_cached(lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw);
41 static lv_fs_res_t lv_fs_seek_cached(lv_fs_file_t * file_p, uint32_t pos, lv_fs_whence_t whence);
42 
43 /**********************
44  *  STATIC VARIABLES
45  **********************/
46 
47 /**********************
48  *      MACROS
49  **********************/
50 
51 /**********************
52  *   GLOBAL FUNCTIONS
53  **********************/
54 
lv_fs_init(void)55 void lv_fs_init(void)
56 {
57     lv_ll_init(fsdrv_ll_p, sizeof(lv_fs_drv_t *));
58 }
59 
lv_fs_deinit(void)60 void lv_fs_deinit(void)
61 {
62     lv_ll_clear(fsdrv_ll_p);
63 }
64 
lv_fs_is_ready(char letter)65 bool lv_fs_is_ready(char letter)
66 {
67     lv_fs_drv_t * drv = lv_fs_get_drv(letter);
68 
69     if(drv == NULL) return false; /*An unknown driver in not ready*/
70 
71     if(drv->ready_cb == NULL) return true; /*Assume the driver is always ready if no handler provided*/
72 
73     return drv->ready_cb(drv);
74 }
75 
lv_fs_open(lv_fs_file_t * file_p,const char * path,lv_fs_mode_t mode)76 lv_fs_res_t lv_fs_open(lv_fs_file_t * file_p, const char * path, lv_fs_mode_t mode)
77 {
78     if(path == NULL) {
79         LV_LOG_WARN("Can't open file: path is NULL");
80         return LV_FS_RES_INV_PARAM;
81     }
82 
83     resolved_path_t resolved_path = lv_fs_resolve_path(path);
84 
85     lv_fs_drv_t * drv = lv_fs_get_drv(resolved_path.driver_letter);
86 
87     if(drv == NULL) {
88         LV_LOG_WARN("Can't open file (%s): unknown driver letter", path);
89         return LV_FS_RES_NOT_EX;
90     }
91 
92     if(drv->ready_cb) {
93         if(drv->ready_cb(drv) == false) {
94             LV_LOG_WARN("Can't open file (%s): driver not ready", path);
95             return LV_FS_RES_HW_ERR;
96         }
97     }
98 
99     if(drv->open_cb == NULL) {
100         LV_LOG_WARN("Can't open file (%s): open function not exists", path);
101         return LV_FS_RES_NOT_IMP;
102     }
103 
104     LV_PROFILER_FS_BEGIN;
105 
106     file_p->drv = drv;
107 
108     /* For memory-mapped files we set the file handle to our file descriptor so that we can access the cache from the file operations */
109     if(drv->cache_size == LV_FS_CACHE_FROM_BUFFER) {
110         file_p->file_d = file_p;
111     }
112     else {
113         void * file_d = drv->open_cb(drv, resolved_path.real_path, mode);
114         if(file_d == NULL || file_d == (void *)(-1)) {
115             LV_PROFILER_FS_END;
116             return LV_FS_RES_UNKNOWN;
117         }
118         file_p->file_d = file_d;
119     }
120 
121     if(drv->cache_size) {
122         file_p->cache = lv_malloc_zeroed(sizeof(lv_fs_file_cache_t));
123         LV_ASSERT_MALLOC(file_p->cache);
124 
125         /* If this is a memory-mapped file, then set "cache" to the memory buffer */
126         if(drv->cache_size == LV_FS_CACHE_FROM_BUFFER) {
127             lv_fs_path_ex_t * path_ex = (lv_fs_path_ex_t *)path;
128             file_p->cache->buffer = (void *)path_ex->buffer;
129             file_p->cache->start = 0;
130             file_p->cache->file_position = 0;
131             file_p->cache->end = path_ex->size;
132         }
133         /*Set an invalid range by default*/
134         else {
135             file_p->cache->start = UINT32_MAX;
136             file_p->cache->end = UINT32_MAX - 1;
137         }
138     }
139 
140     LV_PROFILER_FS_END;
141 
142     return LV_FS_RES_OK;
143 }
144 
lv_fs_make_path_from_buffer(lv_fs_path_ex_t * path,char letter,const void * buf,uint32_t size)145 void lv_fs_make_path_from_buffer(lv_fs_path_ex_t * path, char letter, const void * buf, uint32_t size)
146 {
147     path->path[0] = letter;
148     path->path[1] = ':';
149     path->path[2] = 0;
150     path->buffer = buf;
151     path->size = size;
152 }
153 
lv_fs_close(lv_fs_file_t * file_p)154 lv_fs_res_t lv_fs_close(lv_fs_file_t * file_p)
155 {
156     if(file_p->drv == NULL) {
157         return LV_FS_RES_INV_PARAM;
158     }
159 
160     if(file_p->drv->close_cb == NULL) {
161         return LV_FS_RES_NOT_IMP;
162     }
163 
164     LV_PROFILER_FS_BEGIN;
165 
166     lv_fs_res_t res = file_p->drv->close_cb(file_p->drv, file_p->file_d);
167 
168     if(file_p->drv->cache_size && file_p->cache) {
169         /* Only free cache if it was pre-allocated (for memory-mapped files it is never allocated) */
170         if(file_p->drv->cache_size != LV_FS_CACHE_FROM_BUFFER && file_p->cache->buffer) {
171             lv_free(file_p->cache->buffer);
172         }
173 
174         lv_free(file_p->cache);
175     }
176 
177     file_p->file_d = NULL;
178     file_p->drv    = NULL;
179     file_p->cache  = NULL;
180 
181     LV_PROFILER_FS_END;
182 
183     return res;
184 }
185 
lv_fs_read(lv_fs_file_t * file_p,void * buf,uint32_t btr,uint32_t * br)186 lv_fs_res_t lv_fs_read(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br)
187 {
188     if(br != NULL) *br = 0;
189     if(file_p->drv == NULL) return LV_FS_RES_INV_PARAM;
190 
191     if(file_p->drv->cache_size) {
192         if(file_p->drv->read_cb == NULL || file_p->drv->seek_cb == NULL) return LV_FS_RES_NOT_IMP;
193     }
194     else {
195         if(file_p->drv->read_cb == NULL) return LV_FS_RES_NOT_IMP;
196     }
197 
198     LV_PROFILER_FS_BEGIN;
199 
200     uint32_t br_tmp = 0;
201     lv_fs_res_t res;
202 
203     if(file_p->drv->cache_size) {
204         res = lv_fs_read_cached(file_p, buf, btr, &br_tmp);
205     }
206     else {
207         res = file_p->drv->read_cb(file_p->drv, file_p->file_d, buf, btr, &br_tmp);
208     }
209 
210     if(br != NULL) *br = br_tmp;
211 
212     LV_PROFILER_FS_END;
213 
214     return res;
215 }
216 
lv_fs_write(lv_fs_file_t * file_p,const void * buf,uint32_t btw,uint32_t * bw)217 lv_fs_res_t lv_fs_write(lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw)
218 {
219     if(bw != NULL) *bw = 0;
220 
221     if(file_p->drv == NULL) {
222         return LV_FS_RES_INV_PARAM;
223     }
224 
225     if(file_p->drv->cache_size) {
226         if(file_p->drv->write_cb == NULL || file_p->drv->seek_cb == NULL) return LV_FS_RES_NOT_IMP;
227     }
228     else {
229         if(file_p->drv->write_cb == NULL) return LV_FS_RES_NOT_IMP;
230     }
231 
232     LV_PROFILER_FS_BEGIN;
233 
234     lv_fs_res_t res;
235     uint32_t bw_tmp = 0;
236     if(file_p->drv->cache_size) {
237         res = lv_fs_write_cached(file_p, buf, btw, &bw_tmp);
238     }
239     else {
240         res = file_p->drv->write_cb(file_p->drv, file_p->file_d, buf, btw, &bw_tmp);
241     }
242     if(bw != NULL) *bw = bw_tmp;
243 
244     LV_PROFILER_FS_END;
245     return res;
246 }
247 
lv_fs_seek(lv_fs_file_t * file_p,uint32_t pos,lv_fs_whence_t whence)248 lv_fs_res_t lv_fs_seek(lv_fs_file_t * file_p, uint32_t pos, lv_fs_whence_t whence)
249 {
250     if(file_p->drv == NULL) {
251         return LV_FS_RES_INV_PARAM;
252     }
253 
254     if(file_p->drv->cache_size) {
255         if(file_p->drv->seek_cb == NULL || file_p->drv->tell_cb == NULL) return LV_FS_RES_NOT_IMP;
256     }
257     else {
258         if(file_p->drv->seek_cb == NULL) return LV_FS_RES_NOT_IMP;
259     }
260 
261     LV_PROFILER_FS_BEGIN;
262 
263     lv_fs_res_t res;
264     if(file_p->drv->cache_size) {
265         res = lv_fs_seek_cached(file_p, pos, whence);
266     }
267     else {
268         res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, pos, whence);
269     }
270 
271     LV_PROFILER_FS_END;
272 
273     return res;
274 }
275 
lv_fs_tell(lv_fs_file_t * file_p,uint32_t * pos)276 lv_fs_res_t lv_fs_tell(lv_fs_file_t * file_p, uint32_t * pos)
277 {
278     if(file_p->drv == NULL) {
279         *pos = 0;
280         return LV_FS_RES_INV_PARAM;
281     }
282 
283     if(file_p->drv->cache_size == 0 && file_p->drv->tell_cb == NULL) {
284         *pos = 0;
285         return LV_FS_RES_NOT_IMP;
286     }
287 
288     LV_PROFILER_FS_BEGIN;
289 
290     lv_fs_res_t res;
291     if(file_p->drv->cache_size) {
292         *pos = file_p->cache->file_position;
293         res = LV_FS_RES_OK;
294     }
295     else {
296         res = file_p->drv->tell_cb(file_p->drv, file_p->file_d, pos);
297     }
298 
299     LV_PROFILER_FS_END;
300 
301     return res;
302 }
303 
lv_fs_dir_open(lv_fs_dir_t * rddir_p,const char * path)304 lv_fs_res_t lv_fs_dir_open(lv_fs_dir_t * rddir_p, const char * path)
305 {
306     if(path == NULL) return LV_FS_RES_INV_PARAM;
307 
308     resolved_path_t resolved_path = lv_fs_resolve_path(path);
309 
310     lv_fs_drv_t * drv = lv_fs_get_drv(resolved_path.driver_letter);
311 
312     if(drv == NULL) {
313         return LV_FS_RES_NOT_EX;
314     }
315 
316     if(drv->ready_cb) {
317         if(drv->ready_cb(drv) == false) {
318             return LV_FS_RES_HW_ERR;
319         }
320     }
321 
322     if(drv->dir_open_cb == NULL) {
323         return LV_FS_RES_NOT_IMP;
324     }
325 
326     LV_PROFILER_FS_BEGIN;
327 
328     void * dir_d = drv->dir_open_cb(drv, resolved_path.real_path);
329 
330     if(dir_d == NULL || dir_d == (void *)(-1)) {
331         LV_PROFILER_FS_END;
332         return LV_FS_RES_UNKNOWN;
333     }
334 
335     rddir_p->drv = drv;
336     rddir_p->dir_d = dir_d;
337 
338     LV_PROFILER_FS_END;
339 
340     return LV_FS_RES_OK;
341 }
342 
lv_fs_dir_read(lv_fs_dir_t * rddir_p,char * fn,uint32_t fn_len)343 lv_fs_res_t lv_fs_dir_read(lv_fs_dir_t * rddir_p, char * fn, uint32_t fn_len)
344 {
345     if(fn_len == 0) {
346         return LV_FS_RES_INV_PARAM;
347     }
348 
349     if(rddir_p->drv == NULL || rddir_p->dir_d == NULL) {
350         fn[0] = '\0';
351         return LV_FS_RES_INV_PARAM;
352     }
353 
354     if(rddir_p->drv->dir_read_cb == NULL) {
355         fn[0] = '\0';
356         return LV_FS_RES_NOT_IMP;
357     }
358 
359     LV_PROFILER_FS_BEGIN;
360 
361     lv_fs_res_t res = rddir_p->drv->dir_read_cb(rddir_p->drv, rddir_p->dir_d, fn, fn_len);
362 
363     LV_PROFILER_FS_END;
364 
365     return res;
366 }
367 
lv_fs_dir_close(lv_fs_dir_t * rddir_p)368 lv_fs_res_t lv_fs_dir_close(lv_fs_dir_t * rddir_p)
369 {
370     if(rddir_p->drv == NULL || rddir_p->dir_d == NULL) {
371         return LV_FS_RES_INV_PARAM;
372     }
373 
374     if(rddir_p->drv->dir_close_cb == NULL) {
375         return LV_FS_RES_NOT_IMP;
376     }
377 
378     LV_PROFILER_FS_BEGIN;
379 
380     lv_fs_res_t res = rddir_p->drv->dir_close_cb(rddir_p->drv, rddir_p->dir_d);
381 
382     rddir_p->dir_d = NULL;
383     rddir_p->drv   = NULL;
384 
385     LV_PROFILER_FS_END;
386 
387     return res;
388 }
389 
lv_fs_drv_init(lv_fs_drv_t * drv)390 void lv_fs_drv_init(lv_fs_drv_t * drv)
391 {
392     lv_memzero(drv, sizeof(lv_fs_drv_t));
393 }
394 
lv_fs_drv_register(lv_fs_drv_t * drv_p)395 void lv_fs_drv_register(lv_fs_drv_t * drv_p)
396 {
397     /*Save the new driver*/
398     lv_fs_drv_t ** new_drv;
399     new_drv = lv_ll_ins_head(fsdrv_ll_p);
400     LV_ASSERT_MALLOC(new_drv);
401     if(new_drv == NULL) return;
402 
403     *new_drv = drv_p;
404 }
405 
lv_fs_get_drv(char letter)406 lv_fs_drv_t * lv_fs_get_drv(char letter)
407 {
408     lv_fs_drv_t ** drv;
409 
410     LV_LL_READ(fsdrv_ll_p, drv) {
411         if((*drv)->letter == letter) {
412             return *drv;
413         }
414     }
415 
416     return NULL;
417 }
418 
lv_fs_get_letters(char * buf)419 char * lv_fs_get_letters(char * buf)
420 {
421     lv_fs_drv_t ** drv;
422     uint8_t i = 0;
423 
424     LV_LL_READ(fsdrv_ll_p, drv) {
425         buf[i] = (*drv)->letter;
426         i++;
427     }
428 
429     buf[i] = '\0';
430 
431     return buf;
432 }
433 
lv_fs_get_ext(const char * fn)434 const char * lv_fs_get_ext(const char * fn)
435 {
436     size_t i;
437     for(i = lv_strlen(fn); i > 0; i--) {
438         if(fn[i] == '.') {
439             return &fn[i + 1];
440         }
441         else if(fn[i] == '/' || fn[i] == '\\') {
442             return ""; /*No extension if a '\' or '/' found*/
443         }
444     }
445 
446     return ""; /*Empty string if no '.' in the file name.*/
447 }
448 
lv_fs_up(char * path)449 char * lv_fs_up(char * path)
450 {
451     size_t len = lv_strlen(path);
452     if(len == 0) return path;
453 
454     len--; /*Go before the trailing '\0'*/
455 
456     /*Ignore trailing '/' or '\'*/
457     while(path[len] == '/' || path[len] == '\\') {
458         path[len] = '\0';
459         if(len > 0)
460             len--;
461         else
462             return path;
463     }
464 
465     size_t i;
466     for(i = len; i > 0; i--) {
467         if(path[i] == '/' || path[i] == '\\') break;
468     }
469 
470     if(i > 0) path[i] = '\0';
471 
472     return path;
473 }
474 
lv_fs_get_last(const char * path)475 const char * lv_fs_get_last(const char * path)
476 {
477     size_t len = lv_strlen(path);
478     if(len == 0) return path;
479 
480     len--; /*Go before the trailing '\0'*/
481 
482     /*Ignore trailing '/' or '\'*/
483     while(path[len] == '/' || path[len] == '\\') {
484         if(len > 0)
485             len--;
486         else
487             return path;
488     }
489 
490     size_t i;
491     for(i = len; i > 0; i--) {
492         if(path[i] == '/' || path[i] == '\\') break;
493     }
494 
495     /*No '/' or '\' in the path so return with path itself*/
496     if(i == 0) return path;
497 
498     return &path[i + 1];
499 }
500 /**********************
501  *   STATIC FUNCTIONS
502  **********************/
503 
504 /**
505  * Extract the drive letter and the real path from LVGL's "abstracted file system" path string
506  * @param path path string (E.g. S:/folder/file.txt)
507  */
lv_fs_resolve_path(const char * path)508 static resolved_path_t lv_fs_resolve_path(const char * path)
509 {
510     resolved_path_t resolved;
511 
512 #if LV_FS_DEFAULT_DRIVER_LETTER != '\0' /*When using default drive letter, strict format (X:) is mandatory*/
513     bool has_drive_prefix = ('A' <= path[0]) && (path[0] <= 'Z') && (path[1] == ':');
514 
515     if(has_drive_prefix) {
516         resolved.driver_letter = path[0];
517         resolved.real_path = path + 2;
518     }
519     else {
520         resolved.driver_letter = LV_FS_DEFAULT_DRIVER_LETTER;
521         resolved.real_path = path;
522     }
523 # else /*Lean rules for backward compatibility*/
524     resolved.driver_letter = path[0];
525 
526     if(*path != '\0') {
527         path++; /*Ignore the driver letter*/
528         if(*path == ':') path++;
529     }
530 
531     resolved.real_path = path;
532 #endif
533 
534     return resolved;
535 }
536 
lv_fs_read_cached(lv_fs_file_t * file_p,void * buf,uint32_t btr,uint32_t * br)537 static lv_fs_res_t lv_fs_read_cached(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br)
538 {
539     lv_fs_res_t res = LV_FS_RES_OK;
540     uint32_t file_position = file_p->cache->file_position;
541     uint32_t start = file_p->cache->start;
542     uint32_t end = file_p->cache->end;
543     char * buffer = file_p->cache->buffer;
544     uint32_t buffer_size = file_p->drv->cache_size;
545 
546     if(start <= file_position && file_position <= end) {
547         /* Data can be read from cache buffer */
548         uint32_t buffer_remaining_length = (uint32_t)end - file_position + 1;
549         uint32_t buffer_offset = (end - start) - buffer_remaining_length + 1;
550 
551         /* Do not allow reading beyond the actual memory block for memory-mapped files */
552         if(file_p->drv->cache_size == LV_FS_CACHE_FROM_BUFFER) {
553             if(btr > buffer_remaining_length)
554                 btr = buffer_remaining_length - 1;
555         }
556 
557         if(btr <= buffer_remaining_length) {
558             /*Data is in cache buffer, and buffer end not reached, no need to read from FS*/
559             lv_memcpy(buf, buffer + buffer_offset, btr);
560             *br = btr;
561         }
562         else {
563             /*First part of data is in cache buffer, but we need to read rest of data from FS*/
564             lv_memcpy(buf, buffer + buffer_offset, buffer_remaining_length);
565 
566             file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->end + 1,
567                                  LV_FS_SEEK_SET);
568 
569             uint32_t bytes_read_to_buffer = 0;
570             if(btr - buffer_remaining_length > buffer_size) {
571                 /*If remaining data chuck is bigger than buffer size, then do not use cache, instead read it directly from FS*/
572                 res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (char *)buf + buffer_remaining_length,
573                                            btr - buffer_remaining_length, &bytes_read_to_buffer);
574             }
575             else {
576                 /*If remaining data chunk is smaller than buffer size, then read into cache buffer*/
577                 res = file_p->drv->read_cb(file_p->drv, file_p->file_d, buffer, buffer_size, &bytes_read_to_buffer);
578                 file_p->cache->start = file_p->cache->end + 1;
579                 file_p->cache->end = file_p->cache->start + bytes_read_to_buffer - 1;
580 
581                 uint16_t data_chunk_remaining = LV_MIN(btr - buffer_remaining_length, bytes_read_to_buffer);
582                 lv_memcpy((char *)buf + buffer_remaining_length, buffer, data_chunk_remaining);
583             }
584             *br = LV_MIN(buffer_remaining_length + bytes_read_to_buffer, btr);
585         }
586     }
587     else {
588         file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position,
589                              LV_FS_SEEK_SET);
590 
591         /*Data is not in cache buffer*/
592         if(btr > buffer_size) {
593             /*If bigger data is requested, then do not use cache, instead read it directly*/
594             res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buf, btr, br);
595         }
596         else {
597             /*If small data is requested, then read from FS into cache buffer*/
598             if(buffer == NULL) {
599                 file_p->cache->buffer = lv_malloc(buffer_size);
600                 LV_ASSERT_MALLOC(file_p->cache->buffer);
601                 buffer = file_p->cache->buffer;
602             }
603 
604             uint32_t bytes_read_to_buffer = 0;
605             res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buffer, buffer_size, &bytes_read_to_buffer);
606             file_p->cache->start = file_position;
607             file_p->cache->end = file_p->cache->start + bytes_read_to_buffer - 1;
608 
609             *br = LV_MIN(btr, bytes_read_to_buffer);
610             lv_memcpy(buf, buffer, *br);
611 
612         }
613     }
614 
615     if(res == LV_FS_RES_OK) {
616         file_p->cache->file_position += *br;
617     }
618 
619     return res;
620 }
621 
lv_fs_write_cached(lv_fs_file_t * file_p,const void * buf,uint32_t btw,uint32_t * bw)622 static lv_fs_res_t lv_fs_write_cached(lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw)
623 {
624     lv_fs_res_t res = LV_FS_RES_OK;
625 
626     /*Need to do FS seek before writing data to FS*/
627     res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position, LV_FS_SEEK_SET);
628     if(res != LV_FS_RES_OK) return res;
629 
630     res = file_p->drv->write_cb(file_p->drv, file_p->file_d, buf, btw, bw);
631     if(res != LV_FS_RES_OK) return res;
632 
633     if(file_p->cache->end >= file_p->cache->start) {
634         uint32_t start_position = file_p->cache->file_position;
635         uint32_t end_position = file_p->cache->file_position + *bw - 1;
636         char * cache_buffer = file_p->cache->buffer;
637         const char * write_buffer = buf;
638 
639         if(start_position <= file_p->cache->start && end_position >= file_p->cache->end) {
640             lv_memcpy(cache_buffer,
641                       write_buffer + (file_p->cache->start - start_position),
642                       file_p->cache->end + 1 - file_p->cache->start);
643         }
644         else if(start_position >= file_p->cache->start && end_position <= file_p->cache->end) {
645             lv_memcpy(cache_buffer + (start_position - file_p->cache->start),
646                       write_buffer,
647                       end_position + 1 - start_position);
648         }
649         else if(end_position >= file_p->cache->start && end_position <= file_p->cache->end) {
650             lv_memcpy(cache_buffer,
651                       write_buffer + (file_p->cache->start - start_position),
652                       end_position + 1 - file_p->cache->start);
653         }
654         else if(start_position >= file_p->cache->start && start_position <= file_p->cache->end) {
655             lv_memcpy(cache_buffer + (start_position - file_p->cache->start),
656                       write_buffer,
657                       file_p->cache->end + 1 - start_position);
658         }
659     }
660 
661     file_p->cache->file_position += *bw;
662 
663     return res;
664 }
665 
lv_fs_seek_cached(lv_fs_file_t * file_p,uint32_t pos,lv_fs_whence_t whence)666 static lv_fs_res_t lv_fs_seek_cached(lv_fs_file_t * file_p, uint32_t pos, lv_fs_whence_t whence)
667 {
668     lv_fs_res_t res = LV_FS_RES_OK;
669     switch(whence) {
670         case LV_FS_SEEK_SET: {
671                 file_p->cache->file_position = pos;
672                 break;
673             }
674         case LV_FS_SEEK_CUR: {
675                 file_p->cache->file_position += pos;
676                 break;
677             }
678         case LV_FS_SEEK_END: {
679                 /*Because we don't know the file size, we do a little trick: do a FS seek, then get the new file position from FS*/
680                 res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, pos, whence);
681                 if(res == LV_FS_RES_OK) {
682                     uint32_t tmp_position;
683                     res = file_p->drv->tell_cb(file_p->drv, file_p->file_d, &tmp_position);
684 
685                     if(res == LV_FS_RES_OK) {
686                         file_p->cache->file_position = tmp_position;
687                     }
688                 }
689                 break;
690             }
691     }
692 
693     return res;
694 }
695