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 file_p->cache->start = UINT32_MAX; /*Set an invalid range by default*/
99 file_p->cache->end = UINT32_MAX - 1;
100 }
101
102 return LV_FS_RES_OK;
103 }
104
lv_fs_close(lv_fs_file_t * file_p)105 lv_fs_res_t lv_fs_close(lv_fs_file_t * file_p)
106 {
107 if(file_p->drv == NULL) {
108 return LV_FS_RES_INV_PARAM;
109 }
110
111 if(file_p->drv->close_cb == NULL) {
112 return LV_FS_RES_NOT_IMP;
113 }
114
115 lv_fs_res_t res = file_p->drv->close_cb(file_p->drv, file_p->file_d);
116
117 if(file_p->drv->cache_size && file_p->cache) {
118 if(file_p->cache->buffer) {
119 lv_mem_free(file_p->cache->buffer);
120 }
121
122 lv_mem_free(file_p->cache);
123 }
124
125 file_p->file_d = NULL;
126 file_p->drv = NULL;
127 file_p->cache = NULL;
128
129 return res;
130 }
131
lv_fs_read_cached(lv_fs_file_t * file_p,char * buf,uint32_t btr,uint32_t * br)132 static lv_fs_res_t lv_fs_read_cached(lv_fs_file_t * file_p, char * buf, uint32_t btr, uint32_t * br)
133 {
134 lv_fs_res_t res = LV_FS_RES_OK;
135 uint32_t file_position = file_p->cache->file_position;
136 uint32_t start = file_p->cache->start;
137 uint32_t end = file_p->cache->end;
138 char * buffer = file_p->cache->buffer;
139 uint16_t buffer_size = file_p->drv->cache_size;
140
141 if(start <= file_position && file_position < end) {
142 /* Data can be read from cache buffer */
143 uint16_t buffer_offset = file_position - start;
144 uint32_t buffer_remaining_length = LV_MIN((uint32_t)buffer_size - buffer_offset, (uint32_t)end - file_position);
145
146 if(btr <= buffer_remaining_length) {
147 /*Data is in cache buffer, and buffer end not reached, no need to read from FS*/
148 lv_memcpy(buf, buffer + buffer_offset, btr);
149 *br = btr;
150 }
151 else {
152 /*First part of data is in cache buffer, but we need to read rest of data from FS*/
153 lv_memcpy(buf, buffer + buffer_offset, buffer_remaining_length);
154
155 uint32_t bytes_read_to_buffer = 0;
156 if(btr > buffer_size) {
157 /*If remaining data chuck is bigger than buffer size, then do not use cache, instead read it directly from FS*/
158 res = file_p->drv->read_cb(file_p->drv, file_p->file_d, (void *)(buf + buffer_remaining_length),
159 btr - buffer_remaining_length, &bytes_read_to_buffer);
160 }
161 else {
162 /*If remaining data chunk is smaller than buffer size, then 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;
165 file_p->cache->end = file_p->cache->start + bytes_read_to_buffer;
166
167 uint16_t data_chunk_remaining = LV_MIN(btr - buffer_remaining_length, bytes_read_to_buffer);
168 lv_memcpy(buf + buffer_remaining_length, buffer, data_chunk_remaining);
169 }
170 *br = LV_MIN(buffer_remaining_length + bytes_read_to_buffer, btr);
171 }
172 }
173 else {
174 /*Data is not in cache buffer*/
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 *br = LV_MIN(btr, bytes_read_to_buffer);
193 lv_memcpy(buf, buffer, *br);
194
195 }
196 }
197
198 if(res == LV_FS_RES_OK) {
199 file_p->cache->file_position += *br;
200 }
201
202 return res;
203 }
204
lv_fs_read(lv_fs_file_t * file_p,void * buf,uint32_t btr,uint32_t * br)205 lv_fs_res_t lv_fs_read(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br)
206 {
207 if(br != NULL) *br = 0;
208 if(file_p->drv == NULL) return LV_FS_RES_INV_PARAM;
209 if(file_p->drv->read_cb == NULL) return LV_FS_RES_NOT_IMP;
210
211 uint32_t br_tmp = 0;
212 lv_fs_res_t res;
213
214 if(file_p->drv->cache_size) {
215 res = lv_fs_read_cached(file_p, (char *)buf, btr, &br_tmp);
216 }
217 else {
218 res = file_p->drv->read_cb(file_p->drv, file_p->file_d, buf, btr, &br_tmp);
219 }
220
221 if(br != NULL) *br = br_tmp;
222
223 return res;
224 }
225
lv_fs_write(lv_fs_file_t * file_p,const void * buf,uint32_t btw,uint32_t * bw)226 lv_fs_res_t lv_fs_write(lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw)
227 {
228 if(bw != NULL) *bw = 0;
229
230 if(file_p->drv == NULL) {
231 return LV_FS_RES_INV_PARAM;
232 }
233
234 if(file_p->drv->write_cb == NULL) {
235 return LV_FS_RES_NOT_IMP;
236 }
237
238 uint32_t bw_tmp = 0;
239 lv_fs_res_t res = file_p->drv->write_cb(file_p->drv, file_p->file_d, buf, btw, &bw_tmp);
240 if(bw != NULL) *bw = bw_tmp;
241
242 return res;
243 }
244
lv_fs_seek(lv_fs_file_t * file_p,uint32_t pos,lv_fs_whence_t whence)245 lv_fs_res_t lv_fs_seek(lv_fs_file_t * file_p, uint32_t pos, lv_fs_whence_t whence)
246 {
247 if(file_p->drv == NULL) {
248 return LV_FS_RES_INV_PARAM;
249 }
250
251 if(file_p->drv->seek_cb == NULL) {
252 return LV_FS_RES_NOT_IMP;
253 }
254
255 lv_fs_res_t res = LV_FS_RES_OK;
256 if(file_p->drv->cache_size) {
257 switch(whence) {
258 case LV_FS_SEEK_SET: {
259 file_p->cache->file_position = pos;
260
261 /*FS seek if new position is outside cache buffer*/
262 if(file_p->cache->file_position < file_p->cache->start || file_p->cache->file_position > file_p->cache->end) {
263 res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position, LV_FS_SEEK_SET);
264 }
265
266 break;
267 }
268 case LV_FS_SEEK_CUR: {
269 file_p->cache->file_position += pos;
270
271 /*FS seek if new position is outside cache buffer*/
272 if(file_p->cache->file_position < file_p->cache->start || file_p->cache->file_position > file_p->cache->end) {
273 res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, file_p->cache->file_position, LV_FS_SEEK_SET);
274 }
275
276 break;
277 }
278 case LV_FS_SEEK_END: {
279 /*Because we don't know the file size, we do a little trick: do a FS seek, then get new file position from FS*/
280 res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, pos, whence);
281 if(res == LV_FS_RES_OK) {
282 uint32_t tmp_position;
283 res = file_p->drv->tell_cb(file_p->drv, file_p->file_d, &tmp_position);
284
285 if(res == LV_FS_RES_OK) {
286 file_p->cache->file_position = tmp_position;
287 }
288 }
289 break;
290 }
291 }
292 }
293 else {
294 res = file_p->drv->seek_cb(file_p->drv, file_p->file_d, pos, whence);
295 }
296
297 return res;
298 }
299
lv_fs_tell(lv_fs_file_t * file_p,uint32_t * pos)300 lv_fs_res_t lv_fs_tell(lv_fs_file_t * file_p, uint32_t * pos)
301 {
302 if(file_p->drv == NULL) {
303 *pos = 0;
304 return LV_FS_RES_INV_PARAM;
305 }
306
307 if(file_p->drv->tell_cb == NULL) {
308 *pos = 0;
309 return LV_FS_RES_NOT_IMP;
310 }
311
312 lv_fs_res_t res;
313 if(file_p->drv->cache_size) {
314 *pos = file_p->cache->file_position;
315 res = LV_FS_RES_OK;
316 }
317 else {
318 res = file_p->drv->tell_cb(file_p->drv, file_p->file_d, pos);
319 }
320
321 return res;
322 }
323
lv_fs_dir_open(lv_fs_dir_t * rddir_p,const char * path)324 lv_fs_res_t lv_fs_dir_open(lv_fs_dir_t * rddir_p, const char * path)
325 {
326 if(path == NULL) return LV_FS_RES_INV_PARAM;
327
328 char letter = path[0];
329 lv_fs_drv_t * drv = lv_fs_get_drv(letter);
330
331 if(drv == NULL) {
332 return LV_FS_RES_NOT_EX;
333 }
334
335 if(drv->ready_cb) {
336 if(drv->ready_cb(drv) == false) {
337 return LV_FS_RES_HW_ERR;
338 }
339 }
340
341 if(drv->dir_open_cb == NULL) {
342 return LV_FS_RES_NOT_IMP;
343 }
344
345 const char * real_path = lv_fs_get_real_path(path);
346 void * dir_d = drv->dir_open_cb(drv, real_path);
347
348 if(dir_d == NULL || dir_d == (void *)(-1)) {
349 return LV_FS_RES_UNKNOWN;
350 }
351
352 rddir_p->drv = drv;
353 rddir_p->dir_d = dir_d;
354
355 return LV_FS_RES_OK;
356 }
357
lv_fs_dir_read(lv_fs_dir_t * rddir_p,char * fn)358 lv_fs_res_t lv_fs_dir_read(lv_fs_dir_t * rddir_p, char * fn)
359 {
360 if(rddir_p->drv == NULL || rddir_p->dir_d == NULL) {
361 fn[0] = '\0';
362 return LV_FS_RES_INV_PARAM;
363 }
364
365 if(rddir_p->drv->dir_read_cb == NULL) {
366 fn[0] = '\0';
367 return LV_FS_RES_NOT_IMP;
368 }
369
370 lv_fs_res_t res = rddir_p->drv->dir_read_cb(rddir_p->drv, rddir_p->dir_d, fn);
371
372 return res;
373 }
374
lv_fs_dir_close(lv_fs_dir_t * rddir_p)375 lv_fs_res_t lv_fs_dir_close(lv_fs_dir_t * rddir_p)
376 {
377 if(rddir_p->drv == NULL || rddir_p->dir_d == NULL) {
378 return LV_FS_RES_INV_PARAM;
379 }
380
381 if(rddir_p->drv->dir_close_cb == NULL) {
382 return LV_FS_RES_NOT_IMP;
383 }
384
385 lv_fs_res_t res = rddir_p->drv->dir_close_cb(rddir_p->drv, rddir_p->dir_d);
386
387 rddir_p->dir_d = NULL;
388 rddir_p->drv = NULL;
389
390 return res;
391 }
392
lv_fs_drv_init(lv_fs_drv_t * drv)393 void lv_fs_drv_init(lv_fs_drv_t * drv)
394 {
395 lv_memset_00(drv, sizeof(lv_fs_drv_t));
396 }
397
lv_fs_drv_register(lv_fs_drv_t * drv_p)398 void lv_fs_drv_register(lv_fs_drv_t * drv_p)
399 {
400 /*Save the new driver*/
401 lv_fs_drv_t ** new_drv;
402 new_drv = _lv_ll_ins_head(&LV_GC_ROOT(_lv_fsdrv_ll));
403 LV_ASSERT_MALLOC(new_drv);
404 if(new_drv == NULL) return;
405
406 *new_drv = drv_p;
407 }
408
lv_fs_get_drv(char letter)409 lv_fs_drv_t * lv_fs_get_drv(char letter)
410 {
411 lv_fs_drv_t ** drv;
412
413 _LV_LL_READ(&LV_GC_ROOT(_lv_fsdrv_ll), drv) {
414 if((*drv)->letter == letter) {
415 return *drv;
416 }
417 }
418
419 return NULL;
420 }
421
lv_fs_get_letters(char * buf)422 char * lv_fs_get_letters(char * buf)
423 {
424 lv_fs_drv_t ** drv;
425 uint8_t i = 0;
426
427 _LV_LL_READ(&LV_GC_ROOT(_lv_fsdrv_ll), drv) {
428 buf[i] = (*drv)->letter;
429 i++;
430 }
431
432 buf[i] = '\0';
433
434 return buf;
435 }
436
lv_fs_get_ext(const char * fn)437 const char * lv_fs_get_ext(const char * fn)
438 {
439 size_t i;
440 for(i = strlen(fn); i > 0; i--) {
441 if(fn[i] == '.') {
442 return &fn[i + 1];
443 }
444 else if(fn[i] == '/' || fn[i] == '\\') {
445 return ""; /*No extension if a '\' or '/' found*/
446 }
447 }
448
449 return ""; /*Empty string if no '.' in the file name.*/
450 }
451
lv_fs_up(char * path)452 char * lv_fs_up(char * path)
453 {
454 size_t len = strlen(path);
455 if(len == 0) return path;
456
457 len--; /*Go before the trailing '\0'*/
458
459 /*Ignore trailing '/' or '\'*/
460 while(path[len] == '/' || path[len] == '\\') {
461 path[len] = '\0';
462 if(len > 0)
463 len--;
464 else
465 return path;
466 }
467
468 size_t i;
469 for(i = len; i > 0; i--) {
470 if(path[i] == '/' || path[i] == '\\') break;
471 }
472
473 if(i > 0) path[i] = '\0';
474
475 return path;
476 }
477
lv_fs_get_last(const char * path)478 const char * lv_fs_get_last(const char * path)
479 {
480 size_t len = strlen(path);
481 if(len == 0) return path;
482
483 len--; /*Go before the trailing '\0'*/
484
485 /*Ignore trailing '/' or '\'*/
486 while(path[len] == '/' || path[len] == '\\') {
487 if(len > 0)
488 len--;
489 else
490 return path;
491 }
492
493 size_t i;
494 for(i = len; i > 0; i--) {
495 if(path[i] == '/' || path[i] == '\\') break;
496 }
497
498 /*No '/' or '\' in the path so return with path itself*/
499 if(i == 0) return path;
500
501 return &path[i + 1];
502 }
503 /**********************
504 * STATIC FUNCTIONS
505 **********************/
506
507 /**
508 * Skip the driver letter and the possible : after the letter
509 * @param path path string (E.g. S:/folder/file.txt)
510 * @return pointer to the beginning of the real path (E.g. /folder/file.txt)
511 */
lv_fs_get_real_path(const char * path)512 static const char * lv_fs_get_real_path(const char * path)
513 {
514 path++; /*Ignore the driver letter*/
515 if(*path == ':') path++;
516
517 return path;
518 }
519