acid-drop- Hacking the planet from a LilyGo T-Deck using custom firmware |
git clone git://git.acid.vegas/acid-drop.git |
Log | Files | Refs | Archive | README | LICENSE |
lv_fs.c (14032B)
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 41 void _lv_fs_init(void) 42 { 43 _lv_ll_init(&LV_GC_ROOT(_lv_fsdrv_ll), sizeof(lv_fs_drv_t *)); 44 } 45 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 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 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 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 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 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 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 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 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 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 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 393 void lv_fs_drv_init(lv_fs_drv_t * drv) 394 { 395 lv_memset_00(drv, sizeof(lv_fs_drv_t)); 396 } 397 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 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 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 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 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 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 */ 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 }