diff --git a/applications/gui/modules/file_select.c b/applications/gui/modules/file_select.c index 9d2172ba..55c18174 100644 --- a/applications/gui/modules/file_select.c +++ b/applications/gui/modules/file_select.c @@ -8,13 +8,16 @@ struct FileSelect { // public View* view; FS_Api* fs_api; - char* path; - char* extension; + const char* path; + const char* extension; bool init_completed; FileSelectCallback callback; void* context; + + char* buffer; + uint8_t buffer_size; }; typedef struct { @@ -28,7 +31,7 @@ typedef struct { bool file_select_fill_strings(FileSelect* file_select); bool file_select_fill_count(FileSelect* file_select); -static bool file_select_init(FileSelect* file_select); +static bool file_select_init_inner(FileSelect* file_select); static void file_select_draw_callback(Canvas* canvas, void* _model) { FileSelectModel* model = _model; @@ -66,8 +69,8 @@ static bool file_select_input_callback(InputEvent* event, void* context) { if(event->type == InputTypeShort) { if(!file_select->init_completed) { - if(!file_select_init(file_select)) { - file_select->callback(NULL, file_select->context); + if(!file_select_init_inner(file_select)) { + file_select->callback(false, file_select->context); } } else if(event->key == InputKeyUp) { with_view_model( @@ -127,20 +130,24 @@ static bool file_select_input_callback(InputEvent* event, void* context) { return false; }); - file_select->callback(result, file_select->context); + if(file_select->buffer) { + strlcpy(file_select->buffer, result, file_select->buffer_size); + }; + + file_select->callback(true, file_select->context); } consumed = true; } if(!file_select_fill_strings(file_select)) { - file_select->callback(NULL, file_select->context); + file_select->callback(false, file_select->context); } } return consumed; } -static bool file_select_init(FileSelect* file_select) { +static bool file_select_init_inner(FileSelect* file_select) { bool result = false; if(file_select->path && file_select->extension && file_select->fs_api) { if(file_select_fill_count(file_select)) { @@ -162,13 +169,6 @@ FileSelect* file_select_alloc() { view_set_draw_callback(file_select->view, file_select_draw_callback); view_set_input_callback(file_select->view, file_select_input_callback); - file_select->fs_api = NULL; - file_select->path = NULL; - file_select->extension = NULL; - file_select->init_completed = false; - file_select->callback = NULL; - file_select->context = NULL; - with_view_model( file_select->view, (FileSelectModel * model) { for(uint8_t i = 0; i < FILENAME_COUNT; i++) { @@ -209,12 +209,30 @@ void file_select_set_api(FileSelect* file_select, FS_Api* fs_api) { void file_select_set_callback(FileSelect* file_select, FileSelectCallback callback, void* context) { } -void file_select_set_filter(FileSelect* file_select, char* path, char* extension) { +void file_select_set_filter(FileSelect* file_select, const char* path, const char* extension) { furi_assert(file_select); file_select->path = path; file_select->extension = extension; } +void file_select_set_result_buffer(FileSelect* file_select, char* buffer, uint8_t buffer_size) { + file_select->buffer = buffer; + file_select->buffer_size = buffer_size; + + if(file_select->buffer) { + strlcpy(file_select->buffer, "", file_select->buffer_size); + } +} + +bool file_select_init(FileSelect* file_select) { + if(!file_select_init_inner(file_select)) { + file_select->callback(false, file_select->context); + return false; + } else { + return true; + } +} + static bool filter_file(FileSelect* file_select, FileInfo* file_info, char* name) { bool result = false; diff --git a/applications/gui/modules/file_select.h b/applications/gui/modules/file_select.h index 224ca5ef..8f52771a 100644 --- a/applications/gui/modules/file_select.h +++ b/applications/gui/modules/file_select.h @@ -8,7 +8,7 @@ extern "C" { typedef struct FileSelect FileSelect; -typedef void (*FileSelectCallback)(const char* result, void* context); +typedef void (*FileSelectCallback)(bool result, void* context); FileSelect* file_select_alloc(); @@ -17,7 +17,9 @@ View* file_select_get_view(FileSelect* file_select); void file_select_set_api(FileSelect* file_select, FS_Api* fs_api); void file_select_set_callback(FileSelect* file_select, FileSelectCallback callback, void* context); -void file_select_set_filter(FileSelect* file_select, char* path, char* extension); +void file_select_set_filter(FileSelect* file_select, const char* path, const char* extension); +void file_select_set_result_buffer(FileSelect* file_select, char* buffer, uint8_t buffer_size); +bool file_select_init(FileSelect* file_select); #ifdef __cplusplus } diff --git a/applications/gui/view_dispatcher.h b/applications/gui/view_dispatcher.h index 71fb5ff1..41d41871 100644 --- a/applications/gui/view_dispatcher.h +++ b/applications/gui/view_dispatcher.h @@ -7,47 +7,55 @@ extern "C" { #endif -/* ViewDispatcher view_port placement */ +/** + * @brief ViewDispatcher view_port placement + */ typedef enum { - ViewDispatcherTypeNone, /* Special layer for internal use only */ - ViewDispatcherTypeWindow, /* Main view_port layer, status bar is shown */ - ViewDispatcherTypeFullscreen /* Fullscreen view_port layer */ + ViewDispatcherTypeNone, /**< Special layer for internal use only */ + ViewDispatcherTypeWindow, /**< Main view_port layer, status bar is shown */ + ViewDispatcherTypeFullscreen /**< Fullscreen view_port layer */ } ViewDispatcherType; typedef struct ViewDispatcher ViewDispatcher; -/* Allocate ViewDispatcher +/** + * @brief Allocate ViewDispatcher * @return pointer to ViewDispatcher instance */ ViewDispatcher* view_dispatcher_alloc(); -/* Free ViewDispatcher - * @param pointer to View +/** + * @brief Free ViewDispatcher + * @param view_dispatcher pointer to ViewDispatcher */ void view_dispatcher_free(ViewDispatcher* view_dispatcher); -/* Add view to ViewDispatcher +/** + * @brief Add view to ViewDispatcher * @param view_dispatcher, ViewDispatcher instance - * @param view_id, View id to register - * @param view, View instance + * @param view_id View id to register + * @param view View instance */ void view_dispatcher_add_view(ViewDispatcher* view_dispatcher, uint32_t view_id, View* view); -/* Remove view from ViewDispatcher - * @param view_dispatcher, ViewDispatcher instance - * @param view_id, View id to remove +/** + * @brief Remove view from ViewDispatcher + * @param view_dispatcher ViewDispatcher instance + * @param view_id View id to remove */ void view_dispatcher_remove_view(ViewDispatcher* view_dispatcher, uint32_t view_id); -/* Switch to View - * @param view_dispatcher, ViewDispatcher instance - * @param view_id, View id to register +/** + * @brief Switch to View + * @param view_dispatcher ViewDispatcher instance + * @param view_id View id to register */ void view_dispatcher_switch_to_view(ViewDispatcher* view_dispatcher, uint32_t view_id); -/* Attach ViewDispatcher to GUI - * @param view_dispatcher, ViewDispatcher instance - * @param gui, GUI instance to attach to +/** + * @brief Attach ViewDispatcher to GUI + * @param view_dispatcher ViewDispatcher instance + * @param gui GUI instance to attach to */ void view_dispatcher_attach_to_gui( ViewDispatcher* view_dispatcher, diff --git a/applications/sd-filesystem/sd-filesystem-api.c b/applications/sd-filesystem/sd-filesystem-api.c index c63b7c51..26dceaf1 100644 --- a/applications/sd-filesystem/sd-filesystem-api.c +++ b/applications/sd-filesystem/sd-filesystem-api.c @@ -27,10 +27,12 @@ bool _fs_init(SdFsInfo* _fs_info) { } bool _fs_lock(SdFsInfo* fs_info) { + api_hal_power_insomnia_enter(); return (osMutexAcquire(fs_info->mutex, osWaitForever) == osOK); } bool _fs_unlock(SdFsInfo* fs_info) { + api_hal_power_insomnia_exit(); return (osMutexRelease(fs_info->mutex) == osOK); } diff --git a/applications/sd-filesystem/sd-filesystem.c b/applications/sd-filesystem/sd-filesystem.c index b01aecf2..6f96c094 100644 --- a/applications/sd-filesystem/sd-filesystem.c +++ b/applications/sd-filesystem/sd-filesystem.c @@ -6,6 +6,65 @@ #include "cli/cli.h" #include "api-hal-sd.h" +#include +#include + +typedef enum { + FST_FAT12 = FS_FAT12, + FST_FAT16 = FS_FAT16, + FST_FAT32 = FS_FAT32, + FST_EXFAT = FS_EXFAT, +} SDFsType; + +typedef struct { + SDFsType fs_type; + uint32_t kb_total; + uint32_t kb_free; + uint16_t cluster_size; + uint16_t sector_size; + char label[34]; + SDError error; +} SDInfo; + +typedef enum { + SdAppEventTypeBack, + SdAppEventTypeOK, + SdAppEventTypeFormat, + SdAppEventTypeInfo, + SdAppEventTypeEject, + SdAppEventTypeFileSelect, + SdAppEventTypeCheckError, +} SdAppEventType; + +typedef struct { + const char* path; + const char* extension; + char* result; + uint8_t result_size; +} SdAppFileSelectData; + +typedef struct { + bool result; +} SdAppFileSelectResultEvent; + +typedef struct { + SdAppEventType type; + osMessageQueueId_t result_receiver; + union { + SdAppFileSelectData file_select_data; + } payload; +} SdAppEvent; + +static void sd_icon_draw_callback(Canvas* canvas, void* context); +bool sd_api_file_select( + SdApp* sd_app, + const char* path, + const char* extension, + char* result, + uint8_t result_size); + +/******************* Allocators *******************/ + FS_Api* fs_api_alloc() { FS_Api* fs_api = furi_alloc(sizeof(FS_Api)); @@ -43,77 +102,13 @@ FS_Api* fs_api_alloc() { 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 furi_check(_fs_init(&sd_app->info)); - sd_app->event_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL); - - // init view_port - sd_app->view_port = view_port_alloc(); - view_port_draw_callback_set(sd_app->view_port, sd_app_draw_callback, sd_app); - view_port_input_callback_set(sd_app->view_port, sd_app_input_callback, sd_app); - view_port_enabled_set(sd_app->view_port, false); - - // init lines - sd_set_lines(sd_app, 0); + sd_app->event_queue = osMessageQueueNew(8, sizeof(SdAppEvent), NULL); // init icon view_port sd_app->icon.view_port = view_port_alloc(); @@ -123,143 +118,68 @@ SdApp* sd_app_alloc() { view_port_draw_callback_set(sd_app->icon.view_port, sd_icon_draw_callback, sd_app); view_port_enabled_set(sd_app->icon.view_port, false); + // init sd card api + sd_app->sd_card_api.context = sd_app; + sd_app->sd_card_api.file_select = sd_api_file_select; + sd_app->sd_app_state = SdAppStateBackground; + string_init(sd_app->text_holder); + return sd_app; } -bool app_sd_ask(SdApp* sd_app, InputKey input_true, InputKey input_false) { - bool result; +/******************* Internal sd card related fns *******************/ - InputEvent event; - while(1) { - osStatus_t event_status = - osMessageQueueGet(sd_app->event_queue, &event, NULL, osWaitForever); - - if(event_status == osOK) { - if(event.type == InputTypeShort && event.key == input_true) { - result = true; - break; - } - if(event.type == InputTypeShort && event.key == InputKeyBack) { - result = false; - break; - } - } - } - - return result; -} - -void app_sd_info_callback(void* context) { - furi_assert(context); - SdApp* sd_app = context; - view_port_enabled_set(sd_app->view_port, 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; +void get_sd_info(SdApp* sd_app, SDInfo* sd_info) { uint32_t free_clusters, free_sectors, total_sectors; - char volume_label[34]; + FATFS* fs; - // 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 { - str_buffer[i][0] = 0; - } + // clean data + memset(sd_info, 0, sizeof(SDInfo)); + + // get fs info + _fs_lock(&sd_app->info); + sd_info->error = f_getlabel(sd_app->info.path, sd_info->label, NULL); + if(sd_info->error == SD_OK) { + sd_info->error = f_getfree(sd_app->info.path, &free_clusters, &fs); } + _fs_unlock(&sd_app->info); - 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); - + if(sd_info->error == SD_OK) { // 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); + sd_info->fs_type = fs->fs_type; - 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]); + sd_info->kb_total = total_sectors / 1024 * sector_size; + sd_info->kb_free = free_sectors / 1024 * sector_size; + sd_info->cluster_size = fs->csize; + sd_info->sector_size = sector_size; } +} - app_sd_ask(sd_app, InputKeyBack, InputKeyBack); - - sd_set_lines(sd_app, 0); - view_port_enabled_set(sd_app->view_port, false); - - for(uint8_t i = 0; i < str_count; i++) { - free(str_buffer[i]); +const char* get_fs_type_text(SDFsType fs_type) { + switch(fs_type) { + case(FST_FAT12): + return "FAT12"; + break; + case(FST_FAT16): + return "FAT16"; + break; + case(FST_FAT32): + return "FAT32"; + break; + case(FST_EXFAT): + return "EXFAT"; + break; + default: + return "UNKNOWN"; + break; } } @@ -284,39 +204,6 @@ void app_sd_format_internal(SdApp* sd_app) { _fs_unlock(&sd_app->info); } -void app_sd_format_callback(void* context) { - furi_assert(context); - SdApp* sd_app = context; - - // ask to really format - sd_set_lines(sd_app, 2, "Press UP to format", "or BACK to exit"); - view_port_enabled_set(sd_app->view_port, true); - - // wait for input - if(!app_sd_ask(sd_app, InputKeyUp, InputKeyBack)) { - view_port_enabled_set(sd_app->view_port, false); - return; - } - - // show warning - sd_set_lines(sd_app, 3, "formatting SD card", "procedure can be lengthy", "please wait"); - - // format card - app_sd_format_internal(sd_app); - - 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"); - } - - // wait for BACK - app_sd_ask(sd_app, InputKeyBack, InputKeyBack); - - view_port_enabled_set(sd_app->view_port, false); -} - void app_sd_notify_wait_on() { api_hal_light_set(LightRed, 0xFF); api_hal_light_set(LightBlue, 0xFF); @@ -433,23 +320,149 @@ void app_sd_unmount_card(SdApp* sd_app) { _fs_unlock(&sd_app->info); } -void app_sd_eject_callback(void* context) { +bool app_sd_make_path(const char* path) { + furi_assert(path); + + if(*path) { + char* file_path = strdup(path); + + for(char* p = strchr(file_path + 1, '/'); p; p = strchr(p + 1, '/')) { + *p = '\0'; + SDError result = f_mkdir(file_path); + + if(result != SD_OK) { + if(result != SD_EXIST) { + *p = '/'; + free(file_path); + return false; + } + } + *p = '/'; + } + + free(file_path); + } + + return true; +} + +/******************* Draw callbacks *******************/ + +static void sd_icon_draw_callback(Canvas* canvas, void* context) { + furi_assert(canvas); furi_assert(context); SdApp* sd_app = context; - sd_set_lines(sd_app, 1, "ejecting SD card"); - view_port_enabled_set(sd_app->view_port, 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, InputKeyBack, InputKeyBack); - - view_port_enabled_set(sd_app->view_port, false); + 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; + } } +/******************* SD-api callbacks *******************/ + +bool sd_api_file_select( + SdApp* sd_app, + const char* path, + const char* extension, + char* result, + uint8_t result_size) { + bool retval = false; + + osMessageQueueId_t return_event_queue = + osMessageQueueNew(1, sizeof(SdAppFileSelectResultEvent), NULL); + + SdAppEvent message = { + .type = SdAppEventTypeFileSelect, + .result_receiver = return_event_queue, + .payload = { + .file_select_data = { + .path = path, + .extension = extension, + .result = result, + .result_size = result_size}}}; + + furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); + + SdAppFileSelectResultEvent event; + while(1) { + osStatus_t event_status = + osMessageQueueGet(sd_app->event_queue, &event, NULL, osWaitForever); + if(event_status == osOK) { + retval = event.result; + break; + } + } + + return retval; +} + +/******************* View callbacks *******************/ + +void app_view_back_callback(void* context) { + furi_assert(context); + SdApp* sd_app = context; + SdAppEvent message = {.type = SdAppEventTypeBack}; + furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); +} + +void app_view_dialog_callback(DialogExResult result, void* context) { + furi_assert(context); + SdApp* sd_app = context; + + if(result == DialogExResultLeft) { + SdAppEvent message = {.type = SdAppEventTypeBack}; + furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); + } else if(result == DialogExResultRight) { + SdAppEvent message = {.type = SdAppEventTypeOK}; + furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); + } +} + +void app_view_file_select_callback(bool result, void* context) { + furi_assert(context); + SdApp* sd_app = context; + + if(result) { + SdAppEvent message = {.type = SdAppEventTypeOK}; + furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); + } else { + SdAppEvent message = {.type = SdAppEventTypeBack}; + furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); + } +} + +/******************* Menu callbacks *******************/ + +void app_sd_info_callback(void* context) { + furi_assert(context); + SdApp* sd_app = context; + SdAppEvent message = {.type = SdAppEventTypeInfo}; + furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); +} + +void app_sd_format_callback(void* context) { + furi_assert(context); + SdApp* sd_app = context; + SdAppEvent message = {.type = SdAppEventTypeFormat}; + furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); +} + +void app_sd_eject_callback(void* context) { + furi_assert(context); + SdApp* sd_app = context; + SdAppEvent message = {.type = SdAppEventTypeEject}; + furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); +} + +/******************* Cli callbacks *******************/ + static void cli_sd_status(string_t args, void* _ctx) { SdApp* sd_app = (SdApp*)_ctx; @@ -477,93 +490,81 @@ static void cli_sd_format(string_t args, void* _ctx) { static void cli_sd_info(string_t args, void* _ctx) { SdApp* sd_app = (SdApp*)_ctx; + SDInfo sd_info; - const uint8_t str_buffer_size = 64; - char str_buffer[str_buffer_size]; + get_sd_info(sd_app, &sd_info); - // 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]; - - // 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) { - 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, str_buffer_size, "Label: %s\r\n", volume_label); - printf(str_buffer); - - snprintf(str_buffer, str_buffer_size, "%s, S/N: %lu\r\n", fs_type, serial_num); - printf(str_buffer); - - snprintf(str_buffer, str_buffer_size, "Cluster: %d sectors\r\n", fs->csize); - printf(str_buffer); - - snprintf(str_buffer, str_buffer_size, "Sector: %d bytes\r\n", sector_size); - printf(str_buffer); - - snprintf( - str_buffer, str_buffer_size, "%lu KB total\r\n", total_sectors / 1024 * sector_size); - printf(str_buffer); - - snprintf( - str_buffer, str_buffer_size, "%lu KB free\r\n", free_sectors / 1024 * sector_size); - printf(str_buffer); + if(sd_info.error == SD_OK) { + const char* fs_type = get_fs_type_text(sd_info.fs_type); + printf("Label: %s\r\n", sd_info.label); + printf("%s\r\n", fs_type); + printf("Cluster: %d sectors\r\n", sd_info.cluster_size); + printf("Sector: %d bytes\r\n", sd_info.sector_size); + printf("%lu KB total\r\n", sd_info.kb_total); + printf("%lu KB free\r\n", sd_info.kb_free); } else { - printf("SD status error: "); - snprintf( - str_buffer, - str_buffer_size, - "%s\r\n", - fs_error_get_internal_desc(_fs_status(&sd_app->info))); - printf(str_buffer); - - printf("Label error: "); - snprintf( - str_buffer, str_buffer_size, "%s\r\n", fs_error_get_internal_desc(get_label_result)); - printf(str_buffer); - - printf("Get free error: "); - snprintf( - str_buffer, str_buffer_size, "%s\r\n", fs_error_get_internal_desc(get_free_result)); - printf(str_buffer); + printf("SD status error: %s\r\n", fs_error_get_internal_desc(_fs_status(&sd_app->info))); + printf("SD info error: %s\r\n", fs_error_get_internal_desc(sd_info.error)); } } +/******************* Test *******************/ + +bool try_to_alloc_view_holder(SdApp* sd_app, Gui* gui) { + bool result = false; + + _fs_lock(&sd_app->info); + + if(sd_app->view_holder == NULL) { + sd_app->view_holder = view_holder_alloc(); + view_holder_attach_to_gui(sd_app->view_holder, gui); + view_holder_set_back_callback(sd_app->view_holder, app_view_back_callback, sd_app); + result = true; + } + + _fs_unlock(&sd_app->info); + + return result; +} + +DialogEx* alloc_and_attach_dialog(SdApp* sd_app) { + DialogEx* dialog = dialog_ex_alloc(); + dialog_ex_set_context(dialog, sd_app); + dialog_ex_set_result_callback(dialog, app_view_dialog_callback); + view_holder_set_view(sd_app->view_holder, dialog_ex_get_view(dialog)); + view_holder_set_free_callback(sd_app->view_holder, (FreeCallback)dialog_ex_free, dialog); + return dialog; +} + +FileSelect* alloc_and_attach_file_select(SdApp* sd_app) { + FileSelect* file_select = file_select_alloc(); + file_select_set_callback(file_select, app_view_file_select_callback, sd_app); + view_holder_set_view(sd_app->view_holder, file_select_get_view(file_select)); + view_holder_set_free_callback( + sd_app->view_holder, (FreeCallback)file_select_free, file_select); + return file_select; +} + +void free_view_holder(SdApp* sd_app) { + _fs_lock(&sd_app->info); + + if(sd_app->view_holder) { + view_holder_free(sd_app->view_holder); + sd_app->view_holder = NULL; + } + + _fs_unlock(&sd_app->info); +} + +void app_reset_state(SdApp* sd_app) { + view_holder_stop(sd_app->view_holder); + free_view_holder(sd_app); + string_set_str(sd_app->text_holder, ""); + sd_app->sd_app_state = SdAppStateBackground; +} + +/******************* Main app *******************/ + int32_t sd_filesystem(void* p) { SdApp* sd_app = sd_app_alloc(); FS_Api* fs_api = fs_api_alloc(); @@ -572,7 +573,6 @@ int32_t sd_filesystem(void* p) { Cli* cli = furi_record_open("cli"); ValueMutex* menu_vm = furi_record_open("menu"); - gui_add_view_port(gui, sd_app->view_port, GuiLayerFullscreen); gui_add_view_port(gui, sd_app->icon.view_port, GuiLayerStatusBarLeft); cli_add_command(cli, "sd_status", cli_sd_status, sd_app); @@ -603,6 +603,7 @@ int32_t sd_filesystem(void* p) { // add api record furi_record_create("sdcard", fs_api); + furi_record_create("sdcard-ex", &sd_app->sd_card_api); // sd card cycle bool sd_was_present = true; @@ -648,7 +649,184 @@ int32_t sd_filesystem(void* p) { } } - delay(1000); + SdAppEvent event; + osStatus_t event_status = osMessageQueueGet(sd_app->event_queue, &event, NULL, 1000); + + const uint8_t y_1_line = 32; + const uint8_t y_2_line = 32; + const uint8_t y_4_line = 26; + + if(event_status == osOK) { + switch(event.type) { + case SdAppEventTypeOK: + switch(sd_app->sd_app_state) { + case SdAppStateFormat: { + DialogEx* dialog = view_holder_get_free_context(sd_app->view_holder); + dialog_ex_set_left_button_text(dialog, NULL); + dialog_ex_set_right_button_text(dialog, NULL); + dialog_ex_set_header( + dialog, "Formatting...", 64, y_1_line, AlignCenter, AlignCenter); + dialog_ex_set_text(dialog, NULL, 0, 0, AlignCenter, AlignCenter); + sd_app->sd_app_state = SdAppStateFormatInProgress; + delay(100); + app_sd_format_internal(sd_app); + app_sd_notify_success(); + dialog_ex_set_left_button_text(dialog, "Back"); + dialog_ex_set_header( + dialog, "SD card formatted", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_text( + dialog, "Press back to return", 64, y_1_line, AlignCenter, AlignCenter); + sd_app->sd_app_state = SdAppStateFormatCompleted; + }; break; + case SdAppStateEject: { + DialogEx* dialog = view_holder_get_free_context(sd_app->view_holder); + dialog_ex_set_right_button_text(dialog, NULL); + dialog_ex_set_header( + dialog, "SD card ejected", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_text( + dialog, + "Now the SD card\ncan be removed.", + 64, + y_2_line, + AlignCenter, + AlignCenter); + sd_app->sd_app_state = SdAppStateEjected; + app_sd_unmount_card(sd_app); + app_sd_notify_eject(); + }; break; + case SdAppStateFileSelect: { + SdAppFileSelectResultEvent retval = {.result = true}; + furi_check( + osMessageQueuePut(event.result_receiver, &retval, 0, osWaitForever) == + osOK); + app_reset_state(sd_app); + }; break; + default: + break; + } + break; + case SdAppEventTypeBack: + switch(sd_app->sd_app_state) { + case SdAppStateFormatInProgress: + break; + case SdAppStateFileSelect: { + SdAppFileSelectResultEvent retval = {.result = false}; + furi_check( + osMessageQueuePut(event.result_receiver, &retval, 0, osWaitForever) == + osOK); + app_reset_state(sd_app); + }; break; + + default: + app_reset_state(sd_app); + break; + } + break; + case SdAppEventTypeFormat: + if(try_to_alloc_view_holder(sd_app, gui)) { + DialogEx* dialog = alloc_and_attach_dialog(sd_app); + dialog_ex_set_left_button_text(dialog, "Back"); + dialog_ex_set_right_button_text(dialog, "Format"); + dialog_ex_set_header( + dialog, "Format SD card?", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_text( + dialog, "All data will be lost.", 64, y_1_line, AlignCenter, AlignCenter); + view_holder_start(sd_app->view_holder); + sd_app->sd_app_state = SdAppStateFormat; + } + break; + case SdAppEventTypeInfo: + if(try_to_alloc_view_holder(sd_app, gui)) { + DialogEx* dialog = alloc_and_attach_dialog(sd_app); + dialog_ex_set_left_button_text(dialog, "Back"); + + SDInfo sd_info; + get_sd_info(sd_app, &sd_info); + + if(sd_info.error == SD_OK) { + string_printf( + sd_app->text_holder, + "Label: %s\nType: %s\n%lu KB total\n%lu KB free", + sd_info.label, + get_fs_type_text(sd_info.fs_type), + sd_info.kb_total, + sd_info.kb_free); + dialog_ex_set_text( + dialog, + string_get_cstr(sd_app->text_holder), + 4, + y_4_line, + AlignLeft, + AlignCenter); + view_holder_start(sd_app->view_holder); + } else { + string_printf( + sd_app->text_holder, + "SD status: %s\n SD info: %s", + fs_error_get_internal_desc(_fs_status(&sd_app->info)), + fs_error_get_internal_desc(sd_info.error)); + dialog_ex_set_header(dialog, "Error", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_text( + dialog, + string_get_cstr(sd_app->text_holder), + 64, + y_2_line, + AlignCenter, + AlignCenter); + view_holder_start(sd_app->view_holder); + } + + sd_app->sd_app_state = SdAppStateInfo; + } + break; + case SdAppEventTypeEject: + if(try_to_alloc_view_holder(sd_app, gui)) { + DialogEx* dialog = alloc_and_attach_dialog(sd_app); + dialog_ex_set_left_button_text(dialog, "Back"); + dialog_ex_set_right_button_text(dialog, "Eject"); + dialog_ex_set_header( + dialog, "Eject SD card?", 64, 10, AlignCenter, AlignCenter); + dialog_ex_set_text( + dialog, + "SD card will be\nunavailable", + 64, + y_2_line, + AlignCenter, + AlignCenter); + view_holder_start(sd_app->view_holder); + sd_app->sd_app_state = SdAppStateEject; + } + break; + case SdAppEventTypeFileSelect: + if(!app_sd_make_path(event.payload.file_select_data.path)) { + } + + if(try_to_alloc_view_holder(sd_app, gui)) { + sd_app->result_receiver = event.result_receiver; + FileSelect* file_select = alloc_and_attach_file_select(sd_app); + file_select_set_api(file_select, fs_api); + file_select_set_filter( + file_select, + event.payload.file_select_data.path, + event.payload.file_select_data.extension); + file_select_set_result_buffer( + file_select, + event.payload.file_select_data.result, + event.payload.file_select_data.result_size); + if(!file_select_init(file_select)) { + } + sd_app->sd_app_state = SdAppStateFileSelect; + } else { + SdAppFileSelectResultEvent retval = {.result = false}; + furi_check( + osMessageQueuePut(event.result_receiver, &retval, 0, osWaitForever) == + osOK); + } + break; + case SdAppEventTypeCheckError: + break; + } + } } return 0; diff --git a/applications/sd-filesystem/sd-filesystem.h b/applications/sd-filesystem/sd-filesystem.h index 646df60b..11b56fe7 100644 --- a/applications/sd-filesystem/sd-filesystem.h +++ b/applications/sd-filesystem/sd-filesystem.h @@ -4,6 +4,9 @@ #include #include #include +#include +#include "sd-card-api.h" +#include "view_holder.h" #define SD_FS_MAX_FILES _FS_LOCK #define SD_STATE_LINES_COUNT 6 @@ -77,14 +80,30 @@ typedef struct { FATFS fat_fs; } SdFsInfo; -typedef struct { +typedef enum { + SdAppStateBackground, + SdAppStateFormat, + SdAppStateFormatInProgress, + SdAppStateFormatCompleted, + SdAppStateInfo, + SdAppStateEject, + SdAppStateEjected, + SdAppStateFileSelect, +} SdAppState; + +struct SdApp { SdFsInfo info; SdFsIcon icon; - ViewPort* view_port; - const char* line[SD_STATE_LINES_COUNT]; + SdCard_Api sd_card_api; + SdAppState sd_app_state; + + ViewHolder* view_holder; + osMessageQueueId_t result_receiver; + osMessageQueueId_t event_queue; -} SdApp; + string_t text_holder; +}; /* core api fns */ bool _fs_init(SdFsInfo* _fs_info); diff --git a/applications/sd-filesystem/view_holder.c b/applications/sd-filesystem/view_holder.c new file mode 100644 index 00000000..b84ee178 --- /dev/null +++ b/applications/sd-filesystem/view_holder.c @@ -0,0 +1,110 @@ +#include "view_holder.h" +#include + +struct ViewHolder { + View* view; + ViewPort* view_port; + Gui* gui; + + FreeCallback free_callback; + void* free_context; + + BackCallback back_callback; + void* back_context; +}; + +static void view_holder_draw_callback(Canvas* canvas, void* context); +static void view_holder_input_callback(InputEvent* event, void* context); + +ViewHolder* view_holder_alloc() { + ViewHolder* view_holder = furi_alloc(sizeof(ViewHolder)); + + view_holder->view_port = view_port_alloc(); + view_port_draw_callback_set(view_holder->view_port, view_holder_draw_callback, view_holder); + view_port_input_callback_set(view_holder->view_port, view_holder_input_callback, view_holder); + view_port_enabled_set(view_holder->view_port, false); + + return view_holder; +} + +void view_holder_free(ViewHolder* view_holder) { + furi_assert(view_holder); + + if(view_holder->gui) { + gui_remove_view_port(view_holder->gui, view_holder->view_port); + } + + view_port_free(view_holder->view_port); + + if(view_holder->free_callback) { + view_holder->free_callback(view_holder->free_context); + } + + free(view_holder); +} + +void view_holder_set_view(ViewHolder* view_holder, View* view) { + furi_assert(view_holder); + view_holder->view = view; +} + +void view_holder_set_free_callback( + ViewHolder* view_holder, + FreeCallback free_callback, + void* free_context) { + furi_assert(view_holder); + view_holder->free_callback = free_callback; + view_holder->free_context = free_context; +} + +void* view_holder_get_free_context(ViewHolder* view_holder) { + return view_holder->free_context; +} + +void view_holder_set_back_callback( + ViewHolder* view_holder, + BackCallback back_callback, + void* back_context) { + furi_assert(view_holder); + view_holder->back_callback = back_callback; + view_holder->back_context = back_context; +} + +void view_holder_attach_to_gui(ViewHolder* view_holder, Gui* gui) { + furi_assert(gui); + furi_assert(view_holder); + view_holder->gui = gui; + gui_add_view_port(gui, view_holder->view_port, GuiLayerFullscreen); +} + +void view_holder_start(ViewHolder* view_holder) { + view_port_enabled_set(view_holder->view_port, true); +} + +void view_holder_stop(ViewHolder* view_holder) { + view_port_enabled_set(view_holder->view_port, false); +} + +static void view_holder_draw_callback(Canvas* canvas, void* context) { + ViewHolder* view_holder = context; + if(view_holder->view) { + view_draw(view_holder->view, canvas); + } +} + +static void view_holder_input_callback(InputEvent* event, void* context) { + ViewHolder* view_holder = context; + bool is_consumed = false; + + if(view_holder->view) { + is_consumed = view_input(view_holder->view, event); + } + + if(!is_consumed && event->type == InputTypeShort) { + if(event->key == InputKeyBack) { + if(view_holder->back_callback) { + view_holder->back_callback(view_holder->back_context); + } + } + } +} \ No newline at end of file diff --git a/applications/sd-filesystem/view_holder.h b/applications/sd-filesystem/view_holder.h new file mode 100644 index 00000000..4c1d1f67 --- /dev/null +++ b/applications/sd-filesystem/view_holder.h @@ -0,0 +1,92 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ViewHolder ViewHolder; + +/** + * @brief Free callback type + */ +typedef void (*FreeCallback)(void* free_context); + +/** + * @brief Back callback type + * @warning comes from GUI thread + */ +typedef void (*BackCallback)(void* back_context); + +/** + * @brief Allocate ViewHolder + * @return pointer to ViewHolder instance + */ +ViewHolder* view_holder_alloc(); + +/** + * @brief Free ViewHolder and call Free callback + * @param view_holder pointer to ViewHolder + */ +void view_holder_free(ViewHolder* view_holder); + +/** + * @brief Set view for ViewHolder + * + * @param view_holder ViewHolder instance + * @param view View instance + */ +void view_holder_set_view(ViewHolder* view_holder, View* view); + +/** + * @brief Set Free callback + * + * @param view_holder ViewHolder instance + * @param free_callback callback pointer + * @param free_context callback context + */ +void view_holder_set_free_callback( + ViewHolder* view_holder, + FreeCallback free_callback, + void* free_context); + +/** + * @brief Free callback context getter. Useful if your Free callback is a module destructor, so you can get an instance of the module using this method. + * + * @param view_holder ViewHolder instance + * @return void* free callback context + */ +void* view_holder_get_free_context(ViewHolder* view_holder); + +void view_holder_set_back_callback( + ViewHolder* view_holder, + BackCallback back_callback, + void* back_context); + +/** + * @brief Attach ViewHolder to GUI + * + * @param view_holder ViewHolder instance + * @param gui GUI instance to attach to + */ +void view_holder_attach_to_gui(ViewHolder* view_holder, Gui* gui); + +/** + * @brief Enable view processing + * + * @param view_holder + */ +void view_holder_start(ViewHolder* view_holder); + +/** + * @brief Disable view processing + * + * @param view_holder + */ +void view_holder_stop(ViewHolder* view_holder); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/assets/icons/SDCard/SDError_43x35.png b/assets/icons/SDCard/SDError_43x35.png new file mode 100644 index 00000000..d22e9853 Binary files /dev/null and b/assets/icons/SDCard/SDError_43x35.png differ diff --git a/assets/icons/SDCard/SDQuestion_35x43.png b/assets/icons/SDCard/SDQuestion_35x43.png new file mode 100644 index 00000000..9b9c9a58 Binary files /dev/null and b/assets/icons/SDCard/SDQuestion_35x43.png differ diff --git a/lib/common-api/sd-card-api.h b/lib/common-api/sd-card-api.h new file mode 100644 index 00000000..ed0ba521 --- /dev/null +++ b/lib/common-api/sd-card-api.h @@ -0,0 +1,22 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SdApp SdApp; + +typedef struct { + SdApp* context; + bool (*file_select)( + SdApp* context, + const char* path, + const char* extension, + char* result, + uint8_t result_size); +} SdCard_Api; + +#ifdef __cplusplus +} +#endif \ No newline at end of file