ソースを参照

[FL-781] FURI, CLI, stdlib: stdout hooks, integration between subsystems, uniform printf usage (#311)

* FURI stdglue: stdout hooks, local and global, ISR safe printf. Uniform newlines for terminal/debug output. Power: prevent sleep while core 2 has not started.
* Furi record, stdglue: check mutex allocation
* remove unused test
* Furi stdglue: buferized output, dynamically allocated state. Furi record: dynamically allocated state. Input dump: proper line ending. Hal VCP: dynamically allocated state.
* Interrupt manager: explicitly init list.
* Makefile: cleanup rules, fix broken dfu upload. F4: add compiler stack protection options.
* BLE: call debug uart callback on transmission complete
* FreeRTOS: add configUSE_NEWLIB_REENTRANT
* API HAL Timebase: fix issue with idle thread stack corruption caused by systick interrupt. BT: cleanup debug info output. FreeRTOS: disable reentry for newlib.
* F4: update stack protection CFLAGS to match used compiller
* F4: disable compiller stack protection because of incompatibility with current compiller
* Makefile: return openocd logs to gdb
* BLE: fixed pin, moar power, ble trace info.
* Prevent sleep when connection is active
* Makefile: return serial port to upload rule, add workaround for mac os
* Furi: prevent usage of stack for cmsis functions.
* F4: add missing includes, add debugger breakpoints
* Applications: per app stack size.
* Furi: honor kernel state in stdglue
* FreeRTOS: remove unused hooks
* Cleanup and format sources

Co-authored-by: DrZlo13 <who.just.the.doctor@gmail.com>
あく 5 年 前
コミット
584c0962d8
52 ファイル変更516 行追加388 行削除
  1. 5 5
      applications/app-loader/app-loader.c
  2. 64 43
      applications/applications.c
  3. 1 0
      applications/applications.h
  4. 1 1
      applications/bt/bt.c
  5. 16 18
      applications/cc1101-workaround/cc1101-workaround.cpp
  6. 5 5
      applications/cc1101-workaround/cc1101.cpp
  7. 25 19
      applications/cli/cli.c
  8. 0 6
      applications/cli/cli.h
  9. 17 8
      applications/cli/cli_commands.c
  10. 2 0
      applications/cli/cli_i.h
  11. 1 1
      applications/coreglitch_demo_0/coreglitch_demo_0.c
  12. 3 3
      applications/examples/input_dump.c
  13. 1 1
      applications/examples/u8g2_qrcode.c
  14. 1 1
      applications/floopper-bloopper
  15. 2 2
      applications/gpio-tester/gpio-tester.c
  16. 3 3
      applications/gui/u8g2_periphery.c
  17. 3 3
      applications/input/input.c
  18. 1 1
      applications/irda/irda.c
  19. 1 1
      applications/lf-rfid/em4100.c
  20. 4 4
      applications/lf-rfid/lf-rfid.c
  21. 1 1
      applications/menu/menu.c
  22. 2 2
      applications/music-player/music-player.c
  23. 4 4
      applications/power/power.c
  24. 39 39
      applications/sd-card-test/sd-card-test.cpp
  25. 26 26
      applications/sd-filesystem/sd-filesystem.c
  26. 2 2
      applications/template/template.c.example
  27. 3 3
      applications/tests/furi_valuemutex_test.c
  28. 3 3
      applications/tests/furiac_test.c
  29. 2 2
      bootloader/targets/f4/target.mk
  30. 1 1
      core/api-hal/api-interrupt-mgr.c
  31. 5 3
      core/furi.c
  32. 1 4
      core/furi/pubsub.c
  33. 26 17
      core/furi/record.c
  34. 92 6
      core/furi/stdglue.c
  35. 25 1
      core/furi/stdglue.h
  36. 1 4
      core/furi/value-expanders.c
  37. 1 4
      core/furi/valuemutex.c
  38. 3 0
      firmware/targets/api-hal-include/api-hal-power.h
  39. 1 1
      firmware/targets/api-hal-include/api-hal-vcp.h
  40. 8 18
      firmware/targets/f4/Inc/FreeRTOSConfig.h
  41. 6 32
      firmware/targets/f4/Src/app_freertos.c
  42. 3 7
      firmware/targets/f4/Src/stm32wbxx_it.c
  43. 5 0
      firmware/targets/f4/api-hal/api-hal-power.c
  44. 1 1
      firmware/targets/f4/api-hal/api-hal-timebase-timer.h
  45. 7 3
      firmware/targets/f4/api-hal/api-hal-timebase.c
  46. 23 19
      firmware/targets/f4/api-hal/api-hal-vcp.c
  47. 50 47
      firmware/targets/f4/ble-glue/app_ble.c
  48. 4 4
      firmware/targets/f4/ble-glue/app_conf.h
  49. 1 1
      firmware/targets/f4/ble-glue/app_debug.c
  50. 4 4
      firmware/targets/f4/ble-glue/tl_dbg_conf.h
  51. 2 3
      firmware/targets/f4/target.mk
  52. 8 1
      make/rules.mk

+ 5 - 5
applications/app-loader/app-loader.c

@@ -60,7 +60,7 @@ static void handle_menu(void* _ctx) {
     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 = 1024;
+    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;
@@ -72,7 +72,7 @@ static void handle_cli(string_t args, void* _ctx) {
 
     if(ctx->app->app == NULL) return;
 
-    cli_print("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;
@@ -80,13 +80,13 @@ static void handle_cli(string_t args, void* _ctx) {
     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 = 1024;
+    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);
 
-    cli_print("Press any key to kill application");
+    printf("Press any key to kill application");
 
     char c;
     cli_read(&c, 1);
@@ -184,7 +184,7 @@ void app_loader(void* p) {
             menu_item_add(menu, menu_plugins);
         });
 
-    printf("[app loader] start\n");
+    printf("[app loader] start\r\n");
 
     osThreadSuspend(self_id);
 }

+ 64 - 43
applications/applications.c

@@ -38,114 +38,129 @@ void sd_filesystem(void* p);
 
 const FuriApplication FLIPPER_SERVICES[] = {
 #ifdef APP_DISPLAY
-    {.app = display_u8g2, .name = "display_u8g2", .icon = A_Plugins_14},
+    {.app = display_u8g2, .name = "display_u8g2", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_CLI
-    {.app = cli_task, .name = "cli_task", .icon = A_Plugins_14},
+    {.app = cli_task, .name = "cli_task", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_EXAMPLE_BLINK
-    {.app = application_blink, .name = "blink", .icon = A_Plugins_14},
+    {.app = application_blink, .name = "blink", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_INPUT
-    {.app = input_task, .name = "input_task", .icon = A_Plugins_14},
+    {.app = input_task, .name = "input_task", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_EXAMPLE_INPUT_DUMP
-    {.app = application_input_dump, .name = "input dump", .icon = A_Plugins_14},
+    {.app = application_input_dump, .name = "input dump", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_GUI
-    {.app = backlight_control, .name = "backlight_control", .icon = A_Plugins_14},
-    {.app = gui_task, .name = "gui_task", .icon = A_Plugins_14},
+    {.app = backlight_control,
+     .name = "backlight_control",
+     .stack_size = 1024,
+     .icon = A_Plugins_14},
+    {.app = gui_task, .name = "gui_task", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_MENU
-    {.app = menu_task, .name = "menu_task", .icon = A_Plugins_14},
-    {.app = app_loader, .name = "app_loader", .icon = A_Plugins_14},
+    {.app = menu_task, .name = "menu_task", .stack_size = 1024, .icon = A_Plugins_14},
+    {.app = app_loader, .name = "app_loader", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_SD_FILESYSTEM
-    {.app = sd_filesystem, .name = "sd_filesystem", .icon = A_Plugins_14},
+    {.app = sd_filesystem, .name = "sd_filesystem", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_DOLPHIN
-    {.app = dolphin_task, .name = "dolphin_task", .icon = A_Plugins_14},
+    {.app = dolphin_task, .name = "dolphin_task", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_POWER
-    {.app = power_task, .name = "power_task", .icon = A_Plugins_14},
+    {.app = power_task, .name = "power_task", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_BT
-    {.app = bt_task, .name = "bt_task", .icon = A_Plugins_14},
+    {.app = bt_task, .name = "bt_task", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_CC1101
-    {.app = cc1101_workaround, .name = "cc1101 workaround", .icon = A_Plugins_14},
+    {.app = cc1101_workaround,
+     .name = "cc1101 workaround",
+     .stack_size = 1024,
+     .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_LF_RFID
-    {.app = lf_rfid_workaround, .name = "lf rfid workaround", .icon = A_Plugins_14},
+    {.app = lf_rfid_workaround,
+     .name = "lf rfid workaround",
+     .stack_size = 1024,
+     .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_IRDA
-    {.app = irda, .name = "irda", .icon = A_Plugins_14},
+    {.app = irda, .name = "irda", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_NFC
-    {.app = nfc_task, .name = "nfc_task", .icon = A_Plugins_14},
+    {.app = nfc_task, .name = "nfc_task", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_TEST
-    {.app = flipper_test_app, .name = "test app", .icon = A_Plugins_14},
+    {.app = flipper_test_app, .name = "test app", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_EXAMPLE_IPC
-    {.app = application_ipc_display, .name = "ipc display", .icon = A_Plugins_14},
-    {.app = application_ipc_widget, .name = "ipc widget", .icon = A_Plugins_14},
+    {.app = application_ipc_display,
+     .name = "ipc display",
+     .stack_size = 1024,
+     .icon = A_Plugins_14},
+    {.app = application_ipc_widget, .name = "ipc widget", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_EXAMPLE_QRCODE
-    {.app = u8g2_qrcode, .name = "u8g2_qrcode", .icon = A_Plugins_14},
+    {.app = u8g2_qrcode, .name = "u8g2_qrcode", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_EXAMPLE_FATFS
-    {.app = fatfs_list, .name = "fatfs_list", .icon = A_Plugins_14},
+    {.app = fatfs_list, .name = "fatfs_list", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_EXAMPLE_DISPLAY
-    {.app = u8g2_example, .name = "u8g2_example", .icon = A_Plugins_14},
+    {.app = u8g2_example, .name = "u8g2_example", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_SPEAKER_DEMO
-    {.app = coreglitch_demo_0, .name = "coreglitch_demo_0", .icon = A_Plugins_14},
+    {.app = coreglitch_demo_0,
+     .name = "coreglitch_demo_0",
+     .stack_size = 1024,
+     .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_SD_TEST
-    {.app = sd_card_test, .name = "sd_card_test", .icon = A_Plugins_14},
+    {.app = sd_card_test, .name = "sd_card_test", .stack_size = 4096, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_MUSIC_PLAYER
-    {.app = music_player, .name = "music player", .icon = A_Plugins_14},
+    {.app = music_player, .name = "music player", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_IBUTTON
-    {.app = app_ibutton, .name = "ibutton", .icon = A_Plugins_14},
+    {.app = app_ibutton, .name = "ibutton", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_GPIO_DEMO
-    {.app = app_gpio_test, .name = "gpio test", .icon = A_Plugins_14},
+    {.app = app_gpio_test, .name = "gpio test", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef APP_FLOOPPER_BLOOPPER
-    {.app = floopper_bloopper, .name = "Floopper Bloopper", .icon = A_Games_14},
+    {.app = floopper_bloopper, .name = "Floopper Bloopper", .stack_size = 1024, .icon = A_Games_14},
 #endif
 
 #ifdef APP_SDNFC
-    {.app = sdnfc, .name = "sdnfc", .icon = A_Plugins_14},
+    {.app = sdnfc, .name = "sdnfc", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 };
 
@@ -156,23 +171,23 @@ size_t FLIPPER_SERVICES_size() {
 // Main menu APP
 const FuriApplication FLIPPER_APPS[] = {
 #ifdef BUILD_CC1101
-    {.app = cc1101_workaround, .name = "Sub-1 GHz", .icon = A_Sub1ghz_14},
+    {.app = cc1101_workaround, .name = "Sub-1 GHz", .stack_size = 1024, .icon = A_Sub1ghz_14},
 #endif
 
 #ifdef BUILD_LF_RFID
-    {.app = lf_rfid_workaround, .name = "125 kHz RFID", .icon = A_125khz_14},
+    {.app = lf_rfid_workaround, .name = "125 kHz RFID", .stack_size = 1024, .icon = A_125khz_14},
 #endif
 
 #ifdef BUILD_IRDA
-    {.app = irda, .name = "Infrared", .icon = A_Infrared_14},
+    {.app = irda, .name = "Infrared", .stack_size = 1024, .icon = A_Infrared_14},
 #endif
 
 #ifdef BUILD_IBUTTON
-    {.app = app_ibutton, .name = "iButton", .icon = A_iButton_14},
+    {.app = app_ibutton, .name = "iButton", .stack_size = 1024, .icon = A_iButton_14},
 #endif
 
 #ifdef BUILD_GPIO_DEMO
-    {.app = app_gpio_test, .name = "GPIO", .icon = A_GPIO_14},
+    {.app = app_gpio_test, .name = "GPIO", .stack_size = 1024, .icon = A_GPIO_14},
 #endif
 };
 
@@ -183,35 +198,41 @@ size_t FLIPPER_APPS_size() {
 // Plugin menu
 const FuriApplication FLIPPER_PLUGINS[] = {
 #ifdef BUILD_EXAMPLE_BLINK
-    {.app = application_blink, .name = "blink", .icon = A_Plugins_14},
+    {.app = application_blink, .name = "blink", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef BUILD_EXAMPLE_INPUT_DUMP
-    {.app = application_input_dump, .name = "input dump", .icon = A_Plugins_14},
+    {.app = application_input_dump, .name = "input dump", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef BUILD_SPEAKER_DEMO
-    {.app = coreglitch_demo_0, .name = "coreglitch_demo_0", .icon = A_Plugins_14},
+    {.app = coreglitch_demo_0,
+     .name = "coreglitch_demo_0",
+     .stack_size = 1024,
+     .icon = A_Plugins_14},
 #endif
 
 #ifdef BUILD_SD_TEST
-    {.app = sd_card_test, .name = "sd_card_test", .icon = A_Plugins_14},
+    {.app = sd_card_test, .name = "sd_card_test", .stack_size = 4096, .icon = A_Plugins_14},
 #endif
 
 #ifdef BUILD_VIBRO_DEMO
-    {.app = application_vibro, .name = "application_vibro", .icon = A_Plugins_14},
+    {.app = application_vibro,
+     .name = "application_vibro",
+     .stack_size = 1024,
+     .icon = A_Plugins_14},
 #endif
 
 #ifdef BUILD_MUSIC_PLAYER
-    {.app = music_player, .name = "music player", .icon = A_Plugins_14},
+    {.app = music_player, .name = "music player", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
 #ifdef BUILD_FLOOPPER_BLOOPPER
-    {.app = floopper_bloopper, .name = "Floopper Bloopper", .icon = A_Games_14},
+    {.app = floopper_bloopper, .name = "Floopper Bloopper", .stack_size = 1024, .icon = A_Games_14},
 #endif
 
 #ifdef BUILD_SDNFC
-    {.app = sdnfc, .name = "sdnfc", .icon = A_Plugins_14},
+    {.app = sdnfc, .name = "sdnfc", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 };
 

+ 1 - 0
applications/applications.h

@@ -8,6 +8,7 @@ typedef void (*FlipperApplication)(void*);
 typedef struct {
     const FlipperApplication app;
     const char* name;
+    const size_t stack_size;
     const IconName icon;
 } FuriApplication;
 

+ 1 - 1
applications/bt/bt.c

@@ -33,7 +33,7 @@ void bt_cli_info(string_t args, void* context) {
     string_t buffer;
     string_init(buffer);
     api_hal_bt_dump_state(buffer);
-    cli_print(string_get_cstr(buffer));
+    printf(string_get_cstr(buffer));
     string_clear(buffer);
 }
 

+ 16 - 18
applications/cc1101-workaround/cc1101-workaround.cpp

@@ -3,8 +3,6 @@
 #include <gui/gui.h>
 #include <input/input.h>
 
-extern "C" void cli_print(const char* str);
-
 #define RSSI_DELAY 5000 //rssi delay in micro second
 #define CHAN_SPA 0.05 // channel spacing
 
@@ -81,9 +79,9 @@ int16_t rx_rssi(CC1101* cc1101, const FreqConfig* config) {
     /*
     char buf[256];
     sprintf(buf, "status: %d -> %d, rssi: %d\n", rx_status, cc1101->SpiReadStatus(CC1101_MARCSTATE), rssi_dBm);
-    cli_print(buf);
+    printf(buf);
     sprintf(buf, "begin: %d, end: %d\n", begin_size, end_size);
-    cli_print(buf);
+    printf(buf);
     */
 
     // uint8_t rx_data[64];
@@ -101,7 +99,7 @@ int16_t rx_rssi(CC1101* cc1101, const FreqConfig* config) {
             }
             printf(" ");
         }
-        printf("\n");
+        printf("\r\n");
         *
 
         for(uint8_t i = 0; i < fifo_length; i++) {
@@ -111,7 +109,7 @@ int16_t rx_rssi(CC1101* cc1101, const FreqConfig* config) {
             }
         }
     } else {
-        cli_print("fifo size over\n");
+        printf("fifo size over\r\n");
     }
     */
 
@@ -158,12 +156,12 @@ void flp_config(CC1101* cc1101) {
 
     // bandwidth 50-100 kHz
     if(!cc1101->setRxBandwidth(75.0)) {
-        printf("wrong rx bw\n");
+        printf("wrong rx bw\r\n");
     }
 
     // datarate ~30 kbps
     if(!cc1101->setBitRate(100.)) {
-        printf("wrong bitrate\n");
+        printf("wrong bitrate\r\n");
     }
 
     // mod
@@ -361,7 +359,7 @@ extern "C" void cc1101_workaround(void* p) {
 
     ValueMutex state_mutex;
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
-        printf("[cc1101] cannot create mutex\n");
+        printf("[cc1101] cannot create mutex\r\n");
         furiac_exit(NULL);
     }
 
@@ -373,7 +371,7 @@ extern "C" void cc1101_workaround(void* p) {
     // Open GUI and register widget
     Gui* gui = (Gui*)furi_record_open("gui");
     if(gui == NULL) {
-        printf("[cc1101] gui is not available\n");
+        printf("[cc1101] gui is not available\r\n");
         furiac_exit(NULL);
     }
     gui_add_widget(gui, widget, GuiLayerFullscreen);
@@ -381,7 +379,7 @@ extern "C" void cc1101_workaround(void* p) {
     gpio_init(&debug_0, GpioModeOutputPushPull);
     gpio_write((GpioPin*)&debug_0, false);
 
-    printf("[cc1101] creating device\n");
+    printf("[cc1101] creating device\r\n");
     GpioPin cs_pin = {CC1101_CS_GPIO_Port, CC1101_CS_Pin};
 
     gpio_init(&cc1101_g0_gpio, GpioModeInput);
@@ -389,13 +387,13 @@ extern "C" void cc1101_workaround(void* p) {
     // TODO open record
     GpioPin* cs_pin_record = &cs_pin;
     CC1101 cc1101(cs_pin_record);
-    printf("[cc1101] init device\n");
+    printf("[cc1101] init device\r\n");
 
     uint8_t address = cc1101.Init();
     if(address > 0) {
-        printf("[cc1101] init done: %d\n", address);
+        printf("[cc1101] init done: %d\r\n", address);
     } else {
-        printf("[cc1101] init fail\n");
+        printf("[cc1101] init fail\r\n");
         furiac_exit(NULL);
     }
 
@@ -406,7 +404,7 @@ extern "C" void cc1101_workaround(void* p) {
     // setup_freq(&cc1101, &FREQ_LIST[4]);
     // enable_cc1101_irq();
 
-    printf("init ok\n");
+    printf("init ok\r\n");
 
     // TODO open record
     GpioPin* led_record = (GpioPin*)&led_gpio[1];
@@ -428,12 +426,12 @@ extern "C" void cc1101_workaround(void* p) {
         if(event_status == osOK) {
             if(event.type == EventTypeKey) {
                 if(event.value.input.state && event.value.input.input == InputBack) {
-                    printf("[cc1101] bye!\n");
-                    cli_print("[cc1101] bye!\n");
+                    printf("[cc1101] bye!\r\n");
 
                     cc1101.SpiStrobe(CC1101_SIDLE);
                     cc1101.SpiStrobe(CC1101_SPWD);
-                    cli_print("[cc1101] go to power down\n");
+
+                    printf("[cc1101] go to power down\r\n");
 
                     // TODO remove all widgets create by app
                     widget_enabled_set(widget, false);

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

@@ -227,14 +227,14 @@ bool CC1101::SpiSetRegValue(uint8_t reg, uint8_t value, uint8_t msb, uint8_t lsb
 ****************************************************************/
 uint8_t CC1101::Init(void) {
 #ifdef CC1101_DEBUG
-    printf("Init SPI...\n");
+    printf("Init SPI...\r\n");
 #endif
     SpiInit(); //spi initialization
     gpio_write(ss_pin, true);
 // gpio_write(SCK_PIN, true);
 // gpio_write(MOSI_PIN, false);
 #ifdef CC1101_DEBUG
-    printf("Reset CC1101...\n");
+    printf("Reset CC1101...\r\n");
 #endif
     Reset(); // CC1101 reset
 
@@ -256,7 +256,7 @@ uint8_t CC1101::Init(void) {
     // RegConfigSettings(); //CC1101 register config
 
 #ifdef CC1101_DEBUG
-    printf("Done!\n");
+    printf("Done!\r\n");
 #endif
 
     return version;
@@ -308,7 +308,7 @@ void CC1101::SetMod(uint8_t mode) {
     }
     }
 
-    printf("\n");
+    printf("\r\n");
 #endif
 }
 /****************************************************************
@@ -394,7 +394,7 @@ void CC1101::SetReceive(void) {
     SpiStrobe(CC1101_SRX);
     while(SpiReadStatus(CC1101_MARCSTATE) ^ CC1101_STATUS_RX) {
         // delay(1);
-        // printf("wait status\n");
+        // printf("wait status\r\n");
     }
 }
 /****************************************************************

+ 25 - 19
applications/cli/cli.c

@@ -27,32 +27,42 @@ void cli_putc(char c) {
     api_hal_vcp_tx((uint8_t*)&c, 1);
 }
 
-void cli_read(char* buffer, size_t size) {
-    api_hal_vcp_rx((uint8_t*)buffer, size);
+char cli_getc(Cli* cli) {
+    furi_assert(cli);
+    char c;
+    if(api_hal_vcp_rx((uint8_t*)&c, 1) == 0) {
+        cli_reset_state(cli);
+    }
+    return c;
 }
 
-void cli_print(const char* str) {
-    api_hal_vcp_tx((uint8_t*)str, strlen(str));
+void cli_stdout_callback(void* _cookie, const char* data, size_t size) {
+    api_hal_vcp_tx((const uint8_t*)data, size);
+}
+
+void cli_read(char* buffer, size_t size) {
+    api_hal_vcp_rx((uint8_t*)buffer, size);
 }
 
 void cli_print_version() {
-    cli_print("Build date:" BUILD_DATE ". "
-              "Git Commit:" GIT_COMMIT ". "
-              "Git Branch:" GIT_BRANCH ". "
-              "Commit Number:" GIT_BRANCH_NUM ".");
+    printf("Build date:" BUILD_DATE ". "
+           "Git Commit:" GIT_COMMIT ". "
+           "Git Branch:" GIT_BRANCH ". "
+           "Commit Number:" GIT_BRANCH_NUM ".");
 }
 
 void cli_motd() {
-    cli_print("Flipper cli.\r\n");
+    printf("Flipper cli.\r\n");
     cli_print_version();
 }
 
 void cli_nl() {
-    cli_print("\r\n");
+    printf("\r\n");
 }
 
 void cli_prompt() {
-    cli_print("\r\n>: ");
+    printf("\r\n>: ");
+    fflush(stdout);
 }
 
 void cli_backspace(Cli* cli) {
@@ -100,8 +110,8 @@ void cli_enter(Cli* cli) {
         cli_prompt();
     } else {
         cli_nl();
-        cli_print("Command not found: ");
-        cli_print(string_get_cstr(command));
+        printf("Command not found: ");
+        printf(string_get_cstr(command));
         cli_prompt();
         cli_putc(CliSymbolAsciiBell);
     }
@@ -112,14 +122,9 @@ void cli_enter(Cli* cli) {
 }
 
 void cli_process_input(Cli* cli) {
-    char c;
+    char c = cli_getc(cli);
     size_t r;
 
-    r = api_hal_vcp_rx((uint8_t*)&c, 1);
-    if(r == 0) {
-        cli_reset_state(cli);
-    }
-
     if(c == CliSymbolAsciiTab) {
         cli_putc(CliSymbolAsciiBell);
     } else if(c == CliSymbolAsciiSOH) {
@@ -175,6 +180,7 @@ void cli_task(void* p) {
 
     furi_record_create("cli", cli);
 
+    furi_stdglue_set_thread_stdout_callback(cli_stdout_callback);
     while(1) {
         cli_process_input(cli);
     }

+ 0 - 6
applications/cli/cli.h

@@ -34,12 +34,6 @@ void cli_add_command(Cli* cli, const char* name, CliCallback callback, void* con
  */
 void cli_read(char* buffer, size_t size);
 
-/* Print to terminal
- * Do it only from inside of callback.
- * @param buffer - pointer to null terminated string to print.
- */
-void cli_print(const char* buffer);
-
 /* New line 
  * Send new ine sequence
  */

+ 17 - 8
applications/cli/cli_commands.c

@@ -5,22 +5,22 @@
 void cli_command_help(string_t args, void* context) {
     (void)args;
     Cli* cli = context;
-    cli_print("Commands we have:");
+    printf("Commands we have:");
 
     furi_check(osMutexAcquire(cli->mutex, osWaitForever) == osOK);
     CliCommandDict_it_t it;
     for(CliCommandDict_it(it, cli->commands); !CliCommandDict_end_p(it); CliCommandDict_next(it)) {
         CliCommandDict_itref_t* ref = CliCommandDict_ref(it);
-        cli_print(" ");
-        cli_print(string_get_cstr(ref->key));
+        printf(" ");
+        printf(string_get_cstr(ref->key));
     };
     furi_check(osMutexRelease(cli->mutex) == osOK);
 
     if(string_size(args) > 0) {
         cli_nl();
-        cli_print("Also I have no clue what '");
-        cli_print(string_get_cstr(args));
-        cli_print("' is.");
+        printf("Also I have no clue what '");
+        printf(string_get_cstr(args));
+        printf("' is.");
     }
 }
 
@@ -43,7 +43,7 @@ void cli_command_uuid(string_t args, void* context) {
         uint8_t uid_byte = uid[i];
         string_cat_printf(byte_str, "%02X", uid_byte);
     }
-    cli_print(string_get_cstr(byte_str));
+    printf(string_get_cstr(byte_str));
 }
 
 void cli_command_date(string_t args, void* context) {
@@ -61,11 +61,19 @@ void cli_command_date(string_t args, void* context) {
     string_cat_printf(datetime_str, "%.2d:%.2d:%.2d ", time.Hours, time.Minutes, time.Seconds);
     string_cat_printf(datetime_str, "%.2d-%.2d-%.2d", date.Month, date.Date, 2000 + date.Year);
 
-    cli_print(string_get_cstr(datetime_str));
+    printf(string_get_cstr(datetime_str));
 
     string_clear(datetime_str);
 }
 
+void cli_command_log(string_t args, void* context) {
+    Cli* cli = context;
+    furi_stdglue_set_global_stdout_callback(cli_stdout_callback);
+    printf("Press any key to stop...\r\n");
+    cli_getc(cli);
+    furi_stdglue_set_global_stdout_callback(NULL);
+}
+
 void cli_commands_init(Cli* cli) {
     cli_add_command(cli, "help", cli_command_help, cli);
     cli_add_command(cli, "?", cli_command_help, cli);
@@ -73,4 +81,5 @@ void cli_commands_init(Cli* cli) {
     cli_add_command(cli, "!", cli_command_version, cli);
     cli_add_command(cli, "uid", cli_command_uuid, cli);
     cli_add_command(cli, "date", cli_command_date, cli);
+    cli_add_command(cli, "log", cli_command_log, cli);
 }

+ 2 - 0
applications/cli/cli_i.h

@@ -38,4 +38,6 @@ Cli* cli_alloc();
 void cli_free(Cli* cli);
 void cli_reset_state(Cli* cli);
 void cli_print_version();
+char cli_getc(Cli* cli);
 void cli_putc(char c);
+void cli_stdout_callback(void* _cookie, const char* data, size_t size);

+ 1 - 1
applications/coreglitch_demo_0/coreglitch_demo_0.c

@@ -4,7 +4,7 @@
 extern TIM_HandleTypeDef SPEAKER_TIM;
 
 void coreglitch_demo_0(void* p) {
-    printf("coreglitch demo!\n");
+    printf("coreglitch demo!\r\n");
 
     float notes[] = {
         0.0,

+ 3 - 3
applications/examples/input_dump.c

@@ -11,13 +11,13 @@ static void state_cb(const void* value, void* ctx) {
     InputDump dump = {.packed = 0};
     dump.state = *(InputState*)value;
 
-    printf("state: %02x\n", dump.packed);
+    printf("state: %02x\r\n", dump.packed);
 }
 
 static void event_cb(const void* value, void* ctx) {
     const InputEvent* event = value;
 
-    printf("event: %02x %s\n", event->input, event->state ? "pressed" : "released");
+    printf("event: %02x %s\r\n", event->input, event->state ? "pressed" : "released");
 }
 
 void application_input_dump(void* p) {
@@ -28,7 +28,7 @@ void application_input_dump(void* p) {
     PubSub* event_record = furi_record_open("input_events");
     subscribe_pubsub(event_record, event_cb, NULL);
 
-    printf("Example app [input dump]\n");
+    printf("Example app [input dump]\r\n");
 
     for(;;) {
         delay(100);

+ 1 - 1
applications/examples/u8g2_qrcode.c

@@ -37,7 +37,7 @@ void u8g2_qrcode(void* p) {
     qrcode_initText(&qrcode, qrcodeBytes, qr_version, qr_error_correction, "HELLO FLIPPER");
 
     if(fb_record == NULL) {
-        printf("[widget] cannot create fb record\n");
+        printf("[widget] cannot create fb record\r\n");
         furiac_exit(NULL);
     }
 

+ 1 - 1
applications/floopper-bloopper

@@ -1 +1 @@
-Subproject commit 621044255a8be4d2c3f342e2b22178a342ccbfe8
+Subproject commit 7ba50ef912566c78d2ddd37ab5912a8cada5c35d

+ 2 - 2
applications/gpio-tester/gpio-tester.c

@@ -66,7 +66,7 @@ void app_gpio_test(void* p) {
 
     ValueMutex state_mutex;
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
-        printf("[gpio-tester] cannot create mutex\n");
+        printf("[gpio-tester] cannot create mutex\r\n");
         furiac_exit(NULL);
     }
 
@@ -94,7 +94,7 @@ void app_gpio_test(void* p) {
         if(event_status == osOK) {
             if(event.type == EventTypeKey) {
                 if(event.value.input.state && event.value.input.input == InputBack) {
-                    printf("[gpio-tester] bye!\n");
+                    printf("[gpio-tester] bye!\r\n");
                     // TODO remove all widgets create by app
                     widget_enabled_set(widget, false);
                     furiac_exit(NULL);

+ 3 - 3
applications/gui/u8g2_periphery.c

@@ -76,7 +76,7 @@ uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_
 
     case U8X8_MSG_BYTE_INIT:
 #ifdef DEBUG
-        printf("[u8g2] init\n");
+        printf("[u8g2] init\r\n");
 #endif
 
         // TODO change it to FuriRecord pin
@@ -85,7 +85,7 @@ uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_
 
     case U8X8_MSG_BYTE_START_TRANSFER:
 #ifdef DEBUG
-        printf("[u8g2] start\n");
+        printf("[u8g2] start\r\n");
 #endif
 
         // TODO: SPI manager
@@ -98,7 +98,7 @@ uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_
 
     case U8X8_MSG_BYTE_END_TRANSFER:
 #ifdef DEBUG
-        printf("[u8g2] end\n");
+        printf("[u8g2] end\r\n");
 #endif
 
         asm("nop");

+ 3 - 3
applications/input/input.c

@@ -25,15 +25,15 @@ void input_task(void* p) {
     uint8_t debounce_counters[INPUT_COUNT];
 
     if(!init_managed(&input_state_record, &input_state, sizeof(input_state))) {
-        printf("[input_task] cannot initialize ValueManager for input_state\n");
+        printf("[input_task] cannot initialize ValueManager for input_state\r\n");
         furiac_exit(NULL);
     }
     if(!init_pubsub(&input_events_record)) {
-        printf("[input_task] cannot initialize PubSub for input_events\n");
+        printf("[input_task] cannot initialize PubSub for input_events\r\n");
         furiac_exit(NULL);
     }
     if(!init_event(&event)) {
-        printf("[input_task] cannot initialize Event\n");
+        printf("[input_task] cannot initialize Event\r\n");
         furiac_exit(NULL);
     }
 

+ 1 - 1
applications/irda/irda.c

@@ -263,7 +263,7 @@ void irda(void* p) {
 
     ValueMutex state_mutex;
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
-        printf("cannot create mutex\n");
+        printf("cannot create mutex\r\n");
         furiac_exit(NULL);
     }
 

+ 1 - 1
applications/lf-rfid/em4100.c

@@ -48,7 +48,7 @@ void prepare_data(uint32_t ID, uint32_t VENDOR, uint8_t* data) {
     for(uint8_t i = 0; i < 64; i++) {
         printf("%d ", data[i]);
     }
-    printf("\n");
+    printf("\r\n");
     */
 }
 

+ 4 - 4
applications/lf-rfid/lf-rfid.c

@@ -106,7 +106,7 @@ static bool even_check(uint8_t* buf) {
                 buf[line * (ROW_SIZE + 1) + ROW_SIZE]);
             return false;
         }
-        printf("\n");
+        printf("\r\n");
     }
 
     for(uint8_t col = 0; col < ROW_SIZE; col++) {
@@ -138,7 +138,7 @@ static void extract_data(uint8_t* buf, uint8_t* customer, uint32_t* em_data) {
             offset++;
         }
     }
-    printf("\n");
+    printf("\r\n");
 
     *customer = data;
 
@@ -155,7 +155,7 @@ static void extract_data(uint8_t* buf, uint8_t* customer, uint32_t* em_data) {
             offset++;
         }
     }
-    printf("\n");
+    printf("\r\n");
 
     *em_data = data;
 }
@@ -194,7 +194,7 @@ void lf_rfid_workaround(void* p) {
 
     ValueMutex state_mutex;
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
-        printf("cannot create mutex\n");
+        printf("cannot create mutex\r\n");
         furiac_exit(NULL);
     }
 

+ 1 - 1
applications/menu/menu.c

@@ -33,7 +33,7 @@ ValueMutex* menu_init() {
 
     ValueMutex* menu_mutex = furi_alloc(sizeof(ValueMutex));
     if(menu_mutex == NULL || !init_mutex(menu_mutex, menu, sizeof(Menu))) {
-        printf("[menu_task] cannot create menu mutex\n");
+        printf("[menu_task] cannot create menu mutex\r\n");
         furiac_exit(NULL);
     }
 

+ 2 - 2
applications/music-player/music-player.c

@@ -369,7 +369,7 @@ void music_player(void* p) {
 
     ValueMutex state_mutex;
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
-        printf("cannot create mutex\n");
+        printf("cannot create mutex\r\n");
         furiac_exit(NULL);
     }
 
@@ -393,7 +393,7 @@ void music_player(void* p) {
     osThreadId_t player = osThreadNew(music_player_thread, &context, &player_attr);
 
     if(player == NULL) {
-        printf("cannot create player thread\n");
+        printf("cannot create player thread\r\n");
         furiac_exit(NULL);
     }
 

+ 4 - 4
applications/power/power.c

@@ -142,19 +142,19 @@ void power_free(Power* power) {
 }
 
 void power_cli_poweroff(string_t args, void* context) {
-    cli_print("Poweroff in 3 seconds");
+    printf("Poweroff in 3 seconds");
     osDelay(3000);
     api_hal_power_off();
 }
 
 void power_cli_reset(string_t args, void* context) {
-    cli_print("NVIC System Reset in 3 seconds");
+    printf("NVIC System Reset in 3 seconds");
     osDelay(3000);
     NVIC_SystemReset();
 }
 
 void power_cli_dfu(string_t args, void* context) {
-    cli_print("NVIC System Reset to DFU mode in 3 seconds");
+    printf("NVIC System Reset to DFU mode in 3 seconds");
     api_hal_boot_set_mode(ApiHalBootModeDFU);
     osDelay(3000);
     NVIC_SystemReset();
@@ -164,7 +164,7 @@ void power_cli_test(string_t args, void* context) {
     string_t buffer;
     string_init(buffer);
     api_hal_power_dump_state(buffer);
-    cli_print(string_get_cstr(buffer));
+    printf(string_get_cstr(buffer));
     string_clear(buffer);
 }
 

+ 39 - 39
applications/sd-card-test/sd-card-test.cpp

@@ -610,99 +610,99 @@ void SdTest::cli_read_benchmark(string_t args, void* _ctx) {
     const uint8_t str_buffer_size = 64;
     char str_buffer[str_buffer_size];
 
-    cli_print("preparing benchmark data\r\n");
+    printf("preparing benchmark data\r\n");
     bool data_prepared = _this->prepare_benchmark_data();
     if(data_prepared) {
-        cli_print("benchmark data prepared\r\n");
+        printf("benchmark data prepared\r\n");
     } else {
-        cli_print("error: cannot allocate buffer for benchmark data\r\n");
+        printf("error: cannot allocate buffer for benchmark data\r\n");
     }
 
     // prepare data for read test
-    cli_print("prepare data for read speed test, procedure can be lengthy, please wait\r\n");
+    printf("prepare data for read speed test, procedure can be lengthy, please wait\r\n");
 
     if(!_this->fs_api->file.open(&file, "read.test", FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
-        cli_print("error: cannot open file in prepare read\r\n");
+        printf("error: cannot open file in prepare read\r\n");
     }
 
     for(size_t i = 0; i < benchmark_data_size / b4096_size; i++) {
         bytes_written = _this->fs_api->file.write(&file, benchmark_data, b4096_size);
         if(bytes_written != b4096_size || file.error_id != FSE_OK) {
-            cli_print("error: cannot write to file in prepare read\r\n");
+            printf("error: cannot write to file in prepare read\r\n");
         }
     }
 
     if(!_this->fs_api->file.close(&file)) {
-        cli_print("error: cannot close file in prepare read\r\n");
+        printf("error: cannot close file in prepare read\r\n");
     }
 
     // test start
-    cli_print("read speed test, procedure can be lengthy, please wait\r\n");
+    printf("read speed test, procedure can be lengthy, please wait\r\n");
 
     // open file
     if(!_this->fs_api->file.open(&file, "read.test", FSAM_READ, FSOM_OPEN_EXISTING)) {
-        cli_print("error: cannot open file in read benchmark\r\n");
+        printf("error: cannot open file in read benchmark\r\n");
     }
 
     // 1b test
     benchmark_bps =
         _this->read_benchmark_internal(b1_size, benchmark_data_size / b1_size, &file, true);
     if(benchmark_bps == BENCHMARK_ERROR) {
-        cli_print("error: in 1-byte read test\r\n");
+        printf("error: in 1-byte read test\r\n");
     } else {
         snprintf(str_buffer, str_buffer_size, "1-byte: %lu bytes per second\r\n", benchmark_bps);
-        cli_print(str_buffer);
+        printf(str_buffer);
     }
 
     // 8b test
     benchmark_bps =
         _this->read_benchmark_internal(b8_size, benchmark_data_size / b8_size, &file, true);
     if(benchmark_bps == BENCHMARK_ERROR) {
-        cli_print("error: in 8-byte read test\r\n");
+        printf("error: in 8-byte read test\r\n");
     } else {
         snprintf(str_buffer, str_buffer_size, "8-byte: %lu bytes per second\r\n", benchmark_bps);
-        cli_print(str_buffer);
+        printf(str_buffer);
     }
 
     // 32b test
     benchmark_bps =
         _this->read_benchmark_internal(b32_size, benchmark_data_size / b32_size, &file, true);
     if(benchmark_bps == BENCHMARK_ERROR) {
-        cli_print("error: in 32-byte read test\r\n");
+        printf("error: in 32-byte read test\r\n");
     } else {
         snprintf(str_buffer, str_buffer_size, "32-byte: %lu bytes per second\r\n", benchmark_bps);
-        cli_print(str_buffer);
+        printf(str_buffer);
     }
 
     // 256b test
     benchmark_bps =
         _this->read_benchmark_internal(b256_size, benchmark_data_size / b256_size, &file, true);
     if(benchmark_bps == BENCHMARK_ERROR) {
-        cli_print("error: in 256-byte read test\r\n");
+        printf("error: in 256-byte read test\r\n");
     } else {
         snprintf(str_buffer, str_buffer_size, "256-byte: %lu bytes per second\r\n", benchmark_bps);
-        cli_print(str_buffer);
+        printf(str_buffer);
     }
 
     // 4096b test
     benchmark_bps =
         _this->read_benchmark_internal(b4096_size, benchmark_data_size / b4096_size, &file, true);
     if(benchmark_bps == BENCHMARK_ERROR) {
-        cli_print("error: in 4096-byte read test\r\n");
+        printf("error: in 4096-byte read test\r\n");
     } else {
         snprintf(
             str_buffer, str_buffer_size, "4096-byte: %lu bytes per second\r\n", benchmark_bps);
-        cli_print(str_buffer);
+        printf(str_buffer);
     }
 
     // close file
     if(!_this->fs_api->file.close(&file)) {
-        cli_print("error: cannot close file\r\n");
+        printf("error: cannot close file\r\n");
     }
 
     _this->free_benchmark_data();
 
-    cli_print("test completed\r\n");
+    printf("test completed\r\n");
 }
 
 void SdTest::cli_write_benchmark(string_t args, void* _ctx) {
@@ -721,68 +721,68 @@ void SdTest::cli_write_benchmark(string_t args, void* _ctx) {
     const uint8_t str_buffer_size = 64;
     char str_buffer[str_buffer_size];
 
-    cli_print("preparing benchmark data\r\n");
+    printf("preparing benchmark data\r\n");
     bool data_prepared = _this->prepare_benchmark_data();
     if(data_prepared) {
-        cli_print("benchmark data prepared\r\n");
+        printf("benchmark data prepared\r\n");
     } else {
-        cli_print("error: cannot allocate buffer for benchmark data\r\n");
+        printf("error: cannot allocate buffer for benchmark data\r\n");
     }
 
-    cli_print("write speed test, procedure can be lengthy, please wait\r\n");
+    printf("write speed test, procedure can be lengthy, please wait\r\n");
 
     // 1b test
     benchmark_bps = _this->write_benchmark_internal(b1_size, benchmark_data_size / b1_size, true);
     if(benchmark_bps == BENCHMARK_ERROR) {
-        cli_print("error: in 1-byte write test\r\n");
+        printf("error: in 1-byte write test\r\n");
     } else {
         snprintf(str_buffer, str_buffer_size, "1-byte: %lu bytes per second\r\n", benchmark_bps);
-        cli_print(str_buffer);
+        printf(str_buffer);
     }
 
     // 8b test
     benchmark_bps = _this->write_benchmark_internal(b8_size, benchmark_data_size / b8_size, true);
     if(benchmark_bps == BENCHMARK_ERROR) {
-        cli_print("error: in 8-byte write test\r\n");
+        printf("error: in 8-byte write test\r\n");
     } else {
         snprintf(str_buffer, str_buffer_size, "8-byte: %lu bytes per second\r\n", benchmark_bps);
-        cli_print(str_buffer);
+        printf(str_buffer);
     }
 
     // 32b test
     benchmark_bps =
         _this->write_benchmark_internal(b32_size, benchmark_data_size / b32_size, true);
     if(benchmark_bps == BENCHMARK_ERROR) {
-        cli_print("error: in 32-byte write test\r\n");
+        printf("error: in 32-byte write test\r\n");
     } else {
         snprintf(str_buffer, str_buffer_size, "32-byte: %lu bytes per second\r\n", benchmark_bps);
-        cli_print(str_buffer);
+        printf(str_buffer);
     }
 
     // 256b test
     benchmark_bps =
         _this->write_benchmark_internal(b256_size, benchmark_data_size / b256_size, true);
     if(benchmark_bps == BENCHMARK_ERROR) {
-        cli_print("error: in 256-byte write test\r\n");
+        printf("error: in 256-byte write test\r\n");
     } else {
         snprintf(str_buffer, str_buffer_size, "256-byte: %lu bytes per second\r\n", benchmark_bps);
-        cli_print(str_buffer);
+        printf(str_buffer);
     }
 
     // 4096b test
     benchmark_bps =
         _this->write_benchmark_internal(b4096_size, benchmark_data_size / b4096_size, true);
     if(benchmark_bps == BENCHMARK_ERROR) {
-        cli_print("error: in 4096-byte write test\r\n");
+        printf("error: in 4096-byte write test\r\n");
     } else {
         snprintf(
             str_buffer, str_buffer_size, "4096-byte: %lu bytes per second\r\n", benchmark_bps);
-        cli_print(str_buffer);
+        printf(str_buffer);
     }
 
     _this->free_benchmark_data();
 
-    cli_print("test completed\r\n");
+    printf("test completed\r\n");
 }
 
 // wait for button press
@@ -873,7 +873,7 @@ template <class T> void SdTest::set_error(std::initializer_list<T> list) {
 template <class T> void SdTest::set_text(std::initializer_list<T> list) {
     uint8_t line_position = 0;
     acquire_state();
-    printf("------------------------\n");
+    printf("------------------------\r\n");
 
     // set line strings from args
     for(auto element : list) {
@@ -886,10 +886,10 @@ template <class T> void SdTest::set_text(std::initializer_list<T> list) {
     // set empty lines
     for(; line_position < state.lines_count; line_position++) {
         state.line[line_position] = "";
-        printf("\n");
+        printf("\r\n");
     }
 
-    printf("------------------------\n");
+    printf("------------------------\r\n");
     release_state();
     update_gui();
 }

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

@@ -174,7 +174,7 @@ void app_sd_info_callback(void* context) {
         if(str_buffer[i] == NULL) {
             memory_error = true;
         } else {
-            snprintf(str_buffer[i], str_buffer_size, "");
+            str_buffer[i][0] = 0;
         }
     }
 
@@ -365,25 +365,25 @@ void app_sd_eject_callback(void* context) {
 static void cli_sd_status(string_t args, void* _ctx) {
     SdApp* sd_app = (SdApp*)_ctx;
 
-    cli_print("SD status: ");
-    cli_print(fs_error_get_internal_desc(sd_app->info.status));
-    cli_print("\r\n");
+    printf("SD status: ");
+    printf(fs_error_get_internal_desc(sd_app->info.status));
+    printf("\r\n");
 }
 
 static void cli_sd_format(string_t args, void* _ctx) {
     SdApp* sd_app = (SdApp*)_ctx;
 
-    cli_print("formatting SD card, please wait\r\n");
+    printf("formatting SD card, please wait\r\n");
 
     // format card
     app_sd_format_internal(sd_app);
 
     if(sd_app->info.status != SD_OK) {
-        cli_print("SD card format error: ");
-        cli_print(fs_error_get_internal_desc(sd_app->info.status));
-        cli_print("\r\n");
+        printf("SD card format error: ");
+        printf(fs_error_get_internal_desc(sd_app->info.status));
+        printf("\r\n");
     } else {
-        cli_print("SD card formatted\r\n");
+        printf("SD card formatted\r\n");
     }
 }
 
@@ -437,42 +437,42 @@ static void cli_sd_info(string_t args, void* _ctx) {
         }
 
         snprintf(str_buffer, str_buffer_size, "Label: %s\r\n", volume_label);
-        cli_print(str_buffer);
+        printf(str_buffer);
 
         snprintf(str_buffer, str_buffer_size, "%s, S/N: %lu\r\n", fs_type, serial_num);
-        cli_print(str_buffer);
+        printf(str_buffer);
 
         snprintf(str_buffer, str_buffer_size, "Cluster: %d sectors\r\n", fs->csize);
-        cli_print(str_buffer);
+        printf(str_buffer);
 
         snprintf(str_buffer, str_buffer_size, "Sector: %d bytes\r\n", sector_size);
-        cli_print(str_buffer);
+        printf(str_buffer);
 
         snprintf(
             str_buffer, str_buffer_size, "%lu KB total\r\n", total_sectors / 1024 * sector_size);
-        cli_print(str_buffer);
+        printf(str_buffer);
 
         snprintf(
             str_buffer, str_buffer_size, "%lu KB free\r\n", free_sectors / 1024 * sector_size);
-        cli_print(str_buffer);
+        printf(str_buffer);
     } else {
-        cli_print("SD status error: ");
+        printf("SD status error: ");
         snprintf(
             str_buffer,
             str_buffer_size,
             "%s\r\n",
             fs_error_get_internal_desc(_fs_status(&sd_app->info)));
-        cli_print(str_buffer);
+        printf(str_buffer);
 
-        cli_print("Label error: ");
+        printf("Label error: ");
         snprintf(
             str_buffer, str_buffer_size, "%s\r\n", fs_error_get_internal_desc(get_label_result));
-        cli_print(str_buffer);
+        printf(str_buffer);
 
-        cli_print("Get free error: ");
+        printf("Get free error: ");
         snprintf(
             str_buffer, str_buffer_size, "%s\r\n", fs_error_get_internal_desc(get_free_result));
-        cli_print(str_buffer);
+        printf(str_buffer);
     }
 }
 
@@ -511,7 +511,7 @@ void sd_filesystem(void* p) {
     with_value_mutex(
         menu_vm, (Menu * menu) { menu_item_add(menu, menu_item); });
 
-    printf("[sd_filesystem] start\n");
+    printf("[sd_filesystem] start\r\n");
 
     // add api record
     furi_record_create("sdcard", fs_api);
@@ -522,7 +522,7 @@ void sd_filesystem(void* p) {
     while(true) {
         if(sd_was_present) {
             if(hal_gpio_read_sd_detect()) {
-                printf("[sd_filesystem] card detected\n");
+                printf("[sd_filesystem] card detected\r\n");
 
                 uint8_t bsp_result = BSP_SD_Init();
 
@@ -530,13 +530,13 @@ void sd_filesystem(void* p) {
                     sd_app->info.status = SD_LOW_LEVEL_ERR;
                     printf("[sd_filesystem] bsp error: %x\n", bsp_result);
                 } else {
-                    printf("[sd_filesystem] bsp ok\n");
+                    printf("[sd_filesystem] bsp ok\r\n");
                     sd_app->info.status = f_mount(&sd_app->info.fat_fs, sd_app->info.path, 1);
 
                     if(sd_app->info.status != SD_OK) {
                         printf("[sd_filesystem] mount error: %d\n", sd_app->info.status);
                     } else {
-                        printf("[sd_filesystem] mount ok\n");
+                        printf("[sd_filesystem] mount ok\r\n");
                     }
                 }
 
@@ -545,7 +545,7 @@ void sd_filesystem(void* p) {
             }
         } else {
             if(!hal_gpio_read_sd_detect()) {
-                printf("[sd_filesystem] card removed\n");
+                printf("[sd_filesystem] card removed\r\n");
 
                 widget_enabled_set(sd_app->icon.widget, false);
                 app_sd_unmount_card(sd_app);

+ 2 - 2
applications/template/template.c.example

@@ -44,7 +44,7 @@ void template_app(void* p) {
     /* init state here */
     ValueMutex state_mutex;
     if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
-        printf("cannot create mutex\n");
+        printf("cannot create mutex\r\n");
         furiac_exit(NULL);
     }
 
@@ -56,7 +56,7 @@ void template_app(void* p) {
     // Open GUI and register widget
     Gui* gui = furi_record_open("gui");
     if(gui == NULL) {
-        printf("gui is not available\n");
+        printf("gui is not available\r\n");
         furiac_exit(NULL);
     }
     gui_add_widget(gui, widget, /* specify UI layer */);

+ 3 - 3
applications/tests/furi_valuemutex_test.c

@@ -59,7 +59,7 @@ typedef struct {
 void furi_concurent_app(void* p) {
     ValueMutex* mutex = (ValueMutex*)p;
     if(mutex == NULL) {
-        printf("cannot open mutex\n");
+        printf("cannot open mutex\r\n");
         furiac_exit(NULL);
     }
 
@@ -67,7 +67,7 @@ void furi_concurent_app(void* p) {
         ConcurrentValue* value = (ConcurrentValue*)acquire_mutex_block(mutex);
 
         if(value == NULL) {
-            printf("cannot take record\n");
+            printf("cannot take record\r\n");
             release_mutex(mutex, value);
             furiac_exit(NULL);
         }
@@ -103,7 +103,7 @@ void test_furi_concurrent_access() {
 
         if(value == NULL) {
             release_mutex(&mutex, value);
-            mu_fail("cannot take record\n");
+            mu_fail("cannot take record\r\n");
         }
 
         // emulate read-modify-write broken by context switching

+ 3 - 3
applications/tests/furiac_test.c

@@ -31,19 +31,19 @@ bool test_furi_ac_create_kill() {
 
     FuriApp* widget = furiac_start(create_kill_app, "create_kill_app", (void*)&counter);
     if(widget == NULL) {
-        printf("create widget fail\n");
+        printf("create widget fail\r\n");
         return false;
     }
 
     delay(10);
 
     if(!furiac_kill(widget)) {
-        printf("kill widget fail\n");
+        printf("kill widget fail\r\n");
         return false;
     }
 
     if(value_a == counter) {
-        printf("counter unchanged\n");
+        printf("counter unchanged\r\n");
         return false;
     }
 

+ 2 - 2
bootloader/targets/f4/target.mk

@@ -5,12 +5,12 @@ FW_ADDRESS		= 0x08008000
 OS_OFFSET		= 0x00008000
 FLASH_ADDRESS	= 0x08000000
 
-OPENOCD_OPTS	= -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -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"
 BOOT_CFLAGS		= -DBOOT_ADDRESS=$(BOOT_ADDRESS) -DFW_ADDRESS=$(FW_ADDRESS) -DOS_OFFSET=$(OS_OFFSET)
 MCU_FLAGS		= -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard
 
 CFLAGS			+= $(MCU_FLAGS) $(BOOT_CFLAGS) -DSTM32WB55xx -Wall -fdata-sections -ffunction-sections
-LDFLAGS			+= $(MCU_FLAGS) -specs=nosys.specs -specs=nano.specs
+LDFLAGS			+= $(MCU_FLAGS) -specs=nosys.specs -specs=nano.specs 
 
 CUBE_DIR		= ../lib/STM32CubeWB
 

+ 1 - 1
core/api-hal/api-interrupt-mgr.c

@@ -120,4 +120,4 @@ void api_interrupt_call(InterruptType type, void* hw) {
             }
         }
     }
-}
+}

+ 5 - 3
core/furi.c

@@ -17,20 +17,22 @@ void furi_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 = 1024;
+        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("\n=== Bye from Flipper Zero! ===\n\n");
+    printf("[systemd] === Bye from Flipper Zero! ===\r\n");
 
     return (int)exitcode;
 }

+ 1 - 4
core/furi/pubsub.c

@@ -5,10 +5,7 @@ bool init_pubsub(PubSub* pubsub) {
     // mutex without name,
     // no attributes (unfortunatly robust mutex is not supported by FreeRTOS),
     // with dynamic memory allocation
-    const osMutexAttr_t value_mutex_attr = {
-        .name = NULL, .attr_bits = 0, .cb_mem = NULL, .cb_size = 0U};
-
-    pubsub->mutex = osMutexNew(&value_mutex_attr);
+    pubsub->mutex = osMutexNew(NULL);
     if(pubsub->mutex == NULL) return false;
 
     // construct list

+ 26 - 17
core/furi/record.c

@@ -1,5 +1,6 @@
 #include "record.h"
 #include "check.h"
+#include "memmgr.h"
 
 #include <cmsis_os2.h>
 #include <m-string.h>
@@ -22,34 +23,38 @@ typedef struct {
     FuriRecordDict_t records;
 } FuriRecordData;
 
-FuriRecordData furi_record_data;
+static FuriRecordData* furi_record_data = NULL;
 
 void furi_record_init() {
-    furi_record_data.records_mutex = osMutexNew(NULL);
-    FuriRecordDict_init(furi_record_data.records);
+    furi_record_data = furi_alloc(sizeof(FuriRecordData));
+    furi_record_data->records_mutex = osMutexNew(NULL);
+    furi_check(furi_record_data->records_mutex);
+    FuriRecordDict_init(furi_record_data->records);
 }
 
 FuriRecord* furi_record_get_or_create(string_t name_str) {
-    FuriRecord* record = FuriRecordDict_get(furi_record_data.records, name_str);
+    assert(furi_record_data);
+    FuriRecord* record = FuriRecordDict_get(furi_record_data->records, name_str);
     if(!record) {
         FuriRecord new_record;
         new_record.data = NULL;
         new_record.owner = NULL;
         osThreadIdSet_init(new_record.holders);
-        FuriRecordDict_set_at(furi_record_data.records, name_str, new_record);
-        record = FuriRecordDict_get(furi_record_data.records, name_str);
+        FuriRecordDict_set_at(furi_record_data->records, name_str, new_record);
+        record = FuriRecordDict_get(furi_record_data->records, name_str);
     }
     return record;
 }
 
 void furi_record_create(const char* name, void* data) {
+    assert(furi_record_data);
     osThreadId_t thread_id = osThreadGetId();
 
     string_t name_str;
     string_init_set_str(name_str, name);
 
     // Acquire mutex
-    furi_check(osMutexAcquire(furi_record_data.records_mutex, osWaitForever) == osOK);
+    furi_check(osMutexAcquire(furi_record_data->records_mutex, osWaitForever) == osOK);
     FuriRecord* record = furi_record_get_or_create(name_str);
     record->data = data;
     record->owner = thread_id;
@@ -60,31 +65,34 @@ void furi_record_create(const char* name, void* data) {
         osThreadFlagsSet((osThreadId_t)*osThreadIdSet_ref(it), FURI_RECORD_FLAG_UPDATED);
     }
     // Release mutex
-    furi_check(osMutexRelease(furi_record_data.records_mutex) == osOK);
+    furi_check(osMutexRelease(furi_record_data->records_mutex) == osOK);
 
     string_clear(name_str);
 }
 
 bool furi_record_destroy(const char* name) {
+    assert(furi_record_data);
     osThreadId_t thread_id = osThreadGetId();
 
     string_t name_str;
     string_init_set_str(name_str, name);
 
     bool destroyed = false;
-    furi_check(osMutexAcquire(furi_record_data.records_mutex, osWaitForever) == osOK);
-    FuriRecord* record = FuriRecordDict_get(furi_record_data.records, name_str);
+    furi_check(osMutexAcquire(furi_record_data->records_mutex, osWaitForever) == osOK);
+    FuriRecord* record = FuriRecordDict_get(furi_record_data->records, name_str);
     if(record && record->owner == thread_id && osThreadIdSet_size(record->holders) == 0) {
         osThreadIdSet_clear(record->holders);
-        FuriRecordDict_erase(furi_record_data.records, name_str);
+        FuriRecordDict_erase(furi_record_data->records, name_str);
+        destroyed = true;
     }
-    furi_check(osMutexRelease(furi_record_data.records_mutex) == osOK);
+    furi_check(osMutexRelease(furi_record_data->records_mutex) == osOK);
 
     string_clear(name_str);
     return destroyed;
 }
 
 void* furi_record_open(const char* name) {
+    assert(furi_record_data);
     osThreadId_t thread_id = osThreadGetId();
 
     string_t name_str;
@@ -92,10 +100,10 @@ void* furi_record_open(const char* name) {
 
     FuriRecord* record = NULL;
     while(1) {
-        furi_check(osMutexAcquire(furi_record_data.records_mutex, osWaitForever) == osOK);
+        furi_check(osMutexAcquire(furi_record_data->records_mutex, osWaitForever) == osOK);
         record = furi_record_get_or_create(name_str);
         osThreadIdSet_push(record->holders, (uint32_t)thread_id);
-        furi_check(osMutexRelease(furi_record_data.records_mutex) == osOK);
+        furi_check(osMutexRelease(furi_record_data->records_mutex) == osOK);
         // Check if owner is already arrived
         if(record->owner) {
             break;
@@ -109,15 +117,16 @@ void* furi_record_open(const char* name) {
 }
 
 void furi_record_close(const char* name) {
+    assert(furi_record_data);
     osThreadId_t thread_id = osThreadGetId();
 
     string_t name_str;
     string_init_set_str(name_str, name);
 
-    furi_check(osMutexAcquire(furi_record_data.records_mutex, osWaitForever) == osOK);
-    FuriRecord* record = FuriRecordDict_get(furi_record_data.records, name_str);
+    furi_check(osMutexAcquire(furi_record_data->records_mutex, osWaitForever) == osOK);
+    FuriRecord* record = FuriRecordDict_get(furi_record_data->records, name_str);
     osThreadIdSet_erase(record->holders, (uint32_t)thread_id);
-    furi_check(osMutexRelease(furi_record_data.records_mutex) == osOK);
+    furi_check(osMutexRelease(furi_record_data->records_mutex) == osOK);
 
     string_clear(name_str);
 }

+ 92 - 6
core/furi/stdglue.c

@@ -1,12 +1,57 @@
 #include "stdglue.h"
-#include <main.h>
+#include "check.h"
+#include "memmgr.h"
 
+#include <main.h>
+#include <cmsis_os2.h>
 #include <stdio.h>
 #include <string.h>
+#include <m-dict.h>
 
 extern UART_HandleTypeDef DEBUG_UART;
 
+DICT_DEF2(
+    FuriStdglueCallbackDict,
+    uint32_t,
+    M_DEFAULT_OPLIST,
+    FuriStdglueWriteCallback,
+    M_PTR_OPLIST)
+
+typedef struct {
+    osMutexId_t mutex;
+    FuriStdglueCallbackDict_t global_outputs;
+    FuriStdglueCallbackDict_t thread_outputs;
+} FuriStdglue;
+
+static FuriStdglue* furi_stdglue = NULL;
+
 static ssize_t stdout_write(void* _cookie, const char* data, size_t size) {
+    assert(furi_stdglue);
+    osKernelState_t state = osKernelGetState();
+    osThreadId_t thread_id = osThreadGetId();
+    if(state == osKernelRunning && thread_id &&
+       osMutexAcquire(furi_stdglue->mutex, osWaitForever) == osOK) {
+        // We are in the thread context
+        // Handle global callbacks
+        FuriStdglueCallbackDict_it_t it;
+        for(FuriStdglueCallbackDict_it(it, furi_stdglue->global_outputs);
+            !FuriStdglueCallbackDict_end_p(it);
+            FuriStdglueCallbackDict_next(it)) {
+            osThreadId_t it_thread = (osThreadId_t)FuriStdglueCallbackDict_ref(it)->key;
+            FuriStdglueWriteCallback it_callback = FuriStdglueCallbackDict_ref(it)->value;
+            if(thread_id != it_thread) {
+                it_callback(_cookie, data, size);
+            }
+        }
+        // Handle thread callbacks
+        FuriStdglueWriteCallback* callback_ptr =
+            FuriStdglueCallbackDict_get(furi_stdglue->thread_outputs, (uint32_t)thread_id);
+        if(callback_ptr) {
+            (*callback_ptr)(_cookie, data, size);
+        }
+        furi_check(osMutexRelease(furi_stdglue->mutex) == osOK);
+    }
+    // Flush
     if(data == 0) {
         /*
          * This means that we should flush internal buffers.  Since we
@@ -15,13 +60,20 @@ static ssize_t stdout_write(void* _cookie, const char* data, size_t size) {
          */
         return 0;
     }
-
+    // Debug uart
     HAL_UART_Transmit(&DEBUG_UART, (uint8_t*)data, (uint16_t)size, HAL_MAX_DELAY);
-
+    // All data consumed
     return size;
 }
 
-bool furi_stdglue_init() {
+void furi_stdglue_init() {
+    furi_stdglue = furi_alloc(sizeof(FuriStdglue));
+    // Init outputs structures
+    furi_stdglue->mutex = osMutexNew(NULL);
+    furi_check(furi_stdglue->mutex);
+    FuriStdglueCallbackDict_init(furi_stdglue->global_outputs);
+    FuriStdglueCallbackDict_init(furi_stdglue->thread_outputs);
+    // Prepare and set stdout descriptor
     FILE* fp = fopencookie(
         NULL,
         "w",
@@ -31,8 +83,42 @@ bool furi_stdglue_init() {
             .seek = NULL,
             .close = NULL,
         });
-    setvbuf(fp, NULL, _IONBF, 0);
+    setvbuf(fp, NULL, _IOLBF, 0);
     stdout = fp;
+}
+
+bool furi_stdglue_set_global_stdout_callback(FuriStdglueWriteCallback callback) {
+    assert(furi_stdglue);
+    osThreadId_t thread_id = osThreadGetId();
+    if(thread_id) {
+        furi_check(osMutexAcquire(furi_stdglue->mutex, osWaitForever) == osOK);
+        if(callback) {
+            FuriStdglueCallbackDict_set_at(
+                furi_stdglue->global_outputs, (uint32_t)thread_id, callback);
+        } else {
+            FuriStdglueCallbackDict_erase(furi_stdglue->global_outputs, (uint32_t)thread_id);
+        }
+        furi_check(osMutexRelease(furi_stdglue->mutex) == osOK);
+        return true;
+    } else {
+        return false;
+    }
+}
 
-    return true;
+bool furi_stdglue_set_thread_stdout_callback(FuriStdglueWriteCallback callback) {
+    assert(furi_stdglue);
+    osThreadId_t thread_id = osThreadGetId();
+    if(thread_id) {
+        furi_check(osMutexAcquire(furi_stdglue->mutex, osWaitForever) == osOK);
+        if(callback) {
+            FuriStdglueCallbackDict_set_at(
+                furi_stdglue->thread_outputs, (uint32_t)thread_id, callback);
+        } else {
+            FuriStdglueCallbackDict_erase(furi_stdglue->thread_outputs, (uint32_t)thread_id);
+        }
+        furi_check(osMutexRelease(furi_stdglue->mutex) == osOK);
+        return true;
+    } else {
+        return false;
+    }
 }

+ 25 - 1
core/furi/stdglue.h

@@ -1,12 +1,36 @@
 #pragma once
 
 #include <stdbool.h>
+#include <stdlib.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-bool furi_stdglue_init();
+/* Write callback
+ * @param _cookie - pointer to cookie (see stdio gnu extension)
+ * @param data - pointer to data
+ * @param size - data size
+ * @warnign your handler must consume everything
+ */
+typedef void (*FuriStdglueWriteCallback)(void* _cookie, const char* data, size_t size);
+
+/* Initialized std library glue code */
+void furi_stdglue_init();
+
+/* Set global STDOUT callback
+ * @param callback - callback or NULL to clear
+ * @return true on success, otherwise fail
+ * @warning function is thread aware, use this API from the same thread
+ */
+bool furi_stdglue_set_global_stdout_callback(FuriStdglueWriteCallback callback);
+
+/* Set STDOUT callback for your thread 
+ * @param callback - callback or NULL to clear
+ * @return true on success, otherwise fail
+ * @warning function is thread aware, use this API from the same thread
+ */
+bool furi_stdglue_set_thread_stdout_callback(FuriStdglueWriteCallback callback);
 
 #ifdef __cplusplus
 }

+ 1 - 4
core/furi/value-expanders.c

@@ -10,10 +10,7 @@ bool init_composer(ValueComposer* composer, void* value) {
     // mutex without name,
     // no attributes (unfortunatly robust mutex is not supported by FreeRTOS),
     // with dynamic memory allocation
-    const osMutexAttr_t value_mutex_attr = {
-        .name = NULL, .attr_bits = 0, .cb_mem = NULL, .cb_size = 0U};
-
-    composer->mutex = osMutexNew(&value_mutex_attr);
+    composer->mutex = osMutexNew(NULL);
     if(composer->mutex == NULL) return false;
 
     if(!init_event(&composer->request)) return false;

+ 1 - 4
core/furi/valuemutex.c

@@ -6,10 +6,7 @@ bool init_mutex(ValueMutex* valuemutex, void* value, size_t size) {
     // mutex without name,
     // no attributes (unfortunatly robust mutex is not supported by FreeRTOS),
     // with dynamic memory allocation
-    const osMutexAttr_t value_mutext_attr = {
-        .name = NULL, .attr_bits = 0, .cb_mem = NULL, .cb_size = 0U};
-
-    valuemutex->mutex = osMutexNew(&value_mutext_attr);
+    valuemutex->mutex = osMutexNew(NULL);
     if(valuemutex->mutex == NULL) return false;
 
     valuemutex->value = value;

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

@@ -16,6 +16,9 @@ typedef enum {
 /* Initialize drivers */
 void api_hal_power_init();
 
+/* Check if deep sleep availble */
+bool api_hal_power_deep_available();
+
 /* Go to deep sleep */
 void api_hal_power_deep_sleep();
 

+ 1 - 1
firmware/targets/api-hal-include/api-hal-vcp.h

@@ -25,7 +25,7 @@ size_t api_hal_vcp_rx(uint8_t* buffer, size_t size);
  * @param buffer - pointer to buffer
  * @param size - buffer size
  */
-void api_hal_vcp_tx(uint8_t* buffer, size_t size);
+void api_hal_vcp_tx(const uint8_t* buffer, size_t size);
 
 #ifdef __cplusplus
 }

+ 8 - 18
firmware/targets/f4/Inc/FreeRTOSConfig.h

@@ -58,12 +58,12 @@
 /* USER CODE END 0 */
 #endif
 #define configENABLE_FPU                         1
-#define configENABLE_MPU                         0
+#define configENABLE_MPU                         1
 
 #define configUSE_PREEMPTION                     1
 #define configSUPPORT_STATIC_ALLOCATION          1
 #define configSUPPORT_DYNAMIC_ALLOCATION         1
-#define configUSE_IDLE_HOOK                      1
+#define configUSE_IDLE_HOOK                      0
 #define configUSE_TICK_HOOK                      0
 #define configCPU_CLOCK_HZ                       ( SystemCoreClock )
 #define configTICK_RATE_HZ                       ((TickType_t)1024)
@@ -71,8 +71,8 @@
 #define configMINIMAL_STACK_SIZE                 ((uint16_t)128)
 #define configTOTAL_HEAP_SIZE                    ((size_t)131072)
 #define configMAX_TASK_NAME_LEN                  ( 16 )
-#define configGENERATE_RUN_TIME_STATS            1
-#define configUSE_TRACE_FACILITY                 1
+#define configGENERATE_RUN_TIME_STATS            0
+#define configUSE_TRACE_FACILITY                 0
 #define configUSE_16_BIT_TICKS                   0
 #define configUSE_MUTEXES                        1
 #define configQUEUE_REGISTRY_SIZE                8
@@ -83,6 +83,7 @@
 #define configUSE_PORT_OPTIMISED_TASK_SELECTION  0
 #define configUSE_TICKLESS_IDLE                  2
 #define configRECORD_STACK_HIGH_ADDRESS          1
+#define configUSE_NEWLIB_REENTRANT               0
 /* USER CODE BEGIN MESSAGE_BUFFER_LENGTH_TYPE */
 /* Defaults to size_t for backward compatibility, but can be changed
    if lengths will always be less than the number of bytes in a size_t. */
@@ -106,7 +107,6 @@ to exclude the API function. */
 #define INCLUDE_vTaskPrioritySet             1
 #define INCLUDE_uxTaskPriorityGet            1
 #define INCLUDE_vTaskDelete                  1
-#define INCLUDE_vTaskCleanUpResources        1
 #define INCLUDE_vTaskSuspend                 1
 #define INCLUDE_vTaskDelayUntil              1
 #define INCLUDE_vTaskDelay                   1
@@ -142,15 +142,15 @@ PRIORITY THAN THIS! (higher priorities are lower numeric values. */
 
 /* Interrupt priorities used by the kernel port layer itself.  These are generic
 to all Cortex-M ports, and do not rely on any particular library functions. */
-#define configKERNEL_INTERRUPT_PRIORITY 		( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
+#define configKERNEL_INTERRUPT_PRIORITY         ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
 /* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
 See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
-#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
+#define configMAX_SYSCALL_INTERRUPT_PRIORITY    ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
 
 /* Normal assert() semantics without relying on the provision of an assert.h
 header file. */
 /* USER CODE BEGIN 1 */
-#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for( ;; );}
+#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); asm("bkpt 1"); for( ;; );}
 /* USER CODE END 1 */
 
 /* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
@@ -163,14 +163,4 @@ standard names. */
 
 /* #define xPortSysTickHandler SysTick_Handler */
 
-/* USER CODE BEGIN 2 */
-/* Definitions needed when configGENERATE_RUN_TIME_STATS is on */
-#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS configureTimerForRunTimeStats
-#define portGET_RUN_TIME_COUNTER_VALUE getRunTimeCounterValue
-/* USER CODE END 2 */
-
-/* USER CODE BEGIN Defines */
-/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */
-/* USER CODE END Defines */
-
 #endif /* FREERTOS_CONFIG_H */

+ 6 - 32
firmware/targets/f4/Src/app_freertos.c

@@ -3,49 +3,23 @@
 #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 * 4
+    .stack_size = 1024
 };
 
-void systemd(void *argument);
-
-void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
-
-/* Hook prototypes */
-void configureTimerForRunTimeStats(void);
-unsigned long getRunTimeCounterValue(void);
-void vApplicationIdleHook(void);
-void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName);
-
-__weak void configureTimerForRunTimeStats(void) {
-}
-
-__weak unsigned long getRunTimeCounterValue(void) {
-    return 0;
-}
-
-__weak void vApplicationIdleHook( void ) {
-    /* vApplicationIdleHook() will only be called if configUSE_IDLE_HOOK is set
-    to 1 in FreeRTOSConfig.h. It will be called on each iteration of the idle
-    task. It is essential that code added to this hook function never attempts
-    to block in any way (for example, call xQueueReceive() with a block time
-    specified, or call vTaskDelay()). If the application makes use of the
-    vTaskDelete() API function (as this demo application does) then it is also
-    important that vApplicationIdleHook() is permitted to return to its calling
-    function, because it is the responsibility of the idle task to clean up
-    memory allocated by the kernel to any task that has since been deleted. */
-}
-
-__weak void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName) {
+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);
 }

+ 3 - 7
firmware/targets/f4/Src/stm32wbxx_it.c

@@ -111,7 +111,7 @@ void HardFault_Handler(void)
 void MemManage_Handler(void)
 {
   /* USER CODE BEGIN MemoryManagement_IRQn 0 */
-
+  __asm("bkpt 1");
   /* USER CODE END MemoryManagement_IRQn 0 */
   while (1)
   {
@@ -126,7 +126,7 @@ void MemManage_Handler(void)
 void BusFault_Handler(void)
 {
   /* USER CODE BEGIN BusFault_IRQn 0 */
-
+  __asm("bkpt 1");
   /* USER CODE END BusFault_IRQn 0 */
   while (1)
   {
@@ -141,7 +141,7 @@ void BusFault_Handler(void)
 void UsageFault_Handler(void)
 {
   /* USER CODE BEGIN UsageFault_IRQn 0 */
-
+  __asm("bkpt 1");
   /* USER CODE END UsageFault_IRQn 0 */
   while (1)
   {
@@ -399,7 +399,6 @@ extern void HW_TS_RTC_Wakeup_Handler();
 extern void HW_IPCC_Tx_Handler();
 extern void HW_IPCC_Rx_Handler();
 
-
 void RTC_WKUP_IRQHandler(void)
 {
   HW_TS_RTC_Wakeup_Handler();
@@ -408,14 +407,11 @@ void RTC_WKUP_IRQHandler(void)
 void IPCC_C1_TX_IRQHandler(void)
 {
   HW_IPCC_Tx_Handler();
-
-  return;
 }
 
 void IPCC_C1_RX_IRQHandler(void)
 {
   HW_IPCC_Rx_Handler();
-  return;
 }
 
 /* USER CODE END 1 */

+ 5 - 0
firmware/targets/f4/api-hal/api-hal-power.c

@@ -1,5 +1,6 @@
 #include <api-hal-power.h>
 #include <api-hal-clock.h>
+#include <api-hal-bt.h>
 
 #include <stm32wbxx_ll_rcc.h>
 #include <stm32wbxx_ll_pwr.h>
@@ -22,6 +23,10 @@ void api_hal_power_init() {
     bq25896_init();
 }
 
+bool api_hal_power_deep_available() {
+    return api_hal_bt_is_alive();
+}
+
 void api_hal_power_deep_sleep() {
   while( LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID));
 

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

@@ -69,7 +69,7 @@ static inline bool api_hal_timebase_timer_cmp_is_ok() {
 }
 
 static inline uint32_t api_hal_timebase_timer_get_cmp() {
-    return LL_LPTIM_GetCompare(API_HAL_TIMEBASE_TIMER);;
+    return LL_LPTIM_GetCompare(API_HAL_TIMEBASE_TIMER);
 }
 
 static inline void api_hal_timebase_timer_set_cmp(uint32_t value) {

+ 7 - 3
firmware/targets/f4/api-hal/api-hal-timebase.c

@@ -96,9 +96,12 @@ static inline uint32_t api_hal_timebase_sleep(TickType_t expected_idle_ticks) {
     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
     api_hal_power_deep_sleep();
 
+    HAL_ResumeTick();
+
     // Spin till we are in timer safe zone
     while(!api_hal_timebase_timer_is_safe()) {}
 
@@ -124,14 +127,15 @@ static inline uint32_t api_hal_timebase_sleep(TickType_t expected_idle_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;
     }
 
-    if (api_hal_timebase.insomnia) 
-        return;
-    
     // Stop IRQ handling, no one should disturb us till we finish 
     __disable_irq();
 

+ 23 - 19
firmware/targets/f4/api-hal/api-hal-vcp.c

@@ -12,7 +12,7 @@ typedef struct {
     volatile bool underrun;
 } ApiHalVcp;
 
-ApiHalVcp api_hal_vcp;
+static ApiHalVcp* api_hal_vcp = NULL;
 
 static const uint8_t ascii_soh = 0x01;
 static const uint8_t ascii_eot = 0x04;
@@ -24,19 +24,20 @@ void _api_hal_vcp_rx_callback(const uint8_t* buffer, size_t size);
 void _api_hal_vcp_tx_complete(size_t size);
 
 void api_hal_vcp_init() {
-    api_hal_vcp.rx_stream = xStreamBufferCreate(API_HAL_VCP_RX_BUFFER_SIZE, 1);
-    api_hal_vcp.tx_semaphore = osSemaphoreNew(1, 1, NULL);
-    api_hal_vcp.alive = false;
-    api_hal_vcp.underrun = false;
+    api_hal_vcp = furi_alloc(sizeof(ApiHalVcp));
+    api_hal_vcp->rx_stream = xStreamBufferCreate(API_HAL_VCP_RX_BUFFER_SIZE, 1);
+    api_hal_vcp->tx_semaphore = osSemaphoreNew(1, 1, NULL);
+    api_hal_vcp->alive = false;
+    api_hal_vcp->underrun = false;
 }
 
 void _api_hal_vcp_init() {
-    osSemaphoreRelease(api_hal_vcp.tx_semaphore);
+    osSemaphoreRelease(api_hal_vcp->tx_semaphore);
 }
 
 void _api_hal_vcp_deinit() {
-    api_hal_vcp.alive = false;
-    osSemaphoreRelease(api_hal_vcp.tx_semaphore);
+    api_hal_vcp->alive = false;
+    osSemaphoreRelease(api_hal_vcp->tx_semaphore);
 }
 
 void _api_hal_vcp_control_line(uint8_t state) {
@@ -45,43 +46,46 @@ void _api_hal_vcp_control_line(uint8_t state) {
     bool rts = state & 0b10;
 
     if (rts) {
-        api_hal_vcp.alive = true;
+        api_hal_vcp->alive = true;
         _api_hal_vcp_rx_callback(&ascii_soh, 1); // SOH
     } else {
-        api_hal_vcp.alive = false;
+        api_hal_vcp->alive = false;
         _api_hal_vcp_rx_callback(&ascii_eot, 1); // EOT
     }
 
-    osSemaphoreRelease(api_hal_vcp.tx_semaphore);
+    osSemaphoreRelease(api_hal_vcp->tx_semaphore);
 }
 
 void _api_hal_vcp_rx_callback(const uint8_t* buffer, size_t size) {
     BaseType_t xHigherPriorityTaskWoken = pdFALSE;
-    size_t ret = xStreamBufferSendFromISR(api_hal_vcp.rx_stream, buffer, size, &xHigherPriorityTaskWoken);
+    size_t ret = xStreamBufferSendFromISR(api_hal_vcp->rx_stream, buffer, size, &xHigherPriorityTaskWoken);
     if (ret != size) {
-        api_hal_vcp.underrun = true;
+        api_hal_vcp->underrun = true;
     }
     portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
 }
 
 void _api_hal_vcp_tx_complete(size_t size) {
-    osSemaphoreRelease(api_hal_vcp.tx_semaphore);
+    osSemaphoreRelease(api_hal_vcp->tx_semaphore);
 }
 
 size_t api_hal_vcp_rx(uint8_t* buffer, size_t size) {
-    return xStreamBufferReceive(api_hal_vcp.rx_stream, buffer, size, portMAX_DELAY);
+    furi_assert(api_hal_vcp);
+    return xStreamBufferReceive(api_hal_vcp->rx_stream, buffer, size, portMAX_DELAY);
 }
 
-void api_hal_vcp_tx(uint8_t* buffer, size_t size) {
-    while (size > 0 && api_hal_vcp.alive) {
-        furi_check(osSemaphoreAcquire(api_hal_vcp.tx_semaphore, osWaitForever) == osOK);
+void api_hal_vcp_tx(const uint8_t* buffer, size_t size) {
+    furi_assert(api_hal_vcp);
+
+    while (size > 0 && api_hal_vcp->alive) {
+        furi_check(osSemaphoreAcquire(api_hal_vcp->tx_semaphore, osWaitForever) == osOK);
 
         size_t batch_size = size;
         if (batch_size > APP_TX_DATA_SIZE) {
             batch_size = APP_TX_DATA_SIZE;
         }
 
-        if (CDC_Transmit_FS(buffer, batch_size) == USBD_OK) {
+        if (CDC_Transmit_FS((uint8_t*)buffer, batch_size) == USBD_OK) {
             size -= batch_size;
             buffer += batch_size;
         } else {

+ 50 - 47
firmware/targets/f4/ble-glue/app_ble.c

@@ -1,7 +1,6 @@
 #include "main.h"
 
 #include "app_common.h"
-
 #include "dbg_trace.h"
 #include "ble.h"
 #include "tl.h"
@@ -14,6 +13,8 @@
 #include "dis_app.h"
 #include "hrs_app.h"
 
+#include <api-hal.h>
+
 typedef struct _tSecurityParams {
   uint8_t ioCapability;
   uint8_t mitm_mode;
@@ -214,10 +215,11 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt )
       if (disconnection_complete_event->Connection_Handle == BleApplicationContext.BleApplicationContext_legacy.connectionHandle) {
         BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0;
         BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE;
-        APP_DBG_MSG("\r\n\r** DISCONNECTION EVENT WITH CLIENT \n");
+        APP_DBG_MSG("\r\n\r** DISCONNECTION EVENT WITH CLIENT \r\n");
       }
       /* restart advertising */
       Adv_Request(APP_BLE_FAST_ADV);
+      api_hal_timebase_insomnia_exit();
     }
     break; /* EVT_DISCONN_COMPLETE */
 
@@ -227,45 +229,46 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt )
       switch (meta_evt->subevent)
       {
         case EVT_LE_CONN_UPDATE_COMPLETE:
-          APP_DBG_MSG("\r\n\r** CONNECTION UPDATE EVENT WITH CLIENT \n");
+          APP_DBG_MSG("\r\n\r** CONNECTION UPDATE EVENT WITH CLIENT \r\n");
 
           /* USER CODE BEGIN EVT_LE_CONN_UPDATE_COMPLETE */
 
           /* USER CODE END EVT_LE_CONN_UPDATE_COMPLETE */
           break;
         case EVT_LE_PHY_UPDATE_COMPLETE:
-          APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE \n");
+          APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE \r\n");
           evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data;
           if (evt_le_phy_update_complete->Status == 0)
           {
-            APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status ok \n");
+            APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status ok \r\n");
           }
           else
           {
-            APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status nok \n");
+            APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status nok \r\n");
           }
 
           ret = hci_le_read_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,&TX_PHY,&RX_PHY);
           if (ret == BLE_STATUS_SUCCESS)
           {
-            APP_DBG_MSG("Read_PHY success \n");
+            APP_DBG_MSG("Read_PHY success \r\n");
 
             if ((TX_PHY == TX_2M) && (RX_PHY == RX_2M))
             {
-              APP_DBG_MSG("PHY Param  TX= %d, RX= %d \n", TX_PHY, RX_PHY);
+              APP_DBG_MSG("PHY Param  TX= %d, RX= %d \r\n", TX_PHY, RX_PHY);
             }
             else
             {
-              APP_DBG_MSG("PHY Param  TX= %d, RX= %d \n", TX_PHY, RX_PHY);
+              APP_DBG_MSG("PHY Param  TX= %d, RX= %d \r\n", TX_PHY, RX_PHY);
             }
           }
           else
           {
-            APP_DBG_MSG("Read conf not succeess \n");
+            APP_DBG_MSG("Read conf not succeess \r\n");
           }
           break;
         case EVT_LE_CONN_COMPLETE:
         {
+          api_hal_timebase_insomnia_enter();
           hci_le_connection_complete_event_rp0 *connection_complete_event;
 
           /**
@@ -275,7 +278,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt )
 
           HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id);
 
-          APP_DBG_MSG("EVT_LE_CONN_COMPLETE for connection handle 0x%x\n", connection_complete_event->Connection_Handle);
+          APP_DBG_MSG("EVT_LE_CONN_COMPLETE for connection handle 0x%x\r\n", connection_complete_event->Connection_Handle);
           if (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_CONNECTING)
           {
             /* Connection as client */
@@ -301,71 +304,71 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt )
         aci_gap_pairing_complete_event_rp0 *pairing_complete;
 
       case EVT_BLUE_GAP_LIMITED_DISCOVERABLE: 
-        APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_LIMITED_DISCOVERABLE \n");
+        APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_LIMITED_DISCOVERABLE \r\n");
           break; /* EVT_BLUE_GAP_LIMITED_DISCOVERABLE */
           
       case EVT_BLUE_GAP_PASS_KEY_REQUEST:  
-        APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PASS_KEY_REQUEST \n");
+        APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PASS_KEY_REQUEST \r\n");
 
         aci_gap_pass_key_resp(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,123456);
 
-        APP_DBG_MSG("\r\n\r** aci_gap_pass_key_resp \n");
+        APP_DBG_MSG("\r\n\r** aci_gap_pass_key_resp \r\n");
           break; /* EVT_BLUE_GAP_PASS_KEY_REQUEST */
 
       case EVT_BLUE_GAP_AUTHORIZATION_REQUEST:    
-        APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_AUTHORIZATION_REQUEST \n");
+        APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_AUTHORIZATION_REQUEST \r\n");
           break; /* EVT_BLUE_GAP_AUTHORIZATION_REQUEST */
 
       case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED:   
-        APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED \n");
+        APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED \r\n");
           break; /* EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED */
 
       case EVT_BLUE_GAP_BOND_LOST:    
-        APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_BOND_LOST \n");
+        APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_BOND_LOST \r\n");
           aci_gap_allow_rebond(BleApplicationContext.BleApplicationContext_legacy.connectionHandle);
-        APP_DBG_MSG("\r\n\r** Send allow rebond \n");
+        APP_DBG_MSG("\r\n\r** Send allow rebond \r\n");
           break; /* EVT_BLUE_GAP_BOND_LOST */
 
       case EVT_BLUE_GAP_DEVICE_FOUND:  
-        APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \n");
+        APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \r\n");
           break; /* EVT_BLUE_GAP_DEVICE_FOUND */
 
       case EVT_BLUE_GAP_ADDR_NOT_RESOLVED:
-         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \n");
+         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \r\n");
           break; /* EVT_BLUE_GAP_DEVICE_FOUND */
       
       case (EVT_BLUE_GAP_KEYPRESS_NOTIFICATION):
-         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_KEYPRESS_NOTIFICATION \n");
+         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_KEYPRESS_NOTIFICATION \r\n");
           break; /* EVT_BLUE_GAP_KEY_PRESS_NOTIFICATION */    
 
        case (EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE):
-          APP_DBG_MSG("numeric_value = %ld\n",
+          APP_DBG_MSG("numeric_value = %ld\r\n",
                       ((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value);
 
-          APP_DBG_MSG("Hex_value = %lx\n",
+          APP_DBG_MSG("Hex_value = %lx\r\n",
                       ((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value);
 
           aci_gap_numeric_comparison_value_confirm_yesno(BleApplicationContext.BleApplicationContext_legacy.connectionHandle, 1); /* CONFIRM_YES = 1 */
 
-          APP_DBG_MSG("\r\n\r** aci_gap_numeric_comparison_value_confirm_yesno-->YES \n");
+          APP_DBG_MSG("\r\n\r** aci_gap_numeric_comparison_value_confirm_yesno-->YES \r\n");
           break;
 
         case (EVT_BLUE_GAP_PAIRING_CMPLT):
           {
             pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data;
 
-            APP_DBG_MSG("BLE_CTRL_App_Notification: EVT_BLUE_GAP_PAIRING_CMPLT, pairing_complete->Status = %d\n",pairing_complete->Status);
+            APP_DBG_MSG("BLE_CTRL_App_Notification: EVT_BLUE_GAP_PAIRING_CMPLT, pairing_complete->Status = %d\r\n",pairing_complete->Status);
             if (pairing_complete->Status == 0) {
-              APP_DBG_MSG("\r\n\r** Pairing OK \n");
+              APP_DBG_MSG("\r\n\r** Pairing OK \r\n");
             } else {
-              APP_DBG_MSG("\r\n\r** Pairing KO \n");
+              APP_DBG_MSG("\r\n\r** Pairing KO \r\n");
             }
           }
           break;
 
       /* USER CODE END ecode */
         case EVT_BLUE_GAP_PROCEDURE_COMPLETE:
-          APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PROCEDURE_COMPLETE \n");
+          APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PROCEDURE_COMPLETE \r\n");
           break;
       }
       break; /* EVT_VENDOR */
@@ -385,9 +388,9 @@ void APP_BLE_Key_Button1_Action() {
   tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
   ret = aci_gap_clear_security_db();
   if (ret == BLE_STATUS_SUCCESS) {
-    APP_DBG_MSG("Successfully aci_gap_clear_security_db()\n");
+    APP_DBG_MSG("Successfully aci_gap_clear_security_db()\r\n");
   } else {
-    APP_DBG_MSG("aci_gap_clear_security_db() Failed , result: %d \n", ret);
+    APP_DBG_MSG("aci_gap_clear_security_db() Failed , result: %d \r\n", ret);
   }
 }
 
@@ -397,7 +400,7 @@ void APP_BLE_Key_Button2_Action() {
   if (ret == BLE_STATUS_SUCCESS) {
     APP_DBG_MSG("Successfully aci_gap_slave_security_req()");
   } else {
-    APP_DBG_MSG("aci_gap_slave_security_req() Failed , result: %d \n", ret);
+    APP_DBG_MSG("aci_gap_slave_security_req() Failed , result: %d \r\n", ret);
   }
 }
   
@@ -406,23 +409,23 @@ void APP_BLE_Key_Button3_Action() {
   tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
   ret = hci_le_read_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,&TX_PHY,&RX_PHY);
   if (ret == BLE_STATUS_SUCCESS) {
-    APP_DBG_MSG("Read_PHY success \n");
-    APP_DBG_MSG("PHY Param  TX= %d, RX= %d \n", TX_PHY, RX_PHY);
+    APP_DBG_MSG("Read_PHY success \r\n");
+    APP_DBG_MSG("PHY Param  TX= %d, RX= %d \r\n", TX_PHY, RX_PHY);
     if ((TX_PHY == TX_2M) && (RX_PHY == RX_2M)) {
-      APP_DBG_MSG("hci_le_set_phy PHY Param  TX= %d, RX= %d \n", TX_1M, RX_1M);
+      APP_DBG_MSG("hci_le_set_phy PHY Param  TX= %d, RX= %d \r\n", TX_1M, RX_1M);
       ret = hci_le_set_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,ALL_PHYS_PREFERENCE,TX_1M,RX_1M,0);
     } else {
-      APP_DBG_MSG("hci_le_set_phy PHY Param  TX= %d, RX= %d \n", TX_2M_PREFERRED, RX_2M_PREFERRED);
+      APP_DBG_MSG("hci_le_set_phy PHY Param  TX= %d, RX= %d \r\n", TX_2M_PREFERRED, RX_2M_PREFERRED);
       ret = hci_le_set_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,ALL_PHYS_PREFERENCE,TX_2M_PREFERRED,RX_2M_PREFERRED,0);
     } 
   } else {
-    APP_DBG_MSG("Read conf not succeess \n");
+    APP_DBG_MSG("Read conf not succeess \r\n");
   }
 
   if (ret == BLE_STATUS_SUCCESS) {
-    APP_DBG_MSG("set PHY cmd ok\n");
+    APP_DBG_MSG("set PHY cmd ok\r\n");
   } else {
-    APP_DBG_MSG("set PHY cmd NOK\n");
+    APP_DBG_MSG("set PHY cmd NOK\r\n");
   }
 }
 
@@ -538,7 +541,7 @@ static void Ble_Hci_Gap_Gatt_Init() {
 
     if (aci_gatt_update_char_value(gap_service_handle, gap_dev_name_char_handle, 0, strlen(name), (uint8_t *) name))
     {
-      BLE_DBG_SVCCTL_MSG("Device Name aci_gatt_update_char_value failed.\n");
+      BLE_DBG_SVCCTL_MSG("Device Name aci_gatt_update_char_value failed.\r\n");
     }
   }
 
@@ -548,7 +551,7 @@ static void Ble_Hci_Gap_Gatt_Init() {
                                 2,
                                 (uint8_t *)&appearance))
   {
-    BLE_DBG_SVCCTL_MSG("Appearance aci_gatt_update_char_value failed.\n");
+    BLE_DBG_SVCCTL_MSG("Appearance aci_gatt_update_char_value failed.\r\n");
   }
   /**
    * Initialize Default PHY
@@ -613,7 +616,7 @@ static void Adv_Request(APP_BLE_ConnStatus_t New_Status)
      */
     HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id);
 
-    APP_DBG_MSG("First index in %d state \n", BleApplicationContext.Device_Connection_Status);
+    APP_DBG_MSG("First index in %d state \r\n", BleApplicationContext.Device_Connection_Status);
 
     if ((New_Status == APP_BLE_LP_ADV)
         && ((BleApplicationContext.Device_Connection_Status == APP_BLE_FAST_ADV)
@@ -623,11 +626,11 @@ static void Adv_Request(APP_BLE_ConnStatus_t New_Status)
       ret = aci_gap_set_non_discoverable();
       if (ret == BLE_STATUS_SUCCESS)
       {
-        APP_DBG_MSG("Successfully Stopped Advertising \n");
+        APP_DBG_MSG("Successfully Stopped Advertising \r\n");
       }
       else
       {
-        APP_DBG_MSG("Stop Advertising Failed , result: %d \n", ret);
+        APP_DBG_MSG("Stop Advertising Failed , result: %d \r\n", ret);
       }
     }
 
@@ -650,17 +653,17 @@ static void Adv_Request(APP_BLE_ConnStatus_t New_Status)
     ret = aci_gap_update_adv_data(sizeof(manuf_data), (uint8_t*) manuf_data);
     if (ret == BLE_STATUS_SUCCESS) {
       if (New_Status == APP_BLE_FAST_ADV) {
-        APP_DBG_MSG("Successfully Start Fast Advertising \n" );
+        APP_DBG_MSG("Successfully Start Fast Advertising \r\n" );
         /* Start Timer to STOP ADV - TIMEOUT */
         HW_TS_Start(BleApplicationContext.Advertising_mgr_timer_Id, INITIAL_ADV_TIMEOUT);
       } else {
-        APP_DBG_MSG("Successfully Start Low Power Advertising \n");
+        APP_DBG_MSG("Successfully Start Low Power Advertising \r\n");
       }
     } else {
       if (New_Status == APP_BLE_FAST_ADV) {
-        APP_DBG_MSG("Start Fast Advertising Failed , result: %d \n", ret);
+        APP_DBG_MSG("Start Fast Advertising Failed , result: %d \r\n", ret);
       } else {
-        APP_DBG_MSG("Start Low Power Advertising Failed , result: %d \n", ret);
+        APP_DBG_MSG("Start Low Power Advertising Failed , result: %d \r\n", ret);
       }
     }
 }

+ 4 - 4
firmware/targets/f4/ble-glue/app_conf.h

@@ -5,7 +5,7 @@
 #include "hw_if.h"
 #include "ble_bufsize.h"
 
-#define CFG_TX_POWER                      (0x18) /* -0.15dBm */
+#define CFG_TX_POWER                      (0x1F) /* +6dBm */
 
 /**
  * Define Advertising parameters
@@ -21,7 +21,7 @@
  */
 #define CFG_BONDING_MODE                 (1)
 #define CFG_FIXED_PIN                    (111111)
-#define CFG_USED_FIXED_PIN               (0)
+#define CFG_USED_FIXED_PIN               (1)
 #define CFG_ENCRYPTION_KEY_SIZE_MAX      (16)
 #define CFG_ENCRYPTION_KEY_SIZE_MIN      (8)
 
@@ -367,12 +367,12 @@ typedef enum
 /**
  * When set to 1, the traces are enabled in the BLE services
  */
-#define CFG_DEBUG_BLE_TRACE     0
+#define CFG_DEBUG_BLE_TRACE     1
 
 /**
  * Enable or Disable traces in application
  */
-#define CFG_DEBUG_APP_TRACE     0
+#define CFG_DEBUG_APP_TRACE     1
 
 #if (CFG_DEBUG_APP_TRACE != 0)
 #define APP_DBG_MSG                 PRINT_MESG_DBG

+ 1 - 1
firmware/targets/f4/ble-glue/app_debug.c

@@ -393,7 +393,7 @@ void DbgOutputTraces(  uint8_t *p_data, uint16_t size, void (*cb)(void) )
 /* USER CODE END DbgOutputTraces */
   // HW_UART_Transmit_DMA(CFG_DEBUG_TRACE_UART, p_data, size, cb);
   HAL_UART_Transmit(&DEBUG_UART, (uint8_t*)p_data, (uint16_t)size, HAL_MAX_DELAY);
-
+  cb();
 /* USER CODE END DbgOutputTraces */
   return;
 }

+ 4 - 4
firmware/targets/f4/ble-glue/tl_dbg_conf.h

@@ -40,14 +40,14 @@ extern UART_HandleTypeDef DEBUG_UART;
  * Enable or Disable traces
  * The raw data output is the hci binary packet format as specified by the BT specification *
  */
-#define TL_SHCI_CMD_DBG_EN      1   /* Reports System commands sent to CPU2 and the command response */
+#define TL_SHCI_CMD_DBG_EN      0   /* Reports System commands sent to CPU2 and the command response */
 #define TL_SHCI_CMD_DBG_RAW_EN  0   /* Reports raw data System commands sent to CPU2 and the command response */
-#define TL_SHCI_EVT_DBG_EN      1   /* Reports System Asynchronous Events received from CPU2 */
+#define TL_SHCI_EVT_DBG_EN      0   /* Reports System Asynchronous Events received from CPU2 */
 #define TL_SHCI_EVT_DBG_RAW_EN  0   /* Reports raw data System Asynchronous Events received from CPU2 */
 
-#define TL_HCI_CMD_DBG_EN       1   /* Reports BLE command sent to CPU2 and the command response */
+#define TL_HCI_CMD_DBG_EN       0   /* Reports BLE command sent to CPU2 and the command response */
 #define TL_HCI_CMD_DBG_RAW_EN   0   /* Reports raw data BLE command sent to CPU2 and the command response */
-#define TL_HCI_EVT_DBG_EN       1   /* Reports BLE Asynchronous Events received from CPU2 */
+#define TL_HCI_EVT_DBG_EN       0   /* Reports BLE Asynchronous Events received from CPU2 */
 #define TL_HCI_EVT_DBG_RAW_EN   0   /* Reports raw data BLE Asynchronous Events received from CPU2 */
 
 #define TL_MM_DBG_EN            1   /* Reports the informations of the buffer released to CPU2 */

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

@@ -1,7 +1,5 @@
 TOOLCHAIN = arm
 
-OPENOCD_OPTS	= -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "stm32wbx.cpu configure -rtos auto"
-
 BOOT_ADDRESS	= 0x08000000
 FW_ADDRESS		= 0x08008000
 OS_OFFSET		= 0x00008000
@@ -16,13 +14,14 @@ FLASH_ADDRESS	= 0x08000000
 CFLAGS			+= -DNO_BOOTLOADER
 endif
 
+OPENOCD_OPTS	= -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "stm32wbx.cpu configure -rtos auto" -c "init"
 BOOT_CFLAGS		= -DBOOT_ADDRESS=$(BOOT_ADDRESS) -DFW_ADDRESS=$(FW_ADDRESS) -DOS_OFFSET=$(OS_OFFSET)
 MCU_FLAGS		= -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard
 
 CFLAGS			+= $(MCU_FLAGS) $(BOOT_CFLAGS) -DSTM32WB55xx -Wall -fdata-sections -ffunction-sections
 LDFLAGS			+= $(MCU_FLAGS) -specs=nosys.specs -specs=nano.specs 
 
-CPPFLAGS		+= -fno-rtti -fno-use-cxa-atexit -fno-exceptions
+CPPFLAGS		+= -fno-rtti -fno-use-cxa-atexit -fno-exceptions 
 LDFLAGS			+= -Wl,--start-group -lstdc++ -lsupc++ -Wl,--end-group
 
 MXPROJECT_DIR = $(TARGET_DIR)

+ 8 - 1
make/rules.mk

@@ -11,6 +11,10 @@ OBJECTS += $(addprefix $(OBJ_DIR)/, $(notdir $(CPP_SOURCES:.cpp=.o)))
 # Generate dependencies
 DEPS = $(OBJECTS:.o=.d)
 
+ifdef DFU_SERIAL
+	DFU_OPTIONS += -S $(DFU_SERIAL)
+endif
+
 $(shell test -d $(OBJ_DIR) || mkdir -p $(OBJ_DIR))
 
 BUILD_FLAGS_SHELL=\
@@ -59,7 +63,7 @@ $(OBJ_DIR)/flash: $(OBJ_DIR)/$(PROJECT).bin
 	touch $@
 
 $(OBJ_DIR)/upload: $(OBJ_DIR)/$(PROJECT).bin
-	dfu-util -D $(OBJ_DIR)/$(PROJECT).bin -a 0 -s $(FLASH_ADDRESS) -S $(DFU_SERIAL)
+	dfu-util -D $(OBJ_DIR)/$(PROJECT).bin -a 0 -s $(FLASH_ADDRESS) $(DFU_OPTIONS)
 	touch $@
 
 $(ASSETS): $(ASSETS_SOURCES) $(ASSETS_COMPILLER)
@@ -80,6 +84,9 @@ debug: flash
 		-ex "compare-sections" \
 		$(OBJ_DIR)/$(PROJECT).elf; \
 
+openocd:
+	openocd $(OPENOCD_OPTS)
+
 bm_debug: flash
 	set -m; blackmagic & echo $$! > $(OBJ_DIR)/agent.PID
 	arm-none-eabi-gdb \