diff --git a/applications/archive/helpers/archive_browser.c b/applications/archive/helpers/archive_browser.c index faf10de9..cb3d3ca8 100644 --- a/applications/archive/helpers/archive_browser.c +++ b/applications/archive/helpers/archive_browser.c @@ -21,14 +21,14 @@ void archive_update_offset(ArchiveBrowserView* browser) { browser->view, (ArchiveBrowserViewModel * model) { uint16_t bounds = model->item_cnt > 3 ? 2 : model->item_cnt; - if((model->item_cnt > 3u) && ((uint32_t)model->item_idx >= (model->item_cnt - 1))) { + if((model->item_cnt > 3u) && (model->item_idx >= ((int32_t)model->item_cnt - 1))) { model->list_offset = model->item_idx - 3; } else if(model->list_offset < model->item_idx - bounds) { model->list_offset = - CLAMP((uint32_t)model->item_idx - 2, model->item_cnt - bounds, 0u); + CLAMP(model->item_idx - 2, (int32_t)model->item_cnt - bounds, 0); } else if(model->list_offset > model->item_idx - bounds) { model->list_offset = - CLAMP((uint32_t)model->item_idx - 1, model->item_cnt - bounds, 0u); + CLAMP(model->item_idx - 1, (int32_t)model->item_cnt - bounds, 0); } return true; @@ -80,7 +80,7 @@ void archive_set_item_count(ArchiveBrowserView* browser, uint32_t count) { with_view_model( browser->view, (ArchiveBrowserViewModel * model) { model->item_cnt = count; - model->item_idx = CLAMP((uint32_t)model->item_idx, model->item_cnt - 1, 0u); + model->item_idx = CLAMP(model->item_idx, (int32_t)model->item_cnt - 1, 0); return false; }); archive_update_offset(browser); @@ -97,7 +97,7 @@ void archive_file_array_rm_selected(ArchiveBrowserView* browser) { model->item_idx - model->array_offset, model->item_idx - model->array_offset + 1); model->item_cnt--; - model->item_idx = CLAMP((uint32_t)model->item_idx, model->item_cnt - 1, 0u); + model->item_idx = CLAMP(model->item_idx, (int32_t)model->item_cnt - 1, 0); items_cnt = model->item_cnt; return false; }); @@ -160,7 +160,7 @@ bool archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) { } else { offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 1; } - offset_new = CLAMP((uint32_t)offset_new, model->item_cnt - FILE_LIST_BUF_LEN, 0u); + offset_new = CLAMP(offset_new, (int32_t)model->item_cnt - FILE_LIST_BUF_LEN, 0); } return false; }); diff --git a/applications/power/power_service/power_api.c b/applications/power/power_service/power_api.c index b290cb83..865c21c8 100644 --- a/applications/power/power_service/power_api.c +++ b/applications/power/power_service/power_api.c @@ -2,6 +2,7 @@ #include #include +#include void power_off(Power* power) { furi_hal_power_off(); @@ -14,7 +15,7 @@ void power_off(Power* power) { void power_reboot(PowerBootMode mode) { if(mode == PowerBootModeNormal) { - furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); + update_operation_disarm(); } else if(mode == PowerBootModeDfu) { furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeDfu); } else if(mode == PowerBootModeUpdateStart) { diff --git a/applications/updater/cli/updater_cli.c b/applications/updater/cli/updater_cli.c index 000ff56c..dfc7fbce 100644 --- a/applications/updater/cli/updater_cli.c +++ b/applications/updater/cli/updater_cli.c @@ -134,4 +134,4 @@ void updater_on_system_start() { #else UNUSED(updater_start_app); #endif -} +} \ No newline at end of file diff --git a/applications/updater/scenes/updater_scene_main.c b/applications/updater/scenes/updater_scene_main.c index c5fc99ff..5f7aeaca 100644 --- a/applications/updater/scenes/updater_scene_main.c +++ b/applications/updater/scenes/updater_scene_main.c @@ -45,8 +45,8 @@ void updater_scene_main_on_enter(void* context) { view_dispatcher_switch_to_view(updater->view_dispatcher, UpdaterViewMain); } -static void updater_scene_restart_to_postupdate() { - furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate); +static void updater_scene_cancel_update() { + update_operation_disarm(); furi_hal_power_reset(); } @@ -57,7 +57,7 @@ bool updater_scene_main_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeTick) { if(!update_task_is_running(updater->update_task)) { if(updater->idle_ticks++ >= (UPDATE_DELAY_OPERATION_ERROR / UPDATER_APP_TICK)) { - updater_scene_restart_to_postupdate(); + updater_scene_cancel_update(); } } else { updater->idle_ticks = 0; @@ -74,7 +74,7 @@ bool updater_scene_main_on_event(void* context, SceneManagerEvent event) { case UpdaterCustomEventCancelUpdate: if(!update_task_is_running(updater->update_task)) { - updater_scene_restart_to_postupdate(); + updater_scene_cancel_update(); } consumed = true; break; diff --git a/applications/updater/updater.c b/applications/updater/updater.c index b58c8daa..4c9fe41f 100644 --- a/applications/updater/updater.c +++ b/applications/updater/updater.c @@ -26,15 +26,10 @@ static bool updater_back_event_callback(void* context) { return scene_manager_handle_back_event(updater->scene_manager); } -static void status_update_cb( - const char* message, - const uint8_t progress, - const uint8_t idx_stage, - const uint8_t total_stages, - bool failed, - void* context) { +static void + status_update_cb(const char* message, const uint8_t progress, bool failed, void* context) { UpdaterMainView* main_view = context; - updater_main_model_set_state(main_view, message, progress, idx_stage, total_stages, failed); + updater_main_model_set_state(main_view, message, progress, failed); } Updater* updater_alloc(const char* arg) { @@ -64,9 +59,7 @@ Updater* updater_alloc(const char* arg) { updater->view_dispatcher, updater_tick_event_callback, UPDATER_APP_TICK); view_dispatcher_attach_to_gui( - updater->view_dispatcher, - updater->gui, - arg ? ViewDispatcherTypeFullscreen : ViewDispatcherTypeWindow); + updater->view_dispatcher, updater->gui, ViewDispatcherTypeFullscreen); updater->main_view = updater_main_alloc(); view_dispatcher_add_view( diff --git a/applications/updater/util/update_task.c b/applications/updater/util/update_task.c index 6ff61700..373f680a 100644 --- a/applications/updater/util/update_task.c +++ b/applications/updater/util/update_task.c @@ -14,54 +14,139 @@ static const char* update_task_stage_descr[] = { [UpdateTaskStageReadManifest] = "Loading update manifest", [UpdateTaskStageValidateDFUImage] = "Checking DFU file", [UpdateTaskStageFlashWrite] = "Writing flash", - [UpdateTaskStageFlashValidate] = "Validating", - [UpdateTaskStageRadioImageValidate] = "Checking radio image", - [UpdateTaskStageRadioErase] = "Removing radio stack", - [UpdateTaskStageRadioWrite] = "Writing radio stack", - [UpdateTaskStageRadioInstall] = "Installing radio stack", - [UpdateTaskStageRadioBusy] = "Core2 is updating", + [UpdateTaskStageFlashValidate] = "Validating flash", + [UpdateTaskStageRadioImageValidate] = "Checking radio FW", + [UpdateTaskStageRadioErase] = "Uninstalling radio FW", + [UpdateTaskStageRadioWrite] = "Writing radio FW", + [UpdateTaskStageRadioInstall] = "Installing radio FW", + [UpdateTaskStageRadioBusy] = "Radio is updating", [UpdateTaskStageOBValidation] = "Validating opt. bytes", [UpdateTaskStageLfsBackup] = "Backing up LFS", [UpdateTaskStageLfsRestore] = "Restoring LFS", [UpdateTaskStageResourcesUpdate] = "Updating resources", - [UpdateTaskStageCompleted] = "Completed!", + [UpdateTaskStageCompleted] = "Restarting...", [UpdateTaskStageError] = "Error", - [UpdateTaskStageOBError] = "OB error, pls report", + [UpdateTaskStageOBError] = "OB Err, report", }; -static void update_task_set_status(UpdateTask* update_task, const char* status) { - if(!status) { - if(update_task->state.stage >= COUNT_OF(update_task_stage_descr)) { - status = "..."; - } else { - status = update_task_stage_descr[update_task->state.stage]; - } +typedef struct { + UpdateTaskStageGroup group; + uint8_t weight; +} UpdateTaskStageGroupMap; + +#define STAGE_DEF(GROUP, WEIGHT) \ + { .group = (GROUP), .weight = (WEIGHT), } + +static const UpdateTaskStageGroupMap update_task_stage_progress[] = { + [UpdateTaskStageProgress] = STAGE_DEF(UpdateTaskStageGroupMisc, 0), + + [UpdateTaskStageReadManifest] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 5), + [UpdateTaskStageLfsBackup] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 30), + + [UpdateTaskStageRadioImageValidate] = STAGE_DEF(UpdateTaskStageGroupRadio, 30), + [UpdateTaskStageRadioErase] = STAGE_DEF(UpdateTaskStageGroupRadio, 50), + [UpdateTaskStageRadioWrite] = STAGE_DEF(UpdateTaskStageGroupRadio, 100), + [UpdateTaskStageRadioInstall] = STAGE_DEF(UpdateTaskStageGroupRadio, 5), + [UpdateTaskStageRadioBusy] = STAGE_DEF(UpdateTaskStageGroupRadio, 70), + + [UpdateTaskStageOBValidation] = STAGE_DEF(UpdateTaskStageGroupOptionBytes, 10), + + [UpdateTaskStageValidateDFUImage] = STAGE_DEF(UpdateTaskStageGroupFirmware, 100), + [UpdateTaskStageFlashWrite] = STAGE_DEF(UpdateTaskStageGroupFirmware, 200), + [UpdateTaskStageFlashValidate] = STAGE_DEF(UpdateTaskStageGroupFirmware, 50), + + [UpdateTaskStageLfsRestore] = STAGE_DEF(UpdateTaskStageGroupPostUpdate, 30), + + [UpdateTaskStageResourcesUpdate] = STAGE_DEF(UpdateTaskStageGroupResources, 255), + + [UpdateTaskStageCompleted] = STAGE_DEF(UpdateTaskStageGroupMisc, 1), + [UpdateTaskStageError] = STAGE_DEF(UpdateTaskStageGroupMisc, 1), + [UpdateTaskStageOBError] = STAGE_DEF(UpdateTaskStageGroupMisc, 1), +}; + +static UpdateTaskStageGroup update_task_get_task_groups(UpdateTask* update_task) { + UpdateTaskStageGroup ret = UpdateTaskStageGroupPreUpdate | UpdateTaskStageGroupPostUpdate; + UpdateManifest* manifest = update_task->manifest; + if(!string_empty_p(manifest->radio_image)) { + ret |= UpdateTaskStageGroupRadio; } - string_set_str(update_task->state.status, status); + if(update_manifest_has_obdata(manifest)) { + ret |= UpdateTaskStageGroupOptionBytes; + } + if(!string_empty_p(manifest->firmware_dfu_image)) { + ret |= UpdateTaskStageGroupFirmware; + } + if(!string_empty_p(manifest->resource_bundle)) { + ret |= UpdateTaskStageGroupResources; + } + return ret; +} + +static void update_task_calc_completed_stages(UpdateTask* update_task) { + uint32_t completed_stages_points = 0; + for(UpdateTaskStage past_stage = UpdateTaskStageProgress; + past_stage < update_task->state.stage; + ++past_stage) { + const UpdateTaskStageGroupMap* grp_descr = &update_task_stage_progress[past_stage]; + if((grp_descr->group & update_task->state.groups) == 0) { + continue; + } + completed_stages_points += grp_descr->weight; + } + update_task->state.completed_stages_points = completed_stages_points; } void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, uint8_t progress) { if(stage != UpdateTaskStageProgress) { - // do not override more specific error states - if((update_task->state.stage < UpdateTaskStageError) || (stage < UpdateTaskStageError)) { - update_task->state.stage = stage; + /* do not override more specific error states */ + if((stage >= UpdateTaskStageError) && (update_task->state.stage >= UpdateTaskStageError)) { + return; + } + /* Build error message with code "[stage_idx-stage_percent]" */ + if(stage >= UpdateTaskStageError) { + string_printf( + update_task->state.status, + "%s #[%d-%d]", + update_task_stage_descr[stage], + update_task->state.stage, + update_task->state.stage_progress); + } else { + string_set_str(update_task->state.status, update_task_stage_descr[stage]); + } + /* Store stage update */ + update_task->state.stage = stage; + /* If we are still alive, sum completed stages weights */ + if((stage > UpdateTaskStageProgress) && (stage < UpdateTaskStageCompleted)) { + update_task_calc_completed_stages(update_task); } - update_task->state.current_stage_idx++; - update_task_set_status(update_task, NULL); } - if(progress > 100) { - progress = 100; + /* Store stage progress for all non-error updates - to provide details on error state */ + if(!update_stage_is_error(stage)) { + update_task->state.stage_progress = progress; } - update_task->state.progress = progress; + /* Calculate "overall" progress, based on stage weights */ + uint32_t adapted_progress = 1; + if(update_task->state.total_progress_points != 0) { + if(stage < UpdateTaskStageCompleted) { + adapted_progress = MIN( + (update_task->state.completed_stages_points + + (update_task_stage_progress[update_task->state.stage].weight * progress / 100)) * + 100 / (update_task->state.total_progress_points), + 100u); + + } else { + adapted_progress = update_task->state.overall_progress; + } + } + update_task->state.overall_progress = adapted_progress; + if(update_task->status_change_cb) { (update_task->status_change_cb)( string_get_cstr(update_task->state.status), - progress, - update_task->state.current_stage_idx, - update_task->state.total_stages, - update_task->state.stage >= UpdateTaskStageError, + adapted_progress, + update_stage_is_error(update_task->state.stage), update_task->status_change_cb_state); } } @@ -106,8 +191,7 @@ static void update_task_worker_thread_cb(FuriThreadState state, void* context) { return; } - int32_t op_result = furi_thread_get_return_code(update_task->thread); - if(op_result == UPDATE_TASK_NOERR) { + if(furi_thread_get_return_code(update_task->thread) == UPDATE_TASK_NOERR) { osDelay(UPDATE_DELAY_OPERATION_OK); furi_hal_power_reset(); } @@ -117,7 +201,8 @@ UpdateTask* update_task_alloc() { UpdateTask* update_task = malloc(sizeof(UpdateTask)); update_task->state.stage = UpdateTaskStageProgress; - update_task->state.progress = 0; + update_task->state.stage_progress = 0; + update_task->state.overall_progress = 0; string_init(update_task->state.status); update_task->manifest = update_manifest_alloc(); @@ -163,6 +248,12 @@ void update_task_free(UpdateTask* update_task) { bool update_task_parse_manifest(UpdateTask* update_task) { furi_assert(update_task); + update_task->state.stage_progress = 0; + update_task->state.overall_progress = 0; + update_task->state.total_progress_points = 0; + update_task->state.completed_stages_points = 0; + update_task->state.groups = 0; + update_task_set_progress(update_task, UpdateTaskStageReadManifest, 0); bool result = false; string_t manifest_path; @@ -180,19 +271,31 @@ bool update_task_parse_manifest(UpdateTask* update_task) { UPDATE_MANIFEST_DEFAULT_NAME, manifest_path); update_task_set_progress(update_task, UpdateTaskStageProgress, 30); - if(!update_manifest_init(update_task->manifest, string_get_cstr(manifest_path))) { + + UpdateManifest* manifest = update_task->manifest; + if(!update_manifest_init(manifest, string_get_cstr(manifest_path))) { break; } + update_task->state.groups = update_task_get_task_groups(update_task); + for(size_t stage_counter = 0; stage_counter < COUNT_OF(update_task_stage_progress); + ++stage_counter) { + const UpdateTaskStageGroupMap* grp_descr = &update_task_stage_progress[stage_counter]; + if((grp_descr->group & update_task->state.groups) != 0) { + update_task->state.total_progress_points += grp_descr->weight; + } + } + update_task_set_progress(update_task, UpdateTaskStageProgress, 50); - if(!string_empty_p(update_task->manifest->firmware_dfu_image) && - !update_task_check_file_exists(update_task, update_task->manifest->firmware_dfu_image)) { + if((update_task->state.groups & UpdateTaskStageGroupFirmware) && + !update_task_check_file_exists(update_task, manifest->firmware_dfu_image)) { break; } update_task_set_progress(update_task, UpdateTaskStageProgress, 70); - if(!string_empty_p(update_task->manifest->radio_image) && - !update_task_check_file_exists(update_task, update_task->manifest->radio_image)) { + if((update_task->state.groups & UpdateTaskStageGroupRadio) && + (!update_task_check_file_exists(update_task, manifest->radio_image) || + (manifest->radio_version.version.type == 0))) { break; } diff --git a/applications/updater/util/update_task.h b/applications/updater/util/update_task.h index 3197c8c1..cfbbb850 100644 --- a/applications/updater/util/update_task.h +++ b/applications/updater/util/update_task.h @@ -10,46 +10,63 @@ extern "C" { #include #include -#define UPDATE_DELAY_OPERATION_OK 600 +#define UPDATE_DELAY_OPERATION_OK 300 #define UPDATE_DELAY_OPERATION_ERROR INT_MAX typedef enum { - UpdateTaskStageProgress, + UpdateTaskStageProgress = 0, + UpdateTaskStageReadManifest, - UpdateTaskStageValidateDFUImage, - UpdateTaskStageFlashWrite, - UpdateTaskStageFlashValidate, + UpdateTaskStageLfsBackup, + UpdateTaskStageRadioImageValidate, UpdateTaskStageRadioErase, UpdateTaskStageRadioWrite, UpdateTaskStageRadioInstall, UpdateTaskStageRadioBusy, + UpdateTaskStageOBValidation, - UpdateTaskStageLfsBackup, + + UpdateTaskStageValidateDFUImage, + UpdateTaskStageFlashWrite, + UpdateTaskStageFlashValidate, + UpdateTaskStageLfsRestore, UpdateTaskStageResourcesUpdate, + UpdateTaskStageCompleted, UpdateTaskStageError, - UpdateTaskStageOBError + UpdateTaskStageOBError, + UpdateTaskStageMAX } UpdateTaskStage; +inline bool update_stage_is_error(const UpdateTaskStage stage) { + return stage >= UpdateTaskStageError; +} + +typedef enum { + UpdateTaskStageGroupMisc = 0, + UpdateTaskStageGroupPreUpdate = 1 << 1, + UpdateTaskStageGroupFirmware = 1 << 2, + UpdateTaskStageGroupOptionBytes = 1 << 3, + UpdateTaskStageGroupRadio = 1 << 4, + UpdateTaskStageGroupPostUpdate = 1 << 5, + UpdateTaskStageGroupResources = 1 << 6, +} UpdateTaskStageGroup; + typedef struct { UpdateTaskStage stage; - uint8_t progress; - uint8_t current_stage_idx; - uint8_t total_stages; + uint8_t overall_progress, stage_progress; string_t status; + UpdateTaskStageGroup groups; + uint32_t total_progress_points; + uint32_t completed_stages_points; } UpdateTaskState; typedef struct UpdateTask UpdateTask; -typedef void (*updateProgressCb)( - const char* status, - const uint8_t stage_pct, - const uint8_t idx_stage, - const uint8_t total_stages, - bool failed, - void* state); +typedef void ( + *updateProgressCb)(const char* status, const uint8_t stage_pct, bool failed, void* state); UpdateTask* update_task_alloc(); diff --git a/applications/updater/util/update_task_worker_backup.c b/applications/updater/util/update_task_worker_backup.c index 38d71dfd..485fdd48 100644 --- a/applications/updater/util/update_task_worker_backup.c +++ b/applications/updater/util/update_task_worker_backup.c @@ -27,7 +27,6 @@ static bool update_task_pre_update(UpdateTask* update_task) { path_concat( string_get_cstr(update_task->update_path), LFS_BACKUP_DEFAULT_FILENAME, backup_file_path); - update_task->state.total_stages = 1; update_task_set_progress(update_task, UpdateTaskStageLfsBackup, 0); /* to avoid bootloops */ furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); @@ -62,22 +61,18 @@ static bool update_task_post_update(UpdateTask* update_task) { string_t file_path; string_init(file_path); - /* status text is too long, too few stages to bother with a counter */ - update_task->state.total_stages = 0; - + TarArchive* archive = tar_archive_alloc(update_task->storage); do { CHECK_RESULT(update_task_parse_manifest(update_task)); path_concat( string_get_cstr(update_task->update_path), LFS_BACKUP_DEFAULT_FILENAME, file_path); - bool unpack_resources = !string_empty_p(update_task->manifest->resource_bundle); - update_task_set_progress(update_task, UpdateTaskStageLfsRestore, 0); - furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); + update_operation_disarm(); CHECK_RESULT(lfs_backup_unpack(update_task->storage, string_get_cstr(file_path))); - if(unpack_resources) { + if(update_task->state.groups & UpdateTaskStageGroupResources) { TarUnpackProgress progress = { .update_task = update_task, .total_files = 0, @@ -90,20 +85,19 @@ static bool update_task_post_update(UpdateTask* update_task) { string_get_cstr(update_task->manifest->resource_bundle), file_path); - TarArchive* archive = tar_archive_alloc(update_task->storage); tar_archive_set_file_callback(archive, update_task_resource_unpack_cb, &progress); - success = tar_archive_open(archive, string_get_cstr(file_path), TAR_OPEN_MODE_READ); - if(success) { - progress.total_files = tar_archive_get_entries_count(archive); - if(progress.total_files > 0) { - tar_archive_unpack_to(archive, EXT_PATH); - } + CHECK_RESULT( + tar_archive_open(archive, string_get_cstr(file_path), TAR_OPEN_MODE_READ)); + + progress.total_files = tar_archive_get_entries_count(archive); + if(progress.total_files > 0) { + CHECK_RESULT(tar_archive_unpack_to(archive, EXT_PATH)); } - tar_archive_free(archive); } success = true; } while(false); + tar_archive_free(archive); string_clear(file_path); return success; } @@ -116,18 +110,17 @@ int32_t update_task_worker_backup_restore(void* context) { FuriHalRtcBootMode boot_mode = furi_hal_rtc_get_boot_mode(); if((boot_mode != FuriHalRtcBootModePreUpdate) && (boot_mode != FuriHalRtcBootModePostUpdate)) { /* no idea how we got here. Clear to normal boot */ - furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); + update_operation_disarm(); return UPDATE_TASK_NOERR; } - update_task->state.current_stage_idx = 0; - if(!update_operation_get_current_package_path(update_task->storage, update_task->update_path)) { return UPDATE_TASK_FAILED; } - /* Waiting for BT service to 'start', so we don't race for boot mode */ + /* Waiting for BT service to 'start', so we don't race for boot mode flag */ furi_record_open("bt"); + furi_record_close("bt"); if(boot_mode == FuriHalRtcBootModePreUpdate) { success = update_task_pre_update(update_task); @@ -135,13 +128,11 @@ int32_t update_task_worker_backup_restore(void* context) { success = update_task_post_update(update_task); } - furi_record_close("bt"); - - if(success) { - update_task_set_progress(update_task, UpdateTaskStageCompleted, 100); - } else { - update_task_set_progress(update_task, UpdateTaskStageError, update_task->state.progress); + if(!success) { + update_task_set_progress(update_task, UpdateTaskStageError, 0); + return UPDATE_TASK_FAILED; } - return success ? UPDATE_TASK_NOERR : UPDATE_TASK_FAILED; + update_task_set_progress(update_task, UpdateTaskStageCompleted, 100); + return UPDATE_TASK_NOERR; } diff --git a/applications/updater/util/update_task_worker_flasher.c b/applications/updater/util/update_task_worker_flasher.c index 6b03439d..1f22e2f7 100644 --- a/applications/updater/util/update_task_worker_flasher.c +++ b/applications/updater/util/update_task_worker_flasher.c @@ -111,19 +111,13 @@ static bool update_task_write_stack_data(UpdateTask* update_task) { } bytes_read = storage_file_read(update_task->file, fw_block, n_bytes_to_read); - if(bytes_read == 0) { - break; - } + CHECK_RESULT(bytes_read != 0); int16_t i_page = furi_hal_flash_get_page_number(update_task->manifest->radio_address + element_offs); - if(i_page < 0) { - break; - } + CHECK_RESULT(i_page >= 0); - if(!furi_hal_flash_program_page(i_page, fw_block, bytes_read)) { - break; - } + CHECK_RESULT(furi_hal_flash_program_page(i_page, fw_block, bytes_read)); element_offs += bytes_read; update_task_set_progress( @@ -142,19 +136,19 @@ static void update_task_wait_for_restart(UpdateTask* update_task) { static bool update_task_write_stack(UpdateTask* update_task) { bool success = false; + UpdateManifest* manifest = update_task->manifest; do { FURI_LOG_W(TAG, "Writing stack"); update_task_set_progress(update_task, UpdateTaskStageRadioImageValidate, 0); - CHECK_RESULT(update_task_open_file(update_task, update_task->manifest->radio_image)); + CHECK_RESULT(update_task_open_file(update_task, manifest->radio_image)); CHECK_RESULT( crc32_calc_file(update_task->file, &update_task_file_progress, update_task) == - update_task->manifest->radio_crc); + manifest->radio_crc); CHECK_RESULT(update_task_write_stack_data(update_task)); update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 0); CHECK_RESULT( - ble_glue_fus_stack_install(update_task->manifest->radio_address, 0) != - BleGlueCommandResultError); + ble_glue_fus_stack_install(manifest->radio_address, 0) != BleGlueCommandResultError); update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 80); CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK); update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 100); @@ -280,7 +274,6 @@ bool update_task_validate_optionbytes(UpdateTask* update_task) { manifest->ob_write_mask.obs[idx].values.base) != 0; if(can_patch) { - /* patch & restart loop */ const uint32_t patched_value = /* take all non-writable bits from real value */ (device_ob_value & ~(manifest->ob_write_mask.obs[idx].values.base)) | @@ -297,8 +290,7 @@ bool update_task_validate_optionbytes(UpdateTask* update_task) { if(!is_fixed) { /* Things are so bad that fixing what we are allowed to still doesn't match - * reference value - */ + * reference value */ FURI_LOG_W( TAG, "OB #%d is FUBAR (fixed&masked %08X, not %08X)", @@ -317,11 +309,11 @@ bool update_task_validate_optionbytes(UpdateTask* update_task) { } } if(!match) { - update_task_set_progress(update_task, UpdateTaskStageOBError, 95); + update_task_set_progress(update_task, UpdateTaskStageOBError, 0); } if(ob_dirty) { - FURI_LOG_W(TAG, "OB were changed, applying"); + FURI_LOG_W(TAG, "OBs were changed, applying"); furi_hal_flash_ob_apply(); } return match; @@ -332,24 +324,18 @@ int32_t update_task_worker_flash_writer(void* context) { UpdateTask* update_task = context; bool success = false; - update_task->state.current_stage_idx = 0; - update_task->state.total_stages = 0; - do { CHECK_RESULT(update_task_parse_manifest(update_task)); - if(!string_empty_p(update_task->manifest->radio_image)) { + if(update_task->state.groups & UpdateTaskStageGroupRadio) { CHECK_RESULT(update_task_manage_radiostack(update_task)); } - bool check_ob = update_manifest_has_obdata(update_task->manifest); - if(check_ob) { - update_task->state.total_stages++; + if(update_task->state.groups & UpdateTaskStageGroupOptionBytes) { CHECK_RESULT(update_task_validate_optionbytes(update_task)); } - if(!string_empty_p(update_task->manifest->firmware_dfu_image)) { - update_task->state.total_stages += 4; + if(update_task->state.groups & UpdateTaskStageGroupFirmware) { CHECK_RESULT(update_task_write_dfu(update_task)); } @@ -359,5 +345,10 @@ int32_t update_task_worker_flash_writer(void* context) { success = true; } while(false); - return success ? UPDATE_TASK_NOERR : UPDATE_TASK_FAILED; + if(!success) { + update_task_set_progress(update_task, UpdateTaskStageError, 0); + return UPDATE_TASK_FAILED; + } + + return UPDATE_TASK_NOERR; } diff --git a/applications/updater/views/updater_main.c b/applications/updater/views/updater_main.c index 8d9b3e95..72541b9a 100644 --- a/applications/updater/views/updater_main.c +++ b/applications/updater/views/updater_main.c @@ -15,12 +15,11 @@ struct UpdaterMainView { void* context; }; -static const uint8_t PROGRESS_RENDER_STEP = 3; /* percent, to limit rendering rate */ +static const uint8_t PROGRESS_RENDER_STEP = 1; /* percent, to limit rendering rate */ typedef struct { string_t status; uint8_t progress, rendered_progress; - uint8_t idx_stage, total_stages; bool failed; } UpdaterProgressModel; @@ -28,14 +27,10 @@ void updater_main_model_set_state( UpdaterMainView* main_view, const char* message, uint8_t progress, - uint8_t idx_stage, - uint8_t total_stages, bool failed) { with_view_model( main_view->view, (UpdaterProgressModel * model) { model->failed = failed; - model->idx_stage = idx_stage; - model->total_stages = total_stages; model->progress = progress; if(string_cmp_str(model->status, message)) { string_set(model->status, message); @@ -65,14 +60,10 @@ bool updater_main_input(InputEvent* event, void* context) { return true; } - if(event->type != InputTypeShort) { - return true; - } - - if(event->key == InputKeyOk) { + if((event->type == InputTypeShort) && (event->key == InputKeyOk)) { view_dispatcher_send_custom_event( main_view->view_dispatcher, UpdaterCustomEventRetryUpdate); - } else if(event->key == InputKeyBack) { + } else if((event->type == InputTypeLong) && (event->key == InputKeyBack)) { view_dispatcher_send_custom_event( main_view->view_dispatcher, UpdaterCustomEventCancelUpdate); } @@ -85,27 +76,25 @@ static void updater_main_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontPrimary); - uint16_t y_offset = model->failed ? 5 : 13; - string_t status_text; - if(!model->failed && (model->idx_stage != 0) && (model->idx_stage <= model->total_stages)) { - string_init_printf( - status_text, - "[%d/%d] %s", - model->idx_stage, - model->total_stages, - string_get_cstr(model->status)); - } else { - string_init_set(status_text, model->status); - } - canvas_draw_str_aligned( - canvas, 128 / 2, y_offset, AlignCenter, AlignTop, string_get_cstr(status_text)); - string_clear(status_text); if(model->failed) { + canvas_draw_str_aligned(canvas, 42, 16, AlignLeft, AlignTop, "Update Failed!"); canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned( - canvas, 128 / 2, 20, AlignCenter, AlignTop, "[OK] to retry, [Back] to abort"); + canvas, 42, 32, AlignLeft, AlignTop, string_get_cstr(model->status)); + + canvas_draw_icon(canvas, 7, 16, &I_Warning_30x23); + canvas_draw_str_aligned( + canvas, 18, 51, AlignLeft, AlignTop, "to retry, hold to abort"); + canvas_draw_icon(canvas, 7, 50, &I_Ok_btn_9x9); + canvas_draw_icon(canvas, 75, 51, &I_Pin_back_arrow_10x8); + } else { + canvas_draw_str_aligned(canvas, 55, 14, AlignLeft, AlignTop, "UPDATING"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 64, 51, AlignCenter, AlignTop, string_get_cstr(model->status)); + canvas_draw_icon(canvas, 4, 5, &I_Updating_32x40); + elements_progress_bar(canvas, 42, 29, 80, (float)model->progress / 100); } - elements_progress_bar(canvas, 14, 35, 100, (float)model->progress / 100); } UpdaterMainView* updater_main_alloc() { @@ -116,7 +105,7 @@ UpdaterMainView* updater_main_alloc() { with_view_model( main_view->view, (UpdaterProgressModel * model) { - string_init_set(model->status, "Waiting for storage"); + string_init_set(model->status, "Waiting for SD card"); return true; }); diff --git a/applications/updater/views/updater_main.h b/applications/updater/views/updater_main.h index d7872956..81a0e86c 100644 --- a/applications/updater/views/updater_main.h +++ b/applications/updater/views/updater_main.h @@ -17,8 +17,6 @@ void updater_main_model_set_state( UpdaterMainView* main_view, const char* message, uint8_t progress, - uint8_t idx_stage, - uint8_t total_stages, bool failed); void updater_main_set_storage_pubsub(UpdaterMainView* main_view, FuriPubSubSubscription* sub); diff --git a/assets/compiled/assets_icons.c b/assets/compiled/assets_icons.c index 8b17d078..709fab45 100644 --- a/assets/compiled/assets_icons.c +++ b/assets/compiled/assets_icons.c @@ -642,6 +642,9 @@ const uint8_t* const _I_Drive_112x35[] = {_I_Drive_112x35_0}; const uint8_t _I_Error_62x31_0[] = {0x01,0x00,0x9e,0x00,0x00,0x47,0xc2,0xfe,0x07,0x58,0x66,0x02,0x02,0x07,0x48,0x1c,0x02,0x0c,0x06,0x3c,0x00,0x08,0x61,0x00,0x73,0xa0,0x00,0x86,0x20,0x07,0x39,0x00,0x09,0x01,0x88,0x07,0x70,0xd1,0x09,0x0b,0xe0,0x07,0x38,0x1c,0x62,0x11,0x08,0x80,0x8c,0x8a,0x0f,0x1c,0x82,0x7d,0x20,0x58,0x0b,0xe4,0x02,0x1d,0x0e,0x82,0x6e,0xa0,0xb8,0x0c,0x1c,0x02,0x39,0x07,0x82,0x4e,0xa0,0xb7,0x08,0x04,0x07,0x71,0x03,0x82,0x7e,0xa0,0xb0,0xe8,0x04,0x0b,0xe1,0x01,0x81,0x01,0xc6,0x01,0xc0,0x81,0xf8,0x01,0x42,0x27,0x18,0x04,0xc0,0x1e,0x63,0x71,0x3d,0x0c,0x08,0x3e,0x20,0xa1,0x22,0x94,0x08,0x5e,0x21,0x51,0x0f,0x08,0xbc,0x47,0xe2,0x07,0x29,0x81,0x40,0x49,0xe2,0x07,0x28,0x61,0x80,0x4b,0xe2,0x07,0x28,0x19,0xe0,0xc0,0xe2,0x0d,0x18,0xc0,0x1d,0x00,0x02,0xa8,0x30,0x39,0x2e,0x10,0x0e,0x5e,0x00,0x3b,0x7e,0x00,0xec,0x46,0x10,0x3f,0x80,0xc8,}; const uint8_t* const _I_Error_62x31[] = {_I_Error_62x31_0}; +const uint8_t _I_Updating_32x40_0[] = {0x01,0x00,0x56,0x00,0xc0,0x7f,0xc0,0x03,0xc0,0x01,0x97,0x82,0x07,0x00,0xe0,0x5c,0x00,0x65,0x38,0x01,0x94,0x70,0x06,0x50,0xe0,0x19,0x41,0xc0,0x65,0xff,0x01,0xb4,0x0c,0x02,0x7e,0x08,0x38,0x0c,0x7c,0xd6,0x70,0x18,0xfb,0xfe,0xfc,0x0c,0x18,0xc8,0x78,0x20,0x33,0x81,0x8f,0x8a,0x07,0x3e,0xbe,0x70,0x38,0x71,0xff,0xc7,0x0f,0xc7,0x0f,0xf8,0x71,0xc0,0x76,0x13,0x30,0xd9,0x88,0xcc,0x5f,0x03,0xb2,0x21,0xa1,0x2c,0xc0,0x26,0x82,0x10,0x1f,0x80,0xd1,0x24,0x40,0x04,}; +const uint8_t* const _I_Updating_32x40[] = {_I_Updating_32x40_0}; + const uint8_t _I_DolphinExcited_64x63_0[] = {0x01,0x00,0x36,0x01,0x00,0x25,0x00,0x0f,0xd2,0x00,0x3b,0xe0,0x00,0xeb,0x10,0x0c,0x34,0x40,0x30,0xd0,0x88,0x80,0x1d,0xa1,0x00,0x42,0xfc,0x7f,0xc0,0x63,0x04,0x01,0x0e,0x02,0x0f,0x00,0x00,0x8c,0x08,0x0e,0x37,0x00,0x10,0xc6,0x20,0x10,0x10,0xd9,0x11,0x92,0x1c,0x1a,0x3e,0x00,0x04,0x42,0x02,0x1a,0x20,0xb0,0xce,0x00,0x64,0x07,0x20,0x59,0x16,0x50,0x36,0x45,0x94,0x84,0x78,0x20,0x60,0x75,0x8e,0x43,0x06,0x63,0x3c,0x33,0x94,0x0c,0xd2,0x5c,0x30,0x38,0xe4,0x08,0x43,0x10,0xc0,0x5e,0x06,0x22,0x53,0x1a,0x02,0x08,0x7f,0xd0,0x32,0xc1,0x50,0x21,0x14,0x0e,0x70,0x1c,0x46,0xe2,0x07,0x19,0x06,0x3c,0xdc,0x20,0x91,0xae,0x01,0xcc,0xbe,0x30,0x09,0xfc,0x12,0x41,0xff,0x83,0xcc,0x0a,0xa3,0x1f,0x03,0x99,0xe8,0x7c,0x10,0xf8,0x25,0xa0,0x5e,0x50,0x0f,0x84,0x1e,0x09,0x54,0x03,0x9f,0xf2,0x07,0x02,0xd5,0x11,0xca,0x01,0xfe,0x80,0xc0,0xaa,0x9f,0xf0,0x39,0x5f,0xd0,0x43,0xaa,0x83,0x41,0x92,0xc3,0x1f,0x03,0x8d,0x52,0x02,0x2e,0x25,0xc9,0x6a,0x99,0x46,0xa6,0x2a,0xa0,0x1c,0xaf,0xca,0x62,0x94,0x28,0xcb,0x7e,0x0f,0x15,0x71,0xf8,0x3c,0x22,0x71,0x03,0x8a,0x84,0x67,0x18,0x0f,0xac,0x1c,0x0e,0x38,0x08,0x0c,0x3e,0x01,0xae,0xbd,0x13,0x0c,0x0e,0x35,0x8e,0xa8,0x1c,0xb0,0x1f,0xf8,0x06,0x83,0xf4,0x27,0x38,0x07,0xff,0xff,0x8f,0x03,0xa0,0x4c,0x80,0xed,0x60,0x03,0xb4,0x60,0x0e,0xd0,0x60,0x3a,0x87,0x84,0x0e,0xb7,0xc2,0xfa,0x18,0x05,0x44,0x20,0x73,0xff,0xf7,0xce,0xe4,0x07,0x2d,0x52,0x2c,0x80,0xe7,0x54,0xea,0x81,0xd7,0x50,0x0f,0x7a,0xaa,0x3d,0x41,0xe2,0x07,0x5a,0x80,0x3c,0xa0,0x40,0x72,0xd0,0x6a,0x80,0xa2,0x07,0x3a,0x05,0x54,0x8e,0x20,0x73,0xc0,0x03,0xd8,0x60,0x30,0x40,0x3a,0xc0,0x00,0xee,0xea,0x10,0x3b,0x80,}; const uint8_t* const _I_DolphinExcited_64x63[] = {_I_DolphinExcited_64x63_0}; @@ -835,6 +838,7 @@ const Icon I_Connect_me_62x31 = {.width=62,.height=31,.frame_count=1,.frame_rate const Icon I_Connected_62x31 = {.width=62,.height=31,.frame_count=1,.frame_rate=0,.frames=_I_Connected_62x31}; const Icon I_Drive_112x35 = {.width=112,.height=35,.frame_count=1,.frame_rate=0,.frames=_I_Drive_112x35}; const Icon I_Error_62x31 = {.width=62,.height=31,.frame_count=1,.frame_rate=0,.frames=_I_Error_62x31}; +const Icon I_Updating_32x40 = {.width=32,.height=40,.frame_count=1,.frame_rate=0,.frames=_I_Updating_32x40}; const Icon I_DolphinExcited_64x63 = {.width=64,.height=63,.frame_count=1,.frame_rate=0,.frames=_I_DolphinExcited_64x63}; const Icon I_DolphinMafia_115x62 = {.width=115,.height=62,.frame_count=1,.frame_rate=0,.frames=_I_DolphinMafia_115x62}; const Icon I_DolphinNice_96x59 = {.width=96,.height=59,.frame_count=1,.frame_rate=0,.frames=_I_DolphinNice_96x59}; diff --git a/assets/compiled/assets_icons.h b/assets/compiled/assets_icons.h index be1023f6..12ddb78e 100644 --- a/assets/compiled/assets_icons.h +++ b/assets/compiled/assets_icons.h @@ -173,6 +173,7 @@ extern const Icon I_Connect_me_62x31; extern const Icon I_Connected_62x31; extern const Icon I_Drive_112x35; extern const Icon I_Error_62x31; +extern const Icon I_Updating_32x40; extern const Icon I_DolphinExcited_64x63; extern const Icon I_DolphinMafia_115x62; extern const Icon I_DolphinNice_96x59; diff --git a/assets/icons/Update/Updating_32x40.png b/assets/icons/Update/Updating_32x40.png new file mode 100644 index 00000000..d8f7654b Binary files /dev/null and b/assets/icons/Update/Updating_32x40.png differ diff --git a/firmware/targets/f7/Src/main.c b/firmware/targets/f7/Src/main.c index 629c154e..2c28265f 100644 --- a/firmware/targets/f7/Src/main.c +++ b/firmware/targets/f7/Src/main.c @@ -3,6 +3,7 @@ #include #include #include +#include #define TAG "Main" @@ -47,7 +48,7 @@ int main() { flipper_boot_update_exec(); // if things go nice, we shouldn't reach this point. // But if we do, abandon to avoid bootloops - furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); + update_operation_disarm(); furi_hal_power_reset(); } else { furi_hal_light_sequence("rgb G"); diff --git a/firmware/targets/f7/Src/update.c b/firmware/targets/f7/Src/update.c index 4b3ee3ad..f4a434b1 100644 --- a/firmware/targets/f7/Src/update.c +++ b/firmware/targets/f7/Src/update.c @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -101,7 +102,7 @@ static bool flipper_update_load_stage(const string_t work_dir, UpdateManifest* m static bool flipper_update_get_work_directory(string_t out_dir) { const uint32_t update_index = furi_hal_rtc_get_register(FuriHalRtcRegisterUpdateFolderFSIndex); - if(update_index == 0) { + if(update_index == UPDATE_OPERATION_ROOT_DIR_PACKAGE_MAGIC) { string_set(out_dir, UPDATE_DIR_DEFAULT_REL_PATH); return true; } diff --git a/lib/update_util/update_manifest.h b/lib/update_util/update_manifest.h index 75871e1d..2893be5a 100644 --- a/lib/update_util/update_manifest.h +++ b/lib/update_util/update_manifest.h @@ -12,7 +12,6 @@ extern "C" { /* Paths don't include /ext -- because at startup SD card is mounted as root */ #define UPDATE_DIR_DEFAULT_REL_PATH "/update" #define UPDATE_MANIFEST_DEFAULT_NAME "update.fuf" -#define UPDATE_MAINFEST_DEFAULT_PATH UPDATE_DIR_DEFAULT_REL_PATH "/" UPDATE_MANIFEST_DEFAULT_NAME typedef union { uint8_t raw[6]; diff --git a/lib/update_util/update_operation.c b/lib/update_util/update_operation.c index cf72dd0c..c9a7cd18 100644 --- a/lib/update_util/update_operation.c +++ b/lib/update_util/update_operation.c @@ -9,10 +9,10 @@ #include #include -static const char* UPDATE_ROOT_DIR = "/ext" UPDATE_DIR_DEFAULT_REL_PATH; -static const char* UPDATE_PREFIX = "/ext" UPDATE_DIR_DEFAULT_REL_PATH "/"; -static const char* UPDATE_SUFFIX = "/" UPDATE_MANIFEST_DEFAULT_NAME; -static const uint32_t MAX_DIR_NAME_LEN = 250; +#define UPDATE_ROOT_DIR "/ext" UPDATE_DIR_DEFAULT_REL_PATH +#define UPDATE_PREFIX "/ext" UPDATE_DIR_DEFAULT_REL_PATH "/" +#define UPDATE_SUFFIX "/" UPDATE_MANIFEST_DEFAULT_NAME +#define MAX_DIR_NAME_LEN 250 static const char* update_prepare_result_descr[] = { [UpdatePrepareResultOK] = "OK", @@ -59,7 +59,7 @@ int32_t update_operation_get_package_index(Storage* storage, const char* update_ furi_assert(update_package_dir); if(strlen(update_package_dir) == 0) { - return 0; + return UPDATE_OPERATION_ROOT_DIR_PACKAGE_MAGIC; } bool found = false; @@ -90,9 +90,9 @@ int32_t update_operation_get_package_index(Storage* storage, const char* update_ } bool update_operation_get_current_package_path(Storage* storage, string_t out_path) { - uint32_t update_index = furi_hal_rtc_get_register(FuriHalRtcRegisterUpdateFolderFSIndex); + const uint32_t update_index = furi_hal_rtc_get_register(FuriHalRtcRegisterUpdateFolderFSIndex); string_set_str(out_path, UPDATE_ROOT_DIR); - if(update_index == 0) { + if(update_index == UPDATE_OPERATION_ROOT_DIR_PACKAGE_MAGIC) { return true; } @@ -184,14 +184,19 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { } bool update_operation_is_armed() { - return furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModePreUpdate; + FuriHalRtcBootMode boot_mode = furi_hal_rtc_get_boot_mode(); + return (boot_mode >= FuriHalRtcBootModePreUpdate) && + (boot_mode <= FuriHalRtcBootModePostUpdate) && + (furi_hal_rtc_get_register(FuriHalRtcRegisterUpdateFolderFSIndex) > 0); } void update_operation_disarm() { furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); - furi_hal_rtc_set_register(FuriHalRtcRegisterUpdateFolderFSIndex, 0); + furi_hal_rtc_set_register( + FuriHalRtcRegisterUpdateFolderFSIndex, INT_MAX); } -void update_operation_persist_package_index(uint32_t index) { +void update_operation_persist_package_index(int32_t index) { + furi_check(index >= 0); furi_hal_rtc_set_register(FuriHalRtcRegisterUpdateFolderFSIndex, index); } diff --git a/lib/update_util/update_operation.h b/lib/update_util/update_operation.h index 10f3c6b4..c11f2754 100644 --- a/lib/update_util/update_operation.h +++ b/lib/update_util/update_operation.h @@ -8,6 +8,8 @@ extern "C" { #endif +#define UPDATE_OPERATION_ROOT_DIR_PACKAGE_MAGIC 0 + /* * Checks if supplied full manifest path is valid * @param full_path Full path to manifest file. Must be named UPDATE_MANIFEST_DEFAULT_NAME @@ -39,7 +41,7 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path); * Gets update package index to pass in RTC registers * @param storage Storage API * @param update_package_dir Package directory name - * @return int32_t <0 - error, >= 0 - update index value + * @return int32_t <=0 - error, >0 - update index value */ int32_t update_operation_get_package_index(Storage* storage, const char* update_package_dir); @@ -55,10 +57,10 @@ bool update_operation_get_current_package_path(Storage* storage, string_t out_pa * Stores given update index in RTC registers * @param index Value to store */ -void update_operation_persist_package_index(uint32_t index); +void update_operation_persist_package_index(int32_t index); /* - * Sets up update operation to be performed on reset + * Checks if an update operation step is pending after reset */ bool update_operation_is_armed();