From 4a1695ba1cc787c73a6f135c8a352fe300cdea72 Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Mon, 4 Jul 2022 16:09:46 +0300 Subject: [PATCH 1/7] [FL-2589] RPC App control commands (#1350) * RPC App control commands * Button release timeout * SubGhz tx fix Co-authored-by: Aleksandr Kutuzov --- applications/gpio/usb_uart_bridge.c | 2 - applications/ibutton/ibutton.c | 60 +++++- applications/ibutton/ibutton_custom_event.h | 2 + applications/ibutton/ibutton_i.h | 2 + .../ibutton/scenes/ibutton_scene_config.h | 1 + .../ibutton/scenes/ibutton_scene_rpc.c | 36 ++++ applications/infrared/infrared.c | 76 +++++++- applications/infrared/infrared_i.h | 4 + applications/infrared/infrared_remote.c | 13 ++ applications/infrared/infrared_remote.h | 1 + .../infrared/scenes/infrared_scene_config.h | 1 + .../infrared/scenes/infrared_scene_rpc.c | 37 ++++ applications/lfrfid/lfrfid_app.cpp | 63 ++++++- applications/lfrfid/lfrfid_app.h | 9 +- .../lfrfid/scene/lfrfid_app_scene_rpc.cpp | 37 ++++ .../lfrfid/scene/lfrfid_app_scene_rpc.h | 12 ++ applications/nfc/nfc.c | 78 +++++++- applications/nfc/nfc_device.c | 12 +- applications/nfc/nfc_device.h | 2 +- applications/nfc/nfc_i.h | 13 ++ applications/nfc/scenes/nfc_scene_config.h | 1 + applications/nfc/scenes/nfc_scene_rpc.c | 34 ++++ applications/rpc/rpc.c | 2 +- applications/rpc/rpc_app.c | 175 +++++++++++++++++- applications/rpc/rpc_app.h | 24 +++ applications/rpc/rpc_i.h | 1 + .../subghz/scenes/subghz_scene_config.h | 1 + applications/subghz/scenes/subghz_scene_rpc.c | 34 ++++ applications/subghz/subghz.c | 60 +++++- applications/subghz/subghz_i.c | 12 +- applications/subghz/subghz_i.h | 6 +- assets/protobuf | 2 +- 32 files changed, 768 insertions(+), 45 deletions(-) create mode 100644 applications/ibutton/scenes/ibutton_scene_rpc.c create mode 100644 applications/infrared/scenes/infrared_scene_rpc.c create mode 100644 applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp create mode 100644 applications/lfrfid/scene/lfrfid_app_scene_rpc.h mode change 100755 => 100644 applications/nfc/nfc.c create mode 100644 applications/nfc/scenes/nfc_scene_rpc.c create mode 100644 applications/rpc/rpc_app.h create mode 100644 applications/subghz/scenes/subghz_scene_rpc.c diff --git a/applications/gpio/usb_uart_bridge.c b/applications/gpio/usb_uart_bridge.c index cf7e7687b..9c6ba0a6b 100644 --- a/applications/gpio/usb_uart_bridge.c +++ b/applications/gpio/usb_uart_bridge.c @@ -85,7 +85,6 @@ static void usb_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) { static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) { furi_hal_usb_unlock(); - FURI_LOG_I("", "Init %d", vcp_ch); if(vcp_ch == 0) { Cli* cli = furi_record_open("cli"); cli_session_close(cli); @@ -103,7 +102,6 @@ static void usb_uart_vcp_init(UsbUartBridge* usb_uart, uint8_t vcp_ch) { static void usb_uart_vcp_deinit(UsbUartBridge* usb_uart, uint8_t vcp_ch) { UNUSED(usb_uart); furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL); - FURI_LOG_I("", "Deinit %d", vcp_ch); if(vcp_ch != 0) { Cli* cli = furi_record_open("cli"); cli_session_close(cli); diff --git a/applications/ibutton/ibutton.c b/applications/ibutton/ibutton.c index ae93f9720..0f54dc3ee 100644 --- a/applications/ibutton/ibutton.c +++ b/applications/ibutton/ibutton.c @@ -5,6 +5,9 @@ #include "m-string.h" #include #include +#include "rpc/rpc_app.h" + +#define TAG "iButtonApp" static const NotificationSequence sequence_blink_start_cyan = { &message_blink_start_10, @@ -55,7 +58,7 @@ static void ibutton_make_app_folder(iButton* ibutton) { } } -static bool ibutton_load_key_data(iButton* ibutton, string_t key_path) { +static bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show_dialog) { FlipperFormat* file = flipper_format_file_alloc(ibutton->storage); bool result = false; string_t data; @@ -89,13 +92,40 @@ static bool ibutton_load_key_data(iButton* ibutton, string_t key_path) { flipper_format_free(file); string_clear(data); - if(!result) { + if((!result) && (show_dialog)) { dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file"); } return result; } +static bool ibutton_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { + furi_assert(context); + iButton* ibutton = context; + + bool result = false; + + if(event == RpcAppEventSessionClose) { + rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); + ibutton->rpc_ctx = NULL; + view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); + result = true; + } else if(event == RpcAppEventAppExit) { + view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); + result = true; + } else if(event == RpcAppEventLoadFile) { + if(arg) { + string_set_str(ibutton->file_path, arg); + if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) { + ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key); + result = true; + } + } + } + + return result; +} + bool ibutton_custom_event_callback(void* context, uint32_t event) { furi_assert(context); iButton* ibutton = context; @@ -226,7 +256,7 @@ bool ibutton_file_select(iButton* ibutton) { true); if(success) { - success = ibutton_load_key_data(ibutton, ibutton->file_path); + success = ibutton_load_key_data(ibutton, ibutton->file_path, true); } return success; @@ -334,16 +364,27 @@ int32_t ibutton_app(void* p) { ibutton_make_app_folder(ibutton); bool key_loaded = false; + bool rpc_mode = false; if(p) { - string_set_str(ibutton->file_path, (const char*)p); - if(ibutton_load_key_data(ibutton, ibutton->file_path)) { - key_loaded = true; - // TODO: Display an error if the key from p could not be loaded + uint32_t rpc_ctx = 0; + if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { + FURI_LOG_D(TAG, "Running in RPC mode"); + ibutton->rpc_ctx = (void*)rpc_ctx; + rpc_mode = true; + rpc_system_app_set_callback(ibutton->rpc_ctx, ibutton_rpc_command_callback, ibutton); + } else { + string_set_str(ibutton->file_path, (const char*)p); + if(ibutton_load_key_data(ibutton, ibutton->file_path, true)) { + key_loaded = true; + // TODO: Display an error if the key from p could not be loaded + } } } - if(key_loaded) { + if(rpc_mode) { + scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRpc); + } else if(key_loaded) { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate); } else { scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart); @@ -351,6 +392,9 @@ int32_t ibutton_app(void* p) { view_dispatcher_run(ibutton->view_dispatcher); + if(ibutton->rpc_ctx) { + rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); + } ibutton_free(ibutton); return 0; } diff --git a/applications/ibutton/ibutton_custom_event.h b/applications/ibutton/ibutton_custom_event.h index 2be42d66d..1706e00fa 100644 --- a/applications/ibutton/ibutton_custom_event.h +++ b/applications/ibutton/ibutton_custom_event.h @@ -9,4 +9,6 @@ enum iButtonCustomEvent { iButtonCustomEventByteEditResult, iButtonCustomEventWorkerEmulated, iButtonCustomEventWorkerRead, + + iButtonCustomEventRpcExit, }; diff --git a/applications/ibutton/ibutton_i.h b/applications/ibutton/ibutton_i.h index a85dd5f6d..889d5a67c 100644 --- a/applications/ibutton/ibutton_i.h +++ b/applications/ibutton/ibutton_i.h @@ -50,6 +50,8 @@ struct iButton { Popup* popup; Widget* widget; DialogEx* dialog_ex; + + void* rpc_ctx; }; typedef enum { diff --git a/applications/ibutton/scenes/ibutton_scene_config.h b/applications/ibutton/scenes/ibutton_scene_config.h index d30b43beb..87fa1a036 100644 --- a/applications/ibutton/scenes/ibutton_scene_config.h +++ b/applications/ibutton/scenes/ibutton_scene_config.h @@ -18,3 +18,4 @@ ADD_SCENE(ibutton, delete_confirm, DeleteConfirm) ADD_SCENE(ibutton, delete_success, DeleteSuccess) ADD_SCENE(ibutton, retry_confirm, RetryConfirm) ADD_SCENE(ibutton, exit_confirm, ExitConfirm) +ADD_SCENE(ibutton, rpc, Rpc) diff --git a/applications/ibutton/scenes/ibutton_scene_rpc.c b/applications/ibutton/scenes/ibutton_scene_rpc.c new file mode 100644 index 000000000..ceeca0179 --- /dev/null +++ b/applications/ibutton/scenes/ibutton_scene_rpc.c @@ -0,0 +1,36 @@ +#include "../ibutton_i.h" +#include + +void ibutton_scene_rpc_on_enter(void* context) { + iButton* ibutton = context; + Widget* widget = ibutton->widget; + + widget_add_text_box_element( + widget, 0, 0, 128, 28, AlignCenter, AlignCenter, "RPC mode", false); + + view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget); + + notification_message(ibutton->notifications, &sequence_display_backlight_on); +} + +bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + iButton* ibutton = context; + + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == iButtonCustomEventRpcExit) { + view_dispatcher_stop(ibutton->view_dispatcher); + } + } + + return consumed; +} + +void ibutton_scene_rpc_on_exit(void* context) { + iButton* ibutton = context; + widget_reset(ibutton->widget); +} diff --git a/applications/infrared/infrared.c b/applications/infrared/infrared.c index e641302cd..622061165 100644 --- a/applications/infrared/infrared.c +++ b/applications/infrared/infrared.c @@ -36,6 +36,52 @@ static void infrared_tick_event_callback(void* context) { scene_manager_handle_tick_event(infrared->scene_manager); } +static bool + infrared_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { + furi_assert(context); + Infrared* infrared = context; + + if(!infrared->rpc_ctx) { + return false; + } + + bool result = false; + + if(event == RpcAppEventSessionClose) { + rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); + infrared->rpc_ctx = NULL; + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeBackPressed); + result = true; + } else if(event == RpcAppEventAppExit) { + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeBackPressed); + result = true; + } else if(event == RpcAppEventLoadFile) { + if(arg) { + string_set_str(infrared->file_path, arg); + result = infrared_remote_load(infrared->remote, infrared->file_path); + infrared_worker_tx_set_get_signal_callback( + infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared); + infrared_worker_tx_set_signal_sent_callback( + infrared->worker, infrared_signal_sent_callback, infrared); + } + } else if(event == RpcAppEventButtonPress) { + if(arg) { + size_t button_index = 0; + if(infrared_remote_find_button_by_name(infrared->remote, arg, &button_index)) { + infrared_tx_start_button_index(infrared, button_index); + result = true; + } + } + } else if(event == RpcAppEventButtonRelease) { + infrared_tx_stop(infrared); + result = true; + } + + return result; +} + static void infrared_find_vacant_remote_name(string_t name, const char* path) { Storage* storage = furi_record_open("storage"); @@ -154,6 +200,11 @@ static void infrared_free(Infrared* infrared) { ViewDispatcher* view_dispatcher = infrared->view_dispatcher; InfraredAppState* app_state = &infrared->app_state; + if(infrared->rpc_ctx) { + rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); + infrared->rpc_ctx = NULL; + } + view_dispatcher_remove_view(view_dispatcher, InfraredViewSubmenu); submenu_free(infrared->submenu); @@ -375,18 +426,29 @@ int32_t infrared_app(void* p) { infrared_make_app_folder(infrared); bool is_remote_loaded = false; + bool is_rpc_mode = false; if(p) { - string_set_str(infrared->file_path, (const char*)p); - is_remote_loaded = infrared_remote_load(infrared->remote, infrared->file_path); - if(!is_remote_loaded) { - dialog_message_show_storage_error( - infrared->dialogs, "Failed to load\nselected remote"); - return -1; + uint32_t rpc_ctx = 0; + if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { + infrared->rpc_ctx = (void*)rpc_ctx; + rpc_system_app_set_callback( + infrared->rpc_ctx, infrared_rpc_command_callback, infrared); + is_rpc_mode = true; + } else { + string_set_str(infrared->file_path, (const char*)p); + is_remote_loaded = infrared_remote_load(infrared->remote, infrared->file_path); + if(!is_remote_loaded) { + dialog_message_show_storage_error( + infrared->dialogs, "Failed to load\nselected remote"); + return -1; + } } } - if(is_remote_loaded) { + if(is_rpc_mode) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneRpc); + } else if(is_remote_loaded) { scene_manager_next_scene(infrared->scene_manager, InfraredSceneRemote); } else { scene_manager_next_scene(infrared->scene_manager, InfraredSceneStart); diff --git a/applications/infrared/infrared_i.h b/applications/infrared/infrared_i.h index 5c447adc3..c47753f82 100644 --- a/applications/infrared/infrared_i.h +++ b/applications/infrared/infrared_i.h @@ -30,6 +30,8 @@ #include "views/infrared_progress_view.h" #include "views/infrared_debug_view.h" +#include "rpc/rpc_app.h" + #define INFRARED_FILE_NAME_SIZE 100 #define INFRARED_TEXT_STORE_NUM 2 #define INFRARED_TEXT_STORE_SIZE 128 @@ -95,6 +97,8 @@ struct Infrared { string_t file_path; char text_store[INFRARED_TEXT_STORE_NUM][INFRARED_TEXT_STORE_SIZE + 1]; InfraredAppState app_state; + + void* rpc_ctx; }; typedef enum { diff --git a/applications/infrared/infrared_remote.c b/applications/infrared/infrared_remote.c index ad75efe7f..94658035b 100644 --- a/applications/infrared/infrared_remote.c +++ b/applications/infrared/infrared_remote.c @@ -1,5 +1,7 @@ #include "infrared_remote.h" +#include +#include #include #include #include @@ -73,6 +75,17 @@ InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t return *InfraredButtonArray_get(remote->buttons, index); } +bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index) { + for(size_t i = 0; i < InfraredButtonArray_size(remote->buttons); i++) { + InfraredRemoteButton* button = *InfraredButtonArray_get(remote->buttons, i); + if(!strcmp(infrared_remote_button_get_name(button), name)) { + *index = i; + return true; + } + } + return false; +} + bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal) { InfraredRemoteButton* button = infrared_remote_button_alloc(); infrared_remote_button_set_name(button, name); diff --git a/applications/infrared/infrared_remote.h b/applications/infrared/infrared_remote.h index 1336383f2..b6f63a198 100644 --- a/applications/infrared/infrared_remote.h +++ b/applications/infrared/infrared_remote.h @@ -18,6 +18,7 @@ const char* infrared_remote_get_path(InfraredRemote* remote); size_t infrared_remote_get_button_count(InfraredRemote* remote); InfraredRemoteButton* infrared_remote_get_button(InfraredRemote* remote, size_t index); +bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* name, size_t* index); bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal); bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index); diff --git a/applications/infrared/scenes/infrared_scene_config.h b/applications/infrared/scenes/infrared_scene_config.h index c4e083c17..8ff3b167b 100644 --- a/applications/infrared/scenes/infrared_scene_config.h +++ b/applications/infrared/scenes/infrared_scene_config.h @@ -16,3 +16,4 @@ ADD_SCENE(infrared, universal, Universal) ADD_SCENE(infrared, universal_tv, UniversalTV) ADD_SCENE(infrared, debug, Debug) ADD_SCENE(infrared, error_databases, ErrorDatabases) +ADD_SCENE(infrared, rpc, Rpc) diff --git a/applications/infrared/scenes/infrared_scene_rpc.c b/applications/infrared/scenes/infrared_scene_rpc.c new file mode 100644 index 000000000..3cab9f80e --- /dev/null +++ b/applications/infrared/scenes/infrared_scene_rpc.c @@ -0,0 +1,37 @@ +#include "../infrared_i.h" +#include "gui/canvas.h" + +void infrared_scene_rpc_on_enter(void* context) { + Infrared* infrared = context; + Popup* popup = infrared->popup; + + popup_set_text(popup, "Rpc mode", 64, 28, AlignCenter, AlignCenter); + + popup_set_context(popup, context); + popup_set_callback(popup, infrared_popup_closed_callback); + + infrared_play_notification_message(infrared, InfraredNotificationMessageYellowOn); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup); + + notification_message(infrared->notifications, &sequence_display_backlight_on); +} + +bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { + Infrared* infrared = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == InfraredCustomEventTypeBackPressed) { + view_dispatcher_stop(infrared->view_dispatcher); + } else if(event.event == InfraredCustomEventTypePopupClosed) { + view_dispatcher_stop(infrared->view_dispatcher); + } + } + return consumed; +} + +void infrared_scene_rpc_on_exit(void* context) { + Infrared* infrared = context; + popup_reset(infrared->popup); +} diff --git a/applications/lfrfid/lfrfid_app.cpp b/applications/lfrfid/lfrfid_app.cpp index 4027a07ea..d2c92d248 100644 --- a/applications/lfrfid/lfrfid_app.cpp +++ b/applications/lfrfid/lfrfid_app.cpp @@ -20,10 +20,13 @@ #include "scene/lfrfid_app_scene_saved_info.h" #include "scene/lfrfid_app_scene_delete_confirm.h" #include "scene/lfrfid_app_scene_delete_success.h" +#include "scene/lfrfid_app_scene_rpc.h" #include #include +#include "rpc/rpc_app.h" + const char* LfRfidApp::app_folder = "/any/lfrfid"; const char* LfRfidApp::app_extension = ".rfid"; const char* LfRfidApp::app_filetype = "Flipper RFID key"; @@ -39,6 +42,43 @@ LfRfidApp::LfRfidApp() LfRfidApp::~LfRfidApp() { string_clear(file_path); + if(rpc_ctx) { + rpc_system_app_set_callback(rpc_ctx, NULL, NULL); + } +} + +static bool rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { + furi_assert(context); + LfRfidApp* app = static_cast(context); + + bool result = false; + + if(event == RpcAppEventSessionClose) { + rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL); + app->rpc_ctx = NULL; + LfRfidApp::Event event; + event.type = LfRfidApp::EventType::Exit; + app->view_controller.send_event(&event); + result = true; + } else if(event == RpcAppEventAppExit) { + LfRfidApp::Event event; + event.type = LfRfidApp::EventType::Exit; + app->view_controller.send_event(&event); + result = true; + } else if(event == RpcAppEventLoadFile) { + if(arg) { + string_set_str(app->file_path, arg); + if(app->load_key_data(app->file_path, &(app->worker.key), false)) { + LfRfidApp::Event event; + event.type = LfRfidApp::EventType::EmulateStart; + app->view_controller.send_event(&event); + app->worker.start_emulate(); + result = true; + } + } + } + + return result; } void LfRfidApp::run(void* _args) { @@ -47,10 +87,19 @@ void LfRfidApp::run(void* _args) { make_app_folder(); if(strlen(args)) { - string_set_str(file_path, args); - load_key_data(file_path, &worker.key); - scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate()); - scene_controller.process(100, SceneType::Emulate); + uint32_t rpc_ctx_ptr = 0; + if(sscanf(args, "RPC %lX", &rpc_ctx_ptr) == 1) { + rpc_ctx = (RpcAppSystem*)rpc_ctx_ptr; + rpc_system_app_set_callback(rpc_ctx, rpc_command_callback, this); + scene_controller.add_scene(SceneType::Rpc, new LfRfidAppSceneRpc()); + scene_controller.process(100, SceneType::Rpc); + } else { + string_set_str(file_path, args); + load_key_data(file_path, &worker.key, true); + scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate()); + scene_controller.process(100, SceneType::Emulate); + } + } else { scene_controller.add_scene(SceneType::Start, new LfRfidAppSceneStart()); scene_controller.add_scene(SceneType::Read, new LfRfidAppSceneRead()); @@ -99,7 +148,7 @@ bool LfRfidApp::load_key_from_file_select(bool need_restore) { dialogs, file_path, file_path, app_extension, true, &I_125_10px, true); if(result) { - result = load_key_data(file_path, &worker.key); + result = load_key_data(file_path, &worker.key, true); } return result; @@ -110,7 +159,7 @@ bool LfRfidApp::delete_key(RfidKey* key) { return storage_simply_remove(storage, string_get_cstr(file_path)); } -bool LfRfidApp::load_key_data(string_t path, RfidKey* key) { +bool LfRfidApp::load_key_data(string_t path, RfidKey* key, bool show_dialog) { FlipperFormat* file = flipper_format_file_alloc(storage); bool result = false; string_t str_result; @@ -149,7 +198,7 @@ bool LfRfidApp::load_key_data(string_t path, RfidKey* key) { flipper_format_free(file); string_clear(str_result); - if(!result) { + if((!result) && (show_dialog)) { dialog_message_show_storage_error(dialogs, "Cannot load\nkey file"); } diff --git a/applications/lfrfid/lfrfid_app.h b/applications/lfrfid/lfrfid_app.h index 3f8209a1d..3372552fc 100644 --- a/applications/lfrfid/lfrfid_app.h +++ b/applications/lfrfid/lfrfid_app.h @@ -21,6 +21,7 @@ #include #include "helpers/rfid_worker.h" +#include "rpc/rpc_app.h" class LfRfidApp { public: @@ -30,6 +31,8 @@ public: MenuSelected, Stay, Retry, + Exit, + EmulateStart, }; enum class SceneType : uint8_t { @@ -51,6 +54,7 @@ public: SavedInfo, DeleteConfirm, DeleteSuccess, + Rpc, }; class Event { @@ -79,6 +83,8 @@ public: string_t file_path; + RpcAppSystem* rpc_ctx; + void run(void* args); static const char* app_folder; @@ -89,8 +95,9 @@ public: bool load_key_from_file_select(bool need_restore); bool delete_key(RfidKey* key); - bool load_key_data(string_t path, RfidKey* key); + bool load_key_data(string_t path, RfidKey* key, bool show_dialog); bool save_key_data(string_t path, RfidKey* key); void make_app_folder(); + //bool rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context); }; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp b/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp new file mode 100644 index 000000000..012c96ac1 --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp @@ -0,0 +1,37 @@ +#include "lfrfid_app_scene_rpc.h" +#include "furi/common_defines.h" +#include + +void LfRfidAppSceneRpc::on_enter(LfRfidApp* app, bool /* need_restore */) { + auto popup = app->view_controller.get(); + + popup->set_header("RPC Mode", 64, 30, AlignCenter, AlignTop); + + app->view_controller.switch_to(); + + notification_message(app->notification, &sequence_display_backlight_on); +} + +bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) { + UNUSED(app); + UNUSED(event); + bool consumed = false; + + if(event->type == LfRfidApp::EventType::Exit) { + consumed = true; + LfRfidApp::Event view_event; + view_event.type = LfRfidApp::EventType::Back; + app->view_controller.send_event(&view_event); + } else if(event->type == LfRfidApp::EventType::EmulateStart) { + consumed = true; + emulating = true; + } + return consumed; +} + +void LfRfidAppSceneRpc::on_exit(LfRfidApp* app) { + if(emulating) { + app->worker.stop_emulate(); + } + app->view_controller.get()->clean(); +} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_rpc.h b/applications/lfrfid/scene/lfrfid_app_scene_rpc.h new file mode 100644 index 000000000..f630dfd35 --- /dev/null +++ b/applications/lfrfid/scene/lfrfid_app_scene_rpc.h @@ -0,0 +1,12 @@ +#pragma once +#include "../lfrfid_app.h" + +class LfRfidAppSceneRpc : public GenericScene { +public: + void on_enter(LfRfidApp* app, bool need_restore) final; + bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; + void on_exit(LfRfidApp* app) final; + +private: + bool emulating = false; +}; diff --git a/applications/nfc/nfc.c b/applications/nfc/nfc.c old mode 100755 new mode 100644 index 1999ba6ad..e98b5d9a5 --- a/applications/nfc/nfc.c +++ b/applications/nfc/nfc.c @@ -19,6 +19,77 @@ void nfc_tick_event_callback(void* context) { scene_manager_handle_tick_event(nfc->scene_manager); } +void nfc_rpc_exit_callback(Nfc* nfc) { + if(nfc->rpc_state == NfcRpcStateEmulating) { + // Stop worker + nfc_worker_stop(nfc->worker); + } else if(nfc->rpc_state == NfcRpcStateEmulated) { + // Stop worker + nfc_worker_stop(nfc->worker); + // Save data in shadow file + nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); + } + if(nfc->rpc_ctx) { + rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); + nfc->rpc_ctx = NULL; + } +} + +static void nfc_rpc_emulate_callback(NfcWorkerEvent event, void* context) { + UNUSED(event); + Nfc* nfc = context; + + nfc->rpc_state = NfcRpcStateEmulated; +} + +static bool nfc_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { + furi_assert(context); + Nfc* nfc = context; + + if(!nfc->rpc_ctx) { + return false; + } + + bool result = false; + + if(event == RpcAppEventSessionClose) { + rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); + nfc->rpc_ctx = NULL; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); + result = true; + } else if(event == RpcAppEventAppExit) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); + result = true; + } else if(event == RpcAppEventLoadFile) { + if((arg) && (nfc->rpc_state == NfcRpcStateIdle)) { + if(nfc_device_load(nfc->dev, arg, false)) { + if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { + nfc_worker_start( + nfc->worker, + NfcWorkerStateEmulateMifareUltralight, + &nfc->dev->dev_data, + nfc_rpc_emulate_callback, + nfc); + } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { + nfc_worker_start( + nfc->worker, + NfcWorkerStateEmulateMifareClassic, + &nfc->dev->dev_data, + nfc_rpc_emulate_callback, + nfc); + } else { + nfc_worker_start( + nfc->worker, NfcWorkerStateEmulate, &nfc->dev->dev_data, NULL, nfc); + } + nfc->rpc_state = NfcRpcStateEmulating; + result = true; + } + } + } + + return result; +} + Nfc* nfc_alloc() { Nfc* nfc = malloc(sizeof(Nfc)); @@ -193,7 +264,12 @@ int32_t nfc_app(void* p) { // Check argument and run corresponding scene if((*args != '\0')) { - if(nfc_device_load(nfc->dev, p)) { + uint32_t rpc_ctx = 0; + if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { + nfc->rpc_ctx = (void*)rpc_ctx; + rpc_system_app_set_callback(nfc->rpc_ctx, nfc_rpc_command_callback, nfc); + scene_manager_next_scene(nfc->scene_manager, NfcSceneRpc); + } else if(nfc_device_load(nfc->dev, p, true)) { if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateMifareUl); } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { diff --git a/applications/nfc/nfc_device.c b/applications/nfc/nfc_device.c index 092d00896..155cc3f84 100644 --- a/applications/nfc/nfc_device.c +++ b/applications/nfc/nfc_device.c @@ -837,7 +837,7 @@ bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name) { return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_SHADOW_EXTENSION, true); } -static bool nfc_device_load_data(NfcDevice* dev, string_t path) { +static bool nfc_device_load_data(NfcDevice* dev, string_t path, bool show_dialog) { bool parsed = false; FlipperFormat* file = flipper_format_file_alloc(dev->storage); FuriHalNfcDevData* data = &dev->dev_data.nfc_data; @@ -887,7 +887,7 @@ static bool nfc_device_load_data(NfcDevice* dev, string_t path) { parsed = true; } while(false); - if(!parsed) { + if((!parsed) && (show_dialog)) { if(deprecated_version) { dialog_message_show_storage_error(dev->dialogs, "File format deprecated"); } else { @@ -900,13 +900,13 @@ static bool nfc_device_load_data(NfcDevice* dev, string_t path) { return parsed; } -bool nfc_device_load(NfcDevice* dev, const char* file_path) { +bool nfc_device_load(NfcDevice* dev, const char* file_path, bool show_dialog) { furi_assert(dev); furi_assert(file_path); // Load device data string_set_str(dev->load_path, file_path); - bool dev_load = nfc_device_load_data(dev, dev->load_path); + bool dev_load = nfc_device_load_data(dev, dev->load_path, show_dialog); if(dev_load) { // Set device name string_t filename; @@ -933,7 +933,7 @@ bool nfc_file_select(NfcDevice* dev) { string_init(filename); path_extract_filename(dev->load_path, filename, true); strncpy(dev->dev_name, string_get_cstr(filename), NFC_DEV_NAME_MAX_LEN); - res = nfc_device_load_data(dev, dev->load_path); + res = nfc_device_load_data(dev, dev->load_path, true); if(res) { nfc_device_set_name(dev, dev->dev_name); } @@ -1017,7 +1017,7 @@ bool nfc_device_restore(NfcDevice* dev, bool use_load_path) { } else { string_printf(path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_EXTENSION); } - if(!nfc_device_load_data(dev, path)) break; + if(!nfc_device_load_data(dev, path, true)) break; restored = true; } while(0); diff --git a/applications/nfc/nfc_device.h b/applications/nfc/nfc_device.h index 400f6c363..3b2875c09 100644 --- a/applications/nfc/nfc_device.h +++ b/applications/nfc/nfc_device.h @@ -71,7 +71,7 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name); bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name); -bool nfc_device_load(NfcDevice* dev, const char* file_path); +bool nfc_device_load(NfcDevice* dev, const char* file_path, bool show_dialog); bool nfc_file_select(NfcDevice* dev); diff --git a/applications/nfc/nfc_i.h b/applications/nfc/nfc_i.h index cdbe3bea1..c42ddced2 100755 --- a/applications/nfc/nfc_i.h +++ b/applications/nfc/nfc_i.h @@ -29,10 +29,18 @@ #include #include +#include "rpc/rpc_app.h" + #define NFC_SEND_NOTIFICATION_FALSE (0UL) #define NFC_SEND_NOTIFICATION_TRUE (1UL) #define NFC_TEXT_STORE_SIZE 128 +typedef enum { + NfcRpcStateIdle, + NfcRpcStateEmulating, + NfcRpcStateEmulated, +} NfcRpcState; + // Forward declaration due to circular dependency typedef struct NfcGenerator NfcGenerator; @@ -48,6 +56,9 @@ struct Nfc { char text_store[NFC_TEXT_STORE_SIZE + 1]; string_t text_box_store; + void* rpc_ctx; + NfcRpcState rpc_state; + // Common Views Submenu* submenu; DialogEx* dialog_ex; @@ -85,3 +96,5 @@ void nfc_text_store_clear(Nfc* nfc); void nfc_blink_start(Nfc* nfc); void nfc_blink_stop(Nfc* nfc); + +void nfc_rpc_exit_callback(Nfc* nfc); diff --git a/applications/nfc/scenes/nfc_scene_config.h b/applications/nfc/scenes/nfc_scene_config.h index 5cf2c86f6..ffd757de3 100755 --- a/applications/nfc/scenes/nfc_scene_config.h +++ b/applications/nfc/scenes/nfc_scene_config.h @@ -37,4 +37,5 @@ ADD_SCENE(nfc, read_mifare_classic, ReadMifareClassic) ADD_SCENE(nfc, emulate_mifare_classic, EmulateMifareClassic) ADD_SCENE(nfc, mifare_classic_menu, MifareClassicMenu) ADD_SCENE(nfc, dict_not_found, DictNotFound) +ADD_SCENE(nfc, rpc, Rpc) ADD_SCENE(nfc, generate_info, GenerateInfo) diff --git a/applications/nfc/scenes/nfc_scene_rpc.c b/applications/nfc/scenes/nfc_scene_rpc.c new file mode 100644 index 000000000..b94bf424e --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_rpc.c @@ -0,0 +1,34 @@ +#include "../nfc_i.h" + +void nfc_scene_rpc_on_enter(void* context) { + Nfc* nfc = context; + Widget* widget = nfc->widget; + + widget_add_text_box_element( + widget, 0, 0, 128, 28, AlignCenter, AlignCenter, "RPC mode", false); + + notification_message(nfc->notifications, &sequence_display_backlight_on); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == NfcCustomEventViewExit) { + view_dispatcher_stop(nfc->view_dispatcher); + } + } + return consumed; +} + +void nfc_scene_rpc_on_exit(void* context) { + Nfc* nfc = context; + + nfc_rpc_exit_callback(nfc); + + widget_reset(nfc->widget); +} diff --git a/applications/rpc/rpc.c b/applications/rpc/rpc.c index c974a0e51..4832afe34 100644 --- a/applications/rpc/rpc.c +++ b/applications/rpc/rpc.c @@ -45,7 +45,7 @@ static RpcSystemCallbacks rpc_systems[] = { }, { .alloc = rpc_system_app_alloc, - .free = NULL, + .free = rpc_system_app_free, }, { .alloc = rpc_system_gui_alloc, diff --git a/applications/rpc/rpc_app.c b/applications/rpc/rpc_app.c index 728c20528..f6678c3b6 100644 --- a/applications/rpc/rpc_app.c +++ b/applications/rpc/rpc_app.c @@ -1,16 +1,39 @@ +#include "cmsis_os2.h" #include "flipper.pb.h" #include "furi/record.h" #include "rpc_i.h" #include #include +#include "rpc_app.h" #define TAG "RpcSystemApp" +#define APP_BUTTON_TIMEOUT 1000 + +struct RpcAppSystem { + RpcSession* session; + RpcAppSystemCallback app_callback; + void* app_context; + osTimerId_t timer; +}; + +static void rpc_system_app_timer_callback(void* context) { + furi_assert(context); + RpcAppSystem* rpc_app = context; + + if(rpc_app->app_callback) { + rpc_app->app_callback(RpcAppEventButtonRelease, NULL, rpc_app->app_context); + } +} static void rpc_system_app_start_process(const PB_Main* request, void* context) { furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_app_start_request_tag); - RpcSession* session = (RpcSession*)context; + RpcAppSystem* rpc_app = context; + RpcSession* session = rpc_app->session; furi_assert(session); + char args_temp[16]; FURI_LOG_D(TAG, "Start"); @@ -20,6 +43,11 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) const char* app_name = request->content.app_start_request.name; if(app_name) { const char* app_args = request->content.app_start_request.args; + if(strcmp(app_args, "RPC") == 0) { + // If app is being started in RPC mode - pass RPC context via args string + snprintf(args_temp, 16, "RPC %08lX", (uint32_t)rpc_app); + app_args = args_temp; + } LoaderStatus status = loader_start(loader, app_name, app_args); if(status == LoaderStatusErrorAppStarted) { result = PB_CommandStatus_ERROR_APP_SYSTEM_LOCKED; @@ -43,8 +71,11 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) static void rpc_system_app_lock_status_process(const PB_Main* request, void* context) { furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_app_lock_status_request_tag); - RpcSession* session = (RpcSession*)context; + RpcAppSystem* rpc_app = context; + RpcSession* session = rpc_app->session; furi_assert(session); FURI_LOG_D(TAG, "LockStatus"); @@ -66,13 +97,123 @@ static void rpc_system_app_lock_status_process(const PB_Main* request, void* con pb_release(&PB_Main_msg, &response); } +static void rpc_system_app_exit(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + + furi_assert(request->which_content == PB_Main_app_exit_request_tag); + RpcAppSystem* rpc_app = context; + RpcSession* session = rpc_app->session; + furi_assert(session); + + PB_CommandStatus status; + + if(rpc_app->app_callback) { + if(rpc_app->app_callback(RpcAppEventAppExit, NULL, rpc_app->app_context)) { + status = PB_CommandStatus_OK; + osTimerStop(rpc_app->timer); + } else { + status = PB_CommandStatus_ERROR_APP_CMD_ERROR; + } + } else { + status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; + } + + rpc_send_and_release_empty(session, request->command_id, status); +} + +static void rpc_system_app_load_file(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + + furi_assert(request->which_content == PB_Main_app_load_file_request_tag); + RpcAppSystem* rpc_app = context; + RpcSession* session = rpc_app->session; + furi_assert(session); + + PB_CommandStatus status; + if(rpc_app->app_callback) { + const char* file_path = request->content.app_load_file_request.path; + if(rpc_app->app_callback(RpcAppEventLoadFile, file_path, rpc_app->app_context)) { + status = PB_CommandStatus_OK; + } else { + status = PB_CommandStatus_ERROR_APP_CMD_ERROR; + } + } else { + status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; + } + + rpc_send_and_release_empty(session, request->command_id, status); +} + +static void rpc_system_app_button_press(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + + furi_assert(request->which_content == PB_Main_app_button_press_request_tag); + RpcAppSystem* rpc_app = context; + RpcSession* session = rpc_app->session; + furi_assert(session); + + PB_CommandStatus status; + if(rpc_app->app_callback) { + const char* args = request->content.app_button_press_request.args; + if(rpc_app->app_callback(RpcAppEventButtonPress, args, rpc_app->app_context)) { + status = PB_CommandStatus_OK; + osTimerStart(rpc_app->timer, APP_BUTTON_TIMEOUT); + } else { + status = PB_CommandStatus_ERROR_APP_CMD_ERROR; + } + } else { + status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; + } + + rpc_send_and_release_empty(session, request->command_id, status); +} + +static void rpc_system_app_button_release(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_app_button_release_request_tag); + furi_assert(context); + + RpcAppSystem* rpc_app = context; + RpcSession* session = rpc_app->session; + furi_assert(session); + + PB_CommandStatus status; + if(rpc_app->app_callback) { + if(rpc_app->app_callback(RpcAppEventButtonRelease, NULL, rpc_app->app_context)) { + status = PB_CommandStatus_OK; + osTimerStop(rpc_app->timer); + } else { + status = PB_CommandStatus_ERROR_APP_CMD_ERROR; + } + } else { + status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; + } + + rpc_send_and_release_empty(session, request->command_id, status); +} + +void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx) { + furi_assert(rpc_app); + + rpc_app->app_callback = callback; + rpc_app->app_context = ctx; +} + void* rpc_system_app_alloc(RpcSession* session) { furi_assert(session); + RpcAppSystem* rpc_app = malloc(sizeof(RpcAppSystem)); + rpc_app->session = session; + + rpc_app->timer = osTimerNew(rpc_system_app_timer_callback, osTimerOnce, rpc_app, NULL); + RpcHandler rpc_handler = { .message_handler = NULL, .decode_submessage = NULL, - .context = session, + .context = rpc_app, }; rpc_handler.message_handler = rpc_system_app_start_process; @@ -81,5 +222,31 @@ void* rpc_system_app_alloc(RpcSession* session) { rpc_handler.message_handler = rpc_system_app_lock_status_process; rpc_add_handler(session, PB_Main_app_lock_status_request_tag, &rpc_handler); - return NULL; + rpc_handler.message_handler = rpc_system_app_exit; + rpc_add_handler(session, PB_Main_app_exit_request_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_app_load_file; + rpc_add_handler(session, PB_Main_app_load_file_request_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_app_button_press; + rpc_add_handler(session, PB_Main_app_button_press_request_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_app_button_release; + rpc_add_handler(session, PB_Main_app_button_release_request_tag, &rpc_handler); + + return rpc_app; +} + +void rpc_system_app_free(void* context) { + RpcAppSystem* rpc_app = context; + RpcSession* session = rpc_app->session; + furi_assert(session); + + osTimerDelete(rpc_app->timer); + + if(rpc_app->app_callback) { + rpc_app->app_callback(RpcAppEventSessionClose, NULL, rpc_app->app_context); + } + + free(rpc_app); } diff --git a/applications/rpc/rpc_app.h b/applications/rpc/rpc_app.h new file mode 100644 index 000000000..396eef1a3 --- /dev/null +++ b/applications/rpc/rpc_app.h @@ -0,0 +1,24 @@ +#pragma once +#include "rpc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + RpcAppEventSessionClose, + RpcAppEventAppExit, + RpcAppEventLoadFile, + RpcAppEventButtonPress, + RpcAppEventButtonRelease, +} RpcAppSystemEvent; + +typedef bool (*RpcAppSystemCallback)(RpcAppSystemEvent event, const char* arg, void* context); + +typedef struct RpcAppSystem RpcAppSystem; + +void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx); + +#ifdef __cplusplus +} +#endif diff --git a/applications/rpc/rpc_i.h b/applications/rpc/rpc_i.h index f84cc991e..d0c6cf766 100644 --- a/applications/rpc/rpc_i.h +++ b/applications/rpc/rpc_i.h @@ -29,6 +29,7 @@ void* rpc_system_system_alloc(RpcSession* session); void* rpc_system_storage_alloc(RpcSession* session); void rpc_system_storage_free(void* ctx); void* rpc_system_app_alloc(RpcSession* session); +void rpc_system_app_free(void* ctx); void* rpc_system_gui_alloc(RpcSession* session); void rpc_system_gui_free(void* ctx); diff --git a/applications/subghz/scenes/subghz_scene_config.h b/applications/subghz/scenes/subghz_scene_config.h index 1cb217adc..fd1271c8c 100644 --- a/applications/subghz/scenes/subghz_scene_config.h +++ b/applications/subghz/scenes/subghz_scene_config.h @@ -22,3 +22,4 @@ ADD_SCENE(subghz, read_raw, ReadRAW) ADD_SCENE(subghz, more_raw, MoreRAW) ADD_SCENE(subghz, delete_raw, DeleteRAW) ADD_SCENE(subghz, need_saving, NeedSaving) +ADD_SCENE(subghz, rpc, Rpc) diff --git a/applications/subghz/scenes/subghz_scene_rpc.c b/applications/subghz/scenes/subghz_scene_rpc.c new file mode 100644 index 000000000..c7573fda1 --- /dev/null +++ b/applications/subghz/scenes/subghz_scene_rpc.c @@ -0,0 +1,34 @@ +#include "../subghz_i.h" + +void subghz_scene_rpc_on_enter(void* context) { + SubGhz* subghz = context; + Widget* widget = subghz->widget; + + widget_add_text_box_element( + widget, 0, 0, 128, 28, AlignCenter, AlignCenter, "RPC mode", false); + + notification_message(subghz->notifications, &sequence_display_backlight_on); + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdWidget); +} + +bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == SubGhzCustomEventSceneExit) { + view_dispatcher_stop(subghz->view_dispatcher); + } + } + return consumed; +} + +void subghz_scene_rpc_on_exit(void* context) { + SubGhz* subghz = context; + + //subghz_rpc_exit_callback(subghz); + + widget_reset(subghz->widget); +} diff --git a/applications/subghz/subghz.c b/applications/subghz/subghz.c index ade7a844e..984ce4728 100644 --- a/applications/subghz/subghz.c +++ b/applications/subghz/subghz.c @@ -23,6 +23,54 @@ void subghz_tick_event_callback(void* context) { scene_manager_handle_tick_event(subghz->scene_manager); } +static bool subghz_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { + furi_assert(context); + SubGhz* subghz = context; + + if(!subghz->rpc_ctx) { + return false; + } + + bool result = false; + + if(event == RpcAppEventSessionClose) { + rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); + subghz->rpc_ctx = NULL; + view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); + if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { + subghz_tx_stop(subghz); + subghz_sleep(subghz); + } + result = true; + } else if(event == RpcAppEventAppExit) { + view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); + if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { + subghz_tx_stop(subghz); + subghz_sleep(subghz); + } + result = true; + } else if(event == RpcAppEventLoadFile) { + if(arg) { + if(subghz_key_load(subghz, arg, false)) { + string_set_str(subghz->file_path, arg); + result = true; + } + } + } else if(event == RpcAppEventButtonPress) { + if(subghz->txrx->txrx_state == SubGhzTxRxStateSleep) { + result = subghz_tx_start(subghz, subghz->txrx->fff_data); + } + } else if(event == RpcAppEventButtonRelease) { + if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { + subghz_tx_stop(subghz); + subghz_sleep(subghz); + result = true; + } + } + + return result; +} + SubGhz* subghz_alloc() { SubGhz* subghz = malloc(sizeof(SubGhz)); @@ -168,6 +216,11 @@ SubGhz* subghz_alloc() { void subghz_free(SubGhz* subghz) { furi_assert(subghz); + if(subghz->rpc_ctx) { + rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); + subghz->rpc_ctx = NULL; + } + // Packet Test view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket); subghz_test_packet_free(subghz->subghz_test_packet); @@ -265,7 +318,12 @@ int32_t subghz_app(void* p) { subghz->txrx->environment, "/ext/subghz/assets/keeloq_mfcodes_user"); // Check argument and run corresponding scene if(p) { - if(subghz_key_load(subghz, p)) { + uint32_t rpc_ctx = 0; + if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { + subghz->rpc_ctx = (void*)rpc_ctx; + rpc_system_app_set_callback(subghz->rpc_ctx, subghz_rpc_command_callback, subghz); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneRpc); + } else if(subghz_key_load(subghz, p, true)) { string_set_str(subghz->file_path, p); if((!strcmp(subghz->txrx->decoder_result->protocol->name, "RAW"))) { diff --git a/applications/subghz/subghz_i.c b/applications/subghz/subghz_i.c index 33ceb9bbb..0593cc6f7 100644 --- a/applications/subghz/subghz_i.c +++ b/applications/subghz/subghz_i.c @@ -218,7 +218,7 @@ void subghz_dialog_message_show_only_rx(SubGhz* subghz) { dialog_message_free(message); } -bool subghz_key_load(SubGhz* subghz, const char* file_path) { +bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog) { furi_assert(subghz); furi_assert(file_path); @@ -308,11 +308,15 @@ bool subghz_key_load(SubGhz* subghz, const char* file_path) { switch(load_key_state) { case SubGhzLoadKeyStateParseErr: - dialog_message_show_storage_error(subghz->dialogs, "Cannot parse\nfile"); + if(show_dialog) { + dialog_message_show_storage_error(subghz->dialogs, "Cannot parse\nfile"); + } return false; case SubGhzLoadKeyStateOnlyRx: - subghz_dialog_message_show_only_rx(subghz); + if(show_dialog) { + subghz_dialog_message_show_only_rx(subghz); + } return false; case SubGhzLoadKeyStateOK: @@ -427,7 +431,7 @@ bool subghz_load_protocol_from_file(SubGhz* subghz) { true); if(res) { - res = subghz_key_load(subghz, string_get_cstr(subghz->file_path)); + res = subghz_key_load(subghz, string_get_cstr(subghz->file_path), true); } string_clear(file_path); diff --git a/applications/subghz/subghz_i.h b/applications/subghz/subghz_i.h index d75e92a77..0a4bcf0b4 100644 --- a/applications/subghz/subghz_i.h +++ b/applications/subghz/subghz_i.h @@ -36,6 +36,8 @@ #include #include +#include "rpc/rpc_app.h" + #define SUBGHZ_MAX_LEN_NAME 64 struct SubGhzTxRx { @@ -91,6 +93,8 @@ struct SubGhz { string_t error_str; SubGhzSetting* setting; SubGhzLock lock; + + void* rpc_ctx; }; bool subghz_set_preset(SubGhz* subghz, const char* preset); @@ -102,7 +106,7 @@ void subghz_sleep(SubGhz* subghz); bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); void subghz_tx_stop(SubGhz* subghz); void subghz_dialog_message_show_only_rx(SubGhz* subghz); -bool subghz_key_load(SubGhz* subghz, const char* file_path); +bool subghz_key_load(SubGhz* subghz, const char* file_path, bool show_dialog); bool subghz_get_next_name_file(SubGhz* subghz, uint8_t max_len); bool subghz_save_protocol_to_file( SubGhz* subghz, diff --git a/assets/protobuf b/assets/protobuf index ffa62429f..e3d9cdb66 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit ffa62429f3c678537e0e883a3a8c3ae5f1398ed4 +Subproject commit e3d9cdb66ce789f84f6f8e0bdd6d022187964425 From b95cd2df143d20a5dc5a236e83b455a4c7d2d9b1 Mon Sep 17 00:00:00 2001 From: hedger Date: Mon, 4 Jul 2022 16:36:12 +0300 Subject: [PATCH 2/7] [FL-2578] Updater fixes related to /int handling (#1359) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Updater fixes related to /int handling updater: performing factory reset on update, checking for LFS free space before updating, fixed improper error handling on backup/restore operations, rebalanced update stage weights for better progress visuals scripts: added CLI output validation for selfupdate.py storage: added pointer validation in storage_int_common_fs_info desktop: fixed crash on rendering invalid slideshows * Typo fix * rpc: Updated protobuf to 0.9 * rpc: removed updater status conversion Co-authored-by: あく --- applications/desktop/helpers/slideshow.c | 12 +++++-- applications/desktop/helpers/slideshow.h | 1 + .../desktop/views/desktop_view_slideshow.c | 4 ++- applications/rpc/rpc_system.c | 4 --- applications/storage/storages/storage_int.c | 8 +++-- applications/updater/util/update_task.c | 15 ++++---- applications/updater/util/update_task_i.h | 2 ++ .../updater/util/update_task_worker_backup.c | 35 ++++++++++--------- .../updater/util/update_task_worker_flasher.c | 2 ++ assets/protobuf | 2 +- lib/update_util/update_operation.c | 12 ++++++- lib/update_util/update_operation.h | 2 ++ scripts/selfupdate.py | 15 ++++++-- 13 files changed, 76 insertions(+), 38 deletions(-) diff --git a/applications/desktop/helpers/slideshow.c b/applications/desktop/helpers/slideshow.c index 37f66cf35..593b2854a 100644 --- a/applications/desktop/helpers/slideshow.c +++ b/applications/desktop/helpers/slideshow.c @@ -12,6 +12,7 @@ struct Slideshow { Icon icon; uint32_t current_frame; + bool loaded; }; #pragma pack(push, 1) @@ -34,6 +35,7 @@ _Static_assert(sizeof(SlideshowFrameHeader) == 2, "Incorrect SlideshowFrameHeade Slideshow* slideshow_alloc() { Slideshow* ret = malloc(sizeof(Slideshow)); + ret->loaded = false; return ret; } @@ -52,7 +54,7 @@ void slideshow_free(Slideshow* slideshow) { bool slideshow_load(Slideshow* slideshow, const char* fspath) { Storage* storage = furi_record_open("storage"); File* slideshow_file = storage_file_alloc(storage); - bool load_success = false; + slideshow->loaded = false; do { if(!storage_file_open(slideshow_file, fspath, FSAM_READ, FSOM_OPEN_EXISTING)) { break; @@ -80,12 +82,16 @@ bool slideshow_load(Slideshow* slideshow, const char* fspath) { frame_header.size) { break; } - load_success = (frame_idx + 1) == header.frame_count; + slideshow->loaded = (frame_idx + 1) == header.frame_count; } } while(false); storage_file_free(slideshow_file); furi_record_close("storage"); - return load_success; + return slideshow->loaded; +} + +bool slideshow_is_loaded(Slideshow* slideshow) { + return slideshow->loaded; } bool slideshow_advance(Slideshow* slideshow) { diff --git a/applications/desktop/helpers/slideshow.h b/applications/desktop/helpers/slideshow.h index db19779a3..eeaac0e8b 100644 --- a/applications/desktop/helpers/slideshow.h +++ b/applications/desktop/helpers/slideshow.h @@ -8,6 +8,7 @@ Slideshow* slideshow_alloc(); void slideshow_free(Slideshow* slideshow); bool slideshow_load(Slideshow* slideshow, const char* fspath); +bool slideshow_is_loaded(Slideshow* slideshow); void slideshow_goback(Slideshow* slideshow); bool slideshow_advance(Slideshow* slideshow); void slideshow_draw(Slideshow* slideshow, Canvas* canvas, uint8_t x, uint8_t y); diff --git a/applications/desktop/views/desktop_view_slideshow.c b/applications/desktop/views/desktop_view_slideshow.c index 97f779b58..cd22b39de 100644 --- a/applications/desktop/views/desktop_view_slideshow.c +++ b/applications/desktop/views/desktop_view_slideshow.c @@ -21,7 +21,9 @@ static void desktop_view_slideshow_draw(Canvas* canvas, void* model) { DesktopSlideshowViewModel* m = model; canvas_clear(canvas); - slideshow_draw(m->slideshow, canvas, 0, 0); + if(slideshow_is_loaded(m->slideshow)) { + slideshow_draw(m->slideshow, canvas, 0, 0); + } } static bool desktop_view_slideshow_input(InputEvent* event, void* context) { diff --git a/applications/rpc/rpc_system.c b/applications/rpc/rpc_system.c index 45ae2e5a0..350602fd8 100644 --- a/applications/rpc/rpc_system.c +++ b/applications/rpc/rpc_system.c @@ -277,10 +277,6 @@ static void rpc_system_system_update_request_process(const PB_Main* request, voi UpdatePrepareResult update_prepare_result = update_operation_prepare(request->content.system_update_request.update_manifest); - /* 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)); response->command_id = request->command_id; diff --git a/applications/storage/storages/storage_int.c b/applications/storage/storages/storage_int.c index 291fb426b..9b1ec700f 100644 --- a/applications/storage/storages/storage_int.c +++ b/applications/storage/storages/storage_int.c @@ -655,11 +655,13 @@ static FS_Error storage_int_common_fs_info( lfs_t* lfs = lfs_get_from_storage(storage); LFSData* lfs_data = lfs_data_get_from_storage(storage); - *total_space = lfs_data->config.block_size * lfs_data->config.block_count; + if(total_space) { + *total_space = lfs_data->config.block_size * lfs_data->config.block_count; + } lfs_ssize_t result = lfs_fs_size(lfs); - if(result >= 0) { - *free_space = *total_space - (result * lfs_data->config.block_size); + if(free_space && (result >= 0)) { + *free_space = (lfs_data->config.block_count - result) * lfs_data->config.block_size; } return storage_int_parse_error(result); diff --git a/applications/updater/util/update_task.c b/applications/updater/util/update_task.c index 316fe6802..47bb7ae5a 100644 --- a/applications/updater/util/update_task.c +++ b/applications/updater/util/update_task.c @@ -44,17 +44,17 @@ static const UpdateTaskStageGroupMap update_task_stage_progress[] = { [UpdateTaskStageReadManifest] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 5), [UpdateTaskStageLfsBackup] = STAGE_DEF(UpdateTaskStageGroupPreUpdate, 15), - [UpdateTaskStageRadioImageValidate] = STAGE_DEF(UpdateTaskStageGroupRadio, 10), - [UpdateTaskStageRadioErase] = STAGE_DEF(UpdateTaskStageGroupRadio, 50), - [UpdateTaskStageRadioWrite] = STAGE_DEF(UpdateTaskStageGroupRadio, 90), - [UpdateTaskStageRadioInstall] = STAGE_DEF(UpdateTaskStageGroupRadio, 15), - [UpdateTaskStageRadioBusy] = STAGE_DEF(UpdateTaskStageGroupRadio, 60), + [UpdateTaskStageRadioImageValidate] = STAGE_DEF(UpdateTaskStageGroupRadio, 15), + [UpdateTaskStageRadioErase] = STAGE_DEF(UpdateTaskStageGroupRadio, 60), + [UpdateTaskStageRadioWrite] = STAGE_DEF(UpdateTaskStageGroupRadio, 80), + [UpdateTaskStageRadioInstall] = STAGE_DEF(UpdateTaskStageGroupRadio, 60), + [UpdateTaskStageRadioBusy] = STAGE_DEF(UpdateTaskStageGroupRadio, 80), [UpdateTaskStageOBValidation] = STAGE_DEF(UpdateTaskStageGroupOptionBytes, 10), - [UpdateTaskStageValidateDFUImage] = STAGE_DEF(UpdateTaskStageGroupFirmware, 100), + [UpdateTaskStageValidateDFUImage] = STAGE_DEF(UpdateTaskStageGroupFirmware, 50), [UpdateTaskStageFlashWrite] = STAGE_DEF(UpdateTaskStageGroupFirmware, 200), - [UpdateTaskStageFlashValidate] = STAGE_DEF(UpdateTaskStageGroupFirmware, 50), + [UpdateTaskStageFlashValidate] = STAGE_DEF(UpdateTaskStageGroupFirmware, 30), [UpdateTaskStageLfsRestore] = STAGE_DEF(UpdateTaskStageGroupPostUpdate, 30), @@ -214,6 +214,7 @@ UpdateTask* update_task_alloc() { update_task->storage = furi_record_open("storage"); update_task->file = storage_file_alloc(update_task->storage); update_task->status_change_cb = NULL; + update_task->boot_mode = furi_hal_rtc_get_boot_mode(); string_init(update_task->update_path); FuriThread* thread = update_task->thread = furi_thread_alloc(); diff --git a/applications/updater/util/update_task_i.h b/applications/updater/util/update_task_i.h index fb6b4ac11..95c63f644 100644 --- a/applications/updater/util/update_task_i.h +++ b/applications/updater/util/update_task_i.h @@ -1,6 +1,7 @@ #pragma once #include +#include #define UPDATE_TASK_NOERR 0 #define UPDATE_TASK_FAILED -1 @@ -14,6 +15,7 @@ typedef struct UpdateTask { File* file; updateProgressCb status_change_cb; void* status_change_cb_state; + FuriHalRtcBootMode boot_mode; } UpdateTask; void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, uint8_t progress); diff --git a/applications/updater/util/update_task_worker_backup.c b/applications/updater/util/update_task_worker_backup.c index f1ce52817..1e8c2f09c 100644 --- a/applications/updater/util/update_task_worker_backup.c +++ b/applications/updater/util/update_task_worker_backup.c @@ -67,7 +67,6 @@ static bool update_task_post_update(UpdateTask* update_task) { string_get_cstr(update_task->update_path), LFS_BACKUP_DEFAULT_FILENAME, file_path); update_task_set_progress(update_task, UpdateTaskStageLfsRestore, 0); - update_operation_disarm(); CHECK_RESULT(lfs_backup_unpack(update_task->storage, string_get_cstr(file_path))); @@ -117,28 +116,32 @@ static bool update_task_post_update(UpdateTask* update_task) { int32_t update_task_worker_backup_restore(void* context) { furi_assert(context); UpdateTask* update_task = context; - bool success = false; - FuriHalRtcBootMode boot_mode = furi_hal_rtc_get_boot_mode(); + FuriHalRtcBootMode boot_mode = update_task->boot_mode; if((boot_mode != FuriHalRtcBootModePreUpdate) && (boot_mode != FuriHalRtcBootModePostUpdate)) { - /* no idea how we got here. Clear to normal boot */ - update_operation_disarm(); + /* no idea how we got here. Do nothing */ return UPDATE_TASK_NOERR; } - if(!update_task_parse_manifest(update_task)) { - return UPDATE_TASK_FAILED; - } + bool success = false; + do { + if(!update_task_parse_manifest(update_task)) { + break; + } - /* Waiting for BT service to 'start', so we don't race for boot mode flag */ - furi_record_open("bt"); - furi_record_close("bt"); + /* Waiting for BT service to 'start', so we don't race for boot mode flag */ + furi_record_open("bt"); + furi_record_close("bt"); - if(boot_mode == FuriHalRtcBootModePreUpdate) { - success = update_task_pre_update(update_task); - } else if(boot_mode == FuriHalRtcBootModePostUpdate) { - success = update_task_post_update(update_task); - } + if(boot_mode == FuriHalRtcBootModePreUpdate) { + success = update_task_pre_update(update_task); + } else if(boot_mode == FuriHalRtcBootModePostUpdate) { + success = update_task_post_update(update_task); + if(success) { + update_operation_disarm(); + } + } + } while(false); if(!success) { update_task_set_progress(update_task, UpdateTaskStageError, 0); diff --git a/applications/updater/util/update_task_worker_flasher.c b/applications/updater/util/update_task_worker_flasher.c index 1f22e2f72..d02858bc6 100644 --- a/applications/updater/util/update_task_worker_flasher.c +++ b/applications/updater/util/update_task_worker_flasher.c @@ -340,6 +340,8 @@ int32_t update_task_worker_flash_writer(void* context) { } furi_hal_rtc_set_boot_mode(FuriHalRtcBootModePostUpdate); + // Format LFS before restoring backup on next boot + furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); update_task_set_progress(update_task, UpdateTaskStageCompleted, 100); success = true; diff --git a/assets/protobuf b/assets/protobuf index e3d9cdb66..6c1b8ae66 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit e3d9cdb66ce789f84f6f8e0bdd6d022187964425 +Subproject commit 6c1b8ae66a85bcd7e79e993a0b5573c38c302db5 diff --git a/lib/update_util/update_operation.c b/lib/update_util/update_operation.c index 142dcaaaa..9082d2625 100644 --- a/lib/update_util/update_operation.c +++ b/lib/update_util/update_operation.c @@ -12,6 +12,8 @@ #define UPDATE_ROOT_DIR "/ext" UPDATE_DIR_DEFAULT_REL_PATH #define UPDATE_PREFIX "/ext" UPDATE_DIR_DEFAULT_REL_PATH "/" #define UPDATE_SUFFIX "/" UPDATE_MANIFEST_DEFAULT_NAME +/* Need at least 4 free LFS pages before update */ +#define UPDATE_MIN_INT_FREE_SPACE 4 * 4 * 1024 static const char* update_prepare_result_descr[] = { [UpdatePrepareResultOK] = "OK", @@ -22,6 +24,7 @@ static const char* update_prepare_result_descr[] = { [UpdatePrepareResultStageIntegrityError] = "Corrupted Stage2 loader", [UpdatePrepareResultManifestPointerError] = "Failed to create update pointer file", [UpdatePrepareResultOutdatedManifestVersion] = "Update package is too old", + [UpdatePrepareResultIntFull] = "Need more free space in internal storage", }; const char* update_operation_describe_preparation_result(const UpdatePrepareResult value) { @@ -133,15 +136,22 @@ static bool update_operation_persist_manifest_path(Storage* storage, const char* } UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { - UpdatePrepareResult result = UpdatePrepareResultManifestFolderNotFound; + UpdatePrepareResult result = UpdatePrepareResultIntFull; Storage* storage = furi_record_open("storage"); UpdateManifest* manifest = update_manifest_alloc(); File* file = storage_file_alloc(storage); + uint64_t free_int_space; string_t stage_path; string_init(stage_path); do { + if((storage_common_fs_info(storage, "/int", NULL, &free_int_space) != FSE_OK) || + (free_int_space < UPDATE_MIN_INT_FREE_SPACE)) { + break; + } + if(storage_common_stat(storage, manifest_file_path, NULL) != FSE_OK) { + result = UpdatePrepareResultManifestFolderNotFound; break; } diff --git a/lib/update_util/update_operation.h b/lib/update_util/update_operation.h index 39d580283..056520e11 100644 --- a/lib/update_util/update_operation.h +++ b/lib/update_util/update_operation.h @@ -32,6 +32,8 @@ typedef enum { UpdatePrepareResultManifestPointerError, UpdatePrepareResultTargetMismatch, UpdatePrepareResultOutdatedManifestVersion, + UpdatePrepareResultIntFull, + UpdatePrepareResultUnspecifiedError, } UpdatePrepareResult; const char* update_operation_describe_preparation_result(const UpdatePrepareResult value); diff --git a/scripts/selfupdate.py b/scripts/selfupdate.py index a22ca1f59..6d7057474 100644 --- a/scripts/selfupdate.py +++ b/scripts/selfupdate.py @@ -76,12 +76,15 @@ class Main(App): manifest_name, pkg_name = manifest_path.parts[-1], manifest_path.parts[-2] pkg_dir_name = self.args.pkg_dir_name or pkg_name - flipper_update_path = f"/ext/update/{pkg_dir_name}" + update_root = "/ext/update" + flipper_update_path = f"{update_root}/{pkg_dir_name}" self.logger.info(f'Installing "{pkg_name}" from {flipper_update_path}') # if not os.path.exists(self.args.manifest_path): # self.logger.error("Error: package not found") - if not self.mkdir_on_storage(storage, flipper_update_path): + if not self.mkdir_on_storage( + storage, update_root + ) or not self.mkdir_on_storage(storage, flipper_update_path): self.logger.error(f"Error: cannot create {storage.last_error}") return -2 @@ -99,6 +102,14 @@ class Main(App): storage.send_and_wait_eol( f"update install {flipper_update_path}/{manifest_name}\r" ) + result = storage.read.until(storage.CLI_EOL) + if not b"Verifying" in result: + self.logger.error(f"Unexpected response: {result.decode('ascii')}") + return -4 + result = storage.read.until(storage.CLI_EOL) + if not result.startswith(b"OK"): + self.logger.error(result.decode("ascii")) + return -5 break return 0 finally: From c495677eb5c202280c7a904594479e4dc8879ed5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Mon, 4 Jul 2022 17:26:02 +0300 Subject: [PATCH 3/7] FuriHal: RTC recovery routine and ext3v3 enabled on start (#1357) * FuriHal: leave ext3v3 enabled on start * FuriHal: RTC recovery routine, cleanup resources Co-authored-by: SG --- firmware/targets/f7/furi_hal/furi_hal_power.c | 9 +- .../targets/f7/furi_hal/furi_hal_resources.c | 8 +- .../targets/f7/furi_hal/furi_hal_resources.h | 3 - firmware/targets/f7/furi_hal/furi_hal_rtc.c | 108 ++++++++++++------ 4 files changed, 88 insertions(+), 40 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c index 52bb7bcfe..28e6cb051 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_power.c +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -299,6 +300,10 @@ void furi_hal_power_shutdown() { } void furi_hal_power_off() { + // Crutch: shutting down with ext 3V3 off is causing LSE to stop + furi_hal_power_enable_external_3_3v(); + furi_hal_delay_us(1000); + // Send poweroff to charger furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); bq25896_poweroff(&furi_hal_i2c_handle_power); furi_hal_i2c_release(&furi_hal_i2c_handle_power); @@ -482,11 +487,11 @@ void furi_hal_power_dump_state() { } void furi_hal_power_enable_external_3_3v() { - LL_GPIO_SetOutputPin(PERIPH_POWER_GPIO_Port, PERIPH_POWER_Pin); + furi_hal_gpio_write(&periph_power, 1); } void furi_hal_power_disable_external_3_3v() { - LL_GPIO_ResetOutputPin(PERIPH_POWER_GPIO_Port, PERIPH_POWER_Pin); + furi_hal_gpio_write(&periph_power, 0); } void furi_hal_power_suppress_charge_enter() { diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index 21fac8346..c1238212a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -58,7 +58,7 @@ const GpioPin gpio_i2c_power_scl = {.port = GPIOA, .pin = LL_GPIO_PIN_9}; const GpioPin gpio_speaker = {.port = GPIOB, .pin = LL_GPIO_PIN_8}; -const GpioPin periph_power = {.port = PERIPH_POWER_GPIO_Port, .pin = PERIPH_POWER_Pin}; +const GpioPin periph_power = {.port = GPIOA, .pin = LL_GPIO_PIN_3}; const GpioPin gpio_usb_dm = {.port = GPIOA, .pin = LL_GPIO_PIN_11}; const GpioPin gpio_usb_dp = {.port = GPIOA, .pin = LL_GPIO_PIN_12}; @@ -77,6 +77,10 @@ const size_t input_pins_count = sizeof(input_pins) / sizeof(InputPin); void furi_hal_resources_init_early() { furi_hal_gpio_init(&gpio_button_left, GpioModeInput, GpioPullUp, GpioSpeedLow); + // SD Card stepdown control + furi_hal_gpio_write(&periph_power, 1); + furi_hal_gpio_init(&periph_power, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); + // Display pins furi_hal_gpio_write(&gpio_display_rst_n, 1); furi_hal_gpio_init_simple(&gpio_display_rst_n, GpioModeOutputPushPull); @@ -142,8 +146,6 @@ void furi_hal_resources_init() { furi_hal_gpio_init(&gpio_rf_sw_0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&periph_power, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); - NVIC_SetPriority(EXTI0_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); NVIC_EnableIRQ(EXTI0_IRQn); diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h index 820760a16..d16c567e6 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.h @@ -147,9 +147,6 @@ extern const GpioPin gpio_usb_dp; #define PC3_GPIO_Port GPIOC #define PC3_Pin LL_GPIO_PIN_3 -#define PERIPH_POWER_GPIO_Port GPIOA -#define PERIPH_POWER_Pin LL_GPIO_PIN_3 - #define QUARTZ_32MHZ_IN_GPIO_Port GPIOC #define QUARTZ_32MHZ_IN_Pin LL_GPIO_PIN_14 #define QUARTZ_32MHZ_OUT_GPIO_Port GPIOC diff --git a/firmware/targets/f7/furi_hal/furi_hal_rtc.c b/firmware/targets/f7/furi_hal/furi_hal_rtc.c index df410a9f4..24dad38fa 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rtc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rtc.c @@ -12,7 +12,9 @@ #define TAG "FuriHalRtc" -#define RTC_CLOCK_IS_READY() (LL_RCC_LSE_IsReady() && LL_RCC_LSI1_IsReady()) +#define FURI_HAL_RTC_LSE_STARTUP_TIME 300 + +#define FURI_HAL_RTC_CLOCK_IS_READY() (LL_RCC_LSE_IsReady() && LL_RCC_LSI1_IsReady()) #define FURI_HAL_RTC_HEADER_MAGIC 0x10F1 #define FURI_HAL_RTC_HEADER_VERSION 0 @@ -47,33 +49,83 @@ static const uint8_t furi_hal_rtc_days_per_month[][FURI_HAL_RTC_MONTHS_COUNT] = static const uint16_t furi_hal_rtc_days_per_year[] = {365, 366}; -void furi_hal_rtc_init_early() { - // LSE and RTC +static void furi_hal_rtc_reset() { + LL_RCC_ForceBackupDomainReset(); + LL_RCC_ReleaseBackupDomainReset(); +} + +static bool furi_hal_rtc_start_clock_and_switch() { + // Clock operation require access to Backup Domain LL_PWR_EnableBkUpAccess(); - if(!RTC_CLOCK_IS_READY()) { - LL_RCC_LSI1_Enable(); - // Try to start LSE normal way - LL_RCC_LSE_SetDriveCapability(LL_RCC_LSEDRIVE_HIGH); - LL_RCC_LSE_Enable(); - uint32_t c = 0; - while(!RTC_CLOCK_IS_READY() && c < 200) { - LL_mDelay(10); - c++; - } - // Plan B: reset backup domain - if(!RTC_CLOCK_IS_READY()) { - furi_hal_light_sequence("rgb R.r.R.r.R"); - LL_RCC_ForceBackupDomainReset(); - LL_RCC_ReleaseBackupDomainReset(); - NVIC_SystemReset(); - } - // Set RTC domain clock to LSE - LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE); + + // Enable LSI and LSE + LL_RCC_LSI1_Enable(); + LL_RCC_LSE_SetDriveCapability(LL_RCC_LSEDRIVE_HIGH); + LL_RCC_LSE_Enable(); + + // Wait for LSI and LSE startup + uint32_t c = 0; + while(!FURI_HAL_RTC_CLOCK_IS_READY() && c < FURI_HAL_RTC_LSE_STARTUP_TIME) { + LL_mDelay(1); + c++; } - // Enable clocking - LL_RCC_EnableRTC(); + + if(FURI_HAL_RTC_CLOCK_IS_READY()) { + LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE); + LL_RCC_EnableRTC(); + return LL_RCC_GetRTCClockSource() == LL_RCC_RTC_CLKSOURCE_LSE; + } else { + return false; + } +} + +static void furi_hal_rtc_recover() { + FuriHalRtcDateTime datetime = {0}; + + // Handle fixable LSE failure + if(LL_RCC_LSE_IsCSSDetected()) { + furi_hal_light_sequence("rgb B"); + // Shutdown LSE and LSECSS + LL_RCC_LSE_DisableCSS(); + LL_RCC_LSE_Disable(); + } else { + furi_hal_light_sequence("rgb R"); + } + + // Temporary switch to LSI + LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSI); + if(LL_RCC_GetRTCClockSource() == LL_RCC_RTC_CLKSOURCE_LSI) { + // Get datetime before RTC Domain reset + furi_hal_rtc_get_datetime(&datetime); + } + + // Reset RTC Domain + furi_hal_rtc_reset(); + + // Start Clock + if(!furi_hal_rtc_start_clock_and_switch()) { + // Plan C: reset RTC and restart + furi_hal_light_sequence("rgb R.r.R.r.R.r"); + furi_hal_rtc_reset(); + NVIC_SystemReset(); + } + + // Set date if it valid + if(datetime.year != 0) { + furi_hal_rtc_set_datetime(&datetime); + } +} + +void furi_hal_rtc_init_early() { + // Enable RTCAPB clock LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_RTCAPB); + // Prepare clock + if(!furi_hal_rtc_start_clock_and_switch()) { + // Plan B: try to recover + furi_hal_rtc_recover(); + } + // Verify header register uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterHeader); FuriHalRtcHeader* data = (FuriHalRtcHeader*)&data_reg; @@ -98,14 +150,6 @@ void furi_hal_rtc_deinit_early() { } void furi_hal_rtc_init() { - if(LL_RCC_GetRTCClockSource() != LL_RCC_RTC_CLKSOURCE_LSE) { - LL_RCC_ForceBackupDomainReset(); - LL_RCC_ReleaseBackupDomainReset(); - LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE); - } - - LL_RCC_EnableRTC(); - LL_RTC_InitTypeDef RTC_InitStruct = {0}; RTC_InitStruct.HourFormat = LL_RTC_HOURFORMAT_24HOUR; RTC_InitStruct.AsynchPrescaler = 127; From 793501d62d6ac3ce4d5bca80b3988f06ce9fd6d6 Mon Sep 17 00:00:00 2001 From: Samuel Yvon Date: Mon, 4 Jul 2022 11:32:06 -0400 Subject: [PATCH 4/7] Add GPIO control through RPC (#1282) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add GPIO control through RPC * Assets: sync protobuf to 0.10 * Assets: update protobuf to fixed v10 Co-authored-by: あく --- applications/rpc/rpc.c | 5 +- applications/rpc/rpc_gpio.c | 221 ++++++++++++++++++++++++++++++++++++ applications/rpc/rpc_i.h | 2 + assets/compiled/gpio.pb.c | 33 ++++++ assets/compiled/gpio.pb.h | 183 +++++++++++++++++++++++++++++ assets/protobuf | 2 +- 6 files changed, 444 insertions(+), 2 deletions(-) create mode 100644 applications/rpc/rpc_gpio.c create mode 100644 assets/compiled/gpio.pb.c create mode 100644 assets/compiled/gpio.pb.h diff --git a/applications/rpc/rpc.c b/applications/rpc/rpc.c index 4832afe34..ce063b8e4 100644 --- a/applications/rpc/rpc.c +++ b/applications/rpc/rpc.c @@ -51,7 +51,10 @@ static RpcSystemCallbacks rpc_systems[] = { .alloc = rpc_system_gui_alloc, .free = rpc_system_gui_free, }, -}; + { + .alloc = rpc_system_gpio_alloc, + .free = NULL, + }}; struct RpcSession { Rpc* rpc; diff --git a/applications/rpc/rpc_gpio.c b/applications/rpc/rpc_gpio.c new file mode 100644 index 000000000..614e775a1 --- /dev/null +++ b/applications/rpc/rpc_gpio.c @@ -0,0 +1,221 @@ +#include "flipper.pb.h" +#include "rpc_i.h" +#include "gpio.pb.h" +#include +#include + +static const GpioPin* rpc_pin_to_hal_pin(PB_Gpio_GpioPin rpc_pin) { + switch(rpc_pin) { + case PB_Gpio_GpioPin_PC0: + return &gpio_ext_pc0; + case PB_Gpio_GpioPin_PC1: + return &gpio_ext_pc1; + case PB_Gpio_GpioPin_PC3: + return &gpio_ext_pc3; + case PB_Gpio_GpioPin_PB2: + return &gpio_ext_pb2; + case PB_Gpio_GpioPin_PB3: + return &gpio_ext_pb3; + case PB_Gpio_GpioPin_PA4: + return &gpio_ext_pa4; + case PB_Gpio_GpioPin_PA6: + return &gpio_ext_pa6; + case PB_Gpio_GpioPin_PA7: + return &gpio_ext_pa7; + } + + __builtin_unreachable(); +} + +static GpioMode rpc_mode_to_hal_mode(PB_Gpio_GpioPinMode rpc_mode) { + switch(rpc_mode) { + case PB_Gpio_GpioPinMode_OUTPUT: + return GpioModeOutputPushPull; + case PB_Gpio_GpioPinMode_INPUT: + return GpioModeInput; + } + + __builtin_unreachable(); +} + +static GpioPull rpc_pull_mode_to_hall_pull_mode(PB_Gpio_GpioInputPull pull_mode) { + switch(pull_mode) { + case PB_Gpio_GpioInputPull_UP: + return GpioPullUp; + case PB_Gpio_GpioInputPull_DOWN: + return GpioPullDown; + case PB_Gpio_GpioInputPull_NO: + return GpioPullNo; + } + + __builtin_unreachable(); +} + +static void rpc_system_gpio_set_pin_mode(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_gpio_set_pin_mode_tag); + + RpcSession* session = context; + furi_assert(session); + + PB_Gpio_SetPinMode cmd = request->content.gpio_set_pin_mode; + const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); + GpioMode mode = rpc_mode_to_hal_mode(cmd.mode); + + furi_hal_gpio_init_simple(pin, mode); + if(mode == GpioModeOutputPushPull) { + furi_hal_gpio_write(pin, false); + } + + rpc_send_and_release_empty(session, request->command_id, PB_CommandStatus_OK); +} + +static void rpc_system_gpio_write_pin(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_gpio_write_pin_tag); + + RpcSession* session = context; + furi_assert(session); + + PB_Gpio_WritePin cmd = request->content.gpio_write_pin; + const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); + uint8_t value = !!(cmd.value); + + PB_Main* response = malloc(sizeof(PB_Main)); + response->command_id = request->command_id; + response->has_next = false; + + if(LL_GPIO_MODE_OUTPUT != LL_GPIO_GetPinMode(pin->port, pin->pin)) { + response->command_status = PB_CommandStatus_ERROR_GPIO_MODE_INCORRECT; + } else { + response->command_status = PB_CommandStatus_OK; + furi_hal_gpio_write(pin, value); + } + + rpc_send_and_release(session, response); + + free(response); +} + +static void rpc_system_gpio_read_pin(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_gpio_read_pin_tag); + + RpcSession* session = context; + furi_assert(session); + + PB_Gpio_ReadPin cmd = request->content.gpio_read_pin; + const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); + + PB_Main* response = malloc(sizeof(PB_Main)); + response->command_id = request->command_id; + response->has_next = false; + + if(LL_GPIO_MODE_INPUT != LL_GPIO_GetPinMode(pin->port, pin->pin)) { + response->command_status = PB_CommandStatus_ERROR_GPIO_MODE_INCORRECT; + } else { + response->command_status = PB_CommandStatus_OK; + response->which_content = PB_Main_gpio_read_pin_response_tag; + response->content.gpio_read_pin_response.value = !!furi_hal_gpio_read(pin); + } + + rpc_send_and_release(session, response); + + free(response); +} + +void rpc_system_gpio_get_pin_mode(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_gpio_get_pin_mode_tag); + + RpcSession* session = context; + furi_assert(session); + + PB_Gpio_GetPinMode cmd = request->content.gpio_get_pin_mode; + const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); + + PB_Main* response = malloc(sizeof(PB_Main)); + response->command_id = request->command_id; + response->has_next = false; + + uint32_t raw_pin_mode = LL_GPIO_GetPinMode(pin->port, pin->pin); + + PB_Gpio_GpioPinMode pin_mode; + if(LL_GPIO_MODE_INPUT == raw_pin_mode) { + pin_mode = PB_Gpio_GpioPinMode_INPUT; + response->command_status = PB_CommandStatus_OK; + } else if(LL_GPIO_MODE_OUTPUT == raw_pin_mode) { + pin_mode = PB_Gpio_GpioPinMode_OUTPUT; + response->command_status = PB_CommandStatus_OK; + } else { + pin_mode = PB_Gpio_GpioPinMode_INPUT; + response->command_status = PB_CommandStatus_ERROR_GPIO_UNKNOWN_PIN_MODE; + } + + response->which_content = PB_Main_gpio_get_pin_mode_response_tag; + response->content.gpio_get_pin_mode_response.mode = pin_mode; + + rpc_send_and_release(session, response); + + free(response); +} + +void rpc_system_gpio_set_input_pull(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(context); + furi_assert(request->which_content == PB_Main_gpio_set_input_pull_tag); + + RpcSession* session = context; + furi_assert(session); + + PB_Gpio_SetInputPull cmd = request->content.gpio_set_input_pull; + const GpioPin* pin = rpc_pin_to_hal_pin(cmd.pin); + const GpioPull pull_mode = rpc_pull_mode_to_hall_pull_mode(cmd.pull_mode); + + PB_Main* response = malloc(sizeof(PB_Main)); + response->command_id = request->command_id; + response->has_next = false; + + PB_CommandStatus status; + if(LL_GPIO_MODE_INPUT != LL_GPIO_GetPinMode(pin->port, pin->pin)) { + status = PB_CommandStatus_ERROR_GPIO_MODE_INCORRECT; + } else { + status = PB_CommandStatus_OK; + furi_hal_gpio_init(pin, GpioModeInput, pull_mode, GpioSpeedLow); + } + + rpc_send_and_release_empty(session, request->command_id, status); + + free(response); +} + +void* rpc_system_gpio_alloc(RpcSession* session) { + furi_assert(session); + + RpcHandler rpc_handler = { + .message_handler = NULL, + .decode_submessage = NULL, + .context = session, + }; + + rpc_handler.message_handler = rpc_system_gpio_set_pin_mode; + rpc_add_handler(session, PB_Main_gpio_set_pin_mode_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_gpio_write_pin; + rpc_add_handler(session, PB_Main_gpio_write_pin_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_gpio_read_pin; + rpc_add_handler(session, PB_Main_gpio_read_pin_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_gpio_get_pin_mode; + rpc_add_handler(session, PB_Main_gpio_get_pin_mode_tag, &rpc_handler); + + rpc_handler.message_handler = rpc_system_gpio_set_input_pull; + rpc_add_handler(session, PB_Main_gpio_set_input_pull_tag, &rpc_handler); + + return NULL; +} diff --git a/applications/rpc/rpc_i.h b/applications/rpc/rpc_i.h index d0c6cf766..e512ad397 100644 --- a/applications/rpc/rpc_i.h +++ b/applications/rpc/rpc_i.h @@ -32,6 +32,8 @@ void* rpc_system_app_alloc(RpcSession* session); void rpc_system_app_free(void* ctx); void* rpc_system_gui_alloc(RpcSession* session); void rpc_system_gui_free(void* ctx); +void* rpc_system_gpio_alloc(RpcSession* session); +void rpc_system_gpio_free(void* ctx); void rpc_print_message(const PB_Main* message); void rpc_cli_command_start_session(Cli* cli, string_t args, void* context); diff --git a/assets/compiled/gpio.pb.c b/assets/compiled/gpio.pb.c new file mode 100644 index 000000000..a47c928d4 --- /dev/null +++ b/assets/compiled/gpio.pb.c @@ -0,0 +1,33 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.5 */ + +#include "gpio.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(PB_Gpio_SetPinMode, PB_Gpio_SetPinMode, AUTO) + + +PB_BIND(PB_Gpio_SetInputPull, PB_Gpio_SetInputPull, AUTO) + + +PB_BIND(PB_Gpio_GetPinMode, PB_Gpio_GetPinMode, AUTO) + + +PB_BIND(PB_Gpio_GetPinModeResponse, PB_Gpio_GetPinModeResponse, AUTO) + + +PB_BIND(PB_Gpio_ReadPin, PB_Gpio_ReadPin, AUTO) + + +PB_BIND(PB_Gpio_ReadPinResponse, PB_Gpio_ReadPinResponse, AUTO) + + +PB_BIND(PB_Gpio_WritePin, PB_Gpio_WritePin, AUTO) + + + + + + diff --git a/assets/compiled/gpio.pb.h b/assets/compiled/gpio.pb.h new file mode 100644 index 000000000..a8a08df7b --- /dev/null +++ b/assets/compiled/gpio.pb.h @@ -0,0 +1,183 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.5 */ + +#ifndef PB_PB_GPIO_GPIO_PB_H_INCLUDED +#define PB_PB_GPIO_GPIO_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +typedef enum _PB_Gpio_GpioPin { + PB_Gpio_GpioPin_PC0 = 0, + PB_Gpio_GpioPin_PC1 = 1, + PB_Gpio_GpioPin_PC3 = 2, + PB_Gpio_GpioPin_PB2 = 3, + PB_Gpio_GpioPin_PB3 = 4, + PB_Gpio_GpioPin_PA4 = 5, + PB_Gpio_GpioPin_PA6 = 6, + PB_Gpio_GpioPin_PA7 = 7 +} PB_Gpio_GpioPin; + +typedef enum _PB_Gpio_GpioPinMode { + PB_Gpio_GpioPinMode_OUTPUT = 0, + PB_Gpio_GpioPinMode_INPUT = 1 +} PB_Gpio_GpioPinMode; + +typedef enum _PB_Gpio_GpioInputPull { + PB_Gpio_GpioInputPull_NO = 0, + PB_Gpio_GpioInputPull_UP = 1, + PB_Gpio_GpioInputPull_DOWN = 2 +} PB_Gpio_GpioInputPull; + +/* Struct definitions */ +typedef struct _PB_Gpio_GetPinMode { + PB_Gpio_GpioPin pin; +} PB_Gpio_GetPinMode; + +typedef struct _PB_Gpio_GetPinModeResponse { + PB_Gpio_GpioPinMode mode; +} PB_Gpio_GetPinModeResponse; + +typedef struct _PB_Gpio_ReadPin { + PB_Gpio_GpioPin pin; +} PB_Gpio_ReadPin; + +typedef struct _PB_Gpio_ReadPinResponse { + uint32_t value; +} PB_Gpio_ReadPinResponse; + +typedef struct _PB_Gpio_SetInputPull { + PB_Gpio_GpioPin pin; + PB_Gpio_GpioInputPull pull_mode; +} PB_Gpio_SetInputPull; + +typedef struct _PB_Gpio_SetPinMode { + PB_Gpio_GpioPin pin; + PB_Gpio_GpioPinMode mode; +} PB_Gpio_SetPinMode; + +typedef struct _PB_Gpio_WritePin { + PB_Gpio_GpioPin pin; + uint32_t value; +} PB_Gpio_WritePin; + + +/* Helper constants for enums */ +#define _PB_Gpio_GpioPin_MIN PB_Gpio_GpioPin_PC0 +#define _PB_Gpio_GpioPin_MAX PB_Gpio_GpioPin_PA7 +#define _PB_Gpio_GpioPin_ARRAYSIZE ((PB_Gpio_GpioPin)(PB_Gpio_GpioPin_PA7+1)) + +#define _PB_Gpio_GpioPinMode_MIN PB_Gpio_GpioPinMode_OUTPUT +#define _PB_Gpio_GpioPinMode_MAX PB_Gpio_GpioPinMode_INPUT +#define _PB_Gpio_GpioPinMode_ARRAYSIZE ((PB_Gpio_GpioPinMode)(PB_Gpio_GpioPinMode_INPUT+1)) + +#define _PB_Gpio_GpioInputPull_MIN PB_Gpio_GpioInputPull_NO +#define _PB_Gpio_GpioInputPull_MAX PB_Gpio_GpioInputPull_DOWN +#define _PB_Gpio_GpioInputPull_ARRAYSIZE ((PB_Gpio_GpioInputPull)(PB_Gpio_GpioInputPull_DOWN+1)) + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializer values for message structs */ +#define PB_Gpio_SetPinMode_init_default {_PB_Gpio_GpioPin_MIN, _PB_Gpio_GpioPinMode_MIN} +#define PB_Gpio_SetInputPull_init_default {_PB_Gpio_GpioPin_MIN, _PB_Gpio_GpioInputPull_MIN} +#define PB_Gpio_GetPinMode_init_default {_PB_Gpio_GpioPin_MIN} +#define PB_Gpio_GetPinModeResponse_init_default {_PB_Gpio_GpioPinMode_MIN} +#define PB_Gpio_ReadPin_init_default {_PB_Gpio_GpioPin_MIN} +#define PB_Gpio_ReadPinResponse_init_default {0} +#define PB_Gpio_WritePin_init_default {_PB_Gpio_GpioPin_MIN, 0} +#define PB_Gpio_SetPinMode_init_zero {_PB_Gpio_GpioPin_MIN, _PB_Gpio_GpioPinMode_MIN} +#define PB_Gpio_SetInputPull_init_zero {_PB_Gpio_GpioPin_MIN, _PB_Gpio_GpioInputPull_MIN} +#define PB_Gpio_GetPinMode_init_zero {_PB_Gpio_GpioPin_MIN} +#define PB_Gpio_GetPinModeResponse_init_zero {_PB_Gpio_GpioPinMode_MIN} +#define PB_Gpio_ReadPin_init_zero {_PB_Gpio_GpioPin_MIN} +#define PB_Gpio_ReadPinResponse_init_zero {0} +#define PB_Gpio_WritePin_init_zero {_PB_Gpio_GpioPin_MIN, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define PB_Gpio_GetPinMode_pin_tag 1 +#define PB_Gpio_GetPinModeResponse_mode_tag 1 +#define PB_Gpio_ReadPin_pin_tag 1 +#define PB_Gpio_ReadPinResponse_value_tag 2 +#define PB_Gpio_SetInputPull_pin_tag 1 +#define PB_Gpio_SetInputPull_pull_mode_tag 2 +#define PB_Gpio_SetPinMode_pin_tag 1 +#define PB_Gpio_SetPinMode_mode_tag 2 +#define PB_Gpio_WritePin_pin_tag 1 +#define PB_Gpio_WritePin_value_tag 2 + +/* Struct field encoding specification for nanopb */ +#define PB_Gpio_SetPinMode_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, pin, 1) \ +X(a, STATIC, SINGULAR, UENUM, mode, 2) +#define PB_Gpio_SetPinMode_CALLBACK NULL +#define PB_Gpio_SetPinMode_DEFAULT NULL + +#define PB_Gpio_SetInputPull_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, pin, 1) \ +X(a, STATIC, SINGULAR, UENUM, pull_mode, 2) +#define PB_Gpio_SetInputPull_CALLBACK NULL +#define PB_Gpio_SetInputPull_DEFAULT NULL + +#define PB_Gpio_GetPinMode_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, pin, 1) +#define PB_Gpio_GetPinMode_CALLBACK NULL +#define PB_Gpio_GetPinMode_DEFAULT NULL + +#define PB_Gpio_GetPinModeResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, mode, 1) +#define PB_Gpio_GetPinModeResponse_CALLBACK NULL +#define PB_Gpio_GetPinModeResponse_DEFAULT NULL + +#define PB_Gpio_ReadPin_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, pin, 1) +#define PB_Gpio_ReadPin_CALLBACK NULL +#define PB_Gpio_ReadPin_DEFAULT NULL + +#define PB_Gpio_ReadPinResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, value, 2) +#define PB_Gpio_ReadPinResponse_CALLBACK NULL +#define PB_Gpio_ReadPinResponse_DEFAULT NULL + +#define PB_Gpio_WritePin_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, pin, 1) \ +X(a, STATIC, SINGULAR, UINT32, value, 2) +#define PB_Gpio_WritePin_CALLBACK NULL +#define PB_Gpio_WritePin_DEFAULT NULL + +extern const pb_msgdesc_t PB_Gpio_SetPinMode_msg; +extern const pb_msgdesc_t PB_Gpio_SetInputPull_msg; +extern const pb_msgdesc_t PB_Gpio_GetPinMode_msg; +extern const pb_msgdesc_t PB_Gpio_GetPinModeResponse_msg; +extern const pb_msgdesc_t PB_Gpio_ReadPin_msg; +extern const pb_msgdesc_t PB_Gpio_ReadPinResponse_msg; +extern const pb_msgdesc_t PB_Gpio_WritePin_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define PB_Gpio_SetPinMode_fields &PB_Gpio_SetPinMode_msg +#define PB_Gpio_SetInputPull_fields &PB_Gpio_SetInputPull_msg +#define PB_Gpio_GetPinMode_fields &PB_Gpio_GetPinMode_msg +#define PB_Gpio_GetPinModeResponse_fields &PB_Gpio_GetPinModeResponse_msg +#define PB_Gpio_ReadPin_fields &PB_Gpio_ReadPin_msg +#define PB_Gpio_ReadPinResponse_fields &PB_Gpio_ReadPinResponse_msg +#define PB_Gpio_WritePin_fields &PB_Gpio_WritePin_msg + +/* Maximum encoded size of messages (where known) */ +#define PB_Gpio_GetPinModeResponse_size 2 +#define PB_Gpio_GetPinMode_size 2 +#define PB_Gpio_ReadPinResponse_size 6 +#define PB_Gpio_ReadPin_size 2 +#define PB_Gpio_SetInputPull_size 4 +#define PB_Gpio_SetPinMode_size 4 +#define PB_Gpio_WritePin_size 8 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/assets/protobuf b/assets/protobuf index 6c1b8ae66..d9e343661 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 6c1b8ae66a85bcd7e79e993a0b5573c38c302db5 +Subproject commit d9e343661dd36cfab792b78be1dea4e5950cb4dd From 6b6ea44802d2e12bff37f4c371a9f4e32537d4e5 Mon Sep 17 00:00:00 2001 From: hedger Date: Mon, 4 Jul 2022 19:53:04 +0300 Subject: [PATCH 5/7] fbt: initial blackmagic support (#1362) * fbt: added separate script for Windows env setup; moved flash targets from firmware.scons to SConstruct; added Blackmagic support with automatic probe port resolution; added apps.c rebuild on any manifest.fam changes; fixed simultaneous flash & debug ops * fbt: added networked BlackmagicResolver mode; added `get_blackmagic` target for IDE integration * fbt: cleanup * fbt: docs update; fixed blackmagic lookup on certain usb hubs * fbt: removed explicit python serial port import * fbt: cleanup * fbt: raising exception on multiple serial blackmagic probes --- SConstruct | 109 ++++++++++++++++++++++---- documentation/fbt.md | 3 + fbt | 1 - fbt.cmd | 3 +- fbt_options.py | 16 +++- firmware.scons | 18 +---- site_scons/commandline.scons | 6 ++ site_scons/environ.scons | 1 + site_scons/fbt/util.py | 4 + site_scons/site_tools/blackmagic.py | 74 +++++++++++++++++ site_scons/site_tools/fbt_dist.py | 29 +++---- site_scons/site_tools/sconsmodular.py | 6 +- 12 files changed, 220 insertions(+), 50 deletions(-) create mode 100644 site_scons/site_tools/blackmagic.py diff --git a/SConstruct b/SConstruct index 7e8fc8962..33463cdce 100644 --- a/SConstruct +++ b/SConstruct @@ -28,15 +28,41 @@ SConscript("site_scons/cc.scons", exports={"ENV": coreenv}) # Store root dir in environment for certain tools coreenv["ROOT_DIR"] = Dir(".") + # Create a separate "dist" environment and add construction envs to it distenv = coreenv.Clone( - tools=["fbt_dist", "openocd"], - GDBOPTS="-ex 'target extended-remote | ${OPENOCD} -c \"gdb_port pipe\" ${OPENOCD_OPTS}' " - '-ex "set confirm off" ', + tools=["fbt_dist", "openocd", "blackmagic"], + OPENOCD_GDB_PIPE=["|openocd -c 'gdb_port pipe' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"], + GDBOPTS_BASE=[ + "-ex", + "target extended-remote ${GDBREMOTE}", + "-ex", + "set confirm off", + ], + GDBOPTS_BLACKMAGIC=[ + "-ex", + "monitor swdp_scan", + "-ex", + "monitor debug_bmp enable", + "-ex", + "attach 1", + "-ex", + "set mem inaccessible-by-default off", + ], + GDBPYOPTS=[ + "-ex", + "source debug/FreeRTOS/FreeRTOS.py", + "-ex", + "source debug/PyCortexMDebug/PyCortexMDebug.py", + "-ex", + "svd_load ${SVD_FILE}", + "-ex", + "compare-sections", + ], ENV=os.environ, ) -firmware_out = distenv.AddFwProject( +firmware_env = distenv.AddFwProject( base_env=coreenv, fw_type="firmware", fw_env_key="FW_ENV", @@ -44,7 +70,7 @@ firmware_out = distenv.AddFwProject( # If enabled, initialize updater-related targets if GetOption("fullenv"): - updater_out = distenv.AddFwProject( + updater_env = distenv.AddFwProject( base_env=coreenv, fw_type="updater", fw_env_key="UPD_ENV", @@ -72,19 +98,32 @@ if GetOption("fullenv"): selfupdate_dist = distenv.DistCommand( "updater_package", - (distenv["DIST_DEPENDS"], firmware_out["FW_RESOURCES"]), + (distenv["DIST_DEPENDS"], firmware_env["FW_RESOURCES"]), DIST_EXTRA=dist_arguments, ) # Updater debug - distenv.AddDebugTarget("updater_debug", updater_out, False) + distenv.PhonyTarget( + "updater_debug", + "${GDBPYCOM}", + source=updater_env["FW_ELF"], + GDBREMOTE="${OPENOCD_GDB_PIPE}", + ) + + distenv.PhonyTarget( + "updater_blackmagic", + "${GDBPYCOM}", + source=updater_env["FW_ELF"], + GDBOPTS=distenv.subst("$GDBOPTS_BLACKMAGIC"), + GDBREMOTE="${BLACKMAGIC_ADDR}", + ) # Installation over USB & CLI usb_update_package = distenv.UsbInstall( "#build/usbinstall.flag", ( distenv["DIST_DEPENDS"], - firmware_out["FW_RESOURCES"], + firmware_env["FW_RESOURCES"], selfupdate_dist, ), ) @@ -104,15 +143,47 @@ copro_dist = distenv.CoproBuilder( ) distenv.Alias("copro_dist", copro_dist) +firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env) +distenv.Alias("flash", firmware_flash) + +firmware_bm_flash = distenv.PhonyTarget( + "flash_blackmagic", + "$GDB $GDBOPTS $SOURCES $GDBFLASH", + source=firmware_env["FW_ELF"], + GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", + GDBREMOTE="${BLACKMAGIC_ADDR}", + GDBFLASH=[ + "-ex", + "load", + "-ex", + "quit", + ], +) + # Debugging firmware -distenv.AddDebugTarget("debug", firmware_out) +firmware_debug = distenv.PhonyTarget( + "debug", + "${GDBPYCOM}", + source=firmware_env["FW_ELF"], + GDBOPTS="${GDBOPTS_BASE}", + GDBREMOTE="${OPENOCD_GDB_PIPE}", +) +distenv.Depends(firmware_debug, firmware_flash) + +distenv.PhonyTarget( + "blackmagic", + "${GDBPYCOM}", + source=firmware_env["FW_ELF"], + GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", + GDBREMOTE="${BLACKMAGIC_ADDR}", +) + # Debug alien elf distenv.PhonyTarget( "debug_other", - "$GDBPYCOM", - GDBPYOPTS= - # '-ex "source ${ROOT_DIR.abspath}/debug/FreeRTOS/FreeRTOS.py" ' - '-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ', + "${GDBPYCOM}", + GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ', + GDBREMOTE="${OPENOCD_GDB_PIPE}", ) # Just start OpenOCD @@ -125,11 +196,19 @@ distenv.PhonyTarget( distenv.PhonyTarget( "lint", "${PYTHON3} scripts/lint.py check ${LINT_SOURCES}", - LINT_SOURCES=firmware_out["LINT_SOURCES"], + LINT_SOURCES=firmware_env["LINT_SOURCES"], ) distenv.PhonyTarget( "format", "${PYTHON3} scripts/lint.py format ${LINT_SOURCES}", - LINT_SOURCES=firmware_out["LINT_SOURCES"], + LINT_SOURCES=firmware_env["LINT_SOURCES"], +) + + +# Find blackmagic probe + +distenv.PhonyTarget( + "get_blackmagic", + "@echo $( ${BLACKMAGIC_ADDR} $)", ) diff --git a/documentation/fbt.md b/documentation/fbt.md index 9658ff6a2..48d761f58 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -34,7 +34,9 @@ FBT keeps track of internal dependencies, so you only need to build the highest- - `debug` - build and flash firmware, then attach with gdb with firmware's .elf loaded - `debug_updater` - attach gdb with updater's .elf loaded. _Requires `--with-updater` option_ - `debug_other` - attach gdb without loading any .elf. Allows to manually add external elf files with `add-symbol-file` in gdb. +- `blackmagic` - debug firmware with Blackmagic probe (WiFi dev board) - `openocd` - just start OpenOCD +- `get_blackmagic` - output blackmagic address in gdb remote format. Useful for IDE integration ### Firmware targets @@ -43,6 +45,7 @@ FBT keeps track of internal dependencies, so you only need to build the highest- - Check out `--extra-ext-apps` for force adding extra apps to external build - `firmware_snake_game_list`, etc - generate source + assembler listing for app's .elf - `flash`, `firmware_flash` - flash current version to attached device with OpenOCD over ST-Link +- `flash_blackmagic` - flash current version to attached device with Blackmagic probe - `firmware_cdb` - generate compilation database - `firmware_all`, `updater_all` - build basic set of binaries - `firmware_list`, `updater_list` - generate source + assembler listing diff --git a/fbt b/fbt index c95b1371b..47444aa75 100755 --- a/fbt +++ b/fbt @@ -6,7 +6,6 @@ SCRIPTDIR="$( dirname -- "$0"; )"; SCONS_EP=${SCRIPTDIR}/lib/scons/scripts/scons.py if [[ -d .git ]]; then - echo Updating git submodules git submodule update --init else # Not in a git repo echo Not in a git repo, please clone with git clone --recursive diff --git a/fbt.cmd b/fbt.cmd index 67d42132a..cecab4652 100644 --- a/fbt.cmd +++ b/fbt.cmd @@ -3,8 +3,7 @@ set SCONS_EP=%~dp0\lib\scons\scripts\scons.py if exist ".git" ( - echo Updating git submodules - git submodule update --init + git submodule update --init ) set "SCONS_DEFAULT_FLAGS=-Q --warn=target-not-built" diff --git a/fbt_options.py b/fbt_options.py index 3cad7e58c..1c9f9c82b 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -41,10 +41,24 @@ COPRO_STACK_BIN_DIR = posixpath.join( # Supported toolchain versions FBT_TOOLCHAIN_VERSIONS = (" 10.3.",) -OPENOCD_OPTS = '-f interface/stlink.cfg -c "transport select hla_swd" -f debug/stm32wbx.cfg -c "stm32wbx.cpu configure -rtos auto" -c "init"' +OPENOCD_OPTS = [ + "-f", + "interface/stlink.cfg", + "-c", + "transport select hla_swd", + "-f", + "debug/stm32wbx.cfg", + "-c", + "stm32wbx.cpu configure -rtos auto", + "-c", + "init", +] SVD_FILE = "debug/STM32WB55_CM4.svd" +# Look for blackmagic probe on serial ports +BLACKMAGIC = "auto" + FIRMWARE_APPS = { "default": [ "crypto_start", diff --git a/firmware.scons b/firmware.scons index 4bed960f2..95deea0ad 100644 --- a/firmware.scons +++ b/firmware.scons @@ -6,7 +6,7 @@ from fbt.util import link_dir # Building initial C environment for libs env = ENV.Clone( - tools=["compilation_db", "fwbin", "openocd", "fbt_apps"], + tools=["compilation_db", "fwbin", "fbt_apps"], COMPILATIONDB_USE_ABSPATH=True, BUILD_DIR=fw_build_meta["build_dir"], IS_BASE_FIRMWARE=fw_build_meta["type"] == "firmware", @@ -139,6 +139,8 @@ apps_c = fwenv.ApplicationsC( "applications/applications.c", Value(fwenv["APPS"]), ) +# Adding dependency on manifest files so apps.c is rebuilt when any manifest is changed +fwenv.Depends(apps_c, fwenv.GlobRecursive("*.fam", "applications")) sources = [apps_c] # Gather sources only from app folders from current configuration @@ -235,7 +237,7 @@ AddPostAction(fwelf, Action("@$SIZECOM")) AddPostAction(fwelf, Action(link_latest_dir, None)) link_dir_command = fwenv["LINK_DIR_CMD"] = fwenv.PhonyTarget( - "${FIRMWARE_BUILD_CFG}" + "_latest", + fwenv.subst("${FIRMWARE_BUILD_CFG}_latest"), Action(lambda target, source, env: link_elf_dir_as_latest(env, source[0]), None), source=fwelf, ) @@ -249,18 +251,6 @@ Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_dfu", fwdfu) fwdump = fwenv.ObjDump("${FIRMWARE_BUILD_CFG}") Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_list", fwdump) -# Additional FW-related pseudotargets -flash = fwenv["FW_FLASH"] = fwenv.OpenOCDFlash( - "#build/oocd-${FIRMWARE_BUILD_CFG}-flash.flag", - "${FIRMWARE_BUILD_CFG}", - OPENOCD_COMMAND='-c "program ${SOURCE.posix} reset exit ${IMAGE_BASE_ADDRESS}"', -) -if fwenv["FORCE"]: - fwenv.AlwaysBuild(flash) -fwenv.Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_flash", flash) -if fwenv["IS_BASE_FIRMWARE"]: - fwenv.Alias("flash", flash) - # Compile DB generation fwcdb = fwenv["FW_CDB"] = fwenv.CompilationDatabase("compile_commands.json") diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index 06e836578..5a7a0dd2d 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -162,6 +162,12 @@ vars.Add( default="", ) +vars.Add( + "BLACKMAGIC", + help="Blackmagic probe location", + default="auto", +) + vars.Add( "UPDATE_SPLASH", help="Directory name with slideshow frames to render after installing update package", diff --git a/site_scons/environ.scons b/site_scons/environ.scons index 9e49e8b60..99d4cc0b5 100644 --- a/site_scons/environ.scons +++ b/site_scons/environ.scons @@ -78,5 +78,6 @@ coreenv["TEMPFILEARGESCFUNC"] = util.tempfile_arg_esc_func util.wrap_tempfile(coreenv, "LINKCOM") util.wrap_tempfile(coreenv, "ARCOM") +coreenv["SINGLEQUOTEFUNC"] = util.single_quote Return("coreenv") diff --git a/site_scons/fbt/util.py b/site_scons/fbt/util.py index 11509b2d1..e77c9e584 100644 --- a/site_scons/fbt/util.py +++ b/site_scons/fbt/util.py @@ -42,3 +42,7 @@ def random_alnum(length): return "".join( random.choice(string.ascii_letters + string.digits) for _ in range(length) ) + + +def single_quote(arg_list): + return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list) diff --git a/site_scons/site_tools/blackmagic.py b/site_scons/site_tools/blackmagic.py new file mode 100644 index 000000000..ec48c15fd --- /dev/null +++ b/site_scons/site_tools/blackmagic.py @@ -0,0 +1,74 @@ +from SCons.Errors import StopError + + +class BlackmagicResolver: + BLACKMAGIC_HOSTNAME = "blackmagic.local" + + def __init__(self, env): + self.env = env + + # On Win: + # 'location': '1-5:x.0', 'name': 'COM4', + # 'location': '1-5:x.2', 'name': 'COM13', + # On Linux: + # 'location': '1-1.2:1.0', 'name': 'ttyACM0', + # 'location': '1-1.2:1.2', 'name': 'ttyACM1', + # On MacOS: + # 'location': '0-1.3', 'name': 'cu.usbmodemblackmagic1', + # 'location': '0-1.3', 'name': 'cu.usbmodemblackmagic3', + def _find_probe(self): + import serial.tools.list_ports as list_ports + + ports = list(list_ports.grep("blackmagic")) + if len(ports) == 0: + # Blackmagic probe serial port not found, will be handled later + pass + elif len(ports) > 2: + raise StopError("More than one Blackmagic probe found") + else: + # If you're getting any issues with auto lookup, uncomment this + # print("\n".join([f"{p.device} {vars(p)}" for p in ports])) + return sorted(ports, key=lambda p: f"{p.location}_{p.name}")[0] + + # Look up blackmagic probe hostname with dns + def _resolve_hostname(self): + import socket + + try: + return socket.gethostbyname(self.BLACKMAGIC_HOSTNAME) + except socket.gaierror: + print("Failed to resolve Blackmagic hostname") + return None + + def get_serial(self): + if not (probe := self._find_probe()): + return None + # print(f"Found Blackmagic probe on {probe.device}") + if self.env.subst("$PLATFORM") == "win32": + return f"\\\\.\\{probe.device}" + return probe.device + + def get_networked(self): + if not (probe := self._resolve_hostname()): + return None + + return f"tcp:{probe}:2345" + + def __str__(self): + # print("distenv blackmagic", self.env.subst("$BLACKMAGIC")) + if (blackmagic := self.env.subst("$BLACKMAGIC")) != "auto": + return blackmagic + + # print("Looking for Blackmagic...") + if probe := self.get_serial() or self.get_networked(): + return probe + + raise Exception("Please specify BLACKMAGIC=...") + + +def generate(env): + env.SetDefault(BLACKMAGIC_ADDR=BlackmagicResolver(env)) + + +def exists(env): + return True diff --git a/site_scons/site_tools/fbt_dist.py b/site_scons/site_tools/fbt_dist.py index fcfecbcbf..37fbf74b3 100644 --- a/site_scons/site_tools/fbt_dist.py +++ b/site_scons/site_tools/fbt_dist.py @@ -54,20 +54,20 @@ def AddFwProject(env, base_env, fw_type, fw_env_key): return project_env -def AddDebugTarget(env, alias, targetenv, force_flash=True): - debug_target = env.PhonyTarget( - alias, - "$GDBPYCOM", - source=targetenv["FW_ELF"], - GDBPYOPTS='-ex "source debug/FreeRTOS/FreeRTOS.py" ' - '-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ' - '-ex "svd_load ${SVD_FILE}" ' - '-ex "compare-sections"', +def AddOpenOCDFlashTarget(env, targetenv, **kw): + openocd_target = env.OpenOCDFlash( + "#build/oocd-${BUILD_CFG}-flash.flag", + targetenv["FW_BIN"], + OPENOCD_COMMAND=[ + "-c", + "program ${SOURCE.posix} reset exit ${BASE_ADDRESS}", + ], + BUILD_CFG=targetenv.subst("$FIRMWARE_BUILD_CFG"), + BASE_ADDRESS=targetenv.subst("$IMAGE_BASE_ADDRESS"), + **kw, ) - if force_flash: - env.Depends(debug_target, targetenv["FW_FLASH"]) - - return debug_target + env.Alias(targetenv.subst("${FIRMWARE_BUILD_CFG}_flash"), openocd_target) + return openocd_target def DistCommand(env, name, source, **kw): @@ -85,8 +85,9 @@ def DistCommand(env, name, source, **kw): def generate(env): env.AddMethod(AddFwProject) - env.AddMethod(AddDebugTarget) env.AddMethod(DistCommand) + env.AddMethod(AddOpenOCDFlashTarget) + env.SetDefault( COPRO_MCU_FAMILY="STM32WB5x", ) diff --git a/site_scons/site_tools/sconsmodular.py b/site_scons/site_tools/sconsmodular.py index a4bb9f65a..db0cb8f35 100644 --- a/site_scons/site_tools/sconsmodular.py +++ b/site_scons/site_tools/sconsmodular.py @@ -34,9 +34,9 @@ def PhonyTarget(env, name, action, source=None, **kw): source = [] phony_name = "phony_" + name env.Pseudo(phony_name) - return env.AlwaysBuild( - env.Alias(name, env.Command(phony_name, source, action, **kw)) - ) + command = env.Command(phony_name, source, action, **kw) + env.AlwaysBuild(env.Alias(name, command)) + return command def generate(env): From 43fd2e44339f3c392bdfbf7f57739b51d5aa9157 Mon Sep 17 00:00:00 2001 From: hedger Date: Mon, 4 Jul 2022 20:38:18 +0300 Subject: [PATCH 6/7] fbt: added support for FBT_NO_SYNC environment variable to skip submodule update at start (#1363) --- assets/compiled/gpio.pb.c | 33 ------- assets/compiled/gpio.pb.h | 183 -------------------------------------- documentation/fbt.md | 5 +- fbt | 13 +-- fbt.cmd | 9 +- 5 files changed, 18 insertions(+), 225 deletions(-) delete mode 100644 assets/compiled/gpio.pb.c delete mode 100644 assets/compiled/gpio.pb.h diff --git a/assets/compiled/gpio.pb.c b/assets/compiled/gpio.pb.c deleted file mode 100644 index a47c928d4..000000000 --- a/assets/compiled/gpio.pb.c +++ /dev/null @@ -1,33 +0,0 @@ -/* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.5 */ - -#include "gpio.pb.h" -#if PB_PROTO_HEADER_VERSION != 40 -#error Regenerate this file with the current version of nanopb generator. -#endif - -PB_BIND(PB_Gpio_SetPinMode, PB_Gpio_SetPinMode, AUTO) - - -PB_BIND(PB_Gpio_SetInputPull, PB_Gpio_SetInputPull, AUTO) - - -PB_BIND(PB_Gpio_GetPinMode, PB_Gpio_GetPinMode, AUTO) - - -PB_BIND(PB_Gpio_GetPinModeResponse, PB_Gpio_GetPinModeResponse, AUTO) - - -PB_BIND(PB_Gpio_ReadPin, PB_Gpio_ReadPin, AUTO) - - -PB_BIND(PB_Gpio_ReadPinResponse, PB_Gpio_ReadPinResponse, AUTO) - - -PB_BIND(PB_Gpio_WritePin, PB_Gpio_WritePin, AUTO) - - - - - - diff --git a/assets/compiled/gpio.pb.h b/assets/compiled/gpio.pb.h deleted file mode 100644 index a8a08df7b..000000000 --- a/assets/compiled/gpio.pb.h +++ /dev/null @@ -1,183 +0,0 @@ -/* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.5 */ - -#ifndef PB_PB_GPIO_GPIO_PB_H_INCLUDED -#define PB_PB_GPIO_GPIO_PB_H_INCLUDED -#include - -#if PB_PROTO_HEADER_VERSION != 40 -#error Regenerate this file with the current version of nanopb generator. -#endif - -/* Enum definitions */ -typedef enum _PB_Gpio_GpioPin { - PB_Gpio_GpioPin_PC0 = 0, - PB_Gpio_GpioPin_PC1 = 1, - PB_Gpio_GpioPin_PC3 = 2, - PB_Gpio_GpioPin_PB2 = 3, - PB_Gpio_GpioPin_PB3 = 4, - PB_Gpio_GpioPin_PA4 = 5, - PB_Gpio_GpioPin_PA6 = 6, - PB_Gpio_GpioPin_PA7 = 7 -} PB_Gpio_GpioPin; - -typedef enum _PB_Gpio_GpioPinMode { - PB_Gpio_GpioPinMode_OUTPUT = 0, - PB_Gpio_GpioPinMode_INPUT = 1 -} PB_Gpio_GpioPinMode; - -typedef enum _PB_Gpio_GpioInputPull { - PB_Gpio_GpioInputPull_NO = 0, - PB_Gpio_GpioInputPull_UP = 1, - PB_Gpio_GpioInputPull_DOWN = 2 -} PB_Gpio_GpioInputPull; - -/* Struct definitions */ -typedef struct _PB_Gpio_GetPinMode { - PB_Gpio_GpioPin pin; -} PB_Gpio_GetPinMode; - -typedef struct _PB_Gpio_GetPinModeResponse { - PB_Gpio_GpioPinMode mode; -} PB_Gpio_GetPinModeResponse; - -typedef struct _PB_Gpio_ReadPin { - PB_Gpio_GpioPin pin; -} PB_Gpio_ReadPin; - -typedef struct _PB_Gpio_ReadPinResponse { - uint32_t value; -} PB_Gpio_ReadPinResponse; - -typedef struct _PB_Gpio_SetInputPull { - PB_Gpio_GpioPin pin; - PB_Gpio_GpioInputPull pull_mode; -} PB_Gpio_SetInputPull; - -typedef struct _PB_Gpio_SetPinMode { - PB_Gpio_GpioPin pin; - PB_Gpio_GpioPinMode mode; -} PB_Gpio_SetPinMode; - -typedef struct _PB_Gpio_WritePin { - PB_Gpio_GpioPin pin; - uint32_t value; -} PB_Gpio_WritePin; - - -/* Helper constants for enums */ -#define _PB_Gpio_GpioPin_MIN PB_Gpio_GpioPin_PC0 -#define _PB_Gpio_GpioPin_MAX PB_Gpio_GpioPin_PA7 -#define _PB_Gpio_GpioPin_ARRAYSIZE ((PB_Gpio_GpioPin)(PB_Gpio_GpioPin_PA7+1)) - -#define _PB_Gpio_GpioPinMode_MIN PB_Gpio_GpioPinMode_OUTPUT -#define _PB_Gpio_GpioPinMode_MAX PB_Gpio_GpioPinMode_INPUT -#define _PB_Gpio_GpioPinMode_ARRAYSIZE ((PB_Gpio_GpioPinMode)(PB_Gpio_GpioPinMode_INPUT+1)) - -#define _PB_Gpio_GpioInputPull_MIN PB_Gpio_GpioInputPull_NO -#define _PB_Gpio_GpioInputPull_MAX PB_Gpio_GpioInputPull_DOWN -#define _PB_Gpio_GpioInputPull_ARRAYSIZE ((PB_Gpio_GpioInputPull)(PB_Gpio_GpioInputPull_DOWN+1)) - - -#ifdef __cplusplus -extern "C" { -#endif - -/* Initializer values for message structs */ -#define PB_Gpio_SetPinMode_init_default {_PB_Gpio_GpioPin_MIN, _PB_Gpio_GpioPinMode_MIN} -#define PB_Gpio_SetInputPull_init_default {_PB_Gpio_GpioPin_MIN, _PB_Gpio_GpioInputPull_MIN} -#define PB_Gpio_GetPinMode_init_default {_PB_Gpio_GpioPin_MIN} -#define PB_Gpio_GetPinModeResponse_init_default {_PB_Gpio_GpioPinMode_MIN} -#define PB_Gpio_ReadPin_init_default {_PB_Gpio_GpioPin_MIN} -#define PB_Gpio_ReadPinResponse_init_default {0} -#define PB_Gpio_WritePin_init_default {_PB_Gpio_GpioPin_MIN, 0} -#define PB_Gpio_SetPinMode_init_zero {_PB_Gpio_GpioPin_MIN, _PB_Gpio_GpioPinMode_MIN} -#define PB_Gpio_SetInputPull_init_zero {_PB_Gpio_GpioPin_MIN, _PB_Gpio_GpioInputPull_MIN} -#define PB_Gpio_GetPinMode_init_zero {_PB_Gpio_GpioPin_MIN} -#define PB_Gpio_GetPinModeResponse_init_zero {_PB_Gpio_GpioPinMode_MIN} -#define PB_Gpio_ReadPin_init_zero {_PB_Gpio_GpioPin_MIN} -#define PB_Gpio_ReadPinResponse_init_zero {0} -#define PB_Gpio_WritePin_init_zero {_PB_Gpio_GpioPin_MIN, 0} - -/* Field tags (for use in manual encoding/decoding) */ -#define PB_Gpio_GetPinMode_pin_tag 1 -#define PB_Gpio_GetPinModeResponse_mode_tag 1 -#define PB_Gpio_ReadPin_pin_tag 1 -#define PB_Gpio_ReadPinResponse_value_tag 2 -#define PB_Gpio_SetInputPull_pin_tag 1 -#define PB_Gpio_SetInputPull_pull_mode_tag 2 -#define PB_Gpio_SetPinMode_pin_tag 1 -#define PB_Gpio_SetPinMode_mode_tag 2 -#define PB_Gpio_WritePin_pin_tag 1 -#define PB_Gpio_WritePin_value_tag 2 - -/* Struct field encoding specification for nanopb */ -#define PB_Gpio_SetPinMode_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UENUM, pin, 1) \ -X(a, STATIC, SINGULAR, UENUM, mode, 2) -#define PB_Gpio_SetPinMode_CALLBACK NULL -#define PB_Gpio_SetPinMode_DEFAULT NULL - -#define PB_Gpio_SetInputPull_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UENUM, pin, 1) \ -X(a, STATIC, SINGULAR, UENUM, pull_mode, 2) -#define PB_Gpio_SetInputPull_CALLBACK NULL -#define PB_Gpio_SetInputPull_DEFAULT NULL - -#define PB_Gpio_GetPinMode_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UENUM, pin, 1) -#define PB_Gpio_GetPinMode_CALLBACK NULL -#define PB_Gpio_GetPinMode_DEFAULT NULL - -#define PB_Gpio_GetPinModeResponse_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UENUM, mode, 1) -#define PB_Gpio_GetPinModeResponse_CALLBACK NULL -#define PB_Gpio_GetPinModeResponse_DEFAULT NULL - -#define PB_Gpio_ReadPin_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UENUM, pin, 1) -#define PB_Gpio_ReadPin_CALLBACK NULL -#define PB_Gpio_ReadPin_DEFAULT NULL - -#define PB_Gpio_ReadPinResponse_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UINT32, value, 2) -#define PB_Gpio_ReadPinResponse_CALLBACK NULL -#define PB_Gpio_ReadPinResponse_DEFAULT NULL - -#define PB_Gpio_WritePin_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UENUM, pin, 1) \ -X(a, STATIC, SINGULAR, UINT32, value, 2) -#define PB_Gpio_WritePin_CALLBACK NULL -#define PB_Gpio_WritePin_DEFAULT NULL - -extern const pb_msgdesc_t PB_Gpio_SetPinMode_msg; -extern const pb_msgdesc_t PB_Gpio_SetInputPull_msg; -extern const pb_msgdesc_t PB_Gpio_GetPinMode_msg; -extern const pb_msgdesc_t PB_Gpio_GetPinModeResponse_msg; -extern const pb_msgdesc_t PB_Gpio_ReadPin_msg; -extern const pb_msgdesc_t PB_Gpio_ReadPinResponse_msg; -extern const pb_msgdesc_t PB_Gpio_WritePin_msg; - -/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ -#define PB_Gpio_SetPinMode_fields &PB_Gpio_SetPinMode_msg -#define PB_Gpio_SetInputPull_fields &PB_Gpio_SetInputPull_msg -#define PB_Gpio_GetPinMode_fields &PB_Gpio_GetPinMode_msg -#define PB_Gpio_GetPinModeResponse_fields &PB_Gpio_GetPinModeResponse_msg -#define PB_Gpio_ReadPin_fields &PB_Gpio_ReadPin_msg -#define PB_Gpio_ReadPinResponse_fields &PB_Gpio_ReadPinResponse_msg -#define PB_Gpio_WritePin_fields &PB_Gpio_WritePin_msg - -/* Maximum encoded size of messages (where known) */ -#define PB_Gpio_GetPinModeResponse_size 2 -#define PB_Gpio_GetPinMode_size 2 -#define PB_Gpio_ReadPinResponse_size 6 -#define PB_Gpio_ReadPin_size 2 -#define PB_Gpio_SetInputPull_size 4 -#define PB_Gpio_SetPinMode_size 4 -#define PB_Gpio_WritePin_size 8 - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif diff --git a/documentation/fbt.md b/documentation/fbt.md index 48d761f58..a1e92849f 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -10,7 +10,10 @@ Make sure that `gcc-arm-none-eabi` toolchain & OpenOCD executables are in system ## NB -FBT constructs all referenced environments & their targets' dependency trees on startup. So, to keep startup time as low as possible, we're hiding construction of certain targets behind command-line options. +* `fbt` constructs all referenced environments & their targets' dependency trees on startup. So, to keep startup time as low as possible, we're hiding construction of certain targets behind command-line options. +* `fbt` always performs `git submodule update --init` on start, unless you set `FBT_NO_SYNC=1` in environment: + * On Windows, that's `set "FBT_NO_SYNC=1"` in the shell you're running `fbt` from + * On \*nix, it's `$ FBT_NO_SYNC=1 ./fbt ...` ## Invoking FBT diff --git a/fbt b/fbt index 47444aa75..b3d2ca354 100755 --- a/fbt +++ b/fbt @@ -5,12 +5,13 @@ set -e SCRIPTDIR="$( dirname -- "$0"; )"; SCONS_EP=${SCRIPTDIR}/lib/scons/scripts/scons.py -if [[ -d .git ]]; then - git submodule update --init -else # Not in a git repo - echo Not in a git repo, please clone with git clone --recursive - # Return error code 1 to indicate failure - exit 1 +if [[ -z "${FBT_NO_SYNC:-}" ]] ; then + if [[ -d .git ]]; then + git submodule update --init + else + echo Not in a git repo, please clone with git clone --recursive + exit 1 + fi fi SCONS_DEFAULT_FLAGS="-Q --warn=target-not-built" diff --git a/fbt.cmd b/fbt.cmd index cecab4652..5edecd210 100644 --- a/fbt.cmd +++ b/fbt.cmd @@ -2,8 +2,13 @@ set SCONS_EP=%~dp0\lib\scons\scripts\scons.py -if exist ".git" ( - git submodule update --init +if [%FBT_NO_SYNC%] == [] ( + if exist ".git" ( + git submodule update --init + ) else ( + echo Not in a git repo, please clone with git clone --recursive + exit /b 1 + ) ) set "SCONS_DEFAULT_FLAGS=-Q --warn=target-not-built" From 05b7b7f2db4f811c5a30915286ceed2db904d004 Mon Sep 17 00:00:00 2001 From: Roman Shchekin Date: Mon, 4 Jul 2022 20:49:23 +0300 Subject: [PATCH 7/7] plugins: snake: simplification in direction calculation (#1361) Co-authored-by: hedger --- applications/snake_game/snake_game.c | 43 ++++------------------------ 1 file changed, 5 insertions(+), 38 deletions(-) diff --git a/applications/snake_game/snake_game.c b/applications/snake_game/snake_game.c index bb9e207de..1a4bf8126 100644 --- a/applications/snake_game/snake_game.c +++ b/applications/snake_game/snake_game.c @@ -29,6 +29,8 @@ typedef enum { GameStateGameOver, } GameState; +// Note: do not change without purpose. Current values are used in smart +// orthogonality calculation in `snake_game_get_turn_snake`. typedef enum { DirectionUp, DirectionRight, @@ -195,44 +197,9 @@ static bool } static Direction snake_game_get_turn_snake(SnakeState const* const snake_state) { - switch(snake_state->currentMovement) { - case DirectionUp: - switch(snake_state->nextMovement) { - case DirectionRight: - return DirectionRight; - case DirectionLeft: - return DirectionLeft; - default: - return snake_state->currentMovement; - } - case DirectionRight: - switch(snake_state->nextMovement) { - case DirectionUp: - return DirectionUp; - case DirectionDown: - return DirectionDown; - default: - return snake_state->currentMovement; - } - case DirectionDown: - switch(snake_state->nextMovement) { - case DirectionRight: - return DirectionRight; - case DirectionLeft: - return DirectionLeft; - default: - return snake_state->currentMovement; - } - default: // case DirectionLeft: - switch(snake_state->nextMovement) { - case DirectionUp: - return DirectionUp; - case DirectionDown: - return DirectionDown; - default: - return snake_state->currentMovement; - } - } + // Sum of two `Direction` lies between 0 and 6, odd values indicate orthogonality. + bool is_orthogonal = (snake_state->currentMovement + snake_state->nextMovement) % 2 == 1; + return is_orthogonal ? snake_state->nextMovement : snake_state->currentMovement; } static Point snake_game_get_next_step(SnakeState const* const snake_state) {