diff --git a/applications/archive/scenes/archive_scene_rename.c b/applications/archive/scenes/archive_scene_rename.c index 9e5738017..e0c4ba6c6 100644 --- a/applications/archive/scenes/archive_scene_rename.c +++ b/applications/archive/scenes/archive_scene_rename.c @@ -31,8 +31,8 @@ void archive_scene_rename_on_enter(void* context) { MAX_TEXT_INPUT_LEN, false); - ValidatorIsFile* validator_is_file = - validator_is_file_alloc_init(archive_get_path(archive->browser), archive->file_extension); + ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( + archive_get_path(archive->browser), archive->file_extension, NULL); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); diff --git a/applications/gui/modules/validators.c b/applications/gui/modules/validators.c index df0be729b..242dbe682 100644 --- a/applications/gui/modules/validators.c +++ b/applications/gui/modules/validators.c @@ -5,11 +5,19 @@ struct ValidatorIsFile { const char* app_path_folder; const char* app_extension; + char* current_name; }; bool validator_is_file_callback(const char* text, string_t error, void* context) { furi_assert(context); ValidatorIsFile* instance = context; + + if(instance->current_name != NULL) { + if(strcmp(instance->current_name, text) == 0) { + return true; + } + } + bool ret = true; string_t path; string_init_printf(path, "%s/%s%s", instance->app_path_folder, text, instance->app_extension); @@ -26,17 +34,21 @@ bool validator_is_file_callback(const char* text, string_t error, void* context) return ret; } -ValidatorIsFile* - validator_is_file_alloc_init(const char* app_path_folder, const char* app_extension) { +ValidatorIsFile* validator_is_file_alloc_init( + const char* app_path_folder, + const char* app_extension, + const char* current_name) { ValidatorIsFile* instance = malloc(sizeof(ValidatorIsFile)); instance->app_path_folder = app_path_folder; instance->app_extension = app_extension; + instance->current_name = strdup(current_name); return instance; } void validator_is_file_free(ValidatorIsFile* instance) { furi_assert(instance); + free(instance->current_name); free(instance); } diff --git a/applications/gui/modules/validators.h b/applications/gui/modules/validators.h index f62064b6f..3a834a8a6 100644 --- a/applications/gui/modules/validators.h +++ b/applications/gui/modules/validators.h @@ -8,8 +8,10 @@ extern "C" { #endif typedef struct ValidatorIsFile ValidatorIsFile; -ValidatorIsFile* - validator_is_file_alloc_init(const char* app_path_folder, const char* app_extension); +ValidatorIsFile* validator_is_file_alloc_init( + const char* app_path_folder, + const char* app_extension, + const char* current_name); void validator_is_file_free(ValidatorIsFile* instance); diff --git a/applications/ibutton/scene/ibutton_scene_save_name.cpp b/applications/ibutton/scene/ibutton_scene_save_name.cpp index 94ca45d49..9a7ab8460 100644 --- a/applications/ibutton/scene/ibutton_scene_save_name.cpp +++ b/applications/ibutton/scene/ibutton_scene_save_name.cpp @@ -35,7 +35,7 @@ void iButtonSceneSaveName::on_enter(iButtonApp* app) { key_name_empty); ValidatorIsFile* validator_is_file = - validator_is_file_alloc_init(app->app_folder, app->app_extension); + validator_is_file_alloc_init(app->app_folder, app->app_extension, key_name); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewTextInput); diff --git a/applications/infrared/scene/infrared_app_scene_edit_rename.cpp b/applications/infrared/scene/infrared_app_scene_edit_rename.cpp index 4b3578fac..0bdd90ac8 100644 --- a/applications/infrared/scene/infrared_app_scene_edit_rename.cpp +++ b/applications/infrared/scene/infrared_app_scene_edit_rename.cpp @@ -21,8 +21,8 @@ void InfraredAppSceneEditRename::on_enter(InfraredApp* app) { enter_name_length = InfraredAppRemoteManager::max_remote_name_length; text_input_set_header_text(text_input, "Name the remote"); - ValidatorIsFile* validator_is_file = - validator_is_file_alloc_init(app->infrared_directory, app->infrared_extension); + ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( + app->infrared_directory, app->infrared_extension, remote_name.c_str()); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); } diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp b/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp index e5fee4fe8..c2ad5dd19 100644 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp +++ b/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp @@ -22,7 +22,7 @@ void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool need_restore) { key_name_empty); ValidatorIsFile* validator_is_file = - validator_is_file_alloc_init(app->app_folder, app->app_extension); + validator_is_file_alloc_init(app->app_folder, app->app_extension, key_name); text_input->set_validator(validator_is_file_callback, validator_is_file); app->view_controller.switch_to(); diff --git a/applications/nfc/scenes/nfc_scene_save_name.c b/applications/nfc/scenes/nfc_scene_save_name.c index f239baa8f..aa05c8a6b 100755 --- a/applications/nfc/scenes/nfc_scene_save_name.c +++ b/applications/nfc/scenes/nfc_scene_save_name.c @@ -30,7 +30,7 @@ void nfc_scene_save_name_on_enter(void* context) { dev_name_empty); ValidatorIsFile* validator_is_file = - validator_is_file_alloc_init(NFC_APP_FOLDER, NFC_APP_EXTENSION); + validator_is_file_alloc_init(NFC_APP_FOLDER, NFC_APP_EXTENSION, nfc->dev->dev_name); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput); diff --git a/applications/power/power_cli.c b/applications/power/power_cli.c index f65fb0958..110318acc 100644 --- a/applications/power/power_cli.c +++ b/applications/power/power_cli.c @@ -20,6 +20,14 @@ void power_cli_reboot2dfu(Cli* cli, string_t args) { power_reboot(PowerBootModeDfu); } +static void power_cli_info_callback(const char* key, const char* value, bool last, void* context) { + printf("%-24s: %s\r\n", key, value); +} + +void power_cli_info(Cli* cli, string_t args) { + furi_hal_power_info_get(power_cli_info_callback, NULL); +} + void power_cli_debug(Cli* cli, string_t args) { furi_hal_power_dump_state(); } @@ -52,6 +60,7 @@ static void power_cli_command_print_usage() { printf("\toff\t - shutdown power\r\n"); printf("\treboot\t - reboot\r\n"); printf("\treboot2dfu\t - reboot to dfu bootloader\r\n"); + printf("\tinfo\t - show power info\r\n"); printf("\tdebug\t - show debug information\r\n"); printf("\t5v <0 or 1>\t - enable or disable 5v ext\r\n"); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { @@ -84,6 +93,11 @@ void power_cli(Cli* cli, string_t args, void* context) { break; } + if(string_cmp_str(cmd, "info") == 0) { + power_cli_info(cli, args); + break; + } + if(string_cmp_str(cmd, "debug") == 0) { power_cli_debug(cli, args); break; diff --git a/applications/rpc/rpc_cli.c b/applications/rpc/rpc_cli.c index 849ef361b..fd1f6e7fc 100644 --- a/applications/rpc/rpc_cli.c +++ b/applications/rpc/rpc_cli.c @@ -4,6 +4,8 @@ #include #include +#define TAG "RpcCli" + typedef struct { Cli* cli; bool session_close_request; @@ -38,6 +40,9 @@ static void rpc_session_terminated_callback(void* context) { void rpc_cli_command_start_session(Cli* cli, string_t args, void* context) { Rpc* rpc = context; + uint32_t mem_before = memmgr_get_free_heap(); + FURI_LOG_D(TAG, "Free memory %d", mem_before); + furi_hal_usb_lock(); RpcSession* rpc_session = rpc_session_open(rpc); if(rpc_session == NULL) { diff --git a/applications/rpc/rpc_gui.c b/applications/rpc/rpc_gui.c index 3a4e21f08..84051bffe 100644 --- a/applications/rpc/rpc_gui.c +++ b/applications/rpc/rpc_gui.c @@ -346,8 +346,19 @@ void rpc_system_gui_free(void* context) { } if(rpc_gui->is_streaming) { + rpc_gui->is_streaming = false; + // Remove GUI framebuffer callback gui_remove_framebuffer_callback( rpc_gui->gui, rpc_system_gui_screen_stream_frame_callback, context); + // Stop and release worker thread + osThreadFlagsSet( + furi_thread_get_thread_id(rpc_gui->transmit_thread), RpcGuiWorkerFlagExit); + furi_thread_join(rpc_gui->transmit_thread); + furi_thread_free(rpc_gui->transmit_thread); + // Release frame + pb_release(&PB_Main_msg, rpc_gui->transmit_frame); + free(rpc_gui->transmit_frame); + rpc_gui->transmit_frame = NULL; } furi_record_close("gui"); free(rpc_gui); diff --git a/applications/rpc/rpc_system.c b/applications/rpc/rpc_system.c index 9884e30a5..f3a8242ae 100644 --- a/applications/rpc/rpc_system.c +++ b/applications/rpc/rpc_system.c @@ -6,6 +6,11 @@ #include "rpc_i.h" +typedef struct { + RpcSession* session; + PB_Main* response; +} RpcSystemContext; + static void rpc_system_system_ping_process(const PB_Main* request, void* context) { furi_assert(request); furi_assert(request->which_content == PB_Main_system_ping_request_tag); @@ -55,11 +60,6 @@ static void rpc_system_system_reboot_process(const PB_Main* request, void* conte } } -typedef struct { - RpcSession* session; - PB_Main* response; -} RpcSystemSystemDeviceInfoContext; - static void rpc_system_system_device_info_callback( const char* key, const char* value, @@ -67,7 +67,7 @@ static void rpc_system_system_device_info_callback( void* context) { furi_assert(key); furi_assert(value); - RpcSystemSystemDeviceInfoContext* ctx = context; + RpcSystemContext* ctx = context; char* str_key = strdup(key); char* str_value = strdup(value); @@ -91,7 +91,7 @@ static void rpc_system_system_device_info_process(const PB_Main* request, void* response->which_content = PB_Main_system_device_info_response_tag; response->command_status = PB_CommandStatus_OK; - RpcSystemSystemDeviceInfoContext device_info_context = { + RpcSystemContext device_info_context = { .session = session, .response = response, }; @@ -202,6 +202,46 @@ static void rpc_system_system_protobuf_version_process(const PB_Main* request, v free(response); } +static void rpc_system_system_power_info_callback( + const char* key, + const char* value, + bool last, + void* context) { + furi_assert(key); + furi_assert(value); + RpcSystemContext* ctx = context; + + char* str_key = strdup(key); + char* str_value = strdup(value); + + ctx->response->has_next = !last; + ctx->response->content.system_device_info_response.key = str_key; + ctx->response->content.system_device_info_response.value = str_value; + + rpc_send_and_release(ctx->session, ctx->response); +} + +static void rpc_system_system_get_power_info_process(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_system_power_info_request_tag); + + RpcSession* session = (RpcSession*)context; + furi_assert(session); + + PB_Main* response = malloc(sizeof(PB_Main)); + response->command_id = request->command_id; + response->which_content = PB_Main_system_power_info_response_tag; + response->command_status = PB_CommandStatus_OK; + + RpcSystemContext power_info_context = { + .session = session, + .response = response, + }; + furi_hal_power_info_get(rpc_system_system_power_info_callback, &power_info_context); + + free(response); +} + void* rpc_system_system_alloc(RpcSession* session) { RpcHandler rpc_handler = { .message_handler = NULL, @@ -233,5 +273,8 @@ void* rpc_system_system_alloc(RpcSession* session) { rpc_handler.message_handler = rpc_system_system_protobuf_version_process; rpc_add_handler(session, PB_Main_system_protobuf_version_request_tag, &rpc_handler); + rpc_handler.message_handler = rpc_system_system_get_power_info_process; + rpc_add_handler(session, PB_Main_system_power_info_request_tag, &rpc_handler); + return NULL; } diff --git a/applications/subghz/scenes/subghz_scene_save_name.c b/applications/subghz/scenes/subghz_scene_save_name.c index 0edee9eef..539eb166a 100644 --- a/applications/subghz/scenes/subghz_scene_save_name.c +++ b/applications/subghz/scenes/subghz_scene_save_name.c @@ -42,8 +42,10 @@ void subghz_scene_save_name_on_enter(void* context) { SUBGHZ_MAX_LEN_NAME + 1, // buffer size dev_name_empty); - ValidatorIsFile* validator_is_file = - validator_is_file_alloc_init(SUBGHZ_APP_FOLDER, SUBGHZ_APP_EXTENSION); + ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( + SUBGHZ_APP_FOLDER, + SUBGHZ_APP_EXTENSION, + (dev_name_empty) ? (NULL) : (subghz->file_name_tmp)); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTextInput); diff --git a/applications/subghz/subghz_i.c b/applications/subghz/subghz_i.c index db1a43154..8f7f2cc6c 100644 --- a/applications/subghz/subghz_i.c +++ b/applications/subghz/subghz_i.c @@ -400,12 +400,14 @@ bool subghz_rename_file(SubGhz* subghz) { string_init_printf( new_path, "%s/%s%s", SUBGHZ_APP_FOLDER, subghz->file_name, SUBGHZ_APP_EXTENSION); - FS_Error fs_result = - storage_common_rename(storage, string_get_cstr(old_path), string_get_cstr(new_path)); + if(string_cmp(old_path, new_path) != 0) { + FS_Error fs_result = + storage_common_rename(storage, string_get_cstr(old_path), string_get_cstr(new_path)); - if(fs_result != FSE_OK) { - dialog_message_show_storage_error(subghz->dialogs, "Cannot rename\n file/directory"); - ret = false; + if(fs_result != FSE_OK) { + dialog_message_show_storage_error(subghz->dialogs, "Cannot rename\n file/directory"); + ret = false; + } } string_clear(old_path); diff --git a/assets/compiled/flipper.pb.h b/assets/compiled/flipper.pb.h index 5f283889d..88548beac 100644 --- a/assets/compiled/flipper.pb.h +++ b/assets/compiled/flipper.pb.h @@ -98,6 +98,11 @@ typedef struct _PB_Main { PB_System_PlayAudiovisualAlertRequest system_play_audiovisual_alert_request; PB_System_ProtobufVersionRequest system_protobuf_version_request; PB_System_ProtobufVersionResponse system_protobuf_version_response; + PB_System_UpdateRequest system_update_request; + PB_Storage_BackupCreateRequest storage_backup_create_request; + PB_Storage_BackupRestoreRequest storage_backup_restore_request; + PB_System_PowerInfoRequest system_power_info_request; + PB_System_PowerInfoResponse system_power_info_response; } content; } PB_Main; @@ -161,6 +166,11 @@ extern "C" { #define PB_Main_system_play_audiovisual_alert_request_tag 38 #define PB_Main_system_protobuf_version_request_tag 39 #define PB_Main_system_protobuf_version_response_tag 40 +#define PB_Main_system_update_request_tag 41 +#define PB_Main_storage_backup_create_request_tag 42 +#define PB_Main_storage_backup_restore_request_tag 43 +#define PB_Main_system_power_info_request_tag 44 +#define PB_Main_system_power_info_response_tag 45 /* Struct field encoding specification for nanopb */ #define PB_Empty_FIELDLIST(X, a) \ @@ -213,7 +223,12 @@ X(a, STATIC, ONEOF, MSG_W_CB, (content,system_get_datetime_response,content X(a, STATIC, ONEOF, MSG_W_CB, (content,system_set_datetime_request,content.system_set_datetime_request), 37) \ X(a, STATIC, ONEOF, MSG_W_CB, (content,system_play_audiovisual_alert_request,content.system_play_audiovisual_alert_request), 38) \ X(a, STATIC, ONEOF, MSG_W_CB, (content,system_protobuf_version_request,content.system_protobuf_version_request), 39) \ -X(a, STATIC, ONEOF, MSG_W_CB, (content,system_protobuf_version_response,content.system_protobuf_version_response), 40) +X(a, STATIC, ONEOF, MSG_W_CB, (content,system_protobuf_version_response,content.system_protobuf_version_response), 40) \ +X(a, STATIC, ONEOF, MSG_W_CB, (content,system_update_request,content.system_update_request), 41) \ +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,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) #define PB_Main_CALLBACK NULL #define PB_Main_DEFAULT NULL #define PB_Main_content_empty_MSGTYPE PB_Empty @@ -253,6 +268,11 @@ X(a, STATIC, ONEOF, MSG_W_CB, (content,system_protobuf_version_response,con #define PB_Main_content_system_play_audiovisual_alert_request_MSGTYPE PB_System_PlayAudiovisualAlertRequest #define PB_Main_content_system_protobuf_version_request_MSGTYPE PB_System_ProtobufVersionRequest #define PB_Main_content_system_protobuf_version_response_MSGTYPE PB_System_ProtobufVersionResponse +#define PB_Main_content_system_update_request_MSGTYPE PB_System_UpdateRequest +#define PB_Main_content_storage_backup_create_request_MSGTYPE PB_Storage_BackupCreateRequest +#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_response_MSGTYPE PB_System_PowerInfoResponse extern const pb_msgdesc_t PB_Empty_msg; extern const pb_msgdesc_t PB_StopSession_msg; @@ -266,9 +286,9 @@ extern const pb_msgdesc_t PB_Main_msg; /* Maximum encoded size of messages (where known) */ #define PB_Empty_size 0 #define PB_StopSession_size 0 -#if defined(PB_System_PingRequest_size) && defined(PB_System_PingResponse_size) && defined(PB_Storage_ListRequest_size) && defined(PB_Storage_ListResponse_size) && defined(PB_Storage_ReadRequest_size) && defined(PB_Storage_ReadResponse_size) && defined(PB_Storage_WriteRequest_size) && defined(PB_Storage_DeleteRequest_size) && defined(PB_Storage_MkdirRequest_size) && defined(PB_Storage_Md5sumRequest_size) && defined(PB_App_StartRequest_size) && defined(PB_Gui_ScreenFrame_size) && defined(PB_Storage_StatRequest_size) && defined(PB_Storage_StatResponse_size) && defined(PB_Gui_StartVirtualDisplayRequest_size) && defined(PB_Storage_InfoRequest_size) && defined(PB_Storage_RenameRequest_size) && defined(PB_System_DeviceInfoResponse_size) +#if defined(PB_System_PingRequest_size) && defined(PB_System_PingResponse_size) && defined(PB_Storage_ListRequest_size) && defined(PB_Storage_ListResponse_size) && defined(PB_Storage_ReadRequest_size) && defined(PB_Storage_ReadResponse_size) && defined(PB_Storage_WriteRequest_size) && defined(PB_Storage_DeleteRequest_size) && defined(PB_Storage_MkdirRequest_size) && defined(PB_Storage_Md5sumRequest_size) && defined(PB_App_StartRequest_size) && defined(PB_Gui_ScreenFrame_size) && defined(PB_Storage_StatRequest_size) && defined(PB_Storage_StatResponse_size) && defined(PB_Gui_StartVirtualDisplayRequest_size) && defined(PB_Storage_InfoRequest_size) && defined(PB_Storage_RenameRequest_size) && defined(PB_System_DeviceInfoResponse_size) && defined(PB_System_UpdateRequest_size) && defined(PB_Storage_BackupCreateRequest_size) && defined(PB_Storage_BackupRestoreRequest_size) && defined(PB_System_PowerInfoResponse_size) #define PB_Main_size (10 + sizeof(union PB_Main_content_size_union)) -union PB_Main_content_size_union {char f5[(6 + PB_System_PingRequest_size)]; char f6[(6 + PB_System_PingResponse_size)]; char f7[(6 + PB_Storage_ListRequest_size)]; char f8[(6 + PB_Storage_ListResponse_size)]; char f9[(6 + PB_Storage_ReadRequest_size)]; char f10[(6 + PB_Storage_ReadResponse_size)]; char f11[(6 + PB_Storage_WriteRequest_size)]; char f12[(6 + PB_Storage_DeleteRequest_size)]; char f13[(6 + PB_Storage_MkdirRequest_size)]; char f14[(6 + PB_Storage_Md5sumRequest_size)]; char f16[(7 + PB_App_StartRequest_size)]; char f22[(7 + PB_Gui_ScreenFrame_size)]; char f24[(7 + PB_Storage_StatRequest_size)]; char f25[(7 + PB_Storage_StatResponse_size)]; char f26[(7 + PB_Gui_StartVirtualDisplayRequest_size)]; char f28[(7 + PB_Storage_InfoRequest_size)]; char f30[(7 + PB_Storage_RenameRequest_size)]; char f33[(7 + PB_System_DeviceInfoResponse_size)]; char f0[36];}; +union PB_Main_content_size_union {char f5[(6 + PB_System_PingRequest_size)]; char f6[(6 + PB_System_PingResponse_size)]; char f7[(6 + PB_Storage_ListRequest_size)]; char f8[(6 + PB_Storage_ListResponse_size)]; char f9[(6 + PB_Storage_ReadRequest_size)]; char f10[(6 + PB_Storage_ReadResponse_size)]; char f11[(6 + PB_Storage_WriteRequest_size)]; char f12[(6 + PB_Storage_DeleteRequest_size)]; char f13[(6 + PB_Storage_MkdirRequest_size)]; char f14[(6 + PB_Storage_Md5sumRequest_size)]; char f16[(7 + PB_App_StartRequest_size)]; char f22[(7 + PB_Gui_ScreenFrame_size)]; char f24[(7 + PB_Storage_StatRequest_size)]; char f25[(7 + PB_Storage_StatResponse_size)]; char f26[(7 + PB_Gui_StartVirtualDisplayRequest_size)]; char f28[(7 + PB_Storage_InfoRequest_size)]; char f30[(7 + PB_Storage_RenameRequest_size)]; char f33[(7 + PB_System_DeviceInfoResponse_size)]; char f41[(7 + PB_System_UpdateRequest_size)]; char f42[(7 + PB_Storage_BackupCreateRequest_size)]; char f43[(7 + PB_Storage_BackupRestoreRequest_size)]; char f45[(7 + PB_System_PowerInfoResponse_size)]; char f0[36];}; #endif #ifdef __cplusplus diff --git a/assets/compiled/protobuf_version.h b/assets/compiled/protobuf_version.h index 19d3f1eac..53e0b73bf 100644 --- a/assets/compiled/protobuf_version.h +++ b/assets/compiled/protobuf_version.h @@ -1,3 +1,3 @@ #pragma once #define PROTOBUF_MAJOR_VERSION 0 -#define PROTOBUF_MINOR_VERSION 3 +#define PROTOBUF_MINOR_VERSION 5 diff --git a/assets/compiled/storage.pb.c b/assets/compiled/storage.pb.c index 9db544cb5..244b273d1 100644 --- a/assets/compiled/storage.pb.c +++ b/assets/compiled/storage.pb.c @@ -51,5 +51,11 @@ PB_BIND(PB_Storage_Md5sumResponse, PB_Storage_Md5sumResponse, AUTO) PB_BIND(PB_Storage_RenameRequest, PB_Storage_RenameRequest, AUTO) +PB_BIND(PB_Storage_BackupCreateRequest, PB_Storage_BackupCreateRequest, AUTO) + + +PB_BIND(PB_Storage_BackupRestoreRequest, PB_Storage_BackupRestoreRequest, AUTO) + + diff --git a/assets/compiled/storage.pb.h b/assets/compiled/storage.pb.h index 8a86c937c..9b31dc313 100644 --- a/assets/compiled/storage.pb.h +++ b/assets/compiled/storage.pb.h @@ -16,6 +16,14 @@ typedef enum _PB_Storage_File_FileType { } PB_Storage_File_FileType; /* Struct definitions */ +typedef struct _PB_Storage_BackupCreateRequest { + char *archive_path; +} PB_Storage_BackupCreateRequest; + +typedef struct _PB_Storage_BackupRestoreRequest { + char *archive_path; +} PB_Storage_BackupRestoreRequest; + typedef struct _PB_Storage_InfoRequest { char *path; } PB_Storage_InfoRequest; @@ -114,6 +122,8 @@ extern "C" { #define PB_Storage_Md5sumRequest_init_default {NULL} #define PB_Storage_Md5sumResponse_init_default {""} #define PB_Storage_RenameRequest_init_default {NULL, NULL} +#define PB_Storage_BackupCreateRequest_init_default {NULL} +#define PB_Storage_BackupRestoreRequest_init_default {NULL} #define PB_Storage_File_init_zero {_PB_Storage_File_FileType_MIN, NULL, 0, NULL} #define PB_Storage_InfoRequest_init_zero {NULL} #define PB_Storage_InfoResponse_init_zero {0, 0} @@ -129,8 +139,12 @@ extern "C" { #define PB_Storage_Md5sumRequest_init_zero {NULL} #define PB_Storage_Md5sumResponse_init_zero {""} #define PB_Storage_RenameRequest_init_zero {NULL, NULL} +#define PB_Storage_BackupCreateRequest_init_zero {NULL} +#define PB_Storage_BackupRestoreRequest_init_zero {NULL} /* Field tags (for use in manual encoding/decoding) */ +#define PB_Storage_BackupCreateRequest_archive_path_tag 1 +#define PB_Storage_BackupRestoreRequest_archive_path_tag 1 #define PB_Storage_InfoRequest_path_tag 1 #define PB_Storage_ListRequest_path_tag 1 #define PB_Storage_Md5sumRequest_path_tag 1 @@ -241,6 +255,16 @@ X(a, POINTER, SINGULAR, STRING, new_path, 2) #define PB_Storage_RenameRequest_CALLBACK NULL #define PB_Storage_RenameRequest_DEFAULT NULL +#define PB_Storage_BackupCreateRequest_FIELDLIST(X, a) \ +X(a, POINTER, SINGULAR, STRING, archive_path, 1) +#define PB_Storage_BackupCreateRequest_CALLBACK NULL +#define PB_Storage_BackupCreateRequest_DEFAULT NULL + +#define PB_Storage_BackupRestoreRequest_FIELDLIST(X, a) \ +X(a, POINTER, SINGULAR, STRING, archive_path, 1) +#define PB_Storage_BackupRestoreRequest_CALLBACK NULL +#define PB_Storage_BackupRestoreRequest_DEFAULT NULL + extern const pb_msgdesc_t PB_Storage_File_msg; extern const pb_msgdesc_t PB_Storage_InfoRequest_msg; extern const pb_msgdesc_t PB_Storage_InfoResponse_msg; @@ -256,6 +280,8 @@ extern const pb_msgdesc_t PB_Storage_MkdirRequest_msg; extern const pb_msgdesc_t PB_Storage_Md5sumRequest_msg; extern const pb_msgdesc_t PB_Storage_Md5sumResponse_msg; extern const pb_msgdesc_t PB_Storage_RenameRequest_msg; +extern const pb_msgdesc_t PB_Storage_BackupCreateRequest_msg; +extern const pb_msgdesc_t PB_Storage_BackupRestoreRequest_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define PB_Storage_File_fields &PB_Storage_File_msg @@ -273,6 +299,8 @@ extern const pb_msgdesc_t PB_Storage_RenameRequest_msg; #define PB_Storage_Md5sumRequest_fields &PB_Storage_Md5sumRequest_msg #define PB_Storage_Md5sumResponse_fields &PB_Storage_Md5sumResponse_msg #define PB_Storage_RenameRequest_fields &PB_Storage_RenameRequest_msg +#define PB_Storage_BackupCreateRequest_fields &PB_Storage_BackupCreateRequest_msg +#define PB_Storage_BackupRestoreRequest_fields &PB_Storage_BackupRestoreRequest_msg /* Maximum encoded size of messages (where known) */ /* PB_Storage_File_size depends on runtime parameters */ @@ -288,6 +316,8 @@ extern const pb_msgdesc_t PB_Storage_RenameRequest_msg; /* PB_Storage_MkdirRequest_size depends on runtime parameters */ /* PB_Storage_Md5sumRequest_size depends on runtime parameters */ /* PB_Storage_RenameRequest_size depends on runtime parameters */ +/* PB_Storage_BackupCreateRequest_size depends on runtime parameters */ +/* PB_Storage_BackupRestoreRequest_size depends on runtime parameters */ #define PB_Storage_InfoResponse_size 22 #define PB_Storage_Md5sumResponse_size 34 diff --git a/assets/compiled/system.pb.c b/assets/compiled/system.pb.c index 8f2a37aea..45ab3a010 100644 --- a/assets/compiled/system.pb.c +++ b/assets/compiled/system.pb.c @@ -45,5 +45,14 @@ PB_BIND(PB_System_ProtobufVersionRequest, PB_System_ProtobufVersionRequest, AUTO PB_BIND(PB_System_ProtobufVersionResponse, PB_System_ProtobufVersionResponse, AUTO) +PB_BIND(PB_System_UpdateRequest, PB_System_UpdateRequest, AUTO) + + +PB_BIND(PB_System_PowerInfoRequest, PB_System_PowerInfoRequest, AUTO) + + +PB_BIND(PB_System_PowerInfoResponse, PB_System_PowerInfoResponse, AUTO) + + diff --git a/assets/compiled/system.pb.h b/assets/compiled/system.pb.h index 93c32b22e..0932c6fb6 100644 --- a/assets/compiled/system.pb.h +++ b/assets/compiled/system.pb.h @@ -45,10 +45,23 @@ typedef struct _PB_System_PlayAudiovisualAlertRequest { char dummy_field; } PB_System_PlayAudiovisualAlertRequest; +typedef struct _PB_System_PowerInfoRequest { + char dummy_field; +} PB_System_PowerInfoRequest; + +typedef struct _PB_System_PowerInfoResponse { + char *key; + char *value; +} PB_System_PowerInfoResponse; + typedef struct _PB_System_ProtobufVersionRequest { char dummy_field; } PB_System_ProtobufVersionRequest; +typedef struct _PB_System_UpdateRequest { + char *update_folder; +} PB_System_UpdateRequest; + typedef struct _PB_System_DateTime { /* Time */ uint8_t hour; /* *< Hour in 24H format: 0-23 */ @@ -105,6 +118,9 @@ extern "C" { #define PB_System_PlayAudiovisualAlertRequest_init_default {0} #define PB_System_ProtobufVersionRequest_init_default {0} #define PB_System_ProtobufVersionResponse_init_default {0, 0} +#define PB_System_UpdateRequest_init_default {NULL} +#define PB_System_PowerInfoRequest_init_default {0} +#define PB_System_PowerInfoResponse_init_default {NULL, NULL} #define PB_System_PingRequest_init_zero {NULL} #define PB_System_PingResponse_init_zero {NULL} #define PB_System_RebootRequest_init_zero {_PB_System_RebootRequest_RebootMode_MIN} @@ -118,12 +134,18 @@ extern "C" { #define PB_System_PlayAudiovisualAlertRequest_init_zero {0} #define PB_System_ProtobufVersionRequest_init_zero {0} #define PB_System_ProtobufVersionResponse_init_zero {0, 0} +#define PB_System_UpdateRequest_init_zero {NULL} +#define PB_System_PowerInfoRequest_init_zero {0} +#define PB_System_PowerInfoResponse_init_zero {NULL, NULL} /* Field tags (for use in manual encoding/decoding) */ #define PB_System_DeviceInfoResponse_key_tag 1 #define PB_System_DeviceInfoResponse_value_tag 2 #define PB_System_PingRequest_data_tag 1 #define PB_System_PingResponse_data_tag 1 +#define PB_System_PowerInfoResponse_key_tag 1 +#define PB_System_PowerInfoResponse_value_tag 2 +#define PB_System_UpdateRequest_update_folder_tag 1 #define PB_System_DateTime_hour_tag 1 #define PB_System_DateTime_minute_tag 2 #define PB_System_DateTime_second_tag 3 @@ -213,6 +235,22 @@ X(a, STATIC, SINGULAR, UINT32, minor, 2) #define PB_System_ProtobufVersionResponse_CALLBACK NULL #define PB_System_ProtobufVersionResponse_DEFAULT NULL +#define PB_System_UpdateRequest_FIELDLIST(X, a) \ +X(a, POINTER, SINGULAR, STRING, update_folder, 1) +#define PB_System_UpdateRequest_CALLBACK NULL +#define PB_System_UpdateRequest_DEFAULT NULL + +#define PB_System_PowerInfoRequest_FIELDLIST(X, a) \ + +#define PB_System_PowerInfoRequest_CALLBACK NULL +#define PB_System_PowerInfoRequest_DEFAULT NULL + +#define PB_System_PowerInfoResponse_FIELDLIST(X, a) \ +X(a, POINTER, SINGULAR, STRING, key, 1) \ +X(a, POINTER, SINGULAR, STRING, value, 2) +#define PB_System_PowerInfoResponse_CALLBACK NULL +#define PB_System_PowerInfoResponse_DEFAULT NULL + extern const pb_msgdesc_t PB_System_PingRequest_msg; extern const pb_msgdesc_t PB_System_PingResponse_msg; extern const pb_msgdesc_t PB_System_RebootRequest_msg; @@ -226,6 +264,9 @@ extern const pb_msgdesc_t PB_System_DateTime_msg; 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_ProtobufVersionResponse_msg; +extern const pb_msgdesc_t PB_System_UpdateRequest_msg; +extern const pb_msgdesc_t PB_System_PowerInfoRequest_msg; +extern const pb_msgdesc_t PB_System_PowerInfoResponse_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ #define PB_System_PingRequest_fields &PB_System_PingRequest_msg @@ -241,17 +282,23 @@ extern const pb_msgdesc_t PB_System_ProtobufVersionResponse_msg; #define PB_System_PlayAudiovisualAlertRequest_fields &PB_System_PlayAudiovisualAlertRequest_msg #define PB_System_ProtobufVersionRequest_fields &PB_System_ProtobufVersionRequest_msg #define PB_System_ProtobufVersionResponse_fields &PB_System_ProtobufVersionResponse_msg +#define PB_System_UpdateRequest_fields &PB_System_UpdateRequest_msg +#define PB_System_PowerInfoRequest_fields &PB_System_PowerInfoRequest_msg +#define PB_System_PowerInfoResponse_fields &PB_System_PowerInfoResponse_msg /* Maximum encoded size of messages (where known) */ /* PB_System_PingRequest_size depends on runtime parameters */ /* PB_System_PingResponse_size depends on runtime parameters */ /* PB_System_DeviceInfoResponse_size depends on runtime parameters */ +/* PB_System_UpdateRequest_size depends on runtime parameters */ +/* PB_System_PowerInfoResponse_size depends on runtime parameters */ #define PB_System_DateTime_size 22 #define PB_System_DeviceInfoRequest_size 0 #define PB_System_FactoryResetRequest_size 0 #define PB_System_GetDateTimeRequest_size 0 #define PB_System_GetDateTimeResponse_size 24 #define PB_System_PlayAudiovisualAlertRequest_size 0 +#define PB_System_PowerInfoRequest_size 0 #define PB_System_ProtobufVersionRequest_size 0 #define PB_System_ProtobufVersionResponse_size 12 #define PB_System_RebootRequest_size 2 diff --git a/firmware/targets/f7/furi_hal/furi_hal_crypto.c b/firmware/targets/f7/furi_hal/furi_hal_crypto.c index 3cb0e7357..2164ebc32 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_crypto.c +++ b/firmware/targets/f7/furi_hal/furi_hal_crypto.c @@ -15,13 +15,16 @@ #define CRYPTO_TIMEOUT (1000) #define CRYPTO_MODE_ENCRYPT 0U +#define CRYPTO_MODE_INIT (AES_CR_MODE_0) #define CRYPTO_MODE_DECRYPT (AES_CR_MODE_1) #define CRYPTO_MODE_DECRYPT_INIT (AES_CR_MODE_0 | AES_CR_MODE_1) + #define CRYPTO_DATATYPE_32B 0U #define CRYPTO_KEYSIZE_256B (AES_CR_KEYSIZE) #define CRYPTO_AES_CBC (AES_CR_CHMOD_0) static osMutexId_t furi_hal_crypto_mutex = NULL; +static bool furi_hal_crypto_mode_init_done = false; static const uint8_t enclave_signature_iv[ENCLAVE_FACTORY_KEY_SLOTS][16] = { {0xac, 0x5d, 0x68, 0xb8, 0x79, 0x74, 0xfc, 0x7f, 0x45, 0x02, 0x82, 0xf1, 0x48, 0x7e, 0x75, 0x8a}, @@ -177,20 +180,8 @@ bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot) { return (shci_state == SHCI_Success); } -static void crypto_enable() { - SET_BIT(AES1->CR, AES_CR_EN); -} - -static void crypto_disable() { - CLEAR_BIT(AES1->CR, AES_CR_EN); - FURI_CRITICAL_ENTER(); - LL_AHB2_GRP1_ForceReset(LL_AHB2_GRP1_PERIPH_AES1); - LL_AHB2_GRP1_ReleaseReset(LL_AHB2_GRP1_PERIPH_AES1); - FURI_CRITICAL_EXIT(); -} - static void crypto_key_init(uint32_t* key, uint32_t* iv) { - crypto_disable(); + CLEAR_BIT(AES1->CR, AES_CR_EN); MODIFY_REG( AES1->CR, AES_CR_DATATYPE | AES_CR_KEYSIZE | AES_CR_CHMOD, @@ -254,12 +245,13 @@ bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) { return false; } + furi_hal_crypto_mode_init_done = false; crypto_key_init(NULL, (uint32_t*)iv); if(SHCI_C2_FUS_LoadUsrKey(slot) == SHCI_Success) { return true; } else { - crypto_disable(); + CLEAR_BIT(AES1->CR, AES_CR_EN); furi_check(osMutexRelease(furi_hal_crypto_mutex) == osOK); return false; } @@ -270,9 +262,16 @@ bool furi_hal_crypto_store_unload_key(uint8_t slot) { return false; } - crypto_disable(); + CLEAR_BIT(AES1->CR, AES_CR_EN); SHCI_CmdStatus_t shci_state = SHCI_C2_FUS_UnloadUsrKey(slot); + furi_assert(shci_state == SHCI_Success); + + FURI_CRITICAL_ENTER(); + LL_AHB2_GRP1_ForceReset(LL_AHB2_GRP1_PERIPH_AES1); + LL_AHB2_GRP1_ReleaseReset(LL_AHB2_GRP1_PERIPH_AES1); + FURI_CRITICAL_EXIT(); + furi_check(osMutexRelease(furi_hal_crypto_mutex) == osOK); return (shci_state == SHCI_Success); } @@ -280,7 +279,7 @@ bool furi_hal_crypto_store_unload_key(uint8_t slot) { bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size) { bool state = false; - crypto_enable(); + SET_BIT(AES1->CR, AES_CR_EN); MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_ENCRYPT); @@ -295,7 +294,7 @@ bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size) } } - crypto_disable(); + CLEAR_BIT(AES1->CR, AES_CR_EN); return state; } @@ -303,9 +302,28 @@ bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size) bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size) { bool state = false; - MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_DECRYPT_INIT); + if(!furi_hal_crypto_mode_init_done) { + MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_INIT); - crypto_enable(); + SET_BIT(AES1->CR, AES_CR_EN); + + uint32_t countdown = CRYPTO_TIMEOUT; + while(!READ_BIT(AES1->SR, AES_SR_CCF)) { + if(LL_SYSTICK_IsActiveCounterFlag()) { + countdown--; + } + if(countdown == 0) { + return false; + } + } + + SET_BIT(AES1->CR, AES_CR_CCFC); + + furi_hal_crypto_mode_init_done = true; + } + + MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_DECRYPT); + SET_BIT(AES1->CR, AES_CR_EN); for(size_t i = 0; i < size; i += CRYPTO_BLK_LEN) { size_t blk_len = size - i; @@ -318,7 +336,7 @@ bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size) } } - crypto_disable(); + CLEAR_BIT(AES1->CR, AES_CR_EN); return state; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index ec30656ec..e5060536d 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -239,6 +239,13 @@ uint32_t furi_hal_power_get_battery_full_capacity() { return ret; } +uint32_t furi_hal_power_get_battery_design_capacity() { + furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); + uint32_t ret = bq27220_get_design_capacity(&furi_hal_i2c_handle_power); + furi_hal_i2c_release(&furi_hal_i2c_handle_power); + return ret; +} + float furi_hal_power_get_battery_voltage(FuriHalPowerIC ic) { float ret = 0.0f; @@ -399,3 +406,58 @@ void furi_hal_power_suppress_charge_exit() { furi_hal_i2c_release(&furi_hal_i2c_handle_power); } } + +void furi_hal_power_info_get(FuriHalPowerInfoCallback out, void* context) { + furi_assert(out); + + string_t value; + string_init(value); + + // Power Info version + out("power_info_major", "1", false, context); + out("power_info_minor", "0", false, context); + + uint8_t charge = furi_hal_power_get_pct(); + + string_printf(value, "%u", charge); + out("charge_level", string_get_cstr(value), false, context); + + if(furi_hal_power_is_charging()) { + if(charge < 100) { + string_printf(value, "charging"); + } else { + string_printf(value, "charged"); + } + } else { + string_printf(value, "discharging"); + } + out("charge_state", string_get_cstr(value), false, context); + + uint16_t voltage = + (uint16_t)(furi_hal_power_get_battery_voltage(FuriHalPowerICFuelGauge) * 1000.f); + string_printf(value, "%u", voltage); + out("battery_voltage", string_get_cstr(value), false, context); + + int16_t current = + (int16_t)(furi_hal_power_get_battery_current(FuriHalPowerICFuelGauge) * 1000.f); + string_printf(value, "%d", current); + out("battery_current", string_get_cstr(value), false, context); + + int16_t temperature = (int16_t)furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge); + string_printf(value, "%d", temperature); + out("gauge_temp", string_get_cstr(value), false, context); + + string_printf(value, "%u", furi_hal_power_get_bat_health_pct()); + out("battery_health", string_get_cstr(value), false, context); + + string_printf(value, "%u", furi_hal_power_get_battery_remaining_capacity()); + out("capacity_remain", string_get_cstr(value), false, context); + + string_printf(value, "%u", furi_hal_power_get_battery_full_capacity()); + out("capacity_full", string_get_cstr(value), false, context); + + string_printf(value, "%u", furi_hal_power_get_battery_design_capacity()); + out("capacity_design", string_get_cstr(value), true, context); + + string_clear(value); +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_speaker.c b/firmware/targets/f7/furi_hal/furi_hal_speaker.c index 80c69a083..298574d83 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_speaker.c +++ b/firmware/targets/f7/furi_hal/furi_hal_speaker.c @@ -25,21 +25,32 @@ void furi_hal_speaker_start(float frequency, float volume) { if(volume > 1) volume = 1; volume = volume * volume * volume; + uint32_t autoreload = (SystemCoreClock / FURI_HAL_SPEAKER_PRESCALER / frequency) - 1; + if(autoreload < 2) { + autoreload = 2; + } else if(autoreload > UINT16_MAX) { + autoreload = UINT16_MAX; + } + LL_TIM_InitTypeDef TIM_InitStruct = {0}; TIM_InitStruct.Prescaler = FURI_HAL_SPEAKER_PRESCALER - 1; - TIM_InitStruct.Autoreload = ((SystemCoreClock / FURI_HAL_SPEAKER_PRESCALER) / frequency) - 1; + TIM_InitStruct.Autoreload = autoreload; LL_TIM_Init(FURI_HAL_SPEAKER_TIMER, &TIM_InitStruct); #ifdef FURI_HAL_SPEAKER_NEW_VOLUME - uint16_t compare_value = volume * FURI_HAL_SPEAKER_MAX_VOLUME; - uint16_t clip_value = volume * TIM_InitStruct.Autoreload / 2; + uint32_t compare_value = volume * FURI_HAL_SPEAKER_MAX_VOLUME; + uint32_t clip_value = volume * TIM_InitStruct.Autoreload / 2; if(compare_value > clip_value) { compare_value = clip_value; } #else - uint16_t compare_value = volume * TIM_InitStruct.Autoreload / 2; + uint32_t compare_value = volume * autoreload / 2; #endif + if(compare_value == 0) { + compare_value = 1; + } + LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; @@ -51,6 +62,6 @@ void furi_hal_speaker_start(float frequency, float volume) { } void furi_hal_speaker_stop() { - LL_TIM_CC_DisableChannel(FURI_HAL_SPEAKER_TIMER, FURI_HAL_SPEAKER_CHANNEL); + LL_TIM_DisableAllOutputs(FURI_HAL_SPEAKER_TIMER); LL_TIM_DisableCounter(FURI_HAL_SPEAKER_TIMER); } diff --git a/firmware/targets/furi_hal_include/furi_hal_power.h b/firmware/targets/furi_hal_include/furi_hal_power.h index 3f5a00207..c55e38045 100644 --- a/firmware/targets/furi_hal_include/furi_hal_power.h +++ b/firmware/targets/furi_hal_include/furi_hal_power.h @@ -113,6 +113,12 @@ uint32_t furi_hal_power_get_battery_remaining_capacity(); */ uint32_t furi_hal_power_get_battery_full_capacity(); +/** Get battery capacity in mAh from battery profile + * + * @return capacity in mAh + */ +uint32_t furi_hal_power_get_battery_design_capacity(); + /** Get battery voltage in V * * @param ic FuriHalPowerIc to get measurment @@ -171,6 +177,23 @@ void furi_hal_power_suppress_charge_enter(); */ void furi_hal_power_suppress_charge_exit(); +/** Callback type called by furi_hal_power_info_get every time another key-value pair of information is ready + * + * @param key[in] power information type identifier + * @param value[in] power information value + * @param last[in] whether the passed key-value pair is the last one + * @param context[in] to pass to callback + */ +typedef void ( + *FuriHalPowerInfoCallback)(const char* key, const char* value, bool last, void* context); + +/** Get power information + * + * @param[in] callback callback to provide with new data + * @param[in] context context to pass to callback + */ +void furi_hal_power_info_get(FuriHalPowerInfoCallback callback, void* context); + #ifdef __cplusplus } #endif