[FL-520] Filesystem Api and App (#280)
* update fatfs integer types * fix sector size to 512 * fix sector size calculation * common fs api * fs api realization (sd card + fat fs) * better sector size definition * more api realization fns * add error description api, add common api * fix api flag naming, run app * add fs_info call * disable fatfs strfuncs, enable fatfs chmod * rework filesystem app * sd detect cycle, sd menu, sd eject feature * fix sd detect cycle * sd card format routine * ui improvements, sd info routine * properly unmount card * separate mode flags * add api folder, move app, rename app * fix api naming * update st-card-test to use api * update path to app * fixed potential problem of using sizeof union * updated api documentation, new time/date fns * update codeowners * changed app requirements * changed app order * sd insert/remove log
This commit is contained in:
		
							parent
							
								
									6928122650
								
							
						
					
					
						commit
						f94633863c
					
				
							
								
								
									
										11
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							| @ -43,6 +43,8 @@ applications/gui/** @skotopes | ||||
| # iButton | ||||
| 
 | ||||
| applications/ibutton/** @DrZlo13 | ||||
| lib/cyfral/** @DrZlo13 | ||||
| lib/onewire/** @DrZlo13 | ||||
| 
 | ||||
| # IR | ||||
| 
 | ||||
| @ -61,9 +63,12 @@ applications/menu/** @skotopes | ||||
| applications/nfc/** @skotopes | ||||
| lib/ST25RFAL002/** @skotopes | ||||
| 
 | ||||
| # SD Card | ||||
| # SD Card and filesystem | ||||
| 
 | ||||
| applications/sd-card-test/** @DrZlo13 | ||||
| applications/sd-filesystem/** @DrZlo13 | ||||
| lib/common-api/filesystem-api.h @DrZlo13 | ||||
| lib/fatfs/** @DrZlo13 | ||||
| 
 | ||||
| # Power control app | ||||
| 
 | ||||
| @ -74,3 +79,7 @@ applications/power/** @skotopes | ||||
| applications/music-player/** @DrZlo13 | ||||
| applications/floopper-bloopper/** @glitchcore | ||||
| applications/gpio-tester/** @glitchcore | ||||
| 
 | ||||
| lib/app-template/** @DrZlo13 | ||||
| lib/qrcode/** @DrZlo13 | ||||
| lib/callback-connector/** @DrZlo13 | ||||
| @ -41,6 +41,7 @@ void cli_task(void* p); | ||||
| void music_player(void* p); | ||||
| void sdnfc(void* p); | ||||
| void floopper_bloopper(void* p); | ||||
| void sd_filesystem(void* p); | ||||
| 
 | ||||
| const FlipperStartupApp FLIPPER_STARTUP[] = { | ||||
| #ifdef APP_DISPLAY | ||||
| @ -88,6 +89,13 @@ const FlipperStartupApp FLIPPER_STARTUP[] = { | ||||
|      .icon = A_Plugins_14}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_SD_FILESYSTEM | ||||
|     {.app = sd_filesystem, | ||||
|      .name = "sd_filesystem", | ||||
|      .libs = {1, FURI_LIB{"menu_task"}}, | ||||
|      .icon = A_Plugins_14}, | ||||
| #endif | ||||
| 
 | ||||
| #ifdef APP_DOLPHIN | ||||
|     {.app = dolphin_task, | ||||
|      .name = "dolphin_task", | ||||
| @ -165,7 +173,7 @@ const FlipperStartupApp FLIPPER_STARTUP[] = { | ||||
| #ifdef APP_SD_TEST | ||||
|     {.app = sd_card_test, | ||||
|      .name = "sd_card_test", | ||||
|      .libs = {1, FURI_LIB{"gui_task"}}, | ||||
|      .libs = {2, FURI_LIB{"gui_task", "sd_filesystem"}}, | ||||
|      .icon = A_Plugins_14}, | ||||
| #endif | ||||
| 
 | ||||
| @ -257,7 +265,7 @@ const FlipperStartupApp FLIPPER_PLUGINS[] = { | ||||
| #ifdef BUILD_SD_TEST | ||||
|     {.app = sd_card_test, | ||||
|      .name = "sd_card_test", | ||||
|      .libs = {1, FURI_LIB{"gui_task"}}, | ||||
|      .libs = {2, FURI_LIB{"gui_task", "sd_filesystem"}}, | ||||
|      .icon = A_Plugins_14}, | ||||
| #endif | ||||
| 
 | ||||
|  | ||||
| @ -13,6 +13,7 @@ APP_NFC  = 1 | ||||
| APP_POWER = 1 | ||||
| APP_BT = 1 | ||||
| APP_CLI = 1 | ||||
| APP_SD_FILESYSTEM = 1 | ||||
| BUILD_IRDA  = 1 | ||||
| APP_DOLPHIN = 1 | ||||
| BUILD_EXAMPLE_BLINK = 1 | ||||
| @ -220,6 +221,7 @@ CFLAGS		+= -DBUILD_SD_TEST | ||||
| CPP_SOURCES	+= $(wildcard $(APP_DIR)/sd-card-test/*.cpp) | ||||
| APP_INPUT = 1 | ||||
| APP_GUI = 1 | ||||
| APP_SD_FILESYSTEM = 1 | ||||
| endif | ||||
| 
 | ||||
| APP_SPEAKER_DEMO ?= 0 | ||||
| @ -305,6 +307,12 @@ C_SOURCES	+= $(wildcard $(APP_DIR)/gui/*.c) | ||||
| C_SOURCES	+= $(wildcard $(APP_DIR)/backlight-control/*.c) | ||||
| endif | ||||
| 
 | ||||
| APP_SD_FILESYSTEM	?= 0 | ||||
| ifeq ($(APP_SD_FILESYSTEM), 1) | ||||
| CFLAGS		+= -DAPP_SD_FILESYSTEM | ||||
| C_SOURCES	+= $(wildcard $(APP_DIR)/sd-filesystem/*.c) | ||||
| endif | ||||
| 
 | ||||
| # deprecated
 | ||||
| ifeq ($(APP_DISPLAY), 1) | ||||
| CFLAGS		+= -DAPP_DISPLAY | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| #include "app-template.h" | ||||
| #include "fatfs/ff.h" | ||||
| #include "stm32_adafruit_sd.h" | ||||
| #include "fnv1a-hash.h" | ||||
| #include "filesystem-api.h" | ||||
| 
 | ||||
| // event enumeration type
 | ||||
| typedef uint8_t event_t; | ||||
| @ -43,17 +43,15 @@ public: | ||||
|     // vars
 | ||||
|     GpioPin* red_led_record; | ||||
|     GpioPin* green_led_record; | ||||
|     FATFS sd_fat_fs; | ||||
|     char sd_path[6]; | ||||
|     const uint32_t benchmark_data_size = 4096; | ||||
|     uint8_t* benchmark_data; | ||||
|     FS_Api* fs_api; | ||||
| 
 | ||||
|     // funcs
 | ||||
|     void run(); | ||||
|     void render(Canvas* canvas); | ||||
|     template <class T> void set_text(std::initializer_list<T> list); | ||||
|     template <class T> void set_error(std::initializer_list<T> list); | ||||
|     const char* fatfs_error_desc(FRESULT res); | ||||
|     void wait_for_button(Input input_button); | ||||
|     bool ask(Input input_button_cancel, Input input_button_ok); | ||||
|     void blink_red(); | ||||
| @ -63,11 +61,6 @@ public: | ||||
|     // "tests"
 | ||||
|     void detect_sd_card(); | ||||
|     void show_warning(); | ||||
|     void init_sd_card(); | ||||
|     bool is_sd_card_formatted(); | ||||
|     void ask_and_format_sd_card(); | ||||
|     void mount_sd_card(); | ||||
|     void format_sd_card(); | ||||
|     void get_sd_card_info(); | ||||
| 
 | ||||
|     void prepare_benchmark_data(); | ||||
| @ -76,7 +69,7 @@ public: | ||||
|     uint32_t write_benchmark_internal(const uint32_t size, const uint32_t tcount); | ||||
| 
 | ||||
|     void read_benchmark(); | ||||
|     uint32_t read_benchmark_internal(const uint32_t size, const uint32_t count, FIL* file); | ||||
|     uint32_t read_benchmark_internal(const uint32_t size, const uint32_t count, File* file); | ||||
| 
 | ||||
|     void hash_benchmark(); | ||||
| }; | ||||
| @ -97,16 +90,17 @@ void SdTest::run() { | ||||
| 
 | ||||
|     app_ready(); | ||||
| 
 | ||||
|     detect_sd_card(); | ||||
|     show_warning(); | ||||
|     init_sd_card(); | ||||
|     if(!is_sd_card_formatted()) { | ||||
|         format_sd_card(); | ||||
|     } else { | ||||
|         ask_and_format_sd_card(); | ||||
|     fs_api = static_cast<FS_Api*>(furi_open("sdcard")); | ||||
| 
 | ||||
|     if(fs_api == NULL) { | ||||
|         set_error({"cannot get sdcard api"}); | ||||
|         exit(); | ||||
|     } | ||||
|     mount_sd_card(); | ||||
| 
 | ||||
|     detect_sd_card(); | ||||
|     get_sd_card_info(); | ||||
|     show_warning(); | ||||
| 
 | ||||
|     prepare_benchmark_data(); | ||||
|     write_benchmark(); | ||||
|     read_benchmark(); | ||||
| @ -134,7 +128,7 @@ void SdTest::detect_sd_card() { | ||||
|     uint8_t i = 0; | ||||
| 
 | ||||
|     // detect sd card pin
 | ||||
|     while(!hal_gpio_read_sd_detect()) { | ||||
|     while(fs_api->common.get_fs_info(NULL, NULL) == FSE_NOT_READY) { | ||||
|         delay(100); | ||||
| 
 | ||||
|         snprintf(str_buffer, str_buffer_size, "Waiting%s", dots[i]); | ||||
| @ -155,7 +149,7 @@ void SdTest::show_warning() { | ||||
|     set_text( | ||||
|         {"!!Warning!!", | ||||
|          "during the tests", | ||||
|          "card may be formatted", | ||||
|          "files can be overwritten", | ||||
|          "or data on card may be lost", | ||||
|          "", | ||||
|          "press UP DOWN OK to continue"}); | ||||
| @ -165,96 +159,22 @@ void SdTest::show_warning() { | ||||
|     wait_for_button(InputOk); | ||||
| } | ||||
| 
 | ||||
| // init low level driver
 | ||||
| void SdTest::init_sd_card() { | ||||
|     uint8_t bsp_result = BSP_SD_Init(); | ||||
| 
 | ||||
|     // BSP_SD_OK = 0
 | ||||
|     if(bsp_result) { | ||||
|         set_error({"SD card init error", "BSP error"}); | ||||
|     } | ||||
|     blink_green(); | ||||
| } | ||||
| 
 | ||||
| // test, if sd card need to be formatted
 | ||||
| bool SdTest::is_sd_card_formatted() { | ||||
|     FRESULT result; | ||||
|     set_text({"checking if card needs to be formatted"}); | ||||
| 
 | ||||
|     result = f_mount(&sd_fat_fs, sd_path, 1); | ||||
|     if(result == FR_NO_FILESYSTEM) { | ||||
|         return false; | ||||
|     } else { | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void SdTest::ask_and_format_sd_card() { | ||||
|     set_text({"Want to format sd card?", "", "", "", "", "LEFT to CANCEL | RIGHT to OK"}); | ||||
|     if(ask(InputLeft, InputRight)) { | ||||
|         format_sd_card(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // mount sd card
 | ||||
| void SdTest::mount_sd_card() { | ||||
|     FRESULT result; | ||||
|     set_text({"mounting sdcard"}); | ||||
| 
 | ||||
|     result = f_mount(&sd_fat_fs, sd_path, 1); | ||||
|     if(result) { | ||||
|         set_error({"SD card mount error", fatfs_error_desc(result)}); | ||||
|     } | ||||
|     blink_green(); | ||||
| } | ||||
| 
 | ||||
| // format sd card
 | ||||
| void SdTest::format_sd_card() { | ||||
|     FRESULT result; | ||||
|     BYTE* work_area; | ||||
| 
 | ||||
|     set_text({"formatting sdcard", "procedure can be lengthy", "please wait"}); | ||||
|     delay(100); | ||||
| 
 | ||||
|     work_area = static_cast<BYTE*>(malloc(_MAX_SS)); | ||||
|     if(work_area == NULL) { | ||||
|         set_error({"SD card format error", "cannot allocate memory"}); | ||||
|     } | ||||
| 
 | ||||
|     result = f_mkfs(sd_path, (FM_FAT | FM_FAT32 | FM_EXFAT), 0, work_area, _MAX_SS); | ||||
|     free(work_area); | ||||
| 
 | ||||
|     if(result) { | ||||
|         set_error({"SD card format error", fatfs_error_desc(result)}); | ||||
|     } | ||||
| 
 | ||||
|     result = f_setlabel("Flipper SD"); | ||||
|     if(result) { | ||||
|         set_error({"SD card set label error", fatfs_error_desc(result)}); | ||||
|     } | ||||
|     blink_green(); | ||||
| } | ||||
| 
 | ||||
| // get info about sd card, label, sn
 | ||||
| // sector, cluster, total and free size
 | ||||
| void SdTest::get_sd_card_info() { | ||||
|     const uint8_t str_buffer_size = 26; | ||||
|     char str_buffer[4][str_buffer_size]; | ||||
|     char volume_label[128]; | ||||
|     DWORD serial_num; | ||||
|     FRESULT result; | ||||
|     FATFS* fs; | ||||
|     DWORD free_clusters, free_sectors, total_sectors; | ||||
| 
 | ||||
|     // suppress "'%s' directive output may be truncated" warning about snprintf
 | ||||
|     char str_buffer[2][str_buffer_size]; | ||||
|     FS_Error result; | ||||
|     uint64_t bytes_total, bytes_free; | ||||
|     int __attribute__((unused)) snprintf_count = 0; | ||||
| 
 | ||||
|     // get label and s/n
 | ||||
|     result = f_getlabel(sd_path, volume_label, &serial_num); | ||||
|     if(result) set_error({"f_getlabel error", fatfs_error_desc(result)}); | ||||
|     result = fs_api->common.get_fs_info(&bytes_total, &bytes_free); | ||||
|     if(result != FSE_OK) set_error({"get_fs_info error", fs_api->error.get_desc(result)}); | ||||
| 
 | ||||
|     snprintf_count = snprintf(str_buffer[0], str_buffer_size, "Label: %s", volume_label); | ||||
|     snprintf(str_buffer[1], str_buffer_size, "S/N: %lu", serial_num); | ||||
|     snprintf( | ||||
|         str_buffer[0], str_buffer_size, "%lu KB total", static_cast<uint32_t>(bytes_total / 1024)); | ||||
|     snprintf( | ||||
|         str_buffer[1], str_buffer_size, "%lu KB free", static_cast<uint32_t>(bytes_free / 1024)); | ||||
| 
 | ||||
|     set_text( | ||||
|         {static_cast<const char*>(str_buffer[0]), | ||||
| @ -267,30 +187,6 @@ void SdTest::get_sd_card_info() { | ||||
|     blink_green(); | ||||
| 
 | ||||
|     wait_for_button(InputOk); | ||||
| 
 | ||||
|     // get total and free space
 | ||||
|     result = f_getfree(sd_path, &free_clusters, &fs); | ||||
|     if(result) set_error({"f_getfree error", fatfs_error_desc(result)}); | ||||
| 
 | ||||
|     total_sectors = (fs->n_fatent - 2) * fs->csize; | ||||
|     free_sectors = free_clusters * fs->csize; | ||||
| 
 | ||||
|     snprintf(str_buffer[0], str_buffer_size, "Cluster: %d sectors", fs->csize); | ||||
|     snprintf(str_buffer[1], str_buffer_size, "Sector: %d bytes", fs->ssize); | ||||
|     snprintf(str_buffer[2], str_buffer_size, "%lu KB total", total_sectors / 1024 * fs->ssize); | ||||
|     snprintf(str_buffer[3], str_buffer_size, "%lu KB free", free_sectors / 1024 * fs->ssize); | ||||
| 
 | ||||
|     set_text( | ||||
|         {static_cast<const char*>(str_buffer[0]), | ||||
|          static_cast<const char*>(str_buffer[1]), | ||||
|          static_cast<const char*>(str_buffer[2]), | ||||
|          static_cast<const char*>(str_buffer[3]), | ||||
|          "", | ||||
|          "press OK to continue"}); | ||||
| 
 | ||||
|     blink_green(); | ||||
| 
 | ||||
|     wait_for_button(InputOk); | ||||
| } | ||||
| 
 | ||||
| // prepare benchmark data (allocate data in ram)
 | ||||
| @ -375,30 +271,27 @@ void SdTest::write_benchmark() { | ||||
| 
 | ||||
| uint32_t SdTest::write_benchmark_internal(const uint32_t size, const uint32_t count) { | ||||
|     uint32_t start_tick, stop_tick, benchmark_bps, benchmark_time, bytes_written; | ||||
|     FRESULT result; | ||||
|     FIL file; | ||||
|     File file; | ||||
| 
 | ||||
|     const uint8_t str_buffer_size = 32; | ||||
|     char str_buffer[str_buffer_size]; | ||||
| 
 | ||||
|     result = f_open(&file, "write.test", FA_WRITE | FA_OPEN_ALWAYS); | ||||
|     if(result) { | ||||
|     if(!fs_api->file.open(&file, "write.test", FSAM_WRITE, FSOM_OPEN_ALWAYS)) { | ||||
|         snprintf(str_buffer, str_buffer_size, "in %lu-byte write test", size); | ||||
|         set_error({"cannot open file ", static_cast<const char*>(str_buffer)}); | ||||
|     } | ||||
| 
 | ||||
|     start_tick = osKernelGetTickCount(); | ||||
|     for(size_t i = 0; i < count; i++) { | ||||
|         result = f_write(&file, benchmark_data, size, reinterpret_cast<UINT*>(&bytes_written)); | ||||
|         if(bytes_written != size || result) { | ||||
|         bytes_written = fs_api->file.write(&file, benchmark_data, size); | ||||
|         if(bytes_written != size || file.error_id != FSE_OK) { | ||||
|             snprintf(str_buffer, str_buffer_size, "in %lu-byte write test", size); | ||||
|             set_error({"cannot write to file ", static_cast<const char*>(str_buffer)}); | ||||
|         } | ||||
|     } | ||||
|     stop_tick = osKernelGetTickCount(); | ||||
| 
 | ||||
|     result = f_close(&file); | ||||
|     if(result) { | ||||
|     if(!fs_api->file.close(&file)) { | ||||
|         snprintf(str_buffer, str_buffer_size, "in %lu-byte write test", size); | ||||
|         set_error({"cannot close file ", static_cast<const char*>(str_buffer)}); | ||||
|     } | ||||
| @ -425,8 +318,7 @@ void SdTest::read_benchmark() { | ||||
|         static_cast<const char*>(str_buffer[4]), | ||||
|         static_cast<const char*>(str_buffer[5])}; | ||||
| 
 | ||||
|     FRESULT result; | ||||
|     FIL file; | ||||
|     File file; | ||||
| 
 | ||||
|     const uint32_t b1_size = 1; | ||||
|     const uint32_t b8_size = 8; | ||||
| @ -438,21 +330,18 @@ void SdTest::read_benchmark() { | ||||
|     set_text({"prepare data", "for read speed test", "procedure can be lengthy", "please wait"}); | ||||
|     delay(100); | ||||
| 
 | ||||
|     result = f_open(&file, "read.test", FA_WRITE | FA_OPEN_ALWAYS); | ||||
|     if(result) { | ||||
|     if(!fs_api->file.open(&file, "read.test", FSAM_WRITE, FSOM_OPEN_ALWAYS)) { | ||||
|         set_error({"cannot open file ", "in prepare read"}); | ||||
|     } | ||||
| 
 | ||||
|     for(size_t i = 0; i < benchmark_data_size / b4096_size; i++) { | ||||
|         result = | ||||
|             f_write(&file, benchmark_data, b4096_size, reinterpret_cast<UINT*>(&bytes_written)); | ||||
|         if(bytes_written != b4096_size || result) { | ||||
|         bytes_written = fs_api->file.write(&file, benchmark_data, b4096_size); | ||||
|         if(bytes_written != b4096_size || file.error_id != FSE_OK) { | ||||
|             set_error({"cannot write to file ", "in prepare read"}); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     result = f_close(&file); | ||||
|     if(result) { | ||||
|     if(!fs_api->file.close(&file)) { | ||||
|         set_error({"cannot close file ", "in prepare read"}); | ||||
|     } | ||||
| 
 | ||||
| @ -461,8 +350,7 @@ void SdTest::read_benchmark() { | ||||
|     delay(100); | ||||
| 
 | ||||
|     // open file
 | ||||
|     result = f_open(&file, "read.test", FA_READ | FA_OPEN_EXISTING); | ||||
|     if(result) { | ||||
|     if(!fs_api->file.open(&file, "read.test", FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||
|         set_error({"cannot open file ", "in read benchmark"}); | ||||
|     } | ||||
| 
 | ||||
| @ -497,8 +385,7 @@ void SdTest::read_benchmark() { | ||||
|     set_text(string_list); | ||||
| 
 | ||||
|     // close file
 | ||||
|     result = f_close(&file); | ||||
|     if(result) { | ||||
|     if(!fs_api->file.close(&file)) { | ||||
|         set_error({"cannot close file ", "in read test"}); | ||||
|     } | ||||
| 
 | ||||
| @ -507,9 +394,9 @@ void SdTest::read_benchmark() { | ||||
|     wait_for_button(InputOk); | ||||
| } | ||||
| 
 | ||||
| uint32_t SdTest::read_benchmark_internal(const uint32_t size, const uint32_t count, FIL* file) { | ||||
| uint32_t SdTest::read_benchmark_internal(const uint32_t size, const uint32_t count, File* file) { | ||||
|     uint32_t start_tick, stop_tick, benchmark_bps, benchmark_time, bytes_readed; | ||||
|     FRESULT result; | ||||
|     //FRESULT result;
 | ||||
| 
 | ||||
|     const uint8_t str_buffer_size = 32; | ||||
|     char str_buffer[str_buffer_size]; | ||||
| @ -522,12 +409,12 @@ uint32_t SdTest::read_benchmark_internal(const uint32_t size, const uint32_t cou | ||||
|         set_error({"cannot allocate memory", static_cast<const char*>(str_buffer)}); | ||||
|     } | ||||
| 
 | ||||
|     f_rewind(file); | ||||
|     fs_api->file.seek(file, 0, true); | ||||
| 
 | ||||
|     start_tick = osKernelGetTickCount(); | ||||
|     for(size_t i = 0; i < count; i++) { | ||||
|         result = f_read(file, read_buffer, size, reinterpret_cast<UINT*>(&bytes_readed)); | ||||
|         if(bytes_readed != size || result) { | ||||
|         bytes_readed = fs_api->file.read(file, read_buffer, size); | ||||
|         if(bytes_readed != size || file->error_id != FSE_OK) { | ||||
|             snprintf(str_buffer, str_buffer_size, "in %lu-byte read test", size); | ||||
|             set_error({"cannot read from file ", static_cast<const char*>(str_buffer)}); | ||||
|         } | ||||
| @ -555,8 +442,7 @@ void SdTest::hash_benchmark() { | ||||
|     const uint8_t str_buffer_size = 32; | ||||
|     char str_buffer[3][str_buffer_size] = {"", "", ""}; | ||||
| 
 | ||||
|     FRESULT result; | ||||
|     FIL file; | ||||
|     File file; | ||||
| 
 | ||||
|     const uint32_t b4096_size = 4096; | ||||
|     const uint32_t benchmark_count = 20; | ||||
| @ -566,17 +452,15 @@ void SdTest::hash_benchmark() { | ||||
|     delay(100); | ||||
| 
 | ||||
|     // write data to test file and calculate hash
 | ||||
|     result = f_open(&file, "hash.test", FA_WRITE | FA_OPEN_ALWAYS); | ||||
|     if(result) { | ||||
|     if(!fs_api->file.open(&file, "hash.test", FSAM_WRITE, FSOM_OPEN_ALWAYS)) { | ||||
|         set_error({"cannot open file ", "in prepare hash"}); | ||||
|     } | ||||
| 
 | ||||
|     for(uint32_t i = 0; i < benchmark_count; i++) { | ||||
|         mcu_data_hash = fnv1a_buffer_hash(benchmark_data, b4096_size, mcu_data_hash); | ||||
|         result = | ||||
|             f_write(&file, benchmark_data, b4096_size, reinterpret_cast<UINT*>(&bytes_written)); | ||||
|         bytes_written = fs_api->file.write(&file, benchmark_data, b4096_size); | ||||
| 
 | ||||
|         if(bytes_written != b4096_size || result) { | ||||
|         if(bytes_written != b4096_size || file.error_id != FSE_OK) { | ||||
|             set_error({"cannot write to file ", "in prepare hash"}); | ||||
|         } | ||||
| 
 | ||||
| @ -585,8 +469,7 @@ void SdTest::hash_benchmark() { | ||||
|         delay(100); | ||||
|     } | ||||
| 
 | ||||
|     result = f_close(&file); | ||||
|     if(result) { | ||||
|     if(!fs_api->file.close(&file)) { | ||||
|         set_error({"cannot close file ", "in prepare hash"}); | ||||
|     } | ||||
| 
 | ||||
| @ -602,16 +485,15 @@ void SdTest::hash_benchmark() { | ||||
|         set_error({"cannot allocate memory", "in hash test"}); | ||||
|     } | ||||
| 
 | ||||
|     result = f_open(&file, "hash.test", FA_READ | FA_OPEN_EXISTING); | ||||
|     if(result) { | ||||
|     if(!fs_api->file.open(&file, "hash.test", FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||
|         set_error({"cannot open file ", "in hash test"}); | ||||
|     } | ||||
| 
 | ||||
|     for(uint32_t i = 0; i < benchmark_count; i++) { | ||||
|         result = f_read(&file, read_buffer, b4096_size, reinterpret_cast<UINT*>(&bytes_readed)); | ||||
|         bytes_readed = fs_api->file.read(&file, read_buffer, b4096_size); | ||||
|         sdcard_data_hash = fnv1a_buffer_hash(read_buffer, b4096_size, sdcard_data_hash); | ||||
| 
 | ||||
|         if(bytes_readed != b4096_size || result) { | ||||
|         if(bytes_readed != b4096_size || file.error_id != FSE_OK) { | ||||
|             set_error({"cannot read from file ", "in hash test"}); | ||||
|         } | ||||
| 
 | ||||
| @ -620,9 +502,7 @@ void SdTest::hash_benchmark() { | ||||
|         delay(100); | ||||
|     } | ||||
| 
 | ||||
|     result = f_close(&file); | ||||
| 
 | ||||
|     if(result) { | ||||
|     if(!fs_api->file.close(&file)) { | ||||
|         set_error({"cannot close file ", "in hash test"}); | ||||
|     } | ||||
| 
 | ||||
| @ -730,76 +610,6 @@ void SdTest::blink_green() { | ||||
|     gpio_write(green_led_record, 1); | ||||
| } | ||||
| 
 | ||||
| // FatFs errors descriptions
 | ||||
| const char* SdTest::fatfs_error_desc(FRESULT res) { | ||||
|     switch(res) { | ||||
|     case FR_OK: | ||||
|         return "ok"; | ||||
|         break; | ||||
|     case FR_DISK_ERR: | ||||
|         return "low level error"; | ||||
|         break; | ||||
|     case FR_INT_ERR: | ||||
|         return "internal error"; | ||||
|         break; | ||||
|     case FR_NOT_READY: | ||||
|         return "not ready"; | ||||
|         break; | ||||
|     case FR_NO_FILE: | ||||
|         return "no file"; | ||||
|         break; | ||||
|     case FR_NO_PATH: | ||||
|         return "no path"; | ||||
|         break; | ||||
|     case FR_INVALID_NAME: | ||||
|         return "invalid name"; | ||||
|         break; | ||||
|     case FR_DENIED: | ||||
|         return "denied"; | ||||
|         break; | ||||
|     case FR_EXIST: | ||||
|         return "already exist"; | ||||
|         break; | ||||
|     case FR_INVALID_OBJECT: | ||||
|         return "invalid file/dir obj"; | ||||
|         break; | ||||
|     case FR_WRITE_PROTECTED: | ||||
|         return "write protected"; | ||||
|         break; | ||||
|     case FR_INVALID_DRIVE: | ||||
|         return "invalid drive"; | ||||
|         break; | ||||
|     case FR_NOT_ENABLED: | ||||
|         return "no work area in volume"; | ||||
|         break; | ||||
|     case FR_NO_FILESYSTEM: | ||||
|         return "no valid FS volume"; | ||||
|         break; | ||||
|     case FR_MKFS_ABORTED: | ||||
|         return "aborted, any problem"; | ||||
|         break; | ||||
|     case FR_TIMEOUT: | ||||
|         return "timeout"; | ||||
|         break; | ||||
|     case FR_LOCKED: | ||||
|         return "file locked"; | ||||
|         break; | ||||
|     case FR_NOT_ENOUGH_CORE: | ||||
|         return "not enough core memory"; | ||||
|         break; | ||||
|     case FR_TOO_MANY_OPEN_FILES: | ||||
|         return "too many open files"; | ||||
|         break; | ||||
|     case FR_INVALID_PARAMETER: | ||||
|         return "invalid parameter"; | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         return "unknown error"; | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // set text, but with infinite loop
 | ||||
| template <class T> void SdTest::set_error(std::initializer_list<T> list) { | ||||
|     set_text(list); | ||||
|  | ||||
							
								
								
									
										738
									
								
								applications/sd-filesystem/sd-filesystem-api.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										738
									
								
								applications/sd-filesystem/sd-filesystem-api.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,738 @@ | ||||
| #include "fatfs.h" | ||||
| #include "filesystem-api.h" | ||||
| #include "sd-filesystem.h" | ||||
| 
 | ||||
| /******************* Global vars for api *******************/ | ||||
| 
 | ||||
| static SdFsInfo* fs_info; | ||||
| 
 | ||||
| /******************* Core Functions *******************/ | ||||
| 
 | ||||
| bool _fs_init(SdFsInfo* _fs_info) { | ||||
|     bool result = true; | ||||
|     _fs_info->mutex = osMutexNew(NULL); | ||||
|     if(_fs_info->mutex == NULL) result = false; | ||||
| 
 | ||||
|     for(uint8_t i = 0; i < SD_FS_MAX_FILES; i++) { | ||||
|         _fs_info->files[i].thread_id = NULL; | ||||
|     } | ||||
| 
 | ||||
|     _fs_info->path = "0:/"; | ||||
|     _fs_info->status = SD_NO_CARD; | ||||
| 
 | ||||
|     // store pointer for api fns
 | ||||
|     fs_info = _fs_info; | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| bool _fs_lock(SdFsInfo* fs_info) { | ||||
|     return (osMutexAcquire(fs_info->mutex, osWaitForever) == osOK); | ||||
| } | ||||
| 
 | ||||
| bool _fs_unlock(SdFsInfo* fs_info) { | ||||
|     return (osMutexRelease(fs_info->mutex) == osOK); | ||||
| } | ||||
| 
 | ||||
| SDError _get_filedata(SdFsInfo* fs_info, File* file, FileData** filedata, FiledataFilter filter) { | ||||
|     SDError error = SD_OK; | ||||
|     _fs_lock(fs_info); | ||||
| 
 | ||||
|     if(fs_info->status == SD_OK) { | ||||
|         if(file != NULL && file->file_id < SD_FS_MAX_FILES) { | ||||
|             if(fs_info->files[file->file_id].thread_id == osThreadGetId()) { | ||||
|                 if(filter == FDF_ANY) { | ||||
|                     // any type
 | ||||
|                     *filedata = &fs_info->files[file->file_id]; | ||||
|                 } else if(filter == FDF_FILE) { | ||||
|                     // file type
 | ||||
|                     if(!fs_info->files[file->file_id].is_dir) { | ||||
|                         *filedata = &fs_info->files[file->file_id]; | ||||
|                     } else { | ||||
|                         error = SD_NOT_A_FILE; | ||||
|                     } | ||||
|                 } else if(filter == FDF_DIR) { | ||||
|                     // dir type
 | ||||
|                     if(fs_info->files[file->file_id].is_dir) { | ||||
|                         *filedata = &fs_info->files[file->file_id]; | ||||
|                     } else { | ||||
|                         error = SD_NOT_A_DIR; | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 error = SD_OTHER_APP; | ||||
|             } | ||||
|         } else { | ||||
|             error = SD_INVALID_PARAMETER; | ||||
|         } | ||||
|     } else { | ||||
|         error = SD_NO_CARD; | ||||
|     } | ||||
| 
 | ||||
|     _fs_unlock(fs_info); | ||||
|     return error; | ||||
| } | ||||
| 
 | ||||
| SDError _get_file(SdFsInfo* fs_info, File* file, FileData** filedata) { | ||||
|     return _get_filedata(fs_info, file, filedata, FDF_FILE); | ||||
| } | ||||
| 
 | ||||
| SDError _get_dir(SdFsInfo* fs_info, File* file, FileData** filedata) { | ||||
|     return _get_filedata(fs_info, file, filedata, FDF_DIR); | ||||
| } | ||||
| 
 | ||||
| SDError _get_any(SdFsInfo* fs_info, File* file, FileData** filedata) { | ||||
|     return _get_filedata(fs_info, file, filedata, FDF_ANY); | ||||
| } | ||||
| 
 | ||||
| SDError _fs_status(SdFsInfo* fs_info) { | ||||
|     SDError result; | ||||
| 
 | ||||
|     _fs_lock(fs_info); | ||||
|     result = fs_info->status; | ||||
|     _fs_unlock(fs_info); | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| void _fs_on_client_app_exit(SdFsInfo* fs_info) { | ||||
|     _fs_lock(fs_info); | ||||
|     for(uint8_t i = 0; i < SD_FS_MAX_FILES; i++) { | ||||
|         if(fs_info->files[i].thread_id == osThreadGetId()) { | ||||
|             if(fs_info->files[i].is_dir) { | ||||
|                 // TODO close dir
 | ||||
|             } else { | ||||
|                 // TODO close file
 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     _fs_unlock(fs_info); | ||||
| } | ||||
| 
 | ||||
| FS_Error _fs_parse_error(SDError error) { | ||||
|     FS_Error result; | ||||
|     switch(error) { | ||||
|     case SD_OK: | ||||
|         result = FSE_OK; | ||||
|         break; | ||||
|     case SD_INT_ERR: | ||||
|         result = FSE_INTERNAL; | ||||
|         break; | ||||
|     case SD_NO_FILE: | ||||
|         result = FSE_NOT_EXIST; | ||||
|         break; | ||||
|     case SD_NO_PATH: | ||||
|         result = FSE_NOT_EXIST; | ||||
|         break; | ||||
|     case SD_INVALID_NAME: | ||||
|         result = FSE_INVALID_NAME; | ||||
|         break; | ||||
|     case SD_DENIED: | ||||
|         result = FSE_DENIED; | ||||
|         break; | ||||
|     case SD_EXIST: | ||||
|         result = FSE_EXIST; | ||||
|         break; | ||||
|     case SD_INVALID_OBJECT: | ||||
|         result = FSE_INTERNAL; | ||||
|         break; | ||||
|     case SD_WRITE_PROTECTED: | ||||
|         result = FSE_INTERNAL; | ||||
|         break; | ||||
|     case SD_INVALID_DRIVE: | ||||
|         result = FSE_INTERNAL; | ||||
|         break; | ||||
|     case SD_NOT_ENABLED: | ||||
|         result = FSE_INTERNAL; | ||||
|         break; | ||||
|     case SD_NO_FILESYSTEM: | ||||
|         result = FSE_NOT_READY; | ||||
|         break; | ||||
|     case SD_MKFS_ABORTED: | ||||
|         result = FSE_INTERNAL; | ||||
|         break; | ||||
|     case SD_TIMEOUT: | ||||
|         result = FSE_INTERNAL; | ||||
|         break; | ||||
|     case SD_LOCKED: | ||||
|         result = FSE_INTERNAL; | ||||
|         break; | ||||
|     case SD_NOT_ENOUGH_CORE: | ||||
|         result = FSE_INTERNAL; | ||||
|         break; | ||||
|     case SD_TOO_MANY_OPEN_FILES: | ||||
|         result = FSE_INTERNAL; | ||||
|         break; | ||||
|     case SD_INVALID_PARAMETER: | ||||
|         result = FSE_INVALID_PARAMETER; | ||||
|         break; | ||||
|     case SD_NO_CARD: | ||||
|         result = FSE_NOT_READY; | ||||
|         break; | ||||
|     case SD_NOT_A_FILE: | ||||
|         result = FSE_INVALID_PARAMETER; | ||||
|         break; | ||||
|     case SD_NOT_A_DIR: | ||||
|         result = FSE_INVALID_PARAMETER; | ||||
|         break; | ||||
|     case SD_OTHER_APP: | ||||
|         result = FSE_INTERNAL; | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         result = FSE_INTERNAL; | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| /******************* File Functions *******************/ | ||||
| 
 | ||||
| // Open/Create a file
 | ||||
| bool fs_file_open(File* file, const char* path, FS_AccessMode access_mode, FS_OpenMode open_mode) { | ||||
|     SDFile* sd_file = NULL; | ||||
| 
 | ||||
|     _fs_lock(fs_info); | ||||
|     for(uint8_t index = 0; index < SD_FS_MAX_FILES; index++) { | ||||
|         FileData* filedata = &fs_info->files[index]; | ||||
| 
 | ||||
|         if(filedata->thread_id == NULL) { | ||||
|             file->file_id = index; | ||||
| 
 | ||||
|             memset(&(filedata->data), 0, sizeof(SDFileDirStorage)); | ||||
|             filedata->thread_id = osThreadGetId(); | ||||
|             filedata->is_dir = false; | ||||
|             sd_file = &(filedata->data.file); | ||||
| 
 | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     _fs_unlock(fs_info); | ||||
| 
 | ||||
|     if(sd_file == NULL) { | ||||
|         file->internal_error_id = SD_TOO_MANY_OPEN_FILES; | ||||
|     } else { | ||||
|         uint8_t _mode = 0; | ||||
| 
 | ||||
|         if(access_mode & FSAM_READ) _mode |= FA_READ; | ||||
|         if(access_mode & FSAM_WRITE) _mode |= FA_WRITE; | ||||
|         if(open_mode & FSOM_OPEN_EXISTING) _mode |= FA_OPEN_EXISTING; | ||||
|         if(open_mode & FSOM_OPEN_ALWAYS) _mode |= FA_OPEN_ALWAYS; | ||||
|         if(open_mode & FSOM_OPEN_APPEND) _mode |= FA_OPEN_APPEND; | ||||
|         if(open_mode & FSOM_CREATE_NEW) _mode |= FA_CREATE_NEW; | ||||
|         if(open_mode & FSOM_CREATE_ALWAYS) _mode |= FA_CREATE_ALWAYS; | ||||
| 
 | ||||
|         file->internal_error_id = f_open(sd_file, path, _mode); | ||||
|     } | ||||
| 
 | ||||
|     // TODO on exit
 | ||||
|     //furiac_onexit(_fs_on_client_app_exit, fs_info);
 | ||||
| 
 | ||||
|     file->error_id = _fs_parse_error(file->internal_error_id); | ||||
|     return (file->internal_error_id == SD_OK); | ||||
| } | ||||
| 
 | ||||
| // Close an opened file
 | ||||
| bool fs_file_close(File* file) { | ||||
|     FileData* filedata = NULL; | ||||
|     file->internal_error_id = _get_file(fs_info, file, &filedata); | ||||
| 
 | ||||
|     if(file->internal_error_id == SD_OK) { | ||||
|         file->internal_error_id = f_close(&filedata->data.file); | ||||
| 
 | ||||
|         _fs_lock(fs_info); | ||||
|         filedata->thread_id = NULL; | ||||
|         _fs_unlock(fs_info); | ||||
|     } | ||||
| 
 | ||||
|     file->error_id = _fs_parse_error(file->internal_error_id); | ||||
|     return (file->internal_error_id == SD_OK); | ||||
| } | ||||
| 
 | ||||
| // Read data from the file
 | ||||
| uint16_t fs_file_read(File* file, void* buff, uint16_t const bytes_to_read) { | ||||
|     FileData* filedata = NULL; | ||||
|     uint16_t bytes_readed = 0; | ||||
| 
 | ||||
|     file->internal_error_id = _get_file(fs_info, file, &filedata); | ||||
| 
 | ||||
|     if(file->internal_error_id == SD_OK) { | ||||
|         file->internal_error_id = f_read(&filedata->data.file, buff, bytes_to_read, &bytes_readed); | ||||
|     } | ||||
| 
 | ||||
|     file->error_id = _fs_parse_error(file->internal_error_id); | ||||
|     return bytes_readed; | ||||
| } | ||||
| 
 | ||||
| // Write data to the file
 | ||||
| uint16_t fs_file_write(File* file, void* buff, uint16_t const bytes_to_write) { | ||||
|     FileData* filedata = NULL; | ||||
|     uint16_t bytes_written = 0; | ||||
| 
 | ||||
|     file->internal_error_id = _get_file(fs_info, file, &filedata); | ||||
| 
 | ||||
|     if(file->internal_error_id == SD_OK) { | ||||
|         file->internal_error_id = | ||||
|             f_write(&filedata->data.file, buff, bytes_to_write, &bytes_written); | ||||
|     } | ||||
| 
 | ||||
|     file->error_id = _fs_parse_error(file->internal_error_id); | ||||
|     return bytes_written; | ||||
| } | ||||
| 
 | ||||
| // Move read/write pointer, expand size
 | ||||
| bool fs_file_seek(File* file, const uint32_t offset, const bool from_start) { | ||||
|     FileData* filedata = NULL; | ||||
| 
 | ||||
|     file->internal_error_id = _get_file(fs_info, file, &filedata); | ||||
| 
 | ||||
|     if(file->internal_error_id == SD_OK) { | ||||
|         if(from_start) { | ||||
|             file->internal_error_id = f_lseek(&filedata->data.file, offset); | ||||
|         } else { | ||||
|             uint64_t position = f_tell(&filedata->data.file); | ||||
|             position += offset; | ||||
|             file->internal_error_id = f_lseek(&filedata->data.file, position); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     file->error_id = _fs_parse_error(file->internal_error_id); | ||||
|     return (file->internal_error_id == SD_OK); | ||||
| } | ||||
| 
 | ||||
| // Tell pointer position
 | ||||
| uint64_t fs_file_tell(File* file) { | ||||
|     FileData* filedata = NULL; | ||||
|     uint64_t position = 0; | ||||
|     file->internal_error_id = _get_file(fs_info, file, &filedata); | ||||
| 
 | ||||
|     if(file->internal_error_id == SD_OK) { | ||||
|         position = f_tell(&filedata->data.file); | ||||
|     } | ||||
| 
 | ||||
|     file->error_id = _fs_parse_error(file->internal_error_id); | ||||
|     return position; | ||||
| } | ||||
| 
 | ||||
| // Truncate file size to current pointer value
 | ||||
| bool fs_file_truncate(File* file) { | ||||
|     FileData* filedata = NULL; | ||||
|     uint64_t position = 0; | ||||
| 
 | ||||
|     file->internal_error_id = _get_file(fs_info, file, &filedata); | ||||
| 
 | ||||
|     if(file->internal_error_id == SD_OK) { | ||||
|         file->internal_error_id = f_truncate(&filedata->data.file); | ||||
|     } | ||||
| 
 | ||||
|     file->error_id = _fs_parse_error(file->internal_error_id); | ||||
|     return (file->internal_error_id == SD_OK); | ||||
| } | ||||
| 
 | ||||
| // Flush cached data
 | ||||
| bool fs_file_sync(File* file) { | ||||
|     FileData* filedata = NULL; | ||||
| 
 | ||||
|     file->internal_error_id = _get_file(fs_info, file, &filedata); | ||||
| 
 | ||||
|     if(file->internal_error_id == SD_OK) { | ||||
|         file->internal_error_id = f_sync(&filedata->data.file); | ||||
|     } | ||||
| 
 | ||||
|     file->error_id = _fs_parse_error(file->internal_error_id); | ||||
|     return (file->internal_error_id == SD_OK); | ||||
| } | ||||
| 
 | ||||
| // Get size
 | ||||
| uint64_t fs_file_size(File* file) { | ||||
|     FileData* filedata = NULL; | ||||
|     uint64_t size = 0; | ||||
|     file->internal_error_id = _get_file(fs_info, file, &filedata); | ||||
| 
 | ||||
|     if(file->internal_error_id == SD_OK) { | ||||
|         size = f_size(&filedata->data.file); | ||||
|     } | ||||
| 
 | ||||
|     file->error_id = _fs_parse_error(file->internal_error_id); | ||||
|     return size; | ||||
| } | ||||
| 
 | ||||
| // Test EOF
 | ||||
| bool fs_file_eof(File* file) { | ||||
|     FileData* filedata = NULL; | ||||
|     bool eof = true; | ||||
|     file->internal_error_id = _get_file(fs_info, file, &filedata); | ||||
| 
 | ||||
|     if(file->internal_error_id == SD_OK) { | ||||
|         eof = f_eof(&filedata->data.file); | ||||
|     } | ||||
| 
 | ||||
|     file->error_id = _fs_parse_error(file->internal_error_id); | ||||
|     return eof; | ||||
| } | ||||
| 
 | ||||
| /******************* Dir Functions *******************/ | ||||
| 
 | ||||
| // Open directory
 | ||||
| bool fs_dir_open(File* file, const char* path) { | ||||
|     SDDir* sd_dir = NULL; | ||||
| 
 | ||||
|     _fs_lock(fs_info); | ||||
|     for(uint8_t index = 0; index < SD_FS_MAX_FILES; index++) { | ||||
|         FileData* filedata = &fs_info->files[index]; | ||||
| 
 | ||||
|         if(filedata->thread_id == NULL) { | ||||
|             file->file_id = index; | ||||
| 
 | ||||
|             memset(&(filedata->data), 0, sizeof(SDFileDirStorage)); | ||||
|             filedata->thread_id = osThreadGetId(); | ||||
|             filedata->is_dir = true; | ||||
|             sd_dir = &(filedata->data.dir); | ||||
| 
 | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     _fs_unlock(fs_info); | ||||
| 
 | ||||
|     if(sd_dir == NULL) { | ||||
|         file->internal_error_id = SD_TOO_MANY_OPEN_FILES; | ||||
|     } else { | ||||
|         if(file->internal_error_id == SD_OK) file->internal_error_id = f_opendir(sd_dir, path); | ||||
|     } | ||||
| 
 | ||||
|     // TODO on exit
 | ||||
|     //furiac_onexit(_fs_on_client_app_exit, fs_info);
 | ||||
| 
 | ||||
|     file->error_id = _fs_parse_error(file->internal_error_id); | ||||
|     return (file->internal_error_id == SD_OK); | ||||
| } | ||||
| 
 | ||||
| // Close directory
 | ||||
| bool fs_dir_close(File* file) { | ||||
|     FileData* filedata = NULL; | ||||
|     file->internal_error_id = _get_dir(fs_info, file, &filedata); | ||||
| 
 | ||||
|     if(file->internal_error_id == SD_OK) { | ||||
|         file->internal_error_id = f_closedir(&filedata->data.dir); | ||||
| 
 | ||||
|         _fs_lock(fs_info); | ||||
|         filedata->thread_id = NULL; | ||||
|         _fs_unlock(fs_info); | ||||
|     } | ||||
| 
 | ||||
|     file->error_id = _fs_parse_error(file->internal_error_id); | ||||
|     return (file->internal_error_id == SD_OK); | ||||
| } | ||||
| 
 | ||||
| // Read next file info and name from directory
 | ||||
| bool fs_dir_read(File* file, FileInfo* fileinfo, char* name, const uint16_t name_length) { | ||||
|     FileData* filedata = NULL; | ||||
|     file->internal_error_id = _get_dir(fs_info, file, &filedata); | ||||
| 
 | ||||
|     if(file->internal_error_id == SD_OK) { | ||||
|         SDFileInfo _fileinfo; | ||||
|         file->internal_error_id = f_readdir(&filedata->data.dir, &_fileinfo); | ||||
| 
 | ||||
|         if(fileinfo != NULL) { | ||||
|             fileinfo->date.value = _fileinfo.fdate; | ||||
|             fileinfo->time.value = _fileinfo.ftime; | ||||
|             fileinfo->size = _fileinfo.fsize; | ||||
|             fileinfo->flags = 0; | ||||
| 
 | ||||
|             if(_fileinfo.fattrib & AM_RDO) fileinfo->flags |= FSF_READ_ONLY; | ||||
|             if(_fileinfo.fattrib & AM_HID) fileinfo->flags |= FSF_HIDDEN; | ||||
|             if(_fileinfo.fattrib & AM_SYS) fileinfo->flags |= FSF_SYSTEM; | ||||
|             if(_fileinfo.fattrib & AM_DIR) fileinfo->flags |= FSF_DIRECTORY; | ||||
|             if(_fileinfo.fattrib & AM_ARC) fileinfo->flags |= FSF_ARCHIVE; | ||||
|         } | ||||
| 
 | ||||
|         if(name != NULL && name_length > 0) { | ||||
|             strncpy(name, _fileinfo.fname, name_length); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     file->error_id = _fs_parse_error(file->internal_error_id); | ||||
|     return (file->internal_error_id == SD_OK); | ||||
| } | ||||
| 
 | ||||
| bool fs_dir_rewind(File* file) { | ||||
|     FileData* filedata = NULL; | ||||
|     file->internal_error_id = _get_dir(fs_info, file, &filedata); | ||||
| 
 | ||||
|     if(file->internal_error_id == SD_OK) { | ||||
|         file->internal_error_id = f_readdir(&filedata->data.dir, NULL); | ||||
|     } | ||||
| 
 | ||||
|     file->error_id = _fs_parse_error(file->internal_error_id); | ||||
|     return (file->internal_error_id == SD_OK); | ||||
| } | ||||
| 
 | ||||
| /******************* Common FS Functions *******************/ | ||||
| 
 | ||||
| // Get info about file/dir
 | ||||
| FS_Error | ||||
| fs_common_info(const char* path, FileInfo* fileinfo, char* name, const uint16_t name_length) { | ||||
|     SDFileInfo _fileinfo; | ||||
|     SDError fresult = _fs_status(fs_info); | ||||
| 
 | ||||
|     if(fresult == SD_OK) { | ||||
|         fresult = f_stat(path, &_fileinfo); | ||||
|         if(fresult == FR_OK) { | ||||
|             if(fileinfo != NULL) { | ||||
|                 fileinfo->date.value = _fileinfo.fdate; | ||||
|                 fileinfo->time.value = _fileinfo.ftime; | ||||
|                 fileinfo->size = _fileinfo.fsize; | ||||
|                 fileinfo->flags = 0; | ||||
| 
 | ||||
|                 if(_fileinfo.fattrib & AM_RDO) fileinfo->flags |= FSF_READ_ONLY; | ||||
|                 if(_fileinfo.fattrib & AM_HID) fileinfo->flags |= FSF_HIDDEN; | ||||
|                 if(_fileinfo.fattrib & AM_SYS) fileinfo->flags |= FSF_SYSTEM; | ||||
|                 if(_fileinfo.fattrib & AM_DIR) fileinfo->flags |= FSF_DIRECTORY; | ||||
|                 if(_fileinfo.fattrib & AM_ARC) fileinfo->flags |= FSF_ARCHIVE; | ||||
|             } | ||||
| 
 | ||||
|             if(name != NULL && name_length > 0) { | ||||
|                 strncpy(name, _fileinfo.fname, name_length); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return _fs_parse_error(fresult); | ||||
| } | ||||
| 
 | ||||
| // Delete file/dir
 | ||||
| // File/dir must not have read-only attribute.
 | ||||
| // File/dir must be empty.
 | ||||
| // File/dir must not be opened, or the FAT volume can be collapsed. FF_FS_LOCK fix that.
 | ||||
| FS_Error fs_common_remove(const char* path) { | ||||
|     SDError fresult = _fs_status(fs_info); | ||||
| 
 | ||||
|     if(fresult == SD_OK) { | ||||
|         fresult = f_unlink(path); | ||||
|     } | ||||
| 
 | ||||
|     return _fs_parse_error(fresult); | ||||
| } | ||||
| 
 | ||||
| // Rename file/dir
 | ||||
| // File/dir must not be opened, or the FAT volume can be collapsed. FF_FS_LOCK fix that.
 | ||||
| FS_Error fs_common_rename(const char* old_path, const char* new_path) { | ||||
|     SDError fresult = _fs_status(fs_info); | ||||
| 
 | ||||
|     if(fresult == SD_OK) { | ||||
|         fresult = f_rename(old_path, new_path); | ||||
|     } | ||||
| 
 | ||||
|     return _fs_parse_error(fresult); | ||||
| } | ||||
| 
 | ||||
| // Set attributes of file/dir
 | ||||
| // For example:
 | ||||
| // set "read only" flag and remove "hidden" flag
 | ||||
| // fs_common_set_attr("file.txt", FSF_READ_ONLY, FSF_READ_ONLY | FSF_HIDDEN);
 | ||||
| FS_Error fs_common_set_attr(const char* path, uint8_t attr, uint8_t mask) { | ||||
|     SDError fresult = _fs_status(fs_info); | ||||
| 
 | ||||
|     if(fresult == SD_OK) { | ||||
|         uint8_t _mask = 0; | ||||
|         uint8_t _attr = 0; | ||||
| 
 | ||||
|         if(mask & FSF_READ_ONLY) _mask |= AM_RDO; | ||||
|         if(mask & FSF_HIDDEN) _mask |= AM_HID; | ||||
|         if(mask & FSF_SYSTEM) _mask |= AM_SYS; | ||||
|         if(mask & FSF_DIRECTORY) _mask |= AM_DIR; | ||||
|         if(mask & FSF_ARCHIVE) _mask |= AM_ARC; | ||||
| 
 | ||||
|         if(attr & FSF_READ_ONLY) _attr |= AM_RDO; | ||||
|         if(attr & FSF_HIDDEN) _attr |= AM_HID; | ||||
|         if(attr & FSF_SYSTEM) _attr |= AM_SYS; | ||||
|         if(attr & FSF_DIRECTORY) _attr |= AM_DIR; | ||||
|         if(attr & FSF_ARCHIVE) _attr |= AM_ARC; | ||||
| 
 | ||||
|         fresult = f_chmod(path, attr, mask); | ||||
|     } | ||||
| 
 | ||||
|     return _fs_parse_error(fresult); | ||||
| } | ||||
| 
 | ||||
| // Set time of file/dir
 | ||||
| FS_Error fs_common_set_time(const char* path, FileDateUnion date, FileTimeUnion time) { | ||||
|     SDError fresult = _fs_status(fs_info); | ||||
| 
 | ||||
|     if(fresult == SD_OK) { | ||||
|         SDFileInfo _fileinfo; | ||||
| 
 | ||||
|         _fileinfo.fdate = date.value; | ||||
|         _fileinfo.ftime = time.value; | ||||
| 
 | ||||
|         fresult = f_utime(path, &_fileinfo); | ||||
|     } | ||||
| 
 | ||||
|     return _fs_parse_error(fresult); | ||||
| } | ||||
| 
 | ||||
| // Create new directory
 | ||||
| FS_Error fs_common_mkdir(const char* path) { | ||||
|     SDError fresult = _fs_status(fs_info); | ||||
| 
 | ||||
|     if(fresult == SD_OK) { | ||||
|         fresult = f_mkdir(path); | ||||
|     } | ||||
| 
 | ||||
|     return _fs_parse_error(fresult); | ||||
| } | ||||
| 
 | ||||
| // Get common info about FS
 | ||||
| FS_Error fs_get_fs_info(uint64_t* total_space, uint64_t* free_space) { | ||||
|     SDError fresult = _fs_status(fs_info); | ||||
| 
 | ||||
|     if(fresult == SD_OK) { | ||||
|         DWORD free_clusters; | ||||
|         FATFS* fs; | ||||
| 
 | ||||
|         fresult = f_getfree("0:/", &free_clusters, &fs); | ||||
|         if(fresult == FR_OK) { | ||||
|             uint32_t total_sectors = (fs->n_fatent - 2) * fs->csize; | ||||
|             uint32_t free_sectors = free_clusters * fs->csize; | ||||
| 
 | ||||
|             uint16_t sector_size = _MAX_SS; | ||||
| #if _MAX_SS != _MIN_SS | ||||
|             sector_size = fs->ssize; | ||||
| #endif | ||||
| 
 | ||||
|             if(total_space != NULL) { | ||||
|                 *total_space = (uint64_t)total_sectors * (uint64_t)sector_size; | ||||
|             } | ||||
| 
 | ||||
|             if(free_space != NULL) { | ||||
|                 *free_space = (uint64_t)free_sectors * (uint64_t)sector_size; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return _fs_parse_error(fresult); | ||||
| } | ||||
| 
 | ||||
| /******************* Error Reporting Functions *******************/ | ||||
| 
 | ||||
| // Get common error description
 | ||||
| const char* fs_error_get_desc(FS_Error error_id) { | ||||
|     const char* result; | ||||
|     switch(error_id) { | ||||
|     case(FSE_OK): | ||||
|         result = "OK"; | ||||
|         break; | ||||
|     case(FSE_NOT_READY): | ||||
|         result = "filesystem not ready"; | ||||
|         break; | ||||
|     case(FSE_EXIST): | ||||
|         result = "file/dir already exist"; | ||||
|         break; | ||||
|     case(FSE_NOT_EXIST): | ||||
|         result = "file/dir not exist"; | ||||
|         break; | ||||
|     case(FSE_INVALID_PARAMETER): | ||||
|         result = "invalid parameter"; | ||||
|         break; | ||||
|     case(FSE_DENIED): | ||||
|         result = "access denied"; | ||||
|         break; | ||||
|     case(FSE_INVALID_NAME): | ||||
|         result = "invalid name/path"; | ||||
|         break; | ||||
|     case(FSE_INTERNAL): | ||||
|         result = "internal error"; | ||||
|         break; | ||||
|     case(FSE_NOT_IMPLEMENTED): | ||||
|         result = "function not implemented"; | ||||
|         break; | ||||
|     default: | ||||
|         result = "unknown error"; | ||||
|         break; | ||||
|     } | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| // Get internal error description
 | ||||
| const char* fs_error_get_internal_desc(uint32_t internal_error_id) { | ||||
|     const char* result; | ||||
|     switch(internal_error_id) { | ||||
|     case(SD_OK): | ||||
|         result = "OK"; | ||||
|         break; | ||||
|     case(SD_DISK_ERR): | ||||
|         result = "disk error"; | ||||
|         break; | ||||
|     case(SD_INT_ERR): | ||||
|         result = "internal error"; | ||||
|         break; | ||||
|     case(SD_NO_FILE): | ||||
|         result = "no file"; | ||||
|         break; | ||||
|     case(SD_NO_PATH): | ||||
|         result = "no path"; | ||||
|         break; | ||||
|     case(SD_INVALID_NAME): | ||||
|         result = "invalid name"; | ||||
|         break; | ||||
|     case(SD_DENIED): | ||||
|         result = "access denied"; | ||||
|         break; | ||||
|     case(SD_EXIST): | ||||
|         result = "file/dir exist"; | ||||
|         break; | ||||
|     case(SD_INVALID_OBJECT): | ||||
|         result = "invalid object"; | ||||
|         break; | ||||
|     case(SD_WRITE_PROTECTED): | ||||
|         result = "write protected"; | ||||
|         break; | ||||
|     case(SD_INVALID_DRIVE): | ||||
|         result = "invalid drive"; | ||||
|         break; | ||||
|     case(SD_NOT_ENABLED): | ||||
|         result = "not enabled"; | ||||
|         break; | ||||
|     case(SD_NO_FILESYSTEM): | ||||
|         result = "no filesystem"; | ||||
|         break; | ||||
|     case(SD_MKFS_ABORTED): | ||||
|         result = "aborted"; | ||||
|         break; | ||||
|     case(SD_TIMEOUT): | ||||
|         result = "timeout"; | ||||
|         break; | ||||
|     case(SD_LOCKED): | ||||
|         result = "file locked"; | ||||
|         break; | ||||
|     case(SD_NOT_ENOUGH_CORE): | ||||
|         result = "not enough memory"; | ||||
|         break; | ||||
|     case(SD_TOO_MANY_OPEN_FILES): | ||||
|         result = "too many open files"; | ||||
|         break; | ||||
|     case(SD_INVALID_PARAMETER): | ||||
|         result = "invalid parameter"; | ||||
|         break; | ||||
|     case(SD_NO_CARD): | ||||
|         result = "no SD Card"; | ||||
|         break; | ||||
|     case(SD_NOT_A_FILE): | ||||
|         result = "not a file"; | ||||
|         break; | ||||
|     case(SD_NOT_A_DIR): | ||||
|         result = "not a directory"; | ||||
|         break; | ||||
|     case(SD_OTHER_APP): | ||||
|         result = "opened by other app"; | ||||
|         break; | ||||
|     case(SD_LOW_LEVEL_ERR): | ||||
|         result = "low level error"; | ||||
|         break; | ||||
|     default: | ||||
|         result = "unknown error"; | ||||
|         break; | ||||
|     } | ||||
|     return result; | ||||
| } | ||||
							
								
								
									
										433
									
								
								applications/sd-filesystem/sd-filesystem.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										433
									
								
								applications/sd-filesystem/sd-filesystem.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,433 @@ | ||||
| #include "fatfs.h" | ||||
| #include "filesystem-api.h" | ||||
| #include "sd-filesystem.h" | ||||
| #include "menu/menu.h" | ||||
| #include "menu/menu_item.h" | ||||
| 
 | ||||
| FS_Api* fs_api_alloc() { | ||||
|     FS_Api* fs_api = furi_alloc(sizeof(FS_Api)); | ||||
| 
 | ||||
|     // fill file api
 | ||||
|     fs_api->file.open = fs_file_open; | ||||
|     fs_api->file.close = fs_file_close; | ||||
|     fs_api->file.read = fs_file_read; | ||||
|     fs_api->file.write = fs_file_write; | ||||
|     fs_api->file.seek = fs_file_seek; | ||||
|     fs_api->file.tell = fs_file_tell; | ||||
|     fs_api->file.truncate = fs_file_truncate; | ||||
|     fs_api->file.size = fs_file_size; | ||||
|     fs_api->file.sync = fs_file_sync; | ||||
|     fs_api->file.eof = fs_file_eof; | ||||
| 
 | ||||
|     // fill dir api
 | ||||
|     fs_api->dir.open = fs_dir_open; | ||||
|     fs_api->dir.close = fs_dir_close; | ||||
|     fs_api->dir.read = fs_dir_read; | ||||
|     fs_api->dir.rewind = fs_dir_rewind; | ||||
| 
 | ||||
|     // fill common api
 | ||||
|     fs_api->common.info = fs_common_info; | ||||
|     fs_api->common.remove = fs_common_remove; | ||||
|     fs_api->common.rename = fs_common_rename; | ||||
|     fs_api->common.set_attr = fs_common_set_attr; | ||||
|     fs_api->common.mkdir = fs_common_mkdir; | ||||
|     fs_api->common.set_time = fs_common_set_time; | ||||
|     fs_api->common.get_fs_info = fs_get_fs_info; | ||||
| 
 | ||||
|     // fill errors api
 | ||||
|     fs_api->error.get_desc = fs_error_get_desc; | ||||
|     fs_api->error.get_internal_desc = fs_error_get_internal_desc; | ||||
| 
 | ||||
|     return fs_api; | ||||
| } | ||||
| 
 | ||||
| void sd_set_lines(SdApp* sd_app, uint8_t count, ...) { | ||||
|     va_list argptr; | ||||
|     count = min(count, SD_STATE_LINES_COUNT); | ||||
| 
 | ||||
|     for(uint8_t i = 0; i < SD_STATE_LINES_COUNT; i++) { | ||||
|         sd_app->line[i] = ""; | ||||
|     } | ||||
| 
 | ||||
|     va_start(argptr, count); | ||||
| 
 | ||||
|     for(uint8_t i = 0; i < count; i++) { | ||||
|         sd_app->line[i] = va_arg(argptr, char*); | ||||
|     } | ||||
| 
 | ||||
|     va_end(argptr); | ||||
| } | ||||
| 
 | ||||
| void sd_icon_draw_callback(Canvas* canvas, void* context) { | ||||
|     furi_assert(canvas); | ||||
|     furi_assert(context); | ||||
|     SdApp* sd_app = context; | ||||
| 
 | ||||
|     switch(sd_app->info.status) { | ||||
|     case SD_NO_CARD: | ||||
|         break; | ||||
|     case SD_OK: | ||||
|         canvas_draw_icon(canvas, 0, 0, sd_app->icon.mounted); | ||||
|         break; | ||||
|     default: | ||||
|         canvas_draw_icon(canvas, 0, 0, sd_app->icon.fail); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void sd_app_draw_callback(Canvas* canvas, void* context) { | ||||
|     furi_assert(canvas); | ||||
|     furi_assert(context); | ||||
|     SdApp* sd_app = context; | ||||
| 
 | ||||
|     canvas_clear(canvas); | ||||
|     canvas_set_color(canvas, ColorBlack); | ||||
|     canvas_set_font(canvas, FontPrimary); | ||||
| 
 | ||||
|     for(uint8_t i = 0; i < SD_STATE_LINES_COUNT; i++) { | ||||
|         canvas_draw_str(canvas, 0, (i + 1) * 10, sd_app->line[i]); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void sd_app_input_callback(InputEvent* event, void* context) { | ||||
|     furi_assert(context); | ||||
|     SdApp* sd_app = context; | ||||
| 
 | ||||
|     osMessageQueuePut(sd_app->event_queue, event, 0, 0); | ||||
| } | ||||
| 
 | ||||
| SdApp* sd_app_alloc() { | ||||
|     SdApp* sd_app = furi_alloc(sizeof(SdApp)); | ||||
| 
 | ||||
|     // init inner fs data
 | ||||
|     if(!_fs_init(&sd_app->info)) { | ||||
|         furiac_exit(NULL); | ||||
|     } | ||||
| 
 | ||||
|     sd_app->event_queue = osMessageQueueNew(1, sizeof(InputEvent), NULL); | ||||
| 
 | ||||
|     // init widget
 | ||||
|     sd_app->widget = widget_alloc(); | ||||
|     widget_draw_callback_set(sd_app->widget, sd_app_draw_callback, sd_app); | ||||
|     widget_input_callback_set(sd_app->widget, sd_app_input_callback, sd_app); | ||||
|     widget_enabled_set(sd_app->widget, false); | ||||
| 
 | ||||
|     // init lines
 | ||||
|     sd_set_lines(sd_app, 0); | ||||
| 
 | ||||
|     // init icon widget
 | ||||
|     sd_app->icon.widget = widget_alloc(); | ||||
|     sd_app->icon.mounted = assets_icons_get(I_SDcardMounted_11x8); | ||||
|     sd_app->icon.fail = assets_icons_get(I_SDcardFail_11x8); | ||||
|     widget_set_width(sd_app->icon.widget, icon_get_width(sd_app->icon.mounted)); | ||||
|     widget_draw_callback_set(sd_app->icon.widget, sd_icon_draw_callback, sd_app); | ||||
|     widget_enabled_set(sd_app->icon.widget, false); | ||||
| 
 | ||||
|     return sd_app; | ||||
| } | ||||
| 
 | ||||
| bool app_sd_ask(SdApp* sd_app, Input input_true, Input input_false) { | ||||
|     bool result; | ||||
| 
 | ||||
|     InputEvent event; | ||||
|     while(1) { | ||||
|         osStatus_t event_status = | ||||
|             osMessageQueueGet(sd_app->event_queue, &event, NULL, osWaitForever); | ||||
| 
 | ||||
|         if(event_status == osOK) { | ||||
|             if(event.state && event.input == input_true) { | ||||
|                 result = true; | ||||
|                 break; | ||||
|             } | ||||
|             if(event.state && event.input == InputBack) { | ||||
|                 result = false; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| void app_sd_info_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     SdApp* sd_app = context; | ||||
|     widget_enabled_set(sd_app->widget, true); | ||||
| 
 | ||||
|     // dynamic strings
 | ||||
|     const uint8_t str_buffer_size = 26; | ||||
|     const uint8_t str_count = 6; | ||||
|     char* str_buffer[str_count]; | ||||
|     bool memory_error = false; | ||||
| 
 | ||||
|     // info vars
 | ||||
|     uint32_t serial_num; | ||||
|     SDError get_label_result, get_free_result; | ||||
|     FATFS* fs; | ||||
|     uint32_t free_clusters, free_sectors, total_sectors; | ||||
|     char volume_label[34]; | ||||
| 
 | ||||
|     // init strings
 | ||||
|     for(uint8_t i = 0; i < str_count; i++) { | ||||
|         str_buffer[i] = malloc(str_buffer_size + 1); | ||||
|         if(str_buffer[i] == NULL) { | ||||
|             memory_error = true; | ||||
|         } else { | ||||
|             snprintf(str_buffer[i], str_buffer_size, ""); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(memory_error) { | ||||
|         sd_set_lines(sd_app, 1, "not enough memory"); | ||||
|     } else { | ||||
|         // get fs info
 | ||||
|         _fs_lock(&sd_app->info); | ||||
|         get_label_result = f_getlabel(sd_app->info.path, volume_label, &serial_num); | ||||
|         get_free_result = f_getfree(sd_app->info.path, &free_clusters, &fs); | ||||
|         _fs_unlock(&sd_app->info); | ||||
| 
 | ||||
|         // calculate size
 | ||||
|         total_sectors = (fs->n_fatent - 2) * fs->csize; | ||||
|         free_sectors = free_clusters * fs->csize; | ||||
|         uint16_t sector_size = _MAX_SS; | ||||
| #if _MAX_SS != _MIN_SS | ||||
|         sector_size = fs->ssize; | ||||
| #endif | ||||
| 
 | ||||
|         // output info to dynamic strings
 | ||||
|         if(get_label_result == SD_OK && get_free_result == SD_OK) { | ||||
|             snprintf(str_buffer[0], str_buffer_size, "%s", volume_label); | ||||
| 
 | ||||
|             const char* fs_type = ""; | ||||
| 
 | ||||
|             switch(fs->fs_type) { | ||||
|             case(FS_FAT12): | ||||
|                 fs_type = "FAT12"; | ||||
|                 break; | ||||
|             case(FS_FAT16): | ||||
|                 fs_type = "FAT16"; | ||||
|                 break; | ||||
|             case(FS_FAT32): | ||||
|                 fs_type = "FAT32"; | ||||
|                 break; | ||||
|             case(FS_EXFAT): | ||||
|                 fs_type = "EXFAT"; | ||||
|                 break; | ||||
|             default: | ||||
|                 fs_type = "UNKNOWN"; | ||||
|                 break; | ||||
|             } | ||||
| 
 | ||||
|             snprintf(str_buffer[1], str_buffer_size, "%s, S/N: %lu", fs_type, serial_num); | ||||
| 
 | ||||
|             snprintf(str_buffer[2], str_buffer_size, "Cluster: %d sectors", fs->csize); | ||||
|             snprintf(str_buffer[3], str_buffer_size, "Sector: %d bytes", sector_size); | ||||
|             snprintf( | ||||
|                 str_buffer[4], str_buffer_size, "%lu KB total", total_sectors / 1024 * sector_size); | ||||
|             snprintf( | ||||
|                 str_buffer[5], str_buffer_size, "%lu KB free", free_sectors / 1024 * sector_size); | ||||
|         } else { | ||||
|             snprintf(str_buffer[0], str_buffer_size, "SD status error:"); | ||||
|             snprintf( | ||||
|                 str_buffer[1], | ||||
|                 str_buffer_size, | ||||
|                 "%s", | ||||
|                 fs_error_get_internal_desc(_fs_status(&sd_app->info))); | ||||
|             snprintf(str_buffer[2], str_buffer_size, "Label error:"); | ||||
|             snprintf( | ||||
|                 str_buffer[3], str_buffer_size, "%s", fs_error_get_internal_desc(get_label_result)); | ||||
|             snprintf(str_buffer[4], str_buffer_size, "Get free error:"); | ||||
|             snprintf( | ||||
|                 str_buffer[5], str_buffer_size, "%s", fs_error_get_internal_desc(get_free_result)); | ||||
|         } | ||||
| 
 | ||||
|         // dynamic strings to screen
 | ||||
|         sd_set_lines( | ||||
|             sd_app, | ||||
|             6, | ||||
|             str_buffer[0], | ||||
|             str_buffer[1], | ||||
|             str_buffer[2], | ||||
|             str_buffer[3], | ||||
|             str_buffer[4], | ||||
|             str_buffer[5]); | ||||
|     } | ||||
| 
 | ||||
|     app_sd_ask(sd_app, InputBack, InputBack); | ||||
| 
 | ||||
|     sd_set_lines(sd_app, 0); | ||||
|     widget_enabled_set(sd_app->widget, false); | ||||
| 
 | ||||
|     for(uint8_t i = 0; i < str_count; i++) { | ||||
|         free(str_buffer[i]); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void app_sd_format_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     SdApp* sd_app = context; | ||||
|     uint8_t* work_area; | ||||
| 
 | ||||
|     // ask to really format
 | ||||
|     sd_set_lines(sd_app, 2, "Press UP to format", "or BACK to exit"); | ||||
|     widget_enabled_set(sd_app->widget, true); | ||||
| 
 | ||||
|     // wait for input
 | ||||
|     if(!app_sd_ask(sd_app, InputUp, InputBack)) { | ||||
|         widget_enabled_set(sd_app->widget, false); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     // show warning
 | ||||
|     sd_set_lines(sd_app, 3, "formatting SD card", "procedure can be lengthy", "please wait"); | ||||
| 
 | ||||
|     // format card
 | ||||
|     _fs_lock(&sd_app->info); | ||||
|     work_area = malloc(_MAX_SS); | ||||
|     if(work_area == NULL) { | ||||
|         sd_app->info.status = SD_NOT_ENOUGH_CORE; | ||||
|     } else { | ||||
|         sd_app->info.status = f_mkfs(sd_app->info.path, FM_ANY, 0, work_area, _MAX_SS); | ||||
|         free(work_area); | ||||
| 
 | ||||
|         if(sd_app->info.status == SD_OK) { | ||||
|             // set label and mount card
 | ||||
|             f_setlabel("Flipper SD"); | ||||
|             sd_app->info.status = f_mount(&sd_app->info.fat_fs, sd_app->info.path, 1); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if(sd_app->info.status != SD_OK) { | ||||
|         sd_set_lines( | ||||
|             sd_app, 2, "SD card format error", fs_error_get_internal_desc(sd_app->info.status)); | ||||
|     } else { | ||||
|         sd_set_lines(sd_app, 1, "SD card formatted"); | ||||
|     } | ||||
| 
 | ||||
|     _fs_unlock(&sd_app->info); | ||||
| 
 | ||||
|     // wait for BACK
 | ||||
|     app_sd_ask(sd_app, InputBack, InputBack); | ||||
| 
 | ||||
|     widget_enabled_set(sd_app->widget, false); | ||||
| } | ||||
| 
 | ||||
| void app_sd_unmount_card(SdApp* sd_app) { | ||||
|     _fs_lock(&sd_app->info); | ||||
| 
 | ||||
|     // set status
 | ||||
|     sd_app->info.status = SD_NO_CARD; | ||||
|     widget_enabled_set(sd_app->icon.widget, false); | ||||
| 
 | ||||
|     // close files
 | ||||
|     for(uint8_t index = 0; index < SD_FS_MAX_FILES; index++) { | ||||
|         FileData* filedata = &sd_app->info.files[index]; | ||||
| 
 | ||||
|         if(filedata->thread_id != NULL) { | ||||
|             if(filedata->is_dir) { | ||||
|                 f_closedir(&filedata->data.dir); | ||||
|             } else { | ||||
|                 f_close(&filedata->data.file); | ||||
|             } | ||||
|             filedata->thread_id = NULL; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // unmount volume
 | ||||
|     f_mount(0, sd_app->info.path, 0); | ||||
| 
 | ||||
|     _fs_unlock(&sd_app->info); | ||||
| } | ||||
| 
 | ||||
| void app_sd_eject_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     SdApp* sd_app = context; | ||||
| 
 | ||||
|     sd_set_lines(sd_app, 1, "ejecting SD card"); | ||||
|     widget_enabled_set(sd_app->widget, true); | ||||
| 
 | ||||
|     app_sd_unmount_card(sd_app); | ||||
| 
 | ||||
|     sd_set_lines(sd_app, 1, "SD card can be pulled out"); | ||||
| 
 | ||||
|     // wait for BACK
 | ||||
|     app_sd_ask(sd_app, InputBack, InputBack); | ||||
| 
 | ||||
|     widget_enabled_set(sd_app->widget, false); | ||||
| } | ||||
| 
 | ||||
| void sd_filesystem(void* p) { | ||||
|     SdApp* sd_app = sd_app_alloc(); | ||||
|     FS_Api* fs_api = fs_api_alloc(); | ||||
| 
 | ||||
|     Gui* gui = furi_open("gui"); | ||||
|     gui_add_widget(gui, sd_app->widget, GuiLayerFullscreen); | ||||
|     gui_add_widget(gui, sd_app->icon.widget, GuiLayerStatusBarLeft); | ||||
| 
 | ||||
|     // add api record
 | ||||
|     if(!furi_create("sdcard", fs_api)) { | ||||
|         furiac_exit(NULL); | ||||
|     } | ||||
| 
 | ||||
|     // init menu
 | ||||
|     // TODO menu icon
 | ||||
|     MenuItem* menu_item; | ||||
|     menu_item = menu_item_alloc_menu("SD Card", assets_icons_get(I_SDcardMounted_11x8)); | ||||
| 
 | ||||
|     menu_item_subitem_add( | ||||
|         menu_item, menu_item_alloc_function("Info", NULL, app_sd_info_callback, sd_app)); | ||||
|     menu_item_subitem_add( | ||||
|         menu_item, menu_item_alloc_function("Format", NULL, app_sd_format_callback, sd_app)); | ||||
|     menu_item_subitem_add( | ||||
|         menu_item, menu_item_alloc_function("Eject", NULL, app_sd_eject_callback, sd_app)); | ||||
| 
 | ||||
|     // add item to menu
 | ||||
|     ValueMutex* menu_vm = furi_open("menu"); | ||||
|     furi_check(menu_vm); | ||||
|     with_value_mutex( | ||||
|         menu_vm, (Menu * menu) { menu_item_add(menu, menu_item); }); | ||||
| 
 | ||||
|     furiac_ready(); | ||||
| 
 | ||||
|     printf("[sd_filesystem] start\n"); | ||||
| 
 | ||||
|     // sd card cycle
 | ||||
|     bool sd_was_present = true; | ||||
| 
 | ||||
|     while(true) { | ||||
|         if(sd_was_present) { | ||||
|             if(hal_gpio_read_sd_detect()) { | ||||
|                 printf("[sd_filesystem] card detected\n"); | ||||
| 
 | ||||
|                 uint8_t bsp_result = BSP_SD_Init(); | ||||
| 
 | ||||
|                 if(bsp_result) { | ||||
|                     sd_app->info.status = SD_LOW_LEVEL_ERR; | ||||
|                     printf("[sd_filesystem] bsp error: %x\n", bsp_result); | ||||
|                 } else { | ||||
|                     printf("[sd_filesystem] bsp ok\n"); | ||||
|                     sd_app->info.status = f_mount(&sd_app->info.fat_fs, sd_app->info.path, 1); | ||||
| 
 | ||||
|                     if(sd_app->info.status != SD_OK) { | ||||
|                         printf("[sd_filesystem] mount error: %d\n", sd_app->info.status); | ||||
|                     } else { | ||||
|                         printf("[sd_filesystem] mount ok\n"); | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 widget_enabled_set(sd_app->icon.widget, true); | ||||
|                 sd_was_present = false; | ||||
|             } | ||||
|         } else { | ||||
|             if(!hal_gpio_read_sd_detect()) { | ||||
|                 printf("[sd_filesystem] card removed\n"); | ||||
| 
 | ||||
|                 widget_enabled_set(sd_app->icon.widget, false); | ||||
|                 app_sd_unmount_card(sd_app); | ||||
|                 sd_was_present = true; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         delay(1000); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										122
									
								
								applications/sd-filesystem/sd-filesystem.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								applications/sd-filesystem/sd-filesystem.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,122 @@ | ||||
| #pragma once | ||||
| #include "flipper.h" | ||||
| #include "flipper_v2.h" | ||||
| 
 | ||||
| #define SD_FS_MAX_FILES _FS_LOCK | ||||
| #define SD_STATE_LINES_COUNT 6 | ||||
| 
 | ||||
| #ifndef min | ||||
| #define min(a, b) (((a) < (b)) ? (a) : (b)) | ||||
| #endif | ||||
| 
 | ||||
| /* api data */ | ||||
| typedef FIL SDFile; | ||||
| typedef DIR SDDir; | ||||
| typedef FILINFO SDFileInfo; | ||||
| 
 | ||||
| /* storage for file/directory objects*/ | ||||
| typedef union { | ||||
|     SDFile file; | ||||
|     SDDir dir; | ||||
| } SDFileDirStorage; | ||||
| 
 | ||||
| typedef enum { | ||||
|     SD_OK = FR_OK, | ||||
|     SD_DISK_ERR = FR_DISK_ERR, | ||||
|     SD_INT_ERR = FR_INT_ERR, | ||||
|     SD_NO_FILE = FR_NO_FILE, | ||||
|     SD_NO_PATH = FR_NO_PATH, | ||||
|     SD_INVALID_NAME = FR_INVALID_NAME, | ||||
|     SD_DENIED = FR_DENIED, | ||||
|     SD_EXIST = FR_EXIST, | ||||
|     SD_INVALID_OBJECT = FR_INVALID_OBJECT, | ||||
|     SD_WRITE_PROTECTED = FR_WRITE_PROTECTED, | ||||
|     SD_INVALID_DRIVE = FR_INVALID_DRIVE, | ||||
|     SD_NOT_ENABLED = FR_NOT_ENABLED, | ||||
|     SD_NO_FILESYSTEM = FR_NO_FILESYSTEM, | ||||
|     SD_MKFS_ABORTED = FR_MKFS_ABORTED, | ||||
|     SD_TIMEOUT = FR_TIMEOUT, | ||||
|     SD_LOCKED = FR_LOCKED, | ||||
|     SD_NOT_ENOUGH_CORE = FR_NOT_ENOUGH_CORE, | ||||
|     SD_TOO_MANY_OPEN_FILES = FR_TOO_MANY_OPEN_FILES, | ||||
|     SD_INVALID_PARAMETER = FR_INVALID_PARAMETER, | ||||
|     SD_NO_CARD, | ||||
|     SD_NOT_A_FILE, | ||||
|     SD_NOT_A_DIR, | ||||
|     SD_OTHER_APP, | ||||
|     SD_LOW_LEVEL_ERR, | ||||
| } SDError; | ||||
| 
 | ||||
| typedef enum { | ||||
|     FDF_DIR, | ||||
|     FDF_FILE, | ||||
|     FDF_ANY, | ||||
| } FiledataFilter; | ||||
| 
 | ||||
| typedef struct { | ||||
|     osThreadId_t thread_id; | ||||
|     bool is_dir; | ||||
|     SDFileDirStorage data; | ||||
| } FileData; | ||||
| 
 | ||||
| /* application data */ | ||||
| typedef struct { | ||||
|     Widget* widget; | ||||
|     Icon* mounted; | ||||
|     Icon* fail; | ||||
| } SdFsIcon; | ||||
| 
 | ||||
| typedef struct { | ||||
|     osMutexId_t mutex; | ||||
|     FileData files[SD_FS_MAX_FILES]; | ||||
|     SDError status; | ||||
|     char* path; | ||||
|     FATFS fat_fs; | ||||
| } SdFsInfo; | ||||
| 
 | ||||
| typedef struct { | ||||
|     SdFsInfo info; | ||||
|     SdFsIcon icon; | ||||
| 
 | ||||
|     Widget* widget; | ||||
|     const char* line[SD_STATE_LINES_COUNT]; | ||||
|     osMessageQueueId_t event_queue; | ||||
| } SdApp; | ||||
| 
 | ||||
| /* core api fns */ | ||||
| bool _fs_init(SdFsInfo* _fs_info); | ||||
| bool _fs_lock(SdFsInfo* fs_info); | ||||
| bool _fs_unlock(SdFsInfo* fs_info); | ||||
| SDError _fs_status(SdFsInfo* fs_info); | ||||
| 
 | ||||
| /* sd api fns */ | ||||
| bool fs_file_open(File* file, const char* path, FS_AccessMode access_mode, FS_OpenMode open_mode); | ||||
| bool fs_file_close(File* file); | ||||
| uint16_t fs_file_read(File* file, void* buff, uint16_t bytes_to_read); | ||||
| uint16_t fs_file_write(File* file, void* buff, uint16_t bytes_to_write); | ||||
| bool fs_file_seek(File* file, uint32_t offset, bool from_start); | ||||
| uint64_t fs_file_tell(File* file); | ||||
| bool fs_file_truncate(File* file); | ||||
| uint64_t fs_file_size(File* file); | ||||
| bool fs_file_sync(File* file); | ||||
| bool fs_file_eof(File* file); | ||||
| 
 | ||||
| /* dir api */ | ||||
| bool fs_dir_open(File* file, const char* path); | ||||
| bool fs_dir_close(File* file); | ||||
| bool fs_dir_read(File* file, FileInfo* fileinfo, char* name, uint16_t name_length); | ||||
| bool fs_dir_rewind(File* file); | ||||
| 
 | ||||
| /* common api */ | ||||
| FS_Error | ||||
| fs_common_info(const char* path, FileInfo* fileinfo, char* name, const uint16_t name_length); | ||||
| FS_Error fs_common_remove(const char* path); | ||||
| FS_Error fs_common_rename(const char* old_path, const char* new_path); | ||||
| FS_Error fs_common_set_attr(const char* path, uint8_t attr, uint8_t mask); | ||||
| FS_Error fs_common_mkdir(const char* path); | ||||
| FS_Error fs_common_set_time(const char* path, FileDateUnion date, FileTimeUnion time); | ||||
| FS_Error fs_get_fs_info(uint64_t* total_space, uint64_t* free_space); | ||||
| 
 | ||||
| /* errors api */ | ||||
| const char* fs_error_get_desc(FS_Error error_id); | ||||
| const char* fs_error_get_internal_desc(uint32_t internal_error_id); | ||||
| @ -47,7 +47,7 @@ | ||||
| /   2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. | ||||
| /   3: f_lseek() function is removed in addition to 2. */ | ||||
| 
 | ||||
| #define _USE_STRFUNC         2      /* 0:Disable or 1-2:Enable */ | ||||
| #define _USE_STRFUNC         0      /* 0:Disable or 1-2:Enable */ | ||||
| /* This option switches string functions, f_gets(), f_putc(), f_puts() and
 | ||||
| /  f_printf(). | ||||
| / | ||||
| @ -68,7 +68,7 @@ | ||||
| #define	_USE_EXPAND		0 | ||||
| /* This option switches f_expand function. (0:Disable or 1:Enable) */ | ||||
| 
 | ||||
| #define _USE_CHMOD		0 | ||||
| #define _USE_CHMOD		1 | ||||
| /* This option switches attribute manipulation functions, f_chmod() and f_utime().
 | ||||
| /  (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */ | ||||
| 
 | ||||
| @ -177,7 +177,7 @@ | ||||
| /  arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() | ||||
| /  funciton will be available. */ | ||||
| #define _MIN_SS    512  /* 512, 1024, 2048 or 4096 */ | ||||
| #define _MAX_SS    4096  /* 512, 1024, 2048 or 4096 */ | ||||
| #define _MAX_SS    512  /* 512, 1024, 2048 or 4096 */ | ||||
| /* These options configure the range of sector size to be supported. (512, 1024,
 | ||||
| /  2048 or 4096) Always set both 512 for most systems, all type of memory cards and | ||||
| /  harddisk. But a larger value may be required for on-board flash memory and some | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| #include "api-hal-gpio.h" | ||||
| #include "api-hal-resources.h" | ||||
| #include "spi.h" | ||||
| 
 | ||||
| // init GPIO
 | ||||
| void hal_gpio_init( | ||||
| @ -20,20 +21,27 @@ void hal_gpio_init( | ||||
| 
 | ||||
| bool hal_gpio_read_sd_detect(void) { | ||||
|     bool result = false; | ||||
|      | ||||
| 
 | ||||
|     // TODO open record
 | ||||
|     const GpioPin* sd_cs_record = &sd_cs_gpio; | ||||
| 
 | ||||
|     // TODO: SPI manager
 | ||||
|     api_hal_spi_lock(&SPI_SD_HANDLE); | ||||
| 
 | ||||
|     // configure pin as input
 | ||||
|     gpio_init_ex(sd_cs_record, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh); | ||||
|     delay(50); | ||||
|     delay(1); | ||||
| 
 | ||||
|     // if gpio_read == 0 return true else return false
 | ||||
|     result = !gpio_read(sd_cs_record); | ||||
| 
 | ||||
|     // configure pin back
 | ||||
|     gpio_init_ex(sd_cs_record, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||
|     delay(50); | ||||
|     gpio_write(sd_cs_record, 1); | ||||
|     delay(1); | ||||
| 
 | ||||
|     // TODO: SPI manager
 | ||||
|     api_hal_spi_unlock(&SPI_SD_HANDLE); | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
|  | ||||
							
								
								
									
										330
									
								
								lib/common-api/filesystem-api.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										330
									
								
								lib/common-api/filesystem-api.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,330 @@ | ||||
| #pragma once | ||||
| #include "flipper.h" | ||||
| #include "flipper_v2.h" | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  *  @brief Access mode flags | ||||
|  */ | ||||
| typedef enum { | ||||
|     FSAM_READ = (1 << 0), /**< Read access */ | ||||
|     FSAM_WRITE = (1 << 1), /**< Write access */ | ||||
| } FS_AccessMode; | ||||
| 
 | ||||
| /**
 | ||||
|  *  @brief Open mode flags | ||||
|  */ | ||||
| typedef enum { | ||||
|     FSOM_OPEN_EXISTING = 1, /**< Open file, fail if file doesn't exist */ | ||||
|     FSOM_OPEN_ALWAYS = 2, /**< Open file. Create new file if not exist */ | ||||
|     FSOM_OPEN_APPEND = 4, /**< Open file. Create new file if not exist. Set R/W pointer to EOF */ | ||||
|     FSOM_CREATE_NEW = 8, /**< Creates a new file. Fails if the file is exist */ | ||||
|     FSOM_CREATE_ALWAYS = 16, /**< Creates a new file. If file exist, truncate to zero size */ | ||||
| } FS_OpenMode; | ||||
| 
 | ||||
| /**
 | ||||
|  *  @brief API errors enumeration | ||||
|  */ | ||||
| typedef enum { | ||||
|     FSE_OK, /**< No error */ | ||||
|     FSE_NOT_READY, /**< FS not ready */ | ||||
|     FSE_EXIST, /**< File/Dir alrady exist */ | ||||
|     FSE_NOT_EXIST, /**< File/Dir does not exist */ | ||||
|     FSE_INVALID_PARAMETER, /**< Invalid API parameter */ | ||||
|     FSE_DENIED, /**< Access denied */ | ||||
|     FSE_INVALID_NAME, /**< Invalid name/path */ | ||||
|     FSE_INTERNAL, /**< Internal error */ | ||||
|     FSE_NOT_IMPLEMENTED, /**< Functon not implemented */ | ||||
| } FS_Error; | ||||
| 
 | ||||
| /**
 | ||||
|  *  @brief FileInfo flags | ||||
|  */ | ||||
| typedef enum { | ||||
|     FSF_READ_ONLY = (1 << 0), /**< Readonly */ | ||||
|     FSF_HIDDEN = (1 << 1), /**< Hidden */ | ||||
|     FSF_SYSTEM = (1 << 2), /**< System */ | ||||
|     FSF_DIRECTORY = (1 << 3), /**< Directory */ | ||||
|     FSF_ARCHIVE = (1 << 4), /**< Archive */ | ||||
| } FS_Flags; | ||||
| 
 | ||||
| /** 
 | ||||
|  *  @brief Structure that hold file index and returned api errors  | ||||
|  */ | ||||
| typedef struct { | ||||
|     uint32_t file_id; /**< File ID for internal references */ | ||||
|     FS_Error error_id; /**< Standart API error from FS_Error enum */ | ||||
|     uint32_t internal_error_id; /**< Internal API error value */ | ||||
| } File; | ||||
| 
 | ||||
| // TODO: solve year 2107 problem
 | ||||
| /** 
 | ||||
|  *  @brief Structure that hold packed date values  | ||||
|  */ | ||||
| typedef struct __attribute__((packed)) { | ||||
|     uint16_t month_day : 5; /**< month day */ | ||||
|     uint16_t month : 4; /**< month index */ | ||||
|     uint16_t year : 7; /**< year, year + 1980 to get actual value */ | ||||
| } FileDate; | ||||
| 
 | ||||
| /** 
 | ||||
|  *  @brief Structure that hold packed time values  | ||||
|  */ | ||||
| typedef struct __attribute__((packed)) { | ||||
|     uint16_t second : 5; /**< second, second * 2 to get actual value  */ | ||||
|     uint16_t minute : 6; /**< minute */ | ||||
|     uint16_t hour : 5; /**< hour */ | ||||
| } FileTime; | ||||
| 
 | ||||
| /** 
 | ||||
|  *  @brief Union of simple date and real value  | ||||
|  */ | ||||
| typedef union { | ||||
|     FileDate simple; /**< simple access to date */ | ||||
|     uint16_t value; /**< real date value */ | ||||
| } FileDateUnion; | ||||
| 
 | ||||
| /** 
 | ||||
|  *  @brief Union of simple time and real value  | ||||
|  */ | ||||
| typedef union { | ||||
|     FileTime simple; /**< simple access to time */ | ||||
|     uint16_t value; /**< real time value */ | ||||
| } FileTimeUnion; | ||||
| 
 | ||||
| /** 
 | ||||
|  *  @brief Structure that hold file info | ||||
|  */ | ||||
| typedef struct { | ||||
|     uint8_t flags; /**< flags from FS_Flags enum */ | ||||
|     uint64_t size; /**< file size */ | ||||
|     FileDateUnion date; /**< file date */ | ||||
|     FileTimeUnion time; /**< file time */ | ||||
| } FileInfo; | ||||
| 
 | ||||
| /** @struct FS_File_Api
 | ||||
|  *  @brief File api structure | ||||
|  *  | ||||
|  *  @var FS_File_Api::open | ||||
|  *      @brief Open file | ||||
|  *      @param file pointer to file object, filled by api | ||||
|  *      @param path path to file  | ||||
|  *      @param access_mode access mode from FS_AccessMode  | ||||
|  *      @param open_mode open mode from FS_OpenMode  | ||||
|  *      @return success flag | ||||
|  *  | ||||
|  *  @var FS_File_Api::close  | ||||
|  *      @brief Close file | ||||
|  *      @param file pointer to file object | ||||
|  *      @return success flag | ||||
|  *  | ||||
|  *  @var FS_File_Api::read | ||||
|  *      @brief Read bytes from file to buffer | ||||
|  *      @param file pointer to file object | ||||
|  *      @param buff pointer to buffer for reading | ||||
|  *      @param bytes_to_read how many bytes to read, must be smaller or equal to buffer size  | ||||
|  *      @return how many bytes actually has been readed | ||||
|  *  | ||||
|  *  @var FS_File_Api::write | ||||
|  *      @brief Write bytes from buffer to file | ||||
|  *      @param file pointer to file object | ||||
|  *      @param buff pointer to buffer for writing | ||||
|  *      @param bytes_to_read how many bytes to write, must be smaller or equal to buffer size  | ||||
|  *      @return how many bytes actually has been writed | ||||
|  *  | ||||
|  *  @var FS_File_Api::seek | ||||
|  *      @brief Move r/w pointer  | ||||
|  *      @param file pointer to file object | ||||
|  *      @param offset offset to move r/w pointer | ||||
|  *      @param from_start set offset from start, or from current position | ||||
|  *      @return success flag | ||||
|  *  | ||||
|  *  @var FS_File_Api::tell | ||||
|  *      @brief Get r/w pointer position | ||||
|  *      @param file pointer to file object | ||||
|  *      @return current r/w pointer position | ||||
|  *  | ||||
|  *  @var FS_File_Api::truncate | ||||
|  *      @brief Truncate file size to current r/w pointer position | ||||
|  *      @param file pointer to file object | ||||
|  *      @return success flag | ||||
|  *  | ||||
|  *  @var FS_File_Api::size | ||||
|  *      @brief Fet file size | ||||
|  *      @param file pointer to file object | ||||
|  *      @return file size | ||||
|  *  | ||||
|  *  @var FS_File_Api::sync | ||||
|  *      @brief Write file cache to storage | ||||
|  *      @param file pointer to file object | ||||
|  *      @return success flag | ||||
|  *  | ||||
|  *  @var FS_File_Api::eof | ||||
|  *      @brief Checks that the r/w pointer is at the end of the file | ||||
|  *      @param file pointer to file object | ||||
|  *      @return end of file flag | ||||
|  */ | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief File api structure | ||||
|  */ | ||||
| typedef struct { | ||||
|     bool (*open)(File* file, const char* path, FS_AccessMode access_mode, FS_OpenMode open_mode); | ||||
|     bool (*close)(File* file); | ||||
|     uint16_t (*read)(File* file, void* buff, uint16_t bytes_to_read); | ||||
|     uint16_t (*write)(File* file, void* buff, uint16_t bytes_to_write); | ||||
|     bool (*seek)(File* file, uint32_t offset, bool from_start); | ||||
|     uint64_t (*tell)(File* file); | ||||
|     bool (*truncate)(File* file); | ||||
|     uint64_t (*size)(File* file); | ||||
|     bool (*sync)(File* file); | ||||
|     bool (*eof)(File* file); | ||||
| } FS_File_Api; | ||||
| 
 | ||||
| /** @struct FS_Dir_Api
 | ||||
|  *  @brief Dir api structure | ||||
|  *  | ||||
|  *  @var FS_Dir_Api::open | ||||
|  *      @brief Open directory to get objects from | ||||
|  *      @param file pointer to file object, filled by api | ||||
|  *      @param path path to directory  | ||||
|  *      @return success flag | ||||
|  *  | ||||
|  *  @var FS_Dir_Api::close  | ||||
|  *      @brief Close directory | ||||
|  *      @param file pointer to file object | ||||
|  *      @return success flag | ||||
|  *  | ||||
|  *  @var FS_Dir_Api::read | ||||
|  *      @brief Read next object info in directory | ||||
|  *      @param file pointer to file object | ||||
|  *      @param fileinfo pointer to readed FileInfo, can be NULL | ||||
|  *      @param name pointer to name buffer, can be NULL | ||||
|  *      @param name_length name buffer length | ||||
|  *      @return success flag (if next object not exist also returns false and set error_id to FSE_NOT_EXIST) | ||||
|  *  | ||||
|  *  @var FS_Dir_Api::rewind | ||||
|  *      @brief Rewind to first object info in directory | ||||
|  *      @param file pointer to file object | ||||
|  *      @return success flag | ||||
|  */ | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Dir api structure | ||||
|  */ | ||||
| typedef struct { | ||||
|     bool (*open)(File* file, const char* path); | ||||
|     bool (*close)(File* file); | ||||
|     bool (*read)(File* file, FileInfo* fileinfo, char* name, uint16_t name_length); | ||||
|     bool (*rewind)(File* file); | ||||
| } FS_Dir_Api; | ||||
| 
 | ||||
| /** @struct FS_Common_Api
 | ||||
|  *  @brief Common api structure | ||||
|  *  | ||||
|  *  @var FS_Common_Api::info | ||||
|  *      @brief Open directory to get objects from | ||||
|  *      @param path path to file/directory | ||||
|  *      @param fileinfo pointer to readed FileInfo, can be NULL | ||||
|  *      @param name pointer to name buffer, can be NULL | ||||
|  *      @param name_length name buffer length | ||||
|  *      @return FS_Error error info | ||||
|  *  | ||||
|  *  @var FS_Common_Api::remove | ||||
|  *      @brief Remove file/directory from storage,  | ||||
|  *          directory must be empty, | ||||
|  *          file/directory must not be opened, | ||||
|  *          file/directory must not have FSF_READ_ONLY flag | ||||
|  *      @param path path to file/directory | ||||
|  *      @return FS_Error error info | ||||
|  *  | ||||
|  *  @var FS_Common_Api::rename | ||||
|  *      @brief Rename file/directory, | ||||
|  *          file/directory must not be opened | ||||
|  *      @param path path to file/directory | ||||
|  *      @return FS_Error error info | ||||
|  *  | ||||
|  *  @var FS_Common_Api::set_attr | ||||
|  *      @brief Set attributes of file/directory,  | ||||
|  *          for example: | ||||
|  *          @code | ||||
|  *          set "read only" flag and remove "hidden" flag | ||||
|  *          set_attr("file.txt", FSF_READ_ONLY, FSF_READ_ONLY | FSF_HIDDEN); | ||||
|  *          @endcode | ||||
|  *      @param path path to file/directory | ||||
|  *      @param attr attribute values consist of FS_Flags | ||||
|  *      @param mask attribute mask consist of FS_Flags | ||||
|  *      @return FS_Error error info | ||||
|  *  | ||||
|  *  @var FS_Common_Api::mkdir | ||||
|  *      @brief Create new directory | ||||
|  *      @param path path to new directory | ||||
|  *      @return FS_Error error info | ||||
|  *  | ||||
|  *  @var FS_Common_Api::set_time | ||||
|  *      @brief Set file/directory modification time | ||||
|  *      @param path path to file/directory | ||||
|  *      @param date modification date  | ||||
|  *      @param time modification time | ||||
|  *      @see FileDateUnion | ||||
|  *      @see FileTimeUnion | ||||
|  *      @return FS_Error error info | ||||
|  *  | ||||
|  *  @var FS_Common_Api::get_fs_info | ||||
|  *      @brief Get total and free space storage values | ||||
|  *      @param total_space pointer to total space value | ||||
|  *      @param free_space pointer to free space value | ||||
|  *      @return FS_Error error info | ||||
|  */ | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Common api structure | ||||
|  */ | ||||
| typedef struct { | ||||
|     FS_Error (*info)(const char* path, FileInfo* fileinfo, char* name, const uint16_t name_length); | ||||
|     FS_Error (*remove)(const char* path); | ||||
|     FS_Error (*rename)(const char* old_path, const char* new_path); | ||||
|     FS_Error (*set_attr)(const char* path, uint8_t attr, uint8_t mask); | ||||
|     FS_Error (*mkdir)(const char* path); | ||||
|     FS_Error (*set_time)(const char* path, FileDateUnion date, FileTimeUnion time); | ||||
|     FS_Error (*get_fs_info)(uint64_t* total_space, uint64_t* free_space); | ||||
| } FS_Common_Api; | ||||
| 
 | ||||
| /** @struct FS_Error_Api
 | ||||
|  *  @brief Errors api structure | ||||
|  *  | ||||
|  *  @var FS_Error_Api::get_desc | ||||
|  *      @brief Get error description text | ||||
|  *      @param error_id FS_Error error id (for fire/dir functions result can be obtained from File.error_id) | ||||
|  *      @return pointer to description text | ||||
|  *  | ||||
|  *  @var FS_Error_Api::get_internal_desc | ||||
|  *      @brief Get internal error description text | ||||
|  *      @param internal_error_id error id (for fire/dir functions result can be obtained from File.internal_error_id) | ||||
|  *      @return pointer to description text | ||||
|  */ | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Errors api structure | ||||
|  */ | ||||
| typedef struct { | ||||
|     const char* (*get_desc)(FS_Error error_id); | ||||
|     const char* (*get_internal_desc)(uint32_t internal_error_id); | ||||
| } FS_Error_Api; | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Full filesystem api structure | ||||
|  */ | ||||
| typedef struct { | ||||
|     FS_File_Api file; | ||||
|     FS_Dir_Api dir; | ||||
|     FS_Common_Api common; | ||||
|     FS_Error_Api error; | ||||
| } FS_Api; | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| @ -5,33 +5,33 @@ | ||||
| #ifndef _FF_INTEGER | ||||
| #define _FF_INTEGER | ||||
| 
 | ||||
| #ifdef _WIN32	/* FatFs development platform */ | ||||
| #ifdef _WIN32 /* FatFs development platform */ | ||||
| 
 | ||||
| #include <windows.h> | ||||
| #include <tchar.h> | ||||
| typedef unsigned __int64 QWORD; | ||||
| 
 | ||||
| 
 | ||||
| #else			/* Embedded platform */ | ||||
| #else /* Embedded platform */ | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| /* These types MUST be 16-bit or 32-bit */ | ||||
| typedef int				INT; | ||||
| typedef unsigned int	UINT; | ||||
| typedef int16_t INT; | ||||
| typedef uint16_t UINT; | ||||
| 
 | ||||
| /* This type MUST be 8-bit */ | ||||
| typedef unsigned char	BYTE; | ||||
| typedef uint8_t BYTE; | ||||
| 
 | ||||
| /* These types MUST be 16-bit */ | ||||
| typedef short			SHORT; | ||||
| typedef unsigned short	WORD; | ||||
| typedef unsigned short	WCHAR; | ||||
| typedef int16_t SHORT; | ||||
| typedef uint16_t WORD; | ||||
| typedef uint16_t WCHAR; | ||||
| 
 | ||||
| /* These types MUST be 32-bit */ | ||||
| typedef long			LONG; | ||||
| typedef unsigned long	DWORD; | ||||
| typedef int32_t LONG; | ||||
| typedef uint32_t DWORD; | ||||
| 
 | ||||
| /* This type MUST be 64-bit (Remove this for ANSI C (C89) compatibility) */ | ||||
| typedef unsigned long long QWORD; | ||||
| typedef uint64_t QWORD; | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
|  | ||||
| @ -70,6 +70,9 @@ CFLAGS			+= -I$(CYFRAL_DIR) | ||||
| CPP_SOURCES		+= $(wildcard $(CYFRAL_DIR)/*.cpp) | ||||
| endif | ||||
| 
 | ||||
| # common apps api
 | ||||
| CFLAGS			+= -I$(LIB_DIR)/common-api | ||||
| 
 | ||||
| # drivers
 | ||||
| ifneq ($(TARGET), local) | ||||
| ifneq ($(TARGET), f2) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrZlo13
						DrZlo13