storage_ext.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  1. #include "fatfs.h"
  2. #include "../filesystem_api_internal.h"
  3. #include "storage_ext.h"
  4. #include <furi_hal.h>
  5. #include "sd_notify.h"
  6. #include <furi_hal_sd.h>
  7. typedef FIL SDFile;
  8. typedef DIR SDDir;
  9. typedef FILINFO SDFileInfo;
  10. typedef FRESULT SDError;
  11. #define TAG "StorageExt"
  12. /********************* Definitions ********************/
  13. typedef struct {
  14. FATFS* fs;
  15. const char* path;
  16. bool sd_was_present;
  17. } SDData;
  18. static FS_Error storage_ext_parse_error(SDError error);
  19. /******************* Core Functions *******************/
  20. static bool sd_mount_card(StorageData* storage, bool notify) {
  21. bool result = false;
  22. uint8_t counter = sd_max_mount_retry_count();
  23. uint8_t bsp_result;
  24. SDData* sd_data = storage->data;
  25. while(result == false && counter > 0 && hal_sd_detect()) {
  26. if(notify) {
  27. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  28. sd_notify_wait(notification);
  29. furi_record_close(RECORD_NOTIFICATION);
  30. }
  31. if((counter % 2) == 0) {
  32. // power reset sd card
  33. bsp_result = sd_init(true);
  34. } else {
  35. bsp_result = sd_init(false);
  36. }
  37. if(bsp_result) {
  38. // bsp error
  39. storage->status = StorageStatusErrorInternal;
  40. } else {
  41. SDError status = f_mount(sd_data->fs, sd_data->path, 1);
  42. if(status == FR_OK || status == FR_NO_FILESYSTEM) {
  43. #ifndef FURI_RAM_EXEC
  44. FATFS* fs;
  45. uint32_t free_clusters;
  46. status = f_getfree(sd_data->path, &free_clusters, &fs);
  47. #endif
  48. if(status == FR_OK || status == FR_NO_FILESYSTEM) {
  49. result = true;
  50. }
  51. if(status == FR_OK) {
  52. storage->status = StorageStatusOK;
  53. } else if(status == FR_NO_FILESYSTEM) {
  54. storage->status = StorageStatusNoFS;
  55. } else {
  56. storage->status = StorageStatusNotAccessible;
  57. }
  58. } else {
  59. storage->status = StorageStatusNotMounted;
  60. }
  61. }
  62. if(notify) {
  63. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  64. sd_notify_wait_off(notification);
  65. furi_record_close(RECORD_NOTIFICATION);
  66. }
  67. if(!result) {
  68. furi_delay_ms(1000);
  69. FURI_LOG_E(
  70. TAG, "init cycle %d, error: %s", counter, storage_data_status_text(storage));
  71. counter--;
  72. }
  73. }
  74. storage_data_timestamp(storage);
  75. return result;
  76. }
  77. FS_Error sd_unmount_card(StorageData* storage) {
  78. SDData* sd_data = storage->data;
  79. SDError error;
  80. storage->status = StorageStatusNotReady;
  81. error = FR_DISK_ERR;
  82. // TODO do i need to close the files?
  83. f_mount(0, sd_data->path, 0);
  84. return storage_ext_parse_error(error);
  85. }
  86. FS_Error sd_format_card(StorageData* storage) {
  87. #ifdef FURI_RAM_EXEC
  88. UNUSED(storage);
  89. return FSE_NOT_READY;
  90. #else
  91. uint8_t* work_area;
  92. SDData* sd_data = storage->data;
  93. SDError error;
  94. work_area = malloc(_MAX_SS);
  95. error = f_mkfs(sd_data->path, FM_ANY, 0, work_area, _MAX_SS);
  96. free(work_area);
  97. do {
  98. storage->status = StorageStatusNotAccessible;
  99. if(error != FR_OK) break;
  100. storage->status = StorageStatusNoFS;
  101. error = f_setlabel("Flipper SD");
  102. if(error != FR_OK) break;
  103. storage->status = StorageStatusNotMounted;
  104. error = f_mount(sd_data->fs, sd_data->path, 1);
  105. if(error != FR_OK) break;
  106. storage->status = StorageStatusOK;
  107. } while(false);
  108. return storage_ext_parse_error(error);
  109. #endif
  110. }
  111. FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) {
  112. #ifndef FURI_RAM_EXEC
  113. uint32_t free_clusters, free_sectors, total_sectors;
  114. FATFS* fs;
  115. #endif
  116. SDData* sd_data = storage->data;
  117. SDError error;
  118. // clean data
  119. memset(sd_info, 0, sizeof(SDInfo));
  120. // get fs info
  121. error = f_getlabel(sd_data->path, sd_info->label, NULL);
  122. if(error == FR_OK) {
  123. #ifndef FURI_RAM_EXEC
  124. error = f_getfree(sd_data->path, &free_clusters, &fs);
  125. #endif
  126. }
  127. if(error == FR_OK) {
  128. // calculate size
  129. #ifndef FURI_RAM_EXEC
  130. total_sectors = (fs->n_fatent - 2) * fs->csize;
  131. free_sectors = free_clusters * fs->csize;
  132. #endif
  133. uint16_t sector_size = _MAX_SS;
  134. #if _MAX_SS != _MIN_SS
  135. sector_size = fs->ssize;
  136. #endif
  137. #ifdef FURI_RAM_EXEC
  138. sd_info->fs_type = 0;
  139. sd_info->kb_total = 0;
  140. sd_info->kb_free = 0;
  141. sd_info->cluster_size = 512;
  142. sd_info->sector_size = sector_size;
  143. #else
  144. sd_info->fs_type = fs->fs_type;
  145. switch(fs->fs_type) {
  146. case FS_FAT12:
  147. sd_info->fs_type = FST_FAT12;
  148. break;
  149. case FS_FAT16:
  150. sd_info->fs_type = FST_FAT16;
  151. break;
  152. case FS_FAT32:
  153. sd_info->fs_type = FST_FAT32;
  154. break;
  155. case FS_EXFAT:
  156. sd_info->fs_type = FST_EXFAT;
  157. break;
  158. default:
  159. sd_info->fs_type = FST_UNKNOWN;
  160. break;
  161. }
  162. sd_info->kb_total = total_sectors / 1024 * sector_size;
  163. sd_info->kb_free = free_sectors / 1024 * sector_size;
  164. sd_info->cluster_size = fs->csize;
  165. sd_info->sector_size = sector_size;
  166. #endif
  167. }
  168. SD_CID cid;
  169. SdSpiStatus status = sd_get_cid(&cid);
  170. if(status == SdSpiStatusOK) {
  171. sd_info->manufacturer_id = cid.ManufacturerID;
  172. memcpy(sd_info->oem_id, cid.OEM_AppliID, sizeof(cid.OEM_AppliID));
  173. memcpy(sd_info->product_name, cid.ProdName, sizeof(cid.ProdName));
  174. sd_info->product_revision_major = cid.ProdRev >> 4;
  175. sd_info->product_revision_minor = cid.ProdRev & 0x0F;
  176. sd_info->product_serial_number = cid.ProdSN;
  177. sd_info->manufacturing_year = 2000 + cid.ManufactYear;
  178. sd_info->manufacturing_month = cid.ManufactMonth;
  179. }
  180. return storage_ext_parse_error(error);
  181. }
  182. static void storage_ext_tick_internal(StorageData* storage, bool notify) {
  183. SDData* sd_data = storage->data;
  184. if(sd_data->sd_was_present) {
  185. if(hal_sd_detect()) {
  186. FURI_LOG_I(TAG, "card detected");
  187. sd_mount_card(storage, notify);
  188. if(storage->status != StorageStatusOK) {
  189. FURI_LOG_E(TAG, "sd init error: %s", storage_data_status_text(storage));
  190. if(notify) {
  191. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  192. sd_notify_error(notification);
  193. furi_record_close(RECORD_NOTIFICATION);
  194. }
  195. } else {
  196. FURI_LOG_I(TAG, "card mounted");
  197. if(notify) {
  198. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  199. sd_notify_success(notification);
  200. furi_record_close(RECORD_NOTIFICATION);
  201. }
  202. }
  203. sd_data->sd_was_present = false;
  204. if(!hal_sd_detect()) {
  205. FURI_LOG_I(TAG, "card removed while mounting");
  206. sd_unmount_card(storage);
  207. sd_data->sd_was_present = true;
  208. }
  209. }
  210. } else {
  211. if(!hal_sd_detect()) {
  212. FURI_LOG_I(TAG, "card removed");
  213. sd_data->sd_was_present = true;
  214. sd_unmount_card(storage);
  215. if(notify) {
  216. NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
  217. sd_notify_eject(notification);
  218. furi_record_close(RECORD_NOTIFICATION);
  219. }
  220. }
  221. }
  222. }
  223. static void storage_ext_tick(StorageData* storage) {
  224. storage_ext_tick_internal(storage, true);
  225. }
  226. /****************** Common Functions ******************/
  227. static FS_Error storage_ext_parse_error(SDError error) {
  228. FS_Error result;
  229. switch(error) {
  230. case FR_OK:
  231. result = FSE_OK;
  232. break;
  233. case FR_NOT_READY:
  234. result = FSE_NOT_READY;
  235. break;
  236. case FR_NO_FILE:
  237. case FR_NO_PATH:
  238. case FR_NO_FILESYSTEM:
  239. result = FSE_NOT_EXIST;
  240. break;
  241. case FR_EXIST:
  242. result = FSE_EXIST;
  243. break;
  244. case FR_INVALID_NAME:
  245. result = FSE_INVALID_NAME;
  246. break;
  247. case FR_INVALID_OBJECT:
  248. case FR_INVALID_PARAMETER:
  249. result = FSE_INVALID_PARAMETER;
  250. break;
  251. case FR_DENIED:
  252. result = FSE_DENIED;
  253. break;
  254. default:
  255. result = FSE_INTERNAL;
  256. break;
  257. }
  258. return result;
  259. }
  260. /******************* File Functions *******************/
  261. static bool storage_ext_file_open(
  262. void* ctx,
  263. File* file,
  264. const char* path,
  265. FS_AccessMode access_mode,
  266. FS_OpenMode open_mode) {
  267. StorageData* storage = ctx;
  268. uint8_t _mode = 0;
  269. if(access_mode & FSAM_READ) _mode |= FA_READ;
  270. if(access_mode & FSAM_WRITE) _mode |= FA_WRITE;
  271. if(open_mode & FSOM_OPEN_EXISTING) _mode |= FA_OPEN_EXISTING;
  272. if(open_mode & FSOM_OPEN_ALWAYS) _mode |= FA_OPEN_ALWAYS;
  273. if(open_mode & FSOM_OPEN_APPEND) _mode |= FA_OPEN_APPEND;
  274. if(open_mode & FSOM_CREATE_NEW) _mode |= FA_CREATE_NEW;
  275. if(open_mode & FSOM_CREATE_ALWAYS) _mode |= FA_CREATE_ALWAYS;
  276. SDFile* file_data = malloc(sizeof(SDFile));
  277. storage_set_storage_file_data(file, file_data, storage);
  278. file->internal_error_id = f_open(file_data, path, _mode);
  279. file->error_id = storage_ext_parse_error(file->internal_error_id);
  280. return (file->error_id == FSE_OK);
  281. }
  282. static bool storage_ext_file_close(void* ctx, File* file) {
  283. StorageData* storage = ctx;
  284. SDFile* file_data = storage_get_storage_file_data(file, storage);
  285. file->internal_error_id = f_close(file_data);
  286. file->error_id = storage_ext_parse_error(file->internal_error_id);
  287. free(file_data);
  288. return (file->error_id == FSE_OK);
  289. }
  290. static uint16_t
  291. storage_ext_file_read(void* ctx, File* file, void* buff, uint16_t const bytes_to_read) {
  292. StorageData* storage = ctx;
  293. SDFile* file_data = storage_get_storage_file_data(file, storage);
  294. uint16_t bytes_read = 0;
  295. file->internal_error_id = f_read(file_data, buff, bytes_to_read, &bytes_read);
  296. file->error_id = storage_ext_parse_error(file->internal_error_id);
  297. return bytes_read;
  298. }
  299. static uint16_t
  300. storage_ext_file_write(void* ctx, File* file, const void* buff, uint16_t const bytes_to_write) {
  301. #ifdef FURI_RAM_EXEC
  302. UNUSED(ctx);
  303. UNUSED(file);
  304. UNUSED(buff);
  305. UNUSED(bytes_to_write);
  306. return FSE_NOT_READY;
  307. #else
  308. StorageData* storage = ctx;
  309. SDFile* file_data = storage_get_storage_file_data(file, storage);
  310. uint16_t bytes_written = 0;
  311. file->internal_error_id = f_write(file_data, buff, bytes_to_write, &bytes_written);
  312. file->error_id = storage_ext_parse_error(file->internal_error_id);
  313. return bytes_written;
  314. #endif
  315. }
  316. static bool
  317. storage_ext_file_seek(void* ctx, File* file, const uint32_t offset, const bool from_start) {
  318. StorageData* storage = ctx;
  319. SDFile* file_data = storage_get_storage_file_data(file, storage);
  320. if(from_start) {
  321. file->internal_error_id = f_lseek(file_data, offset);
  322. } else {
  323. uint64_t position = f_tell(file_data);
  324. position += offset;
  325. file->internal_error_id = f_lseek(file_data, position);
  326. }
  327. file->error_id = storage_ext_parse_error(file->internal_error_id);
  328. return (file->error_id == FSE_OK);
  329. }
  330. static uint64_t storage_ext_file_tell(void* ctx, File* file) {
  331. StorageData* storage = ctx;
  332. SDFile* file_data = storage_get_storage_file_data(file, storage);
  333. uint64_t position = 0;
  334. position = f_tell(file_data);
  335. file->error_id = FSE_OK;
  336. return position;
  337. }
  338. static bool storage_ext_file_truncate(void* ctx, File* file) {
  339. #ifdef FURI_RAM_EXEC
  340. UNUSED(ctx);
  341. UNUSED(file);
  342. return FSE_NOT_READY;
  343. #else
  344. StorageData* storage = ctx;
  345. SDFile* file_data = storage_get_storage_file_data(file, storage);
  346. file->internal_error_id = f_truncate(file_data);
  347. file->error_id = storage_ext_parse_error(file->internal_error_id);
  348. return (file->error_id == FSE_OK);
  349. #endif
  350. }
  351. static bool storage_ext_file_sync(void* ctx, File* file) {
  352. #ifdef FURI_RAM_EXEC
  353. UNUSED(ctx);
  354. UNUSED(file);
  355. return FSE_NOT_READY;
  356. #else
  357. StorageData* storage = ctx;
  358. SDFile* file_data = storage_get_storage_file_data(file, storage);
  359. file->internal_error_id = f_sync(file_data);
  360. file->error_id = storage_ext_parse_error(file->internal_error_id);
  361. return (file->error_id == FSE_OK);
  362. #endif
  363. }
  364. static uint64_t storage_ext_file_size(void* ctx, File* file) {
  365. StorageData* storage = ctx;
  366. SDFile* file_data = storage_get_storage_file_data(file, storage);
  367. uint64_t size = 0;
  368. size = f_size(file_data);
  369. file->error_id = FSE_OK;
  370. return size;
  371. }
  372. static bool storage_ext_file_eof(void* ctx, File* file) {
  373. StorageData* storage = ctx;
  374. SDFile* file_data = storage_get_storage_file_data(file, storage);
  375. bool eof = f_eof(file_data);
  376. file->internal_error_id = 0;
  377. file->error_id = FSE_OK;
  378. return eof;
  379. }
  380. /******************* Dir Functions *******************/
  381. static bool storage_ext_dir_open(void* ctx, File* file, const char* path) {
  382. StorageData* storage = ctx;
  383. SDDir* file_data = malloc(sizeof(SDDir));
  384. storage_set_storage_file_data(file, file_data, storage);
  385. file->internal_error_id = f_opendir(file_data, path);
  386. file->error_id = storage_ext_parse_error(file->internal_error_id);
  387. return (file->error_id == FSE_OK);
  388. }
  389. static bool storage_ext_dir_close(void* ctx, File* file) {
  390. StorageData* storage = ctx;
  391. SDDir* file_data = storage_get_storage_file_data(file, storage);
  392. file->internal_error_id = f_closedir(file_data);
  393. file->error_id = storage_ext_parse_error(file->internal_error_id);
  394. free(file_data);
  395. return (file->error_id == FSE_OK);
  396. }
  397. static bool storage_ext_dir_read(
  398. void* ctx,
  399. File* file,
  400. FileInfo* fileinfo,
  401. char* name,
  402. const uint16_t name_length) {
  403. StorageData* storage = ctx;
  404. SDDir* file_data = storage_get_storage_file_data(file, storage);
  405. SDFileInfo _fileinfo;
  406. file->internal_error_id = f_readdir(file_data, &_fileinfo);
  407. file->error_id = storage_ext_parse_error(file->internal_error_id);
  408. if(fileinfo != NULL) {
  409. fileinfo->size = _fileinfo.fsize;
  410. fileinfo->flags = 0;
  411. if(_fileinfo.fattrib & AM_DIR) fileinfo->flags |= FSF_DIRECTORY;
  412. }
  413. if(name != NULL) {
  414. snprintf(name, name_length, "%s", _fileinfo.fname);
  415. }
  416. if(_fileinfo.fname[0] == 0) {
  417. file->error_id = FSE_NOT_EXIST;
  418. }
  419. return (file->error_id == FSE_OK);
  420. }
  421. static bool storage_ext_dir_rewind(void* ctx, File* file) {
  422. StorageData* storage = ctx;
  423. SDDir* file_data = storage_get_storage_file_data(file, storage);
  424. file->internal_error_id = f_readdir(file_data, NULL);
  425. file->error_id = storage_ext_parse_error(file->internal_error_id);
  426. return (file->error_id == FSE_OK);
  427. }
  428. /******************* Common FS Functions *******************/
  429. static FS_Error storage_ext_common_stat(void* ctx, const char* path, FileInfo* fileinfo) {
  430. UNUSED(ctx);
  431. SDFileInfo _fileinfo;
  432. SDError result = f_stat(path, &_fileinfo);
  433. if(fileinfo != NULL) {
  434. fileinfo->size = _fileinfo.fsize;
  435. fileinfo->flags = 0;
  436. if(_fileinfo.fattrib & AM_DIR) fileinfo->flags |= FSF_DIRECTORY;
  437. }
  438. return storage_ext_parse_error(result);
  439. }
  440. static FS_Error storage_ext_common_remove(void* ctx, const char* path) {
  441. UNUSED(ctx);
  442. #ifdef FURI_RAM_EXEC
  443. UNUSED(path);
  444. return FSE_NOT_READY;
  445. #else
  446. SDError result = f_unlink(path);
  447. return storage_ext_parse_error(result);
  448. #endif
  449. }
  450. static FS_Error storage_ext_common_mkdir(void* ctx, const char* path) {
  451. UNUSED(ctx);
  452. #ifdef FURI_RAM_EXEC
  453. UNUSED(path);
  454. return FSE_NOT_READY;
  455. #else
  456. SDError result = f_mkdir(path);
  457. return storage_ext_parse_error(result);
  458. #endif
  459. }
  460. static FS_Error storage_ext_common_fs_info(
  461. void* ctx,
  462. const char* fs_path,
  463. uint64_t* total_space,
  464. uint64_t* free_space) {
  465. UNUSED(fs_path);
  466. #ifdef FURI_RAM_EXEC
  467. UNUSED(ctx);
  468. UNUSED(total_space);
  469. UNUSED(free_space);
  470. return FSE_NOT_READY;
  471. #else
  472. StorageData* storage = ctx;
  473. SDData* sd_data = storage->data;
  474. DWORD free_clusters;
  475. FATFS* fs;
  476. SDError fresult = f_getfree(sd_data->path, &free_clusters, &fs);
  477. if((FRESULT)fresult == FR_OK) {
  478. uint32_t total_sectors = (fs->n_fatent - 2) * fs->csize;
  479. uint32_t free_sectors = free_clusters * fs->csize;
  480. uint16_t sector_size = _MAX_SS;
  481. #if _MAX_SS != _MIN_SS
  482. sector_size = fs->ssize;
  483. #endif
  484. if(total_space != NULL) {
  485. *total_space = (uint64_t)total_sectors * (uint64_t)sector_size;
  486. }
  487. if(free_space != NULL) {
  488. *free_space = (uint64_t)free_sectors * (uint64_t)sector_size;
  489. }
  490. }
  491. return storage_ext_parse_error(fresult);
  492. #endif
  493. }
  494. /******************* Init Storage *******************/
  495. static const FS_Api fs_api = {
  496. .file =
  497. {
  498. .open = storage_ext_file_open,
  499. .close = storage_ext_file_close,
  500. .read = storage_ext_file_read,
  501. .write = storage_ext_file_write,
  502. .seek = storage_ext_file_seek,
  503. .tell = storage_ext_file_tell,
  504. .truncate = storage_ext_file_truncate,
  505. .size = storage_ext_file_size,
  506. .sync = storage_ext_file_sync,
  507. .eof = storage_ext_file_eof,
  508. },
  509. .dir =
  510. {
  511. .open = storage_ext_dir_open,
  512. .close = storage_ext_dir_close,
  513. .read = storage_ext_dir_read,
  514. .rewind = storage_ext_dir_rewind,
  515. },
  516. .common =
  517. {
  518. .stat = storage_ext_common_stat,
  519. .mkdir = storage_ext_common_mkdir,
  520. .remove = storage_ext_common_remove,
  521. .fs_info = storage_ext_common_fs_info,
  522. },
  523. };
  524. void storage_ext_init(StorageData* storage) {
  525. fatfs_init();
  526. SDData* sd_data = malloc(sizeof(SDData));
  527. sd_data->fs = &fatfs_object;
  528. sd_data->path = "0:/";
  529. sd_data->sd_was_present = true;
  530. storage->data = sd_data;
  531. storage->api.tick = storage_ext_tick;
  532. storage->fs_api = &fs_api;
  533. hal_sd_detect_init();
  534. // do not notify on first launch, notifications app is waiting for our thread to read settings
  535. storage_ext_tick_internal(storage, false);
  536. }