Sfoglia il codice sorgente

[FL-2399, FL-2261] Tickless sleep shenanigans (#1168)

* Disable USART in sleep
* Restore UART state on suspend/resume
* FuriHal: Enable stop mode and add insomnia to I2C and SPI
* Remove IDLE interrupt
* FuriHal: add FPU isr and disable all FPU interrupt, add core2 stop mode configuration on deep sleep
* FuriHal: tie stop mode debug with debug rtc flag
* FuriHal: adjust flash latency on clock switch, tie mcu debug with RTC debug flag
* FuriHal: move resource init to early stage
* Add EXTI pending check, enable debug traps with compile-time flag
* Wrap sleep debug functions in conditional compilation
* Remove erroneous changed
* Do not use CSS, remove it from everywhere
* Enable/disable USB on VBUS connect (prototype)
* FuriHal: add LPMS and DEEPSLEEP magic, workaround state inconsistency between cores
* FuriHal: honor c1 LMPS
* USB mode switch fix
* Applications: add flags and insomnia bypass system
* Correct spelling
* FuriHal: cleanup insomnia usage, reset sleep flags on wakeup, add shutdown api
* FuriHal: extra check on reinit request
* FuriHal: rename gpio_display_rst pin to gpio_display_rst_n
* FuriHal: add debug HAL
* FuriHal: add some magic to core2 reload procedure, fix issue with crash on ble keyboard exit
* FuriHal: cleanup ble glue, add BLE_GLUE_DEBUG flag
* FuriHal: ble reinit API, move os timer to LPTIM1 for deep sleep capability, shutdown that works
* FuriHal: take insomnia while shutdown
* Remove USB switch on/off on VBUS change
* Better tick skew handling
* Improve tick consistency under load
* Add USB_HP dummy IRQ handler
* Move interrupt check closer to sleep
* Clean up includes
* Re-enable Insomnia globally
* FuriHal: enable CSS
* FuriHal: remove questionable core2 clock shenanigans
* FuriHal: use core1 RCC registers in idle timer config
* FuriHal: return back CSS handlers, add lptim isr dispatching

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
Co-authored-by: nminaylov <nm29719@gmail.com>
Georgii Surkov 3 anni fa
parent
commit
01434265f6
36 ha cambiato i file con 703 aggiunte e 295 eliminazioni
  1. 1 3
      applications/accessor/accessor_app.cpp
  2. 243 50
      applications/applications.c
  3. 6 0
      applications/applications.h
  4. 0 4
      applications/gui/canvas.c
  5. 1 4
      applications/ibutton/ibutton_app.cpp
  6. 1 3
      applications/lfrfid/lfrfid_app.cpp
  7. 7 2
      applications/loader/loader.c
  8. 0 2
      applications/nfc/nfc_worker.c
  9. 0 2
      applications/storage/storage_glue.c
  10. 1 1
      firmware/targets/f7/ble_glue/app_conf.h
  11. 8 103
      firmware/targets/f7/ble_glue/app_debug.c
  12. 8 9
      firmware/targets/f7/ble_glue/ble_glue.c
  13. 19 8
      firmware/targets/f7/ble_glue/gap.c
  14. 2 2
      firmware/targets/f7/furi_hal/furi_hal.c
  15. 21 11
      firmware/targets/f7/furi_hal/furi_hal_bt.c
  16. 4 0
      firmware/targets/f7/furi_hal/furi_hal_clock.c
  17. 20 0
      firmware/targets/f7/furi_hal/furi_hal_debug.c
  18. 3 0
      firmware/targets/f7/furi_hal/furi_hal_i2c.c
  19. 5 3
      firmware/targets/f7/furi_hal/furi_hal_idle_timer.h
  20. 59 30
      firmware/targets/f7/furi_hal/furi_hal_interrupt.c
  21. 4 0
      firmware/targets/f7/furi_hal/furi_hal_interrupt.h
  22. 70 19
      firmware/targets/f7/furi_hal/furi_hal_os.c
  23. 91 8
      firmware/targets/f7/furi_hal/furi_hal_power.c
  24. 25 14
      firmware/targets/f7/furi_hal/furi_hal_resources.c
  25. 1 1
      firmware/targets/f7/furi_hal/furi_hal_resources.h
  26. 15 3
      firmware/targets/f7/furi_hal/furi_hal_rtc.c
  27. 5 0
      firmware/targets/f7/furi_hal/furi_hal_spi.c
  28. 24 9
      firmware/targets/f7/furi_hal/furi_hal_uart.c
  29. 15 3
      firmware/targets/f7/furi_hal/furi_hal_uart.h
  30. 2 0
      firmware/targets/f7/furi_hal/furi_hal_usb.c
  31. 8 0
      firmware/targets/f7/target.mk
  32. 1 0
      firmware/targets/furi_hal_include/furi_hal.h
  33. 6 0
      firmware/targets/furi_hal_include/furi_hal_bt.h
  34. 23 0
      firmware/targets/furi_hal_include/furi_hal_debug.h
  35. 3 0
      firmware/targets/furi_hal_include/furi_hal_power.h
  36. 1 1
      lib/u8g2/u8g2_glue.c

+ 1 - 3
applications/accessor/accessor_app.cpp

@@ -32,7 +32,6 @@ void AccessorApp::run(void) {
 }
 
 AccessorApp::AccessorApp() {
-    furi_hal_power_insomnia_enter();
     notification = static_cast<NotificationApp*>(furi_record_open("notification"));
     onewire_host = onewire_host_alloc();
     furi_hal_power_enable_otg();
@@ -42,7 +41,6 @@ AccessorApp::~AccessorApp() {
     furi_hal_power_disable_otg();
     furi_record_close("notification");
     onewire_host_free(onewire_host);
-    furi_hal_power_insomnia_exit();
 }
 
 AccessorAppViewManager* AccessorApp::get_view_manager() {
@@ -139,4 +137,4 @@ WIEGAND* AccessorApp::get_wiegand() {
 
 OneWireHost* AccessorApp::get_one_wire() {
     return onewire_host;
-}
+}

+ 243 - 50
applications/applications.c

@@ -74,61 +74,113 @@ extern int32_t system_settings_app(void* p);
 const FlipperApplication FLIPPER_SERVICES[] = {
 /* Services */
 #ifdef SRV_RPC
-    {.app = rpc_srv, .name = "RpcSrv", .stack_size = 1024 * 4, .icon = NULL},
+    {.app = rpc_srv,
+     .name = "RpcSrv",
+     .stack_size = 1024 * 4,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef SRV_BT
-    {.app = bt_srv, .name = "BtSrv", .stack_size = 1024, .icon = NULL},
+    {.app = bt_srv,
+     .name = "BtSrv",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef SRV_CLI
-    {.app = cli_srv, .name = "CliSrv", .stack_size = 4096, .icon = NULL},
+    {.app = cli_srv,
+     .name = "CliSrv",
+     .stack_size = 4096,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef SRV_DIALOGS
-    {.app = dialogs_srv, .name = "DialogsSrv", .stack_size = 1024, .icon = NULL},
+    {.app = dialogs_srv,
+     .name = "DialogsSrv",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef SRV_DOLPHIN
-    {.app = dolphin_srv, .name = "DolphinSrv", .stack_size = 1024, .icon = NULL},
+    {.app = dolphin_srv,
+     .name = "DolphinSrv",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef SRV_DESKTOP
 #ifdef SRV_UPDATER
 #error SRV_UPDATER and SRV_DESKTOP are mutually exclusive!
 #endif
-    {.app = desktop_srv, .name = "DesktopSrv", .stack_size = 2048, .icon = NULL},
+    {.app = desktop_srv,
+     .name = "DesktopSrv",
+     .stack_size = 2048,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef SRV_GUI
-    {.app = gui_srv, .name = "GuiSrv", .stack_size = 2048, .icon = NULL},
+    {.app = gui_srv,
+     .name = "GuiSrv",
+     .stack_size = 2048,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef SRV_INPUT
-    {.app = input_srv, .name = "InputSrv", .stack_size = 1024, .icon = NULL},
+    {.app = input_srv,
+     .name = "InputSrv",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef SRV_LOADER
-    {.app = loader_srv, .name = "LoaderSrv", .stack_size = 1024, .icon = NULL},
+    {.app = loader_srv,
+     .name = "LoaderSrv",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef SRV_NOTIFICATION
-    {.app = notification_srv, .name = "NotificationSrv", .stack_size = 1536, .icon = NULL},
+    {.app = notification_srv,
+     .name = "NotificationSrv",
+     .stack_size = 1536,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef SRV_POWER
-    {.app = power_srv, .name = "PowerSrv", .stack_size = 1024, .icon = NULL},
+    {.app = power_srv,
+     .name = "PowerSrv",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef SRV_STORAGE
-    {.app = storage_srv, .name = "StorageSrv", .stack_size = 3072, .icon = NULL},
+    {.app = storage_srv,
+     .name = "StorageSrv",
+     .stack_size = 3072,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef SRV_UPDATER
 #ifdef SRV_DESKTOP
 #error SRV_UPDATER and SRV_DESKTOP are mutually exclusive!
 #endif
-    {.app = updater_srv, .name = "UpdaterSrv", .stack_size = 2048, .icon = NULL},
+    {.app = updater_srv,
+     .name = "UpdaterSrv",
+     .stack_size = 2048,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 };
 
@@ -139,7 +191,11 @@ const FlipperApplication FLIPPER_SYSTEM_APPS[] = {
 #ifdef SRV_UPDATER
 #error APP_UPDATER and SRV_UPDATER are mutually exclusive!
 #endif
-    {.app = updater_srv, .name = "UpdaterApp", .stack_size = 2048, .icon = NULL},
+    {.app = updater_srv,
+     .name = "UpdaterApp",
+     .stack_size = 2048,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 };
 
@@ -149,35 +205,67 @@ const size_t FLIPPER_SYSTEM_APPS_COUNT = COUNT_OF(FLIPPER_SYSTEM_APPS);
 const FlipperApplication FLIPPER_APPS[] = {
 
 #ifdef APP_SUBGHZ
-    {.app = subghz_app, .name = "Sub-GHz", .stack_size = 2048, .icon = &A_Sub1ghz_14},
+    {.app = subghz_app,
+     .name = "Sub-GHz",
+     .stack_size = 2048,
+     .icon = &A_Sub1ghz_14,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_LF_RFID
-    {.app = lfrfid_app, .name = "125 kHz RFID", .stack_size = 2048, .icon = &A_125khz_14},
+    {.app = lfrfid_app,
+     .name = "125 kHz RFID",
+     .stack_size = 2048,
+     .icon = &A_125khz_14,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_NFC
-    {.app = nfc_app, .name = "NFC", .stack_size = 4096, .icon = &A_NFC_14},
+    {.app = nfc_app,
+     .name = "NFC",
+     .stack_size = 4096,
+     .icon = &A_NFC_14,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_INFRARED
-    {.app = infrared_app, .name = "Infrared", .stack_size = 1024 * 3, .icon = &A_Infrared_14},
+    {.app = infrared_app,
+     .name = "Infrared",
+     .stack_size = 1024 * 3,
+     .icon = &A_Infrared_14,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_GPIO
-    {.app = gpio_app, .name = "GPIO", .stack_size = 1024, .icon = &A_GPIO_14},
+    {.app = gpio_app,
+     .name = "GPIO",
+     .stack_size = 1024,
+     .icon = &A_GPIO_14,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_IBUTTON
-    {.app = ibutton_app, .name = "iButton", .stack_size = 2048, .icon = &A_iButton_14},
+    {.app = ibutton_app,
+     .name = "iButton",
+     .stack_size = 2048,
+     .icon = &A_iButton_14,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_BAD_USB
-    {.app = bad_usb_app, .name = "Bad USB", .stack_size = 2048, .icon = &A_BadUsb_14},
+    {.app = bad_usb_app,
+     .name = "Bad USB",
+     .stack_size = 2048,
+     .icon = &A_BadUsb_14,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_U2F
-    {.app = u2f_app, .name = "U2F", .stack_size = 2048, .icon = &A_U2F_14},
+    {.app = u2f_app,
+     .name = "U2F",
+     .stack_size = 2048,
+     .icon = &A_U2F_14,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 };
@@ -234,15 +322,27 @@ const size_t FLIPPER_ON_SYSTEM_START_COUNT = COUNT_OF(FLIPPER_ON_SYSTEM_START);
 // Plugin menu
 const FlipperApplication FLIPPER_PLUGINS[] = {
 #ifdef APP_BLE_HID
-    {.app = bt_hid_app, .name = "Bluetooth Remote", .stack_size = 1024, .icon = NULL},
+    {.app = bt_hid_app,
+     .name = "Bluetooth Remote",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_MUSIC_PLAYER
-    {.app = music_player_app, .name = "Music Player", .stack_size = 1024, .icon = &A_Plugins_14},
+    {.app = music_player_app,
+     .name = "Music Player",
+     .stack_size = 1024,
+     .icon = &A_Plugins_14,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_SNAKE_GAME
-    {.app = snake_game_app, .name = "Snake Game", .stack_size = 1024, .icon = &A_Plugins_14},
+    {.app = snake_game_app,
+     .name = "Snake Game",
+     .stack_size = 1024,
+     .icon = &A_Plugins_14,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 };
 
@@ -251,108 +351,201 @@ const size_t FLIPPER_PLUGINS_COUNT = COUNT_OF(FLIPPER_PLUGINS);
 // Plugin menu
 const FlipperApplication FLIPPER_DEBUG_APPS[] = {
 #ifdef APP_BLINK
-    {.app = blink_test_app, .name = "Blink Test", .stack_size = 1024, .icon = NULL},
+    {.app = blink_test_app,
+     .name = "Blink Test",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_VIBRO_TEST
-    {.app = vibro_test_app, .name = "Vibro Test", .stack_size = 1024, .icon = NULL},
+    {.app = vibro_test_app,
+     .name = "Vibro Test",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_KEYPAD_TEST
-    {.app = keypad_test_app, .name = "Keypad Test", .stack_size = 1024, .icon = NULL},
+    {.app = keypad_test_app,
+     .name = "Keypad Test",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_ACCESSOR
-    {.app = accessor_app, .name = "Accessor", .stack_size = 4096, .icon = NULL},
+    {.app = accessor_app,
+     .name = "Accessor",
+     .stack_size = 4096,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_USB_TEST
-    {.app = usb_test_app, .name = "USB Test", .stack_size = 1024, .icon = NULL},
+    {.app = usb_test_app,
+     .name = "USB Test",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_USB_MOUSE
-    {.app = usb_mouse_app, .name = "USB Mouse Demo", .stack_size = 1024, .icon = NULL},
+    {.app = usb_mouse_app,
+     .name = "USB Mouse Demo",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_UART_ECHO
-    {.app = uart_echo_app, .name = "Uart Echo", .stack_size = 2048, .icon = NULL},
+    {.app = uart_echo_app,
+     .name = "Uart Echo",
+     .stack_size = 2048,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_INFRARED_MONITOR
-    {.app = infrared_monitor_app, .name = "Infrared Monitor", .stack_size = 1024, .icon = NULL},
+    {.app = infrared_monitor_app,
+     .name = "Infrared Monitor",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_SCENED
-    {.app = scened_app, .name = "Templated Scene", .stack_size = 1024, .icon = NULL},
+    {.app = scened_app,
+     .name = "Templated Scene",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_LF_RFID
-    {.app = lfrfid_debug_app, .name = "LF-RFID Debug", .stack_size = 1024, .icon = NULL},
+    {.app = lfrfid_debug_app,
+     .name = "LF-RFID Debug",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef SRV_BT
-    {.app = bt_debug_app, .name = "Bluetooth Debug", .stack_size = 1024, .icon = NULL},
+    {.app = bt_debug_app,
+     .name = "Bluetooth Debug",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_UNIT_TESTS
-    {.app = delay_test_app, .name = "Delay Test", .stack_size = 1024, .icon = NULL},
+    {.app = delay_test_app,
+     .name = "Delay Test",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_DISPLAY_TEST
-    {.app = display_test_app, .name = "Display Test", .stack_size = 1024, .icon = NULL},
+    {.app = display_test_app,
+     .name = "Display Test",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_BATTERY_TEST
-    {.app = battery_test_app, .name = "Battery Test", .stack_size = 1024, .icon = NULL},
+    {.app = battery_test_app,
+     .name = "Battery Test",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_TEXT_BOX_TEST
-    {.app = text_box_test_app, .name = "Text Box Test", .stack_size = 1024, .icon = NULL},
+    {.app = text_box_test_app,
+     .name = "Text Box Test",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 };
 
 const size_t FLIPPER_DEBUG_APPS_COUNT = COUNT_OF(FLIPPER_DEBUG_APPS);
 
 #ifdef APP_ARCHIVE
-const FlipperApplication FLIPPER_ARCHIVE =
-    {.app = archive_app, .name = "Archive", .stack_size = 4096, .icon = &A_FileManager_14};
+const FlipperApplication FLIPPER_ARCHIVE = {
+    .app = archive_app,
+    .name = "Archive",
+    .stack_size = 4096,
+    .icon = &A_FileManager_14,
+    .flags = FlipperApplicationFlagDefault};
 #endif
 
 // Settings menu
 const FlipperApplication FLIPPER_SETTINGS_APPS[] = {
 #ifdef SRV_BT
-    {.app = bt_settings_app, .name = "Bluetooth", .stack_size = 1024, .icon = NULL},
+    {.app = bt_settings_app,
+     .name = "Bluetooth",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef SRV_NOTIFICATION
     {.app = notification_settings_app,
      .name = "LCD and Notifications",
      .stack_size = 1024,
-     .icon = NULL},
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef SRV_STORAGE
-    {.app = storage_settings_app, .name = "Storage", .stack_size = 2048, .icon = NULL},
+    {.app = storage_settings_app,
+     .name = "Storage",
+     .stack_size = 2048,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef SRV_POWER
-    {.app = power_settings_app, .name = "Power", .stack_size = 1024, .icon = NULL},
+    {.app = power_settings_app,
+     .name = "Power",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagInsomniaSafe},
 #endif
 
 #ifdef SRV_DESKTOP
-    {.app = desktop_settings_app, .name = "Desktop", .stack_size = 1024, .icon = NULL},
+    {.app = desktop_settings_app,
+     .name = "Desktop",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_PASSPORT
-    {.app = passport_app, .name = "Passport", .stack_size = 1024, .icon = NULL},
+    {.app = passport_app,
+     .name = "Passport",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef SRV_GUI
-    {.app = system_settings_app, .name = "System", .stack_size = 1024, .icon = NULL},
+    {.app = system_settings_app,
+     .name = "System",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 
 #ifdef APP_ABOUT
-    {.app = about_settings_app, .name = "About", .stack_size = 1024, .icon = NULL},
+    {.app = about_settings_app,
+     .name = "About",
+     .stack_size = 1024,
+     .icon = NULL,
+     .flags = FlipperApplicationFlagDefault},
 #endif
 };
 

+ 6 - 0
applications/applications.h

@@ -3,11 +3,17 @@
 #include <furi.h>
 #include <gui/icon.h>
 
+typedef enum {
+    FlipperApplicationFlagDefault = 0,
+    FlipperApplicationFlagInsomniaSafe = (1 << 0),
+} FlipperApplicationFlag;
+
 typedef struct {
     const FuriThreadCallback app;
     const char* name;
     const size_t stack_size;
     const Icon* icon;
+    const FlipperApplicationFlag flags;
 } FlipperApplication;
 
 typedef void (*FlipperOnStartHook)(void);

+ 0 - 4
applications/gui/canvas.c

@@ -17,8 +17,6 @@ const CanvasFontParameters canvas_font_params[FontTotalNumber] = {
 Canvas* canvas_init() {
     Canvas* canvas = malloc(sizeof(Canvas));
 
-    furi_hal_power_insomnia_enter();
-
     // Setup u8g2
     u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
     canvas->orientation = CanvasOrientationHorizontal;
@@ -31,8 +29,6 @@ Canvas* canvas_init() {
     canvas_clear(canvas);
     canvas_commit(canvas);
 
-    furi_hal_power_insomnia_exit();
-
     return canvas;
 }
 

+ 1 - 4
applications/ibutton/ibutton_app.cpp

@@ -41,7 +41,6 @@ iButtonApp::iButtonApp()
     : notification{"notification"}
     , storage{"storage"}
     , dialogs{"dialogs"} {
-    furi_hal_power_insomnia_enter();
     key = ibutton_key_alloc();
     key_worker = ibutton_worker_alloc();
     ibutton_worker_start_thread(key_worker);
@@ -56,8 +55,6 @@ iButtonApp::~iButtonApp() {
     ibutton_worker_stop_thread(key_worker);
     ibutton_worker_free(key_worker);
     ibutton_key_free(key);
-
-    furi_hal_power_insomnia_exit();
 }
 
 iButtonAppViewManager* iButtonApp::get_view_manager() {
@@ -342,4 +339,4 @@ void iButtonApp::make_app_folder() {
     if(!storage_simply_mkdir(storage, app_folder)) {
         dialog_message_show_storage_error(dialogs, "Cannot create\napp folder");
     }
-}
+}

+ 1 - 3
applications/lfrfid/lfrfid_app.cpp

@@ -31,11 +31,9 @@ LfRfidApp::LfRfidApp()
     , storage{"storage"}
     , dialogs{"dialogs"}
     , text_store(40) {
-    furi_hal_power_insomnia_enter();
 }
 
 LfRfidApp::~LfRfidApp() {
-    furi_hal_power_insomnia_exit();
 }
 
 void LfRfidApp::run(void* _args) {
@@ -201,4 +199,4 @@ void LfRfidApp::make_app_folder() {
     if(!storage_simply_mkdir(storage, app_folder)) {
         dialog_message_show_storage_error(dialogs, "Cannot create\napp folder");
     }
-}
+}

+ 7 - 2
applications/loader/loader.c

@@ -238,7 +238,10 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con
     if(thread_state == FuriThreadStateRunning) {
         event.type = LoaderEventTypeApplicationStarted;
         furi_pubsub_publish(loader_instance->pubsub, &event);
-        furi_hal_power_insomnia_enter();
+
+        if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) {
+            furi_hal_power_insomnia_enter();
+        }
     } else if(thread_state == FuriThreadStateStopped) {
         FURI_LOG_I(
             TAG,
@@ -251,7 +254,9 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con
             loader_instance->application_arguments = NULL;
         }
 
-        furi_hal_power_insomnia_exit();
+        if(!loader_instance->application->flags & FlipperApplicationFlagInsomniaSafe) {
+            furi_hal_power_insomnia_exit();
+        }
         loader_unlock(instance);
 
         event.type = LoaderEventTypeApplicationStopped;

+ 0 - 2
applications/nfc/nfc_worker.c

@@ -86,7 +86,6 @@ void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) {
 int32_t nfc_worker_task(void* context) {
     NfcWorker* nfc_worker = context;
 
-    furi_hal_power_insomnia_enter();
     furi_hal_nfc_exit_sleep();
 
     if(nfc_worker->state == NfcWorkerStateDetect) {
@@ -110,7 +109,6 @@ int32_t nfc_worker_task(void* context) {
     }
     furi_hal_nfc_sleep();
     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
-    furi_hal_power_insomnia_exit();
 
     return 0;
 }

+ 0 - 2
applications/storage/storage_glue.c

@@ -39,12 +39,10 @@ void storage_data_init(StorageData* storage) {
 }
 
 bool storage_data_lock(StorageData* storage) {
-    furi_hal_power_insomnia_enter();
     return (osMutexAcquire(storage->mutex, osWaitForever) == osOK);
 }
 
 bool storage_data_unlock(StorageData* storage) {
-    furi_hal_power_insomnia_exit();
     return (osMutexRelease(storage->mutex) == osOK);
 }
 

+ 1 - 1
firmware/targets/f7/ble_glue/app_conf.h

@@ -396,7 +396,7 @@ typedef enum {
  * keep debugger enabled while in any low power mode when set to 1
  * should be set to 0 in production
  */
-#define CFG_DEBUGGER_SUPPORTED 0
+#define CFG_DEBUGGER_SUPPORTED 1
 
 /**
  * When set to 1, the traces are enabled in the BLE services

+ 8 - 103
firmware/targets/f7/ble_glue/app_debug.c

@@ -1,25 +1,3 @@
-/* USER CODE BEGIN Header */
-/**
- ******************************************************************************
-  * File Name          : app_debug.c
-  * Description        : Debug capabilities source file for STM32WPAN Middleware
- ******************************************************************************
-  * @attention
-  *
-  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
-  * All rights reserved.</center></h2>
-  *
-  * This software component is licensed by ST under Ultimate Liberty license
-  * SLA0044, the "License"; You may not use this file except in compliance with
-  * the License. You may obtain a copy of the License at:
-  *                             www.st.com/SLA0044
-  *
- ******************************************************************************
- */
-/* USER CODE END Header */
-
-/* Includes ------------------------------------------------------------------*/
-/* USER CODE BEGIN Includes */
 #include "utilities_common.h"
 
 #include "app_common.h"
@@ -28,10 +6,7 @@
 #include "tl.h"
 #include "dbg_trace.h"
 #include <furi_hal.h>
-/* USER CODE END Includes */
 
-/* Private typedef -----------------------------------------------------------*/
-/* USER CODE BEGIN PTD */
 typedef PACKED_STRUCT {
     GPIO_TypeDef* port;
     uint16_t pin;
@@ -39,10 +14,7 @@ typedef PACKED_STRUCT {
     uint8_t reserved;
 }
 APPD_GpioConfig_t;
-/* USER CODE END PTD */
 
-/* Private defines -----------------------------------------------------------*/
-/* USER CODE BEGIN PD */
 #define GPIO_NBR_OF_RF_SIGNALS 9
 #define GPIO_CFG_NBR_OF_FEATURES 34
 #define NBR_OF_TRACES_CONFIG_PARAMETERS 4
@@ -51,12 +23,11 @@ APPD_GpioConfig_t;
 /**
  * THIS SHALL BE SET TO A VALUE DIFFERENT FROM 0 ONLY ON REQUEST FROM ST SUPPORT
  */
-#define BLE_DTB_CFG 7
+#define BLE_DTB_CFG 0
+// #define BLE_DTB_CFG 7
 #define SYS_DBG_CFG1 (SHCI_C2_DEBUG_OPTIONS_IPCORE_LP | SHCI_C2_DEBUG_OPTIONS_CPU2_STOP_EN)
-/* USER CODE END PD */
 
 /* Private variables ---------------------------------------------------------*/
-/* USER CODE BEGIN PV */
 PLACE_IN_SECTION("MB_MEM2")
 ALIGN(4) static SHCI_C2_DEBUG_TracesConfig_t APPD_TracesConfig = {0, 0, 0, 0};
 PLACE_IN_SECTION("MB_MEM2")
@@ -91,7 +62,7 @@ static const APPD_GpioConfig_t aGpioConfigList[GPIO_CFG_NBR_OF_FEATURES] = {
     {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IPCC_TRACES_TX - Set on Entry / Reset on Exit */
     {GPIOA, LL_GPIO_PIN_6, 1, 0}, /* HARD_FAULT - Set on Entry / Reset on Exit */
     /* From v1.1.1 */
-    {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* IP_CORE_LP_STATUS - Set on Entry / Reset on Exit */
+    {GPIOC, LL_GPIO_PIN_1, 1, 0}, /* IP_CORE_LP_STATUS - Set on Entry / Reset on Exit */
     /* From v1.2.0 */
     {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* END_OF_CONNECTION_EVENT - Set on Entry / Reset on Exit */
     {GPIOA, LL_GPIO_PIN_0, 0, 0}, /* TIMER_SERVER_CALLBACK - Toggle on Entry */
@@ -130,65 +101,20 @@ static const APPD_GpioConfig_t aRfConfigList[GPIO_NBR_OF_RF_SIGNALS] = {
     {GPIOB, LL_GPIO_PIN_10, 0, 0}, /* DTB18 - FSM4 */
 };
 #endif
-/* USER CODE END PV */
 
-/* Global variables ----------------------------------------------------------*/
-/* USER CODE BEGIN GV */
-/* USER CODE END GV */
-
-/* Private function prototypes -----------------------------------------------*/
-/* USER CODE BEGIN PFP */
 static void APPD_SetCPU2GpioConfig(void);
 static void APPD_BleDtbCfg(void);
-/* USER CODE END PFP */
-
-/* Functions Definition ------------------------------------------------------*/
-void APPD_Init(void) {
-/* USER CODE BEGIN APPD_Init */
-#if(CFG_DEBUGGER_SUPPORTED == 1)
-    /**
-   * Keep debugger enabled while in any low power mode
-   */
-    LL_DBGMCU_EnableDBGSleepMode();
-    LL_DBGMCU_EnableDBGStopMode();
-
-    /***************** ENABLE DEBUGGER *************************************/
-    LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48);
-
-#else
-    LL_GPIO_InitTypeDef gpio_config = {0};
-    LL_PWR_EnableVddUSB();
-
-    gpio_config.Mode = LL_GPIO_MODE_ANALOG;
-    gpio_config.Speed = LL_GPIO_SPEED_FREQ_LOW;
-    // gpio_config.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
-    // gpio_config.Pull = LL_GPIO_PULL_NO;
-    // gpio_config.Alternate = LL_GPIO_AF_10;
-    gpio_config.Pin = LL_GPIO_PIN_15 | LL_GPIO_PIN_14 | LL_GPIO_PIN_13;
-    LL_GPIO_Init(GPIOA, &gpio_config);
-
-    gpio_config.Pin = LL_GPIO_PIN_4 | LL_GPIO_PIN_3;
-    LL_GPIO_Init(GPIOB, &gpio_config);
-
-    LL_DBGMCU_DisableDBGSleepMode();
-    LL_DBGMCU_DisableDBGStopMode();
-    LL_DBGMCU_DisableDBGStandbyMode();
-
-#endif /* (CFG_DEBUGGER_SUPPORTED == 1) */
 
+void APPD_Init() {
 #if(CFG_DEBUG_TRACE != 0)
     DbgTraceInit();
 #endif
 
     APPD_SetCPU2GpioConfig();
     APPD_BleDtbCfg();
-
-    /* USER CODE END APPD_Init */
-    return;
 }
 
 void APPD_EnableCPU2(void) {
-    /* USER CODE BEGIN APPD_EnableCPU2 */
     SHCI_C2_DEBUG_Init_Cmd_Packet_t DebugCmdPacket = {
         {{0, 0, 0}}, /**< Does not need to be initialized */
         {(uint8_t*)aGpioConfigList,
@@ -204,6 +130,7 @@ void APPD_EnableCPU2(void) {
     /** GPIO DEBUG Initialization */
     SHCI_C2_DEBUG_Init(&DebugCmdPacket);
 
+    // We don't need External Power Amplifier
     // LL_GPIO_InitTypeDef  gpio_config;
     // gpio_config.Pull = GPIO_NOPULL;
     // gpio_config.Mode = GPIO_MODE_OUTPUT_PP;
@@ -212,17 +139,10 @@ void APPD_EnableCPU2(void) {
     // HAL_GPIO_Init(GPIOC, &gpio_config);
     // SHCI_C2_ExtpaConfig((uint32_t)GPIOC, LL_GPIO_PIN_3, EXT_PA_ENABLED_LOW, EXT_PA_ENABLED);
 
-    /* USER CODE END APPD_EnableCPU2 */
     return;
 }
 
-/*************************************************************
- *
- * LOCAL FUNCTIONS
- *
- *************************************************************/
 static void APPD_SetCPU2GpioConfig(void) {
-    /* USER CODE BEGIN APPD_SetCPU2GpioConfig */
     LL_GPIO_InitTypeDef gpio_config = {0};
     uint8_t local_loop;
     uint16_t gpioa_pin_list;
@@ -259,8 +179,9 @@ static void APPD_SetCPU2GpioConfig(void) {
     gpio_config.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
     gpio_config.Pull = LL_GPIO_PULL_NO;
 
-    gpio_config.Pin = LL_GPIO_PIN_15 | LL_GPIO_PIN_14 | LL_GPIO_PIN_13;
-    LL_GPIO_Init(GPIOA, &gpio_config);
+    // Never disable SWD, why would you?
+    // gpio_config.Pin = LL_GPIO_PIN_15 | LL_GPIO_PIN_14 | LL_GPIO_PIN_13;
+    // LL_GPIO_Init(GPIOA, &gpio_config);
 
     if(gpioa_pin_list != 0) {
         gpio_config.Pin = gpioa_pin_list;
@@ -282,13 +203,9 @@ static void APPD_SetCPU2GpioConfig(void) {
         LL_GPIO_Init(GPIOC, &gpio_config);
         LL_GPIO_ResetOutputPin(GPIOC, gpioa_pin_list);
     }
-
-    /* USER CODE END APPD_SetCPU2GpioConfig */
-    return;
 }
 
 static void APPD_BleDtbCfg(void) {
-/* USER CODE BEGIN APPD_BleDtbCfg */
 #if(BLE_DTB_CFG != 0)
     LL_GPIO_InitTypeDef gpio_config = {0};
     uint8_t local_loop;
@@ -304,11 +221,9 @@ static void APPD_BleDtbCfg(void) {
             case(uint32_t)GPIOA:
                 gpioa_pin_list |= aRfConfigList[local_loop].pin;
                 break;
-
             case(uint32_t)GPIOB:
                 gpiob_pin_list |= aRfConfigList[local_loop].pin;
                 break;
-
             default:
                 break;
             }
@@ -334,16 +249,8 @@ static void APPD_BleDtbCfg(void) {
         LL_GPIO_Init(GPIOB, &gpio_config);
     }
 #endif
-
-    /* USER CODE END APPD_BleDtbCfg */
-    return;
 }
 
-/*************************************************************
- *
- * WRAP FUNCTIONS
- *
-*************************************************************/
 #if(CFG_DEBUG_TRACE != 0)
 void DbgOutputInit(void) {
 }
@@ -353,5 +260,3 @@ void DbgOutputTraces(uint8_t* p_data, uint16_t size, void (*cb)(void)) {
     cb();
 }
 #endif
-
-/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

+ 8 - 9
firmware/targets/f7/ble_glue/ble_glue.c

@@ -63,9 +63,10 @@ void ble_glue_init() {
     LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI);
     /* Initialize the CPU2 reset value before starting CPU2 with C2BOOT */
     LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
-    furi_hal_power_insomnia_enter();
 
-    // APPD_Init();
+#ifdef BLE_GLUE_DEBUG
+    APPD_Init();
+#endif
 
     // Initialize all transport layers
     TL_MM_Config_t tl_mm_config;
@@ -198,7 +199,6 @@ bool ble_glue_start() {
     }
 
     bool ret = false;
-    furi_hal_power_insomnia_enter();
     if(ble_app_init()) {
         FURI_LOG_I(TAG, "Radio stack started");
         ble_glue->status = BleGlueStatusRadioStackRunning;
@@ -213,7 +213,6 @@ bool ble_glue_start() {
         ble_glue->status = BleGlueStatusRadioStackMissing;
         ble_app_thread_stop();
     }
-    furi_hal_power_insomnia_exit();
 
     return ret;
 }
@@ -293,8 +292,10 @@ static void ble_glue_sys_status_not_callback(SHCI_TL_CmdStatus_t status) {
  */
 static void ble_glue_sys_user_event_callback(void* pPayload) {
     UNUSED(pPayload);
-    /* Traces channel initialization */
-    // APPD_EnableCPU2( );
+
+#ifdef BLE_GLUE_DEBUG
+    APPD_EnableCPU2();
+#endif
 
     TL_AsynchEvt_t* p_sys_event =
         (TL_AsynchEvt_t*)(((tSHCI_UserEvtRxParam*)pPayload)->pckt->evtserial.evt.payload);
@@ -309,10 +310,8 @@ static void ble_glue_sys_user_event_callback(void* pPayload) {
         }
 
         ble_glue->status = BleGlueStatusC2Started;
-        furi_hal_power_insomnia_exit();
     } else if(p_sys_event->subevtcode == SHCI_SUB_EVT_ERROR_NOTIF) {
         FURI_LOG_E(TAG, "Error during initialization");
-        furi_hal_power_insomnia_exit();
     } else if(p_sys_event->subevtcode == SHCI_SUB_EVT_BLE_NVM_RAM_UPDATE) {
         SHCI_C2_BleNvmRamUpdate_Evt_t* p_sys_ble_nvm_ram_update_event =
             (SHCI_C2_BleNvmRamUpdate_Evt_t*)p_sys_event->payload;
@@ -444,4 +443,4 @@ BleGlueCommandResult ble_glue_fus_wait_operation() {
     } while(wip);
 
     return BleGlueCommandResultOK;
-}
+}

+ 19 - 8
firmware/targets/f7/ble_glue/gap.c

@@ -115,7 +115,6 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
         if(gap->enable_adv) {
             // Restart advertising
             gap_advertise_start(GapStateAdvFast);
-            furi_hal_power_insomnia_exit();
         }
         GapEvent event = {.type = GapEventTypeDisconnected};
         gap->on_event_cb(event, gap->context);
@@ -151,8 +150,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
             }
             break;
 
-        case EVT_LE_CONN_COMPLETE:
-            furi_hal_power_insomnia_enter();
+        case EVT_LE_CONN_COMPLETE: {
             hci_le_connection_complete_event_rp0* event =
                 (hci_le_connection_complete_event_rp0*)meta_evt->data;
             gap->connection_params.conn_interval = event->Conn_Interval;
@@ -169,7 +167,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) {
             gap_verify_connection_parameters(gap);
             // Start pairing by sending security request
             aci_gap_slave_security_req(event->Connection_Handle);
-            break;
+        } break;
 
         case EVT_LE_ADVERTISING_REPORT: {
             if(gap_scan) {
@@ -414,7 +412,9 @@ static void gap_advertise_start(GapState new_state) {
         // Stop advertising
         status = aci_gap_set_non_discoverable();
         if(status) {
-            FURI_LOG_E(TAG, "Stop Advertising Failed, result: %d", status);
+            FURI_LOG_E(TAG, "set_non_discoverable failed %d", status);
+        } else {
+            FURI_LOG_D(TAG, "set_non_discoverable success");
         }
     }
     // Configure advertising
@@ -431,7 +431,7 @@ static void gap_advertise_start(GapState new_state) {
         0,
         0);
     if(status) {
-        FURI_LOG_E(TAG, "Set discoverable err: %d", status);
+        FURI_LOG_E(TAG, "set_discoverable failed %d", status);
     }
     gap->state = new_state;
     GapEvent event = {.type = GapEventTypeStartAdvertising};
@@ -440,14 +440,25 @@ static void gap_advertise_start(GapState new_state) {
 }
 
 static void gap_advertise_stop() {
+    tBleStatus ret;
     if(gap->state > GapStateIdle) {
         if(gap->state == GapStateConnected) {
             // Terminate connection
-            aci_gap_terminate(gap->service.connection_handle, 0x13);
+            ret = aci_gap_terminate(gap->service.connection_handle, 0x13);
+            if(ret != BLE_STATUS_SUCCESS) {
+                FURI_LOG_E(TAG, "terminate failed %d", ret);
+            } else {
+                FURI_LOG_D(TAG, "terminate success");
+            }
         }
         // Stop advertising
         osTimerStop(gap->advertise_timer);
-        aci_gap_set_non_discoverable();
+        ret = aci_gap_set_non_discoverable();
+        if(ret != BLE_STATUS_SUCCESS) {
+            FURI_LOG_E(TAG, "set_non_discoverable failed %d", ret);
+        } else {
+            FURI_LOG_D(TAG, "set_non_discoverable success");
+        }
         gap->state = GapStateIdle;
     }
     GapEvent event = {.type = GapEventTypeStopAdvertising};

+ 2 - 2
firmware/targets/f7/furi_hal/furi_hal.c

@@ -10,10 +10,10 @@ void furi_hal_init_early() {
     furi_hal_clock_init_early();
     furi_hal_delay_init();
 
-    furi_hal_os_init();
-
     furi_hal_resources_init_early();
 
+    furi_hal_os_init();
+
     furi_hal_spi_init_early();
 
     furi_hal_i2c_init_early();

+ 21 - 11
firmware/targets/f7/furi_hal/furi_hal_bt.c

@@ -134,7 +134,6 @@ bool furi_hal_bt_start_radio_stack() {
         // Wait until C2 is started or timeout
         if(!ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT)) {
             FURI_LOG_E(TAG, "Core2 start failed");
-            LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
             ble_glue_thread_stop();
             break;
         }
@@ -154,7 +153,6 @@ bool furi_hal_bt_start_radio_stack() {
         // Starting radio stack
         if(!ble_glue_start()) {
             FURI_LOG_E(TAG, "Failed to start radio stack");
-            LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
             ble_glue_thread_stop();
             ble_app_thread_stop();
             break;
@@ -221,27 +219,39 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb,
     return ret;
 }
 
-bool furi_hal_bt_change_app(FuriHalBtProfile profile, GapEventCallback event_cb, void* context) {
-    furi_assert(event_cb);
-    furi_assert(profile < FuriHalBtProfileNumber);
-    bool ret = true;
+void furi_hal_bt_reinit() {
+    FURI_LOG_I(TAG, "Disconnect and stop advertising");
+    furi_hal_bt_stop_advertising();
 
     FURI_LOG_I(TAG, "Stop current profile services");
     current_profile->stop();
-    FURI_LOG_I(TAG, "Disconnect and stop advertising");
-    furi_hal_bt_stop_advertising();
-    FURI_LOG_I(TAG, "Shutdow 2nd core");
-    LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
+
+    // Magic happens here
+    hci_reset();
+
     FURI_LOG_I(TAG, "Stop BLE related RTOS threads");
     ble_app_thread_stop();
     gap_thread_stop();
+
     FURI_LOG_I(TAG, "Reset SHCI");
-    ble_glue_reinit_c2();
+    furi_check(ble_glue_reinit_c2());
+
     osDelay(100);
     ble_glue_thread_stop();
+
     FURI_LOG_I(TAG, "Start BT initialization");
     furi_hal_bt_init();
+
     furi_hal_bt_start_radio_stack();
+}
+
+bool furi_hal_bt_change_app(FuriHalBtProfile profile, GapEventCallback event_cb, void* context) {
+    furi_assert(event_cb);
+    furi_assert(profile < FuriHalBtProfileNumber);
+    bool ret = true;
+
+    furi_hal_bt_reinit();
+
     ret = furi_hal_bt_start_app(profile, event_cb, context);
     if(ret) {
         current_profile = &profile_config[profile];

+ 4 - 0
firmware/targets/f7/furi_hal/furi_hal_clock.c

@@ -204,6 +204,8 @@ void furi_hal_clock_switch_to_hsi() {
 
     while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI)
         ;
+
+    LL_FLASH_SetLatency(LL_FLASH_LATENCY_1);
 }
 
 void furi_hal_clock_switch_to_pll() {
@@ -215,6 +217,8 @@ void furi_hal_clock_switch_to_pll() {
     while(!LL_RCC_PLL_IsReady())
         ;
 
+    LL_FLASH_SetLatency(LL_FLASH_LATENCY_3);
+
     LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
     LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE);
 

+ 20 - 0
firmware/targets/f7/furi_hal/furi_hal_debug.c

@@ -0,0 +1,20 @@
+#include <furi_hal_debug.h>
+
+#include <stm32wbxx_ll_exti.h>
+#include <stm32wbxx_ll_system.h>
+
+void furi_hal_debug_enable() {
+    // Low power mode debug
+    LL_DBGMCU_EnableDBGSleepMode();
+    LL_DBGMCU_EnableDBGStopMode();
+    LL_DBGMCU_EnableDBGStandbyMode();
+    LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_48);
+}
+
+void furi_hal_debug_disable() {
+    // Low power mode debug
+    LL_DBGMCU_DisableDBGSleepMode();
+    LL_DBGMCU_DisableDBGStopMode();
+    LL_DBGMCU_DisableDBGStandbyMode();
+    LL_EXTI_DisableIT_32_63(LL_EXTI_LINE_48);
+}

+ 3 - 0
firmware/targets/f7/furi_hal/furi_hal_i2c.c

@@ -1,6 +1,7 @@
 #include <furi_hal_i2c.h>
 #include <furi_hal_delay.h>
 #include <furi_hal_version.h>
+#include <furi_hal_power.h>
 
 #include <stm32wbxx_ll_i2c.h>
 #include <stm32wbxx_ll_gpio.h>
@@ -23,6 +24,7 @@ void furi_hal_i2c_init() {
 }
 
 void furi_hal_i2c_acquire(FuriHalI2cBusHandle* handle) {
+    furi_hal_power_insomnia_enter();
     // Lock bus access
     handle->bus->callback(handle->bus, FuriHalI2cBusEventLock);
     // Ensuree that no active handle set
@@ -46,6 +48,7 @@ void furi_hal_i2c_release(FuriHalI2cBusHandle* handle) {
     handle->bus->current_handle = NULL;
     // Unlock bus
     handle->bus->callback(handle->bus, FuriHalI2cBusEventUnlock);
+    furi_hal_power_insomnia_exit();
 }
 
 bool furi_hal_i2c_tx(

+ 5 - 3
firmware/targets/f7/furi_hal/furi_hal_idle_timer.h

@@ -7,12 +7,14 @@
 
 // Timer used for tickless idle
 #define FURI_HAL_IDLE_TIMER_MAX 0xFFFF
-#define FURI_HAL_IDLE_TIMER LPTIM2
-#define FURI_HAL_IDLE_TIMER_IRQ LPTIM2_IRQn
+#define FURI_HAL_IDLE_TIMER LPTIM1
+#define FURI_HAL_IDLE_TIMER_IRQ LPTIM1_IRQn
 
 static inline void furi_hal_idle_timer_init() {
     // Configure clock source
-    LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_LSE);
+    LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM1_CLKSOURCE_LSE);
+    // There is a theoretical possibility that we need it
+    LL_APB1_GRP1_EnableClockSleep(LL_APB1_GRP1_PERIPH_LPTIM1);
     // Set interrupt priority and enable them
     NVIC_SetPriority(
         FURI_HAL_IDLE_TIMER_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0));

+ 59 - 30
firmware/targets/f7/furi_hal/furi_hal_interrupt.c

@@ -54,6 +54,10 @@ const IRQn_Type furi_hal_interrupt_irqn[FuriHalInterruptIdMax] = {
 
     // HSEM
     [FuriHalInterruptIdHsem] = HSEM_IRQn,
+
+    // LPTIMx
+    [FuriHalInterruptIdLpTim1] = LPTIM1_IRQn,
+    [FuriHalInterruptIdLpTim2] = LPTIM2_IRQn,
 };
 
 __attribute__((always_inline)) static inline void
@@ -82,6 +86,16 @@ void furi_hal_interrupt_init() {
 
     NVIC_SetPriority(PendSV_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0));
 
+    NVIC_SetPriority(FPU_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0));
+    NVIC_EnableIRQ(FPU_IRQn);
+
+    LL_SYSCFG_DisableIT_FPU_IOC();
+    LL_SYSCFG_DisableIT_FPU_DZC();
+    LL_SYSCFG_DisableIT_FPU_UFC();
+    LL_SYSCFG_DisableIT_FPU_OFC();
+    LL_SYSCFG_DisableIT_FPU_IDC();
+    LL_SYSCFG_DisableIT_FPU_IXC();
+
     FURI_LOG_I(TAG, "Init OK");
 }
 
@@ -120,82 +134,82 @@ void furi_hal_interrupt_set_isr_ex(
 }
 
 /* Timer 2 */
-void TIM2_IRQHandler(void) {
+void TIM2_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdTIM2);
 }
 
 /* Timer 1 Update */
-void TIM1_UP_TIM16_IRQHandler(void) {
+void TIM1_UP_TIM16_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdTim1UpTim16);
 }
 
-void TIM1_TRG_COM_TIM17_IRQHandler(void) {
+void TIM1_TRG_COM_TIM17_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdTim1TrgComTim17);
 }
 
-void TIM1_CC_IRQHandler(void) {
+void TIM1_CC_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdTim1Cc);
 }
 
 /* DMA 1 */
-void DMA1_Channel1_IRQHandler(void) {
+void DMA1_Channel1_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdDma1Ch1);
 }
 
-void DMA1_Channel2_IRQHandler(void) {
+void DMA1_Channel2_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdDma1Ch2);
 }
 
-void DMA1_Channel3_IRQHandler(void) {
+void DMA1_Channel3_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdDma1Ch3);
 }
 
-void DMA1_Channel4_IRQHandler(void) {
+void DMA1_Channel4_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdDma1Ch4);
 }
 
-void DMA1_Channel5_IRQHandler(void) {
+void DMA1_Channel5_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdDma1Ch5);
 }
 
-void DMA1_Channel6_IRQHandler(void) {
+void DMA1_Channel6_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdDma1Ch6);
 }
 
-void DMA1_Channel7_IRQHandler(void) {
+void DMA1_Channel7_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdDma1Ch7);
 }
 
 /* DMA 2 */
-void DMA2_Channel1_IRQHandler(void) {
+void DMA2_Channel1_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdDma2Ch1);
 }
 
-void DMA2_Channel2_IRQHandler(void) {
+void DMA2_Channel2_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdDma2Ch2);
 }
 
-void DMA2_Channel3_IRQHandler(void) {
+void DMA2_Channel3_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdDma2Ch3);
 }
 
-void DMA2_Channel4_IRQHandler(void) {
+void DMA2_Channel4_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdDma2Ch4);
 }
 
-void DMA2_Channel5_IRQHandler(void) {
+void DMA2_Channel5_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdDma2Ch5);
 }
 
-void DMA2_Channel6_IRQHandler(void) {
+void DMA2_Channel6_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdDma2Ch6);
 }
 
-void DMA2_Channel7_IRQHandler(void) {
+void DMA2_Channel7_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdDma2Ch7);
 }
 
-void HSEM_IRQHandler(void) {
+void HSEM_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdHsem);
 }
 
@@ -211,11 +225,11 @@ void TAMP_STAMP_LSECSS_IRQHandler(void) {
     }
 }
 
-void RCC_IRQHandler(void) {
+void RCC_IRQHandler() {
     furi_hal_interrupt_call(FuriHalInterruptIdRcc);
 }
 
-void NMI_Handler(void) {
+void NMI_Handler() {
     if(LL_RCC_IsActiveFlag_HSECSS()) {
         LL_RCC_ClearFlag_HSECSS();
         FURI_LOG_E(TAG, "HSE CSS fired: resetting system");
@@ -223,23 +237,23 @@ void NMI_Handler(void) {
     }
 }
 
-void HardFault_Handler(void) {
+void HardFault_Handler() {
     furi_crash("HardFault");
 }
 
-void MemManage_Handler(void) {
+void MemManage_Handler() {
     furi_crash("MemManage");
 }
 
-void BusFault_Handler(void) {
+void BusFault_Handler() {
     furi_crash("BusFault");
 }
 
-void UsageFault_Handler(void) {
+void UsageFault_Handler() {
     furi_crash("UsageFault");
 }
 
-void DebugMon_Handler(void) {
+void DebugMon_Handler() {
 }
 
 #include "usbd_core.h"
@@ -249,20 +263,35 @@ extern usbd_device udev;
 extern void HW_IPCC_Tx_Handler();
 extern void HW_IPCC_Rx_Handler();
 
-void SysTick_Handler(void) {
+void SysTick_Handler() {
     furi_hal_os_tick();
 }
 
-void USB_LP_IRQHandler(void) {
+void USB_LP_IRQHandler() {
 #ifndef FURI_RAM_EXEC
     usbd_poll(&udev);
 #endif
 }
 
-void IPCC_C1_TX_IRQHandler(void) {
+void USB_HP_IRQHandler() {
+}
+
+void IPCC_C1_TX_IRQHandler() {
     HW_IPCC_Tx_Handler();
 }
 
-void IPCC_C1_RX_IRQHandler(void) {
+void IPCC_C1_RX_IRQHandler() {
     HW_IPCC_Rx_Handler();
 }
+
+void FPU_IRQHandler() {
+    furi_crash("FpuFault");
+}
+
+void LPTIM1_IRQHandler() {
+    furi_hal_interrupt_call(FuriHalInterruptIdLpTim1);
+}
+
+void LPTIM2_IRQHandler() {
+    furi_hal_interrupt_call(FuriHalInterruptIdLpTim2);
+}

+ 4 - 0
firmware/targets/f7/furi_hal/furi_hal_interrupt.h

@@ -45,6 +45,10 @@ typedef enum {
     // HSEM
     FuriHalInterruptIdHsem,
 
+    // LPTIMx
+    FuriHalInterruptIdLpTim1,
+    FuriHalInterruptIdLpTim2,
+
     // Service value
     FuriHalInterruptIdMax,
 } FuriHalInterruptId;

+ 70 - 19
firmware/targets/f7/furi_hal/furi_hal_os.c

@@ -1,8 +1,10 @@
 #include <furi_hal_os.h>
 #include <furi_hal_clock.h>
 #include <furi_hal_power.h>
-#include <furi_hal_delay.h>
+#include <furi_hal_gpio.h>
+#include <furi_hal_resources.h>
 #include <furi_hal_idle_timer.h>
+
 #include <stm32wbxx_ll_cortex.h>
 
 #include <furi.h>
@@ -18,32 +20,32 @@
 #define FURI_HAL_IDLE_TIMER_TICK_PER_EPOCH (FURI_HAL_OS_IDLE_CNT_TO_TICKS(FURI_HAL_IDLE_TIMER_MAX))
 #define FURI_HAL_OS_MAX_SLEEP (FURI_HAL_IDLE_TIMER_TICK_PER_EPOCH - 1)
 
+#define FURI_HAL_OS_NVIC_IS_PENDING() (NVIC->ISPR[0] || NVIC->ISPR[1])
+#define FURI_HAL_OS_EXTI_LINE_0_31 0
+#define FURI_HAL_OS_EXTI_LINE_32_63 1
+
+// Arbitrary (but small) number for better tick consistency
+#define FURI_HAL_OS_EXTRA_CNT 3
+
 #ifdef FURI_HAL_OS_DEBUG
 #include <stm32wbxx_ll_gpio.h>
 
-#define LED_SLEEP_PORT GPIOA
-#define LED_SLEEP_PIN LL_GPIO_PIN_7
-#define LED_TICK_PORT GPIOA
-#define LED_TICK_PIN LL_GPIO_PIN_6
-#define LED_SECOND_PORT GPIOA
-#define LED_SECOND_PIN LL_GPIO_PIN_4
-
 void furi_hal_os_timer_callback() {
-    LL_GPIO_TogglePin(LED_SECOND_PORT, LED_SECOND_PIN);
+    furi_hal_gpio_write(&gpio_ext_pa4, !furi_hal_gpio_read(&gpio_ext_pa4));
 }
 #endif
 
 extern void xPortSysTickHandler();
 
-static volatile uint32_t furi_hal_os_skew = 0;
+static volatile uint32_t furi_hal_os_skew;
 
 void furi_hal_os_init() {
     furi_hal_idle_timer_init();
 
 #ifdef FURI_HAL_OS_DEBUG
-    LL_GPIO_SetPinMode(LED_SLEEP_PORT, LED_SLEEP_PIN, LL_GPIO_MODE_OUTPUT);
-    LL_GPIO_SetPinMode(LED_TICK_PORT, LED_TICK_PIN, LL_GPIO_MODE_OUTPUT);
-    LL_GPIO_SetPinMode(LED_SECOND_PORT, LED_SECOND_PIN, LL_GPIO_MODE_OUTPUT);
+    furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull);
+    furi_hal_gpio_init_simple(&gpio_ext_pa6, GpioModeOutputPushPull);
+    furi_hal_gpio_init_simple(&gpio_ext_pa4, GpioModeOutputPushPull);
     osTimerId_t second_timer = osTimerNew(furi_hal_os_timer_callback, osTimerPeriodic, NULL, NULL);
     osTimerStart(second_timer, FURI_HAL_OS_TICK_HZ);
 #endif
@@ -54,12 +56,61 @@ void furi_hal_os_init() {
 void furi_hal_os_tick() {
     if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
 #ifdef FURI_HAL_OS_DEBUG
-        LL_GPIO_TogglePin(LED_TICK_PORT, LED_TICK_PIN);
+        furi_hal_gpio_write(&gpio_ext_pa6, !furi_hal_gpio_read(&gpio_ext_pa6));
 #endif
         xPortSysTickHandler();
     }
 }
 
+#ifdef FURI_HAL_OS_DEBUG
+// Find out the IRQ number while debugging
+static void furi_hal_os_nvic_dbg_trap() {
+    for(int32_t i = WWDG_IRQn; i <= DMAMUX1_OVR_IRQn; i++) {
+        if(NVIC_GetPendingIRQ(i)) {
+            (void)i;
+            // Break here
+            __NOP();
+        }
+    }
+}
+
+// Find out the EXTI line number while debugging
+static void furi_hal_os_exti_dbg_trap(uint32_t exti, uint32_t val) {
+    for(uint32_t i = 0; val; val >>= 1U, ++i) {
+        if(val & 1U) {
+            (void)exti;
+            (void)i;
+            // Break here
+            __NOP();
+        }
+    }
+}
+#endif
+
+static inline bool furi_hal_os_is_pending_irq() {
+    if(FURI_HAL_OS_NVIC_IS_PENDING()) {
+#ifdef FURI_HAL_OS_DEBUG
+        furi_hal_os_nvic_dbg_trap();
+#endif
+        return true;
+    }
+
+    uint32_t exti_lines_active;
+    if((exti_lines_active = LL_EXTI_ReadFlag_0_31(LL_EXTI_LINE_ALL_0_31))) {
+#ifdef FURI_HAL_OS_DEBUG
+        furi_hal_os_exti_dbg_trap(FURI_HAL_OS_EXTI_LINE_0_31, exti_lines_active);
+#endif
+        return true;
+    } else if((exti_lines_active = LL_EXTI_ReadFlag_32_63(LL_EXTI_LINE_ALL_32_63))) {
+#ifdef FURI_HAL_OS_DEBUG
+        furi_hal_os_exti_dbg_trap(FURI_HAL_OS_EXTI_LINE_32_63, exti_lines_active);
+#endif
+        return true;
+    }
+
+    return false;
+}
+
 static inline uint32_t furi_hal_os_sleep(TickType_t expected_idle_ticks) {
     // Stop ticks
     furi_hal_clock_suspend_tick();
@@ -68,20 +119,20 @@ static inline uint32_t furi_hal_os_sleep(TickType_t expected_idle_ticks) {
     furi_hal_idle_timer_start(FURI_HAL_OS_TICKS_TO_IDLE_CNT(expected_idle_ticks));
 
 #ifdef FURI_HAL_OS_DEBUG
-    LL_GPIO_ResetOutputPin(LED_SLEEP_PORT, LED_SLEEP_PIN);
+    furi_hal_gpio_write(&gpio_ext_pa7, 0);
 #endif
 
     // Go to sleep mode
     furi_hal_power_sleep();
 
 #ifdef FURI_HAL_OS_DEBUG
-    LL_GPIO_SetOutputPin(LED_SLEEP_PORT, LED_SLEEP_PIN);
+    furi_hal_gpio_write(&gpio_ext_pa7, 1);
 #endif
 
     // Calculate how much time we spent in the sleep
-    uint32_t after_cnt = furi_hal_idle_timer_get_cnt() + furi_hal_os_skew;
+    uint32_t after_cnt = furi_hal_idle_timer_get_cnt() + furi_hal_os_skew + FURI_HAL_OS_EXTRA_CNT;
     uint32_t after_tick = FURI_HAL_OS_IDLE_CNT_TO_TICKS(after_cnt);
-    furi_hal_os_skew = after_cnt - (after_cnt / after_tick);
+    furi_hal_os_skew = after_cnt - FURI_HAL_OS_TICKS_TO_IDLE_CNT(after_tick);
 
     bool cmpm = LL_LPTIM_IsActiveFlag_CMPM(FURI_HAL_IDLE_TIMER);
     bool arrm = LL_LPTIM_IsActiveFlag_ARRM(FURI_HAL_IDLE_TIMER);
@@ -110,7 +161,7 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) {
     __disable_irq();
 
     // Confirm OS that sleep is still possible
-    if(eTaskConfirmSleepModeStatus() == eAbortSleep) {
+    if(eTaskConfirmSleepModeStatus() == eAbortSleep || furi_hal_os_is_pending_irq()) {
         __enable_irq();
         return;
     }

+ 91 - 8
firmware/targets/f7/furi_hal/furi_hal_power.c

@@ -2,6 +2,7 @@
 #include <furi_hal_clock.h>
 #include <furi_hal_bt.h>
 #include <furi_hal_resources.h>
+#include <furi_hal_uart.h>
 
 #include <stm32wbxx_ll_rcc.h>
 #include <stm32wbxx_ll_pwr.h>
@@ -17,6 +18,12 @@
 
 #define TAG "FuriHalPower"
 
+#ifdef FURI_HAL_POWER_DEEP_SLEEP_ENABLED
+#define FURI_HAL_POWER_DEEP_INSOMNIA 0
+#else
+#define FURI_HAL_POWER_DEEP_INSOMNIA 1
+#endif
+
 typedef struct {
     volatile uint8_t insomnia;
     volatile uint8_t deep_insomnia;
@@ -28,7 +35,7 @@ typedef struct {
 
 static volatile FuriHalPower furi_hal_power = {
     .insomnia = 0,
-    .deep_insomnia = 1,
+    .deep_insomnia = FURI_HAL_POWER_DEEP_INSOMNIA,
     .suppress_charge = 0,
 };
 
@@ -70,11 +77,6 @@ const ParamCEDV cedv = {
     .DOD100 = 3299,
 };
 
-void HAL_RCC_CSSCallback(void) {
-    // TODO: notify user about issue with HSE
-    furi_hal_power_reset();
-}
-
 void furi_hal_power_init() {
     LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
     LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN);
@@ -84,6 +86,11 @@ void furi_hal_power_init() {
     bq25896_init(&furi_hal_i2c_handle_power);
     furi_hal_i2c_release(&furi_hal_i2c_handle_power);
 
+#ifdef FURI_HAL_OS_DEBUG
+    furi_hal_gpio_init_simple(&gpio_ext_pb2, GpioModeOutputPushPull);
+    furi_hal_gpio_init_simple(&gpio_ext_pc3, GpioModeOutputPushPull);
+#endif
+
     FURI_LOG_I(TAG, "Init OK");
 }
 
@@ -140,12 +147,28 @@ void furi_hal_power_light_sleep() {
     __WFI();
 }
 
+static inline void furi_hal_power_suspend_aux_periphs() {
+    // Disable USART
+    furi_hal_uart_suspend(FuriHalUartIdUSART1);
+    furi_hal_uart_suspend(FuriHalUartIdLPUART1);
+    // TODO: Disable USB
+}
+
+static inline void furi_hal_power_resume_aux_periphs() {
+    // Re-enable USART
+    furi_hal_uart_resume(FuriHalUartIdUSART1);
+    furi_hal_uart_resume(FuriHalUartIdLPUART1);
+    // TODO: Re-enable USB
+}
+
 void furi_hal_power_deep_sleep() {
+    furi_hal_power_suspend_aux_periphs();
+
     while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID))
         ;
 
     if(!LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID)) {
-        if(LL_PWR_IsActiveFlag_C2DS()) {
+        if(LL_PWR_IsActiveFlag_C2DS() || LL_PWR_IsActiveFlag_C2SB()) {
             // Release ENTRY_STOP_MODE semaphore
             LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
 
@@ -163,7 +186,8 @@ void furi_hal_power_deep_sleep() {
     LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
 
     // Prepare deep sleep
-    LL_PWR_SetPowerMode(LL_PWR_MODE_STOP1);
+    LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2);
+    LL_C2_PWR_SetPowerMode(LL_PWR_MODE_STOP2);
     LL_LPM_EnableDeepSleep();
 
 #if defined(__CC_ARM)
@@ -173,6 +197,15 @@ void furi_hal_power_deep_sleep() {
 
     __WFI();
 
+    LL_LPM_EnableSleep();
+
+    // Make sure that values differ to prevent disaster on wfi
+    LL_PWR_SetPowerMode(LL_PWR_MODE_STOP0);
+    LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
+
+    LL_PWR_ClearFlag_C1STOP_C1STB();
+    LL_PWR_ClearFlag_C2STOP_C2STB();
+
     /* Release ENTRY_STOP_MODE semaphore */
     LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
 
@@ -184,13 +217,31 @@ void furi_hal_power_deep_sleep() {
     }
 
     LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
+
+    furi_hal_power_resume_aux_periphs();
 }
 
 void furi_hal_power_sleep() {
     if(furi_hal_power_deep_sleep_available()) {
+#ifdef FURI_HAL_OS_DEBUG
+        furi_hal_gpio_write(&gpio_ext_pc3, 1);
+#endif
+
         furi_hal_power_deep_sleep();
+
+#ifdef FURI_HAL_OS_DEBUG
+        furi_hal_gpio_write(&gpio_ext_pc3, 0);
+#endif
     } else {
+#ifdef FURI_HAL_OS_DEBUG
+        furi_hal_gpio_write(&gpio_ext_pb2, 1);
+#endif
+
         furi_hal_power_light_sleep();
+
+#ifdef FURI_HAL_OS_DEBUG
+        furi_hal_gpio_write(&gpio_ext_pb2, 0);
+#endif
     }
 }
 
@@ -215,6 +266,38 @@ bool furi_hal_power_is_charging() {
     return ret;
 }
 
+void furi_hal_power_shutdown() {
+    furi_hal_power_insomnia_enter();
+
+    furi_hal_bt_reinit();
+
+    while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID))
+        ;
+
+    if(!LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID)) {
+        if(LL_PWR_IsActiveFlag_C2DS() || LL_PWR_IsActiveFlag_C2SB()) {
+            // Release ENTRY_STOP_MODE semaphore
+            LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
+        }
+    }
+
+    // Prepare Wakeup pin
+    LL_PWR_SetWakeUpPinPolarityLow(LL_PWR_WAKEUP_PIN2);
+    LL_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN2);
+    LL_C2_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN2);
+
+    /* Release RCC semaphore */
+    LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
+
+    LL_PWR_DisableBootC2();
+    LL_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
+    LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
+    LL_LPM_EnableDeepSleep();
+
+    __WFI();
+    furi_crash("Insomniac core2");
+}
+
 void furi_hal_power_off() {
     furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
     bq25896_poweroff(&furi_hal_i2c_handle_power);

+ 25 - 14
firmware/targets/f7/furi_hal/furi_hal_resources.c

@@ -1,6 +1,9 @@
 #include <furi_hal_resources.h>
 #include <furi.h>
 
+#include <stm32wbxx_ll_rcc.h>
+#include <stm32wbxx_ll_pwr.h>
+
 const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin};
 const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin};
 
@@ -9,7 +12,7 @@ const GpioPin gpio_rf_sw_0 = {.port = RF_SW_0_GPIO_Port, .pin = RF_SW_0_Pin};
 
 const GpioPin gpio_subghz_cs = {.port = CC1101_CS_GPIO_Port, .pin = CC1101_CS_Pin};
 const GpioPin gpio_display_cs = {.port = DISPLAY_CS_GPIO_Port, .pin = DISPLAY_CS_Pin};
-const GpioPin gpio_display_rst = {.port = DISPLAY_RST_GPIO_Port, .pin = DISPLAY_RST_Pin};
+const GpioPin gpio_display_rst_n = {.port = DISPLAY_RST_GPIO_Port, .pin = DISPLAY_RST_Pin};
 const GpioPin gpio_display_di = {.port = DISPLAY_DI_GPIO_Port, .pin = DISPLAY_DI_Pin};
 const GpioPin gpio_sdcard_cs = {.port = SD_CS_GPIO_Port, .pin = SD_CS_Pin};
 const GpioPin gpio_sdcard_cd = {.port = SD_CD_GPIO_Port, .pin = SD_CD_Pin};
@@ -72,14 +75,32 @@ 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);
-    furi_hal_gpio_init_simple(&gpio_display_rst, GpioModeOutputPushPull);
+
+    // Display pins
+    furi_hal_gpio_write(&gpio_display_rst_n, 1);
+    furi_hal_gpio_init_simple(&gpio_display_rst_n, GpioModeOutputPushPull);
     furi_hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull);
 
+    // Alternative pull configuration for shutdown
+    SET_BIT(PWR->PUCRB, DISPLAY_RST_Pin);
+    CLEAR_BIT(PWR->PDCRB, DISPLAY_RST_Pin);
+    SET_BIT(PWR->CR3, PWR_CR3_APC);
+
     // Hard reset USB
     furi_hal_gpio_init_simple(&gpio_usb_dm, GpioModeOutputOpenDrain);
     furi_hal_gpio_init_simple(&gpio_usb_dp, GpioModeOutputOpenDrain);
     furi_hal_gpio_write(&gpio_usb_dm, 0);
     furi_hal_gpio_write(&gpio_usb_dp, 0);
+
+    // External header pins
+    furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+    furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+    furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+    furi_hal_gpio_init(&gpio_ext_pb2, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+    furi_hal_gpio_init(&gpio_ext_pb3, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+    furi_hal_gpio_init(&gpio_ext_pa4, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+    furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+    furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
 }
 
 void furi_hal_resources_deinit_early() {
@@ -92,19 +113,9 @@ void furi_hal_resources_init() {
             input_pins[i].gpio, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedLow);
     }
 
-    // External header pins
-    furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-    furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-    furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-    furi_hal_gpio_init(&gpio_ext_pb2, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-    furi_hal_gpio_init(&gpio_ext_pb3, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-    furi_hal_gpio_init(&gpio_ext_pa4, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-    furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-    furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
-
     // Display pins
-    furi_hal_gpio_init(&gpio_display_rst, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
-    furi_hal_gpio_write(&gpio_display_rst, 0);
+    furi_hal_gpio_init(&gpio_display_rst_n, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
+    furi_hal_gpio_write(&gpio_display_rst_n, 0);
 
     furi_hal_gpio_init(&gpio_display_di, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
     furi_hal_gpio_write(&gpio_display_di, 0);

+ 1 - 1
firmware/targets/f7/furi_hal/furi_hal_resources.h

@@ -48,7 +48,7 @@ extern const GpioPin gpio_rf_sw_0;
 
 extern const GpioPin gpio_subghz_cs;
 extern const GpioPin gpio_display_cs;
-extern const GpioPin gpio_display_rst;
+extern const GpioPin gpio_display_rst_n;
 extern const GpioPin gpio_display_di;
 extern const GpioPin gpio_sdcard_cs;
 extern const GpioPin gpio_sdcard_cd;

+ 15 - 3
firmware/targets/f7/furi_hal/furi_hal_rtc.c

@@ -1,5 +1,6 @@
 #include <furi_hal_rtc.h>
 #include <furi_hal_light.h>
+#include <furi_hal_debug.h>
 
 #include <stm32wbxx_ll_bus.h>
 #include <stm32wbxx_ll_pwr.h>
@@ -36,7 +37,6 @@ void furi_hal_rtc_init_early() {
     // LSE and RTC
     LL_PWR_EnableBkUpAccess();
     if(!RTC_CLOCK_IS_READY()) {
-        // Start LSI1 needed for CSS
         LL_RCC_LSI1_Enable();
         // Try to start LSE normal way
         LL_RCC_LSE_SetDriveCapability(LL_RCC_LSEDRIVE_HIGH);
@@ -55,8 +55,6 @@ void furi_hal_rtc_init_early() {
         }
         // Set RTC domain clock to LSE
         LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE);
-        // Enable LSE CSS
-        LL_RCC_LSE_EnableCSS();
     }
     // Enable clocking
     LL_RCC_EnableRTC();
@@ -74,6 +72,12 @@ void furi_hal_rtc_init_early() {
         data->version = FURI_HAL_RTC_HEADER_VERSION;
         furi_hal_rtc_set_register(FuriHalRtcRegisterHeader, data_reg);
     }
+
+    if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
+        furi_hal_debug_enable();
+    } else {
+        furi_hal_debug_disable();
+    }
 }
 
 void furi_hal_rtc_deinit_early() {
@@ -126,6 +130,10 @@ void furi_hal_rtc_set_flag(FuriHalRtcFlag flag) {
     DeveloperReg* data = (DeveloperReg*)&data_reg;
     data->flags |= flag;
     furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg);
+
+    if(flag & FuriHalRtcFlagDebug) {
+        furi_hal_debug_enable();
+    }
 }
 
 void furi_hal_rtc_reset_flag(FuriHalRtcFlag flag) {
@@ -133,6 +141,10 @@ void furi_hal_rtc_reset_flag(FuriHalRtcFlag flag) {
     DeveloperReg* data = (DeveloperReg*)&data_reg;
     data->flags &= ~flag;
     furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg);
+
+    if(flag & FuriHalRtcFlagDebug) {
+        furi_hal_debug_disable();
+    }
 }
 
 bool furi_hal_rtc_is_flag_set(FuriHalRtcFlag flag) {

+ 5 - 0
firmware/targets/f7/furi_hal/furi_hal_spi.c

@@ -1,5 +1,6 @@
 #include "furi_hal_spi.h"
 #include "furi_hal_resources.h"
+#include <furi_hal_power.h>
 
 #include <stdbool.h>
 #include <string.h>
@@ -55,6 +56,8 @@ void furi_hal_spi_bus_handle_deinit(FuriHalSpiBusHandle* handle) {
 void furi_hal_spi_acquire(FuriHalSpiBusHandle* handle) {
     furi_assert(handle);
 
+    furi_hal_power_insomnia_enter();
+
     handle->bus->callback(handle->bus, FuriHalSpiBusEventLock);
     handle->bus->callback(handle->bus, FuriHalSpiBusEventActivate);
 
@@ -75,6 +78,8 @@ void furi_hal_spi_release(FuriHalSpiBusHandle* handle) {
     // Bus events
     handle->bus->callback(handle->bus, FuriHalSpiBusEventDeactivate);
     handle->bus->callback(handle->bus, FuriHalSpiBusEventUnlock);
+
+    furi_hal_power_insomnia_exit();
 }
 
 static void furi_hal_spi_bus_end_txrx(FuriHalSpiBusHandle* handle, uint32_t timeout) {

+ 24 - 9
firmware/targets/f7/furi_hal/furi_hal_uart.c

@@ -8,6 +8,8 @@
 #include <furi.h>
 #include <furi_hal_delay.h>
 
+static bool furi_hal_usart_prev_enabled[2];
+
 static void (*irq_cb[2])(uint8_t ev, uint8_t data, void* context);
 static void* irq_ctx[2];
 
@@ -44,7 +46,6 @@ static void furi_hal_usart_init(uint32_t baud) {
         ;
 
     LL_USART_EnableIT_RXNE_RXFNE(USART1);
-    LL_USART_EnableIT_IDLE(USART1);
     NVIC_SetPriority(USART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
 }
 
@@ -81,7 +82,6 @@ static void furi_hal_lpuart_init(uint32_t baud) {
     furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud);
 
     LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);
-    LL_LPUART_EnableIT_IDLE(LPUART1);
     NVIC_SetPriority(LPUART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
 }
 
@@ -136,6 +136,28 @@ void furi_hal_uart_deinit(FuriHalUartId ch) {
     }
 }
 
+void furi_hal_uart_suspend(FuriHalUartId channel) {
+    if(channel == FuriHalUartIdLPUART1 && LL_LPUART_IsEnabled(LPUART1)) {
+        LL_LPUART_Disable(LPUART1);
+        furi_hal_usart_prev_enabled[channel] = true;
+    } else if(channel == FuriHalUartIdUSART1 && LL_USART_IsEnabled(USART1)) {
+        LL_USART_Disable(USART1);
+        furi_hal_usart_prev_enabled[channel] = true;
+    }
+}
+
+void furi_hal_uart_resume(FuriHalUartId channel) {
+    if(!furi_hal_usart_prev_enabled[channel]) {
+        return;
+    } else if(channel == FuriHalUartIdLPUART1) {
+        LL_LPUART_Enable(LPUART1);
+    } else if(channel == FuriHalUartIdUSART1) {
+        LL_USART_Enable(USART1);
+    }
+
+    furi_hal_usart_prev_enabled[channel] = false;
+}
+
 void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size) {
     if(ch == FuriHalUartIdUSART1) {
         if(LL_USART_IsEnabled(USART1) == 0) return;
@@ -189,22 +211,15 @@ void LPUART1_IRQHandler(void) {
     if(LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) {
         uint8_t data = LL_LPUART_ReceiveData8(LPUART1);
         irq_cb[FuriHalUartIdLPUART1](UartIrqEventRXNE, data, irq_ctx[FuriHalUartIdLPUART1]);
-    } else if(LL_LPUART_IsActiveFlag_IDLE(LPUART1)) {
-        irq_cb[FuriHalUartIdLPUART1](UartIrqEventIDLE, 0, irq_ctx[FuriHalUartIdLPUART1]);
-        LL_LPUART_ClearFlag_IDLE(LPUART1);
     } else if(LL_LPUART_IsActiveFlag_ORE(LPUART1)) {
         LL_LPUART_ClearFlag_ORE(LPUART1);
     }
-    //TODO: more events
 }
 
 void USART1_IRQHandler(void) {
     if(LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) {
         uint8_t data = LL_USART_ReceiveData8(USART1);
         irq_cb[FuriHalUartIdUSART1](UartIrqEventRXNE, data, irq_ctx[FuriHalUartIdUSART1]);
-    } else if(LL_USART_IsActiveFlag_IDLE(USART1)) {
-        irq_cb[FuriHalUartIdUSART1](UartIrqEventIDLE, 0, irq_ctx[FuriHalUartIdUSART1]);
-        LL_USART_ClearFlag_IDLE(USART1);
     } else if(LL_USART_IsActiveFlag_ORE(USART1)) {
         LL_USART_ClearFlag_ORE(USART1);
     }

+ 15 - 3
firmware/targets/f7/furi_hal/furi_hal_uart.h

@@ -27,8 +27,6 @@ typedef enum {
  */
 typedef enum {
     UartIrqEventRXNE,
-    UartIrqEventIDLE,
-    //TODO: more events
 } UartIrqEvent;
 
 /**
@@ -46,6 +44,20 @@ void furi_hal_uart_init(FuriHalUartId channel, uint32_t baud);
  */
 void furi_hal_uart_deinit(FuriHalUartId channel);
 
+/**
+ * Suspend UART operation
+ * Disables UART hardware, settings and callbacks are preserved
+ * @param channel UART channel
+ */
+void furi_hal_uart_suspend(FuriHalUartId channel);
+
+/**
+ * Resume UART operation
+ * Resumes UART hardware from suspended state
+ * @param channel UART channel
+ */
+void furi_hal_uart_resume(FuriHalUartId channel);
+
 /**
  * Changes UART baudrate
  * @param channel UART channel
@@ -74,4 +86,4 @@ void furi_hal_uart_set_irq_cb(
 
 #ifdef __cplusplus
 }
-#endif
+#endif

+ 2 - 0
firmware/targets/f7/furi_hal/furi_hal_usb.c

@@ -76,7 +76,9 @@ void furi_hal_usb_init(void) {
     usb.enabled = false;
     usb.if_cur = NULL;
     NVIC_SetPriority(USB_LP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
+    NVIC_SetPriority(USB_HP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0));
     NVIC_EnableIRQ(USB_LP_IRQn);
+    NVIC_EnableIRQ(USB_HP_IRQn);
 
     usb.thread = furi_thread_alloc();
     furi_thread_set_name(usb.thread, "UsbDriver");

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

@@ -119,6 +119,10 @@ ifeq ($(FURI_HAL_USB_VCP_DEBUG), 1)
 CFLAGS += -DFURI_HAL_USB_VCP_DEBUG
 endif
 
+ifeq ($(FURI_HAL_POWER_DEEP_SLEEP_ENABLED), 1)
+CFLAGS += -DFURI_HAL_POWER_DEEP_SLEEP_ENABLED
+endif
+
 FURI_HAL_SUBGHZ_TX_GPIO ?= 0
 ifneq ($(FURI_HAL_SUBGHZ_TX_GPIO), 0)
 CFLAGS += -DFURI_HAL_SUBGHZ_TX_GPIO=$(FURI_HAL_SUBGHZ_TX_GPIO)
@@ -128,6 +132,10 @@ ifeq ($(INVERT_RFID_IN), 1)
 CFLAGS += -DINVERT_RFID_IN
 endif
 
+ifeq ($(BLE_GLUE_DEBUG), 1)
+CFLAGS += -DBLE_GLUE_DEBUG
+endif
+
 FURI_HAL_DIR = $(TARGET_DIR)/furi_hal
 CFLAGS += -I$(FURI_HAL_DIR)
 C_SOURCES += $(wildcard $(FURI_HAL_DIR)/*.c)

+ 1 - 0
firmware/targets/furi_hal_include/furi_hal.h

@@ -12,6 +12,7 @@ template <unsigned int N> struct STOP_EXTERNING_ME {};
 #include "furi_hal_clock.h"
 #include "furi_hal_crypto.h"
 #include "furi_hal_console.h"
+#include "furi_hal_debug.h"
 #include "furi_hal_os.h"
 #include "furi_hal_sd.h"
 #include "furi_hal_i2c.h"

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

@@ -68,6 +68,12 @@ FuriHalBtStack furi_hal_bt_get_radio_stack();
 */
 bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, void* context);
 
+/** Reinitialize core2
+ * 
+ * Also can be used to prepare core2 for stop modes
+ */
+void furi_hal_bt_reinit();
+
 /** Change BLE app
  * Restarts 2nd core
  *

+ 23 - 0
firmware/targets/furi_hal_include/furi_hal_debug.h

@@ -0,0 +1,23 @@
+/**
+ * @file furi_hal_debug.h
+ * Debug HAL API
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Enable MCU debug */
+void furi_hal_debug_enable();
+
+/** Disable MCU debug */
+void furi_hal_debug_disable();
+
+#ifdef __cplusplus
+}
+#endif

+ 3 - 0
firmware/targets/furi_hal_include/furi_hal_power.h

@@ -85,6 +85,9 @@ uint8_t furi_hal_power_get_bat_health_pct();
  */
 bool furi_hal_power_is_charging();
 
+/** Switch MCU to SHUTDOWN */
+void furi_hal_power_shutdown();
+
 /** Poweroff device
  */
 void furi_hal_power_off();

+ 1 - 1
lib/u8g2/u8g2_glue.c

@@ -17,7 +17,7 @@ uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, vo
         asm("nop");
         break;
     case U8X8_MSG_GPIO_RESET:
-        furi_hal_gpio_write(&gpio_display_rst, arg_int);
+        furi_hal_gpio_write(&gpio_display_rst_n, arg_int);
         break;
     default:
         return 0;