1 /**
2  * @file lv_fs.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_fs.h"
10 
11 #include "../misc/lv_assert.h"
12 #include "lv_ll.h"
13 #include <string.h>
14 #include "lv_gc.h"
15 
16 /*********************
17  *      DEFINES
18  *********************/
19 
20 /**********************
21  *      TYPEDEFS
22  **********************/
23 
24 /**********************
25  *  STATIC PROTOTYPES
26  **********************/
27 static const char * lv_fs_get_real_path(const char * path);
28 
29 /**********************
30  *  STATIC VARIABLES
31  **********************/
32 
33 /**********************
34  *      MACROS
35  **********************/
36 
37 /**********************
38  *   GLOBAL FUNCTIONS
39  **********************/
40 
_lv_fs_init(void)41 void _lv_fs_init(void)
42 {
43     _lv_ll_init(&LV_GC_ROOT(_lv_fsdrv_ll), sizeof(lv_fs_drv_t *));
44 }
45 
lv_fs_is_ready(char letter)46 bool lv_fs_is_ready(char letter)
47 {
48     lv_fs_drv_t * drv = lv_fs_get_drv(letter);
49 
50     if(drv == NULL) return false; /*An unknown driver in not ready*/
51 
52     if(drv->ready_cb == NULL) return true; /*Assume the driver is always ready if no handler provided*/
53 
54     return drv->ready_cb(drv);
55 }
56 
lv_fs_open(lv_fs_file_t * file_p,const char * path,lv_fs_mode_t mode)57 lv_fs_res_t lv_fs_open(lv_fs_file_t * file_p, const char * path, lv_fs_mode_t mode)
58 {
59     if(path == NULL) {
60         LV_LOG_WARN("Can't open file: path is NULL");
61         return LV_FS_RES_INV_PARAM;
62     }
63 
64     char letter = path[0];
65     lv_fs_drv_t * drv = lv_fs_get_drv(letter);
66 
67     if(drv == NULL) {
68         LV_LOG_WARN("Can't open file (%s): unknown driver letter", path);
69         return LV_FS_RES_NOT_EX;
70     }
71 
72     if(drv->ready_cb) {
73         if(drv->ready_cb(drv) == false) {
74             LV_LOG_WARN("Can't open file (%s): driver not ready", path);
75             return LV_FS_RES_HW_ERR;
76         }
77     }
78 
79     if(drv->open_cb == NULL) {
80         LV_LOG_WARN("Can't open file (%s): open function not exists", path);
81         return LV_FS_RES_NOT_IMP;
82     }
83 
84     const char * real_path = lv_fs_get_real_path(path);
85     void * file_d = drv->open_cb(drv, real_path, mode);
86 
87     if(file_d == NULL || file_d == (void *)(-1)) {
88         return LV_FS_RES_UNKNOWN;
89     }
90 
91     file_p->drv = drv;
92     file_p->file_d = file_d;
93 
94     if(drv->cache_size) {
95         file_p->cache = lv_mem_alloc(sizeof(lv_fs_file_cache_t));
96         LV_ASSERT_MALLOC(file_p->cache);
97         lv_memset_00(file_p->cache, sizeof(lv_fs_file_cache_t));
98     }
99 
100     return LV_FS_RES_OK;
101 }
102 
lv_fs_close(lv_fs_file_t * file_p)103 lv_fs_res_t lv_fs_close(lv_fs_file_t * file_p)
104 {
105     if(file_p->drv == NULL) {
106         return LV_FS_RES_INV_PARAM;
107     }
108 
109     if(file_p->drv->close_cb == NULL) {
110         return LV_FS_RES_NOT_IMP;
111     }
112 
113     lv_fs_res_t res = file_p->drv->close_cb(file_p->drv, file_p->file_d);
114 
115     if(file_p->drv->cache_size && file_p->cache) {
116         if(file_p->cache->buffer) {
117             lv_mem_free(file_p->cache->buffer);
118         }
119 
120         lv_mem_free(file_p->cache);
121     }
122 
123     file_p->file_d = NULL;
124     file_p->drv    = NULL;
125     file_p->cache  = NULL;
126 
127     return res;
128 }
129 
lv_fs_read_cached(lv_fs_file_t * file_p,char * buf,uint32_t btr,uint32_t * br)130 static lv_fs_res_t lv_fs_read_cached(lv_fs_file_t * file_p, char * buf, uint32_t btr, uint32_t * br)
131 {
132     lv_fs_res_t res = LV_FS_RES_OK;
133     uint32_t file_position = file_p->cache->file_position;
134     uint32_t start = file_p->cache->start;
135     uint32_t end = file_p->cache->end;
136     char * buffer = file_p->cache->buffer;
137     uint16_t buffer_size = file_p->drv->cache_size;
138 
139     if(start <= file_position && file_position < end) {
140         /* Data can be read from cache buffer */
141 
142         uint16_t buffer_offset = file_position - start;
143         uint16_t buffer_remaining_length = buffer_size - buffer_offset;
144 
145         if(btr <= buffer_remaining_length) {
146             /*Data is in cache buffer, and buffer end not reached, no need to read from FS*/
147             lv_memcpy(buf, buffer + buffer_offset, btr);
148         }
149         else {
150             /*First part of data is in cache buffer, but we need to read rest of data from FS*/
151             lv_memcpy(buf, buffer + buffer_offset, buffer_remaining_length);
152 
153             if(btr > buffer_size) {
154                 /*If remaining data chuck is bigger than buffer size, then do not use cache, instead read it directly from FS*/
155                 res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)(buf + buffer_remaining_length),
156                                            btr - buffer_remaining_length, br);
157             }
158             else {
159                 /*If remaining data chunk is smaller than buffer size, then read into cache buffer*/
160                 uint32_t bytes_read_to_buffer = 0;
161 
162                 /*Read into cache buffer:*/
163                 res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buffer, buffer_size, &bytes_read_to_buffer);
164                 file_p->cache->start = file_p->cache->end + 1;
165                 file_p->cache->end = file_p->cache->start + bytes_read_to_buffer;
166 
167                 uint16_t data_chunk_remaining = btr - buffer_remaining_length;
168                 memcpy(buf + buffer_remaining_length, buffer, data_chunk_remaining);
169             }
170         }
171     }
172     else {
173         /*Data is not in cache buffer*/
174 
175         if(btr > buffer_size) {
176             /*If bigger data is requested, then do not use cache, instead read it directly*/
177             res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buf, btr, br);
178         }
179         else {
180             /*If small data is requested, then read from FS into cache buffer*/
181             if(buffer == NULL) {
182                 file_p->cache->buffer = lv_mem_alloc(buffer_size);
183                 LV_ASSERT_MALLOC(file_p->cache->buffer);
184                 buffer = file_p->cache->buffer;
185             }
186 
187             uint32_t bytes_read_to_buffer = 0;
188             res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)buffer, buffer_size, &bytes_read_to_buffer);
189             file_p->cache->start = file_position;
190             file_p->cache->end = file_p->cache->start + bytes_read_to_buffer;
191 
192             memcpy(buf, buffer, btr);
193         }
194     }
195 
196     if(res == LV_FS_RES_OK) {
197         *br = btr;
198         file_p->cache->file_position += btr;
199     }
200 
201     return res;
202 }
203 
lv_fs_read(lv_fs_file_t * file_p,void * buf,uint32_t btr,uint32_t * br)204 lv_fs_res_t lv_fs_read(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br)
205 {
206     if(br != NULL) *br = 0;
207     if(file_p->drv == NULL) return LV_FS_RES_INV_PARAM;
208     if(file_p->drv->read_cb == NULL) return LV_FS_RES_NOT_IMP;
209 
210     uint32_t br_tmp = 0;
211     lv_fs_res_t res;
212 
213     if(file_p->drv->cache_size) {
214         res = lv_fs_read_cached(file_p, (char *)buf, btr, &br_tmp);
215     }
216     else {
217         res = file_p->drv->read_cb(file_p->drv, file_p->file_d, buf, btr, &br_tmp);
218     }
219 
220     if(br != NULL) *br = br_tmp;
221 
222     return res;
223 }
224 
lv_fs_write(lv_fs_file_t * file_p,const void * buf,uint32_t btw,uint32_t * bw)225 lv_fs_res_t lv_fs_write(lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw)
226 {
227     if(bw != NULL) *bw = 0;
228 
229     if(file_p->drv == NULL) {
230         return LV_FS_RES_INV_PARAM;
231     }
232 
233     if(file_p->drv->write_cb == NULL) {
234         return LV_FS_RES_NOT_IMP;
235     }
236 
237     uint32_t bw_tmp = 0;
238     lv_fs_res_t res = file_p->drv->write_cb(file_p->drv, file_p->file_d, buf, btw, &bw_tmp);
239     if(bw != NULL) *bw = bw_tmp;
240 
241     return res;
242 }
243 
lv_fs_seek(lv_fs_file_t * file_p,uint32_t pos,lv_fs_whence_t whence)244 lv_fs_res_t lv_fs_seek(lv_fs_file_t * file_p, uint32_t pos, lv_fs_whence_t whence)
245 {
246     if(file_p->drv == NULL) {
247         return LV_FS_RES_INV_PARAM;
248     }
249 
250     if(file_p->drv->seek_cb == NULL) {
251         return LV_FS_RES_NOT_IMP;
252     }
253 
254     lv_fs_res_t res = LV_FS_RES_OK;
255     if(file_p->drv->cache_size) {
256         switch(whence) {
257             case LV_FS_SEEK_SET: {
258                     file_p->cache->file_position = pos;
259 
260                     /*FS seek if new position is outside cache buffer*/
261                     if(file_p->cache->file_position < file_p->cache->start || file_p->cache->file_position > file_p->cache->end) {
262                         res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position, LV_FS_SEEK_SET);
263                     }
264 
265                     break;
266                 }
267             case LV_FS_SEEK_CUR: {
268                     file_p->cache->file_position += pos;
269 
270                     /*FS seek if new position is outside cache buffer*/
271                     if(file_p->cache->file_position < file_p->cache->start || file_p->cache->file_position > file_p->cache->end) {
272                         res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position, LV_FS_SEEK_SET);
273                     }
274 
275                     break;
276                 }
277             case LV_FS_SEEK_END: {
278                     /*Because we don't know the file size, we do a little trick: do a FS seek, then get new file position from FS*/
279                     res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, pos, whence);
280                     if(res == LV_FS_RES_OK) {
281                         uint32_t tmp_position;
282                         res = file_p->drv->tell_cb(file_p->drv, file_p->file_d, &tmp_position);
283 
284                         if(res == LV_FS_RES_OK) {
285                             file_p->cache->file_position = tmp_position;
286                         }
287                     }
288                     break;
289                 }
290         }
291     }
292     else {
293         res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, pos, whence);
294     }
295 
296     return res;
297 }
298 
lv_fs_tell(lv_fs_file_t * file_p,uint32_t * pos)299 lv_fs_res_t lv_fs_tell(lv_fs_file_t * file_p, uint32_t * pos)
300 {
301     if(file_p->drv == NULL) {
302         *pos = 0;
303         return LV_FS_RES_INV_PARAM;
304     }
305 
306     if(file_p->drv->tell_cb == NULL) {
307         *pos = 0;
308         return LV_FS_RES_NOT_IMP;
309     }
310 
311     lv_fs_res_t res;
312     if(file_p->drv->cache_size) {
313         *pos = file_p->cache->file_position;
314         res = LV_FS_RES_OK;
315     }
316     else {
317         res = file_p->drv->tell_cb(file_p->drv, file_p->file_d, pos);
318     }
319 
320     return res;
321 }
322 
lv_fs_dir_open(lv_fs_dir_t * rddir_p,const char * path)323 lv_fs_res_t lv_fs_dir_open(lv_fs_dir_t * rddir_p, const char * path)
324 {
325     if(path == NULL) return LV_FS_RES_INV_PARAM;
326 
327     char letter = path[0];
328     lv_fs_drv_t * drv = lv_fs_get_drv(letter);
329 
330     if(drv == NULL) {
331         return LV_FS_RES_NOT_EX;
332     }
333 
334     if(drv->ready_cb) {
335         if(drv->ready_cb(drv) == false) {
336             return LV_FS_RES_HW_ERR;
337         }
338     }
339 
340     if(drv->dir_open_cb == NULL) {
341         return LV_FS_RES_NOT_IMP;
342     }
343 
344     const char * real_path = lv_fs_get_real_path(path);
345     void * dir_d = drv->dir_open_cb(drv, real_path);
346 
347     if(dir_d == NULL || dir_d == (void *)(-1)) {
348         return LV_FS_RES_UNKNOWN;
349     }
350 
351     rddir_p->drv = drv;
352     rddir_p->dir_d = dir_d;
353 
354     return LV_FS_RES_OK;
355 }
356 
lv_fs_dir_read(lv_fs_dir_t * rddir_p,char * fn)357 lv_fs_res_t lv_fs_dir_read(lv_fs_dir_t * rddir_p, char * fn)
358 {
359     if(rddir_p->drv == NULL || rddir_p->dir_d == NULL) {
360         fn[0] = '\0';
361         return LV_FS_RES_INV_PARAM;
362     }
363 
364     if(rddir_p->drv->dir_read_cb == NULL) {
365         fn[0] = '\0';
366         return LV_FS_RES_NOT_IMP;
367     }
368 
369     lv_fs_res_t res = rddir_p->drv->dir_read_cb(rddir_p->drv, rddir_p->dir_d, fn);
370 
371     return res;
372 }
373 
lv_fs_dir_close(lv_fs_dir_t * rddir_p)374 lv_fs_res_t lv_fs_dir_close(lv_fs_dir_t * rddir_p)
375 {
376     if(rddir_p->drv == NULL || rddir_p->dir_d == NULL) {
377         return LV_FS_RES_INV_PARAM;
378     }
379 
380     if(rddir_p->drv->dir_close_cb == NULL) {
381         return LV_FS_RES_NOT_IMP;
382     }
383 
384     lv_fs_res_t res = rddir_p->drv->dir_close_cb(rddir_p->drv, rddir_p->dir_d);
385 
386     rddir_p->dir_d = NULL;
387     rddir_p->drv   = NULL;
388 
389     return res;
390 }
391 
lv_fs_drv_init(lv_fs_drv_t * drv)392 void lv_fs_drv_init(lv_fs_drv_t * drv)
393 {
394     lv_memset_00(drv, sizeof(lv_fs_drv_t));
395 }
396 
lv_fs_drv_register(lv_fs_drv_t * drv_p)397 void lv_fs_drv_register(lv_fs_drv_t * drv_p)
398 {
399     /*Save the new driver*/
400     lv_fs_drv_t ** new_drv;
401     new_drv = _lv_ll_ins_head(&LV_GC_ROOT(_lv_fsdrv_ll));
402     LV_ASSERT_MALLOC(new_drv);
403     if(new_drv == NULL) return;
404 
405     *new_drv = drv_p;
406 }
407 
lv_fs_get_drv(char letter)408 lv_fs_drv_t * lv_fs_get_drv(char letter)
409 {
410     lv_fs_drv_t ** drv;
411 
412     _LV_LL_READ(&LV_GC_ROOT(_lv_fsdrv_ll), drv) {
413         if((*drv)->letter == letter) {
414             return *drv;
415         }
416     }
417 
418     return NULL;
419 }
420 
lv_fs_get_letters(char * buf)421 char * lv_fs_get_letters(char * buf)
422 {
423     lv_fs_drv_t ** drv;
424     uint8_t i = 0;
425 
426     _LV_LL_READ(&LV_GC_ROOT(_lv_fsdrv_ll), drv) {
427         buf[i] = (*drv)->letter;
428         i++;
429     }
430 
431     buf[i] = '\0';
432 
433     return buf;
434 }
435 
lv_fs_get_ext(const char * fn)436 const char * lv_fs_get_ext(const char * fn)
437 {
438     size_t i;
439     for(i = strlen(fn); i > 0; i--) {
440         if(fn[i] == '.') {
441             return &fn[i + 1];
442         }
443         else if(fn[i] == '/' || fn[i] == '\\') {
444             return ""; /*No extension if a '\' or '/' found*/
445         }
446     }
447 
448     return ""; /*Empty string if no '.' in the file name.*/
449 }
450 
lv_fs_up(char * path)451 char * lv_fs_up(char * path)
452 {
453     size_t len = strlen(path);
454     if(len == 0) return path;
455 
456     len--; /*Go before the trailing '\0'*/
457 
458     /*Ignore trailing '/' or '\'*/
459     while(path[len] == '/' || path[len] == '\\') {
460         path[len] = '\0';
461         if(len > 0)
462             len--;
463         else
464             return path;
465     }
466 
467     size_t i;
468     for(i = len; i > 0; i--) {
469         if(path[i] == '/' || path[i] == '\\') break;
470     }
471 
472     if(i > 0) path[i] = '\0';
473 
474     return path;
475 }
476 
lv_fs_get_last(const char * path)477 const char * lv_fs_get_last(const char * path)
478 {
479     size_t len = strlen(path);
480     if(len == 0) return path;
481 
482     len--; /*Go before the trailing '\0'*/
483 
484     /*Ignore trailing '/' or '\'*/
485     while(path[len] == '/' || path[len] == '\\') {
486         if(len > 0)
487             len--;
488         else
489             return path;
490     }
491 
492     size_t i;
493     for(i = len; i > 0; i--) {
494         if(path[i] == '/' || path[i] == '\\') break;
495     }
496 
497     /*No '/' or '\' in the path so return with path itself*/
498     if(i == 0) return path;
499 
500     return &path[i + 1];
501 }
502 /**********************
503  *   STATIC FUNCTIONS
504  **********************/
505 
506 /**
507  * Skip the driver letter and the possible : after the letter
508  * @param path path string (E.g. S:/folder/file.txt)
509  * @return pointer to the beginning of the real path (E.g. /folder/file.txt)
510  */
lv_fs_get_real_path(const char * path)511 static const char * lv_fs_get_real_path(const char * path)
512 {
513     path++; /*Ignore the driver letter*/
514     if(*path == ':') path++;
515 
516     return path;
517 }
518