[FL-2527] Updater: Migrating to new manifest path convention (#1213)
* Updater: Migrating to new manifest path convention * RPC: Added update preparation status to RPC * RPC: bumped protobuf submodule * Bumped protobuf_version.h * FuriCore: add missing include. Lib: make mlib smaller * Explicitly tell where we have doubles and fix random in animations * makefile: added -DLFS_NO_DEBUG * Updater: path len constant dedup * Updater: checking for hardware version match before parsing manifest * LD: moved _DRIVER_CONTEXT sections to .bss, where they belong. * LD: avoiding PROBGITS warning, moved _CONTEXT to data * Updater: Added version check on update package - refusing to install outdated Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									dfdc33b076
								
							
						
					
					
						commit
						597ee5b939
					
				| @ -68,7 +68,7 @@ static void bt_cli_command_carrier_rx(Cli* cli, string_t args, void* context) { | |||||||
| 
 | 
 | ||||||
|         while(!cli_cmd_interrupt_received(cli)) { |         while(!cli_cmd_interrupt_received(cli)) { | ||||||
|             osDelay(250); |             osDelay(250); | ||||||
|             printf("RSSI: %6.1f dB\r", furi_hal_bt_get_rssi()); |             printf("RSSI: %6.1f dB\r", (double)furi_hal_bt_get_rssi()); | ||||||
|             fflush(stdout); |             fflush(stdout); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -140,11 +140,9 @@ static void bt_cli_command_packet_rx(Cli* cli, string_t args, void* context) { | |||||||
|         printf("Press CTRL+C to stop\r\n"); |         printf("Press CTRL+C to stop\r\n"); | ||||||
|         furi_hal_bt_start_packet_rx(channel, datarate); |         furi_hal_bt_start_packet_rx(channel, datarate); | ||||||
| 
 | 
 | ||||||
|         float rssi_raw = 0; |  | ||||||
|         while(!cli_cmd_interrupt_received(cli)) { |         while(!cli_cmd_interrupt_received(cli)) { | ||||||
|             osDelay(250); |             osDelay(250); | ||||||
|             rssi_raw = furi_hal_bt_get_rssi(); |             printf("RSSI: %03.1f dB\r", (double)furi_hal_bt_get_rssi()); | ||||||
|             printf("RSSI: %03.1f dB\r", rssi_raw); |  | ||||||
|             fflush(stdout); |             fflush(stdout); | ||||||
|         } |         } | ||||||
|         uint16_t packets_received = furi_hal_bt_stop_packet_test(); |         uint16_t packets_received = furi_hal_bt_stop_packet_test(); | ||||||
|  | |||||||
| @ -99,7 +99,7 @@ static void bt_test_draw_callback(Canvas* canvas, void* _model) { | |||||||
|     canvas_draw_str(canvas, 6, 60, model->message); |     canvas_draw_str(canvas, 6, 60, model->message); | ||||||
|     if(model->state == BtTestStateStarted) { |     if(model->state == BtTestStateStarted) { | ||||||
|         if(model->rssi != 0.0f) { |         if(model->rssi != 0.0f) { | ||||||
|             snprintf(info_str, sizeof(info_str), "RSSI:%3.1f dB", model->rssi); |             snprintf(info_str, sizeof(info_str), "RSSI:%3.1f dB", (double)model->rssi); | ||||||
|             canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str); |             canvas_draw_str_aligned(canvas, 124, 60, AlignRight, AlignBottom, info_str); | ||||||
|         } |         } | ||||||
|     } else if(model->state == BtTestStateStopped) { |     } else if(model->state == BtTestStateStopped) { | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| #include <gui/view_stack.h> | #include <gui/view_stack.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
|  | #include <furi_hal.h> | ||||||
| #include <m-string.h> | #include <m-string.h> | ||||||
| #include <portmacro.h> | #include <portmacro.h> | ||||||
| #include <dolphin/dolphin.h> | #include <dolphin/dolphin.h> | ||||||
| @ -391,7 +392,7 @@ static StorageAnimation* | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     uint32_t lucky_number = random() % whole_weight; |     uint32_t lucky_number = furi_hal_random_get() % whole_weight; | ||||||
|     uint32_t weight = 0; |     uint32_t weight = 0; | ||||||
| 
 | 
 | ||||||
|     StorageAnimation* selected = NULL; |     StorageAnimation* selected = NULL; | ||||||
|  | |||||||
| @ -100,7 +100,8 @@ static const FrameBubble* | |||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     uint8_t index = random() % (active ? model->active_bubbles : model->passive_bubbles); |     uint8_t index = | ||||||
|  |         furi_hal_random_get() % (active ? model->active_bubbles : model->passive_bubbles); | ||||||
|     const BubbleAnimation* animation = model->current; |     const BubbleAnimation* animation = model->current; | ||||||
| 
 | 
 | ||||||
|     for(int i = 0; i < animation->frame_bubble_sequences_count; ++i) { |     for(int i = 0; i < animation->frame_bubble_sequences_count; ++i) { | ||||||
|  | |||||||
| @ -145,7 +145,7 @@ bool infrared_parser_is_raw_signal_valid( | |||||||
|             frequency); |             frequency); | ||||||
|         result = false; |         result = false; | ||||||
|     } else if((duty_cycle <= 0) || (duty_cycle > 1)) { |     } else if((duty_cycle <= 0) || (duty_cycle > 1)) { | ||||||
|         FURI_LOG_E(TAG, "Duty cycle is out of range (0 - 1): %f", duty_cycle); |         FURI_LOG_E(TAG, "Duty cycle is out of range (0 - 1): %f", (double)duty_cycle); | ||||||
|         result = false; |         result = false; | ||||||
|     } else if((timings_cnt <= 0) || (timings_cnt > MAX_TIMINGS_AMOUNT)) { |     } else if((timings_cnt <= 0) || (timings_cnt > MAX_TIMINGS_AMOUNT)) { | ||||||
|         FURI_LOG_E( |         FURI_LOG_E( | ||||||
|  | |||||||
| @ -29,6 +29,8 @@ void LfRfidViewTuneVM::view_draw_callback(Canvas* canvas, void* _model) { | |||||||
| 
 | 
 | ||||||
|     constexpr uint8_t buffer_size = 128; |     constexpr uint8_t buffer_size = 128; | ||||||
|     char buffer[buffer_size + 1]; |     char buffer[buffer_size + 1]; | ||||||
|  |     double freq = ((float)SystemCoreClock / ((float)model->ARR + 1)); | ||||||
|  |     double duty = ((float)model->CCR + 1) / ((float)model->ARR + 1) * 100.0f; | ||||||
|     snprintf( |     snprintf( | ||||||
|         buffer, |         buffer, | ||||||
|         buffer_size, |         buffer_size, | ||||||
| @ -38,10 +40,10 @@ void LfRfidViewTuneVM::view_draw_callback(Canvas* canvas, void* _model) { | |||||||
|         "duty = %.4f", |         "duty = %.4f", | ||||||
|         model->pos == 0 ? ">" : "", |         model->pos == 0 ? ">" : "", | ||||||
|         model->ARR, |         model->ARR, | ||||||
|         (float)SystemCoreClock / ((float)model->ARR + 1), |         freq, | ||||||
|         model->pos == 1 ? ">" : "", |         model->pos == 1 ? ">" : "", | ||||||
|         model->CCR, |         model->CCR, | ||||||
|         ((float)model->CCR + 1) / ((float)model->ARR + 1) * 100.0f); |         duty); | ||||||
|     elements_multiline_text_aligned(canvas, 2, 2, AlignLeft, AlignTop, buffer); |     elements_multiline_text_aligned(canvas, 2, 2, AlignLeft, AlignTop, buffer); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -275,15 +275,22 @@ static void rpc_system_system_update_request_process(const PB_Main* request, voi | |||||||
|     RpcSession* session = (RpcSession*)context; |     RpcSession* session = (RpcSession*)context; | ||||||
|     furi_assert(session); |     furi_assert(session); | ||||||
| 
 | 
 | ||||||
|     bool update_prepare_result = |     UpdatePrepareResult update_prepare_result = | ||||||
|         update_operation_prepare(request->content.system_update_request.update_manifest) == |         update_operation_prepare(request->content.system_update_request.update_manifest); | ||||||
|         UpdatePrepareResultOK; |     /* RPC enum does not have such entry; setting to closest one */ | ||||||
|  |     if(update_prepare_result == UpdatePrepareResultOutdatedManifestVersion) { | ||||||
|  |         update_prepare_result = UpdatePrepareResultManifestInvalid; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     PB_Main* response = malloc(sizeof(PB_Main)); |     PB_Main* response = malloc(sizeof(PB_Main)); | ||||||
|     response->command_id = request->command_id; |     response->command_id = request->command_id; | ||||||
|     response->has_next = false; |     response->has_next = false; | ||||||
|     response->command_status = update_prepare_result ? PB_CommandStatus_OK : |     response->command_status = (update_prepare_result == UpdatePrepareResultOK) ? | ||||||
|                                                        PB_CommandStatus_ERROR_INVALID_PARAMETERS; |                                    PB_CommandStatus_OK : | ||||||
|  |                                    PB_CommandStatus_ERROR_INVALID_PARAMETERS; | ||||||
|  |     response->which_content = PB_Main_system_update_response_tag; | ||||||
|  |     response->content.system_update_response.code = | ||||||
|  |         (PB_System_UpdateResponse_UpdateResultCode)update_prepare_result; | ||||||
|     rpc_send_and_release(session, response); |     rpc_send_and_release(session, response); | ||||||
|     free(response); |     free(response); | ||||||
| } | } | ||||||
|  | |||||||
| @ -94,7 +94,7 @@ void subghz_cli_command_rx_carrier(Cli* cli, string_t args, void* context) { | |||||||
| 
 | 
 | ||||||
|     while(!cli_cmd_interrupt_received(cli)) { |     while(!cli_cmd_interrupt_received(cli)) { | ||||||
|         osDelay(250); |         osDelay(250); | ||||||
|         printf("RSSI: %03.1fdbm\r", furi_hal_subghz_get_rssi()); |         printf("RSSI: %03.1fdbm\r", (double)furi_hal_subghz_get_rssi()); | ||||||
|         fflush(stdout); |         fflush(stdout); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -70,7 +70,7 @@ void unit_tests_cli(Cli* cli, string_t args, void* context) { | |||||||
| 
 | 
 | ||||||
|         cycle_counter = (furi_hal_get_tick() - cycle_counter); |         cycle_counter = (furi_hal_get_tick() - cycle_counter); | ||||||
| 
 | 
 | ||||||
|         FURI_LOG_I(TAG, "Consumed: %0.2fs", (float)cycle_counter / 1000); |         FURI_LOG_I(TAG, "Consumed: %u us", cycle_counter); | ||||||
| 
 | 
 | ||||||
|         if(test_result == 0) { |         if(test_result == 0) { | ||||||
|             furi_hal_delay_ms(200); /* wait for tested services and apps to deallocate */ |             furi_hal_delay_ms(200); /* wait for tested services and apps to deallocate */ | ||||||
|  | |||||||
| @ -52,7 +52,7 @@ static void updater_cli_restore(string_t args) { | |||||||
| static void updater_cli_help(string_t args) { | static void updater_cli_help(string_t args) { | ||||||
|     UNUSED(args); |     UNUSED(args); | ||||||
|     printf("Commands:\r\n" |     printf("Commands:\r\n" | ||||||
|            "\tinstall /ext/update/PACKAGE/update.fuf - verify & apply update package\r\n" |            "\tinstall /ext/path/to/update.fuf - verify & apply update package\r\n" | ||||||
|            "\tbackup /ext/path/to/backup.tar - create internal storage backup\r\n" |            "\tbackup /ext/path/to/backup.tar - create internal storage backup\r\n" | ||||||
|            "\trestore /ext/path/to/backup.tar - restore internal storage backup\r\n"); |            "\trestore /ext/path/to/backup.tar - restore internal storage backup\r\n"); | ||||||
| } | } | ||||||
|  | |||||||
| @ -260,16 +260,18 @@ bool update_task_parse_manifest(UpdateTask* update_task) { | |||||||
|     string_init(manifest_path); |     string_init(manifest_path); | ||||||
| 
 | 
 | ||||||
|     do { |     do { | ||||||
|         update_task_set_progress(update_task, UpdateTaskStageProgress, 10); |         update_task_set_progress(update_task, UpdateTaskStageProgress, 13); | ||||||
|         if(!update_operation_get_current_package_path( |         if(!furi_hal_version_do_i_belong_here()) { | ||||||
|                update_task->storage, update_task->update_path)) { |  | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         path_concat( |         update_task_set_progress(update_task, UpdateTaskStageProgress, 20); | ||||||
|             string_get_cstr(update_task->update_path), |         if(!update_operation_get_current_package_manifest_path( | ||||||
|             UPDATE_MANIFEST_DEFAULT_NAME, |                update_task->storage, manifest_path)) { | ||||||
|             manifest_path); |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         path_extract_dirname(string_get_cstr(manifest_path), update_task->update_path); | ||||||
|         update_task_set_progress(update_task, UpdateTaskStageProgress, 30); |         update_task_set_progress(update_task, UpdateTaskStageProgress, 30); | ||||||
| 
 | 
 | ||||||
|         UpdateManifest* manifest = update_task->manifest; |         UpdateManifest* manifest = update_task->manifest; | ||||||
| @ -277,6 +279,16 @@ bool update_task_parse_manifest(UpdateTask* update_task) { | |||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         update_task_set_progress(update_task, UpdateTaskStageProgress, 40); | ||||||
|  |         if(manifest->manifest_version < UPDATE_OPERATION_MIN_MANIFEST_VERSION) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         update_task_set_progress(update_task, UpdateTaskStageProgress, 50); | ||||||
|  |         if(manifest->target != furi_hal_version_get_hw_target()) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         update_task->state.groups = update_task_get_task_groups(update_task); |         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); |         for(size_t stage_counter = 0; stage_counter < COUNT_OF(update_task_stage_progress); | ||||||
|             ++stage_counter) { |             ++stage_counter) { | ||||||
| @ -286,13 +298,13 @@ bool update_task_parse_manifest(UpdateTask* update_task) { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         update_task_set_progress(update_task, UpdateTaskStageProgress, 50); |         update_task_set_progress(update_task, UpdateTaskStageProgress, 60); | ||||||
|         if((update_task->state.groups & UpdateTaskStageGroupFirmware) && |         if((update_task->state.groups & UpdateTaskStageGroupFirmware) && | ||||||
|            !update_task_check_file_exists(update_task, manifest->firmware_dfu_image)) { |            !update_task_check_file_exists(update_task, manifest->firmware_dfu_image)) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         update_task_set_progress(update_task, UpdateTaskStageProgress, 70); |         update_task_set_progress(update_task, UpdateTaskStageProgress, 80); | ||||||
|         if((update_task->state.groups & UpdateTaskStageGroupRadio) && |         if((update_task->state.groups & UpdateTaskStageGroupRadio) && | ||||||
|            (!update_task_check_file_exists(update_task, manifest->radio_image) || |            (!update_task_check_file_exists(update_task, manifest->radio_image) || | ||||||
|             (manifest->radio_version.version.type == 0))) { |             (manifest->radio_version.version.type == 0))) { | ||||||
|  | |||||||
| @ -63,7 +63,6 @@ static bool update_task_post_update(UpdateTask* update_task) { | |||||||
| 
 | 
 | ||||||
|     TarArchive* archive = tar_archive_alloc(update_task->storage); |     TarArchive* archive = tar_archive_alloc(update_task->storage); | ||||||
|     do { |     do { | ||||||
|         CHECK_RESULT(update_task_parse_manifest(update_task)); |  | ||||||
|         path_concat( |         path_concat( | ||||||
|             string_get_cstr(update_task->update_path), LFS_BACKUP_DEFAULT_FILENAME, file_path); |             string_get_cstr(update_task->update_path), LFS_BACKUP_DEFAULT_FILENAME, file_path); | ||||||
| 
 | 
 | ||||||
| @ -114,7 +113,7 @@ int32_t update_task_worker_backup_restore(void* context) { | |||||||
|         return UPDATE_TASK_NOERR; |         return UPDATE_TASK_NOERR; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(!update_operation_get_current_package_path(update_task->storage, update_task->update_path)) { |     if(!update_task_parse_manifest(update_task)) { | ||||||
|         return UPDATE_TASK_FAILED; |         return UPDATE_TASK_FAILED; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -103,6 +103,7 @@ typedef struct _PB_Main { | |||||||
|         PB_Storage_BackupRestoreRequest storage_backup_restore_request; |         PB_Storage_BackupRestoreRequest storage_backup_restore_request; | ||||||
|         PB_System_PowerInfoRequest system_power_info_request; |         PB_System_PowerInfoRequest system_power_info_request; | ||||||
|         PB_System_PowerInfoResponse system_power_info_response; |         PB_System_PowerInfoResponse system_power_info_response; | ||||||
|  |         PB_System_UpdateResponse system_update_response; | ||||||
|     } content;  |     } content;  | ||||||
| } PB_Main; | } PB_Main; | ||||||
| 
 | 
 | ||||||
| @ -171,6 +172,7 @@ extern "C" { | |||||||
| #define PB_Main_storage_backup_restore_request_tag 43 | #define PB_Main_storage_backup_restore_request_tag 43 | ||||||
| #define PB_Main_system_power_info_request_tag    44 | #define PB_Main_system_power_info_request_tag    44 | ||||||
| #define PB_Main_system_power_info_response_tag   45 | #define PB_Main_system_power_info_response_tag   45 | ||||||
|  | #define PB_Main_system_update_response_tag       46 | ||||||
| 
 | 
 | ||||||
| /* Struct field encoding specification for nanopb */ | /* Struct field encoding specification for nanopb */ | ||||||
| #define PB_Empty_FIELDLIST(X, a) \ | #define PB_Empty_FIELDLIST(X, a) \ | ||||||
| @ -228,7 +230,8 @@ X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_update_request,content.system | |||||||
| X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_backup_create_request,content.storage_backup_create_request),  42) \ | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_backup_create_request,content.storage_backup_create_request),  42) \ | ||||||
| X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_backup_restore_request,content.storage_backup_restore_request),  43) \ | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,storage_backup_restore_request,content.storage_backup_restore_request),  43) \ | ||||||
| X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_power_info_request,content.system_power_info_request),  44) \ | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_power_info_request,content.system_power_info_request),  44) \ | ||||||
| X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_power_info_response,content.system_power_info_response),  45) | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_power_info_response,content.system_power_info_response),  45) \ | ||||||
|  | X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_update_response,content.system_update_response),  46) | ||||||
| #define PB_Main_CALLBACK NULL | #define PB_Main_CALLBACK NULL | ||||||
| #define PB_Main_DEFAULT NULL | #define PB_Main_DEFAULT NULL | ||||||
| #define PB_Main_content_empty_MSGTYPE PB_Empty | #define PB_Main_content_empty_MSGTYPE PB_Empty | ||||||
| @ -273,6 +276,7 @@ X(a, STATIC,   ONEOF,    MSG_W_CB, (content,system_power_info_response,content.s | |||||||
| #define PB_Main_content_storage_backup_restore_request_MSGTYPE PB_Storage_BackupRestoreRequest | #define PB_Main_content_storage_backup_restore_request_MSGTYPE PB_Storage_BackupRestoreRequest | ||||||
| #define PB_Main_content_system_power_info_request_MSGTYPE PB_System_PowerInfoRequest | #define PB_Main_content_system_power_info_request_MSGTYPE PB_System_PowerInfoRequest | ||||||
| #define PB_Main_content_system_power_info_response_MSGTYPE PB_System_PowerInfoResponse | #define PB_Main_content_system_power_info_response_MSGTYPE PB_System_PowerInfoResponse | ||||||
|  | #define PB_Main_content_system_update_response_MSGTYPE PB_System_UpdateResponse | ||||||
| 
 | 
 | ||||||
| extern const pb_msgdesc_t PB_Empty_msg; | extern const pb_msgdesc_t PB_Empty_msg; | ||||||
| extern const pb_msgdesc_t PB_StopSession_msg; | extern const pb_msgdesc_t PB_StopSession_msg; | ||||||
|  | |||||||
| @ -1,3 +1,3 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #define PROTOBUF_MAJOR_VERSION 0 | #define PROTOBUF_MAJOR_VERSION 0 | ||||||
| #define PROTOBUF_MINOR_VERSION 6 | #define PROTOBUF_MINOR_VERSION 7 | ||||||
|  | |||||||
| @ -48,6 +48,9 @@ PB_BIND(PB_System_ProtobufVersionResponse, PB_System_ProtobufVersionResponse, AU | |||||||
| PB_BIND(PB_System_UpdateRequest, PB_System_UpdateRequest, AUTO) | PB_BIND(PB_System_UpdateRequest, PB_System_UpdateRequest, AUTO) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | PB_BIND(PB_System_UpdateResponse, PB_System_UpdateResponse, AUTO) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| PB_BIND(PB_System_PowerInfoRequest, PB_System_PowerInfoRequest, AUTO) | PB_BIND(PB_System_PowerInfoRequest, PB_System_PowerInfoRequest, AUTO) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -56,3 +59,4 @@ PB_BIND(PB_System_PowerInfoResponse, PB_System_PowerInfoResponse, AUTO) | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | |||||||
| @ -16,6 +16,17 @@ typedef enum _PB_System_RebootRequest_RebootMode { | |||||||
|     PB_System_RebootRequest_RebootMode_UPDATE = 2  |     PB_System_RebootRequest_RebootMode_UPDATE = 2  | ||||||
| } PB_System_RebootRequest_RebootMode; | } PB_System_RebootRequest_RebootMode; | ||||||
| 
 | 
 | ||||||
|  | typedef enum _PB_System_UpdateResponse_UpdateResultCode {  | ||||||
|  |     PB_System_UpdateResponse_UpdateResultCode_OK = 0,  | ||||||
|  |     PB_System_UpdateResponse_UpdateResultCode_ManifestPathInvalid = 1,  | ||||||
|  |     PB_System_UpdateResponse_UpdateResultCode_ManifestFolderNotFound = 2,  | ||||||
|  |     PB_System_UpdateResponse_UpdateResultCode_ManifestInvalid = 3,  | ||||||
|  |     PB_System_UpdateResponse_UpdateResultCode_StageMissing = 4,  | ||||||
|  |     PB_System_UpdateResponse_UpdateResultCode_StageIntegrityError = 5,  | ||||||
|  |     PB_System_UpdateResponse_UpdateResultCode_ManifestPointerError = 6,  | ||||||
|  |     PB_System_UpdateResponse_UpdateResultCode_TargetMismatch = 7  | ||||||
|  | } PB_System_UpdateResponse_UpdateResultCode; | ||||||
|  | 
 | ||||||
| /* Struct definitions */ | /* Struct definitions */ | ||||||
| typedef struct _PB_System_DeviceInfoRequest {  | typedef struct _PB_System_DeviceInfoRequest {  | ||||||
|     char dummy_field; |     char dummy_field; | ||||||
| @ -84,6 +95,10 @@ typedef struct _PB_System_RebootRequest { | |||||||
|     PB_System_RebootRequest_RebootMode mode;  |     PB_System_RebootRequest_RebootMode mode;  | ||||||
| } PB_System_RebootRequest; | } PB_System_RebootRequest; | ||||||
| 
 | 
 | ||||||
|  | typedef struct _PB_System_UpdateResponse {  | ||||||
|  |     PB_System_UpdateResponse_UpdateResultCode code;  | ||||||
|  | } PB_System_UpdateResponse; | ||||||
|  | 
 | ||||||
| typedef struct _PB_System_GetDateTimeResponse {  | typedef struct _PB_System_GetDateTimeResponse {  | ||||||
|     bool has_datetime; |     bool has_datetime; | ||||||
|     PB_System_DateTime datetime;  |     PB_System_DateTime datetime;  | ||||||
| @ -100,6 +115,10 @@ typedef struct _PB_System_SetDateTimeRequest { | |||||||
| #define _PB_System_RebootRequest_RebootMode_MAX PB_System_RebootRequest_RebootMode_UPDATE | #define _PB_System_RebootRequest_RebootMode_MAX PB_System_RebootRequest_RebootMode_UPDATE | ||||||
| #define _PB_System_RebootRequest_RebootMode_ARRAYSIZE ((PB_System_RebootRequest_RebootMode)(PB_System_RebootRequest_RebootMode_UPDATE+1)) | #define _PB_System_RebootRequest_RebootMode_ARRAYSIZE ((PB_System_RebootRequest_RebootMode)(PB_System_RebootRequest_RebootMode_UPDATE+1)) | ||||||
| 
 | 
 | ||||||
|  | #define _PB_System_UpdateResponse_UpdateResultCode_MIN PB_System_UpdateResponse_UpdateResultCode_OK | ||||||
|  | #define _PB_System_UpdateResponse_UpdateResultCode_MAX PB_System_UpdateResponse_UpdateResultCode_TargetMismatch | ||||||
|  | #define _PB_System_UpdateResponse_UpdateResultCode_ARRAYSIZE ((PB_System_UpdateResponse_UpdateResultCode)(PB_System_UpdateResponse_UpdateResultCode_TargetMismatch+1)) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| @ -120,6 +139,7 @@ extern "C" { | |||||||
| #define PB_System_ProtobufVersionRequest_init_default {0} | #define PB_System_ProtobufVersionRequest_init_default {0} | ||||||
| #define PB_System_ProtobufVersionResponse_init_default {0, 0} | #define PB_System_ProtobufVersionResponse_init_default {0, 0} | ||||||
| #define PB_System_UpdateRequest_init_default     {NULL} | #define PB_System_UpdateRequest_init_default     {NULL} | ||||||
|  | #define PB_System_UpdateResponse_init_default    {_PB_System_UpdateResponse_UpdateResultCode_MIN} | ||||||
| #define PB_System_PowerInfoRequest_init_default  {0} | #define PB_System_PowerInfoRequest_init_default  {0} | ||||||
| #define PB_System_PowerInfoResponse_init_default {NULL, NULL} | #define PB_System_PowerInfoResponse_init_default {NULL, NULL} | ||||||
| #define PB_System_PingRequest_init_zero          {NULL} | #define PB_System_PingRequest_init_zero          {NULL} | ||||||
| @ -136,6 +156,7 @@ extern "C" { | |||||||
| #define PB_System_ProtobufVersionRequest_init_zero {0} | #define PB_System_ProtobufVersionRequest_init_zero {0} | ||||||
| #define PB_System_ProtobufVersionResponse_init_zero {0, 0} | #define PB_System_ProtobufVersionResponse_init_zero {0, 0} | ||||||
| #define PB_System_UpdateRequest_init_zero        {NULL} | #define PB_System_UpdateRequest_init_zero        {NULL} | ||||||
|  | #define PB_System_UpdateResponse_init_zero       {_PB_System_UpdateResponse_UpdateResultCode_MIN} | ||||||
| #define PB_System_PowerInfoRequest_init_zero     {0} | #define PB_System_PowerInfoRequest_init_zero     {0} | ||||||
| #define PB_System_PowerInfoResponse_init_zero    {NULL, NULL} | #define PB_System_PowerInfoResponse_init_zero    {NULL, NULL} | ||||||
| 
 | 
 | ||||||
| @ -157,6 +178,7 @@ extern "C" { | |||||||
| #define PB_System_ProtobufVersionResponse_major_tag 1 | #define PB_System_ProtobufVersionResponse_major_tag 1 | ||||||
| #define PB_System_ProtobufVersionResponse_minor_tag 2 | #define PB_System_ProtobufVersionResponse_minor_tag 2 | ||||||
| #define PB_System_RebootRequest_mode_tag         1 | #define PB_System_RebootRequest_mode_tag         1 | ||||||
|  | #define PB_System_UpdateResponse_code_tag        1 | ||||||
| #define PB_System_GetDateTimeResponse_datetime_tag 1 | #define PB_System_GetDateTimeResponse_datetime_tag 1 | ||||||
| #define PB_System_SetDateTimeRequest_datetime_tag 1 | #define PB_System_SetDateTimeRequest_datetime_tag 1 | ||||||
| 
 | 
 | ||||||
| @ -241,6 +263,11 @@ X(a, POINTER,  SINGULAR, STRING,   update_manifest,   1) | |||||||
| #define PB_System_UpdateRequest_CALLBACK NULL | #define PB_System_UpdateRequest_CALLBACK NULL | ||||||
| #define PB_System_UpdateRequest_DEFAULT NULL | #define PB_System_UpdateRequest_DEFAULT NULL | ||||||
| 
 | 
 | ||||||
|  | #define PB_System_UpdateResponse_FIELDLIST(X, a) \ | ||||||
|  | X(a, STATIC,   SINGULAR, UENUM,    code,              1) | ||||||
|  | #define PB_System_UpdateResponse_CALLBACK NULL | ||||||
|  | #define PB_System_UpdateResponse_DEFAULT NULL | ||||||
|  | 
 | ||||||
| #define PB_System_PowerInfoRequest_FIELDLIST(X, a) \ | #define PB_System_PowerInfoRequest_FIELDLIST(X, a) \ | ||||||
| 
 | 
 | ||||||
| #define PB_System_PowerInfoRequest_CALLBACK NULL | #define PB_System_PowerInfoRequest_CALLBACK NULL | ||||||
| @ -266,6 +293,7 @@ extern const pb_msgdesc_t PB_System_PlayAudiovisualAlertRequest_msg; | |||||||
| extern const pb_msgdesc_t PB_System_ProtobufVersionRequest_msg; | extern const pb_msgdesc_t PB_System_ProtobufVersionRequest_msg; | ||||||
| extern const pb_msgdesc_t PB_System_ProtobufVersionResponse_msg; | extern const pb_msgdesc_t PB_System_ProtobufVersionResponse_msg; | ||||||
| extern const pb_msgdesc_t PB_System_UpdateRequest_msg; | extern const pb_msgdesc_t PB_System_UpdateRequest_msg; | ||||||
|  | extern const pb_msgdesc_t PB_System_UpdateResponse_msg; | ||||||
| extern const pb_msgdesc_t PB_System_PowerInfoRequest_msg; | extern const pb_msgdesc_t PB_System_PowerInfoRequest_msg; | ||||||
| extern const pb_msgdesc_t PB_System_PowerInfoResponse_msg; | extern const pb_msgdesc_t PB_System_PowerInfoResponse_msg; | ||||||
| 
 | 
 | ||||||
| @ -284,6 +312,7 @@ extern const pb_msgdesc_t PB_System_PowerInfoResponse_msg; | |||||||
| #define PB_System_ProtobufVersionRequest_fields &PB_System_ProtobufVersionRequest_msg | #define PB_System_ProtobufVersionRequest_fields &PB_System_ProtobufVersionRequest_msg | ||||||
| #define PB_System_ProtobufVersionResponse_fields &PB_System_ProtobufVersionResponse_msg | #define PB_System_ProtobufVersionResponse_fields &PB_System_ProtobufVersionResponse_msg | ||||||
| #define PB_System_UpdateRequest_fields &PB_System_UpdateRequest_msg | #define PB_System_UpdateRequest_fields &PB_System_UpdateRequest_msg | ||||||
|  | #define PB_System_UpdateResponse_fields &PB_System_UpdateResponse_msg | ||||||
| #define PB_System_PowerInfoRequest_fields &PB_System_PowerInfoRequest_msg | #define PB_System_PowerInfoRequest_fields &PB_System_PowerInfoRequest_msg | ||||||
| #define PB_System_PowerInfoResponse_fields &PB_System_PowerInfoResponse_msg | #define PB_System_PowerInfoResponse_fields &PB_System_PowerInfoResponse_msg | ||||||
| 
 | 
 | ||||||
| @ -304,6 +333,7 @@ extern const pb_msgdesc_t PB_System_PowerInfoResponse_msg; | |||||||
| #define PB_System_ProtobufVersionResponse_size   12 | #define PB_System_ProtobufVersionResponse_size   12 | ||||||
| #define PB_System_RebootRequest_size             2 | #define PB_System_RebootRequest_size             2 | ||||||
| #define PB_System_SetDateTimeRequest_size        24 | #define PB_System_SetDateTimeRequest_size        24 | ||||||
|  | #define PB_System_UpdateResponse_size            2 | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } /* extern "C" */ | } /* extern "C" */ | ||||||
|  | |||||||
| @ -1 +1 @@ | |||||||
| Subproject commit 0ad90705b9434b6f8fb2c4b605069f0d56d8cc70 | Subproject commit ffa62429f3c678537e0e883a3a8c3ae5f1398ed4 | ||||||
| @ -37,6 +37,7 @@ | |||||||
| #include "memmgr_heap.h" | #include "memmgr_heap.h" | ||||||
| #include "check.h" | #include "check.h" | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
|  | #include <stdio.h> | ||||||
| #include <cmsis_os2.h> | #include <cmsis_os2.h> | ||||||
| #include <stm32wbxx.h> | #include <stm32wbxx.h> | ||||||
| #include <furi_hal_console.h> | #include <furi_hal_console.h> | ||||||
|  | |||||||
| @ -48,7 +48,7 @@ int main() { | |||||||
|         flipper_boot_update_exec(); |         flipper_boot_update_exec(); | ||||||
|         // if things go nice, we shouldn't reach this point.
 |         // if things go nice, we shouldn't reach this point.
 | ||||||
|         // But if we do, abandon to avoid bootloops
 |         // But if we do, abandon to avoid bootloops
 | ||||||
|         update_operation_disarm(); |         furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); | ||||||
|         furi_hal_power_reset(); |         furi_hal_power_reset(); | ||||||
|     } else { |     } else { | ||||||
|         furi_hal_light_sequence("rgb G"); |         furi_hal_light_sequence("rgb G"); | ||||||
|  | |||||||
| @ -11,9 +11,10 @@ | |||||||
| #include <toolbox/path.h> | #include <toolbox/path.h> | ||||||
| #include <toolbox/crc32_calc.h> | #include <toolbox/crc32_calc.h> | ||||||
| 
 | 
 | ||||||
| static FATFS* pfs = NULL; | #define FS_ROOT_PATH "/" | ||||||
|  | #define UPDATE_POINTER_FILE_PATH FS_ROOT_PATH UPDATE_MANIFEST_POINTER_FILE_NAME | ||||||
| 
 | 
 | ||||||
| static const char FS_ROOT_PATH[] = "/"; | static FATFS* pfs = NULL; | ||||||
| 
 | 
 | ||||||
| #define CHECK_FRESULT(result)   \ | #define CHECK_FRESULT(result)   \ | ||||||
|     {                           \ |     {                           \ | ||||||
| @ -100,41 +101,34 @@ static bool flipper_update_load_stage(const string_t work_dir, UpdateManifest* m | |||||||
|     return false; |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static bool flipper_update_get_work_directory(string_t out_dir) { | static bool flipper_update_get_manifest_path(string_t out_path) { | ||||||
|     const uint32_t update_index = furi_hal_rtc_get_register(FuriHalRtcRegisterUpdateFolderFSIndex); |  | ||||||
|     if(update_index == UPDATE_OPERATION_ROOT_DIR_PACKAGE_MAGIC) { |  | ||||||
|         string_set(out_dir, UPDATE_DIR_DEFAULT_REL_PATH); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     DIR dir; |  | ||||||
|     UINT entry_idx = 0; |  | ||||||
|     FILINFO fno; |  | ||||||
|     CHECK_FRESULT(f_opendir(&dir, UPDATE_DIR_DEFAULT_REL_PATH)); |  | ||||||
|     string_set(out_dir, UPDATE_DIR_DEFAULT_REL_PATH); |  | ||||||
| 
 |  | ||||||
|     while(f_readdir(&dir, &fno) == FR_OK) { |  | ||||||
|         entry_idx++; |  | ||||||
|         if(fno.fname[0] == '\0') { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|         if(entry_idx == update_index) { |  | ||||||
|             path_append(out_dir, fno.fname); |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     string_reset(out_dir); |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static UpdateManifest* flipper_update_process_manifest(const string_t work_dir) { |  | ||||||
|     FIL file; |     FIL file; | ||||||
|     FILINFO stat; |     FILINFO stat; | ||||||
|  |     uint16_t size_read = 0; | ||||||
|  |     char manifest_name_buf[UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN] = {0}; | ||||||
| 
 | 
 | ||||||
|     string_t manifest_path; |     string_reset(out_path); | ||||||
|     string_init_set(manifest_path, work_dir); |     CHECK_FRESULT(f_stat(UPDATE_POINTER_FILE_PATH, &stat)); | ||||||
|     path_append(manifest_path, UPDATE_MANIFEST_DEFAULT_NAME); |     CHECK_FRESULT(f_open(&file, UPDATE_POINTER_FILE_PATH, FA_OPEN_EXISTING | FA_READ)); | ||||||
|  |     do { | ||||||
|  |         if(f_read(&file, manifest_name_buf, UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN, &size_read) != | ||||||
|  |            FR_OK) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if((size_read == 0) || (size_read == UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN)) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         string_set_str(out_path, manifest_name_buf); | ||||||
|  |         string_right(out_path, strlen("/ext")); | ||||||
|  |     } while(0); | ||||||
|  |     f_close(&file); | ||||||
|  |     return !string_empty_p(out_path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static UpdateManifest* flipper_update_process_manifest(const string_t manifest_path) { | ||||||
|  |     FIL file; | ||||||
|  |     FILINFO stat; | ||||||
| 
 | 
 | ||||||
|     CHECK_FRESULT(f_stat(string_get_cstr(manifest_path), &stat)); |     CHECK_FRESULT(f_stat(string_get_cstr(manifest_path), &stat)); | ||||||
|     CHECK_FRESULT(f_open(&file, string_get_cstr(manifest_path), FA_OPEN_EXISTING | FA_READ)); |     CHECK_FRESULT(f_open(&file, string_get_cstr(manifest_path), FA_OPEN_EXISTING | FA_READ)); | ||||||
| @ -164,7 +158,7 @@ static UpdateManifest* flipper_update_process_manifest(const string_t work_dir) | |||||||
|         } |         } | ||||||
|     } while(false); |     } while(false); | ||||||
| 
 | 
 | ||||||
|     string_clear(manifest_path); |     f_close(&file); | ||||||
|     free(manifest_data); |     free(manifest_data); | ||||||
|     return manifest; |     return manifest; | ||||||
| } | } | ||||||
| @ -174,22 +168,25 @@ void flipper_boot_update_exec() { | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     string_t work_dir; |     string_t work_dir, manifest_path; | ||||||
|     string_init(work_dir); |     string_init(work_dir); | ||||||
|  |     string_init(manifest_path); | ||||||
|     do { |     do { | ||||||
|         if(!flipper_update_get_work_directory(work_dir)) { |         if(!flipper_update_get_manifest_path(manifest_path)) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         UpdateManifest* manifest = flipper_update_process_manifest(work_dir); |         UpdateManifest* manifest = flipper_update_process_manifest(manifest_path); | ||||||
|         if(!manifest) { |         if(!manifest) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         path_extract_dirname(string_get_cstr(manifest_path), work_dir); | ||||||
|         if(!flipper_update_load_stage(work_dir, manifest)) { |         if(!flipper_update_load_stage(work_dir, manifest)) { | ||||||
|             update_manifest_free(manifest); |             update_manifest_free(manifest); | ||||||
|         } |         } | ||||||
|     } while(false); |     } while(false); | ||||||
|  |     string_clear(manifest_path); | ||||||
|     string_clear(work_dir); |     string_clear(work_dir); | ||||||
|     free(pfs); |     free(pfs); | ||||||
| } | } | ||||||
|  | |||||||
| @ -7,6 +7,8 @@ | |||||||
| 
 | 
 | ||||||
| #include <hw_conf.h> | #include <hw_conf.h> | ||||||
| 
 | 
 | ||||||
|  | #define TAG "FuriHalRandom" | ||||||
|  | 
 | ||||||
| uint32_t furi_hal_random_get() { | uint32_t furi_hal_random_get() { | ||||||
|     while(LL_HSEM_1StepLock(HSEM, CFG_HW_RNG_SEMID)) |     while(LL_HSEM_1StepLock(HSEM, CFG_HW_RNG_SEMID)) | ||||||
|         ; |         ; | ||||||
| @ -51,9 +53,13 @@ void furi_hal_random_fill_buf(uint8_t* buf, uint32_t len) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void srand(unsigned seed) { | void srand(unsigned seed) { | ||||||
|     UNUSED(seed); // FIXME!
 |     UNUSED(seed); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int rand() { | int rand() { | ||||||
|     return (furi_hal_random_get() & RAND_MAX); |     return (furi_hal_random_get() & RAND_MAX); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | long random() { | ||||||
|  |     return (furi_hal_random_get() & RAND_MAX); | ||||||
|  | } | ||||||
|  | |||||||
| @ -972,9 +972,9 @@ void furi_hal_subghz_stop_async_tx() { | |||||||
|     FURI_LOG_D( |     FURI_LOG_D( | ||||||
|         TAG, |         TAG, | ||||||
|         "Async TX Radio stats: on %0.0fus, off %0.0fus, DutyCycle: %0.0f%%", |         "Async TX Radio stats: on %0.0fus, off %0.0fus, DutyCycle: %0.0f%%", | ||||||
|         (float)furi_hal_subghz_async_tx.duty_high, |         (double)furi_hal_subghz_async_tx.duty_high, | ||||||
|         (float)furi_hal_subghz_async_tx.duty_low, |         (double)furi_hal_subghz_async_tx.duty_low, | ||||||
|         duty_cycle); |         (double)duty_cycle); | ||||||
| 
 | 
 | ||||||
|     furi_hal_subghz_state = SubGhzStateIdle; |     furi_hal_subghz_state = SubGhzStateIdle; | ||||||
| } | } | ||||||
|  | |||||||
| @ -135,6 +135,7 @@ SECTIONS | |||||||
|     _sdata = .;        /* create a global symbol at data start */ |     _sdata = .;        /* create a global symbol at data start */ | ||||||
|     *(.data)           /* .data sections */ |     *(.data)           /* .data sections */ | ||||||
|     *(.data*)          /* .data* sections */ |     *(.data*)          /* .data* sections */ | ||||||
|  |     *(*_DRIVER_CONTEXT) | ||||||
| 
 | 
 | ||||||
|     . = ALIGN(4); |     . = ALIGN(4); | ||||||
|     _edata = .;        /* define a global symbol at data end */ |     _edata = .;        /* define a global symbol at data end */ | ||||||
| @ -158,7 +159,7 @@ SECTIONS | |||||||
|   } >RAM1 |   } >RAM1 | ||||||
| 
 | 
 | ||||||
|   /* User_heap_stack section, used to check that there is enough RAM left */ |   /* User_heap_stack section, used to check that there is enough RAM left */ | ||||||
|   ._user_heap_stack : |   ._user_heap_stack(NOLOAD): | ||||||
|   { |   { | ||||||
|     . = ALIGN(8); |     . = ALIGN(8); | ||||||
|     __heap_start__ = .; |     __heap_start__ = .; | ||||||
|  | |||||||
| @ -19,9 +19,9 @@ endif | |||||||
| MCU_FLAGS		= -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard | MCU_FLAGS		= -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard | ||||||
| 
 | 
 | ||||||
| # Warnings configuration
 | # Warnings configuration
 | ||||||
| CFLAGS			+= -Wall -Wextra -Wredundant-decls | CFLAGS			+= -Wall -Wextra -Wredundant-decls -Wdouble-promotion | ||||||
| 
 | 
 | ||||||
| CFLAGS			+= $(MCU_FLAGS) -DSTM32WB55xx -fdata-sections -ffunction-sections | CFLAGS			+= $(MCU_FLAGS) -DSTM32WB55xx -fdata-sections -ffunction-sections -fsingle-precision-constant | ||||||
| LDFLAGS			+= $(MCU_FLAGS) -specs=nosys.specs -specs=nano.specs | LDFLAGS			+= $(MCU_FLAGS) -specs=nosys.specs -specs=nano.specs | ||||||
| 
 | 
 | ||||||
| CPPFLAGS		+= -fno-rtti -fno-use-cxa-atexit -fno-exceptions | CPPFLAGS		+= -fno-rtti -fno-use-cxa-atexit -fno-exceptions | ||||||
|  | |||||||
| @ -276,7 +276,7 @@ bool flipper_format_stream_write_value_line(Stream* stream, FlipperStreamWriteDa | |||||||
| #ifndef FLIPPER_STREAM_LITE | #ifndef FLIPPER_STREAM_LITE | ||||||
|                 case FlipperStreamValueFloat: { |                 case FlipperStreamValueFloat: { | ||||||
|                     const float* data = write_data->data; |                     const float* data = write_data->data; | ||||||
|                     string_printf(value, "%f", data[i]); |                     string_printf(value, "%f", (double)data[i]); | ||||||
|                 }; break; |                 }; break; | ||||||
| #endif | #endif | ||||||
|                 case FlipperStreamValueInt32: { |                 case FlipperStreamValueInt32: { | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ LIB_DIR			= $(PROJECT_ROOT)/lib | |||||||
| CFLAGS			+= -I$(LIB_DIR) | CFLAGS			+= -I$(LIB_DIR) | ||||||
| 
 | 
 | ||||||
| # Mlib containers
 | # Mlib containers
 | ||||||
| CFLAGS			+= -I$(LIB_DIR)/mlib | CFLAGS			+= -I$(LIB_DIR)/mlib -D'M_MEMORY_FULL(x)=abort()' | ||||||
| 
 | 
 | ||||||
| # U8G2 display library
 | # U8G2 display library
 | ||||||
| U8G2_DIR		= $(LIB_DIR)/u8g2 | U8G2_DIR		= $(LIB_DIR)/u8g2 | ||||||
|  | |||||||
| @ -83,7 +83,10 @@ bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* | |||||||
| 
 | 
 | ||||||
|     if(furi_hal_subghz_rx_pipe_not_empty()) { |     if(furi_hal_subghz_rx_pipe_not_empty()) { | ||||||
|         FURI_LOG_I( |         FURI_LOG_I( | ||||||
|             TAG, "RSSI: %03.1fdbm LQI: %d", furi_hal_subghz_get_rssi(), furi_hal_subghz_get_lqi()); |             TAG, | ||||||
|  |             "RSSI: %03.1fdbm LQI: %d", | ||||||
|  |             (double)furi_hal_subghz_get_rssi(), | ||||||
|  |             furi_hal_subghz_get_lqi()); | ||||||
|         if(furi_hal_subghz_is_rx_data_crc_valid()) { |         if(furi_hal_subghz_is_rx_data_crc_valid()) { | ||||||
|             furi_hal_subghz_read_packet(data, size); |             furi_hal_subghz_read_packet(data, size); | ||||||
|             ret = true; |             ret = true; | ||||||
|  | |||||||
| @ -26,6 +26,7 @@ UpdateManifest* update_manifest_alloc() { | |||||||
|     string_init(update_manifest->staged_loader_file); |     string_init(update_manifest->staged_loader_file); | ||||||
|     string_init(update_manifest->resource_bundle); |     string_init(update_manifest->resource_bundle); | ||||||
|     update_manifest->target = 0; |     update_manifest->target = 0; | ||||||
|  |     update_manifest->manifest_version = 0; | ||||||
|     memset(update_manifest->ob_reference.bytes, 0, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES); |     memset(update_manifest->ob_reference.bytes, 0, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES); | ||||||
|     memset(update_manifest->ob_compare_mask.bytes, 0, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES); |     memset(update_manifest->ob_compare_mask.bytes, 0, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES); | ||||||
|     memset(update_manifest->ob_write_mask.bytes, 0, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES); |     memset(update_manifest->ob_write_mask.bytes, 0, FURI_HAL_FLASH_OB_RAW_SIZE_BYTES); | ||||||
| @ -49,12 +50,11 @@ static bool | |||||||
|     furi_assert(flipper_file); |     furi_assert(flipper_file); | ||||||
| 
 | 
 | ||||||
|     string_t filetype; |     string_t filetype; | ||||||
|     uint32_t version = 0; |  | ||||||
| 
 | 
 | ||||||
|     // TODO: compare filetype?
 |     // TODO: compare filetype?
 | ||||||
|     string_init(filetype); |     string_init(filetype); | ||||||
|     update_manifest->valid = |     update_manifest->valid = | ||||||
|         flipper_format_read_header(flipper_file, filetype, &version) && |         flipper_format_read_header(flipper_file, filetype, &update_manifest->manifest_version) && | ||||||
|         flipper_format_read_string(flipper_file, MANIFEST_KEY_INFO, update_manifest->version) && |         flipper_format_read_string(flipper_file, MANIFEST_KEY_INFO, update_manifest->version) && | ||||||
|         flipper_format_read_uint32( |         flipper_format_read_uint32( | ||||||
|             flipper_file, MANIFEST_KEY_TARGET, &update_manifest->target, 1) && |             flipper_file, MANIFEST_KEY_TARGET, &update_manifest->target, 1) && | ||||||
| @ -68,7 +68,7 @@ static bool | |||||||
|     string_clear(filetype); |     string_clear(filetype); | ||||||
| 
 | 
 | ||||||
|     if(update_manifest->valid) { |     if(update_manifest->valid) { | ||||||
|         /* Optional fields - we can have dfu, radio, or both */ |         /* Optional fields - we can have dfu, radio, resources, or any combination */ | ||||||
|         flipper_format_read_string( |         flipper_format_read_string( | ||||||
|             flipper_file, MANIFEST_KEY_DFU_FILE, update_manifest->firmware_dfu_image); |             flipper_file, MANIFEST_KEY_DFU_FILE, update_manifest->firmware_dfu_image); | ||||||
|         flipper_format_read_string( |         flipper_format_read_string( | ||||||
| @ -131,8 +131,7 @@ static bool ob_data_check_masked_values_valid( | |||||||
|     const FuriHalFlashRawOptionByteData* mask) { |     const FuriHalFlashRawOptionByteData* mask) { | ||||||
|     bool valid = true; |     bool valid = true; | ||||||
|     for(size_t idx = 0; valid && (idx < FURI_HAL_FLASH_OB_TOTAL_VALUES); ++idx) { |     for(size_t idx = 0; valid && (idx < FURI_HAL_FLASH_OB_TOTAL_VALUES); ++idx) { | ||||||
|         valid &= (data->obs[idx]. dword & mask->obs[idx].dword) == |         valid &= (data->obs[idx].dword & mask->obs[idx].dword) == data->obs[idx].dword; | ||||||
|                  data->obs[idx].dword; |  | ||||||
|     } |     } | ||||||
|     return valid; |     return valid; | ||||||
| } | } | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ extern "C" { | |||||||
| /* Paths don't include /ext -- because at startup SD card is mounted as root */ | /* Paths don't include /ext -- because at startup SD card is mounted as root */ | ||||||
| #define UPDATE_DIR_DEFAULT_REL_PATH "/update" | #define UPDATE_DIR_DEFAULT_REL_PATH "/update" | ||||||
| #define UPDATE_MANIFEST_DEFAULT_NAME "update.fuf" | #define UPDATE_MANIFEST_DEFAULT_NAME "update.fuf" | ||||||
|  | #define UPDATE_MANIFEST_POINTER_FILE_NAME ".fupdate" | ||||||
| 
 | 
 | ||||||
| typedef union { | typedef union { | ||||||
|     uint8_t raw[6]; |     uint8_t raw[6]; | ||||||
| @ -27,6 +28,7 @@ typedef union { | |||||||
| _Static_assert(sizeof(UpdateManifestRadioVersion) == 6, "UpdateManifestRadioVersion size error"); | _Static_assert(sizeof(UpdateManifestRadioVersion) == 6, "UpdateManifestRadioVersion size error"); | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|  |     uint32_t manifest_version; | ||||||
|     string_t version; |     string_t version; | ||||||
|     uint32_t target; |     uint32_t target; | ||||||
|     string_t staged_loader_file; |     string_t staged_loader_file; | ||||||
|  | |||||||
| @ -12,7 +12,6 @@ | |||||||
| #define UPDATE_ROOT_DIR "/ext" UPDATE_DIR_DEFAULT_REL_PATH | #define UPDATE_ROOT_DIR "/ext" UPDATE_DIR_DEFAULT_REL_PATH | ||||||
| #define UPDATE_PREFIX "/ext" UPDATE_DIR_DEFAULT_REL_PATH "/" | #define UPDATE_PREFIX "/ext" UPDATE_DIR_DEFAULT_REL_PATH "/" | ||||||
| #define UPDATE_SUFFIX "/" UPDATE_MANIFEST_DEFAULT_NAME | #define UPDATE_SUFFIX "/" UPDATE_MANIFEST_DEFAULT_NAME | ||||||
| #define MAX_DIR_NAME_LEN 250 |  | ||||||
| 
 | 
 | ||||||
| static const char* update_prepare_result_descr[] = { | static const char* update_prepare_result_descr[] = { | ||||||
|     [UpdatePrepareResultOK] = "OK", |     [UpdatePrepareResultOK] = "OK", | ||||||
| @ -21,6 +20,8 @@ static const char* update_prepare_result_descr[] = { | |||||||
|     [UpdatePrepareResultManifestInvalid] = "Invalid manifest data", |     [UpdatePrepareResultManifestInvalid] = "Invalid manifest data", | ||||||
|     [UpdatePrepareResultStageMissing] = "Missing Stage2 loader", |     [UpdatePrepareResultStageMissing] = "Missing Stage2 loader", | ||||||
|     [UpdatePrepareResultStageIntegrityError] = "Corrupted Stage2 loader", |     [UpdatePrepareResultStageIntegrityError] = "Corrupted Stage2 loader", | ||||||
|  |     [UpdatePrepareResultManifestPointerError] = "Failed to create update pointer file", | ||||||
|  |     [UpdatePrepareResultOutdatedManifestVersion] = "Update package is too old", | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const char* update_operation_describe_preparation_result(const UpdatePrepareResult value) { | const char* update_operation_describe_preparation_result(const UpdatePrepareResult value) { | ||||||
| @ -31,65 +32,7 @@ const char* update_operation_describe_preparation_result(const UpdatePrepareResu | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool update_operation_get_package_dir_name(const char* full_path, string_t out_manifest_dir) { | static bool update_operation_get_current_package_path_rtc(Storage* storage, string_t out_path) { | ||||||
|     bool path_ok = false; |  | ||||||
|     string_t full_path_str; |  | ||||||
|     string_init_set(full_path_str, full_path); |  | ||||||
|     string_reset(out_manifest_dir); |  | ||||||
|     bool start_end_ok = string_start_with_str_p(full_path_str, UPDATE_PREFIX) && |  | ||||||
|                         string_end_with_str_p(full_path_str, UPDATE_SUFFIX); |  | ||||||
|     int16_t dir_name_len = |  | ||||||
|         strlen(full_path) - strlen(UPDATE_PREFIX) - strlen(UPDATE_MANIFEST_DEFAULT_NAME) - 1; |  | ||||||
|     if(dir_name_len == -1) { |  | ||||||
|         path_ok = true; |  | ||||||
|     } else if(start_end_ok && (dir_name_len > 0)) { |  | ||||||
|         string_set_n(out_manifest_dir, full_path_str, strlen(UPDATE_PREFIX), dir_name_len); |  | ||||||
|         path_ok = true; |  | ||||||
|         if(string_search_char(out_manifest_dir, '/') != STRING_FAILURE) { |  | ||||||
|             string_reset(out_manifest_dir); |  | ||||||
|             path_ok = false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     string_clear(full_path_str); |  | ||||||
|     return path_ok; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int32_t update_operation_get_package_index(Storage* storage, const char* update_package_dir) { |  | ||||||
|     furi_assert(storage); |  | ||||||
|     furi_assert(update_package_dir); |  | ||||||
| 
 |  | ||||||
|     if(strlen(update_package_dir) == 0) { |  | ||||||
|         return UPDATE_OPERATION_ROOT_DIR_PACKAGE_MAGIC; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     bool found = false; |  | ||||||
|     int32_t index = 0; |  | ||||||
|     File* dir = storage_file_alloc(storage); |  | ||||||
|     FileInfo fi = {0}; |  | ||||||
|     char* name_buffer = malloc(MAX_DIR_NAME_LEN); |  | ||||||
|     do { |  | ||||||
|         if(!storage_dir_open(dir, UPDATE_ROOT_DIR)) { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         while(storage_dir_read(dir, &fi, name_buffer, MAX_DIR_NAME_LEN)) { |  | ||||||
|             index++; |  | ||||||
|             if(strcmp(name_buffer, update_package_dir)) { |  | ||||||
|                 continue; |  | ||||||
|             } else { |  | ||||||
|                 found = true; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } while(false); |  | ||||||
| 
 |  | ||||||
|     free(name_buffer); |  | ||||||
|     storage_file_free(dir); |  | ||||||
| 
 |  | ||||||
|     return found ? index : -1; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool update_operation_get_current_package_path(Storage* storage, string_t out_path) { |  | ||||||
|     const 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); |     string_set_str(out_path, UPDATE_ROOT_DIR); | ||||||
|     if(update_index == UPDATE_OPERATION_ROOT_DIR_PACKAGE_MAGIC) { |     if(update_index == UPDATE_OPERATION_ROOT_DIR_PACKAGE_MAGIC) { | ||||||
| @ -100,13 +43,13 @@ bool update_operation_get_current_package_path(Storage* storage, string_t out_pa | |||||||
|     uint32_t iter_index = 0; |     uint32_t iter_index = 0; | ||||||
|     File* dir = storage_file_alloc(storage); |     File* dir = storage_file_alloc(storage); | ||||||
|     FileInfo fi = {0}; |     FileInfo fi = {0}; | ||||||
|     char* name_buffer = malloc(MAX_DIR_NAME_LEN); |     char* name_buffer = malloc(UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN); | ||||||
|     do { |     do { | ||||||
|         if(!storage_dir_open(dir, UPDATE_ROOT_DIR)) { |         if(!storage_dir_open(dir, UPDATE_ROOT_DIR)) { | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         while(storage_dir_read(dir, &fi, name_buffer, MAX_DIR_NAME_LEN)) { |         while(storage_dir_read(dir, &fi, name_buffer, UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN)) { | ||||||
|             if(++iter_index == update_index) { |             if(++iter_index == update_index) { | ||||||
|                 found = true; |                 found = true; | ||||||
|                 path_append(out_path, name_buffer); |                 path_append(out_path, name_buffer); | ||||||
| @ -124,79 +67,148 @@ bool update_operation_get_current_package_path(Storage* storage, string_t out_pa | |||||||
|     return found; |     return found; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #define UPDATE_FILE_POINTER_FN "/ext/" UPDATE_MANIFEST_POINTER_FILE_NAME | ||||||
|  | #define UPDATE_MANIFEST_MAX_PATH_LEN 256u | ||||||
|  | 
 | ||||||
|  | bool update_operation_get_current_package_manifest_path(Storage* storage, string_t out_path) { | ||||||
|  |     string_reset(out_path); | ||||||
|  |     if(storage_common_stat(storage, UPDATE_FILE_POINTER_FN, NULL) == FSE_OK) { | ||||||
|  |         char* manifest_name_buffer = malloc(UPDATE_MANIFEST_MAX_PATH_LEN); | ||||||
|  |         File* upd_file = NULL; | ||||||
|  |         do { | ||||||
|  |             upd_file = storage_file_alloc(storage); | ||||||
|  |             if(!storage_file_open( | ||||||
|  |                    upd_file, UPDATE_FILE_POINTER_FN, FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             uint16_t bytes_read = | ||||||
|  |                 storage_file_read(upd_file, manifest_name_buffer, UPDATE_MANIFEST_MAX_PATH_LEN); | ||||||
|  |             if((bytes_read == 0) || (bytes_read == UPDATE_MANIFEST_MAX_PATH_LEN)) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             if(storage_common_stat(storage, manifest_name_buffer, NULL) != FSE_OK) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             string_set_str(out_path, manifest_name_buffer); | ||||||
|  |         } while(0); | ||||||
|  |         free(manifest_name_buffer); | ||||||
|  |         storage_file_free(upd_file); | ||||||
|  |     } else { | ||||||
|  |         /* legacy, will be deprecated */ | ||||||
|  |         string_t rtcpath; | ||||||
|  |         string_init(rtcpath); | ||||||
|  |         do { | ||||||
|  |             if(!update_operation_get_current_package_path_rtc(storage, rtcpath)) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             path_concat(string_get_cstr(rtcpath), UPDATE_MANIFEST_DEFAULT_NAME, out_path); | ||||||
|  |         } while(0); | ||||||
|  |         string_clear(rtcpath); | ||||||
|  |     } | ||||||
|  |     return !string_empty_p(out_path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool update_operation_persist_manifest_path(Storage* storage, const char* manifest_path) { | ||||||
|  |     const uint16_t manifest_path_len = strlen(manifest_path); | ||||||
|  |     furi_check(manifest_path && manifest_path_len); | ||||||
|  |     bool success = false; | ||||||
|  |     File* file = storage_file_alloc(storage); | ||||||
|  |     do { | ||||||
|  |         if(manifest_path_len >= UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(!storage_file_open(file, UPDATE_FILE_POINTER_FN, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(storage_file_write(file, manifest_path, manifest_path_len) != manifest_path_len) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         success = true; | ||||||
|  |     } while(0); | ||||||
|  |     storage_file_free(file); | ||||||
|  |     return success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { | UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { | ||||||
|     string_t update_folder; |     UpdatePrepareResult result = UpdatePrepareResultManifestFolderNotFound; | ||||||
|     string_init(update_folder); |  | ||||||
|     if(!update_operation_get_package_dir_name(manifest_file_path, update_folder)) { |  | ||||||
|         string_clear(update_folder); |  | ||||||
|         return UpdatePrepareResultManifestPathInvalid; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     Storage* storage = furi_record_open("storage"); |     Storage* storage = furi_record_open("storage"); | ||||||
|     int32_t update_index = |  | ||||||
|         update_operation_get_package_index(storage, string_get_cstr(update_folder)); |  | ||||||
|     string_clear(update_folder); |  | ||||||
| 
 |  | ||||||
|     if(update_index < 0) { |  | ||||||
|         furi_record_close("storage"); |  | ||||||
|         return UpdatePrepareResultManifestFolderNotFound; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     string_t update_dir_path; |  | ||||||
|     string_init(update_dir_path); |  | ||||||
|     path_extract_dirname(manifest_file_path, update_dir_path); |  | ||||||
| 
 |  | ||||||
|     UpdatePrepareResult result = UpdatePrepareResultManifestInvalid; |  | ||||||
|     UpdateManifest* manifest = update_manifest_alloc(); |     UpdateManifest* manifest = update_manifest_alloc(); | ||||||
|     if(update_manifest_init(manifest, manifest_file_path)) { |     File* file = storage_file_alloc(storage); | ||||||
|         result = UpdatePrepareResultStageMissing; | 
 | ||||||
|         File* file = storage_file_alloc(storage); |     string_t stage_path; | ||||||
|  |     string_init(stage_path); | ||||||
|  |     do { | ||||||
|  |         if(storage_common_stat(storage, manifest_file_path, NULL) != FSE_OK) { | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(!update_manifest_init(manifest, manifest_file_path)) { | ||||||
|  |             result = UpdatePrepareResultManifestInvalid; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(manifest->manifest_version < UPDATE_OPERATION_MIN_MANIFEST_VERSION) { | ||||||
|  |             result = UpdatePrepareResultOutdatedManifestVersion; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(furi_hal_version_get_hw_target() != manifest->target) { | ||||||
|  |             result = UpdatePrepareResultTargetMismatch; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         string_t stage_path; |  | ||||||
|         string_init(stage_path); |  | ||||||
|         path_extract_dirname(manifest_file_path, stage_path); |         path_extract_dirname(manifest_file_path, stage_path); | ||||||
|         path_append(stage_path, string_get_cstr(manifest->staged_loader_file)); |         path_append(stage_path, string_get_cstr(manifest->staged_loader_file)); | ||||||
| 
 | 
 | ||||||
|         uint32_t crc = 0; |         if(!storage_file_open(file, string_get_cstr(stage_path), FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||||
|         do { |             result = UpdatePrepareResultStageMissing; | ||||||
|             if(!storage_file_open( |             break; | ||||||
|                    file, string_get_cstr(stage_path), FSAM_READ, FSOM_OPEN_EXISTING)) { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             result = UpdatePrepareResultStageIntegrityError; |  | ||||||
|             crc = crc32_calc_file(file, NULL, NULL); |  | ||||||
|         } while(false); |  | ||||||
| 
 |  | ||||||
|         string_clear(stage_path); |  | ||||||
|         storage_file_free(file); |  | ||||||
| 
 |  | ||||||
|         if(crc == manifest->staged_loader_crc) { |  | ||||||
|             furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePreUpdate); |  | ||||||
|             update_operation_persist_package_index(update_index); |  | ||||||
|             result = UpdatePrepareResultOK; |  | ||||||
|         } |         } | ||||||
|     } | 
 | ||||||
|     furi_record_close("storage"); |         uint32_t crc = crc32_calc_file(file, NULL, NULL); | ||||||
|  |         if(crc != manifest->staged_loader_crc) { | ||||||
|  |             result = UpdatePrepareResultStageIntegrityError; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(!update_operation_persist_manifest_path(storage, manifest_file_path)) { | ||||||
|  |             result = UpdatePrepareResultManifestPointerError; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         result = UpdatePrepareResultOK; | ||||||
|  |         furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePreUpdate); | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     string_clear(stage_path); | ||||||
|  |     storage_file_free(file); | ||||||
|  | 
 | ||||||
|     update_manifest_free(manifest); |     update_manifest_free(manifest); | ||||||
|  |     furi_record_close("storage"); | ||||||
| 
 | 
 | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool update_operation_is_armed() { | bool update_operation_is_armed() { | ||||||
|     FuriHalRtcBootMode boot_mode = furi_hal_rtc_get_boot_mode(); |     FuriHalRtcBootMode boot_mode = furi_hal_rtc_get_boot_mode(); | ||||||
|  |     const uint32_t rtc_upd_index = | ||||||
|  |         furi_hal_rtc_get_register(FuriHalRtcRegisterUpdateFolderFSIndex); | ||||||
|  |     Storage* storage = furi_record_open("storage"); | ||||||
|  |     const bool upd_fn_ptr_exists = | ||||||
|  |         (storage_common_stat(storage, UPDATE_FILE_POINTER_FN, NULL) == FSE_OK); | ||||||
|  |     furi_record_close("storage"); | ||||||
|     return (boot_mode >= FuriHalRtcBootModePreUpdate) && |     return (boot_mode >= FuriHalRtcBootModePreUpdate) && | ||||||
|            (boot_mode <= FuriHalRtcBootModePostUpdate) && |            (boot_mode <= FuriHalRtcBootModePostUpdate) && | ||||||
|            (furi_hal_rtc_get_register(FuriHalRtcRegisterUpdateFolderFSIndex) > 0); |            ((rtc_upd_index != INT_MAX) || upd_fn_ptr_exists); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void update_operation_disarm() { | void update_operation_disarm() { | ||||||
|     furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); |     furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); | ||||||
|     furi_hal_rtc_set_register( |     furi_hal_rtc_set_register(FuriHalRtcRegisterUpdateFolderFSIndex, INT_MAX); | ||||||
|         FuriHalRtcRegisterUpdateFolderFSIndex, INT_MAX); |     Storage* storage = furi_record_open("storage"); | ||||||
| } |     storage_simply_remove(storage, UPDATE_FILE_POINTER_FN); | ||||||
| 
 |     furi_record_close("storage"); | ||||||
| void update_operation_persist_package_index(int32_t index) { |  | ||||||
|     furi_check(index >= 0); |  | ||||||
|     furi_hal_rtc_set_register(FuriHalRtcRegisterUpdateFolderFSIndex, index); |  | ||||||
| } | } | ||||||
| @ -9,6 +9,8 @@ extern "C" { | |||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #define UPDATE_OPERATION_ROOT_DIR_PACKAGE_MAGIC 0 | #define UPDATE_OPERATION_ROOT_DIR_PACKAGE_MAGIC 0 | ||||||
|  | #define UPDATE_OPERATION_MAX_MANIFEST_PATH_LEN 255u | ||||||
|  | #define UPDATE_OPERATION_MIN_MANIFEST_VERSION 2 | ||||||
| 
 | 
 | ||||||
| /* 
 | /* 
 | ||||||
|  * Checks if supplied full manifest path is valid |  * Checks if supplied full manifest path is valid | ||||||
| @ -19,6 +21,7 @@ extern "C" { | |||||||
|  */ |  */ | ||||||
| bool update_operation_get_package_dir_name(const char* full_path, string_t out_manifest_dir); | bool update_operation_get_package_dir_name(const char* full_path, string_t out_manifest_dir); | ||||||
| 
 | 
 | ||||||
|  | /* When updating this enum, also update assets/protobuf/system.proto */ | ||||||
| typedef enum { | typedef enum { | ||||||
|     UpdatePrepareResultOK, |     UpdatePrepareResultOK, | ||||||
|     UpdatePrepareResultManifestPathInvalid, |     UpdatePrepareResultManifestPathInvalid, | ||||||
| @ -26,6 +29,9 @@ typedef enum { | |||||||
|     UpdatePrepareResultManifestInvalid, |     UpdatePrepareResultManifestInvalid, | ||||||
|     UpdatePrepareResultStageMissing, |     UpdatePrepareResultStageMissing, | ||||||
|     UpdatePrepareResultStageIntegrityError, |     UpdatePrepareResultStageIntegrityError, | ||||||
|  |     UpdatePrepareResultManifestPointerError, | ||||||
|  |     UpdatePrepareResultTargetMismatch, | ||||||
|  |     UpdatePrepareResultOutdatedManifestVersion, | ||||||
| } UpdatePrepareResult; | } UpdatePrepareResult; | ||||||
| 
 | 
 | ||||||
| const char* update_operation_describe_preparation_result(const UpdatePrepareResult value); | const char* update_operation_describe_preparation_result(const UpdatePrepareResult value); | ||||||
| @ -37,27 +43,13 @@ const char* update_operation_describe_preparation_result(const UpdatePrepareResu | |||||||
|  */ |  */ | ||||||
| UpdatePrepareResult update_operation_prepare(const char* manifest_file_path); | 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 |  | ||||||
|  */ |  | ||||||
| int32_t update_operation_get_package_index(Storage* storage, const char* update_package_dir); |  | ||||||
| 
 |  | ||||||
| /* 
 | /* 
 | ||||||
|  * Gets filesystem path for current update package |  * Gets filesystem path for current update package | ||||||
|  * @param storage Storage API |  * @param storage Storage API | ||||||
|  * @param out_path Path to directory with manifest & related files. Must be initialized |  * @param out_path Path to manifest. Must be initialized | ||||||
|  * @return true if path was restored successfully |  * @return true if path was restored successfully | ||||||
|  */ |  */ | ||||||
| bool update_operation_get_current_package_path(Storage* storage, string_t out_path); | bool update_operation_get_current_package_manifest_path(Storage* storage, string_t out_path); | ||||||
| 
 |  | ||||||
| /* 
 |  | ||||||
|  * Stores given update index in RTC registers |  | ||||||
|  * @param index Value to store |  | ||||||
|  */ |  | ||||||
| void update_operation_persist_package_index(int32_t index); |  | ||||||
| 
 | 
 | ||||||
| /* 
 | /* 
 | ||||||
|  * Checks if an update operation step is pending after reset |  * Checks if an update operation step is pending after reset | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ import math | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Main(App): | class Main(App): | ||||||
|  |     UPDATE_MANIFEST_VERSION = 2 | ||||||
|     UPDATE_MANIFEST_NAME = "update.fuf" |     UPDATE_MANIFEST_NAME = "update.fuf" | ||||||
| 
 | 
 | ||||||
|     #  No compression, plain tar |     #  No compression, plain tar | ||||||
| @ -93,7 +94,9 @@ class Main(App): | |||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|         file = FlipperFormatFile() |         file = FlipperFormatFile() | ||||||
|         file.setHeader("Flipper firmware upgrade configuration", 1) |         file.setHeader( | ||||||
|  |             "Flipper firmware upgrade configuration", self.UPDATE_MANIFEST_VERSION | ||||||
|  |         ) | ||||||
|         file.writeKey("Info", self.args.version) |         file.writeKey("Info", self.args.version) | ||||||
|         file.writeKey("Target", self.args.target[1:])  # dirty 'f' strip |         file.writeKey("Target", self.args.target[1:])  # dirty 'f' strip | ||||||
|         file.writeKey("Loader", stage_basename) |         file.writeKey("Loader", stage_basename) | ||||||
| @ -102,7 +105,7 @@ class Main(App): | |||||||
|         file.writeKey("Firmware", dfu_basename) |         file.writeKey("Firmware", dfu_basename) | ||||||
|         file.writeKey("Radio", radiobin_basename or "") |         file.writeKey("Radio", radiobin_basename or "") | ||||||
|         file.writeKey("Radio address", self.int2ffhex(radio_addr)) |         file.writeKey("Radio address", self.int2ffhex(radio_addr)) | ||||||
|         file.writeKey("Radio version", self.int2ffhex(radio_version)) |         file.writeKey("Radio version", self.int2ffhex(radio_version, 12)) | ||||||
|         if radiobin_basename: |         if radiobin_basename: | ||||||
|             file.writeKey("Radio CRC", self.int2ffhex(self.crc(self.args.radiobin))) |             file.writeKey("Radio CRC", self.int2ffhex(self.crc(self.args.radiobin))) | ||||||
|         else: |         else: | ||||||
| @ -149,11 +152,10 @@ class Main(App): | |||||||
|         return " ".join(f"{b:02X}" for b in value) |         return " ".join(f"{b:02X}" for b in value) | ||||||
| 
 | 
 | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def int2ffhex(value: int): |     def int2ffhex(value: int, n_hex_syms=8): | ||||||
|         n_hex_bytes = 4 |  | ||||||
|         if value: |         if value: | ||||||
|             n_hex_bytes = math.ceil(math.ceil(math.log2(value)) / 8) * 2 |             n_hex_syms = math.ceil(math.ceil(math.log2(value)) / 8) * 2 | ||||||
|         fmtstr = f"%0{n_hex_bytes}X" |         fmtstr = f"%0{n_hex_syms}X" | ||||||
|         hexstr = fmtstr % value |         hexstr = fmtstr % value | ||||||
|         return " ".join(list(Main.batch(hexstr, 2))[::-1]) |         return " ".join(list(Main.batch(hexstr, 2))[::-1]) | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 hedger
						hedger