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