Quellcode durchsuchen

[FL-2204] Bluetooth forget devices (#967)

* bt: update connection parameters
* bt: set correct connection latency and timeout
* gui popup: add clean method
* furi_hal_bt: add connection parameters request, clear database
* bt: add forget bonded devices API
* bt_settings: add forget bonded devices GUI
* bt: rework pin code show with view port to hide view
* bt: support conn parameters for different profiles
* furi_hal_bt: sync f6 target
* target f6: fix build
* bt: format sources
* furi_hal_bt: update connection parameters
* bt: update connection params, fix GUI
* FuriHal: fix spelling
* Refactoring: rename _clean to _reset

Co-authored-by: あく <alleteam@gmail.com>
gornekich vor 4 Jahren
Ursprung
Commit
23ff6723cf
82 geänderte Dateien mit 471 neuen und 116 gelöschten Zeilen
  1. 1 1
      applications/archive/scenes/archive_scene_rename.c
  2. 53 17
      applications/bt/bt_service/bt.c
  3. 7 0
      applications/bt/bt_service/bt.h
  4. 6 0
      applications/bt/bt_service/bt_api.c
  5. 4 1
      applications/bt/bt_service/bt_i.h
  6. 13 0
      applications/bt/bt_service/bt_keys_storage.c
  7. 2 0
      applications/bt/bt_service/bt_keys_storage.h
  8. 23 2
      applications/bt/bt_settings_app/bt_settings_app.c
  9. 12 1
      applications/bt/bt_settings_app/bt_settings_app.h
  10. 2 0
      applications/bt/bt_settings_app/scenes/bt_settings_scene_config.h
  11. 44 0
      applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c
  12. 43 0
      applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c
  13. 19 4
      applications/bt/bt_settings_app/scenes/bt_settings_scene_start.c
  14. 2 2
      applications/desktop/desktop_settings/scenes/desktop_settings_scene_favorite.c
  15. 2 2
      applications/desktop/desktop_settings/scenes/desktop_settings_scene_pincode_menu.c
  16. 1 1
      applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c
  17. 1 1
      applications/gpio/scenes/gpio_scene_start.c
  18. 1 1
      applications/gpio/scenes/gpio_scene_usb_uart_config.c
  19. 1 1
      applications/gui/modules/button_menu.c
  20. 1 1
      applications/gui/modules/button_menu.h
  21. 2 2
      applications/gui/modules/button_panel.c
  22. 1 1
      applications/gui/modules/button_panel.h
  23. 1 1
      applications/gui/modules/dialog_ex.c
  24. 2 2
      applications/gui/modules/dialog_ex.h
  25. 2 2
      applications/gui/modules/menu.c
  26. 1 1
      applications/gui/modules/menu.h
  27. 17 1
      applications/gui/modules/popup.c
  28. 6 0
      applications/gui/modules/popup.h
  29. 1 1
      applications/gui/modules/submenu.c
  30. 1 1
      applications/gui/modules/submenu.h
  31. 1 1
      applications/gui/modules/text_box.c
  32. 1 1
      applications/gui/modules/text_box.h
  33. 2 2
      applications/gui/modules/text_input.c
  34. 2 2
      applications/gui/modules/text_input.h
  35. 1 1
      applications/gui/modules/variable_item_list.c
  36. 1 1
      applications/gui/modules/variable_item_list.h
  37. 2 2
      applications/ibutton/scene/ibutton_scene_add_type.cpp
  38. 2 2
      applications/ibutton/scene/ibutton_scene_readed_key_menu.cpp
  39. 2 2
      applications/ibutton/scene/ibutton_scene_save_name.cpp
  40. 2 2
      applications/ibutton/scene/ibutton_scene_saved_key_menu.cpp
  41. 2 2
      applications/ibutton/scene/ibutton_scene_start.cpp
  42. 1 1
      applications/irda/scene/irda_app_scene_edit.cpp
  43. 1 1
      applications/irda/scene/irda_app_scene_edit_key_select.cpp
  44. 1 1
      applications/irda/scene/irda_app_scene_remote.cpp
  45. 1 1
      applications/irda/scene/irda_app_scene_start.cpp
  46. 1 1
      applications/irda/scene/irda_app_scene_universal.cpp
  47. 1 1
      applications/irda/scene/irda_app_scene_universal_common.cpp
  48. 1 1
      applications/loader/loader.c
  49. 1 1
      applications/nfc/scenes/nfc_scene_card_menu.c
  50. 1 1
      applications/nfc/scenes/nfc_scene_device_info.c
  51. 1 1
      applications/nfc/scenes/nfc_scene_mifare_ul_menu.c
  52. 1 1
      applications/nfc/scenes/nfc_scene_read_mifare_ul_success.c
  53. 1 1
      applications/nfc/scenes/nfc_scene_save_name.c
  54. 1 1
      applications/nfc/scenes/nfc_scene_saved_menu.c
  55. 1 1
      applications/nfc/scenes/nfc_scene_scripts_menu.c
  56. 1 1
      applications/nfc/scenes/nfc_scene_set_type.c
  57. 1 1
      applications/nfc/scenes/nfc_scene_start.c
  58. 1 1
      applications/power/power_settings_app/scenes/power_settings_scene_power_off.c
  59. 1 1
      applications/power/power_settings_app/scenes/power_settings_scene_reboot.c
  60. 1 1
      applications/power/power_settings_app/scenes/power_settings_scene_start.c
  61. 1 1
      applications/storage_settings/scenes/storage_settings_scene_start.c
  62. 1 1
      applications/subghz/scenes/subghz_scene_more_raw.c
  63. 1 1
      applications/subghz/scenes/subghz_scene_receiver.c
  64. 1 1
      applications/subghz/scenes/subghz_scene_receiver_config.c
  65. 1 1
      applications/subghz/scenes/subghz_scene_save_name.c
  66. 1 1
      applications/subghz/scenes/subghz_scene_saved_menu.c
  67. 1 1
      applications/subghz/scenes/subghz_scene_set_type.c
  68. 1 1
      applications/subghz/scenes/subghz_scene_start.c
  69. 1 1
      applications/subghz/scenes/subghz_scene_test.c
  70. 2 2
      applications/subghz/subghz_history.c
  71. 1 1
      applications/subghz/subghz_history.h
  72. 37 9
      firmware/targets/f6/ble_glue/gap.c
  73. 8 0
      firmware/targets/f6/ble_glue/gap.h
  74. 25 0
      firmware/targets/f6/furi_hal/furi_hal_bt.c
  75. 1 0
      firmware/targets/f6/target.mk
  76. 37 9
      firmware/targets/f7/ble_glue/gap.c
  77. 8 0
      firmware/targets/f7/ble_glue/gap.h
  78. 25 0
      firmware/targets/f7/furi_hal/furi_hal_bt.c
  79. 1 0
      firmware/targets/f7/target.mk
  80. 6 0
      firmware/targets/furi_hal_include/furi_hal_bt.h
  81. 1 1
      lib/app-scened-template/view_modules/submenu_vm.cpp
  82. 1 1
      lib/app-scened-template/view_modules/text_input_vm.cpp

+ 1 - 1
applications/archive/scenes/archive_scene_rename.c

@@ -75,5 +75,5 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) {
 void archive_scene_rename_on_exit(void* context) {
 void archive_scene_rename_on_exit(void* context) {
     ArchiveApp* archive = (ArchiveApp*)context;
     ArchiveApp* archive = (ArchiveApp*)context;
     // Clear view
     // Clear view
-    text_input_clean(archive->text_input);
+    text_input_reset(archive->text_input);
 }
 }

+ 53 - 17
applications/bt/bt_service/bt.c

@@ -2,7 +2,8 @@
 #include "battery_service.h"
 #include "battery_service.h"
 #include "bt_keys_storage.h"
 #include "bt_keys_storage.h"
 
 
-#include <applications/notification/notification_messages.h>
+#include <notification/notification_messages.h>
+#include <gui/elements.h>
 
 
 #define TAG "BtSrv"
 #define TAG "BtSrv"
 
 
@@ -29,17 +30,46 @@ static ViewPort* bt_statusbar_view_port_alloc(Bt* bt) {
     return statusbar_view_port;
     return statusbar_view_port;
 }
 }
 
 
-static void bt_pin_code_show_event_handler(Bt* bt, uint32_t pin) {
-    furi_assert(bt);
+static void bt_pin_code_view_port_draw_callback(Canvas* canvas, void* context) {
+    furi_assert(context);
+    Bt* bt = context;
+    char pin_code_info[24];
+    canvas_draw_icon(canvas, 0, 0, &I_BLE_Pairing_128x64);
+    snprintf(pin_code_info, sizeof(pin_code_info), "Pairing code\n%06ld", bt->pin_code);
+    elements_multiline_text_aligned(canvas, 64, 4, AlignCenter, AlignTop, pin_code_info);
+    elements_button_left(canvas, "Quit");
+}
+
+static void bt_pin_code_view_port_input_callback(InputEvent* event, void* context) {
+    furi_assert(context);
+    Bt* bt = context;
+    if(event->type == InputTypeShort) {
+        if(event->key == InputKeyLeft || event->key == InputKeyBack) {
+            view_port_enabled_set(bt->pin_code_view_port, false);
+        }
+    }
+}
+
+static ViewPort* bt_pin_code_view_port_alloc(Bt* bt) {
+    ViewPort* view_port = view_port_alloc();
+    view_port_draw_callback_set(view_port, bt_pin_code_view_port_draw_callback, bt);
+    view_port_input_callback_set(view_port, bt_pin_code_view_port_input_callback, bt);
+    view_port_enabled_set(view_port, false);
+    return view_port;
+}
+
+static void bt_pin_code_show(Bt* bt, uint32_t pin_code) {
+    bt->pin_code = pin_code;
     notification_message(bt->notification, &sequence_display_on);
     notification_message(bt->notification, &sequence_display_on);
-    string_t pin_str;
-    dialog_message_set_icon(bt->dialog_message, &I_BLE_Pairing_128x64, 0, 0);
-    string_init_printf(pin_str, "Pairing code\n%06d", pin);
-    dialog_message_set_text(
-        bt->dialog_message, string_get_cstr(pin_str), 64, 4, AlignCenter, AlignTop);
-    dialog_message_set_buttons(bt->dialog_message, "Quit", NULL, NULL);
-    dialog_message_show(bt->dialogs, bt->dialog_message);
-    string_clear(pin_str);
+    gui_view_port_send_to_front(bt->gui, bt->pin_code_view_port);
+    view_port_enabled_set(bt->pin_code_view_port, true);
+}
+
+static void bt_pin_code_hide(Bt* bt) {
+    bt->pin_code = 0;
+    if(view_port_is_enabled(bt->pin_code_view_port)) {
+        view_port_enabled_set(bt->pin_code_view_port, false);
+    }
 }
 }
 
 
 static bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) {
 static bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) {
@@ -84,11 +114,14 @@ Bt* bt_alloc() {
 
 
     // Setup statusbar view port
     // Setup statusbar view port
     bt->statusbar_view_port = bt_statusbar_view_port_alloc(bt);
     bt->statusbar_view_port = bt_statusbar_view_port_alloc(bt);
+    // Pin code view port
+    bt->pin_code_view_port = bt_pin_code_view_port_alloc(bt);
     // Notification
     // Notification
     bt->notification = furi_record_open("notification");
     bt->notification = furi_record_open("notification");
     // Gui
     // Gui
     bt->gui = furi_record_open("gui");
     bt->gui = furi_record_open("gui");
     gui_add_view_port(bt->gui, bt->statusbar_view_port, GuiLayerStatusBarLeft);
     gui_add_view_port(bt->gui, bt->statusbar_view_port, GuiLayerStatusBarLeft);
+    gui_add_view_port(bt->gui, bt->pin_code_view_port, GuiLayerFullscreen);
 
 
     // Dialogs
     // Dialogs
     bt->dialogs = furi_record_open("dialogs");
     bt->dialogs = furi_record_open("dialogs");
@@ -162,7 +195,7 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) {
     if(event.type == GapEventTypeConnected) {
     if(event.type == GapEventTypeConnected) {
         // Update status bar
         // Update status bar
         bt->status = BtStatusConnected;
         bt->status = BtStatusConnected;
-        BtMessage message = {.type = BtMessageTypeUpdateStatusbar};
+        BtMessage message = {.type = BtMessageTypeUpdateStatus};
         furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
         furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
         if(bt->profile == BtProfileSerial) {
         if(bt->profile == BtProfileSerial) {
             // Open RPC session
             // Open RPC session
@@ -192,12 +225,12 @@ static bool bt_on_gap_event_callback(GapEvent event, void* context) {
         ret = true;
         ret = true;
     } else if(event.type == GapEventTypeStartAdvertising) {
     } else if(event.type == GapEventTypeStartAdvertising) {
         bt->status = BtStatusAdvertising;
         bt->status = BtStatusAdvertising;
-        BtMessage message = {.type = BtMessageTypeUpdateStatusbar};
+        BtMessage message = {.type = BtMessageTypeUpdateStatus};
         furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
         furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
         ret = true;
         ret = true;
     } else if(event.type == GapEventTypeStopAdvertising) {
     } else if(event.type == GapEventTypeStopAdvertising) {
         bt->status = BtStatusOff;
         bt->status = BtStatusOff;
-        BtMessage message = {.type = BtMessageTypeUpdateStatusbar};
+        BtMessage message = {.type = BtMessageTypeUpdateStatus};
         furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
         furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
         ret = true;
         ret = true;
     } else if(event.type == GapEventTypePinCodeShow) {
     } else if(event.type == GapEventTypePinCodeShow) {
@@ -313,9 +346,10 @@ int32_t bt_srv() {
     BtMessage message;
     BtMessage message;
     while(1) {
     while(1) {
         furi_check(osMessageQueueGet(bt->message_queue, &message, NULL, osWaitForever) == osOK);
         furi_check(osMessageQueueGet(bt->message_queue, &message, NULL, osWaitForever) == osOK);
-        if(message.type == BtMessageTypeUpdateStatusbar) {
-            // Update statusbar
+        if(message.type == BtMessageTypeUpdateStatus) {
+            // Update view ports
             bt_statusbar_update(bt);
             bt_statusbar_update(bt);
+            bt_pin_code_hide(bt);
             if(bt->status_changed_cb) {
             if(bt->status_changed_cb) {
                 bt->status_changed_cb(bt->status, bt->status_changed_ctx);
                 bt->status_changed_cb(bt->status, bt->status_changed_ctx);
             }
             }
@@ -324,11 +358,13 @@ int32_t bt_srv() {
             furi_hal_bt_update_battery_level(message.data.battery_level);
             furi_hal_bt_update_battery_level(message.data.battery_level);
         } else if(message.type == BtMessageTypePinCodeShow) {
         } else if(message.type == BtMessageTypePinCodeShow) {
             // Display PIN code
             // Display PIN code
-            bt_pin_code_show_event_handler(bt, message.data.pin_code);
+            bt_pin_code_show(bt, message.data.pin_code);
         } else if(message.type == BtMessageTypeKeysStorageUpdated) {
         } else if(message.type == BtMessageTypeKeysStorageUpdated) {
             bt_save_key_storage(bt);
             bt_save_key_storage(bt);
         } else if(message.type == BtMessageTypeSetProfile) {
         } else if(message.type == BtMessageTypeSetProfile) {
             bt_change_profile(bt, &message);
             bt_change_profile(bt, &message);
+        } else if(message.type == BtMessageTypeForgetBondedDevices) {
+            bt_delete_key_storage(bt);
         }
         }
     }
     }
     return 0;
     return 0;

+ 7 - 0
applications/bt/bt_service/bt.h

@@ -41,6 +41,13 @@ bool bt_set_profile(Bt* bt, BtProfile profile);
  */
  */
 void bt_set_status_changed_callback(Bt* bt, BtStatusChangedCallback callback, void* context);
 void bt_set_status_changed_callback(Bt* bt, BtStatusChangedCallback callback, void* context);
 
 
+/** Forget bonded devices
+ * @note Leads to wipe ble key storage and deleting bt.keys
+ *
+ * @param bt        Bt instance
+ */
+void bt_forget_bonded_devices(Bt* bt);
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 6 - 0
applications/bt/bt_service/bt_api.c

@@ -20,3 +20,9 @@ void bt_set_status_changed_callback(Bt* bt, BtStatusChangedCallback callback, vo
     bt->status_changed_cb = callback;
     bt->status_changed_cb = callback;
     bt->status_changed_ctx = context;
     bt->status_changed_ctx = context;
 }
 }
+
+void bt_forget_bonded_devices(Bt* bt) {
+    furi_assert(bt);
+    BtMessage message = {.type = BtMessageTypeForgetBondedDevices};
+    furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
+}

+ 4 - 1
applications/bt/bt_service/bt_i.h

@@ -19,11 +19,12 @@
 #define BT_API_UNLOCK_EVENT (1UL << 0)
 #define BT_API_UNLOCK_EVENT (1UL << 0)
 
 
 typedef enum {
 typedef enum {
-    BtMessageTypeUpdateStatusbar,
+    BtMessageTypeUpdateStatus,
     BtMessageTypeUpdateBatteryLevel,
     BtMessageTypeUpdateBatteryLevel,
     BtMessageTypePinCodeShow,
     BtMessageTypePinCodeShow,
     BtMessageTypeKeysStorageUpdated,
     BtMessageTypeKeysStorageUpdated,
     BtMessageTypeSetProfile,
     BtMessageTypeSetProfile,
+    BtMessageTypeForgetBondedDevices,
 } BtMessageType;
 } BtMessageType;
 
 
 typedef union {
 typedef union {
@@ -49,6 +50,8 @@ struct Bt {
     NotificationApp* notification;
     NotificationApp* notification;
     Gui* gui;
     Gui* gui;
     ViewPort* statusbar_view_port;
     ViewPort* statusbar_view_port;
+    ViewPort* pin_code_view_port;
+    uint32_t pin_code;
     DialogsApp* dialogs;
     DialogsApp* dialogs;
     DialogMessage* dialog_message;
     DialogMessage* dialog_message;
     Power* power;
     Power* power;

+ 13 - 0
applications/bt/bt_service/bt_keys_storage.c

@@ -39,3 +39,16 @@ bool bt_save_key_storage(Bt* bt) {
     file_worker_free(file_worker);
     file_worker_free(file_worker);
     return file_saved;
     return file_saved;
 }
 }
+
+bool bt_delete_key_storage(Bt* bt) {
+    furi_assert(bt);
+    bool delete_succeed = false;
+
+    furi_hal_bt_stop_advertising();
+    delete_succeed = furi_hal_bt_clear_white_list();
+    if(bt->bt_settings.enabled) {
+        furi_hal_bt_start_advertising();
+    }
+
+    return delete_succeed;
+}

+ 2 - 0
applications/bt/bt_service/bt_keys_storage.h

@@ -5,3 +5,5 @@
 bool bt_load_key_storage(Bt* bt);
 bool bt_load_key_storage(Bt* bt);
 
 
 bool bt_save_key_storage(Bt* bt);
 bool bt_save_key_storage(Bt* bt);
+
+bool bt_delete_key_storage(Bt* bt);

+ 23 - 2
applications/bt/bt_settings_app/bt_settings_app.c

@@ -18,7 +18,9 @@ BtSettingsApp* bt_settings_app_alloc() {
     // Load settings
     // Load settings
     bt_settings_load(&app->settings);
     bt_settings_load(&app->settings);
     app->gui = furi_record_open("gui");
     app->gui = furi_record_open("gui");
+    app->bt = furi_record_open("bt");
 
 
+    // View Dispatcher and Scene Manager
     app->view_dispatcher = view_dispatcher_alloc();
     app->view_dispatcher = view_dispatcher_alloc();
     app->scene_manager = scene_manager_alloc(&bt_settings_scene_handlers, app);
     app->scene_manager = scene_manager_alloc(&bt_settings_scene_handlers, app);
     view_dispatcher_enable_queue(app->view_dispatcher);
     view_dispatcher_enable_queue(app->view_dispatcher);
@@ -31,26 +33,45 @@ BtSettingsApp* bt_settings_app_alloc() {
 
 
     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
 
 
+    // Gui Modules
     app->var_item_list = variable_item_list_alloc();
     app->var_item_list = variable_item_list_alloc();
     view_dispatcher_add_view(
     view_dispatcher_add_view(
         app->view_dispatcher,
         app->view_dispatcher,
         BtSettingsAppViewVarItemList,
         BtSettingsAppViewVarItemList,
         variable_item_list_get_view(app->var_item_list));
         variable_item_list_get_view(app->var_item_list));
 
 
+    app->dialog = dialog_ex_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher, BtSettingsAppViewDialog, dialog_ex_get_view(app->dialog));
+
+    app->popup = popup_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher, BtSettingsAppViewPopup, popup_get_view(app->popup));
+
+    // Set first scene
     scene_manager_next_scene(app->scene_manager, BtSettingsAppSceneStart);
     scene_manager_next_scene(app->scene_manager, BtSettingsAppSceneStart);
     return app;
     return app;
 }
 }
 
 
 void bt_settings_app_free(BtSettingsApp* app) {
 void bt_settings_app_free(BtSettingsApp* app) {
     furi_assert(app);
     furi_assert(app);
-    // Variable item list
+    // Gui modules
     view_dispatcher_remove_view(app->view_dispatcher, BtSettingsAppViewVarItemList);
     view_dispatcher_remove_view(app->view_dispatcher, BtSettingsAppViewVarItemList);
     variable_item_list_free(app->var_item_list);
     variable_item_list_free(app->var_item_list);
-    // View dispatcher
+
+    view_dispatcher_remove_view(app->view_dispatcher, BtSettingsAppViewDialog);
+    dialog_ex_free(app->dialog);
+
+    view_dispatcher_remove_view(app->view_dispatcher, BtSettingsAppViewPopup);
+    popup_free(app->popup);
+
+    // View Dispatcher and Scene Manager
     view_dispatcher_free(app->view_dispatcher);
     view_dispatcher_free(app->view_dispatcher);
     scene_manager_free(app->scene_manager);
     scene_manager_free(app->scene_manager);
+
     // Records
     // Records
     furi_record_close("gui");
     furi_record_close("gui");
+    furi_record_close("bt");
     free(app);
     free(app);
 }
 }
 
 

+ 12 - 1
applications/bt/bt_settings_app/bt_settings_app.h

@@ -1,22 +1,33 @@
 #pragma once
 #pragma once
 
 
 #include <furi.h>
 #include <furi.h>
+#include <bt/bt_service/bt.h>
 #include <gui/gui.h>
 #include <gui/gui.h>
 #include <gui/view.h>
 #include <gui/view.h>
 #include <gui/view_dispatcher.h>
 #include <gui/view_dispatcher.h>
 #include <gui/scene_manager.h>
 #include <gui/scene_manager.h>
 
 
 #include <gui/modules/variable_item_list.h>
 #include <gui/modules/variable_item_list.h>
+#include <gui/modules/dialog_ex.h>
+#include <gui/modules/popup.h>
 
 
 #include "../bt_settings.h"
 #include "../bt_settings.h"
 #include "scenes/bt_settings_scene.h"
 #include "scenes/bt_settings_scene.h"
 
 
 typedef struct {
 typedef struct {
     BtSettings settings;
     BtSettings settings;
+    Bt* bt;
     Gui* gui;
     Gui* gui;
     SceneManager* scene_manager;
     SceneManager* scene_manager;
     ViewDispatcher* view_dispatcher;
     ViewDispatcher* view_dispatcher;
+
     VariableItemList* var_item_list;
     VariableItemList* var_item_list;
+    DialogEx* dialog;
+    Popup* popup;
 } BtSettingsApp;
 } BtSettingsApp;
 
 
-typedef enum { BtSettingsAppViewVarItemList } BtSettingsAppView;
+typedef enum {
+    BtSettingsAppViewVarItemList,
+    BtSettingsAppViewDialog,
+    BtSettingsAppViewPopup,
+} BtSettingsAppView;

+ 2 - 0
applications/bt/bt_settings_app/scenes/bt_settings_scene_config.h

@@ -1 +1,3 @@
 ADD_SCENE(bt_settings, start, Start)
 ADD_SCENE(bt_settings, start, Start)
+ADD_SCENE(bt_settings, forget_dev_confirm, ForgetDevConfirm)
+ADD_SCENE(bt_settings, forget_dev_success, ForgetDevSuccess)

+ 44 - 0
applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c

@@ -0,0 +1,44 @@
+#include "../bt_settings_app.h"
+#include "furi_hal_bt.h"
+
+void bt_settings_scene_forget_dev_confirm_dialog_callback(DialogExResult result, void* context) {
+    furi_assert(context);
+    BtSettingsApp* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, result);
+}
+
+void bt_settings_scene_forget_dev_confirm_on_enter(void* context) {
+    BtSettingsApp* app = context;
+    DialogEx* dialog = app->dialog;
+    dialog_ex_set_header(dialog, "Unpair all devices?", 64, 3, AlignCenter, AlignTop);
+    dialog_ex_set_text(
+        dialog, "All previous pairings\nwill be lost.", 64, 22, AlignCenter, AlignTop);
+    dialog_ex_set_left_button_text(dialog, "Back");
+    dialog_ex_set_right_button_text(dialog, "Unpair");
+    dialog_ex_set_context(dialog, app);
+    dialog_ex_set_result_callback(dialog, bt_settings_scene_forget_dev_confirm_dialog_callback);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, BtSettingsAppViewDialog);
+}
+
+bool bt_settings_scene_forget_dev_confirm_on_event(void* context, SceneManagerEvent event) {
+    BtSettingsApp* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == DialogExResultLeft) {
+            consumed = scene_manager_previous_scene(app->scene_manager);
+        } else if(event.event == DialogExResultRight) {
+            bt_forget_bonded_devices(app->bt);
+            scene_manager_next_scene(app->scene_manager, BtSettingsAppSceneForgetDevSuccess);
+            consumed = true;
+        }
+    }
+
+    return consumed;
+}
+
+void bt_settings_scene_forget_dev_confirm_on_exit(void* context) {
+    BtSettingsApp* app = context;
+    dialog_ex_reset(app->dialog);
+}

+ 43 - 0
applications/bt/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c

@@ -0,0 +1,43 @@
+#include "../bt_settings_app.h"
+#include "furi_hal_bt.h"
+
+#define SCENE_FORGET_DEV_SUCCESS_CUSTOM_EVENT (0UL)
+
+void bt_settings_app_scene_forget_dev_success_popup_callback(void* context) {
+    BtSettingsApp* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_FORGET_DEV_SUCCESS_CUSTOM_EVENT);
+}
+
+void bt_settings_scene_forget_dev_success_on_enter(void* context) {
+    BtSettingsApp* app = context;
+    Popup* popup = app->popup;
+
+    popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
+    popup_set_header(popup, "Done", 14, 15, AlignLeft, AlignTop);
+    popup_set_timeout(popup, 1500);
+    popup_set_context(popup, app);
+    popup_set_callback(popup, bt_settings_app_scene_forget_dev_success_popup_callback);
+    popup_enable_timeout(popup);
+    view_dispatcher_switch_to_view(app->view_dispatcher, BtSettingsAppViewPopup);
+}
+
+bool bt_settings_scene_forget_dev_success_on_event(void* context, SceneManagerEvent event) {
+    BtSettingsApp* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == SCENE_FORGET_DEV_SUCCESS_CUSTOM_EVENT) {
+            if(scene_manager_has_previous_scene(app->scene_manager, BtSettingsAppSceneStart)) {
+                consumed = scene_manager_search_and_switch_to_previous_scene(
+                    app->scene_manager, BtSettingsAppSceneStart);
+            }
+        }
+    }
+
+    return consumed;
+}
+
+void bt_settings_scene_forget_dev_success_on_exit(void* context) {
+    BtSettingsApp* app = context;
+    popup_reset(app->popup);
+}

+ 19 - 4
applications/bt/bt_settings_app/scenes/bt_settings_scene_start.c

@@ -1,6 +1,8 @@
 #include "../bt_settings_app.h"
 #include "../bt_settings_app.h"
 #include "furi_hal_bt.h"
 #include "furi_hal_bt.h"
 
 
+#define SCENE_START_FORGET_DEV_SELECTED_EVENT (10UL)
+
 enum BtSetting {
 enum BtSetting {
     BtSettingOff,
     BtSettingOff,
     BtSettingOn,
     BtSettingOn,
@@ -8,8 +10,8 @@ enum BtSetting {
 };
 };
 
 
 const char* const bt_settings_text[BtSettingNum] = {
 const char* const bt_settings_text[BtSettingNum] = {
-    "Off",
-    "On",
+    "OFF",
+    "ON",
 };
 };
 
 
 static void bt_settings_scene_start_var_list_change_callback(VariableItem* item) {
 static void bt_settings_scene_start_var_list_change_callback(VariableItem* item) {
@@ -20,6 +22,12 @@ static void bt_settings_scene_start_var_list_change_callback(VariableItem* item)
     view_dispatcher_send_custom_event(app->view_dispatcher, index);
     view_dispatcher_send_custom_event(app->view_dispatcher, index);
 }
 }
 
 
+static void bt_settings_scene_start_var_list_enter_callback(void* context, uint32_t index) {
+    furi_assert(context);
+    BtSettingsApp* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_START_FORGET_DEV_SELECTED_EVENT);
+}
+
 void bt_settings_scene_start_on_enter(void* context) {
 void bt_settings_scene_start_on_enter(void* context) {
     BtSettingsApp* app = context;
     BtSettingsApp* app = context;
     VariableItemList* var_item_list = app->var_item_list;
     VariableItemList* var_item_list = app->var_item_list;
@@ -40,6 +48,9 @@ void bt_settings_scene_start_on_enter(void* context) {
             variable_item_set_current_value_index(item, BtSettingOff);
             variable_item_set_current_value_index(item, BtSettingOff);
             variable_item_set_current_value_text(item, bt_settings_text[BtSettingOff]);
             variable_item_set_current_value_text(item, bt_settings_text[BtSettingOff]);
         }
         }
+        variable_item_list_add(var_item_list, "Forget all paired devices", 1, NULL, NULL);
+        variable_item_list_set_enter_callback(
+            var_item_list, bt_settings_scene_start_var_list_enter_callback, app);
     } else {
     } else {
         item = variable_item_list_add(var_item_list, "Bluetooth", 1, NULL, NULL);
         item = variable_item_list_add(var_item_list, "Bluetooth", 1, NULL, NULL);
         variable_item_set_current_value_text(item, "Broken");
         variable_item_set_current_value_text(item, "Broken");
@@ -56,16 +67,20 @@ bool bt_settings_scene_start_on_event(void* context, SceneManagerEvent event) {
         if(event.event == BtSettingOn) {
         if(event.event == BtSettingOn) {
             furi_hal_bt_start_advertising();
             furi_hal_bt_start_advertising();
             app->settings.enabled = true;
             app->settings.enabled = true;
+            consumed = true;
         } else if(event.event == BtSettingOff) {
         } else if(event.event == BtSettingOff) {
             app->settings.enabled = false;
             app->settings.enabled = false;
             furi_hal_bt_stop_advertising();
             furi_hal_bt_stop_advertising();
+            consumed = true;
+        } else if(event.event == SCENE_START_FORGET_DEV_SELECTED_EVENT) {
+            scene_manager_next_scene(app->scene_manager, BtSettingsAppSceneForgetDevConfirm);
+            consumed = true;
         }
         }
-        consumed = true;
     }
     }
     return consumed;
     return consumed;
 }
 }
 
 
 void bt_settings_scene_start_on_exit(void* context) {
 void bt_settings_scene_start_on_exit(void* context) {
     BtSettingsApp* app = context;
     BtSettingsApp* app = context;
-    variable_item_list_clean(app->var_item_list);
+    variable_item_list_reset(app->var_item_list);
 }
 }

+ 2 - 2
applications/desktop/desktop_settings/scenes/desktop_settings_scene_favorite.c

@@ -10,7 +10,7 @@ static void desktop_settings_scene_favorite_submenu_callback(void* context, uint
 void desktop_settings_scene_favorite_on_enter(void* context) {
 void desktop_settings_scene_favorite_on_enter(void* context) {
     DesktopSettingsApp* app = context;
     DesktopSettingsApp* app = context;
     Submenu* submenu = app->submenu;
     Submenu* submenu = app->submenu;
-    submenu_clean(submenu);
+    submenu_reset(submenu);
 
 
     for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
     for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
         submenu_add_item(
         submenu_add_item(
@@ -45,5 +45,5 @@ bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent e
 void desktop_settings_scene_favorite_on_exit(void* context) {
 void desktop_settings_scene_favorite_on_exit(void* context) {
     DesktopSettingsApp* app = context;
     DesktopSettingsApp* app = context;
     SAVE_DESKTOP_SETTINGS(&app->settings);
     SAVE_DESKTOP_SETTINGS(&app->settings);
-    submenu_clean(app->submenu);
+    submenu_reset(app->submenu);
 }
 }

+ 2 - 2
applications/desktop/desktop_settings/scenes/desktop_settings_scene_pincode_menu.c

@@ -9,7 +9,7 @@ static void desktop_settings_scene_pincode_menu_submenu_callback(void* context,
 void desktop_settings_scene_pincode_menu_on_enter(void* context) {
 void desktop_settings_scene_pincode_menu_on_enter(void* context) {
     DesktopSettingsApp* app = context;
     DesktopSettingsApp* app = context;
     Submenu* submenu = app->submenu;
     Submenu* submenu = app->submenu;
-    submenu_clean(submenu);
+    submenu_reset(submenu);
 
 
     if(!app->settings.pincode.length) {
     if(!app->settings.pincode.length) {
         submenu_add_item(
         submenu_add_item(
@@ -74,5 +74,5 @@ bool desktop_settings_scene_pincode_menu_on_event(void* context, SceneManagerEve
 
 
 void desktop_settings_scene_pincode_menu_on_exit(void* context) {
 void desktop_settings_scene_pincode_menu_on_exit(void* context) {
     DesktopSettingsApp* app = context;
     DesktopSettingsApp* app = context;
-    submenu_clean(app->submenu);
+    submenu_reset(app->submenu);
 }
 }

+ 1 - 1
applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c

@@ -53,5 +53,5 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even
 
 
 void desktop_settings_scene_start_on_exit(void* context) {
 void desktop_settings_scene_start_on_exit(void* context) {
     DesktopSettingsApp* app = context;
     DesktopSettingsApp* app = context;
-    submenu_clean(app->submenu);
+    submenu_reset(app->submenu);
 }
 }

+ 1 - 1
applications/gpio/scenes/gpio_scene_start.c

@@ -95,5 +95,5 @@ bool gpio_scene_start_on_event(void* context, SceneManagerEvent event) {
 
 
 void gpio_scene_start_on_exit(void* context) {
 void gpio_scene_start_on_exit(void* context) {
     GpioApp* app = context;
     GpioApp* app = context;
-    variable_item_list_clean(app->var_item_list);
+    variable_item_list_reset(app->var_item_list);
 }
 }

+ 1 - 1
applications/gpio/scenes/gpio_scene_usb_uart_config.c

@@ -135,6 +135,6 @@ void gpio_scene_usb_uart_cfg_on_exit(void* context) {
         app->scene_manager,
         app->scene_manager,
         GpioAppViewUsbUartCfg,
         GpioAppViewUsbUartCfg,
         variable_item_list_get_selected_item_index(app->var_item_list));
         variable_item_list_get_selected_item_index(app->var_item_list));
-    variable_item_list_clean(app->var_item_list);
+    variable_item_list_reset(app->var_item_list);
     free(cfg_set);
     free(cfg_set);
 }
 }

+ 1 - 1
applications/gui/modules/button_menu.c

@@ -241,7 +241,7 @@ View* button_menu_get_view(ButtonMenu* button_menu) {
     return button_menu->view;
     return button_menu->view;
 }
 }
 
 
-void button_menu_clean(ButtonMenu* button_menu) {
+void button_menu_reset(ButtonMenu* button_menu) {
     furi_assert(button_menu);
     furi_assert(button_menu);
 
 
     with_view_model(
     with_view_model(

+ 1 - 1
applications/gui/modules/button_menu.h

@@ -39,7 +39,7 @@ View* button_menu_get_view(ButtonMenu* button_menu);
  *
  *
  * @param      button_menu  ButtonMenu instance
  * @param      button_menu  ButtonMenu instance
  */
  */
-void button_menu_clean(ButtonMenu* button_menu);
+void button_menu_reset(ButtonMenu* button_menu);
 
 
 /** Add item to button menu instance
 /** Add item to button menu instance
  *
  *

+ 2 - 2
applications/gui/modules/button_panel.c

@@ -112,7 +112,7 @@ void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t re
 void button_panel_free(ButtonPanel* button_panel) {
 void button_panel_free(ButtonPanel* button_panel) {
     furi_assert(button_panel);
     furi_assert(button_panel);
 
 
-    button_panel_clean(button_panel);
+    button_panel_reset(button_panel);
 
 
     with_view_model(
     with_view_model(
         button_panel->view, (ButtonPanelModel * model) {
         button_panel->view, (ButtonPanelModel * model) {
@@ -125,7 +125,7 @@ void button_panel_free(ButtonPanel* button_panel) {
     free(button_panel);
     free(button_panel);
 }
 }
 
 
-void button_panel_clean(ButtonPanel* button_panel) {
+void button_panel_reset(ButtonPanel* button_panel) {
     furi_assert(button_panel);
     furi_assert(button_panel);
 
 
     with_view_model(
     with_view_model(

+ 1 - 1
applications/gui/modules/button_panel.h

@@ -39,7 +39,7 @@ void button_panel_free(ButtonPanel* button_panel);
  *
  *
  * @param      button_panel  ButtonPanel instance
  * @param      button_panel  ButtonPanel instance
  */
  */
-void button_panel_clean(ButtonPanel* button_panel);
+void button_panel_reset(ButtonPanel* button_panel);
 
 
 /** Reserve space for adding items.
 /** Reserve space for adding items.
  *
  *

+ 1 - 1
applications/gui/modules/dialog_ex.c

@@ -244,7 +244,7 @@ void dialog_ex_set_right_button_text(DialogEx* dialog_ex, const char* text) {
         });
         });
 }
 }
 
 
-void dialog_ex_clean(DialogEx* dialog_ex) {
+void dialog_ex_reset(DialogEx* dialog_ex) {
     furi_assert(dialog_ex);
     furi_assert(dialog_ex);
     TextElement clean_text_el = {
     TextElement clean_text_el = {
         .text = NULL, .x = 0, .y = 0, .horizontal = AlignLeft, .vertical = AlignLeft};
         .text = NULL, .x = 0, .y = 0, .horizontal = AlignLeft, .vertical = AlignLeft};

+ 2 - 2
applications/gui/modules/dialog_ex.h

@@ -143,8 +143,8 @@ void dialog_ex_set_right_button_text(DialogEx* dialog_ex, const char* text);
  *
  *
  * @param      dialog_ex  DialogEx instance
  * @param      dialog_ex  DialogEx instance
  */
  */
-void dialog_ex_clean(DialogEx* dialog_ex);
+void dialog_ex_reset(DialogEx* dialog_ex);
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
-#endif
+#endif

+ 2 - 2
applications/gui/modules/menu.c

@@ -146,7 +146,7 @@ Menu* menu_alloc() {
 
 
 void menu_free(Menu* menu) {
 void menu_free(Menu* menu) {
     furi_assert(menu);
     furi_assert(menu);
-    menu_clean(menu);
+    menu_reset(menu);
     view_free(menu->view);
     view_free(menu->view);
     free(menu);
     free(menu);
 }
 }
@@ -180,7 +180,7 @@ void menu_add_item(
         });
         });
 }
 }
 
 
-void menu_clean(Menu* menu) {
+void menu_reset(Menu* menu) {
     furi_assert(menu);
     furi_assert(menu);
     with_view_model(
     with_view_model(
         menu->view, (MenuModel * model) {
         menu->view, (MenuModel * model) {

+ 1 - 1
applications/gui/modules/menu.h

@@ -59,7 +59,7 @@ void menu_add_item(
  *
  *
  * @param      menu  Menu instance
  * @param      menu  Menu instance
  */
  */
-void menu_clean(Menu* menu);
+void menu_reset(Menu* menu);
 
 
 /** Set current menu item
 /** Set current menu item
  *
  *

+ 17 - 1
applications/gui/modules/popup.c

@@ -226,4 +226,20 @@ void popup_enable_timeout(Popup* popup) {
 
 
 void popup_disable_timeout(Popup* popup) {
 void popup_disable_timeout(Popup* popup) {
     popup->timer_enabled = false;
     popup->timer_enabled = false;
-}
+}
+
+void popup_reset(Popup* popup) {
+    furi_assert(popup);
+
+    with_view_model(
+        popup->view, (PopupModel * model) {
+            memset(&model->header, 0, sizeof(model->header));
+            memset(&model->text, 0, sizeof(model->text));
+            memset(&model->icon, 0, sizeof(model->icon));
+            return false;
+        });
+    popup->callback = NULL;
+    popup->context = NULL;
+    popup->timer_enabled = false;
+    popup->timer_period_in_ms = 0;
+}

+ 6 - 0
applications/gui/modules/popup.h

@@ -123,6 +123,12 @@ void popup_enable_timeout(Popup* popup);
  */
  */
 void popup_disable_timeout(Popup* popup);
 void popup_disable_timeout(Popup* popup);
 
 
+/** Reset popup instance state
+ *
+ * @param       popup Popup instance
+ */
+void popup_reset(Popup* popup);
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 1 - 1
applications/gui/modules/submenu.c

@@ -177,7 +177,7 @@ void submenu_add_item(
         });
         });
 }
 }
 
 
-void submenu_clean(Submenu* submenu) {
+void submenu_reset(Submenu* submenu) {
     furi_assert(submenu);
     furi_assert(submenu);
 
 
     with_view_model(
     with_view_model(

+ 1 - 1
applications/gui/modules/submenu.h

@@ -57,7 +57,7 @@ void submenu_add_item(
  *
  *
  * @param      submenu  Submenu instance
  * @param      submenu  Submenu instance
  */
  */
-void submenu_clean(Submenu* submenu);
+void submenu_reset(Submenu* submenu);
 
 
 /** Set submenu item selector
 /** Set submenu item selector
  *
  *

+ 1 - 1
applications/gui/modules/text_box.c

@@ -164,7 +164,7 @@ View* text_box_get_view(TextBox* text_box) {
     return text_box->view;
     return text_box->view;
 }
 }
 
 
-void text_box_clean(TextBox* text_box) {
+void text_box_reset(TextBox* text_box) {
     furi_assert(text_box);
     furi_assert(text_box);
 
 
     with_view_model(
     with_view_model(

+ 1 - 1
applications/gui/modules/text_box.h

@@ -44,7 +44,7 @@ View* text_box_get_view(TextBox* text_box);
  *
  *
  * @param      text_box  TextBox instance
  * @param      text_box  TextBox instance
  */
  */
-void text_box_clean(TextBox* text_box);
+void text_box_reset(TextBox* text_box);
 
 
 /** Set text for text_box
 /** Set text for text_box
  *
  *

+ 2 - 2
applications/gui/modules/text_input.c

@@ -425,7 +425,7 @@ TextInput* text_input_alloc() {
             return false;
             return false;
         });
         });
 
 
-    text_input_clean(text_input);
+    text_input_reset(text_input);
 
 
     return text_input;
     return text_input;
 }
 }
@@ -450,7 +450,7 @@ void text_input_free(TextInput* text_input) {
     free(text_input);
     free(text_input);
 }
 }
 
 
-void text_input_clean(TextInput* text_input) {
+void text_input_reset(TextInput* text_input) {
     furi_assert(text_input);
     furi_assert(text_input);
     with_view_model(
     with_view_model(
         text_input->view, (TextInputModel * model) {
         text_input->view, (TextInputModel * model) {

+ 2 - 2
applications/gui/modules/text_input.h

@@ -36,7 +36,7 @@ void text_input_free(TextInput* text_input);
  *
  *
  * @param      text_input  Text input instance
  * @param      text_input  Text input instance
  */
  */
-void text_input_clean(TextInput* text_input);
+void text_input_reset(TextInput* text_input);
 
 
 /** Get text input view
 /** Get text input view
  *
  *
@@ -84,4 +84,4 @@ void text_input_set_header_text(TextInput* text_input, const char* text);
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
-#endif
+#endif

+ 1 - 1
applications/gui/modules/variable_item_list.c

@@ -312,7 +312,7 @@ void variable_item_list_free(VariableItemList* variable_item_list) {
     free(variable_item_list);
     free(variable_item_list);
 }
 }
 
 
-void variable_item_list_clean(VariableItemList* variable_item_list) {
+void variable_item_list_reset(VariableItemList* variable_item_list) {
     furi_assert(variable_item_list);
     furi_assert(variable_item_list);
 
 
     with_view_model(
     with_view_model(

+ 1 - 1
applications/gui/modules/variable_item_list.h

@@ -32,7 +32,7 @@ void variable_item_list_free(VariableItemList* variable_item_list);
  *
  *
  * @param      variable_item_list  VariableItemList instance
  * @param      variable_item_list  VariableItemList instance
  */
  */
-void variable_item_list_clean(VariableItemList* variable_item_list);
+void variable_item_list_reset(VariableItemList* variable_item_list);
 
 
 /** Get VariableItemList View instance
 /** Get VariableItemList View instance
  *
  *

+ 2 - 2
applications/ibutton/scene/ibutton_scene_add_type.cpp

@@ -52,7 +52,7 @@ void iButtonSceneAddType::on_exit(iButtonApp* app) {
     iButtonAppViewManager* view = app->get_view_manager();
     iButtonAppViewManager* view = app->get_view_manager();
     Submenu* submenu = view->get_submenu();
     Submenu* submenu = view->get_submenu();
 
 
-    submenu_clean(submenu);
+    submenu_reset(submenu);
 }
 }
 
 
 void iButtonSceneAddType::submenu_callback(void* context, uint32_t index) {
 void iButtonSceneAddType::submenu_callback(void* context, uint32_t index) {
@@ -63,4 +63,4 @@ void iButtonSceneAddType::submenu_callback(void* context, uint32_t index) {
     event.payload.menu_index = index;
     event.payload.menu_index = index;
 
 
     app->get_view_manager()->send_event(&event);
     app->get_view_manager()->send_event(&event);
-}
+}

+ 2 - 2
applications/ibutton/scene/ibutton_scene_readed_key_menu.cpp

@@ -57,7 +57,7 @@ void iButtonSceneReadedKeyMenu::on_exit(iButtonApp* app) {
     iButtonAppViewManager* view = app->get_view_manager();
     iButtonAppViewManager* view = app->get_view_manager();
     Submenu* submenu = view->get_submenu();
     Submenu* submenu = view->get_submenu();
 
 
-    submenu_clean(submenu);
+    submenu_reset(submenu);
 }
 }
 
 
 void iButtonSceneReadedKeyMenu::submenu_callback(void* context, uint32_t index) {
 void iButtonSceneReadedKeyMenu::submenu_callback(void* context, uint32_t index) {
@@ -68,4 +68,4 @@ void iButtonSceneReadedKeyMenu::submenu_callback(void* context, uint32_t index)
     event.payload.menu_index = index;
     event.payload.menu_index = index;
 
 
     app->get_view_manager()->send_event(&event);
     app->get_view_manager()->send_event(&event);
-}
+}

+ 2 - 2
applications/ibutton/scene/ibutton_scene_save_name.cpp

@@ -48,7 +48,7 @@ bool iButtonSceneSaveName::on_event(iButtonApp* app, iButtonEvent* event) {
 
 
 void iButtonSceneSaveName::on_exit(iButtonApp* app) {
 void iButtonSceneSaveName::on_exit(iButtonApp* app) {
     TextInput* text_input = app->get_view_manager()->get_text_input();
     TextInput* text_input = app->get_view_manager()->get_text_input();
-    text_input_clean(text_input);
+    text_input_reset(text_input);
 }
 }
 
 
 void iButtonSceneSaveName::text_input_callback(void* context) {
 void iButtonSceneSaveName::text_input_callback(void* context) {
@@ -58,4 +58,4 @@ void iButtonSceneSaveName::text_input_callback(void* context) {
     event.type = iButtonEvent::Type::EventTypeTextEditResult;
     event.type = iButtonEvent::Type::EventTypeTextEditResult;
 
 
     app->get_view_manager()->send_event(&event);
     app->get_view_manager()->send_event(&event);
-}
+}

+ 2 - 2
applications/ibutton/scene/ibutton_scene_saved_key_menu.cpp

@@ -59,7 +59,7 @@ void iButtonSceneSavedKeyMenu::on_exit(iButtonApp* app) {
     iButtonAppViewManager* view = app->get_view_manager();
     iButtonAppViewManager* view = app->get_view_manager();
     Submenu* submenu = view->get_submenu();
     Submenu* submenu = view->get_submenu();
 
 
-    submenu_clean(submenu);
+    submenu_reset(submenu);
 }
 }
 
 
 void iButtonSceneSavedKeyMenu::submenu_callback(void* context, uint32_t index) {
 void iButtonSceneSavedKeyMenu::submenu_callback(void* context, uint32_t index) {
@@ -70,4 +70,4 @@ void iButtonSceneSavedKeyMenu::submenu_callback(void* context, uint32_t index) {
     event.payload.menu_index = index;
     event.payload.menu_index = index;
 
 
     app->get_view_manager()->send_event(&event);
     app->get_view_manager()->send_event(&event);
-}
+}

+ 2 - 2
applications/ibutton/scene/ibutton_scene_start.cpp

@@ -49,7 +49,7 @@ void iButtonSceneStart::on_exit(iButtonApp* app) {
     iButtonAppViewManager* view = app->get_view_manager();
     iButtonAppViewManager* view = app->get_view_manager();
     Submenu* submenu = view->get_submenu();
     Submenu* submenu = view->get_submenu();
 
 
-    submenu_clean(submenu);
+    submenu_reset(submenu);
 }
 }
 
 
 void iButtonSceneStart::submenu_callback(void* context, uint32_t index) {
 void iButtonSceneStart::submenu_callback(void* context, uint32_t index) {
@@ -60,4 +60,4 @@ void iButtonSceneStart::submenu_callback(void* context, uint32_t index) {
     event.payload.menu_index = index;
     event.payload.menu_index = index;
 
 
     app->get_view_manager()->send_event(&event);
     app->get_view_manager()->send_event(&event);
-}
+}

+ 1 - 1
applications/irda/scene/irda_app_scene_edit.cpp

@@ -75,5 +75,5 @@ void IrdaAppSceneEdit::on_exit(IrdaApp* app) {
     IrdaAppViewManager* view_manager = app->get_view_manager();
     IrdaAppViewManager* view_manager = app->get_view_manager();
     Submenu* submenu = view_manager->get_submenu();
     Submenu* submenu = view_manager->get_submenu();
 
 
-    submenu_clean(submenu);
+    submenu_reset(submenu);
 }
 }

+ 1 - 1
applications/irda/scene/irda_app_scene_edit_key_select.cpp

@@ -53,5 +53,5 @@ void IrdaAppSceneEditKeySelect::on_exit(IrdaApp* app) {
     IrdaAppViewManager* view_manager = app->get_view_manager();
     IrdaAppViewManager* view_manager = app->get_view_manager();
     Submenu* submenu = view_manager->get_submenu();
     Submenu* submenu = view_manager->get_submenu();
 
 
-    submenu_clean(submenu);
+    submenu_reset(submenu);
 }
 }

+ 1 - 1
applications/irda/scene/irda_app_scene_remote.cpp

@@ -132,5 +132,5 @@ void IrdaAppSceneRemote::on_exit(IrdaApp* app) {
     IrdaAppViewManager* view_manager = app->get_view_manager();
     IrdaAppViewManager* view_manager = app->get_view_manager();
     ButtonMenu* button_menu = view_manager->get_button_menu();
     ButtonMenu* button_menu = view_manager->get_button_menu();
 
 
-    button_menu_clean(button_menu);
+    button_menu_reset(button_menu);
 }
 }

+ 1 - 1
applications/irda/scene/irda_app_scene_start.cpp

@@ -62,5 +62,5 @@ void IrdaAppSceneStart::on_exit(IrdaApp* app) {
     Submenu* submenu = view_manager->get_submenu();
     Submenu* submenu = view_manager->get_submenu();
 
 
     app->get_remote_manager()->reset_remote();
     app->get_remote_manager()->reset_remote();
-    submenu_clean(submenu);
+    submenu_reset(submenu);
 }
 }

+ 1 - 1
applications/irda/scene/irda_app_scene_universal.cpp

@@ -53,5 +53,5 @@ void IrdaAppSceneUniversal::on_exit(IrdaApp* app) {
     IrdaAppViewManager* view_manager = app->get_view_manager();
     IrdaAppViewManager* view_manager = app->get_view_manager();
     Submenu* submenu = view_manager->get_submenu();
     Submenu* submenu = view_manager->get_submenu();
 
 
-    submenu_clean(submenu);
+    submenu_reset(submenu);
 }
 }

+ 1 - 1
applications/irda/scene/irda_app_scene_universal_common.cpp

@@ -102,5 +102,5 @@ bool IrdaAppSceneUniversalCommon::on_event(IrdaApp* app, IrdaAppEvent* event) {
 void IrdaAppSceneUniversalCommon::on_exit(IrdaApp* app) {
 void IrdaAppSceneUniversalCommon::on_exit(IrdaApp* app) {
     IrdaAppViewManager* view_manager = app->get_view_manager();
     IrdaAppViewManager* view_manager = app->get_view_manager();
     ButtonPanel* button_panel = view_manager->get_button_panel();
     ButtonPanel* button_panel = view_manager->get_button_panel();
-    button_panel_clean(button_panel);
+    button_panel_reset(button_panel);
 }
 }

+ 1 - 1
applications/loader/loader.c

@@ -429,7 +429,7 @@ void loader_show_menu() {
 }
 }
 
 
 void loader_update_menu() {
 void loader_update_menu() {
-    menu_clean(loader_instance->primary_menu);
+    menu_reset(loader_instance->primary_menu);
     loader_build_menu();
     loader_build_menu();
 }
 }
 
 

+ 1 - 1
applications/nfc/scenes/nfc_scene_card_menu.c

@@ -81,5 +81,5 @@ bool nfc_scene_card_menu_on_event(void* context, SceneManagerEvent event) {
 void nfc_scene_card_menu_on_exit(void* context) {
 void nfc_scene_card_menu_on_exit(void* context) {
     Nfc* nfc = (Nfc*)context;
     Nfc* nfc = (Nfc*)context;
 
 
-    submenu_clean(nfc->submenu);
+    submenu_reset(nfc->submenu);
 }
 }

+ 1 - 1
applications/nfc/scenes/nfc_scene_device_info.c

@@ -199,7 +199,7 @@ void nfc_scene_device_info_on_exit(void* context) {
         dialog_ex_set_context(dialog_ex, NULL);
         dialog_ex_set_context(dialog_ex, NULL);
     } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
     } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
         // Clear TextBox
         // Clear TextBox
-        text_box_clean(nfc->text_box);
+        text_box_reset(nfc->text_box);
         string_reset(nfc->text_box_store);
         string_reset(nfc->text_box_store);
     } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) {
     } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) {
         // Clear Bank Card
         // Clear Bank Card

+ 1 - 1
applications/nfc/scenes/nfc_scene_mifare_ul_menu.c

@@ -54,5 +54,5 @@ bool nfc_scene_mifare_ul_menu_on_event(void* context, SceneManagerEvent event) {
 void nfc_scene_mifare_ul_menu_on_exit(void* context) {
 void nfc_scene_mifare_ul_menu_on_exit(void* context) {
     Nfc* nfc = (Nfc*)context;
     Nfc* nfc = (Nfc*)context;
 
 
-    submenu_clean(nfc->submenu);
+    submenu_reset(nfc->submenu);
 }
 }

+ 1 - 1
applications/nfc/scenes/nfc_scene_read_mifare_ul_success.c

@@ -126,6 +126,6 @@ void nfc_scene_read_mifare_ul_success_on_exit(void* context) {
 
 
     // Clean TextBox
     // Clean TextBox
     TextBox* text_box = nfc->text_box;
     TextBox* text_box = nfc->text_box;
-    text_box_clean(text_box);
+    text_box_reset(text_box);
     string_reset(nfc->text_box_store);
     string_reset(nfc->text_box_store);
 }
 }

+ 1 - 1
applications/nfc/scenes/nfc_scene_save_name.c

@@ -60,5 +60,5 @@ void nfc_scene_save_name_on_exit(void* context) {
     Nfc* nfc = (Nfc*)context;
     Nfc* nfc = (Nfc*)context;
 
 
     // Clear view
     // Clear view
-    text_input_clean(nfc->text_input);
+    text_input_reset(nfc->text_input);
 }
 }

+ 1 - 1
applications/nfc/scenes/nfc_scene_saved_menu.c

@@ -81,5 +81,5 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
 void nfc_scene_saved_menu_on_exit(void* context) {
 void nfc_scene_saved_menu_on_exit(void* context) {
     Nfc* nfc = (Nfc*)context;
     Nfc* nfc = (Nfc*)context;
 
 
-    submenu_clean(nfc->submenu);
+    submenu_reset(nfc->submenu);
 }
 }

+ 1 - 1
applications/nfc/scenes/nfc_scene_scripts_menu.c

@@ -55,5 +55,5 @@ bool nfc_scene_scripts_menu_on_event(void* context, SceneManagerEvent event) {
 void nfc_scene_scripts_menu_on_exit(void* context) {
 void nfc_scene_scripts_menu_on_exit(void* context) {
     Nfc* nfc = (Nfc*)context;
     Nfc* nfc = (Nfc*)context;
 
 
-    submenu_clean(nfc->submenu);
+    submenu_reset(nfc->submenu);
 }
 }

+ 1 - 1
applications/nfc/scenes/nfc_scene_set_type.c

@@ -45,5 +45,5 @@ bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) {
 void nfc_scene_set_type_on_exit(void* context) {
 void nfc_scene_set_type_on_exit(void* context) {
     Nfc* nfc = (Nfc*)context;
     Nfc* nfc = (Nfc*)context;
 
 
-    submenu_clean(nfc->submenu);
+    submenu_reset(nfc->submenu);
 }
 }

+ 1 - 1
applications/nfc/scenes/nfc_scene_start.c

@@ -78,5 +78,5 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
 void nfc_scene_start_on_exit(void* context) {
 void nfc_scene_start_on_exit(void* context) {
     Nfc* nfc = (Nfc*)context;
     Nfc* nfc = (Nfc*)context;
 
 
-    submenu_clean(nfc->submenu);
+    submenu_reset(nfc->submenu);
 }
 }

+ 1 - 1
applications/power/power_settings_app/scenes/power_settings_scene_power_off.c

@@ -39,5 +39,5 @@ bool power_settings_scene_power_off_on_event(void* context, SceneManagerEvent ev
 
 
 void power_settings_scene_power_off_on_exit(void* context) {
 void power_settings_scene_power_off_on_exit(void* context) {
     PowerSettingsApp* app = context;
     PowerSettingsApp* app = context;
-    dialog_ex_clean(app->dialog);
+    dialog_ex_reset(app->dialog);
 }
 }

+ 1 - 1
applications/power/power_settings_app/scenes/power_settings_scene_reboot.c

@@ -48,5 +48,5 @@ bool power_settings_scene_reboot_on_event(void* context, SceneManagerEvent event
 
 
 void power_settings_scene_reboot_on_exit(void* context) {
 void power_settings_scene_reboot_on_exit(void* context) {
     PowerSettingsApp* app = context;
     PowerSettingsApp* app = context;
-    submenu_clean(app->submenu);
+    submenu_reset(app->submenu);
 }
 }

+ 1 - 1
applications/power/power_settings_app/scenes/power_settings_scene_start.c

@@ -60,5 +60,5 @@ bool power_settings_scene_start_on_event(void* context, SceneManagerEvent event)
 
 
 void power_settings_scene_start_on_exit(void* context) {
 void power_settings_scene_start_on_exit(void* context) {
     PowerSettingsApp* app = context;
     PowerSettingsApp* app = context;
-    submenu_clean(app->submenu);
+    submenu_reset(app->submenu);
 }
 }

+ 1 - 1
applications/storage_settings/scenes/storage_settings_scene_start.c

@@ -115,5 +115,5 @@ bool storage_settings_scene_start_on_event(void* context, SceneManagerEvent even
 
 
 void storage_settings_scene_start_on_exit(void* context) {
 void storage_settings_scene_start_on_exit(void* context) {
     StorageSettings* app = context;
     StorageSettings* app = context;
-    submenu_clean(app->submenu);
+    submenu_reset(app->submenu);
 }
 }

+ 1 - 1
applications/subghz/scenes/subghz_scene_more_raw.c

@@ -56,5 +56,5 @@ bool subghz_scene_more_raw_on_event(void* context, SceneManagerEvent event) {
 
 
 void subghz_scene_more_raw_on_exit(void* context) {
 void subghz_scene_more_raw_on_exit(void* context) {
     SubGhz* subghz = context;
     SubGhz* subghz = context;
-    submenu_clean(subghz->submenu);
+    submenu_reset(subghz->submenu);
 }
 }

+ 1 - 1
applications/subghz/scenes/subghz_scene_receiver.c

@@ -66,7 +66,7 @@ void subghz_scene_receiver_on_enter(void* context) {
     string_init(str_buff);
     string_init(str_buff);
 
 
     if(subghz->txrx->rx_key_state == SubGhzRxKeyStateIDLE) {
     if(subghz->txrx->rx_key_state == SubGhzRxKeyStateIDLE) {
-        subghz_history_clean(subghz->txrx->history);
+        subghz_history_reset(subghz->txrx->history);
     }
     }
 
 
     //Load history to receiver
     //Load history to receiver

+ 1 - 1
applications/subghz/scenes/subghz_scene_receiver_config.c

@@ -161,7 +161,7 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even
 
 
 void subghz_scene_receiver_config_on_exit(void* context) {
 void subghz_scene_receiver_config_on_exit(void* context) {
     SubGhz* subghz = context;
     SubGhz* subghz = context;
-    variable_item_list_clean(subghz->variable_item_list);
+    variable_item_list_reset(subghz->variable_item_list);
     scene_manager_set_scene_state(
     scene_manager_set_scene_state(
         subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerNoSet);
         subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerNoSet);
 }
 }

+ 1 - 1
applications/subghz/scenes/subghz_scene_save_name.c

@@ -91,7 +91,7 @@ void subghz_scene_save_name_on_exit(void* context) {
     text_input_set_validator(subghz->text_input, NULL, NULL);
     text_input_set_validator(subghz->text_input, NULL, NULL);
     validator_is_file_free(validator_context);
     validator_is_file_free(validator_context);
 
 
-    text_input_clean(subghz->text_input);
+    text_input_reset(subghz->text_input);
     scene_manager_set_scene_state(
     scene_manager_set_scene_state(
         subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerNoSet);
         subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerNoSet);
 }
 }

+ 1 - 1
applications/subghz/scenes/subghz_scene_saved_menu.c

@@ -67,5 +67,5 @@ bool subghz_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
 
 
 void subghz_scene_saved_menu_on_exit(void* context) {
 void subghz_scene_saved_menu_on_exit(void* context) {
     SubGhz* subghz = context;
     SubGhz* subghz = context;
-    submenu_clean(subghz->submenu);
+    submenu_reset(subghz->submenu);
 }
 }

+ 1 - 1
applications/subghz/scenes/subghz_scene_set_type.c

@@ -202,5 +202,5 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
 
 
 void subghz_scene_set_type_on_exit(void* context) {
 void subghz_scene_set_type_on_exit(void* context) {
     SubGhz* subghz = context;
     SubGhz* subghz = context;
-    submenu_clean(subghz->submenu);
+    submenu_reset(subghz->submenu);
 }
 }

+ 1 - 1
applications/subghz/scenes/subghz_scene_start.c

@@ -97,5 +97,5 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) {
 
 
 void subghz_scene_start_on_exit(void* context) {
 void subghz_scene_start_on_exit(void* context) {
     SubGhz* subghz = context;
     SubGhz* subghz = context;
-    submenu_clean(subghz->submenu);
+    submenu_reset(subghz->submenu);
 }
 }

+ 1 - 1
applications/subghz/scenes/subghz_scene_test.c

@@ -57,5 +57,5 @@ bool subghz_scene_test_on_event(void* context, SceneManagerEvent event) {
 
 
 void subghz_scene_test_on_exit(void* context) {
 void subghz_scene_test_on_exit(void* context) {
     SubGhz* subghz = context;
     SubGhz* subghz = context;
-    submenu_clean(subghz->submenu);
+    submenu_reset(subghz->submenu);
 }
 }

+ 2 - 2
applications/subghz/subghz_history.c

@@ -60,7 +60,7 @@ FuriHalSubGhzPreset subghz_history_get_preset(SubGhzHistory* instance, uint16_t
     return instance->history[idx].preset;
     return instance->history[idx].preset;
 }
 }
 
 
-void subghz_history_clean(SubGhzHistory* instance) {
+void subghz_history_reset(SubGhzHistory* instance) {
     furi_assert(instance);
     furi_assert(instance);
     instance->last_index_write = 0;
     instance->last_index_write = 0;
     instance->code_last_found = 0;
     instance->code_last_found = 0;
@@ -168,4 +168,4 @@ bool subghz_history_add_to_history(
 
 
     instance->last_index_write++;
     instance->last_index_write++;
     return true;
     return true;
-}
+}

+ 1 - 1
applications/subghz/subghz_history.h

@@ -21,7 +21,7 @@ void subghz_history_free(SubGhzHistory* instance);
  * 
  * 
  * @param instance - SubGhzHistory instance
  * @param instance - SubGhzHistory instance
  */
  */
-void subghz_history_clean(SubGhzHistory* instance);
+void subghz_history_reset(SubGhzHistory* instance);
 
 
 /** Set frequency and preset to history[idx]
 /** Set frequency and preset to history[idx]
  * 
  * 

+ 37 - 9
firmware/targets/f6/ble_glue/gap.c

@@ -94,9 +94,17 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
     case EVT_LE_META_EVENT:
     case EVT_LE_META_EVENT:
         meta_evt = (evt_le_meta_event*)event_pckt->data;
         meta_evt = (evt_le_meta_event*)event_pckt->data;
         switch(meta_evt->subevent) {
         switch(meta_evt->subevent) {
-        case EVT_LE_CONN_UPDATE_COMPLETE:
-            FURI_LOG_D(TAG, "Connection update event");
+        case EVT_LE_CONN_UPDATE_COMPLETE: {
+            hci_le_connection_update_complete_event_rp0* event =
+                (hci_le_connection_update_complete_event_rp0*)meta_evt->data;
+            FURI_LOG_I(
+                TAG,
+                "Connection interval: %d, latency: %d, supervision timeout: %d",
+                event->Conn_Interval,
+                event->Conn_Latency,
+                event->Supervision_Timeout);
             break;
             break;
+        }
 
 
         case EVT_LE_PHY_UPDATE_COMPLETE:
         case EVT_LE_PHY_UPDATE_COMPLETE:
             evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data;
             evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data;
@@ -129,6 +137,15 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
             // Update connection status and handle
             // Update connection status and handle
             gap->state = GapStateConnected;
             gap->state = GapStateConnected;
             gap->service.connection_handle = connection_complete_event->Connection_Handle;
             gap->service.connection_handle = connection_complete_event->Connection_Handle;
+            GapConnectionParams* params = &gap->config->conn_param;
+            if(aci_l2cap_connection_parameter_update_req(
+                   gap->service.connection_handle,
+                   params->conn_int_min,
+                   params->conn_int_max,
+                   params->slave_latency,
+                   params->supervisor_timeout)) {
+                FURI_LOG_W(TAG, "Failed to request connection parameters update");
+            }
 
 
             // Start pairing by sending security request
             // Start pairing by sending security request
             aci_gap_slave_security_req(connection_complete_event->Connection_Handle);
             aci_gap_slave_security_req(connection_complete_event->Connection_Handle);
@@ -184,28 +201,28 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
         } break;
         } break;
 
 
         case EVT_BLUE_GAP_AUTHORIZATION_REQUEST:
         case EVT_BLUE_GAP_AUTHORIZATION_REQUEST:
-            FURI_LOG_I(TAG, "Authorization request event");
+            FURI_LOG_D(TAG, "Authorization request event");
             break;
             break;
 
 
         case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED:
         case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED:
-            FURI_LOG_I(TAG, "Slave security initiated");
+            FURI_LOG_D(TAG, "Slave security initiated");
             break;
             break;
 
 
         case EVT_BLUE_GAP_BOND_LOST:
         case EVT_BLUE_GAP_BOND_LOST:
-            FURI_LOG_I(TAG, "Bond lost event. Start rebonding");
+            FURI_LOG_D(TAG, "Bond lost event. Start rebonding");
             aci_gap_allow_rebond(gap->service.connection_handle);
             aci_gap_allow_rebond(gap->service.connection_handle);
             break;
             break;
 
 
         case EVT_BLUE_GAP_DEVICE_FOUND:
         case EVT_BLUE_GAP_DEVICE_FOUND:
-            FURI_LOG_I(TAG, "Device found event");
+            FURI_LOG_D(TAG, "Device found event");
             break;
             break;
 
 
         case EVT_BLUE_GAP_ADDR_NOT_RESOLVED:
         case EVT_BLUE_GAP_ADDR_NOT_RESOLVED:
-            FURI_LOG_I(TAG, "Address not resolved event");
+            FURI_LOG_D(TAG, "Address not resolved event");
             break;
             break;
 
 
         case EVT_BLUE_GAP_KEYPRESS_NOTIFICATION:
         case EVT_BLUE_GAP_KEYPRESS_NOTIFICATION:
-            FURI_LOG_I(TAG, "Key press notification event");
+            FURI_LOG_D(TAG, "Key press notification event");
             break;
             break;
 
 
         case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE: {
         case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE: {
@@ -234,8 +251,19 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
             break;
             break;
 
 
         case EVT_BLUE_GAP_PROCEDURE_COMPLETE:
         case EVT_BLUE_GAP_PROCEDURE_COMPLETE:
-            FURI_LOG_I(TAG, "Procedure complete event");
+            FURI_LOG_D(TAG, "Procedure complete event");
             break;
             break;
+
+        case EVT_BLUE_L2CAP_CONNECTION_UPDATE_RESP: {
+            uint16_t result =
+                ((aci_l2cap_connection_update_resp_event_rp0*)(blue_evt->data))->Result;
+            if(result == 0) {
+                FURI_LOG_D(TAG, "Connection parameters accepted");
+            } else if(result == 1) {
+                FURI_LOG_D(TAG, "Connection parameters denied");
+            }
+            break;
+        }
         }
         }
     default:
     default:
         break;
         break;

+ 8 - 0
firmware/targets/f6/ble_glue/gap.h

@@ -55,6 +55,13 @@ typedef enum {
     GapPairingPinCodeVerifyYesNo,
     GapPairingPinCodeVerifyYesNo,
 } GapPairing;
 } GapPairing;
 
 
+typedef struct {
+    uint16_t conn_int_min;
+    uint16_t conn_int_max;
+    uint16_t slave_latency;
+    uint16_t supervisor_timeout;
+} GapConnectionParams;
+
 typedef struct {
 typedef struct {
     uint16_t adv_service_uuid;
     uint16_t adv_service_uuid;
     uint16_t appearance_char;
     uint16_t appearance_char;
@@ -62,6 +69,7 @@ typedef struct {
     GapPairing pairing_method;
     GapPairing pairing_method;
     uint8_t mac_address[GAP_MAC_ADDR_SIZE];
     uint8_t mac_address[GAP_MAC_ADDR_SIZE];
     char adv_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH];
     char adv_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH];
+    GapConnectionParams conn_param;
 } GapConfig;
 } GapConfig;
 
 
 bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context);
 bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context);

+ 25 - 0
firmware/targets/f6/furi_hal/furi_hal_bt.c

@@ -42,6 +42,13 @@ FuriHalBtProfileConfig profile_config[FuriHalBtProfileNumber] = {
                     .bonding_mode = true,
                     .bonding_mode = true,
                     .pairing_method = GapPairingPinCodeShow,
                     .pairing_method = GapPairingPinCodeShow,
                     .mac_address = FURI_HAL_BT_DEFAULT_MAC_ADDR,
                     .mac_address = FURI_HAL_BT_DEFAULT_MAC_ADDR,
+                    .conn_param =
+                        {
+                            .conn_int_min = 0x08,
+                            .conn_int_max = 0x18,
+                            .slave_latency = 0,
+                            .supervisor_timeout = 50,
+                        },
                 },
                 },
         },
         },
     [FuriHalBtProfileHidKeyboard] =
     [FuriHalBtProfileHidKeyboard] =
@@ -55,6 +62,14 @@ FuriHalBtProfileConfig profile_config[FuriHalBtProfileNumber] = {
                     .bonding_mode = true,
                     .bonding_mode = true,
                     .pairing_method = GapPairingPinCodeVerifyYesNo,
                     .pairing_method = GapPairingPinCodeVerifyYesNo,
                     .mac_address = FURI_HAL_BT_DEFAULT_MAC_ADDR,
                     .mac_address = FURI_HAL_BT_DEFAULT_MAC_ADDR,
+                    // TODO optimize
+                    .conn_param =
+                        {
+                            .conn_int_min = 0x12,
+                            .conn_int_max = 0x1e,
+                            .slave_latency = 6,
+                            .supervisor_timeout = 700,
+                        },
                 },
                 },
         },
         },
 };
 };
@@ -277,6 +292,16 @@ void furi_hal_bt_nvm_sram_sem_release() {
     HAL_HSEM_Release(CFG_HW_BLE_NVM_SRAM_SEMID, 0);
     HAL_HSEM_Release(CFG_HW_BLE_NVM_SRAM_SEMID, 0);
 }
 }
 
 
+bool furi_hal_bt_clear_white_list() {
+    furi_hal_bt_nvm_sram_sem_acquire();
+    tBleStatus status = aci_gap_clear_security_db();
+    if(status) {
+        FURI_LOG_E(TAG, "Clear while list failed with status %d", status);
+    }
+    furi_hal_bt_nvm_sram_sem_release();
+    return status != BLE_STATUS_SUCCESS;
+}
+
 void furi_hal_bt_dump_state(string_t buffer) {
 void furi_hal_bt_dump_state(string_t buffer) {
     if(furi_hal_bt_is_alive()) {
     if(furi_hal_bt_is_alive()) {
         uint8_t HCI_Version;
         uint8_t HCI_Version;

+ 1 - 0
firmware/targets/f6/target.mk

@@ -112,6 +112,7 @@ C_SOURCES += \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_gap_aci.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_gap_aci.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_gatt_aci.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_gatt_aci.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_hal_aci.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_hal_aci.c \
+	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_l2cap_aci.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/tl_mbox.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/tl_mbox.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/hci_tl.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/hci_tl.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/hci_tl_if.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/hci_tl_if.c \

+ 37 - 9
firmware/targets/f7/ble_glue/gap.c

@@ -94,9 +94,17 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
     case EVT_LE_META_EVENT:
     case EVT_LE_META_EVENT:
         meta_evt = (evt_le_meta_event*)event_pckt->data;
         meta_evt = (evt_le_meta_event*)event_pckt->data;
         switch(meta_evt->subevent) {
         switch(meta_evt->subevent) {
-        case EVT_LE_CONN_UPDATE_COMPLETE:
-            FURI_LOG_D(TAG, "Connection update event");
+        case EVT_LE_CONN_UPDATE_COMPLETE: {
+            hci_le_connection_update_complete_event_rp0* event =
+                (hci_le_connection_update_complete_event_rp0*)meta_evt->data;
+            FURI_LOG_I(
+                TAG,
+                "Connection interval: %d, latency: %d, supervision timeout: %d",
+                event->Conn_Interval,
+                event->Conn_Latency,
+                event->Supervision_Timeout);
             break;
             break;
+        }
 
 
         case EVT_LE_PHY_UPDATE_COMPLETE:
         case EVT_LE_PHY_UPDATE_COMPLETE:
             evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data;
             evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data;
@@ -129,6 +137,15 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
             // Update connection status and handle
             // Update connection status and handle
             gap->state = GapStateConnected;
             gap->state = GapStateConnected;
             gap->service.connection_handle = connection_complete_event->Connection_Handle;
             gap->service.connection_handle = connection_complete_event->Connection_Handle;
+            GapConnectionParams* params = &gap->config->conn_param;
+            if(aci_l2cap_connection_parameter_update_req(
+                   gap->service.connection_handle,
+                   params->conn_int_min,
+                   params->conn_int_max,
+                   params->slave_latency,
+                   params->supervisor_timeout)) {
+                FURI_LOG_W(TAG, "Failed to request connection parameters update");
+            }
 
 
             // Start pairing by sending security request
             // Start pairing by sending security request
             aci_gap_slave_security_req(connection_complete_event->Connection_Handle);
             aci_gap_slave_security_req(connection_complete_event->Connection_Handle);
@@ -184,28 +201,28 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
         } break;
         } break;
 
 
         case EVT_BLUE_GAP_AUTHORIZATION_REQUEST:
         case EVT_BLUE_GAP_AUTHORIZATION_REQUEST:
-            FURI_LOG_I(TAG, "Authorization request event");
+            FURI_LOG_D(TAG, "Authorization request event");
             break;
             break;
 
 
         case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED:
         case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED:
-            FURI_LOG_I(TAG, "Slave security initiated");
+            FURI_LOG_D(TAG, "Slave security initiated");
             break;
             break;
 
 
         case EVT_BLUE_GAP_BOND_LOST:
         case EVT_BLUE_GAP_BOND_LOST:
-            FURI_LOG_I(TAG, "Bond lost event. Start rebonding");
+            FURI_LOG_D(TAG, "Bond lost event. Start rebonding");
             aci_gap_allow_rebond(gap->service.connection_handle);
             aci_gap_allow_rebond(gap->service.connection_handle);
             break;
             break;
 
 
         case EVT_BLUE_GAP_DEVICE_FOUND:
         case EVT_BLUE_GAP_DEVICE_FOUND:
-            FURI_LOG_I(TAG, "Device found event");
+            FURI_LOG_D(TAG, "Device found event");
             break;
             break;
 
 
         case EVT_BLUE_GAP_ADDR_NOT_RESOLVED:
         case EVT_BLUE_GAP_ADDR_NOT_RESOLVED:
-            FURI_LOG_I(TAG, "Address not resolved event");
+            FURI_LOG_D(TAG, "Address not resolved event");
             break;
             break;
 
 
         case EVT_BLUE_GAP_KEYPRESS_NOTIFICATION:
         case EVT_BLUE_GAP_KEYPRESS_NOTIFICATION:
-            FURI_LOG_I(TAG, "Key press notification event");
+            FURI_LOG_D(TAG, "Key press notification event");
             break;
             break;
 
 
         case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE: {
         case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE: {
@@ -234,8 +251,19 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
             break;
             break;
 
 
         case EVT_BLUE_GAP_PROCEDURE_COMPLETE:
         case EVT_BLUE_GAP_PROCEDURE_COMPLETE:
-            FURI_LOG_I(TAG, "Procedure complete event");
+            FURI_LOG_D(TAG, "Procedure complete event");
             break;
             break;
+
+        case EVT_BLUE_L2CAP_CONNECTION_UPDATE_RESP: {
+            uint16_t result =
+                ((aci_l2cap_connection_update_resp_event_rp0*)(blue_evt->data))->Result;
+            if(result == 0) {
+                FURI_LOG_D(TAG, "Connection parameters accepted");
+            } else if(result == 1) {
+                FURI_LOG_D(TAG, "Connection parameters denied");
+            }
+            break;
+        }
         }
         }
     default:
     default:
         break;
         break;

+ 8 - 0
firmware/targets/f7/ble_glue/gap.h

@@ -55,6 +55,13 @@ typedef enum {
     GapPairingPinCodeVerifyYesNo,
     GapPairingPinCodeVerifyYesNo,
 } GapPairing;
 } GapPairing;
 
 
+typedef struct {
+    uint16_t conn_int_min;
+    uint16_t conn_int_max;
+    uint16_t slave_latency;
+    uint16_t supervisor_timeout;
+} GapConnectionParams;
+
 typedef struct {
 typedef struct {
     uint16_t adv_service_uuid;
     uint16_t adv_service_uuid;
     uint16_t appearance_char;
     uint16_t appearance_char;
@@ -62,6 +69,7 @@ typedef struct {
     GapPairing pairing_method;
     GapPairing pairing_method;
     uint8_t mac_address[GAP_MAC_ADDR_SIZE];
     uint8_t mac_address[GAP_MAC_ADDR_SIZE];
     char adv_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH];
     char adv_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH];
+    GapConnectionParams conn_param;
 } GapConfig;
 } GapConfig;
 
 
 bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context);
 bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context);

+ 25 - 0
firmware/targets/f7/furi_hal/furi_hal_bt.c

@@ -42,6 +42,13 @@ FuriHalBtProfileConfig profile_config[FuriHalBtProfileNumber] = {
                     .bonding_mode = true,
                     .bonding_mode = true,
                     .pairing_method = GapPairingPinCodeShow,
                     .pairing_method = GapPairingPinCodeShow,
                     .mac_address = FURI_HAL_BT_DEFAULT_MAC_ADDR,
                     .mac_address = FURI_HAL_BT_DEFAULT_MAC_ADDR,
+                    .conn_param =
+                        {
+                            .conn_int_min = 0x08,
+                            .conn_int_max = 0x18,
+                            .slave_latency = 0,
+                            .supervisor_timeout = 50,
+                        },
                 },
                 },
         },
         },
     [FuriHalBtProfileHidKeyboard] =
     [FuriHalBtProfileHidKeyboard] =
@@ -55,6 +62,14 @@ FuriHalBtProfileConfig profile_config[FuriHalBtProfileNumber] = {
                     .bonding_mode = true,
                     .bonding_mode = true,
                     .pairing_method = GapPairingPinCodeVerifyYesNo,
                     .pairing_method = GapPairingPinCodeVerifyYesNo,
                     .mac_address = FURI_HAL_BT_DEFAULT_MAC_ADDR,
                     .mac_address = FURI_HAL_BT_DEFAULT_MAC_ADDR,
+                    // TODO optimize
+                    .conn_param =
+                        {
+                            .conn_int_min = 0x12,
+                            .conn_int_max = 0x1e,
+                            .slave_latency = 6,
+                            .supervisor_timeout = 700,
+                        },
                 },
                 },
         },
         },
 };
 };
@@ -277,6 +292,16 @@ void furi_hal_bt_nvm_sram_sem_release() {
     HAL_HSEM_Release(CFG_HW_BLE_NVM_SRAM_SEMID, 0);
     HAL_HSEM_Release(CFG_HW_BLE_NVM_SRAM_SEMID, 0);
 }
 }
 
 
+bool furi_hal_bt_clear_white_list() {
+    furi_hal_bt_nvm_sram_sem_acquire();
+    tBleStatus status = aci_gap_clear_security_db();
+    if(status) {
+        FURI_LOG_E(TAG, "Clear while list failed with status %d", status);
+    }
+    furi_hal_bt_nvm_sram_sem_release();
+    return status != BLE_STATUS_SUCCESS;
+}
+
 void furi_hal_bt_dump_state(string_t buffer) {
 void furi_hal_bt_dump_state(string_t buffer) {
     if(furi_hal_bt_is_alive()) {
     if(furi_hal_bt_is_alive()) {
         uint8_t HCI_Version;
         uint8_t HCI_Version;

+ 1 - 0
firmware/targets/f7/target.mk

@@ -112,6 +112,7 @@ C_SOURCES += \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_gap_aci.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_gap_aci.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_gatt_aci.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_gatt_aci.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_hal_aci.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_hal_aci.c \
+	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/ble/core/auto/ble_l2cap_aci.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/tl_mbox.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/tl_mbox.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/hci_tl.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/hci_tl.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/hci_tl_if.c \
 	$(CUBE_DIR)/Middlewares/ST/STM32_WPAN/interface/patterns/ble_thread/tl/hci_tl_if.c \

+ 6 - 0
firmware/targets/furi_hal_include/furi_hal_bt.h

@@ -121,6 +121,12 @@ void furi_hal_bt_nvm_sram_sem_acquire();
  */
  */
 void furi_hal_bt_nvm_sram_sem_release();
 void furi_hal_bt_nvm_sram_sem_release();
 
 
+/** Clear key storage
+ *
+ * @return      true on success
+*/
+bool furi_hal_bt_clear_white_list();
+
 /** Set key storage change callback
 /** Set key storage change callback
  *
  *
  * @param       callback    BleGlueKeyStorageChangedCallback instance
  * @param       callback    BleGlueKeyStorageChangedCallback instance

+ 1 - 1
lib/app-scened-template/view_modules/submenu_vm.cpp

@@ -13,7 +13,7 @@ View* SubmenuVM::get_view() {
 }
 }
 
 
 void SubmenuVM::clean() {
 void SubmenuVM::clean() {
-    submenu_clean(submenu);
+    submenu_reset(submenu);
 }
 }
 
 
 void SubmenuVM::add_item(
 void SubmenuVM::add_item(

+ 1 - 1
lib/app-scened-template/view_modules/text_input_vm.cpp

@@ -13,7 +13,7 @@ View* TextInputVM::get_view() {
 }
 }
 
 
 void TextInputVM::clean() {
 void TextInputVM::clean() {
-    text_input_clean(text_input);
+    text_input_reset(text_input);
 }
 }
 
 
 void TextInputVM::set_result_callback(
 void TextInputVM::set_result_callback(