Просмотр исходного кода

[FL-872] Furi, API-HAL, App-Loader cleanup and improvements (#334)

* Furi: replace obsolete furiac_exit with osThreadExit, drop obsolete apis and test. Rename systemd to flipper and move to separate file, cleanup. ApiHal: Rename timebase to os and move freertos hooks there, move insomnia api to power module.
* Furi: new thread helper
* Furi: cleanup thread documentation
* Flipper, AppLoader: update to use FuriThread. Update tasks signatures to match FuriThreadCallback signature.
* F4: rename API_HAL_TIMEBASE_DEBUG to API_HAL_OS_DEBUG
* Applications: rename FuriApplication to FlipperApplication, use FuriThreadCallback signature for apps.
* C++ app template sample, new exit method
あく 4 лет назад
Родитель
Сommit
b835d7a451
67 измененных файлов с 916 добавлено и 1504 удалено
  1. 46 49
      applications/app-loader/app-loader.c
  2. 39 50
      applications/applications.c
  3. 17 10
      applications/applications.h
  4. 0 15
      applications/applications.mk
  5. 3 1
      applications/backlight-control/backlight-control.c
  6. 3 1
      applications/bt/bt.c
  7. 7 5
      applications/cc1101-workaround/cc1101-workaround.cpp
  8. 3 1
      applications/cli/cli.c
  9. 3 1
      applications/coreglitch_demo_0/coreglitch_demo_0.c
  10. 3 1
      applications/dolphin/dolphin.c
  11. 3 1
      applications/examples/blink.c
  12. 3 1
      applications/examples/input_dump.c
  13. 2 2
      applications/examples/u8g2_example.c
  14. 5 3
      applications/examples/u8g2_qrcode.c
  15. 3 1
      applications/examples/uart_write.c
  16. 3 1
      applications/examples/vibro.c
  17. 1 1
      applications/floopper-bloopper
  18. 5 3
      applications/gpio-tester/gpio-tester.c
  19. 3 1
      applications/gui-test/gui-test.c
  20. 3 1
      applications/gui/gui.c
  21. 5 4
      applications/ibutton/ibutton.cpp
  22. 3 1
      applications/input/input.c
  23. 3 3
      applications/irda/irda.c
  24. 5 3
      applications/lf-rfid/lf-rfid.c
  25. 4 2
      applications/menu/menu.c
  26. 5 3
      applications/music-player/music-player.c
  27. 3 1
      applications/nfc/nfc.c
  28. 1 1
      applications/nfc/nfc_i.h
  29. 2 2
      applications/nfc/nfc_worker.c
  30. 3 1
      applications/power/power.c
  31. 2 1
      applications/sd-card-test/sd-card-test.cpp
  32. 4 4
      applications/sd-filesystem/sd-filesystem.c
  33. 2 1
      applications/sd-nfc/sdnfc.cpp
  34. 5 3
      applications/template/template.c.example
  35. 0 33
      applications/tests/furi_event_test.c
  36. 0 145
      applications/tests/furi_value_expanders_test.c
  37. 3 3
      applications/tests/furi_valuemutex_test.c
  38. 0 134
      applications/tests/furiac_test.c
  39. 0 28
      applications/tests/minunit_test.c
  40. 2 4
      applications/tests/test_index.c
  41. 26 0
      core/flipper.c
  42. 3 0
      core/flipper.h
  43. 0 30
      core/furi.c
  44. 2 3
      core/furi.h
  45. 0 24
      core/furi/event.c
  46. 0 44
      core/furi/event.h
  47. 122 0
      core/furi/thread.c
  48. 102 0
      core/furi/thread.h
  49. 0 174
      core/furi/value-expanders.c
  50. 0 115
      core/furi/value-expanders.h
  51. 19 0
      firmware/targets/api-hal-include/api-hal-power.h
  52. 2 2
      firmware/targets/api-hal-include/api-hal.h
  53. 0 25
      firmware/targets/f4/Src/app_freertos.c
  54. 120 232
      firmware/targets/f4/Src/main.c
  55. 95 0
      firmware/targets/f4/api-hal/api-hal-os-timer.h
  56. 169 0
      firmware/targets/f4/api-hal/api-hal-os.c
  57. 17 0
      firmware/targets/f4/api-hal/api-hal-os.h
  58. 15 1
      firmware/targets/f4/api-hal/api-hal-power.c
  59. 0 95
      firmware/targets/f4/api-hal/api-hal-timebase-timer.h
  60. 0 179
      firmware/targets/f4/api-hal/api-hal-timebase.c
  61. 0 37
      firmware/targets/f4/api-hal/api-hal-timebase.h
  62. 1 1
      firmware/targets/f4/api-hal/api-hal.c
  63. 2 2
      firmware/targets/f4/ble-glue/app_ble.c
  64. 2 2
      firmware/targets/f4/ble-glue/app_entry.c
  65. 3 3
      firmware/targets/f4/target.mk
  66. 5 5
      lib/app-template/app-template.cpp
  67. 4 4
      lib/app-template/app-template.h

+ 46 - 49
applications/app-loader/app-loader.c

@@ -8,20 +8,19 @@
 #include <api-hal.h>
 #include <api-hal.h>
 
 
 typedef struct {
 typedef struct {
-    osThreadAttr_t app_thread_attr;
-    osThreadId_t app_thread_id;
+    FuriThread* thread;
     ViewPort* view_port;
     ViewPort* view_port;
-    const FuriApplication* current_app;
+    const FlipperApplication* current_app;
 } AppLoaderState;
 } AppLoaderState;
 
 
 typedef struct {
 typedef struct {
     AppLoaderState* state;
     AppLoaderState* state;
-    const FuriApplication* app;
+    const FlipperApplication* app;
 } AppLoaderContext;
 } AppLoaderContext;
 
 
 // TODO add mutex for contex
 // TODO add mutex for contex
 
 
-static void render_callback(Canvas* canvas, void* _ctx) {
+static void app_loader_render_callback(Canvas* canvas, void* _ctx) {
     AppLoaderState* ctx = (AppLoaderState*)_ctx;
     AppLoaderState* ctx = (AppLoaderState*)_ctx;
 
 
     canvas_clear(canvas);
     canvas_clear(canvas);
@@ -33,77 +32,71 @@ static void render_callback(Canvas* canvas, void* _ctx) {
     canvas_draw_str(canvas, 2, 44, "press back to exit");
     canvas_draw_str(canvas, 2, 44, "press back to exit");
 }
 }
 
 
-static void input_callback(InputEvent* input_event, void* _ctx) {
+static void app_loader_input_callback(InputEvent* input_event, void* _ctx) {
     AppLoaderState* ctx = (AppLoaderState*)_ctx;
     AppLoaderState* ctx = (AppLoaderState*)_ctx;
 
 
     if(input_event->type == InputTypeShort && input_event->key == InputKeyBack) {
     if(input_event->type == InputTypeShort && input_event->key == InputKeyBack) {
-        osThreadTerminate(ctx->app_thread_id);
-        view_port_enabled_set(ctx->view_port, false);
-        api_hal_timebase_insomnia_exit();
+        furi_thread_terminate(ctx->thread);
     }
     }
 }
 }
 
 
-static void handle_menu(void* _ctx) {
+static void app_loader_menu_callback(void* _ctx) {
     AppLoaderContext* ctx = (AppLoaderContext*)_ctx;
     AppLoaderContext* ctx = (AppLoaderContext*)_ctx;
 
 
     if(ctx->app->app == NULL) return;
     if(ctx->app->app == NULL) return;
 
 
     view_port_enabled_set(ctx->state->view_port, true);
     view_port_enabled_set(ctx->state->view_port, true);
 
 
-    // TODO how to call this?
-    // furiac_wait_libs(&FLIPPER_STARTUP[i].libs);
-    api_hal_timebase_insomnia_enter();
+    api_hal_power_insomnia_enter();
 
 
     ctx->state->current_app = ctx->app;
     ctx->state->current_app = ctx->app;
-    ctx->state->app_thread_attr.name = ctx->app->name;
-    ctx->state->app_thread_attr.attr_bits = osThreadDetached;
-    ctx->state->app_thread_attr.cb_mem = NULL;
-    ctx->state->app_thread_attr.cb_size = 0;
-    ctx->state->app_thread_attr.stack_mem = NULL;
-    ctx->state->app_thread_attr.stack_size = ctx->app->stack_size;
-    ctx->state->app_thread_attr.priority = osPriorityNormal;
-    ctx->state->app_thread_attr.tz_module = 0;
-    ctx->state->app_thread_attr.reserved = 0;
-    ctx->state->app_thread_id = osThreadNew(ctx->app->app, NULL, &ctx->state->app_thread_attr);
+
+    furi_thread_set_name(ctx->state->thread, ctx->app->name);
+    furi_thread_set_stack_size(ctx->state->thread, ctx->app->stack_size);
+    furi_thread_set_callback(ctx->state->thread, ctx->app->app);
+    furi_thread_start(ctx->state->thread);
 }
 }
 
 
-static void handle_cli(string_t args, void* _ctx) {
+static void app_loader_cli_callback(string_t args, void* _ctx) {
     AppLoaderContext* ctx = (AppLoaderContext*)_ctx;
     AppLoaderContext* ctx = (AppLoaderContext*)_ctx;
 
 
     if(ctx->app->app == NULL) return;
     if(ctx->app->app == NULL) return;
 
 
     printf("Starting furi application\r\n");
     printf("Starting furi application\r\n");
 
 
-    ctx->state->current_app = ctx->app;
-    ctx->state->app_thread_attr.name = ctx->app->name;
-    ctx->state->app_thread_attr.attr_bits = osThreadDetached;
-    ctx->state->app_thread_attr.cb_mem = NULL;
-    ctx->state->app_thread_attr.cb_size = 0;
-    ctx->state->app_thread_attr.stack_mem = NULL;
-    ctx->state->app_thread_attr.stack_size = ctx->app->stack_size;
-    ctx->state->app_thread_attr.priority = osPriorityNormal;
-    ctx->state->app_thread_attr.tz_module = 0;
-    ctx->state->app_thread_attr.reserved = 0;
-    ctx->state->app_thread_id = osThreadNew(ctx->app->app, NULL, &ctx->state->app_thread_attr);
+    api_hal_power_insomnia_enter();
+
+    furi_thread_set_name(ctx->state->thread, ctx->app->name);
+    furi_thread_set_stack_size(ctx->state->thread, ctx->app->stack_size);
+    furi_thread_set_callback(ctx->state->thread, ctx->app->app);
+    furi_thread_start(ctx->state->thread);
 
 
     printf("Press any key to kill application");
     printf("Press any key to kill application");
 
 
     char c;
     char c;
     cli_read(&c, 1);
     cli_read(&c, 1);
-    osThreadTerminate(ctx->state->app_thread_id);
+    furi_thread_terminate(ctx->state->thread);
 }
 }
 
 
-void app_loader(void* p) {
-    osThreadId_t self_id = osThreadGetId();
-    furi_check(self_id);
+void app_loader_thread_state_callback(FuriThreadState state, void* context) {
+    furi_assert(context);
+    AppLoaderState* app_loader_state = context;
+    if(state == FuriThreadStateStopped) {
+        view_port_enabled_set(app_loader_state->view_port, false);
+        api_hal_power_insomnia_exit();
+    }
+}
 
 
+int32_t app_loader(void* p) {
     AppLoaderState state;
     AppLoaderState state;
-    state.app_thread_id = NULL;
+    state.thread = furi_thread_alloc();
+    furi_thread_set_state_context(state.thread, &state);
+    furi_thread_set_state_callback(state.thread, app_loader_thread_state_callback);
 
 
     state.view_port = view_port_alloc();
     state.view_port = view_port_alloc();
     view_port_enabled_set(state.view_port, false);
     view_port_enabled_set(state.view_port, false);
-    view_port_draw_callback_set(state.view_port, render_callback, &state);
-    view_port_input_callback_set(state.view_port, input_callback, &state);
+    view_port_draw_callback_set(state.view_port, app_loader_render_callback, &state);
+    view_port_input_callback_set(state.view_port, app_loader_input_callback, &state);
 
 
     ValueMutex* menu_mutex = furi_record_open("menu");
     ValueMutex* menu_mutex = furi_record_open("menu");
     Cli* cli = furi_record_open("cli");
     Cli* cli = furi_record_open("cli");
@@ -114,7 +107,7 @@ void app_loader(void* p) {
     // Main menu
     // Main menu
     with_value_mutex(
     with_value_mutex(
         menu_mutex, (Menu * menu) {
         menu_mutex, (Menu * menu) {
-            for(size_t i = 0; i < FLIPPER_APPS_size(); i++) {
+            for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
                 AppLoaderContext* ctx = furi_alloc(sizeof(AppLoaderContext));
                 AppLoaderContext* ctx = furi_alloc(sizeof(AppLoaderContext));
                 ctx->state = &state;
                 ctx->state = &state;
                 ctx->app = &FLIPPER_APPS[i];
                 ctx->app = &FLIPPER_APPS[i];
@@ -124,14 +117,14 @@ void app_loader(void* p) {
                     menu_item_alloc_function(
                     menu_item_alloc_function(
                         FLIPPER_APPS[i].name,
                         FLIPPER_APPS[i].name,
                         assets_icons_get(FLIPPER_APPS[i].icon),
                         assets_icons_get(FLIPPER_APPS[i].icon),
-                        handle_menu,
+                        app_loader_menu_callback,
                         ctx));
                         ctx));
 
 
                 // Add cli command
                 // Add cli command
                 string_t cli_name;
                 string_t cli_name;
                 string_init_set_str(cli_name, "app_");
                 string_init_set_str(cli_name, "app_");
                 string_cat_str(cli_name, FLIPPER_APPS[i].name);
                 string_cat_str(cli_name, FLIPPER_APPS[i].name);
-                cli_add_command(cli, string_get_cstr(cli_name), handle_cli, ctx);
+                cli_add_command(cli, string_get_cstr(cli_name), app_loader_cli_callback, ctx);
                 string_clear(cli_name);
                 string_clear(cli_name);
             }
             }
         });
         });
@@ -160,7 +153,7 @@ void app_loader(void* p) {
             MenuItem* menu_plugins =
             MenuItem* menu_plugins =
                 menu_item_alloc_menu("Plugins", assets_icons_get(A_Plugins_14));
                 menu_item_alloc_menu("Plugins", assets_icons_get(A_Plugins_14));
 
 
-            for(size_t i = 0; i < FLIPPER_PLUGINS_size(); i++) {
+            for(size_t i = 0; i < FLIPPER_PLUGINS_COUNT; i++) {
                 AppLoaderContext* ctx = furi_alloc(sizeof(AppLoaderContext));
                 AppLoaderContext* ctx = furi_alloc(sizeof(AppLoaderContext));
                 ctx->state = &state;
                 ctx->state = &state;
                 ctx->app = &FLIPPER_PLUGINS[i];
                 ctx->app = &FLIPPER_PLUGINS[i];
@@ -170,14 +163,14 @@ void app_loader(void* p) {
                     menu_item_alloc_function(
                     menu_item_alloc_function(
                         FLIPPER_PLUGINS[i].name,
                         FLIPPER_PLUGINS[i].name,
                         assets_icons_get(FLIPPER_PLUGINS[i].icon),
                         assets_icons_get(FLIPPER_PLUGINS[i].icon),
-                        handle_menu,
+                        app_loader_menu_callback,
                         ctx));
                         ctx));
 
 
                 // Add cli command
                 // Add cli command
                 string_t cli_name;
                 string_t cli_name;
                 string_init_set_str(cli_name, "app_");
                 string_init_set_str(cli_name, "app_");
                 string_cat_str(cli_name, FLIPPER_PLUGINS[i].name);
                 string_cat_str(cli_name, FLIPPER_PLUGINS[i].name);
-                cli_add_command(cli, string_get_cstr(cli_name), handle_cli, ctx);
+                cli_add_command(cli, string_get_cstr(cli_name), app_loader_cli_callback, ctx);
                 string_clear(cli_name);
                 string_clear(cli_name);
             }
             }
 
 
@@ -186,5 +179,9 @@ void app_loader(void* p) {
 
 
     printf("[app loader] start\r\n");
     printf("[app loader] start\r\n");
 
 
-    osThreadSuspend(self_id);
+    while(1) {
+        osThreadSuspend(osThreadGetId());
+    }
+
+    return 0;
 }
 }

+ 39 - 50
applications/applications.c

@@ -1,41 +1,40 @@
 #include "applications.h"
 #include "applications.h"
 
 
 #ifdef APP_TEST
 #ifdef APP_TEST
-void flipper_test_app(void* p);
-#endif
-
-void application_blink(void* p);
-void application_uart_write(void* p);
-void application_input_dump(void* p);
-void u8g2_example(void* p);
-void input_task(void* p);
-void menu_task(void* p);
-void coreglitch_demo_0(void* p);
-void u8g2_qrcode(void* p);
-void fatfs_list(void* p);
-void gui_task(void* p);
-void backlight_control(void* p);
-void irda(void* p);
-void app_loader(void* p);
-void cc1101_workaround(void* p);
-void lf_rfid_workaround(void* p);
-void nfc_task(void* p);
-void dolphin_task(void* p);
-void power_task(void* p);
-void bt_task(void* p);
-void sd_card_test(void* p);
-void application_vibro(void* p);
-void app_gpio_test(void* p);
-void app_ibutton(void* p);
-void cli_task(void* p);
-void music_player(void* p);
-void sdnfc(void* p);
-void floopper_bloopper(void* p);
-void sd_filesystem(void* p);
-
-void gui_test(void* p);
-
-const FuriApplication FLIPPER_SERVICES[] = {
+int32_t flipper_test_app(void* p);
+#endif
+
+int32_t application_blink(void* p);
+int32_t application_uart_write(void* p);
+int32_t application_input_dump(void* p);
+int32_t u8g2_example(void* p);
+int32_t input_task(void* p);
+int32_t menu_task(void* p);
+int32_t coreglitch_demo_0(void* p);
+int32_t u8g2_qrcode(void* p);
+int32_t gui_task(void* p);
+int32_t backlight_control(void* p);
+int32_t irda(void* p);
+int32_t app_loader(void* p);
+int32_t cc1101_workaround(void* p);
+int32_t lf_rfid_workaround(void* p);
+int32_t nfc_task(void* p);
+int32_t dolphin_task(void* p);
+int32_t power_task(void* p);
+int32_t bt_task(void* p);
+int32_t sd_card_test(void* p);
+int32_t application_vibro(void* p);
+int32_t app_gpio_test(void* p);
+int32_t app_ibutton(void* p);
+int32_t cli_task(void* p);
+int32_t music_player(void* p);
+int32_t sdnfc(void* p);
+int32_t floopper_bloopper(void* p);
+int32_t sd_filesystem(void* p);
+
+int32_t gui_test(void* p);
+
+const FlipperApplication FLIPPER_SERVICES[] = {
 #ifdef APP_CLI
 #ifdef APP_CLI
     {.app = cli_task, .name = "cli_task", .stack_size = 1024, .icon = A_Plugins_14},
     {.app = cli_task, .name = "cli_task", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 #endif
@@ -111,10 +110,6 @@ const FuriApplication FLIPPER_SERVICES[] = {
     {.app = u8g2_qrcode, .name = "u8g2_qrcode", .stack_size = 1024, .icon = A_Plugins_14},
     {.app = u8g2_qrcode, .name = "u8g2_qrcode", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 #endif
 
 
-#ifdef APP_EXAMPLE_FATFS
-    {.app = fatfs_list, .name = "fatfs_list", .stack_size = 1024, .icon = A_Plugins_14},
-#endif
-
 #ifdef APP_EXAMPLE_DISPLAY
 #ifdef APP_EXAMPLE_DISPLAY
     {.app = u8g2_example, .name = "u8g2_example", .stack_size = 1024, .icon = A_Plugins_14},
     {.app = u8g2_example, .name = "u8g2_example", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 #endif
@@ -155,12 +150,10 @@ const FuriApplication FLIPPER_SERVICES[] = {
 #endif
 #endif
 };
 };
 
 
-size_t FLIPPER_SERVICES_size() {
-    return sizeof(FLIPPER_SERVICES) / sizeof(FuriApplication);
-}
+const size_t FLIPPER_SERVICES_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(FlipperApplication);
 
 
 // Main menu APP
 // Main menu APP
-const FuriApplication FLIPPER_APPS[] = {
+const FlipperApplication FLIPPER_APPS[] = {
 #ifdef BUILD_CC1101
 #ifdef BUILD_CC1101
     {.app = cc1101_workaround, .name = "Sub-1 GHz", .stack_size = 1024, .icon = A_Sub1ghz_14},
     {.app = cc1101_workaround, .name = "Sub-1 GHz", .stack_size = 1024, .icon = A_Sub1ghz_14},
 #endif
 #endif
@@ -182,12 +175,10 @@ const FuriApplication FLIPPER_APPS[] = {
 #endif
 #endif
 };
 };
 
 
-size_t FLIPPER_APPS_size() {
-    return sizeof(FLIPPER_APPS) / sizeof(FuriApplication);
-}
+const size_t FLIPPER_APPS_COUNT = sizeof(FLIPPER_APPS) / sizeof(FlipperApplication);
 
 
 // Plugin menu
 // Plugin menu
-const FuriApplication FLIPPER_PLUGINS[] = {
+const FlipperApplication FLIPPER_PLUGINS[] = {
 #ifdef BUILD_EXAMPLE_BLINK
 #ifdef BUILD_EXAMPLE_BLINK
     {.app = application_blink, .name = "blink", .stack_size = 1024, .icon = A_Plugins_14},
     {.app = application_blink, .name = "blink", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 #endif
@@ -231,6 +222,4 @@ const FuriApplication FLIPPER_PLUGINS[] = {
 #endif
 #endif
 };
 };
 
 
-size_t FLIPPER_PLUGINS_size() {
-    return sizeof(FLIPPER_PLUGINS) / sizeof(FuriApplication);
-}
+const size_t FLIPPER_PLUGINS_COUNT = sizeof(FLIPPER_PLUGINS) / sizeof(FlipperApplication);

+ 17 - 10
applications/applications.h

@@ -3,20 +3,27 @@
 #include <furi.h>
 #include <furi.h>
 #include <assets_icons.h>
 #include <assets_icons.h>
 
 
-typedef void (*FlipperApplication)(void*);
-
 typedef struct {
 typedef struct {
-    const FlipperApplication app;
+    const FuriThreadCallback app;
     const char* name;
     const char* name;
     const size_t stack_size;
     const size_t stack_size;
     const IconName icon;
     const IconName icon;
-} FuriApplication;
+} FlipperApplication;
 
 
-extern const FuriApplication FLIPPER_SERVICES[];
-size_t FLIPPER_SERVICES_size();
+/* Services list
+ * Spawned on startup
+ */
+extern const FlipperApplication FLIPPER_SERVICES[];
+extern const size_t FLIPPER_SERVICES_COUNT;
 
 
-extern const FuriApplication FLIPPER_APPS[];
-size_t FLIPPER_APPS_size();
+/* Apps list
+ * Spawned by app-loader
+ */
+extern const FlipperApplication FLIPPER_APPS[];
+extern const size_t FLIPPER_APPS_COUNT;
 
 
-extern const FuriApplication FLIPPER_PLUGINS[];
-size_t FLIPPER_PLUGINS_size();
+/* Plugins list
+ * Spawned by app-loader
+ */
+extern const FlipperApplication FLIPPER_PLUGINS[];
+extern const size_t FLIPPER_PLUGINS_COUNT;

+ 0 - 15
applications/applications.mk

@@ -78,15 +78,12 @@ endif
 APP_TEST	?= 0
 APP_TEST	?= 0
 ifeq ($(APP_TEST), 1)
 ifeq ($(APP_TEST), 1)
 CFLAGS		+= -DAPP_TEST
 CFLAGS		+= -DAPP_TEST
-C_SOURCES	+= $(APP_DIR)/tests/furiac_test.c
 C_SOURCES	+= $(APP_DIR)/tests/furi_record_test.c
 C_SOURCES	+= $(APP_DIR)/tests/furi_record_test.c
 C_SOURCES	+= $(APP_DIR)/tests/test_index.c
 C_SOURCES	+= $(APP_DIR)/tests/test_index.c
 C_SOURCES	+= $(APP_DIR)/tests/minunit_test.c
 C_SOURCES	+= $(APP_DIR)/tests/minunit_test.c
 C_SOURCES	+= $(APP_DIR)/tests/furi_valuemutex_test.c
 C_SOURCES	+= $(APP_DIR)/tests/furi_valuemutex_test.c
 C_SOURCES	+= $(APP_DIR)/tests/furi_pubsub_test.c
 C_SOURCES	+= $(APP_DIR)/tests/furi_pubsub_test.c
 C_SOURCES	+= $(APP_DIR)/tests/furi_memmgr_test.c
 C_SOURCES	+= $(APP_DIR)/tests/furi_memmgr_test.c
-C_SOURCES	+= $(APP_DIR)/tests/furi_value_expanders_test.c
-C_SOURCES	+= $(APP_DIR)/tests/furi_event_test.c
 endif
 endif
 
 
 APP_EXAMPLE_BLINK ?= 0
 APP_EXAMPLE_BLINK ?= 0
@@ -147,18 +144,6 @@ C_SOURCES	+= $(APP_DIR)/examples/u8g2_qrcode.c
 C_SOURCES	+= $(LIB_DIR)/qrcode/qrcode.c
 C_SOURCES	+= $(LIB_DIR)/qrcode/qrcode.c
 endif
 endif
 
 
-APP_EXAMPLE_FATFS ?= 0
-ifeq ($(APP_EXAMPLE_FATFS), 1)
-CFLAGS		+= -DAPP_EXAMPLE_FATFS
-BUILD_EXAMPLE_FATFS = 1
-endif
-BUILD_EXAMPLE_FATFS ?= 0
-ifeq ($(BUILD_EXAMPLE_FATFS), 1)
-CFLAGS		+= -DBUILD_EXAMPLE_FATFS
-C_SOURCES	+= $(APP_DIR)/examples/fatfs_list.c
-APP_INPUT = 1
-endif
-
 APP_CC1101 ?= 0
 APP_CC1101 ?= 0
 ifeq ($(APP_CC1101), 1)
 ifeq ($(APP_CC1101), 1)
 CFLAGS		+= -DAPP_CC1101
 CFLAGS		+= -DAPP_CC1101

+ 3 - 1
applications/backlight-control/backlight-control.c

@@ -7,7 +7,7 @@ static void event_cb(const void* value, void* ctx) {
     osThreadFlagsSet((osThreadId_t)ctx, BACKLIGHT_FLAG_ACTIVITY);
     osThreadFlagsSet((osThreadId_t)ctx, BACKLIGHT_FLAG_ACTIVITY);
 }
 }
 
 
-void backlight_control(void* p) {
+int32_t backlight_control(void* p) {
     // TODO open record
     // TODO open record
     const GpioPin* backlight_record = &backlight_gpio;
     const GpioPin* backlight_record = &backlight_gpio;
 
 
@@ -28,4 +28,6 @@ void backlight_control(void* p) {
             gpio_write(backlight_record, false);
             gpio_write(backlight_record, false);
         }
         }
     }
     }
+
+    return 0;
 }
 }

+ 3 - 1
applications/bt/bt.c

@@ -37,7 +37,7 @@ void bt_cli_info(string_t args, void* context) {
     string_clear(buffer);
     string_clear(buffer);
 }
 }
 
 
-void bt_task() {
+int32_t bt_task() {
     Bt* bt = bt_alloc();
     Bt* bt = bt_alloc();
 
 
     furi_record_create("bt", bt);
     furi_record_create("bt", bt);
@@ -48,4 +48,6 @@ void bt_task() {
         view_port_enabled_set(bt->statusbar_view_port, api_hal_bt_is_alive());
         view_port_enabled_set(bt->statusbar_view_port, api_hal_bt_is_alive());
         osDelay(1024);
         osDelay(1024);
     }
     }
+
+    return 0;
 }
 }

+ 7 - 5
applications/cc1101-workaround/cc1101-workaround.cpp

@@ -345,7 +345,7 @@ static void input_callback(InputEvent* input_event, void* ctx) {
     osMessageQueuePut(event_queue, &event, 0, 0);
     osMessageQueuePut(event_queue, &event, 0, 0);
 }
 }
 
 
-extern "C" void cc1101_workaround(void* p) {
+extern "C" int32_t cc1101_workaround(void* p) {
     osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
     osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
     furi_check(event_queue);
     furi_check(event_queue);
 
 
@@ -363,7 +363,7 @@ extern "C" void cc1101_workaround(void* p) {
     ValueMutex state_mutex;
     ValueMutex state_mutex;
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
         printf("[cc1101] cannot create mutex\r\n");
         printf("[cc1101] cannot create mutex\r\n");
-        furiac_exit(NULL);
+        return 255;
     }
     }
 
 
     ViewPort* view_port = view_port_alloc();
     ViewPort* view_port = view_port_alloc();
@@ -375,7 +375,7 @@ extern "C" void cc1101_workaround(void* p) {
     Gui* gui = (Gui*)furi_record_open("gui");
     Gui* gui = (Gui*)furi_record_open("gui");
     if(gui == NULL) {
     if(gui == NULL) {
         printf("[cc1101] gui is not available\r\n");
         printf("[cc1101] gui is not available\r\n");
-        furiac_exit(NULL);
+        return 255;
     }
     }
     gui_add_view_port(gui, view_port, GuiLayerFullscreen);
     gui_add_view_port(gui, view_port, GuiLayerFullscreen);
 
 
@@ -397,7 +397,7 @@ extern "C" void cc1101_workaround(void* p) {
         printf("[cc1101] init done: %d\r\n", address);
         printf("[cc1101] init done: %d\r\n", address);
     } else {
     } else {
         printf("[cc1101] init fail\r\n");
         printf("[cc1101] init fail\r\n");
-        furiac_exit(NULL);
+        return 255;
     }
     }
 
 
     cc1101.SpiStrobe(CC1101_SIDLE);
     cc1101.SpiStrobe(CC1101_SIDLE);
@@ -439,7 +439,7 @@ extern "C" void cc1101_workaround(void* p) {
 
 
                     // TODO remove all view_ports create by app
                     // TODO remove all view_ports create by app
                     view_port_enabled_set(view_port, false);
                     view_port_enabled_set(view_port, false);
-                    furiac_exit(NULL);
+                    return 255;
                 }
                 }
 
 
                 if(event.value.input.type == InputTypeShort &&
                 if(event.value.input.type == InputTypeShort &&
@@ -597,4 +597,6 @@ extern "C" void cc1101_workaround(void* p) {
         release_mutex(&state_mutex, state);
         release_mutex(&state_mutex, state);
         view_port_update(view_port);
         view_port_update(view_port);
     }
     }
+
+    return 0;
 }
 }

+ 3 - 1
applications/cli/cli.c

@@ -172,7 +172,7 @@ void cli_add_command(Cli* cli, const char* name, CliCallback callback, void* con
     string_clear(name_str);
     string_clear(name_str);
 }
 }
 
 
-void cli_task(void* p) {
+int32_t cli_task(void* p) {
     Cli* cli = cli_alloc();
     Cli* cli = cli_alloc();
 
 
     // Init basic cli commands
     // Init basic cli commands
@@ -184,4 +184,6 @@ void cli_task(void* p) {
     while(1) {
     while(1) {
         cli_process_input(cli);
         cli_process_input(cli);
     }
     }
+
+    return 0;
 }
 }

+ 3 - 1
applications/coreglitch_demo_0/coreglitch_demo_0.c

@@ -3,7 +3,7 @@
 
 
 extern TIM_HandleTypeDef SPEAKER_TIM;
 extern TIM_HandleTypeDef SPEAKER_TIM;
 
 
-void coreglitch_demo_0(void* p) {
+int32_t coreglitch_demo_0(void* p) {
     printf("coreglitch demo!\r\n");
     printf("coreglitch demo!\r\n");
 
 
     float notes[] = {
     float notes[] = {
@@ -47,4 +47,6 @@ void coreglitch_demo_0(void* p) {
             delay(100);
             delay(100);
         }
         }
     }
     }
+
+    return 0;
 }
 }

+ 3 - 1
applications/dolphin/dolphin.c

@@ -129,7 +129,7 @@ void dolphin_deed(Dolphin* dolphin, DolphinDeed deed) {
     furi_check(osMessageQueuePut(dolphin->event_queue, &event, 0, osWaitForever) == osOK);
     furi_check(osMessageQueuePut(dolphin->event_queue, &event, 0, osWaitForever) == osOK);
 }
 }
 
 
-void dolphin_task() {
+int32_t dolphin_task() {
     Dolphin* dolphin = dolphin_alloc();
     Dolphin* dolphin = dolphin_alloc();
 
 
     Gui* gui = furi_record_open("gui");
     Gui* gui = furi_record_open("gui");
@@ -163,4 +163,6 @@ void dolphin_task() {
             dolphin_state_save(dolphin->state);
             dolphin_state_save(dolphin->state);
         }
         }
     }
     }
+
+    return 0;
 }
 }

+ 3 - 1
applications/examples/blink.c

@@ -12,7 +12,7 @@ void rgb_set(
     gpio_write(led_b, !b);
     gpio_write(led_b, !b);
 }
 }
 
 
-void application_blink(void* p) {
+int32_t application_blink(void* p) {
     // TODO open record
     // TODO open record
     const GpioPin* led_r_record = &led_gpio[0];
     const GpioPin* led_r_record = &led_gpio[0];
     const GpioPin* led_g_record = &led_gpio[1];
     const GpioPin* led_g_record = &led_gpio[1];
@@ -41,4 +41,6 @@ void application_blink(void* p) {
         rgb_set(0, 0, 0, led_r_record, led_g_record, led_b_record);
         rgb_set(0, 0, 0, led_r_record, led_g_record, led_b_record);
         delay(500);
         delay(500);
     }
     }
+
+    return 0;
 }
 }

+ 3 - 1
applications/examples/input_dump.c

@@ -13,7 +13,7 @@ static void event_cb(const void* value, void* ctx) {
     printf("event: %02x %s\r\n", event->key, event->type ? "pressed" : "released");
     printf("event: %02x %s\r\n", event->key, event->type ? "pressed" : "released");
 }
 }
 
 
-void application_input_dump(void* p) {
+int32_t application_input_dump(void* p) {
     // open record
     // open record
     PubSub* event_record = furi_record_open("input_events");
     PubSub* event_record = furi_record_open("input_events");
     subscribe_pubsub(event_record, event_cb, NULL);
     subscribe_pubsub(event_record, event_cb, NULL);
@@ -23,4 +23,6 @@ void application_input_dump(void* p) {
     for(;;) {
     for(;;) {
         delay(100);
         delay(100);
     }
     }
+
+    return 0;
 }
 }

+ 2 - 2
applications/examples/u8g2_example.c

@@ -1,7 +1,7 @@
 #include "u8g2/u8g2.h"
 #include "u8g2/u8g2.h"
 #include <furi.h>
 #include <furi.h>
 
 
-void u8g2_example(void* p) {
+int32_t u8g2_example(void* p) {
     // open record
     // open record
     u8g2_t* fb = furi_record_open("u8g2_fb");
     u8g2_t* fb = furi_record_open("u8g2_fb");
     u8g2_SetFont(fb, u8g2_font_6x10_mf);
     u8g2_SetFont(fb, u8g2_font_6x10_mf);
@@ -10,5 +10,5 @@ void u8g2_example(void* p) {
     u8g2_DrawStr(fb, 2, 12, "hello world!");
     u8g2_DrawStr(fb, 2, 12, "hello world!");
     furi_record_close("u8g2_fb");
     furi_record_close("u8g2_fb");
 
 
-    furiac_exit(NULL);
+    return 0;
 }
 }

+ 5 - 3
applications/examples/u8g2_qrcode.c

@@ -13,7 +13,7 @@ void u8g2_DrawPixelSize(u8g2_t* u8g2, uint8_t x, uint8_t y, uint8_t size) {
     }
     }
 }
 }
 
 
-void u8g2_qrcode(void* p) {
+int32_t u8g2_qrcode(void* p) {
     // open record
     // open record
     FuriRecordSubscriber* fb_record =
     FuriRecordSubscriber* fb_record =
         furi_open_deprecated("u8g2_fb", false, false, NULL, NULL, NULL);
         furi_open_deprecated("u8g2_fb", false, false, NULL, NULL, NULL);
@@ -38,7 +38,7 @@ void u8g2_qrcode(void* p) {
 
 
     if(fb_record == NULL) {
     if(fb_record == NULL) {
         printf("[view_port] cannot create fb record\r\n");
         printf("[view_port] cannot create fb record\r\n");
-        furiac_exit(NULL);
+        return 255;
     }
     }
 
 
     u8g2_t* fb = furi_take(fb_record);
     u8g2_t* fb = furi_take(fb_record);
@@ -63,12 +63,14 @@ void u8g2_qrcode(void* p) {
                 }
                 }
             }
             }
         } else {
         } else {
-            furiac_exit(NULL);
+            return 255;
         }
         }
 
 
         furi_commit(fb_record);
         furi_commit(fb_record);
 
 
         delay(1);
         delay(1);
     }
     }
+
+    return 0;
 }
 }
 */
 */

+ 3 - 1
applications/examples/uart_write.c

@@ -1,7 +1,7 @@
 #include <furi.h>
 #include <furi.h>
 #include <string.h>
 #include <string.h>
 
 
-void application_uart_write(void* p) {
+int32_t application_uart_write(void* p) {
     // Red led for showing progress
     // Red led for showing progress
     GpioPin led = {.pin = GPIO_PIN_8, .port = GPIOA};
     GpioPin led = {.pin = GPIO_PIN_8, .port = GPIOA};
     // TODO open record
     // TODO open record
@@ -29,4 +29,6 @@ void application_uart_write(void* p) {
         // delay with overall perion of 1s
         // delay with overall perion of 1s
         delay(950);
         delay(950);
     }
     }
+
+    return 0;
 }
 }

+ 3 - 1
applications/examples/vibro.c

@@ -21,7 +21,7 @@ static void button_handler(const void* value, void* _ctx) {
     }
     }
 }
 }
 
 
-void application_vibro(void* p) {
+int32_t application_vibro(void* p) {
     Ctx ctx = {.led = (GpioPin*)&led_gpio[1], .vibro = (GpioPin*)&vibro_gpio};
     Ctx ctx = {.led = (GpioPin*)&led_gpio[1], .vibro = (GpioPin*)&vibro_gpio};
 
 
     gpio_init(ctx.led, GpioModeOutputOpenDrain);
     gpio_init(ctx.led, GpioModeOutputOpenDrain);
@@ -37,4 +37,6 @@ void application_vibro(void* p) {
     while(1) {
     while(1) {
         osDelay(osWaitForever);
         osDelay(osWaitForever);
     }
     }
+
+    return 0;
 }
 }

+ 1 - 1
applications/floopper-bloopper

@@ -1 +1 @@
-Subproject commit 598edd54197d58b35d8cc2513549ddda3938fa38
+Subproject commit a68a7eb200712b24c16a62713a5d137152805cd1

+ 5 - 3
applications/gpio-tester/gpio-tester.c

@@ -57,7 +57,7 @@ static void input_callback(InputEvent* input_event, void* ctx) {
     osMessageQueuePut(event_queue, &event, 0, 0);
     osMessageQueuePut(event_queue, &event, 0, 0);
 }
 }
 
 
-void app_gpio_test(void* p) {
+int32_t app_gpio_test(void* p) {
     osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
     osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
     furi_check(event_queue);
     furi_check(event_queue);
 
 
@@ -67,7 +67,7 @@ void app_gpio_test(void* p) {
     ValueMutex state_mutex;
     ValueMutex state_mutex;
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
         printf("[gpio-tester] cannot create mutex\r\n");
         printf("[gpio-tester] cannot create mutex\r\n");
-        furiac_exit(NULL);
+        return 255;
     }
     }
 
 
     ViewPort* view_port = view_port_alloc();
     ViewPort* view_port = view_port_alloc();
@@ -98,7 +98,7 @@ void app_gpio_test(void* p) {
                     printf("[gpio-tester] bye!\r\n");
                     printf("[gpio-tester] bye!\r\n");
                     // TODO remove all view_ports create by app
                     // TODO remove all view_ports create by app
                     view_port_enabled_set(view_port, false);
                     view_port_enabled_set(view_port, false);
-                    furiac_exit(NULL);
+                    return 0;
                 }
                 }
 
 
                 if(event.value.input.type == InputTypeShort &&
                 if(event.value.input.type == InputTypeShort &&
@@ -130,4 +130,6 @@ void app_gpio_test(void* p) {
         release_mutex(&state_mutex, state);
         release_mutex(&state_mutex, state);
         view_port_update(view_port);
         view_port_update(view_port);
     }
     }
+
+    return 0;
 }
 }

+ 3 - 1
applications/gui-test/gui-test.c

@@ -92,7 +92,7 @@ void text_input_callback(void* context, char* text) {
     next_view(context);
     next_view(context);
 }
 }
 
 
-void gui_test(void* param) {
+int32_t gui_test(void* param) {
     (void)param;
     (void)param;
     GuiTester* gui_tester = gui_test_alloc();
     GuiTester* gui_tester = gui_test_alloc();
 
 
@@ -154,4 +154,6 @@ void gui_test(void* param) {
     while(1) {
     while(1) {
         osDelay(1000);
         osDelay(1000);
     }
     }
+
+    return 0;
 }
 }

+ 3 - 1
applications/gui/gui.c

@@ -280,7 +280,7 @@ Gui* gui_alloc() {
     return gui;
     return gui;
 }
 }
 
 
-void gui_task(void* p) {
+int32_t gui_task(void* p) {
     Gui* gui = gui_alloc();
     Gui* gui = gui_alloc();
 
 
     furi_record_create("gui", gui);
     furi_record_create("gui", gui);
@@ -302,4 +302,6 @@ void gui_task(void* p) {
             gui_redraw(gui);
             gui_redraw(gui);
         }
         }
     }
     }
+
+    return 0;
 }
 }

+ 5 - 4
applications/ibutton/ibutton.cpp

@@ -23,7 +23,7 @@ void AppiButton::run() {
     gpio_init(red_led_record, GpioModeOutputOpenDrain);
     gpio_init(red_led_record, GpioModeOutputOpenDrain);
     gpio_init(green_led_record, GpioModeOutputOpenDrain);
     gpio_init(green_led_record, GpioModeOutputOpenDrain);
 
 
-    api_hal_timebase_insomnia_enter();
+    api_hal_power_insomnia_enter();
     app_ready();
     app_ready();
 
 
     AppiButtonEvent event;
     AppiButtonEvent event;
@@ -35,9 +35,9 @@ void AppiButton::run() {
                    event.value.input.key == InputKeyBack) {
                    event.value.input.key == InputKeyBack) {
                     view_port_enabled_set(view_port, false);
                     view_port_enabled_set(view_port, false);
                     gui_remove_view_port(gui, view_port);
                     gui_remove_view_port(gui, view_port);
-                    api_hal_timebase_insomnia_exit();
+                    api_hal_power_insomnia_exit();
 
 
-                    osThreadExit();
+                    return;
                 }
                 }
 
 
                 if(event.value.input.type == InputTypeShort &&
                 if(event.value.input.type == InputTypeShort &&
@@ -174,7 +174,8 @@ void AppiButton::switch_to_mode(uint8_t mode_index) {
 }
 }
 
 
 // app enter function
 // app enter function
-extern "C" void app_ibutton(void* p) {
+extern "C" int32_t app_ibutton(void* p) {
     AppiButton* app = new AppiButton();
     AppiButton* app = new AppiButton();
     app->run();
     app->run();
+    return 0;
 }
 }

+ 3 - 1
applications/input/input.c

@@ -18,7 +18,7 @@ void input_isr(void* _pin, void* _ctx) {
     osThreadFlagsSet(input->thread, INPUT_THREAD_FLAG_ISR);
     osThreadFlagsSet(input->thread, INPUT_THREAD_FLAG_ISR);
 }
 }
 
 
-void input_task() {
+int32_t input_task() {
     input = furi_alloc(sizeof(Input));
     input = furi_alloc(sizeof(Input));
     input->thread = osThreadGetId();
     input->thread = osThreadGetId();
     init_pubsub(&input->event_pubsub);
     init_pubsub(&input->event_pubsub);
@@ -69,4 +69,6 @@ void input_task() {
             osThreadFlagsWait(INPUT_THREAD_FLAG_ISR, osFlagsWaitAny, osWaitForever);
             osThreadFlagsWait(INPUT_THREAD_FLAG_ISR, osFlagsWaitAny, osWaitForever);
         }
         }
     }
     }
+
+    return 0;
 }
 }

+ 3 - 3
applications/irda/irda.c

@@ -234,7 +234,7 @@ void init_packet(
     state->packets[index].command = command;
     state->packets[index].command = command;
 }
 }
 
 
-void irda(void* p) {
+int32_t irda(void* p) {
     osMessageQueueId_t event_queue = osMessageQueueNew(32, sizeof(AppEvent), NULL);
     osMessageQueueId_t event_queue = osMessageQueueNew(32, sizeof(AppEvent), NULL);
 
 
     State _state;
     State _state;
@@ -262,7 +262,7 @@ void irda(void* p) {
     ValueMutex state_mutex;
     ValueMutex state_mutex;
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
         printf("cannot create mutex\r\n");
         printf("cannot create mutex\r\n");
-        furiac_exit(NULL);
+        return 255;
     }
     }
 
 
     ViewPort* view_port = view_port_alloc();
     ViewPort* view_port = view_port_alloc();
@@ -316,7 +316,7 @@ void irda(void* p) {
                     osMessageQueueDelete(event_queue);
                     osMessageQueueDelete(event_queue);
 
 
                     // exit
                     // exit
-                    furiac_exit(NULL);
+                    return 0;
                 }
                 }
 
 
                 if(event.value.input.type == InputTypeShort &&
                 if(event.value.input.type == InputTypeShort &&

+ 5 - 3
applications/lf-rfid/lf-rfid.c

@@ -160,7 +160,7 @@ static void extract_data(uint8_t* buf, uint8_t* customer, uint32_t* em_data) {
     *em_data = data;
     *em_data = data;
 }
 }
 
 
-void lf_rfid_workaround(void* p) {
+int32_t lf_rfid_workaround(void* p) {
     osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
     osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
 
 
     // create pin
     // create pin
@@ -195,7 +195,7 @@ void lf_rfid_workaround(void* p) {
     ValueMutex state_mutex;
     ValueMutex state_mutex;
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
         printf("cannot create mutex\r\n");
         printf("cannot create mutex\r\n");
-        furiac_exit(NULL);
+        return 255;
     }
     }
 
 
     ViewPort* view_port = view_port_alloc();
     ViewPort* view_port = view_port_alloc();
@@ -293,7 +293,7 @@ void lf_rfid_workaround(void* p) {
 
 
                         // TODO remove all view_ports create by app
                         // TODO remove all view_ports create by app
                         view_port_enabled_set(view_port, false);
                         view_port_enabled_set(view_port, false);
-                        furiac_exit(NULL);
+                        return 255;
                     }
                     }
 
 
                     if(event.value.input.type == InputTypePress &&
                     if(event.value.input.type == InputTypePress &&
@@ -356,4 +356,6 @@ void lf_rfid_workaround(void* p) {
             release_mutex(&state_mutex, state);
             release_mutex(&state_mutex, state);
         }
         }
     }
     }
+
+    return 0;
 }
 }

+ 4 - 2
applications/menu/menu.c

@@ -35,7 +35,7 @@ ValueMutex* menu_init() {
     ValueMutex* menu_mutex = furi_alloc(sizeof(ValueMutex));
     ValueMutex* menu_mutex = furi_alloc(sizeof(ValueMutex));
     if(menu_mutex == NULL || !init_mutex(menu_mutex, menu, sizeof(Menu))) {
     if(menu_mutex == NULL || !init_mutex(menu_mutex, menu, sizeof(Menu))) {
         printf("[menu_task] cannot create menu mutex\r\n");
         printf("[menu_task] cannot create menu mutex\r\n");
-        furiac_exit(NULL);
+        furi_check(0);
     }
     }
 
 
     // OpenGui record
     // OpenGui record
@@ -223,7 +223,7 @@ void menu_exit(Menu* menu) {
     menu_update(menu);
     menu_update(menu);
 }
 }
 
 
-void menu_task(void* p) {
+int32_t menu_task(void* p) {
     ValueMutex* menu_mutex = menu_init();
     ValueMutex* menu_mutex = menu_init();
 
 
     MenuEvent* menu_event = NULL;
     MenuEvent* menu_event = NULL;
@@ -267,4 +267,6 @@ void menu_task(void* p) {
 
 
         release_mutex(menu_mutex, menu);
         release_mutex(menu_mutex, menu);
     }
     }
+
+    return 0;
 }
 }

+ 5 - 3
applications/music-player/music-player.c

@@ -356,7 +356,7 @@ void music_player_thread(void* p) {
     }
     }
 }
 }
 
 
-void music_player(void* p) {
+int32_t music_player(void* p) {
     osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(MusicDemoEvent), NULL);
     osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(MusicDemoEvent), NULL);
 
 
     State _state;
     State _state;
@@ -370,7 +370,7 @@ void music_player(void* p) {
     ValueMutex state_mutex;
     ValueMutex state_mutex;
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
         printf("cannot create mutex\r\n");
         printf("cannot create mutex\r\n");
-        furiac_exit(NULL);
+        return 255;
     }
     }
 
 
     ViewPort* view_port = view_port_alloc();
     ViewPort* view_port = view_port_alloc();
@@ -394,7 +394,7 @@ void music_player(void* p) {
 
 
     if(player == NULL) {
     if(player == NULL) {
         printf("cannot create player thread\r\n");
         printf("cannot create player thread\r\n");
-        furiac_exit(NULL);
+        return 255;
     }
     }
 
 
     MusicDemoEvent event;
     MusicDemoEvent event;
@@ -449,4 +449,6 @@ void music_player(void* p) {
         view_port_update(view_port);
         view_port_update(view_port);
         release_mutex(&state_mutex, state);
         release_mutex(&state_mutex, state);
     }
     }
+
+    return 0;
 }
 }

+ 3 - 1
applications/nfc/nfc.c

@@ -100,7 +100,7 @@ void nfc_start(Nfc* nfc, NfcView view_id, NfcWorkerState worker_state) {
     }
     }
 }
 }
 
 
-void nfc_task(void* p) {
+int32_t nfc_task(void* p) {
     Nfc* nfc = nfc_alloc();
     Nfc* nfc = nfc_alloc();
 
 
     Gui* gui = furi_record_open("gui");
     Gui* gui = furi_record_open("gui");
@@ -142,4 +142,6 @@ void nfc_task(void* p) {
                 });
                 });
         }
         }
     }
     }
+
+    return 0;
 }
 }

+ 1 - 1
applications/nfc/nfc_i.h

@@ -41,4 +41,4 @@ void nfc_menu_field_callback(void* context);
 
 
 void nfc_start(Nfc* nfc, NfcView view_id, NfcWorkerState worker_state);
 void nfc_start(Nfc* nfc, NfcView view_id, NfcWorkerState worker_state);
 
 
-void nfc_task(void* p);
+int32_t nfc_task(void* p);

+ 2 - 2
applications/nfc/nfc_worker.c

@@ -55,7 +55,7 @@ void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) {
 void nfc_worker_task(void* context) {
 void nfc_worker_task(void* context) {
     NfcWorker* nfc_worker = context;
     NfcWorker* nfc_worker = context;
 
 
-    api_hal_timebase_insomnia_enter();
+    api_hal_power_insomnia_enter();
 
 
     rfalLowPowerModeStop();
     rfalLowPowerModeStop();
     if(nfc_worker->state == NfcWorkerStatePoll) {
     if(nfc_worker->state == NfcWorkerStatePoll) {
@@ -69,7 +69,7 @@ void nfc_worker_task(void* context) {
 
 
     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
     nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
 
 
-    api_hal_timebase_insomnia_exit();
+    api_hal_power_insomnia_exit();
     osThreadExit();
     osThreadExit();
 }
 }
 
 

+ 3 - 1
applications/power/power.c

@@ -177,7 +177,7 @@ void power_cli_otg_off(string_t args, void* context) {
     api_hal_power_disable_otg();
     api_hal_power_disable_otg();
 }
 }
 
 
-void power_task(void* p) {
+int32_t power_task(void* p) {
     (void)p;
     (void)p;
     Power* power = power_alloc();
     Power* power = power_alloc();
 
 
@@ -223,4 +223,6 @@ void power_task(void* p) {
         view_port_enabled_set(power->usb_view_port, api_hal_power_is_charging());
         view_port_enabled_set(power->usb_view_port, api_hal_power_is_charging());
         osDelay(1024);
         osDelay(1024);
     }
     }
+
+    return 0;
 }
 }

+ 2 - 1
applications/sd-card-test/sd-card-test.cpp

@@ -904,7 +904,8 @@ void SdTest::render(Canvas* canvas) {
 }
 }
 
 
 // app enter function
 // app enter function
-extern "C" void sd_card_test(void* p) {
+extern "C" int32_t sd_card_test(void* p) {
     SdTest* app = new SdTest();
     SdTest* app = new SdTest();
     app->run();
     app->run();
+    return 0;
 }
 }

+ 4 - 4
applications/sd-filesystem/sd-filesystem.c

@@ -101,9 +101,7 @@ SdApp* sd_app_alloc() {
     SdApp* sd_app = furi_alloc(sizeof(SdApp));
     SdApp* sd_app = furi_alloc(sizeof(SdApp));
 
 
     // init inner fs data
     // init inner fs data
-    if(!_fs_init(&sd_app->info)) {
-        furiac_exit(NULL);
-    }
+    furi_check(_fs_init(&sd_app->info));
 
 
     sd_app->event_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL);
     sd_app->event_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL);
 
 
@@ -476,7 +474,7 @@ static void cli_sd_info(string_t args, void* _ctx) {
     }
     }
 }
 }
 
 
-void sd_filesystem(void* p) {
+int32_t sd_filesystem(void* p) {
     SdApp* sd_app = sd_app_alloc();
     SdApp* sd_app = sd_app_alloc();
     FS_Api* fs_api = fs_api_alloc();
     FS_Api* fs_api = fs_api_alloc();
 
 
@@ -555,4 +553,6 @@ void sd_filesystem(void* p) {
 
 
         delay(1000);
         delay(1000);
     }
     }
+
+    return 0;
 }
 }

+ 2 - 1
applications/sd-nfc/sdnfc.cpp

@@ -161,7 +161,8 @@ void AppSdNFC::blink_green() {
 }
 }
 
 
 // app enter function
 // app enter function
-extern "C" void sdnfc(void* p) {
+extern "C" int32_t sdnfc(void* p) {
     AppSdNFC* app = new AppSdNFC();
     AppSdNFC* app = new AppSdNFC();
     app->run();
     app->run();
+    return 0;
 }
 }

+ 5 - 3
applications/template/template.c.example

@@ -37,7 +37,7 @@ static void input_callback(InputEvent* input_event, void* ctx) {
     osMessageQueuePut(event_queue, &event, 0, 0);
     osMessageQueuePut(event_queue, &event, 0, 0);
 }
 }
 
 
-void template_app(void* p) {
+int32_t template_app(void* p) {
     osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(Event), NULL);
     osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(Event), NULL);
 
 
     State _state;
     State _state;
@@ -45,7 +45,7 @@ void template_app(void* p) {
     ValueMutex state_mutex;
     ValueMutex state_mutex;
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
         printf("cannot create mutex\r\n");
         printf("cannot create mutex\r\n");
-        furiac_exit(NULL);
+        return 255;
     }
     }
 
 
     ViewPort* view_port = view_port_alloc();
     ViewPort* view_port = view_port_alloc();
@@ -57,7 +57,7 @@ void template_app(void* p) {
     Gui* gui = furi_record_open("gui");
     Gui* gui = furi_record_open("gui");
     if(gui == NULL) {
     if(gui == NULL) {
         printf("gui is not available\r\n");
         printf("gui is not available\r\n");
-        furiac_exit(NULL);
+        return 255;
     }
     }
     gui_add_view_port(gui, view_port, /* specify UI layer */);
     gui_add_view_port(gui, view_port, /* specify UI layer */);
 
 
@@ -99,4 +99,6 @@ void template_app(void* p) {
 
 
         release_mutex(&state_mutex, state);
         release_mutex(&state_mutex, state);
     }
     }
+
+    return 0;
 }
 }

+ 0 - 33
applications/tests/furi_event_test.c

@@ -1,33 +0,0 @@
-#include <furi.h>
-#include "minunit.h"
-
-static void furi_concurent_app(void* p) {
-    Event* event = p;
-
-    signal_event(event);
-
-    furiac_exit(NULL);
-}
-
-void test_furi_event() {
-    mu_assert(false, "please reimplement or delete test");
-
-    /*Event event;
-
-    mu_check(init_event(&event));
-
-    // The event should not be signalled right after creation
-    mu_check(!wait_event_with_timeout(&event, 100));
-
-    // Create second app
-    FuriApp* second_app __attribute__((unused)) =
-        furiac_start(furi_concurent_app, "furi concurent app", (void*)&event);
-
-    // The event should be signalled now
-    mu_check(wait_event_with_timeout(&event, 100));
-
-    // The event should not be signalled once it's processed
-    mu_check(!wait_event_with_timeout(&event, 100));
-
-    mu_check(delete_event(&event));*/
-}

+ 0 - 145
applications/tests/furi_value_expanders_test.c

@@ -1,145 +0,0 @@
-#include <furi.h>
-#include "minunit.h"
-#include <stdint.h>
-
-typedef struct {
-    uint8_t red;
-    uint8_t green;
-    uint8_t blue;
-} Rgb;
-
-static uint32_t rgb_final_state;
-
-static void rgb_clear(void* ctx, void* state) {
-    Rgb* rgb = state;
-    rgb->red = 0;
-    rgb->green = 0;
-    rgb->blue = 0;
-}
-
-static void rgb_commit(void* ctx, void* state) {
-    Rgb* rgb = state;
-    rgb_final_state = ((uint32_t)rgb->red) | (((uint32_t)rgb->green) << 8) |
-                      (((uint32_t)rgb->blue) << 16);
-}
-
-static void set_red_composer(void* ctx, void* state) {
-    Rgb* rgb = state;
-    uint8_t* red = ctx;
-
-    rgb->red = *red;
-}
-
-void test_furi_value_composer() {
-    Rgb rgb = {0, 0, 0};
-    ValueComposer composer;
-    Rgb layer1_rgb = {0, 0, 0};
-    ValueMutex layer1_mutex;
-    uint8_t layer2_red = 0;
-
-    rgb_final_state = 0xdeadbeef;
-
-    mu_check(init_composer(&composer, &rgb));
-
-    mu_check(init_mutex(&layer1_mutex, &layer1_rgb, sizeof(layer1_rgb)));
-
-    perform_compose(&composer, rgb_clear, rgb_commit, NULL);
-    mu_assert_int_eq(0xdeadbeef, rgb_final_state);
-
-    ValueComposerHandle* layer1_handle =
-        add_compose_layer(&composer, COPY_COMPOSE, &layer1_mutex, UiLayerNotify);
-    mu_assert_pointers_not_eq(layer1_handle, NULL);
-
-    // RGB state should be updated with the layer1 state
-    perform_compose(&composer, rgb_clear, rgb_commit, NULL);
-    mu_assert_int_eq(0x000000, rgb_final_state);
-
-    layer2_red = 0xcc;
-    ValueComposerHandle* layer2_handle =
-        add_compose_layer(&composer, set_red_composer, &layer2_red, UiLayerAboveNotify);
-    mu_assert_pointers_not_eq(layer2_handle, NULL);
-
-    // RGB state should be updated with the layer1 and layer2 state, in order
-    perform_compose(&composer, rgb_clear, rgb_commit, NULL);
-    mu_assert_int_eq(0x0000cc, rgb_final_state);
-
-    // Change layer1 state
-    Rgb* state = acquire_mutex(&layer1_mutex, 0);
-    mu_assert_pointers_not_eq(state, NULL);
-    state->red = 0x12;
-    state->green = 0x34;
-    state->blue = 0x56;
-    release_mutex(&layer1_mutex, state);
-
-    // Nothing should happen, we need to trigger composition request first
-    perform_compose(&composer, rgb_clear, rgb_commit, NULL);
-    mu_assert_int_eq(0x0000cc, rgb_final_state);
-
-    request_compose(layer1_handle);
-
-    perform_compose(&composer, rgb_clear, rgb_commit, NULL);
-    mu_assert_int_eq(0x5634cc, rgb_final_state);
-
-    // Change layer2 state
-    layer2_red = 0xff;
-
-    // Nothing should happen, we need to trigger composition request first
-    perform_compose(&composer, rgb_clear, rgb_commit, NULL);
-    mu_assert_int_eq(0x5634cc, rgb_final_state);
-
-    request_compose(layer2_handle);
-
-    perform_compose(&composer, rgb_clear, rgb_commit, NULL);
-    mu_assert_int_eq(0x5634ff, rgb_final_state);
-
-    // Remove layer1
-    mu_check(remove_compose_layer(layer1_handle));
-
-    perform_compose(&composer, rgb_clear, rgb_commit, NULL);
-    mu_assert_int_eq(0x0000ff, rgb_final_state);
-
-    // Remove layer2
-    mu_check(remove_compose_layer(layer2_handle));
-
-    perform_compose(&composer, rgb_clear, rgb_commit, NULL);
-    mu_assert_int_eq(0x000000, rgb_final_state);
-
-    mu_check(delete_composer(&composer));
-}
-
-static const uint32_t notify_value_0 = 0x12345678;
-static const uint32_t notify_value_1 = 0x11223344;
-
-static uint32_t pubsub_value = 0;
-
-void test_value_manager_handler(const void* arg, void* ctx) {
-    pubsub_value = *(uint32_t*)arg;
-}
-
-void test_furi_value_manager() {
-    uint32_t value = 0;
-    ValueManager managed;
-
-    mu_check(init_managed(&managed, &value, sizeof(value)));
-
-    pubsub_value = 0;
-
-    PubSubItem* test_pubsub_item;
-    test_pubsub_item = subscribe_pubsub(&managed.pubsub, test_value_manager_handler, 0);
-    mu_assert_pointers_not_eq(test_pubsub_item, NULL);
-
-    mu_check(write_managed(&managed, (void*)&notify_value_0, sizeof(notify_value_0), 100));
-
-    mu_assert_int_eq(pubsub_value, notify_value_0);
-
-    uint32_t* ptr = acquire_mutex(&managed.value, 100);
-    mu_assert_pointers_not_eq(ptr, NULL);
-
-    *ptr = notify_value_1;
-
-    mu_check(commit_managed(&managed, ptr));
-
-    mu_assert_int_eq(pubsub_value, notify_value_1);
-
-    mu_check(delete_managed(&managed));
-}

+ 3 - 3
applications/tests/furi_valuemutex_test.c

@@ -60,7 +60,7 @@ void furi_concurent_app(void* p) {
     ValueMutex* mutex = (ValueMutex*)p;
     ValueMutex* mutex = (ValueMutex*)p;
     if(mutex == NULL) {
     if(mutex == NULL) {
         printf("cannot open mutex\r\n");
         printf("cannot open mutex\r\n");
-        furiac_exit(NULL);
+        osThreadExit();
     }
     }
 
 
     for(size_t i = 0; i < 10; i++) {
     for(size_t i = 0; i < 10; i++) {
@@ -69,7 +69,7 @@ void furi_concurent_app(void* p) {
         if(value == NULL) {
         if(value == NULL) {
             printf("cannot take record\r\n");
             printf("cannot take record\r\n");
             release_mutex(mutex, value);
             release_mutex(mutex, value);
-            furiac_exit(NULL);
+            osThreadExit();
         }
         }
 
 
         // emulate read-modify-write broken by context switching
         // emulate read-modify-write broken by context switching
@@ -83,7 +83,7 @@ void furi_concurent_app(void* p) {
         release_mutex(mutex, value);
         release_mutex(mutex, value);
     }
     }
 
 
-    furiac_exit(NULL);
+    osThreadExit();
 }
 }
 
 
 void test_furi_concurrent_access() {
 void test_furi_concurrent_access() {

+ 0 - 134
applications/tests/furiac_test.c

@@ -1,134 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include <furi.h>
-
-/*
-Test: creating and killing task
-
-1. create task
-2. delay 10 ms
-3. kill task
-4. check that value changes
-5. delay 2 ms
-6. check that value stay unchanged
-*/
-
-void create_kill_app(void* p) {
-    // this app simply increase counter
-    uint8_t* counter = (uint8_t*)p;
-    while(1) {
-        *counter = *counter + 1;
-        delay(1);
-    }
-}
-
-bool test_furi_ac_create_kill() {
-    mu_assert(false, "please reimplement or delete test");
-    /*
-    uint8_t counter = 0;
-
-    uint8_t value_a = counter;
-
-    FuriApp* widget = furiac_start(create_kill_app, "create_kill_app", (void*)&counter);
-    if(widget == NULL) {
-        printf("create widget fail\r\n");
-        return false;
-    }
-
-    delay(10);
-
-    if(!furiac_kill(widget)) {
-        printf("kill widget fail\r\n");
-        return false;
-    }
-
-    if(value_a == counter) {
-        printf("counter unchanged\r\n");
-        return false;
-    }
-
-    value_a = counter;
-
-    delay(10);
-
-    if(value_a != counter) {
-        printf("counter changes after kill (counter = %d vs %d)\n", value_a, counter);
-        return false;
-    }
-
-    return true;
-    */
-}
-
-/*
-Test: switch between tasks
-1. init s
-2. create task A, add 'A" to sequence'
-3. switch to task B, add 'B' to sequence
-4. exit from task B -> switch to A and add 'A' to sequence
-5. cleanup: exit from task A
-6. check sequence
-*/
-
-#define TEST_SWITCH_CONTEXT_SEQ_SIZE 8
-
-typedef struct {
-    char sequence[TEST_SWITCH_CONTEXT_SEQ_SIZE];
-    size_t count;
-} TestSwitchSequence;
-
-void task_a(void*);
-void task_b(void*);
-
-void task_a(void* p) {
-    // simply starts, add 'A' letter to sequence and switch
-    // if sequence counter = 0, call task B, exit otherwise
-
-    TestSwitchSequence* seq = (TestSwitchSequence*)p;
-
-    seq->sequence[seq->count] = 'A';
-    seq->count++;
-
-    if(seq->count == 1) {
-        furiac_switch(task_b, "task B", p);
-
-        // if switch unsuccessfull, this code will executed
-        seq->sequence[seq->count] = 'x';
-        seq->count++;
-    } else {
-        // add '/' symbol on exit
-        seq->sequence[seq->count] = '/';
-        seq->count++;
-        furiac_exit(NULL);
-    }
-}
-
-// application simply add 'B' end exit
-void task_b(void* p) {
-    TestSwitchSequence* seq = (TestSwitchSequence*)p;
-
-    seq->sequence[seq->count] = 'B';
-    seq->count++;
-
-    furiac_exit(p);
-}
-
-bool test_furi_ac_switch_exit() {
-    // init sequence
-    TestSwitchSequence seq;
-    seq.count = 0;
-
-    furiac_start(task_a, "task A", (void*)&seq);
-    // TODO how to check that all child task ends?
-
-    delay(10); // wait while task do its work
-
-    seq.sequence[seq.count] = '\0';
-
-    if(strcmp(seq.sequence, "ABA/") != 0) {
-        printf("wrong sequence: %s\n", seq.sequence);
-        return false;
-    }
-
-    return true;
-}

+ 0 - 28
applications/tests/minunit_test.c

@@ -3,17 +3,11 @@
 #include "minunit_vars.h"
 #include "minunit_vars.h"
 #include "minunit.h"
 #include "minunit.h"
 
 
-bool test_furi_ac_create_kill();
-bool test_furi_ac_switch_exit();
-
 // v2 tests
 // v2 tests
 void test_furi_create_open();
 void test_furi_create_open();
 void test_furi_valuemutex();
 void test_furi_valuemutex();
 void test_furi_concurrent_access();
 void test_furi_concurrent_access();
 void test_furi_pubsub();
 void test_furi_pubsub();
-void test_furi_value_composer();
-void test_furi_value_manager();
-void test_furi_event();
 
 
 void test_furi_memmgr();
 void test_furi_memmgr();
 
 
@@ -31,14 +25,6 @@ MU_TEST(test_check) {
     mu_check(foo != 6);
     mu_check(foo != 6);
 }
 }
 
 
-MU_TEST(mu_test_furi_ac_create_kill) {
-    mu_assert_int_eq(test_furi_ac_create_kill(), true);
-}
-
-MU_TEST(mu_test_furi_ac_switch_exit) {
-    mu_assert_int_eq(test_furi_ac_switch_exit(), true);
-}
-
 // v2 tests
 // v2 tests
 MU_TEST(mu_test_furi_create_open) {
 MU_TEST(mu_test_furi_create_open) {
     test_furi_create_open();
     test_furi_create_open();
@@ -62,30 +48,16 @@ MU_TEST(mu_test_furi_memmgr) {
     test_furi_memmgr();
     test_furi_memmgr();
 }
 }
 
 
-MU_TEST(mu_test_furi_value_expanders) {
-    test_furi_value_composer();
-    test_furi_value_manager();
-}
-
-MU_TEST(mu_test_furi_event) {
-    test_furi_event();
-}
-
 MU_TEST_SUITE(test_suite) {
 MU_TEST_SUITE(test_suite) {
     MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
     MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
 
 
     MU_RUN_TEST(test_check);
     MU_RUN_TEST(test_check);
-    MU_RUN_TEST(mu_test_furi_ac_create_kill);
-    MU_RUN_TEST(mu_test_furi_ac_switch_exit);
 
 
     // v2 tests
     // v2 tests
     MU_RUN_TEST(mu_test_furi_create_open);
     MU_RUN_TEST(mu_test_furi_create_open);
     MU_RUN_TEST(mu_test_furi_valuemutex);
     MU_RUN_TEST(mu_test_furi_valuemutex);
     MU_RUN_TEST(mu_test_furi_concurrent_access);
     MU_RUN_TEST(mu_test_furi_concurrent_access);
     MU_RUN_TEST(mu_test_furi_pubsub);
     MU_RUN_TEST(mu_test_furi_pubsub);
-    MU_RUN_TEST(mu_test_furi_value_expanders);
-    MU_RUN_TEST(mu_test_furi_event);
-
     MU_RUN_TEST(mu_test_furi_memmgr);
     MU_RUN_TEST(mu_test_furi_memmgr);
 }
 }
 
 

+ 2 - 4
applications/tests/test_index.c

@@ -5,7 +5,7 @@
 
 
 int run_minunit();
 int run_minunit();
 
 
-void flipper_test_app(void* p) {
+int32_t flipper_test_app(void* p) {
     // create pins
     // create pins
     GpioPin red = {.pin = LED_RED_Pin, .port = LED_RED_GPIO_Port};
     GpioPin red = {.pin = LED_RED_Pin, .port = LED_RED_GPIO_Port};
     GpioPin green = {.pin = LED_GREEN_Pin, .port = LED_GREEN_GPIO_Port};
     GpioPin green = {.pin = LED_GREEN_Pin, .port = LED_GREEN_GPIO_Port};
@@ -38,7 +38,5 @@ void flipper_test_app(void* p) {
         gpio_write(blue_record, true);
         gpio_write(blue_record, true);
     }
     }
 
 
-    set_exitcode(exitcode);
-
-    furiac_exit(NULL);
+    return 0;
 }
 }

+ 26 - 0
core/flipper.c

@@ -0,0 +1,26 @@
+#include "flipper.h"
+#include <applications.h>
+#include <furi.h>
+
+void flipper_init() {
+    printf("[flipper] Build date:" BUILD_DATE ". "
+           "Git Commit:" GIT_COMMIT ". "
+           "Git Branch:" GIT_BRANCH ". "
+           "Commit Number:" GIT_BRANCH_NUM "\r\n");
+
+    printf("[flipper] starting services\r\n");
+
+    for(size_t i = 0; i < FLIPPER_SERVICES_COUNT; i++) {
+        printf("[flipper] starting service %s\r\n", FLIPPER_SERVICES[i].name);
+
+        FuriThread* thread = furi_thread_alloc();
+
+        furi_thread_set_name(thread, FLIPPER_SERVICES[i].name);
+        furi_thread_set_stack_size(thread, FLIPPER_SERVICES[i].stack_size);
+        furi_thread_set_callback(thread, FLIPPER_SERVICES[i].app);
+
+        furi_thread_start(thread);
+    }
+
+    printf("[flipper] services startup complete\r\n");
+}

+ 3 - 0
core/flipper.h

@@ -0,0 +1,3 @@
+#pragma once
+
+void flipper_init();

+ 0 - 30
core/furi.c

@@ -1,12 +1,4 @@
 #include "furi.h"
 #include "furi.h"
-#include <applications.h>
-
-// for testing purpose
-uint32_t exitcode = 0;
-
-void set_exitcode(uint32_t _exitcode) {
-    exitcode = _exitcode;
-}
 
 
 void furi_init() {
 void furi_init() {
     gpio_api_init();
     gpio_api_init();
@@ -14,25 +6,3 @@ void furi_init() {
     furi_record_init();
     furi_record_init();
     furi_stdglue_init();
     furi_stdglue_init();
 }
 }
-
-int systemd() {
-    furi_init();
-    printf("[systemd] furi initialized\r\n");
-    // FURI startup
-    for(size_t i = 0; i < FLIPPER_SERVICES_size(); i++) {
-        printf("[systemd] starting service %s\r\n", FLIPPER_SERVICES[i].name);
-        osThreadAttr_t* attr = furi_alloc(sizeof(osThreadAttr_t));
-        attr->name = FLIPPER_SERVICES[i].name;
-        attr->stack_size = FLIPPER_SERVICES[i].stack_size;
-        osThreadNew(FLIPPER_SERVICES[i].app, NULL, attr);
-    }
-    printf("[systemd] all services started\r\n");
-
-    while(1) {
-        osThreadSuspend(osThreadGetId());
-    }
-
-    printf("[systemd] === Bye from Flipper Zero! ===\r\n");
-
-    return (int)exitcode;
-}

+ 2 - 3
core/furi.h

@@ -3,12 +3,11 @@
 #include <cmsis_os2.h>
 #include <cmsis_os2.h>
 
 
 #include <furi/check.h>
 #include <furi/check.h>
-#include <furi/event.h>
 #include <furi/memmgr.h>
 #include <furi/memmgr.h>
 #include <furi/pubsub.h>
 #include <furi/pubsub.h>
 #include <furi/record.h>
 #include <furi/record.h>
 #include <furi/stdglue.h>
 #include <furi/stdglue.h>
-#include <furi/value-expanders.h>
+#include <furi/thread.h>
 #include <furi/valuemutex.h>
 #include <furi/valuemutex.h>
 
 
 #include <api-hal/api-gpio.h>
 #include <api-hal/api-gpio.h>
@@ -21,7 +20,7 @@
 extern "C" {
 extern "C" {
 #endif
 #endif
 
 
-#define furiac_exit(ptr) osThreadExit()
+void furi_init();
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }

+ 0 - 24
core/furi/event.c

@@ -1,24 +0,0 @@
-#include "event.h"
-#include <string.h>
-
-bool init_event(Event* event) {
-    event->semaphore_id = osSemaphoreNew(1, 0, NULL);
-    return event->semaphore_id != NULL;
-}
-
-bool delete_event(Event* event) {
-    return osSemaphoreDelete(event->semaphore_id) == osOK;
-}
-
-void signal_event(Event* event) {
-    // Ignore the result, as we do not care about repeated event signalling.
-    osSemaphoreRelease(event->semaphore_id);
-}
-
-void wait_event(Event* event) {
-    wait_event_with_timeout(event, osWaitForever);
-}
-
-bool wait_event_with_timeout(Event* event, uint32_t timeout_ms) {
-    return osSemaphoreAcquire(event->semaphore_id, timeout_ms) == osOK;
-}

+ 0 - 44
core/furi/event.h

@@ -1,44 +0,0 @@
-#pragma once
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <cmsis_os2.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct {
-    osSemaphoreId_t semaphore_id;
-} Event;
-
-/*
-Creates Event.
-*/
-bool init_event(Event* event);
-
-/*
-Free resources allocated by `init_event`.
-This function doesn't free the memory occupied by `Event` itself.
-*/
-bool delete_event(Event* event);
-
-/*
-Signals the event.
-If the event is already in "signalled" state, nothing happens.
-*/
-void signal_event(Event* event);
-
-/*
-Waits until the event is signalled.
-*/
-void wait_event(Event* event);
-
-/*
-Waits with a timeout until the event is signalled.
-*/
-bool wait_event_with_timeout(Event* event, uint32_t timeout_ms);
-
-#ifdef __cplusplus
-}
-#endif

+ 122 - 0
core/furi/thread.c

@@ -0,0 +1,122 @@
+#include "thread.h"
+#include "memmgr.h"
+#include "check.h"
+
+#include <m-string.h>
+
+struct FuriThread {
+    FuriThreadState state;
+    int32_t ret;
+
+    FuriThreadCallback callback;
+    void* context;
+
+    FuriThreadStateCallback state_callback;
+    void* state_context;
+
+    osThreadAttr_t attr;
+    osThreadId_t id;
+};
+
+void furi_thread_set_state(FuriThread* thread, FuriThreadState state) {
+    furi_assert(thread);
+    thread->state = state;
+    if(thread->state_callback) {
+        thread->state_callback(state, thread->state_context);
+    }
+}
+
+void furi_thread_body(void* context) {
+    furi_assert(context);
+    FuriThread* thread = context;
+
+    furi_assert(thread->state == FuriThreadStateStarting);
+    furi_thread_set_state(thread, FuriThreadStateRunning);
+
+    thread->ret = thread->callback(thread->context);
+
+    furi_assert(thread->state == FuriThreadStateRunning);
+    furi_thread_set_state(thread, FuriThreadStateStopped);
+
+    osThreadExit();
+}
+
+FuriThread* furi_thread_alloc() {
+    FuriThread* thread = furi_alloc(sizeof(FuriThread));
+
+    return thread;
+}
+
+void furi_thread_free(FuriThread* thread) {
+    furi_assert(thread);
+    furi_assert(thread->state == FuriThreadStateStopped);
+
+    if(thread->attr.name) free((void*)thread->attr.name);
+    free(thread);
+}
+
+void furi_thread_set_name(FuriThread* thread, const char* name) {
+    furi_assert(thread);
+    furi_assert(thread->state == FuriThreadStateStopped);
+    if(thread->attr.name) free((void*)thread->attr.name);
+    thread->attr.name = strdup(name);
+}
+
+void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size) {
+    furi_assert(thread);
+    furi_assert(thread->state == FuriThreadStateStopped);
+    thread->attr.stack_size = stack_size;
+}
+
+void furi_thread_set_callback(FuriThread* thread, FuriThreadCallback callback) {
+    furi_assert(thread);
+    furi_assert(thread->state == FuriThreadStateStopped);
+    thread->callback = callback;
+}
+
+void furi_thread_set_context(FuriThread* thread, void* context) {
+    furi_assert(thread);
+    furi_assert(thread->state == FuriThreadStateStopped);
+    thread->context = context;
+}
+
+void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback) {
+    furi_assert(thread);
+    furi_assert(thread->state == FuriThreadStateStopped);
+    thread->state_callback = callback;
+}
+
+void furi_thread_set_state_context(FuriThread* thread, void* context) {
+    furi_assert(thread);
+    furi_assert(thread->state == FuriThreadStateStopped);
+    thread->state_context = context;
+}
+
+bool furi_thread_start(FuriThread* thread) {
+    furi_assert(thread);
+    furi_assert(thread->callback);
+    furi_assert(thread->state == FuriThreadStateStopped);
+    furi_thread_set_state(thread, FuriThreadStateStarting);
+    thread->id = osThreadNew(furi_thread_body, thread, &thread->attr);
+    if(thread->id) {
+        return true;
+    } else {
+        furi_assert(thread->state == FuriThreadStateStarting);
+        furi_thread_set_state(thread, FuriThreadStateStopped);
+        return false;
+    }
+}
+
+osStatus_t furi_thread_terminate(FuriThread* thread) {
+    furi_assert(thread);
+    osStatus_t ret = osThreadTerminate(thread->id);
+    if(ret == osOK) {
+        furi_thread_set_state(thread, FuriThreadStateStopped);
+    }
+    return ret;
+}
+
+osStatus_t furi_thread_join(FuriThread* thread) {
+    furi_assert(thread);
+    return osThreadJoin(thread->id);
+}

+ 102 - 0
core/furi/thread.h

@@ -0,0 +1,102 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <cmsis_os2.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* FuriThreadState */
+typedef enum {
+    FuriThreadStateStopped,
+    FuriThreadStateStarting,
+    FuriThreadStateRunning,
+} FuriThreadState;
+
+/* FuriThread anonymous structure */
+typedef struct FuriThread FuriThread;
+
+/* FuriThreadCallback
+ * Your callback to run in new thread
+ * @warning don't use osThreadExit
+ */
+typedef int32_t (*FuriThreadCallback)(void* context);
+
+/* FuriThread state change calback
+ * called upon thread state change
+ * @param state - new thread state
+ * @param context - callback context
+ */
+typedef void (*FuriThreadStateCallback)(FuriThreadState state, void* context);
+
+/* Allocate FuriThread
+ * @return FuriThread instance
+ */
+FuriThread* furi_thread_alloc();
+
+/* Release FuriThread
+ * @param thread - FuriThread instance
+ */
+void furi_thread_free(FuriThread* thread);
+
+/* Set FuriThread name
+ * @param thread - FuriThread instance
+ * @param name - string
+ */
+void furi_thread_set_name(FuriThread* thread, const char* name);
+
+/* Set FuriThread stack size
+ * @param thread - FuriThread instance
+ * @param stack_size - stack size in bytes
+ */
+void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size);
+
+/* Set FuriThread callback
+ * @param thread - FuriThread instance
+ * @param callback - FuriThreadCallback, called upon thread run
+ */
+void furi_thread_set_callback(FuriThread* thread, FuriThreadCallback callback);
+
+/* Set FuriThread context
+ * @param thread - FuriThread instance
+ * @param context - pointer to context for thread callback
+ */
+void furi_thread_set_context(FuriThread* thread, void* context);
+
+/* Set FuriThread state change callback
+ * @param thread - FuriThread instance
+ * @param callack - state change callback
+ */
+void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback);
+
+/* Set FuriThread state change context
+ * @param thread - FuriThread instance
+ * @param context - pointer to context
+ */
+void furi_thread_set_state_context(FuriThread* thread, void* context);
+
+/* Start FuriThread
+ * @param thread - FuriThread instance
+ * @return true on success
+ */
+bool furi_thread_start(FuriThread* thread);
+
+/* Treminate FuriThread
+ * @param thread - FuriThread instance
+ * @return osStatus_t
+ * @warning terminating statefull thread is dangerous
+ * use only if you know what you doing
+ */
+osStatus_t furi_thread_terminate(FuriThread* thread);
+
+/* Join FuriThread
+ * @param thread - FuriThread instance
+ * @return osStatus_t
+ */
+osStatus_t furi_thread_join(FuriThread* thread);
+
+#ifdef __cplusplus
+}
+#endif

+ 0 - 174
core/furi/value-expanders.c

@@ -1,174 +0,0 @@
-#include "value-expanders.h"
-
-bool init_composer(ValueComposer* composer, void* value) {
-    if(!init_mutex(&composer->value, value, 0)) return false;
-
-    for(size_t i = 0; i < sizeof(composer->layers) / sizeof(composer->layers[0]); i++) {
-        list_composer_cb_init(composer->layers[i]);
-    }
-
-    // mutex without name,
-    // no attributes (unfortunatly robust mutex is not supported by FreeRTOS),
-    // with dynamic memory allocation
-    composer->mutex = osMutexNew(NULL);
-    if(composer->mutex == NULL) return false;
-
-    if(!init_event(&composer->request)) return false;
-
-    return true;
-}
-
-bool delete_composer(ValueComposer* composer) {
-    if(osMutexAcquire(composer->mutex, osWaitForever) == osOK) {
-        bool result = true;
-        result &= delete_mutex(&composer->value);
-
-        for(size_t i = 0; i < sizeof(composer->layers) / sizeof(composer->layers[0]); i++) {
-            list_composer_cb_clear(composer->layers[i]);
-        }
-
-        result &= osMutexDelete(composer->mutex) == osOK;
-
-        return result;
-    } else {
-        return false;
-    }
-}
-
-ValueComposerHandle*
-add_compose_layer(ValueComposer* composer, ValueComposerCallback cb, void* ctx, UiLayer layer) {
-    if(osMutexAcquire(composer->mutex, osWaitForever) == osOK) {
-        // put uninitialized item to the list
-        ValueComposerHandle* handle = list_composer_cb_push_raw(composer->layers[layer]);
-
-        handle->cb = cb;
-        handle->ctx = ctx;
-        handle->layer = layer;
-        handle->composer = composer;
-
-        // TODO unregister handle on app exit
-        //flapp_on_exit(remove_compose_layer, handle);
-
-        osMutexRelease(composer->mutex);
-
-        // Layers changed, request composition
-        signal_event(&composer->request);
-
-        return handle;
-    } else {
-        return NULL;
-    }
-}
-
-bool remove_compose_layer(ValueComposerHandle* handle) {
-    ValueComposer* composer = handle->composer;
-
-    if(osMutexAcquire(composer->mutex, osWaitForever) == osOK) {
-        bool result = false;
-
-        // iterate over items
-        list_composer_cb_it_t it;
-        for(list_composer_cb_it(it, composer->layers[handle->layer]); !list_composer_cb_end_p(it);
-            list_composer_cb_next(it)) {
-            const ValueComposerHandle* item = list_composer_cb_cref(it);
-
-            // if the iterator is equal to our element
-            if(item == handle) {
-                list_composer_cb_remove(composer->layers[handle->layer], it);
-                result = true;
-                break;
-            }
-        }
-
-        osMutexRelease(composer->mutex);
-
-        // Layers changed, request composition
-        signal_event(&composer->request);
-
-        return result;
-    } else {
-        return false;
-    }
-}
-
-void request_compose(ValueComposerHandle* handle) {
-    ValueComposer* composer = handle->composer;
-    signal_event(&composer->request);
-}
-
-void perform_compose(
-    ValueComposer* composer,
-    ValueComposerCallback start_cb,
-    ValueComposerCallback end_cb,
-    void* ctx) {
-    if(!wait_event_with_timeout(&composer->request, 0)) return;
-
-    void* state = acquire_mutex(&composer->value, 0);
-    if(state == NULL) return;
-
-    if(start_cb != NULL) start_cb(ctx, state);
-    perform_compose_internal(composer, state);
-    if(end_cb != NULL) end_cb(ctx, state);
-
-    release_mutex(&composer->value, state);
-}
-
-void perform_compose_internal(ValueComposer* composer, void* state) {
-    if(osMutexAcquire(composer->mutex, osWaitForever) == osOK) {
-        // Compose all levels for now
-        for(size_t i = 0; i < sizeof(composer->layers) / sizeof(composer->layers[0]); i++) {
-            // iterate over items
-            list_composer_cb_it_t it;
-            for(list_composer_cb_it(it, composer->layers[i]); !list_composer_cb_end_p(it);
-                list_composer_cb_next(it)) {
-                const ValueComposerHandle* h = list_composer_cb_cref(it);
-                h->cb(h->ctx, state);
-            }
-        }
-
-        osMutexRelease(composer->mutex);
-    }
-}
-
-void COPY_COMPOSE(void* ctx, void* state) {
-    read_mutex((ValueMutex*)ctx, state, 0, osWaitForever);
-}
-
-bool init_managed(ValueManager* managed, void* value, size_t size) {
-    if(!init_pubsub(&managed->pubsub)) return false;
-    if(!init_mutex(&managed->value, value, size)) {
-        delete_pubsub(&managed->pubsub);
-        return false;
-    }
-    return true;
-}
-
-bool delete_managed(ValueManager* managed) {
-    bool result = true;
-    result &= delete_mutex(&managed->value);
-    result &= delete_pubsub(&managed->pubsub);
-    return result;
-}
-
-bool write_managed(ValueManager* managed, void* data, size_t len, uint32_t timeout) {
-    void* value = acquire_mutex(&managed->value, timeout);
-    if(value == NULL) return false;
-
-    memcpy(value, data, len);
-
-    notify_pubsub(&managed->pubsub, value);
-
-    if(!release_mutex(&managed->value, value)) return false;
-
-    return true;
-}
-
-bool commit_managed(ValueManager* managed, void* value) {
-    if(value != managed->value.value) return false;
-
-    notify_pubsub(&managed->pubsub, value);
-
-    if(!release_mutex(&managed->value, value)) return false;
-
-    return true;
-}

+ 0 - 115
core/furi/value-expanders.h

@@ -1,115 +0,0 @@
-#pragma once
-
-#include "valuemutex.h"
-#include "pubsub.h"
-#include "event.h"
-#include <m-list.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
-== Value composer ==
-*/
-
-typedef struct ValueComposer ValueComposer;
-
-typedef void (*ValueComposerCallback)(void* ctx, void* state);
-
-typedef enum { UiLayerBelowNotify, UiLayerNotify, UiLayerAboveNotify } UiLayer;
-
-typedef struct {
-    ValueComposerCallback cb;
-    void* ctx;
-    UiLayer layer;
-    ValueComposer* composer;
-} ValueComposerHandle;
-
-LIST_DEF(list_composer_cb, ValueComposerHandle, M_POD_OPLIST);
-
-struct ValueComposer {
-    ValueMutex value;
-    list_composer_cb_t layers[3];
-    osMutexId_t mutex;
-    Event request;
-};
-
-void COPY_COMPOSE(void* ctx, void* state);
-
-bool init_composer(ValueComposer* composer, void* value);
-
-/*
-Free resources allocated by `init_composer`.
-This function doesn't free the memory occupied by `ValueComposer` itself.
-*/
-bool delete_composer(ValueComposer* composer);
-
-ValueComposerHandle*
-add_compose_layer(ValueComposer* composer, ValueComposerCallback cb, void* ctx, UiLayer layer);
-
-bool remove_compose_layer(ValueComposerHandle* handle);
-
-void request_compose(ValueComposerHandle* handle);
-
-/*
-Perform composition if requested.
-
-`start_cb` and `end_cb` will be called before and after all layer callbacks, respectively.
-Both `start_cb` and `end_cb` can be NULL. They can be used to set initial state (e.g. clear screen)
-and commit the final state.
-*/
-void perform_compose(
-    ValueComposer* composer,
-    ValueComposerCallback start_cb,
-    ValueComposerCallback end_cb,
-    void* ctx);
-
-/*
-Perform composition.
-
-This function should be called with value mutex acquired.
-This function is here for convenience, so that developers can write their own compose loops.
-See `perform_compose` function body for an example.
-*/
-void perform_compose_internal(ValueComposer* composer, void* state);
-
-// See [LED](LED-API) or [Display](Display-API) API for examples.
-
-/*
-== ValueManager ==
-
-More complicated concept is ValueManager.
-It is like ValueMutex, but user can subscribe to value updates.
-
-First of all you can use value and pubsub part as showing above:
-aquire/release mutex, read value, subscribe/unsubscribe pubsub.
-There are two specific methods for ValueManager: write_managed, commit_managed
-*/
-
-typedef struct {
-    ValueMutex value;
-    PubSub pubsub;
-} ValueManager;
-
-bool init_managed(ValueManager* managed, void* value, size_t size);
-
-/*
-Free resources allocated by `init_managed`.
-This function doesn't free the memory occupied by `ValueManager` itself.
-*/
-bool delete_managed(ValueManager* managed);
-
-/*
-acquire value, changes it and send notify with current value.
-*/
-bool write_managed(ValueManager* managed, void* data, size_t len, uint32_t timeout);
-
-/*
-commit_managed works as `release_mutex` but send notify with current value.
-*/
-bool commit_managed(ValueManager* managed, void* value);
-
-#ifdef __cplusplus
-}
-#endif

+ 19 - 0
firmware/targets/api-hal-include/api-hal-power.h

@@ -16,6 +16,25 @@ typedef enum {
 /* Initialize drivers */
 /* Initialize drivers */
 void api_hal_power_init();
 void api_hal_power_init();
 
 
+/* Get current insomnia level
+ * @return insomnia level: 0 - no insomnia, >0 - insomnia, bearer count.
+ */
+uint16_t api_hal_power_insomnia_level();
+
+/* Enter insomnia mode
+ * Prevents device from going to sleep
+ * @warning Internally increases insomnia level
+ * Must be paired with api_hal_power_insomnia_exit
+ */
+void api_hal_power_insomnia_enter();
+
+/* Exit insomnia mode
+ * Allow device to go to sleep
+ * @warning Internally decreases insomnia level.
+ * Must be paired with api_hal_power_insomnia_enter
+ */
+void api_hal_power_insomnia_exit();
+
 /* Check if deep sleep availble */
 /* Check if deep sleep availble */
 bool api_hal_power_deep_available();
 bool api_hal_power_deep_available();
 
 

+ 2 - 2
firmware/targets/api-hal-include/api-hal.h

@@ -4,9 +4,9 @@
 template <unsigned int N> struct STOP_EXTERNING_ME {};
 template <unsigned int N> struct STOP_EXTERNING_ME {};
 #endif
 #endif
 
 
-#include "api-hal-resources.h"
-#include "api-hal-timebase.h"
 #include "api-hal-boot.h"
 #include "api-hal-boot.h"
+#include "api-hal-os.h"
+#include "api-hal-resources.h"
 #include "api-hal-gpio.h"
 #include "api-hal-gpio.h"
 #include "api-hal-delay.h"
 #include "api-hal-delay.h"
 #include "api-hal-pwm.h"
 #include "api-hal-pwm.h"

+ 0 - 25
firmware/targets/f4/Src/app_freertos.c

@@ -1,25 +0,0 @@
-#include <cmsis_os2.h>
-#include <FreeRTOS.h>
-#include <task.h>
-#include <main.h>
-
-void systemd(void *argument);
-
-osThreadId_t systemdHandle;
-const osThreadAttr_t systemd_attributes = {
-    .name = "systemd",
-    .priority = (osPriority_t) osPriorityNormal,
-    .stack_size = 1024
-};
-
-void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName) {
-    /* Run time stack overflow checking is performed if
-    configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook function is
-    called if a stack overflow is detected. */
-    asm("bkpt 1");
-    while(1);
-}
-
-void MX_FREERTOS_Init(void) {
-    systemdHandle = osThreadNew(systemd, NULL, &systemd_attributes);
-}

+ 120 - 232
firmware/targets/f4/Src/main.c

@@ -1,25 +1,6 @@
-/* USER CODE BEGIN Header */
-/**
-  ******************************************************************************
-  * @file           : main.c
-  * @brief          : Main program body
-  ******************************************************************************
-  * @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 ------------------------------------------------------------------*/
 #include "main.h"
 #include "main.h"
-#include "cmsis_os.h"
+
+#include "cmsis_os2.h"
 #include "adc.h"
 #include "adc.h"
 #include "aes.h"
 #include "aes.h"
 #include "comp.h"
 #include "comp.h"
@@ -34,234 +15,141 @@
 #include "usart.h"
 #include "usart.h"
 #include "usb_device.h"
 #include "usb_device.h"
 #include "gpio.h"
 #include "gpio.h"
-
-/* Private includes ----------------------------------------------------------*/
-/* USER CODE BEGIN Includes */
 #include "fatfs/fatfs.h"
 #include "fatfs/fatfs.h"
-#include "api-hal.h"
-/* USER CODE END Includes */
-
-/* Private typedef -----------------------------------------------------------*/
-/* USER CODE BEGIN PTD */
-
-/* USER CODE END PTD */
-
-/* Private define ------------------------------------------------------------*/
-/* USER CODE BEGIN PD */
-/* USER CODE END PD */
-
-/* Private macro -------------------------------------------------------------*/
-/* USER CODE BEGIN PM */
-
-/* USER CODE END PM */
-
-/* Private variables ---------------------------------------------------------*/
 
 
-/* USER CODE BEGIN PV */
+#include <furi.h>
+#include <api-hal.h>
+#include <flipper.h>
 
 
-/* USER CODE END PV */
-
-/* Private function prototypes -----------------------------------------------*/
 void SystemClock_Config(void);
 void SystemClock_Config(void);
 void MX_FREERTOS_Init(void);
 void MX_FREERTOS_Init(void);
-/* USER CODE BEGIN PFP */
-
-/* USER CODE END PFP */
-
-/* Private user code ---------------------------------------------------------*/
-/* USER CODE BEGIN 0 */
-
-/* USER CODE END 0 */
 
 
-/**
-  * @brief  The application entry point.
-  * @retval int
-  */
 int main(void)
 int main(void)
 {
 {
-  /* USER CODE BEGIN 1 */
-
-  /* USER CODE END 1 */
-
-  /* MCU Configuration--------------------------------------------------------*/
-
-  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
-  HAL_Init();
-
-  /* USER CODE BEGIN Init */
-  /* USER CODE END Init */
-
-  /* Configure the system clock */
-  SystemClock_Config();
-
-  /* USER CODE BEGIN SysInit */
-
-  /* USER CODE END SysInit */
-
-  /* Initialize all configured peripherals */
-  MX_GPIO_Init();
-  MX_ADC1_Init();
-  MX_I2C1_Init();
-  MX_RTC_Init();
-  MX_SPI1_Init();
-  MX_SPI2_Init();
-  MX_USART1_UART_Init();
-  MX_USB_Device_Init();
-  MX_TIM1_Init();
-  MX_TIM2_Init();
-  MX_TIM16_Init();
-  MX_COMP1_Init();
-  MX_RF_Init();
-  MX_PKA_Init();
-  MX_RNG_Init();
-  MX_AES1_Init();
-  MX_AES2_Init();
-  MX_CRC_Init();
-  /* USER CODE BEGIN 2 */
-  api_hal_init();
-  MX_FATFS_Init();
-  delay_us_init_DWT();
-  // Errata 2.2.9, Flash OPTVERR flag is always set after system reset
-  __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS);
-  /* USER CODE END 2 */
-
-  /* Init scheduler */
-  osKernelInitialize();  /* Call init function for freertos objects (in freertos.c) */
-  MX_FREERTOS_Init();
-  /* Start scheduler */
-  osKernelStart();
-
-  /* We should never get here as control is now taken by the scheduler */
-  /* Infinite loop */
-  /* USER CODE BEGIN WHILE */
-  while (1)
-  {
-    /* USER CODE END WHILE */
-
-    /* USER CODE BEGIN 3 */
-  }
-  /* USER CODE END 3 */
+    HAL_Init();
+    SystemClock_Config();
+
+    MX_GPIO_Init();
+    MX_ADC1_Init();
+    MX_I2C1_Init();
+    MX_RTC_Init();
+    MX_SPI1_Init();
+    MX_SPI2_Init();
+    MX_USART1_UART_Init();
+    MX_USB_Device_Init();
+    MX_TIM1_Init();
+    MX_TIM2_Init();
+    MX_TIM16_Init();
+    MX_COMP1_Init();
+    MX_RF_Init();
+    MX_PKA_Init();
+    MX_RNG_Init();
+    MX_AES1_Init();
+    MX_AES2_Init();
+    MX_CRC_Init();
+
+    api_hal_init();
+    MX_FATFS_Init();
+    delay_us_init_DWT();
+
+    furi_init();
+    // CMSIS initialization
+    osKernelInitialize();
+    // Init flipper
+    flipper_init();
+    // Start kernel
+    osKernelStart();
+
+    while (1) {}
 }
 }
 
 
-/**
-  * @brief System Clock Configuration
-  * @retval None
-  */
 void SystemClock_Config(void)
 void SystemClock_Config(void)
 {
 {
-  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
-  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
-  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
-
-  /** Configure LSE Drive Capability
-  */
-  HAL_PWR_EnableBkUpAccess();
-  __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_MEDIUMLOW);
-  /** Configure the main internal regulator output voltage
-  */
-  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
-  /** Initializes the RCC Oscillators according to the specified parameters
-  * in the RCC_OscInitTypeDef structure.
-  */
-  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSE
-                              |RCC_OSCILLATORTYPE_LSE;
-  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
-  RCC_OscInitStruct.LSEState = RCC_LSE_ON;
-  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
-  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
-  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
-  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
-  RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV2;
-  RCC_OscInitStruct.PLL.PLLN = 8;
-  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
-  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
-  RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
-  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
-  {
-    Error_Handler();
-  }
-  /** Configure the SYSCLKSource, HCLK, PCLK1 and PCLK2 clocks dividers
-  */
-  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK4|RCC_CLOCKTYPE_HCLK2
-                              |RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
-                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
-  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
-  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
-  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
-  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
-  RCC_ClkInitStruct.AHBCLK2Divider = RCC_SYSCLK_DIV2;
-  RCC_ClkInitStruct.AHBCLK4Divider = RCC_SYSCLK_DIV1;
-
-  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
-  {
-    Error_Handler();
-  }
-  /** Initializes the peripherals clocks
-  */
-  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SMPS|RCC_PERIPHCLK_RFWAKEUP
-                              |RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USART1
-                              |RCC_PERIPHCLK_I2C1|RCC_PERIPHCLK_CLK48SEL
-                              |RCC_PERIPHCLK_USB|RCC_PERIPHCLK_RNG
-                              |RCC_PERIPHCLK_ADC;
-  PeriphClkInitStruct.PLLSAI1.PLLN = 6;
-  PeriphClkInitStruct.PLLSAI1.PLLP = RCC_PLLP_DIV2;
-  PeriphClkInitStruct.PLLSAI1.PLLQ = RCC_PLLQ_DIV2;
-  PeriphClkInitStruct.PLLSAI1.PLLR = RCC_PLLR_DIV2;
-  PeriphClkInitStruct.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_USBCLK|RCC_PLLSAI1_ADCCLK;
-  PeriphClkInitStruct.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
-  PeriphClkInitStruct.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;
-  PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_PLLSAI1;
-  PeriphClkInitStruct.RngClockSelection = RCC_RNGCLKSOURCE_CLK48;
-  PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1;
-  PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
-  PeriphClkInitStruct.RFWakeUpClockSelection = RCC_RFWKPCLKSOURCE_LSE;
-  PeriphClkInitStruct.SmpsClockSelection = RCC_SMPSCLKSOURCE_HSE;
-  PeriphClkInitStruct.SmpsDivSelection = RCC_SMPSCLKDIV_RANGE1;
-  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
-  {
-    Error_Handler();
-  }
-  /* USER CODE BEGIN Smps */
-  /* USER CODE END Smps */
-  /** Enables the Clock Security System
-  */
-  HAL_RCC_EnableCSS();
-  /** Enables the Clock Security System
-  */
-  HAL_RCCEx_EnableLSECSS();
+    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
+    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
+    RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
+
+    HAL_PWR_EnableBkUpAccess();
+
+    __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_MEDIUMLOW);
+    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
+
+    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSE
+                                                            |RCC_OSCILLATORTYPE_LSE;
+    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
+    RCC_OscInitStruct.LSEState = RCC_LSE_ON;
+    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
+    RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
+    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
+    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
+    RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV2;
+    RCC_OscInitStruct.PLL.PLLN = 8;
+    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
+    RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
+    RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
+    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
+        Error_Handler();
+    }
+    
+    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK4|RCC_CLOCKTYPE_HCLK2
+                                                            |RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
+                                                            |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
+    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
+    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
+    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
+    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
+    RCC_ClkInitStruct.AHBCLK2Divider = RCC_SYSCLK_DIV2;
+    RCC_ClkInitStruct.AHBCLK4Divider = RCC_SYSCLK_DIV1;
+
+    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK) {
+        Error_Handler();
+    }
+
+    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SMPS|RCC_PERIPHCLK_RFWAKEUP
+                                                            |RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_USART1
+                                                            |RCC_PERIPHCLK_I2C1|RCC_PERIPHCLK_CLK48SEL
+                                                            |RCC_PERIPHCLK_USB|RCC_PERIPHCLK_RNG
+                                                            |RCC_PERIPHCLK_ADC;
+    PeriphClkInitStruct.PLLSAI1.PLLN = 6;
+    PeriphClkInitStruct.PLLSAI1.PLLP = RCC_PLLP_DIV2;
+    PeriphClkInitStruct.PLLSAI1.PLLQ = RCC_PLLQ_DIV2;
+    PeriphClkInitStruct.PLLSAI1.PLLR = RCC_PLLR_DIV2;
+    PeriphClkInitStruct.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_USBCLK|RCC_PLLSAI1_ADCCLK;
+    PeriphClkInitStruct.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
+    PeriphClkInitStruct.I2c1ClockSelection = RCC_I2C1CLKSOURCE_PCLK1;
+    PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_PLLSAI1;
+    PeriphClkInitStruct.RngClockSelection = RCC_RNGCLKSOURCE_CLK48;
+    PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1;
+    PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
+    PeriphClkInitStruct.RFWakeUpClockSelection = RCC_RFWKPCLKSOURCE_LSE;
+    PeriphClkInitStruct.SmpsClockSelection = RCC_SMPSCLKSOURCE_HSE;
+    PeriphClkInitStruct.SmpsDivSelection = RCC_SMPSCLKDIV_RANGE1;
+    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) {
+        Error_Handler();
+    }
+
+    // Enable CSS for both clocks
+    HAL_RCC_EnableCSS();
+    HAL_RCCEx_EnableLSECSS();
 }
 }
 
 
-/* USER CODE BEGIN 4 */
-
-/* USER CODE END 4 */
-
-/**
-  * @brief  This function is executed in case of error occurrence.
-  * @retval None
-  */
-void Error_Handler(void)
-{
-  /* USER CODE BEGIN Error_Handler_Debug */
-  /* User can add his own implementation to report the HAL error return state */
-  asm("bkpt 1");
-  /* USER CODE END Error_Handler_Debug */
+void Error_Handler(void) {
+    asm("bkpt 1");
+    while(1) {}
 }
 }
 
 
 #ifdef  USE_FULL_ASSERT
 #ifdef  USE_FULL_ASSERT
 /**
 /**
-  * @brief  Reports the name of the source file and the source line number
-  *         where the assert_param error has occurred.
-  * @param  file: pointer to the source file name
-  * @param  line: assert_param error line source number
-  * @retval None
-  */
-void assert_failed(uint8_t *file, uint32_t line)
-{
-  /* USER CODE BEGIN 6 */
-  /* User can add his own implementation to report the file name and line number,
-     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
-  /* USER CODE END 6 */
+    * @brief  Reports the name of the source file and the source line number
+    *         where the assert_param error has occurred.
+    * @param  file: pointer to the source file name
+    * @param  line: assert_param error line source number
+    * @retval None
+    */
+void assert_failed(uint8_t *file, uint32_t line) {
+    /* USER CODE BEGIN 6 */
+    /* User can add his own implementation to report the file name and line number,
+         tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
+    /* USER CODE END 6 */
 }
 }
 #endif /* USE_FULL_ASSERT */
 #endif /* USE_FULL_ASSERT */
-
-/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

+ 95 - 0
firmware/targets/f4/api-hal/api-hal-os-timer.h

@@ -0,0 +1,95 @@
+#pragma once
+
+#include <stm32wbxx_ll_lptim.h>
+#include <stdbool.h>
+
+static inline void assert(bool value) {
+    if (!value) asm("bkpt 1");
+}
+
+// Timer used for system ticks
+#define API_HAL_OS_TIMER_MAX  0xFFFF
+#define API_HAL_OS_TIMER_REG_LOAD_DLY 0x1
+#define API_HAL_OS_TIMER       LPTIM2
+#define API_HAL_OS_TIMER_IRQ   LPTIM2_IRQn
+#define API_HAL_OS_TIMER_CLOCK_INIT() \
+{ \
+    LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_LSE); \
+    LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPTIM2); \
+} \
+
+static inline void api_hal_os_timer_init() {
+    API_HAL_OS_TIMER_CLOCK_INIT();
+
+    LL_LPTIM_Enable(API_HAL_OS_TIMER);
+    while(!LL_LPTIM_IsEnabled(API_HAL_OS_TIMER)) {}
+
+    LL_LPTIM_SetClockSource(API_HAL_OS_TIMER, LL_LPTIM_CLK_SOURCE_INTERNAL);
+    LL_LPTIM_SetPrescaler(API_HAL_OS_TIMER, LL_LPTIM_PRESCALER_DIV1);
+    LL_LPTIM_SetPolarity(API_HAL_OS_TIMER, LL_LPTIM_OUTPUT_POLARITY_REGULAR);
+    LL_LPTIM_SetUpdateMode(API_HAL_OS_TIMER, LL_LPTIM_UPDATE_MODE_IMMEDIATE);
+    LL_LPTIM_SetCounterMode(API_HAL_OS_TIMER, LL_LPTIM_COUNTER_MODE_INTERNAL);
+    LL_LPTIM_TrigSw(API_HAL_OS_TIMER);
+    LL_LPTIM_SetInput1Src(API_HAL_OS_TIMER, LL_LPTIM_INPUT1_SRC_GPIO);
+    LL_LPTIM_SetInput2Src(API_HAL_OS_TIMER, LL_LPTIM_INPUT2_SRC_GPIO);
+
+    NVIC_SetPriority(API_HAL_OS_TIMER_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0));
+    NVIC_EnableIRQ(API_HAL_OS_TIMER_IRQ);
+}
+
+static inline uint32_t api_hal_os_timer_get_cnt() {
+    uint32_t counter = LL_LPTIM_GetCounter(API_HAL_OS_TIMER);
+    uint32_t counter_shadow = LL_LPTIM_GetCounter(API_HAL_OS_TIMER);
+    while(counter != counter_shadow) {
+        counter = counter_shadow;
+        counter_shadow = LL_LPTIM_GetCounter(API_HAL_OS_TIMER);
+    }
+    return counter;
+}
+
+static inline bool api_hal_os_timer_arr_is_ok() {
+    return LL_LPTIM_IsActiveFlag_ARROK(API_HAL_OS_TIMER);
+}
+
+static inline uint32_t api_hal_os_timer_get_arr() {
+    return LL_LPTIM_GetAutoReload(API_HAL_OS_TIMER);;
+}
+
+static inline void api_hal_os_timer_set_arr(uint32_t value) {
+    value &= API_HAL_OS_TIMER_MAX;
+    if (value != api_hal_os_timer_get_arr()) {
+        assert(api_hal_os_timer_arr_is_ok());
+        LL_LPTIM_ClearFlag_ARROK(API_HAL_OS_TIMER);
+        LL_LPTIM_SetAutoReload(API_HAL_OS_TIMER, value);
+    }
+}
+
+static inline bool api_hal_os_timer_cmp_is_ok() {
+    return LL_LPTIM_IsActiveFlag_CMPOK(API_HAL_OS_TIMER);
+}
+
+static inline uint32_t api_hal_os_timer_get_cmp() {
+    return LL_LPTIM_GetCompare(API_HAL_OS_TIMER);
+}
+
+static inline void api_hal_os_timer_set_cmp(uint32_t value) {
+    value &= API_HAL_OS_TIMER_MAX;
+    if (value != api_hal_os_timer_get_cmp()) {
+        assert(api_hal_os_timer_cmp_is_ok());
+        LL_LPTIM_ClearFlag_CMPOK(API_HAL_OS_TIMER);
+        LL_LPTIM_SetCompare(API_HAL_OS_TIMER, value);
+    }
+}
+
+static inline bool api_hal_os_timer_is_safe() {
+    uint16_t cmp = api_hal_os_timer_get_cmp();
+    uint16_t cnt = api_hal_os_timer_get_cnt();
+    uint16_t margin = (cmp > cnt) ? cmp - cnt : cnt - cmp;
+    if (margin < 8) {
+        return false;
+    }
+    if (!api_hal_os_timer_cmp_is_ok()) {
+        return false;
+    }
+    return true;
+}

+ 169 - 0
firmware/targets/f4/api-hal/api-hal-os.c

@@ -0,0 +1,169 @@
+#include <api-hal-os.h>
+#include <api-hal-os-timer.h>
+#include <api-hal-power.h>
+
+#include <FreeRTOS.h>
+#include <cmsis_os.h>
+
+#define API_HAL_OS_CLK_FREQUENCY 32768
+#define API_HAL_OS_TICK_PER_SECOND 1024
+#define API_HAL_OS_CLK_PER_TICK (API_HAL_OS_CLK_FREQUENCY / API_HAL_OS_TICK_PER_SECOND)
+#define API_HAL_OS_TICK_PER_EPOCH (API_HAL_OS_TIMER_MAX / API_HAL_OS_CLK_PER_TICK)
+#define API_HAL_OS_MAX_SLEEP (API_HAL_OS_TICK_PER_EPOCH - 1)
+
+#ifdef API_HAL_OS_DEBUG
+#include <stm32wbxx_ll_gpio.h>
+#define LED_GREEN_PORT GPIOA
+#define LED_GREEN_PIN LL_GPIO_PIN_2
+#endif
+
+typedef struct {
+    // Tick counters
+    volatile uint32_t in_sleep;
+    volatile uint32_t in_awake;
+    // Error counters
+    volatile uint32_t sleep_error;
+    volatile uint32_t awake_error;
+} ApiHalOs;
+
+ApiHalOs api_hal_os = {
+    .in_sleep = 0,
+    .in_awake = 0,
+    .sleep_error = 0,
+    .awake_error = 0,
+};
+
+void api_hal_os_init() {
+    api_hal_os_timer_init();
+    LL_DBGMCU_APB1_GRP2_FreezePeriph(LL_DBGMCU_APB1_GRP2_LPTIM2_STOP);
+
+    LL_LPTIM_EnableIT_CMPM(API_HAL_OS_TIMER);
+    LL_LPTIM_EnableIT_ARRM(API_HAL_OS_TIMER);
+
+    LL_LPTIM_SetAutoReload(API_HAL_OS_TIMER, API_HAL_OS_TIMER_MAX);
+    LL_LPTIM_SetCompare(API_HAL_OS_TIMER, API_HAL_OS_CLK_PER_TICK);
+
+    LL_LPTIM_StartCounter(API_HAL_OS_TIMER, LL_LPTIM_OPERATING_MODE_CONTINUOUS);
+}
+
+void LPTIM2_IRQHandler(void) {
+    // Autoreload
+    const bool arrm_flag = LL_LPTIM_IsActiveFlag_ARRM(API_HAL_OS_TIMER);
+    if(arrm_flag) {
+        LL_LPTIM_ClearFLAG_ARRM(API_HAL_OS_TIMER);
+    }
+    if(LL_LPTIM_IsActiveFlag_CMPM(API_HAL_OS_TIMER)) {
+        LL_LPTIM_ClearFLAG_CMPM(API_HAL_OS_TIMER);
+
+        // Store important value
+        uint16_t cnt = api_hal_os_timer_get_cnt();
+        uint16_t cmp = api_hal_os_timer_get_cmp();
+        uint16_t current_tick = cnt / API_HAL_OS_CLK_PER_TICK;
+        uint16_t compare_tick = cmp / API_HAL_OS_CLK_PER_TICK;
+
+        // Calculate error
+        // happens when HAL or other high priority IRQ takes our time
+        int32_t error = (int32_t)compare_tick - current_tick;
+        api_hal_os.awake_error += ((error>0) ? error : -error);
+
+        // Calculate and set next tick 
+        uint16_t next_tick = current_tick + 1;
+        api_hal_os_timer_set_cmp(next_tick * API_HAL_OS_CLK_PER_TICK);
+
+        // Notify OS
+        api_hal_os.in_awake ++;
+        if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
+            xPortSysTickHandler();
+        }
+    }
+}
+
+static inline uint32_t api_hal_os_sleep(TickType_t expected_idle_ticks) {
+    // Store important value before going to sleep
+    const uint16_t before_cnt = api_hal_os_timer_get_cnt();
+    const uint16_t before_tick = before_cnt / API_HAL_OS_CLK_PER_TICK;
+
+    // Calculate and set next wakeup compare value
+    const uint16_t expected_cnt = (before_tick + expected_idle_ticks - 2) * API_HAL_OS_CLK_PER_TICK;
+    api_hal_os_timer_set_cmp(expected_cnt);
+
+    HAL_SuspendTick();
+    // Go to stop2 mode
+#ifdef API_HAL_OS_DEBUG
+    LL_GPIO_SetOutputPin(LED_GREEN_PORT, LED_GREEN_PIN);
+#endif
+    api_hal_power_deep_sleep();
+#ifdef API_HAL_OS_DEBUG
+    LL_GPIO_ResetOutputPin(LED_GREEN_PORT, LED_GREEN_PIN);
+#endif
+
+    HAL_ResumeTick();
+
+    // Spin till we are in timer safe zone
+    while(!api_hal_os_timer_is_safe()) {}
+
+    // Store current counter value, calculate current tick
+    const uint16_t after_cnt = api_hal_os_timer_get_cnt();
+    const uint16_t after_tick = after_cnt / API_HAL_OS_CLK_PER_TICK;
+
+    // Store and clear interrupt flags
+    // we don't want handler to be called after renabling IRQ
+    bool arrm_flag = LL_LPTIM_IsActiveFlag_ARRM(API_HAL_OS_TIMER);
+
+    // Calculate and set next wakeup compare value
+    const uint16_t next_cmp = (after_tick + 1) * API_HAL_OS_CLK_PER_TICK;
+    api_hal_os_timer_set_cmp(next_cmp);
+
+    // Calculate ticks count spent in sleep and perform sanity checks
+    int32_t completed_ticks = arrm_flag ? (int32_t)before_tick - after_tick : (int32_t)after_tick - before_tick;
+
+    return completed_ticks;
+}
+
+void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) {
+    if (!api_hal_power_deep_available()) {
+        return;
+    }
+
+    // Limit mount of ticks to maximum that timer can count
+    if (expected_idle_ticks > API_HAL_OS_MAX_SLEEP) {
+        expected_idle_ticks = API_HAL_OS_MAX_SLEEP;
+    }
+
+    // Stop IRQ handling, no one should disturb us till we finish 
+    __disable_irq();
+
+    // Confirm OS that sleep is still possible
+    // And check if timer is in safe zone
+    // (8 clocks till any IRQ event or ongoing synchronization)
+    if (eTaskConfirmSleepModeStatus() == eAbortSleep
+        || !api_hal_os_timer_is_safe()) {
+        __enable_irq();
+        return;
+    }
+
+    uint32_t completed_ticks = api_hal_os_sleep(expected_idle_ticks);
+    assert(completed_ticks >= 0);
+
+    // Reenable IRQ
+    __enable_irq();
+
+    // Notify system about time spent in sleep
+    if (completed_ticks > 0) {
+        api_hal_os.in_sleep += completed_ticks;
+        if (completed_ticks > expected_idle_ticks) {
+            // We are late, count error
+            api_hal_os.sleep_error += (completed_ticks - expected_idle_ticks);
+            // Freertos is not happy when we overleep
+            // But we are not going to tell her
+            vTaskStepTick(expected_idle_ticks);
+        } else {
+            vTaskStepTick(completed_ticks);
+        }
+    }
+}
+
+void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName) {
+    asm("bkpt 1");
+    while(1) {};
+}

+ 17 - 0
firmware/targets/f4/api-hal/api-hal-os.h

@@ -0,0 +1,17 @@
+#pragma once
+
+#include <stdint.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Initialize OS helpers
+ * Configure and start tick timer
+ */
+void api_hal_os_init();
+
+#ifdef __cplusplus
+}
+#endif

+ 15 - 1
firmware/targets/f4/api-hal/api-hal-power.c

@@ -12,6 +12,8 @@
 #include <bq27220.h>
 #include <bq27220.h>
 #include <bq25896.h>
 #include <bq25896.h>
 
 
+volatile uint32_t api_hal_power_insomnia = 0;
+
 void HAL_RCC_CSSCallback(void) {
 void HAL_RCC_CSSCallback(void) {
     LL_RCC_ForceBackupDomainReset();
     LL_RCC_ForceBackupDomainReset();
     LL_RCC_ReleaseBackupDomainReset();
     LL_RCC_ReleaseBackupDomainReset();
@@ -24,8 +26,20 @@ void api_hal_power_init() {
     bq25896_init();
     bq25896_init();
 }
 }
 
 
+uint16_t api_hal_power_insomnia_level() {
+    return api_hal_power_insomnia;
+}
+
+void api_hal_power_insomnia_enter() {
+    api_hal_power_insomnia++;
+}
+
+void api_hal_power_insomnia_exit() {
+    api_hal_power_insomnia--;
+}
+
 bool api_hal_power_deep_available() {
 bool api_hal_power_deep_available() {
-    return api_hal_bt_is_alive();
+    return api_hal_bt_is_alive() && api_hal_power_insomnia == 0;
 }
 }
 
 
 void api_hal_power_deep_sleep() {
 void api_hal_power_deep_sleep() {

+ 0 - 95
firmware/targets/f4/api-hal/api-hal-timebase-timer.h

@@ -1,95 +0,0 @@
-#pragma once
-
-#include <stm32wbxx_ll_lptim.h>
-#include <stdbool.h>
-
-static inline void assert(bool value) {
-    if (!value) asm("bkpt 1");
-}
-
-// Timer used for system ticks
-#define API_HAL_TIMEBASE_TIMER_MAX  0xFFFF
-#define API_HAL_TIMEBASE_TIMER_REG_LOAD_DLY 0x1
-#define API_HAL_TIMEBASE_TIMER       LPTIM2
-#define API_HAL_TIMEBASE_TIMER_IRQ   LPTIM2_IRQn
-#define API_HAL_TIMEBASE_TIMER_CLOCK_INIT() \
-{ \
-    LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_LSE); \
-    LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPTIM2); \
-} \
-
-static inline void api_hal_timebase_timer_init() {
-    API_HAL_TIMEBASE_TIMER_CLOCK_INIT();
-
-    LL_LPTIM_Enable(API_HAL_TIMEBASE_TIMER);
-    while(!LL_LPTIM_IsEnabled(API_HAL_TIMEBASE_TIMER)) {}
-
-    LL_LPTIM_SetClockSource(API_HAL_TIMEBASE_TIMER, LL_LPTIM_CLK_SOURCE_INTERNAL);
-    LL_LPTIM_SetPrescaler(API_HAL_TIMEBASE_TIMER, LL_LPTIM_PRESCALER_DIV1);
-    LL_LPTIM_SetPolarity(API_HAL_TIMEBASE_TIMER, LL_LPTIM_OUTPUT_POLARITY_REGULAR);
-    LL_LPTIM_SetUpdateMode(API_HAL_TIMEBASE_TIMER, LL_LPTIM_UPDATE_MODE_IMMEDIATE);
-    LL_LPTIM_SetCounterMode(API_HAL_TIMEBASE_TIMER, LL_LPTIM_COUNTER_MODE_INTERNAL);
-    LL_LPTIM_TrigSw(API_HAL_TIMEBASE_TIMER);
-    LL_LPTIM_SetInput1Src(API_HAL_TIMEBASE_TIMER, LL_LPTIM_INPUT1_SRC_GPIO);
-    LL_LPTIM_SetInput2Src(API_HAL_TIMEBASE_TIMER, LL_LPTIM_INPUT2_SRC_GPIO);
-
-    NVIC_SetPriority(API_HAL_TIMEBASE_TIMER_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0));
-    NVIC_EnableIRQ(API_HAL_TIMEBASE_TIMER_IRQ);
-}
-
-static inline uint32_t api_hal_timebase_timer_get_cnt() {
-    uint32_t counter = LL_LPTIM_GetCounter(API_HAL_TIMEBASE_TIMER);
-    uint32_t counter_shadow = LL_LPTIM_GetCounter(API_HAL_TIMEBASE_TIMER);
-    while(counter != counter_shadow) {
-        counter = counter_shadow;
-        counter_shadow = LL_LPTIM_GetCounter(API_HAL_TIMEBASE_TIMER);
-    }
-    return counter;
-}
-
-static inline bool api_hal_timebase_timer_arr_is_ok() {
-    return LL_LPTIM_IsActiveFlag_ARROK(API_HAL_TIMEBASE_TIMER);
-}
-
-static inline uint32_t api_hal_timebase_timer_get_arr() {
-    return LL_LPTIM_GetAutoReload(API_HAL_TIMEBASE_TIMER);;
-}
-
-static inline void api_hal_timebase_timer_set_arr(uint32_t value) {
-    value &= API_HAL_TIMEBASE_TIMER_MAX;
-    if (value != api_hal_timebase_timer_get_arr()) {
-        assert(api_hal_timebase_timer_arr_is_ok());
-        LL_LPTIM_ClearFlag_ARROK(API_HAL_TIMEBASE_TIMER);
-        LL_LPTIM_SetAutoReload(API_HAL_TIMEBASE_TIMER, value);
-    }
-}
-
-static inline bool api_hal_timebase_timer_cmp_is_ok() {
-    return LL_LPTIM_IsActiveFlag_CMPOK(API_HAL_TIMEBASE_TIMER);
-}
-
-static inline uint32_t api_hal_timebase_timer_get_cmp() {
-    return LL_LPTIM_GetCompare(API_HAL_TIMEBASE_TIMER);
-}
-
-static inline void api_hal_timebase_timer_set_cmp(uint32_t value) {
-    value &= API_HAL_TIMEBASE_TIMER_MAX;
-    if (value != api_hal_timebase_timer_get_cmp()) {
-        assert(api_hal_timebase_timer_cmp_is_ok());
-        LL_LPTIM_ClearFlag_CMPOK(API_HAL_TIMEBASE_TIMER);
-        LL_LPTIM_SetCompare(API_HAL_TIMEBASE_TIMER, value);
-    }
-}
-
-static inline bool api_hal_timebase_timer_is_safe() {
-    uint16_t cmp = api_hal_timebase_timer_get_cmp();
-    uint16_t cnt = api_hal_timebase_timer_get_cnt();
-    uint16_t margin = (cmp > cnt) ? cmp - cnt : cnt - cmp;
-    if (margin < 8) {
-        return false;
-    }
-    if (!api_hal_timebase_timer_cmp_is_ok()) {
-        return false;
-    }
-    return true;
-}

+ 0 - 179
firmware/targets/f4/api-hal/api-hal-timebase.c

@@ -1,179 +0,0 @@
-#include <api-hal-timebase.h>
-#include <api-hal-timebase-timer.h>
-#include <api-hal-power.h>
-
-#include <FreeRTOS.h>
-#include <cmsis_os.h>
-
-#define API_HAL_TIMEBASE_CLK_FREQUENCY 32768
-#define API_HAL_TIMEBASE_TICK_PER_SECOND 1024
-#define API_HAL_TIMEBASE_CLK_PER_TICK (API_HAL_TIMEBASE_CLK_FREQUENCY / API_HAL_TIMEBASE_TICK_PER_SECOND)
-#define API_HAL_TIMEBASE_TICK_PER_EPOCH (API_HAL_TIMEBASE_TIMER_MAX / API_HAL_TIMEBASE_CLK_PER_TICK)
-#define API_HAL_TIMEBASE_MAX_SLEEP (API_HAL_TIMEBASE_TICK_PER_EPOCH - 1)
-
-#ifdef API_HAL_TIMEBASE_DEBUG
-#include <stm32wbxx_ll_gpio.h>
-#define LED_GREEN_PORT GPIOA
-#define LED_GREEN_PIN LL_GPIO_PIN_2
-#endif
-
-typedef struct {
-    // Sleep control
-    volatile uint16_t insomnia;
-    // Tick counters
-    volatile uint32_t in_sleep;
-    volatile uint32_t in_awake;
-    // Error counters
-    volatile uint32_t sleep_error;
-    volatile uint32_t awake_error;
-} ApiHalTimbase;
-
-ApiHalTimbase api_hal_timebase = {
-    .insomnia = 0,
-    .in_sleep = 0,
-    .in_awake = 0,
-    .sleep_error = 0,
-    .awake_error = 0,
-};
-
-void api_hal_timebase_init() {
-    api_hal_timebase_timer_init();
-    LL_DBGMCU_APB1_GRP2_FreezePeriph(LL_DBGMCU_APB1_GRP2_LPTIM2_STOP);
-
-    LL_LPTIM_EnableIT_CMPM(API_HAL_TIMEBASE_TIMER);
-    LL_LPTIM_EnableIT_ARRM(API_HAL_TIMEBASE_TIMER);
-
-    LL_LPTIM_SetAutoReload(API_HAL_TIMEBASE_TIMER, API_HAL_TIMEBASE_TIMER_MAX);
-    LL_LPTIM_SetCompare(API_HAL_TIMEBASE_TIMER, API_HAL_TIMEBASE_CLK_PER_TICK);
-
-    LL_LPTIM_StartCounter(API_HAL_TIMEBASE_TIMER, LL_LPTIM_OPERATING_MODE_CONTINUOUS);
-}
-
-uint16_t api_hal_timebase_insomnia_level() {
-    return api_hal_timebase.insomnia;
-}
-
-void api_hal_timebase_insomnia_enter() {
-    api_hal_timebase.insomnia++;
-}
-
-void api_hal_timebase_insomnia_exit() {
-    api_hal_timebase.insomnia--;
-}
-
-void LPTIM2_IRQHandler(void) {
-    // Autoreload
-    const bool arrm_flag = LL_LPTIM_IsActiveFlag_ARRM(API_HAL_TIMEBASE_TIMER);
-    if(arrm_flag) {
-        LL_LPTIM_ClearFLAG_ARRM(API_HAL_TIMEBASE_TIMER);
-    }
-    if(LL_LPTIM_IsActiveFlag_CMPM(API_HAL_TIMEBASE_TIMER)) {
-        LL_LPTIM_ClearFLAG_CMPM(API_HAL_TIMEBASE_TIMER);
-
-        // Store important value
-        uint16_t cnt = api_hal_timebase_timer_get_cnt();
-        uint16_t cmp = api_hal_timebase_timer_get_cmp();
-        uint16_t current_tick = cnt / API_HAL_TIMEBASE_CLK_PER_TICK;
-        uint16_t compare_tick = cmp / API_HAL_TIMEBASE_CLK_PER_TICK;
-
-        // Calculate error
-        // happens when HAL or other high priority IRQ takes our time
-        int32_t error = (int32_t)compare_tick - current_tick;
-        api_hal_timebase.awake_error += ((error>0) ? error : -error);
-
-        // Calculate and set next tick 
-        uint16_t next_tick = current_tick + 1;
-        api_hal_timebase_timer_set_cmp(next_tick * API_HAL_TIMEBASE_CLK_PER_TICK);
-
-        // Notify OS
-        api_hal_timebase.in_awake ++;
-        if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
-            xPortSysTickHandler();
-        }
-    }
-}
-
-static inline uint32_t api_hal_timebase_sleep(TickType_t expected_idle_ticks) {
-    // Store important value before going to sleep
-    const uint16_t before_cnt = api_hal_timebase_timer_get_cnt();
-    const uint16_t before_tick = before_cnt / API_HAL_TIMEBASE_CLK_PER_TICK;
-
-    // Calculate and set next wakeup compare value
-    const uint16_t expected_cnt = (before_tick + expected_idle_ticks - 2) * API_HAL_TIMEBASE_CLK_PER_TICK;
-    api_hal_timebase_timer_set_cmp(expected_cnt);
-
-    HAL_SuspendTick();
-    // Go to stop2 mode
-#ifdef API_HAL_TIMEBASE_DEBUG
-    LL_GPIO_SetOutputPin(LED_GREEN_PORT, LED_GREEN_PIN);
-#endif
-    api_hal_power_deep_sleep();
-#ifdef API_HAL_TIMEBASE_DEBUG
-    LL_GPIO_ResetOutputPin(LED_GREEN_PORT, LED_GREEN_PIN);
-#endif
-
-    HAL_ResumeTick();
-
-    // Spin till we are in timer safe zone
-    while(!api_hal_timebase_timer_is_safe()) {}
-
-    // Store current counter value, calculate current tick
-    const uint16_t after_cnt = api_hal_timebase_timer_get_cnt();
-    const uint16_t after_tick = after_cnt / API_HAL_TIMEBASE_CLK_PER_TICK;
-
-    // Store and clear interrupt flags
-    // we don't want handler to be called after renabling IRQ
-    bool arrm_flag = LL_LPTIM_IsActiveFlag_ARRM(API_HAL_TIMEBASE_TIMER);
-
-    // Calculate and set next wakeup compare value
-    const uint16_t next_cmp = (after_tick + 1) * API_HAL_TIMEBASE_CLK_PER_TICK;
-    api_hal_timebase_timer_set_cmp(next_cmp);
-
-    // Calculate ticks count spent in sleep and perform sanity checks
-    int32_t completed_ticks = arrm_flag ? (int32_t)before_tick - after_tick : (int32_t)after_tick - before_tick;
-
-    return completed_ticks;
-}
-
-void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) {
-    if (!api_hal_power_deep_available() || api_hal_timebase.insomnia) {
-        return;
-    }
-
-    // Limit mount of ticks to maximum that timer can count
-    if (expected_idle_ticks > API_HAL_TIMEBASE_MAX_SLEEP) {
-        expected_idle_ticks = API_HAL_TIMEBASE_MAX_SLEEP;
-    }
-
-    // Stop IRQ handling, no one should disturb us till we finish 
-    __disable_irq();
-
-    // Confirm OS that sleep is still possible
-    // And check if timer is in safe zone
-    // (8 clocks till any IRQ event or ongoing synchronization)
-    if (eTaskConfirmSleepModeStatus() == eAbortSleep
-        || !api_hal_timebase_timer_is_safe()) {
-        __enable_irq();
-        return;
-    }
-
-    uint32_t completed_ticks = api_hal_timebase_sleep(expected_idle_ticks);
-    assert(completed_ticks >= 0);
-
-    // Reenable IRQ
-    __enable_irq();
-
-    // Notify system about time spent in sleep
-    if (completed_ticks > 0) {
-        api_hal_timebase.in_sleep += completed_ticks;
-        if (completed_ticks > expected_idle_ticks) {
-            // We are late, count error
-            api_hal_timebase.sleep_error += (completed_ticks - expected_idle_ticks);
-            // Freertos is not happy when we overleep
-            // But we are not going to tell her
-            vTaskStepTick(expected_idle_ticks);
-        } else {
-            vTaskStepTick(completed_ticks);
-        }
-    }
-}

+ 0 - 37
firmware/targets/f4/api-hal/api-hal-timebase.h

@@ -1,37 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Initialize timebase
- * Configure and start tick timer
- */
-void api_hal_timebase_init();
-
-/* Get current insomnia level
- * @return insomnia level: 0 - no insomnia, >0 - insomnia, bearer count.
- */
-uint16_t api_hal_timebase_insomnia_level();
-
-/* Enter insomnia mode
- * Prevents device from going to sleep
- * @warning Internally increases insomnia level
- * Must be paired with api_hal_timebase_insomnia_exit
- */
-void api_hal_timebase_insomnia_enter();
-
-/* Exit insomnia mode
- * Allow device to go to sleep
- * @warning Internally decreases insomnia level.
- * Must be paired with api_hal_timebase_insomnia_enter
- */
-void api_hal_timebase_insomnia_exit();
-
-
-#ifdef __cplusplus
-}
-#endif

+ 1 - 1
firmware/targets/f4/api-hal/api-hal.c

@@ -1,7 +1,7 @@
 #include <api-hal.h>
 #include <api-hal.h>
 
 
 void api_hal_init() {
 void api_hal_init() {
-    api_hal_timebase_init();
+    api_hal_os_init();
     api_hal_vcp_init();
     api_hal_vcp_init();
     api_hal_spi_init();
     api_hal_spi_init();
 }
 }

+ 2 - 2
firmware/targets/f4/ble-glue/app_ble.c

@@ -219,7 +219,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt )
       }
       }
       /* restart advertising */
       /* restart advertising */
       Adv_Request(APP_BLE_FAST_ADV);
       Adv_Request(APP_BLE_FAST_ADV);
-      api_hal_timebase_insomnia_exit();
+      api_hal_power_insomnia_exit();
     }
     }
     break; /* EVT_DISCONN_COMPLETE */
     break; /* EVT_DISCONN_COMPLETE */
 
 
@@ -268,7 +268,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt )
           break;
           break;
         case EVT_LE_CONN_COMPLETE:
         case EVT_LE_CONN_COMPLETE:
         {
         {
-          api_hal_timebase_insomnia_enter();
+          api_hal_power_insomnia_enter();
           hci_le_connection_complete_event_rp0 *connection_complete_event;
           hci_le_connection_complete_event_rp0 *connection_complete_event;
 
 
           /**
           /**

+ 2 - 2
firmware/targets/f4/ble-glue/app_entry.c

@@ -52,7 +52,7 @@ void APPE_Init() {
   HW_TS_Init(hw_ts_InitMode_Full, &hrtc); /**< Initialize the TimerServer */
   HW_TS_Init(hw_ts_InitMode_Full, &hrtc); /**< Initialize the TimerServer */
 
 
   // APPD_Init();
   // APPD_Init();
-  api_hal_timebase_insomnia_enter();
+  api_hal_power_insomnia_enter();
 
 
   appe_Tl_Init();	/* Initialize all transport layers */
   appe_Tl_Init();	/* Initialize all transport layers */
 
 
@@ -144,7 +144,7 @@ static void APPE_SysUserEvtRx( void * pPayload ) {
   } else {
   } else {
     ble_glue_status = BleGlueStatusBroken;
     ble_glue_status = BleGlueStatusBroken;
   }
   }
-  api_hal_timebase_insomnia_exit();
+  api_hal_power_insomnia_exit();
 }
 }
 
 
 /*************************************************************
 /*************************************************************

+ 3 - 3
firmware/targets/f4/target.mk

@@ -14,9 +14,9 @@ FLASH_ADDRESS	= 0x08000000
 CFLAGS			+= -DNO_BOOTLOADER
 CFLAGS			+= -DNO_BOOTLOADER
 endif
 endif
 
 
-API_HAL_TIMEBASE_DEBUG ?= 0
-ifeq ($(API_HAL_TIMEBASE_DEBUG), 1)
-CFLAGS			+= -DAPI_HAL_TIMEBASE_DEBUG
+API_HAL_OS_DEBUG ?= 0
+ifeq ($(API_HAL_OS_DEBUG), 1)
+CFLAGS			+= -DAPI_HAL_OS_DEBUG
 endif
 endif
 
 
 OPENOCD_OPTS	= -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "stm32wbx.cpu configure -rtos auto" -c "init"
 OPENOCD_OPTS	= -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "stm32wbx.cpu configure -rtos auto" -c "init"

+ 5 - 5
lib/app-template/app-template.cpp

@@ -44,12 +44,12 @@ public:
 // with template variables <state, events>
 // with template variables <state, events>
 class AppExample : public AppTemplate<AppExampleState, AppExampleEvent> {
 class AppExample : public AppTemplate<AppExampleState, AppExampleEvent> {
 public:
 public:
-    void run();
+    uint8_t run();
     void render(Canvas* canvas);
     void render(Canvas* canvas);
 };
 };
 
 
 // start app
 // start app
-void AppExample::run() {
+uint8_t AppExample::run() {
     // here we dont need to acquire or release state
     // here we dont need to acquire or release state
     // because before we call app_ready our application is "single threaded"
     // because before we call app_ready our application is "single threaded"
     state.example_data = 12;
     state.example_data = 12;
@@ -67,7 +67,7 @@ void AppExample::run() {
                 // press events
                 // press events
                 if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyBack) {
                 if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyBack) {
                     printf("bye!\n");
                     printf("bye!\n");
-                    exit();
+                    return exit();
                 }
                 }
 
 
                 if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyUp) {
                 if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyUp) {
@@ -95,7 +95,7 @@ void AppExample::render(Canvas* canvas) {
 }
 }
 
 
 // app enter function
 // app enter function
-extern "C" void app_cpp_example(void* p) {
+extern "C" uint8_t app_cpp_example(void* p) {
     AppExample* app = new AppExample();
     AppExample* app = new AppExample();
-    app->run();
+    return app->run();
 }
 }

+ 4 - 4
lib/app-template/app-template.h

@@ -22,7 +22,7 @@ public:
     void release_state(void);
     void release_state(void);
     bool get_event(TEvent* event, uint32_t timeout);
     bool get_event(TEvent* event, uint32_t timeout);
     void app_ready(void);
     void app_ready(void);
-    void exit(void);
+    uint8_t exit(void);
     void update_gui(void);
     void update_gui(void);
 };
 };
 
 
@@ -34,7 +34,7 @@ template <class TState, class TEvent> AppTemplate<TState, TEvent>::AppTemplate()
     // TODO: use plain os mutex?
     // TODO: use plain os mutex?
     if(!init_mutex(&state_mutex, &state, sizeof(TState))) {
     if(!init_mutex(&state_mutex, &state, sizeof(TState))) {
         printf("cannot create mutex\n");
         printf("cannot create mutex\n");
-        furiac_exit();
+        furi_check(0);
     }
     }
 
 
     // open gui
     // open gui
@@ -100,10 +100,10 @@ template <class TState, class TEvent> void AppTemplate<TState, TEvent>::app_read
     gui_add_view_port(gui, view_port, GuiLayerFullscreen);
     gui_add_view_port(gui, view_port, GuiLayerFullscreen);
 }
 }
 
 
-template <class TState, class TEvent> void AppTemplate<TState, TEvent>::exit(void) {
+template <class TState, class TEvent> uint8_t AppTemplate<TState, TEvent>::exit(void) {
     // TODO remove all view_ports create by app
     // TODO remove all view_ports create by app
     view_port_enabled_set(view_port, false);
     view_port_enabled_set(view_port, false);
-    osThreadExit();
+    return 255;
 }
 }
 
 
 template <class TState, class TEvent> void AppTemplate<TState, TEvent>::update_gui(void) {
 template <class TState, class TEvent> void AppTemplate<TState, TEvent>::update_gui(void) {