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

[FL-1952] BLE bonding fix (#805)

* furi-hal-bt: add mutex guarding core2 state
* ble-glue: configure ble keys storage in SRAM2
* bt: add load and save ble keys in internal storage
* bt: improve work furi_hal_bt API
* bt: rework app_entry -> ble_glue
* bt: apply changes for f6 target
* desktop: remove furi check
* ble-glue: comment NVM in SRAM2 configuration
* FuriHal: fix flash controller state corruption, fix incorrect semaphore release, implement C1-C2 flash controller access according to spec. Gui: change logging level.
* Libs: better lfs integration with lfs_config.
* Ble: switch C2 NVM to RAM.
* FuriHalCrypto: ensure that core2 is alive before sending shci commands
* Ble: fix incorrect nvm buffer size

Co-authored-by: あく <alleteam@gmail.com>
gornekich 4 лет назад
Родитель
Сommit
3225f40870
39 измененных файлов с 1178 добавлено и 562 удалено
  1. 19 1
      applications/bt/bt_service/bt.c
  2. 3 0
      applications/bt/bt_service/bt_i.h
  3. 41 0
      applications/bt/bt_service/bt_keys_storage.c
  4. 7 0
      applications/bt/bt_service/bt_keys_storage.h
  5. 1 2
      applications/desktop/desktop.c
  6. 28 13
      applications/dolphin/helpers/dolphin_state.c
  7. 3 3
      applications/gui/gui.c
  8. 2 2
      applications/gui/view_dispatcher.c
  9. 1 1
      applications/storage/storages/storage-int.c
  10. 0 11
      firmware/targets/f6/ble-glue/app_conf.h
  11. 0 180
      firmware/targets/f6/ble-glue/app_entry.c
  12. 0 20
      firmware/targets/f6/ble-glue/app_entry.h
  13. 20 2
      firmware/targets/f6/ble-glue/ble_app.c
  14. 2 0
      firmware/targets/f6/ble-glue/ble_app.h
  15. 173 0
      firmware/targets/f6/ble-glue/ble_glue.c
  16. 26 0
      firmware/targets/f6/ble-glue/ble_glue.h
  17. 1 2
      firmware/targets/f6/ble-glue/gap.c
  18. 31 0
      firmware/targets/f6/ble-glue/hw_conf.h
  19. 123 32
      firmware/targets/f6/furi-hal/furi-hal-bt.c
  20. 13 0
      firmware/targets/f6/furi-hal/furi-hal-crypto.c
  21. 32 16
      firmware/targets/f6/furi-hal/furi-hal-flash.c
  22. 2 2
      firmware/targets/f6/furi-hal/furi-hal-flash.h
  23. 0 11
      firmware/targets/f7/ble-glue/app_conf.h
  24. 0 180
      firmware/targets/f7/ble-glue/app_entry.c
  25. 0 20
      firmware/targets/f7/ble-glue/app_entry.h
  26. 20 2
      firmware/targets/f7/ble-glue/ble_app.c
  27. 2 0
      firmware/targets/f7/ble-glue/ble_app.h
  28. 173 0
      firmware/targets/f7/ble-glue/ble_glue.c
  29. 26 0
      firmware/targets/f7/ble-glue/ble_glue.h
  30. 1 2
      firmware/targets/f7/ble-glue/gap.c
  31. 31 0
      firmware/targets/f7/ble-glue/hw_conf.h
  32. 123 32
      firmware/targets/f7/furi-hal/furi-hal-bt.c
  33. 13 0
      firmware/targets/f7/furi-hal/furi-hal-crypto.c
  34. 32 16
      firmware/targets/f7/furi-hal/furi-hal-flash.c
  35. 2 2
      firmware/targets/f7/furi-hal/furi-hal-flash.h
  36. 36 6
      firmware/targets/furi-hal-include/furi-hal-bt.h
  37. 187 0
      lib/lfs_config.h
  38. 1 1
      lib/lib.mk
  39. 3 3
      make/toolchain.mk

+ 19 - 1
applications/bt/bt_service/bt.c

@@ -1,5 +1,6 @@
 #include "bt_i.h"
 #include "battery_service.h"
+#include "bt_keys_storage.h"
 
 #define BT_SERVICE_TAG "BT"
 
@@ -161,6 +162,14 @@ static void bt_on_gap_event_callback(BleEvent event, void* context) {
     }
 }
 
+static void bt_on_key_storage_change_callback(uint8_t* addr, uint16_t size, void* context) {
+    furi_assert(context);
+    Bt* bt = context;
+    FURI_LOG_I(BT_SERVICE_TAG, "Changed addr start: %08lX, size changed: %d", addr, size);
+    BtMessage message = {.type = BtMessageTypeKeysStorageUpdated};
+    furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK);
+}
+
 static void bt_statusbar_update(Bt* bt) {
     if(bt->status == BtStatusAdvertising) {
         view_port_set_width(bt->statusbar_view_port, icon_get_width(&I_Bluetooth_5x8));
@@ -177,7 +186,12 @@ int32_t bt_srv() {
     Bt* bt = bt_alloc();
     furi_record_create("bt", bt);
 
-    if(!furi_hal_bt_wait_startup()) {
+    // Read keys
+    if(!bt_load_key_storage(bt)) {
+        FURI_LOG_W(BT_SERVICE_TAG, "Failed to load saved bonding keys");
+    }
+    // Start 2nd core
+    if(!furi_hal_bt_start_core2()) {
         FURI_LOG_E(BT_SERVICE_TAG, "Core2 startup failed");
     } else {
         view_port_enabled_set(bt->statusbar_view_port, true);
@@ -190,6 +204,8 @@ int32_t bt_srv() {
             FURI_LOG_E(BT_SERVICE_TAG, "BT App start failed");
         }
     }
+    furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt);
+
     // Update statusbar
     bt_statusbar_update(bt);
 
@@ -207,6 +223,8 @@ int32_t bt_srv() {
         } else if(message.type == BtMessageTypePinCodeShow) {
             // Display PIN code
             bt_pin_code_show_event_handler(bt, message.data.pin_code);
+        } else if(message.type == BtMessageTypeKeysStorageUpdated) {
+            bt_save_key_storage(bt);
         }
     }
     return 0;

+ 3 - 0
applications/bt/bt_service/bt_i.h

@@ -25,6 +25,7 @@ typedef enum {
     BtMessageTypeUpdateStatusbar,
     BtMessageTypeUpdateBatteryLevel,
     BtMessageTypePinCodeShow,
+    BtMessageTypeKeysStorageUpdated,
 } BtMessageType;
 
 typedef union {
@@ -38,6 +39,8 @@ typedef struct {
 } BtMessage;
 
 struct Bt {
+    uint8_t* bt_keys_addr_start;
+    uint16_t bt_keys_size;
     BtSettings bt_settings;
     BtStatus status;
     osMessageQueueId_t message_queue;

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

@@ -0,0 +1,41 @@
+#include "bt_keys_storage.h"
+#include <furi.h>
+#include <file-worker.h>
+
+#define BT_KEYS_STORAGE_TAG "bt keys storage"
+#define BT_KEYS_STORAGE_PATH "/int/bt.keys"
+
+bool bt_load_key_storage(Bt* bt) {
+    furi_assert(bt);
+
+    bool file_loaded = false;
+    furi_hal_bt_get_key_storage_buff(&bt->bt_keys_addr_start, &bt->bt_keys_size);
+
+    FileWorker* file_worker = file_worker_alloc(true);
+    if(file_worker_open(file_worker, BT_KEYS_STORAGE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) {
+        furi_hal_bt_nvm_sram_sem_acquire();
+        if(file_worker_read(file_worker, bt->bt_keys_addr_start, bt->bt_keys_size)) {
+            file_loaded = true;
+        }
+        furi_hal_bt_nvm_sram_sem_release();
+    }
+    file_worker_free(file_worker);
+    return file_loaded;
+}
+
+bool bt_save_key_storage(Bt* bt) {
+    furi_assert(bt);
+    furi_assert(bt->bt_keys_addr_start);
+
+    bool file_saved = false;
+    FileWorker* file_worker = file_worker_alloc(true);
+    if(file_worker_open(file_worker, BT_KEYS_STORAGE_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) {
+        furi_hal_bt_nvm_sram_sem_acquire();
+        if(file_worker_write(file_worker, bt->bt_keys_addr_start, bt->bt_keys_size)) {
+            file_saved = true;
+        }
+        furi_hal_bt_nvm_sram_sem_release();
+    }
+    file_worker_free(file_worker);
+    return file_saved;
+}

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

@@ -0,0 +1,7 @@
+#pragma once
+
+#include "bt_i.h"
+
+bool bt_load_key_storage(Bt* bt);
+
+bool bt_save_key_storage(Bt* bt);

+ 1 - 2
applications/desktop/desktop.c

@@ -122,8 +122,7 @@ int32_t desktop_srv(void* p) {
     if(!loaded) {
         furi_hal_lock_set(false);
         memset(&desktop->settings, 0, sizeof(desktop->settings));
-        bool saved = SAVE_DESKTOP_SETTINGS(&desktop->settings);
-        furi_check(saved);
+        SAVE_DESKTOP_SETTINGS(&desktop->settings);
     }
 
     scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);

+ 28 - 13
applications/dolphin/helpers/dolphin_state.c

@@ -4,9 +4,10 @@
 #include <math.h>
 #include <toolbox/saved_struct.h>
 
-#define DOLPHIN_STORE_PATH "/int/dolphin.state"
-#define DOLPHIN_STORE_HEADER_MAGIC 0xD0
-#define DOLPHIN_STORE_HEADER_VERSION 0x01
+#define DOLPHIN_STATE_TAG "DolphinState"
+#define DOLPHIN_STATE_PATH "/int/dolphin.state"
+#define DOLPHIN_STATE_HEADER_MAGIC 0xD0
+#define DOLPHIN_STATE_HEADER_VERSION 0x01
 #define DOLPHIN_LVL_THRESHOLD 20.0f
 
 typedef struct {
@@ -35,28 +36,42 @@ void dolphin_state_free(DolphinState* dolphin_state) {
 }
 
 bool dolphin_state_save(DolphinState* dolphin_state) {
-    return saved_struct_save(
-        DOLPHIN_STORE_PATH,
+    if(!dolphin_state->dirty) {
+        return true;
+    }
+
+    bool result = saved_struct_save(
+        DOLPHIN_STATE_PATH,
         &dolphin_state->data,
         sizeof(DolphinStoreData),
-        DOLPHIN_STORE_HEADER_MAGIC,
-        DOLPHIN_STORE_HEADER_VERSION);
+        DOLPHIN_STATE_HEADER_MAGIC,
+        DOLPHIN_STATE_HEADER_VERSION);
+
+    if(result) {
+        FURI_LOG_I(DOLPHIN_STATE_TAG, "State saved");
+        dolphin_state->dirty = false;
+    } else {
+        FURI_LOG_E(DOLPHIN_STATE_TAG, "Failed to save state");
+    }
+
+    return result;
 }
 
 bool dolphin_state_load(DolphinState* dolphin_state) {
     bool loaded = saved_struct_load(
-        DOLPHIN_STORE_PATH,
+        DOLPHIN_STATE_PATH,
         &dolphin_state->data,
         sizeof(DolphinStoreData),
-        DOLPHIN_STORE_HEADER_MAGIC,
-        DOLPHIN_STORE_HEADER_VERSION);
+        DOLPHIN_STATE_HEADER_MAGIC,
+        DOLPHIN_STATE_HEADER_VERSION);
+
     if(!loaded) {
-        FURI_LOG_W("dolphin-state", "Reset dolphin-state");
+        FURI_LOG_W(DOLPHIN_STATE_TAG, "Reset dolphin-state");
         memset(dolphin_state, 0, sizeof(*dolphin_state));
-        dolphin_state_save(dolphin_state);
+        dolphin_state->dirty = true;
     }
 
-    return true;
+    return loaded;
 }
 
 uint64_t dolphin_state_timestamp() {

+ 3 - 3
applications/gui/gui.c

@@ -189,7 +189,7 @@ void gui_input(Gui* gui, InputEvent* input_event) {
     } else if(input_event->type == InputTypePress) {
         gui->ongoing_input |= key_bit;
     } else if(!(gui->ongoing_input & key_bit)) {
-        FURI_LOG_W(
+        FURI_LOG_D(
             "Gui",
             "non-complementary input, discarding key: %s type: %s, sequence: %p",
             input_get_key_name(input_event->key),
@@ -211,7 +211,7 @@ void gui_input(Gui* gui, InputEvent* input_event) {
     if(view_port && view_port == gui->ongoing_input_view_port) {
         view_port_input(view_port, input_event);
     } else if(gui->ongoing_input_view_port && input_event->type == InputTypeRelease) {
-        FURI_LOG_W(
+        FURI_LOG_D(
             "Gui",
             "ViewPort changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port",
             gui->ongoing_input_view_port,
@@ -221,7 +221,7 @@ void gui_input(Gui* gui, InputEvent* input_event) {
             input_event->sequence);
         view_port_input(gui->ongoing_input_view_port, input_event);
     } else {
-        FURI_LOG_W(
+        FURI_LOG_D(
             "Gui",
             "ViewPort changed while key press %p -> %p. Discarding key: %s, type: %s, sequence: %p",
             gui->ongoing_input_view_port,

+ 2 - 2
applications/gui/view_dispatcher.c

@@ -236,7 +236,7 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e
     } else if(event->type == InputTypeRelease) {
         view_dispatcher->ongoing_input &= ~key_bit;
     } else if(!(view_dispatcher->ongoing_input & key_bit)) {
-        FURI_LOG_W(
+        FURI_LOG_D(
             "ViewDispatcher",
             "non-complementary input, discarding key: %s, type: %s, sequence: %p",
             input_get_key_name(event->key),
@@ -275,7 +275,7 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e
             }
         }
     } else if(view_dispatcher->ongoing_input_view && event->type == InputTypeRelease) {
-        FURI_LOG_W(
+        FURI_LOG_D(
             "ViewDispatcher",
             "View changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port",
             view_dispatcher->ongoing_input_view,

+ 1 - 1
applications/storage/storages/storage-int.c

@@ -119,7 +119,7 @@ static int storage_int_device_erase(const struct lfs_config* c, lfs_block_t bloc
     LFSData* lfs_data = c->context;
     size_t page = lfs_data->start_page + block;
 
-    FURI_LOG_D(TAG, "Device erase: page %d, translated page: %d", block, page);
+    FURI_LOG_D(TAG, "Device erase: page %d, translated page: %x", block, page);
 
     if(furi_hal_flash_erase(page, 1)) {
         return 0;

+ 0 - 11
firmware/targets/f6/ble-glue/app_conf.h

@@ -427,16 +427,5 @@ typedef enum
 #define DBG_TRACE_MSG_QUEUE_SIZE 4096
 #define MAX_DBG_TRACE_MSG_SIZE 1024
 
-/******************************************************************************
- * FreeRTOS
- ******************************************************************************/
-#define CFG_SHCI_USER_EVT_PROCESS_NAME        "ble_shci_evt"
-#define CFG_SHCI_USER_EVT_PROCESS_ATTR_BITS   (0)
-#define CFG_SHCI_USER_EVT_PROCESS_CB_MEM      (0)
-#define CFG_SHCI_USER_EVT_PROCESS_CB_SIZE     (0)
-#define CFG_SHCI_USER_EVT_PROCESS_STACK_MEM   (0)
-#define CFG_SHCI_USER_EVT_PROCESS_PRIORITY    osPriorityNone
-#define CFG_SHCI_USER_EVT_PROCESS_STACK_SIZE  (128 * 7)
-
 #define CFG_OTP_BASE_ADDRESS    OTP_AREA_BASE
 #define CFG_OTP_END_ADRESS      OTP_AREA_END_ADDR

+ 0 - 180
firmware/targets/f6/ble-glue/app_entry.c

@@ -1,180 +0,0 @@
-#include "app_common.h"
-#include "main.h"
-#include "app_entry.h"
-#include "ble_app.h"
-#include "ble.h"
-#include "tl.h"
-#include "cmsis_os.h"
-#include "shci_tl.h"
-#include "app_debug.h"
-#include <furi-hal.h>
-
-extern RTC_HandleTypeDef hrtc;
-
-#define POOL_SIZE (CFG_TLBLE_EVT_QUEUE_LENGTH*4U*DIVC(( sizeof(TL_PacketHeader_t) + TL_BLE_EVENT_FRAME_SIZE ), 4U))
-
-PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t EvtPool[POOL_SIZE];
-PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static TL_CmdPacket_t SystemCmdBuffer;
-PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t SystemSpareEvtBuffer[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255U];
-PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t BleSpareEvtBuffer[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255];
-
-osMutexId_t MtxShciId;
-osSemaphoreId_t SemShciId;
-osThreadId_t ShciUserEvtProcessId;
-
-volatile static BleGlueStatus ble_glue_status = BleGlueStatusUninitialized;
-
-const osThreadAttr_t ShciUserEvtProcess_attr = {
-    .name = CFG_SHCI_USER_EVT_PROCESS_NAME,
-    .attr_bits = CFG_SHCI_USER_EVT_PROCESS_ATTR_BITS,
-    .cb_mem = CFG_SHCI_USER_EVT_PROCESS_CB_MEM,
-    .cb_size = CFG_SHCI_USER_EVT_PROCESS_CB_SIZE,
-    .stack_mem = CFG_SHCI_USER_EVT_PROCESS_STACK_MEM,
-    .priority = CFG_SHCI_USER_EVT_PROCESS_PRIORITY,
-    .stack_size = CFG_SHCI_USER_EVT_PROCESS_STACK_SIZE
-};
-
-static void ShciUserEvtProcess(void *argument);
-static void SystemPower_Config( void );
-static void appe_Tl_Init( void );
-static void APPE_SysStatusNot( SHCI_TL_CmdStatus_t status );
-static void APPE_SysUserEvtRx( void * pPayload );
-
-BleGlueStatus APPE_Status() {
-  return ble_glue_status;
-}
-
-void APPE_Init() {
-  ble_glue_status = BleGlueStatusStartup;
-  SystemPower_Config(); /**< Configure the system Power Mode */
-
-  // APPD_Init();
-  furi_hal_power_insomnia_enter();
-
-  appe_Tl_Init();	/* Initialize all transport layers */
-
-  /**
-   * From now, the application is waiting for the ready event ( VS_HCI_C2_Ready )
-   * received on the system channel before starting the Stack
-   * This system event is received with APPE_SysUserEvtRx()
-   */
-}
-
-/*************************************************************
- *
- * LOCAL FUNCTIONS
- *
- *************************************************************/
-
-/**
- * @brief  Configure the system for power optimization
- *
- * @note  This API configures the system to be ready for low power mode
- *
- * @param  None
- * @retval None
- */
-static void SystemPower_Config(void) {
-  // Select HSI as system clock source after Wake Up from Stop mode
-  LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI);
-
-  /* Initialize the CPU2 reset value before starting CPU2 with C2BOOT */
-  LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
-}
-
-static void appe_Tl_Init( void ) {
-  TL_MM_Config_t tl_mm_config;
-  SHCI_TL_HciInitConf_t SHci_Tl_Init_Conf;
-  /**< Reference table initialization */
-  TL_Init();
-
-  MtxShciId = osMutexNew( NULL );
-  SemShciId = osSemaphoreNew( 1, 0, NULL ); /*< Create the semaphore and make it busy at initialization */
-
-  /** FreeRTOS system task creation */
-  ShciUserEvtProcessId = osThreadNew(ShciUserEvtProcess, NULL, &ShciUserEvtProcess_attr);
-
-  /**< System channel initialization */
-  SHci_Tl_Init_Conf.p_cmdbuffer = (uint8_t*)&SystemCmdBuffer;
-  SHci_Tl_Init_Conf.StatusNotCallBack = APPE_SysStatusNot;
-  shci_init(APPE_SysUserEvtRx, (void*) &SHci_Tl_Init_Conf);
-
-  /**< Memory Manager channel initialization */
-  tl_mm_config.p_BleSpareEvtBuffer = BleSpareEvtBuffer;
-  tl_mm_config.p_SystemSpareEvtBuffer = SystemSpareEvtBuffer;
-  tl_mm_config.p_AsynchEvtPool = EvtPool;
-  tl_mm_config.AsynchEvtPoolSize = POOL_SIZE;
-  TL_MM_Init( &tl_mm_config );
-
-  TL_Enable();
-}
-
-static void APPE_SysStatusNot( SHCI_TL_CmdStatus_t status ) {
-  switch (status) {
-    case SHCI_TL_CmdBusy:
-      osMutexAcquire( MtxShciId, osWaitForever );
-      break;
-    case SHCI_TL_CmdAvailable:
-      osMutexRelease( MtxShciId );
-      break;
-    default:
-      break;
-  }
-}
-
-/**
- * The type of the payload for a system user event is tSHCI_UserEvtRxParam
- * When the system event is both :
- *    - a ready event (subevtcode = SHCI_SUB_EVT_CODE_READY)
- *    - reported by the FUS (sysevt_ready_rsp == FUS_FW_RUNNING)
- * The buffer shall not be released
- * ( eg ((tSHCI_UserEvtRxParam*)pPayload)->status shall be set to SHCI_TL_UserEventFlow_Disable )
- * When the status is not filled, the buffer is released by default
- */
-static void APPE_SysUserEvtRx( void * pPayload ) {
-  UNUSED(pPayload);
-  /* Traces channel initialization */
-  // APPD_EnableCPU2( );
-  
-  if(ble_app_init()) {
-    FURI_LOG_I("Core2", "BLE stack started");
-    ble_glue_status = BleGlueStatusStarted;
-  } else {
-    FURI_LOG_E("Core2", "BLE stack startup failed");
-    ble_glue_status = BleGlueStatusBroken;
-  }
-  furi_hal_power_insomnia_exit();
-}
-
-/*************************************************************
- *
- * FREERTOS WRAPPER FUNCTIONS
- *
-*************************************************************/
-static void ShciUserEvtProcess(void *argument) {
-  UNUSED(argument);
-  for(;;) {
-    osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever);
-    shci_user_evt_proc();
-  }
-}
-
-/*************************************************************
- *
- * WRAP FUNCTIONS
- *
- *************************************************************/
-void shci_notify_asynch_evt(void* pdata) {
-  UNUSED(pdata);
-  osThreadFlagsSet( ShciUserEvtProcessId, 1 );
-}
-
-void shci_cmd_resp_release(uint32_t flag) {
-  UNUSED(flag);
-  osSemaphoreRelease( SemShciId );
-}
-
-void shci_cmd_resp_wait(uint32_t timeout) {
-  UNUSED(timeout);
-  osSemaphoreAcquire( SemShciId, osWaitForever );
-}

+ 0 - 20
firmware/targets/f6/ble-glue/app_entry.h

@@ -1,20 +0,0 @@
-#pragma once
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum {
-    BleGlueStatusUninitialized,
-    BleGlueStatusStartup,
-    BleGlueStatusBroken,
-    BleGlueStatusStarted
-} BleGlueStatus;
-
-void APPE_Init();
-
-BleGlueStatus APPE_Status();
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif

+ 20 - 2
firmware/targets/f6/ble-glue/ble_app.c

@@ -11,6 +11,7 @@
 #define BLE_APP_TAG "ble app"
 
 PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer;
+PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE];
 
 typedef struct {
     osMutexId_t hci_mtx;
@@ -26,8 +27,8 @@ static void ble_app_hci_event_handler(void * pPayload);
 static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status);
 
 bool ble_app_init() {
+    SHCI_CmdStatus_t status;
     ble_app = furi_alloc(sizeof(BleApp));
-
     // Allocate semafore and mutex for ble command buffer access
     ble_app->hci_mtx = osMutexNew(NULL);
     ble_app->hci_sem = osSemaphoreNew(1, 0, NULL);
@@ -43,6 +44,18 @@ bool ble_app_init() {
     };
     hci_init(ble_app_hci_event_handler, (void*)&hci_tl_config);
 
+    // Configure NVM store for pairing data
+    SHCI_C2_CONFIG_Cmd_Param_t config_param = {
+        .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE,
+        .Config1 =SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM,
+        .BleNvmRamAddress = (uint32_t)ble_app_nvm,
+        .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE,
+    };
+    status = SHCI_C2_Config(&config_param);
+    if(status) {
+        FURI_LOG_E(BLE_APP_TAG, "Failed to configure 2nd core: %d", status);
+    }
+
     // Start ble stack on 2nd core
     SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = {
         .Header = {{0,0,0}}, // Header unused
@@ -67,13 +80,18 @@ bool ble_app_init() {
             0,
         }
     };
-    SHCI_CmdStatus_t status = SHCI_C2_BLE_Init(&ble_init_cmd_packet);
+    status = SHCI_C2_BLE_Init(&ble_init_cmd_packet);
     if(status) {
         FURI_LOG_E(BLE_APP_TAG, "Failed to start ble stack: %d", status);
     }
     return status == SHCI_Success;
 }
 
+void ble_app_get_key_storage_buff(uint8_t** addr, uint16_t* size) {
+    *addr = (uint8_t*)ble_app_nvm;
+    *size = sizeof(ble_app_nvm);
+}
+
 static void ble_app_hci_thread(void *arg) {
     while(1) {
         osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever);

+ 2 - 0
firmware/targets/f6/ble-glue/ble_app.h

@@ -5,8 +5,10 @@ extern "C" {
 #endif
 
 #include <stdbool.h>
+#include <stdint.h>
 
 bool ble_app_init();
+void ble_app_get_key_storage_buff(uint8_t** addr, uint16_t* size);
 
 #ifdef __cplusplus
 }

+ 173 - 0
firmware/targets/f6/ble-glue/ble_glue.c

@@ -0,0 +1,173 @@
+#include "ble_glue.h"
+#include "app_common.h"
+#include "main.h"
+#include "ble_app.h"
+#include "ble.h"
+#include "tl.h"
+#include "shci.h"
+#include "cmsis_os.h"
+#include "shci_tl.h"
+#include "app_debug.h"
+#include <furi-hal.h>
+
+#define POOL_SIZE (CFG_TLBLE_EVT_QUEUE_LENGTH*4U*DIVC(( sizeof(TL_PacketHeader_t) + TL_BLE_EVENT_FRAME_SIZE ), 4U))
+
+PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t ble_glue_event_pool[POOL_SIZE];
+PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static TL_CmdPacket_t ble_glue_system_cmd_buff;
+PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t ble_glue_system_spare_event_buff[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255U];
+PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t ble_glue_ble_spare_event_buff[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255];
+
+typedef struct {
+    osMutexId_t shci_mtx;
+    osSemaphoreId_t shci_sem;
+    osThreadId_t shci_user_event_thread_id;
+    osThreadAttr_t shci_user_event_thread_attr;
+    BleGlueStatus status;
+    BleGlueKeyStorageChangedCallback callback;
+    void* context;
+} BleGlue;
+
+static BleGlue* ble_glue = NULL;
+
+static void ble_glue_user_event_thread(void *argument);
+static void ble_glue_sys_status_not_callback(SHCI_TL_CmdStatus_t status);
+static void ble_glue_sys_user_event_callback(void* pPayload);
+
+BleGlueStatus ble_glue_get_status() {
+    if(!ble_glue) {
+        return BleGlueStatusUninitialized;
+    }
+    return ble_glue->status;
+}
+
+void ble_glue_set_key_storage_changed_callback(BleGlueKeyStorageChangedCallback callback, void* context) {
+    furi_assert(ble_glue);
+    furi_assert(callback);
+    ble_glue->callback = callback;
+    ble_glue->context = context;
+}
+
+void ble_glue_init() {
+    ble_glue = furi_alloc(sizeof(BleGlue));
+    ble_glue->status = BleGlueStatusStartup;
+    ble_glue->shci_user_event_thread_attr.name = "ble_shci_evt";
+    ble_glue->shci_user_event_thread_attr.stack_size = 1024;
+
+    // Configure the system Power Mode
+    // Select HSI as system clock source after Wake Up from Stop mode
+    LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI);
+    /* Initialize the CPU2 reset value before starting CPU2 with C2BOOT */
+    LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
+    furi_hal_power_insomnia_enter();
+
+    // APPD_Init();
+
+    // Initialize all transport layers
+    TL_MM_Config_t tl_mm_config;
+    SHCI_TL_HciInitConf_t SHci_Tl_Init_Conf;
+    // Reference table initialization
+    TL_Init();
+
+    ble_glue->shci_mtx = osMutexNew(NULL);
+    ble_glue->shci_sem = osSemaphoreNew(1, 0, NULL);
+
+    // FreeRTOS system task creation
+    ble_glue->shci_user_event_thread_id = osThreadNew(ble_glue_user_event_thread, NULL, &ble_glue->shci_user_event_thread_attr);
+
+    // System channel initialization
+    SHci_Tl_Init_Conf.p_cmdbuffer = (uint8_t*)&ble_glue_system_cmd_buff;
+    SHci_Tl_Init_Conf.StatusNotCallBack = ble_glue_sys_status_not_callback;
+    shci_init(ble_glue_sys_user_event_callback, (void*) &SHci_Tl_Init_Conf);
+
+    /**< Memory Manager channel initialization */
+    tl_mm_config.p_BleSpareEvtBuffer = ble_glue_ble_spare_event_buff;
+    tl_mm_config.p_SystemSpareEvtBuffer = ble_glue_system_spare_event_buff;
+    tl_mm_config.p_AsynchEvtPool = ble_glue_event_pool;
+    tl_mm_config.AsynchEvtPoolSize = POOL_SIZE;
+    TL_MM_Init( &tl_mm_config );
+    TL_Enable();
+
+    /*
+     * From now, the application is waiting for the ready event ( VS_HCI_C2_Ready )
+     * received on the system channel before starting the Stack
+     * This system event is received with ble_glue_sys_user_event_callback()
+     */
+}
+
+static void ble_glue_sys_status_not_callback(SHCI_TL_CmdStatus_t status) {
+    switch (status) {
+    case SHCI_TL_CmdBusy:
+        osMutexAcquire( ble_glue->shci_mtx, osWaitForever );
+        break;
+    case SHCI_TL_CmdAvailable:
+        osMutexRelease( ble_glue->shci_mtx );
+        break;
+    default:
+        break;
+  }
+}
+
+/*
+ * The type of the payload for a system user event is tSHCI_UserEvtRxParam
+ * When the system event is both :
+ *    - a ready event (subevtcode = SHCI_SUB_EVT_CODE_READY)
+ *    - reported by the FUS (sysevt_ready_rsp == FUS_FW_RUNNING)
+ * The buffer shall not be released
+ * ( eg ((tSHCI_UserEvtRxParam*)pPayload)->status shall be set to SHCI_TL_UserEventFlow_Disable )
+ * When the status is not filled, the buffer is released by default
+ */
+static void ble_glue_sys_user_event_callback( void * pPayload ) {
+    UNUSED(pPayload);
+    /* Traces channel initialization */
+    // APPD_EnableCPU2( );
+
+    TL_AsynchEvt_t *p_sys_event = (TL_AsynchEvt_t*)(((tSHCI_UserEvtRxParam*)pPayload)->pckt->evtserial.evt.payload);
+    
+    if(p_sys_event->subevtcode == SHCI_SUB_EVT_CODE_READY) {
+        if(ble_app_init()) {
+            FURI_LOG_I("Core2", "BLE stack started");
+            ble_glue->status = BleGlueStatusStarted;
+            if(SHCI_C2_SetFlashActivityControl(FLASH_ACTIVITY_CONTROL_SEM7) == SHCI_Success) {
+                FURI_LOG_I("Core2", "Flash activity control switched to SEM7");
+            } else {
+                FURI_LOG_E("Core2", "Failed to switch flash activity control to SEM7");
+            }
+        } else {
+            FURI_LOG_E("Core2", "BLE stack startup failed");
+            ble_glue->status = BleGlueStatusBleStackMissing;
+        }
+        furi_hal_power_insomnia_exit();
+    } else if(p_sys_event->subevtcode == SHCI_SUB_EVT_ERROR_NOTIF) {
+        FURI_LOG_E("Core2", "Error during initialization");
+        furi_hal_power_insomnia_exit();
+    } else if(p_sys_event->subevtcode == SHCI_SUB_EVT_BLE_NVM_RAM_UPDATE) {
+        SHCI_C2_BleNvmRamUpdate_Evt_t* p_sys_ble_nvm_ram_update_event = (SHCI_C2_BleNvmRamUpdate_Evt_t*)p_sys_event->payload;
+        if(ble_glue->callback) {
+            ble_glue->callback((uint8_t*)p_sys_ble_nvm_ram_update_event->StartAddress, p_sys_ble_nvm_ram_update_event->Size, ble_glue->context);
+        }
+    }
+}
+
+// Wrap functions
+static void ble_glue_user_event_thread(void *argument) {
+    UNUSED(argument);
+    for(;;) {
+        osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever);
+        shci_user_evt_proc();
+    }
+}
+
+void shci_notify_asynch_evt(void* pdata) {
+    UNUSED(pdata);
+    osThreadFlagsSet(ble_glue->shci_user_event_thread_id, 1);
+}
+
+void shci_cmd_resp_release(uint32_t flag) {
+    UNUSED(flag);
+    osSemaphoreRelease(ble_glue->shci_sem);
+}
+
+void shci_cmd_resp_wait(uint32_t timeout) {
+    UNUSED(timeout);
+    osSemaphoreAcquire(ble_glue->shci_sem, osWaitForever);
+}

+ 26 - 0
firmware/targets/f6/ble-glue/ble_glue.h

@@ -0,0 +1,26 @@
+#pragma once
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void(*BleGlueKeyStorageChangedCallback)(uint8_t* change_addr_start, uint16_t size, void* context);
+
+typedef enum {
+    BleGlueStatusUninitialized,
+    BleGlueStatusStartup,
+    BleGlueStatusBleStackMissing,
+    BleGlueStatusStarted
+} BleGlueStatus;
+
+void ble_glue_init();
+
+BleGlueStatus ble_glue_get_status();
+
+void ble_glue_set_key_storage_changed_callback(BleGlueKeyStorageChangedCallback callback, void* context);
+
+#ifdef __cplusplus
+}
+#endif

+ 1 - 2
firmware/targets/f6/ble-glue/gap.c

@@ -1,6 +1,5 @@
 #include "gap.h"
 
-#include "app_entry.h"
 #include "ble.h"
 
 #include "cmsis_os.h"
@@ -375,7 +374,7 @@ static void gap_advetise_timer_callback(void* context) {
 }
 
 bool gap_init(BleEventCallback on_event_cb, void* context) {
-    if (APPE_Status() != BleGlueStatusStarted) {
+    if (ble_glue_get_status() != BleGlueStatusStarted) {
         return false;
     }
 

+ 31 - 0
firmware/targets/f6/ble-glue/hw_conf.h

@@ -29,6 +29,37 @@
  * Semaphores
  * THIS SHALL NO BE CHANGED AS THESE SEMAPHORES ARE USED AS WELL ON THE CM0+
  *****************************************************************************/
+/**
+* Index of the semaphore used the prevent conflicts after standby sleep.
+* Each CPUs takes this semaphore at standby wakeup until conclicting elements are restored.
+*/
+#define CFG_HW_PWR_STANDBY_SEMID                               10
+/**
+*  The CPU2 may be configured to store the Thread persistent data either in internal NVM storage on CPU2 or in
+*  SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config()
+*  When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed.
+*  In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be:
+*  + CPU1 takes CFG_HW_THREAD_NVM_SRAM_SEMID semaphore
+*  + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1)
+*  + CPU1 releases CFG_HW_THREAD_NVM_SRAM_SEMID semaphore
+*  CFG_HW_THREAD_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them.
+*  There is no timing constraint on how long this semaphore can be kept.
+*/
+#define CFG_HW_THREAD_NVM_SRAM_SEMID                           9
+
+/**
+*  The CPU2 may be configured to store the BLE persistent data either in internal NVM storage on CPU2 or in
+*  SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config()
+*  When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed.
+*  In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be:
+*  + CPU1 takes CFG_HW_BLE_NVM_SRAM_SEMID semaphore
+*  + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1)
+*  + CPU1 releases CFG_HW_BLE_NVM_SRAM_SEMID semaphore
+*  CFG_HW_BLE_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them.
+*  There is no timing constraint on how long this semaphore can be kept.
+*/
+#define CFG_HW_BLE_NVM_SRAM_SEMID                              8
+
 /**
 *  Index of the semaphore used by CPU2 to prevent the CPU1 to either write or erase data in flash
 *  The CPU1 shall not either write or erase in flash when this semaphore is taken by the CPU2

+ 123 - 32
firmware/targets/f6/furi-hal/furi-hal-bt.c

@@ -1,5 +1,4 @@
 #include <furi-hal-bt.h>
-#include <app_entry.h>
 #include <ble.h>
 #include <stm32wbxx.h>
 #include <shci.h>
@@ -7,11 +6,37 @@
 
 #include <furi.h>
 
+osMutexId_t furi_hal_bt_core2_mtx = NULL;
+
 void furi_hal_bt_init() {
+    furi_hal_bt_core2_mtx = osMutexNew(NULL);
+}
+
+static bool furi_hal_bt_wait_startup() {
+    uint16_t counter = 0;
+    while (!(ble_glue_get_status() == BleGlueStatusStarted || ble_glue_get_status() == BleGlueStatusBleStackMissing)) {
+        osDelay(10);
+        counter++;
+        if (counter > 1000) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool furi_hal_bt_start_core2() {
+    furi_assert(furi_hal_bt_core2_mtx);
+
+    bool ret = false;
+    osMutexAcquire(furi_hal_bt_core2_mtx, osWaitForever);
     // Explicitly tell that we are in charge of CLK48 domain
     HAL_HSEM_FastTake(CFG_HW_CLK48_CONFIG_SEMID);
-    // Start Core2, init HCI and start GAP/GATT
-    APPE_Init();
+    // Start Core2
+    ble_glue_init();
+    // Wait for Core2 start
+    ret = furi_hal_bt_wait_startup();
+    osMutexRelease(furi_hal_bt_core2_mtx);
+    return ret;
 }
 
 bool furi_hal_bt_init_app(BleEventCallback event_cb, void* context) {
@@ -45,8 +70,33 @@ bool furi_hal_bt_tx(uint8_t* data, uint16_t size) {
     return serial_svc_update_tx(data, size);
 }
 
+bool furi_hal_bt_get_key_storage_buff(uint8_t** key_buff_addr, uint16_t* key_buff_size) {
+    bool ret = false;
+    BleGlueStatus status = ble_glue_get_status();
+    if(status == BleGlueStatusUninitialized || BleGlueStatusStarted) {
+        ble_app_get_key_storage_buff(key_buff_addr, key_buff_size);
+        ret = true;
+    }
+    return ret;
+}
+
+void furi_hal_bt_set_key_storage_change_callback(BleGlueKeyStorageChangedCallback callback, void* context) {
+    furi_assert(callback);
+    ble_glue_set_key_storage_changed_callback(callback, context);
+}
+
+void furi_hal_bt_nvm_sram_sem_acquire() {
+    while(HAL_HSEM_FastTake(CFG_HW_BLE_NVM_SRAM_SEMID) != HAL_OK) {
+        osDelay(1);
+    }
+}
+
+void furi_hal_bt_nvm_sram_sem_release() {
+    HAL_HSEM_Release(CFG_HW_BLE_NVM_SRAM_SEMID, 0);
+}
+
 void furi_hal_bt_dump_state(string_t buffer) {
-    BleGlueStatus status = APPE_Status();
+    BleGlueStatus status = ble_glue_get_status();
     if (status == BleGlueStatusStarted) {
         uint8_t HCI_Version;
         uint16_t HCI_Revision;
@@ -68,56 +118,97 @@ void furi_hal_bt_dump_state(string_t buffer) {
 }
 
 bool furi_hal_bt_is_alive() {
-    BleGlueStatus status = APPE_Status();
-    return (status == BleGlueStatusBroken) || (status == BleGlueStatusStarted);
+    BleGlueStatus status = ble_glue_get_status();
+    return (status == BleGlueStatusBleStackMissing) || (status == BleGlueStatusStarted);
 }
 
 bool furi_hal_bt_is_active() {
     return gap_get_state() > GapStateIdle;
 }
 
-bool furi_hal_bt_wait_startup() {
-    uint16_t counter = 0;
-    while (!(APPE_Status() == BleGlueStatusStarted || APPE_Status() == BleGlueStatusBroken)) {
-        osDelay(10);
-        counter++;
-        if (counter > 1000) {
-            return false;
-        }
-    }
-    return true;
-}
-
-bool furi_hal_bt_lock_flash(bool erase_flag) {
-    if (!furi_hal_bt_wait_startup()) {
-        return false;
-    }
-    
+static void furi_hal_bt_lock_flash_core2(bool erase_flag) {
+    // Take flash controller ownership 
     while (HAL_HSEM_FastTake(CFG_HW_FLASH_SEMID) != HAL_OK) {
-        osDelay(1);
+        taskYIELD();
     }
 
+    // Unlock flash operation
     HAL_FLASH_Unlock();
 
+    // Erase activity notification
     if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON);
 
-    while(LL_FLASH_IsActiveFlag_OperationSuspended()) {
-        osDelay(1);
-    };
+    while(true) {
+        // Wait till flash controller become usable
+        while(LL_FLASH_IsActiveFlag_OperationSuspended()) {
+            taskYIELD();
+        };
 
-    __disable_irq();
+        // Just a little more love
+        taskENTER_CRITICAL();
 
-    return true;
+        // Actually we already have mutex for it, but specification is specification
+        if (HAL_HSEM_IsSemTaken(CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID)) {
+            taskEXIT_CRITICAL();
+            continue;
+        }
+
+        // Take sempahopre and prevent core2 from anyting funky
+        if (HAL_HSEM_FastTake(CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID) != HAL_OK) {
+            taskEXIT_CRITICAL();
+            continue;
+        }
+
+        break;
+    }
 }
 
-void furi_hal_bt_unlock_flash(bool erase_flag) {
-    __enable_irq();
+void furi_hal_bt_lock_flash(bool erase_flag) {
+    // Acquire dangerous ops mutex
+    osMutexAcquire(furi_hal_bt_core2_mtx, osWaitForever);
+
+    // If Core2 is running use IPC locking
+    BleGlueStatus status = ble_glue_get_status();
+    if(status == BleGlueStatusStarted || status == BleGlueStatusBleStackMissing) {
+        furi_hal_bt_lock_flash_core2(erase_flag);
+    } else { 
+        HAL_FLASH_Unlock();
+    }
+}
+
+static void furi_hal_bt_unlock_flash_core2(bool erase_flag) {
+    // Funky ops are ok at this point
+    HAL_HSEM_Release(CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID, 0);
+
+    // Task switching is ok
+    taskEXIT_CRITICAL();
 
+    // Doesn't make much sense, does it?
+    while (__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) {
+        taskYIELD();
+    }
+
+    // Erase activity over, core2 can continue
     if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF);
 
+    // Lock flash controller
     HAL_FLASH_Lock();
 
-    HAL_HSEM_Release(CFG_HW_FLASH_SEMID, HSEM_CPU1_COREID);
+    // Release flash controller ownership
+    HAL_HSEM_Release(CFG_HW_FLASH_SEMID, 0);
+}
+
+void furi_hal_bt_unlock_flash(bool erase_flag) {
+    // If Core2 is running use IPC locking
+    BleGlueStatus status = ble_glue_get_status();
+    if(status == BleGlueStatusStarted || status == BleGlueStatusBleStackMissing) {
+        furi_hal_bt_unlock_flash_core2(erase_flag);
+    } else { 
+        HAL_FLASH_Lock();
+    }
+
+    // Release dangerous ops mutex
+    osMutexRelease(furi_hal_bt_core2_mtx);
 }
 
 void furi_hal_bt_start_tone_tx(uint8_t channel, uint8_t power) {

+ 13 - 0
firmware/targets/f6/furi-hal/furi-hal-crypto.c

@@ -1,4 +1,5 @@
 #include <furi-hal-crypto.h>
+#include <furi-hal-bt.h>
 #include <furi.h>
 #include <shci.h>
 
@@ -12,6 +13,10 @@ bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot) {
     furi_assert(key);
     furi_assert(slot);
 
+    if(!furi_hal_bt_is_alive()) {
+        return false;
+    }
+
     SHCI_C2_FUS_StoreUsrKey_Cmd_Param_t pParam;
     size_t key_data_size = 0;
 
@@ -44,6 +49,10 @@ bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot) {
 bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) {
     furi_assert(slot > 0 && slot <= 100);
 
+    if(!furi_hal_bt_is_alive()) {
+        return false;
+    }
+
     crypt.Instance = AES1;
     crypt.Init.DataType = CRYP_DATATYPE_32B;
     crypt.Init.KeySize = CRYP_KEYSIZE_256B;
@@ -63,6 +72,10 @@ bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) {
 }
 
 bool furi_hal_crypto_store_unload_key(uint8_t slot) {
+    if(!furi_hal_bt_is_alive()) {
+        return false;
+    }
+
     furi_check(HAL_CRYP_DeInit(&crypt) == HAL_OK);
     return SHCI_C2_FUS_UnloadUsrKey(slot) == SHCI_Success;
 }

+ 32 - 16
firmware/targets/f6/furi-hal/furi-hal-flash.c

@@ -1,11 +1,14 @@
 #include <furi-hal-flash.h>
 #include <furi-hal-bt.h>
-#include <stm32wbxx.h>
 #include <furi.h>
 
+#include <stm32wbxx.h>
+
 /* Free flash space borders, exported by linker */
 extern const void __free_flash_start__;
 
+#define FURI_HAL_TAG "FuriHalFlash"
+#define FURI_HAL_CRITICAL_MSG "Critical flash operation fail"
 #define FURI_HAL_FLASH_READ_BLOCK 8
 #define FURI_HAL_FLASH_WRITE_BLOCK 8
 #define FURI_HAL_FLASH_PAGE_SIZE 4096
@@ -57,33 +60,46 @@ size_t furi_hal_flash_get_free_page_count() {
 }
 
 bool furi_hal_flash_erase(uint8_t page, uint8_t count) {
-    if (!furi_hal_bt_lock_flash(true)) {
-        return false;
-    }
+    furi_hal_bt_lock_flash(true);
+
     FLASH_EraseInitTypeDef erase;
     erase.TypeErase = FLASH_TYPEERASE_PAGES;
     erase.Page = page;
     erase.NbPages = count;
-    uint32_t error;
-    HAL_StatusTypeDef status = HAL_FLASHEx_Erase(&erase, &error);
+
+    uint32_t error_page = 0;
+    HAL_StatusTypeDef status = HAL_FLASHEx_Erase(&erase, &error_page);
+    if (status != HAL_OK) {
+        FURI_LOG_E(FURI_HAL_TAG, "Erase failed, ret: %d, page: %d", status, error_page);
+        furi_crash(FURI_HAL_CRITICAL_MSG);
+    }
+
     furi_hal_bt_unlock_flash(true);
-    return status == HAL_OK;
+
+    return true;
 }
 
 bool furi_hal_flash_write_dword(size_t address, uint64_t data) {
-    if (!furi_hal_bt_lock_flash(false)) {
-        return false;
-    }
+    furi_hal_bt_lock_flash(false);
+
     HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, data);
+    if (status != HAL_OK) {
+        FURI_LOG_E(FURI_HAL_TAG, "Programming failed, ret: %d, address: %p", status, address);
+        furi_crash(FURI_HAL_CRITICAL_MSG);
+    }
+
     furi_hal_bt_unlock_flash(false);
-    return status == HAL_OK;
+
+    return true;
 }
 
-bool furi_hal_flash_write_dword_from(size_t address, size_t source_address) {
-    if (!furi_hal_bt_lock_flash(false)) {
-        return false;
-    }
+bool furi_hal_flash_write_row(size_t address, size_t source_address) {
+    furi_hal_bt_lock_flash(false);
+
     HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FAST, address, source_address);
+    furi_check(status == HAL_OK);
+
     furi_hal_bt_unlock_flash(false);
-    return status == HAL_OK;
+
+    return true;
 }

+ 2 - 2
firmware/targets/f6/furi-hal/furi-hal-flash.h

@@ -80,7 +80,7 @@ bool furi_hal_flash_erase(uint8_t page, uint8_t count);
  */
 bool furi_hal_flash_write_dword(size_t address, uint64_t data);
 
-/** Write double word (64 bits) from address
+/** Write row: 64 double word (64 bits) from address
  *
  * Locking operation, uses HSEM to manage shared access.
  *
@@ -89,4 +89,4 @@ bool furi_hal_flash_write_dword(size_t address, uint64_t data);
  *
  * @return     true on success
  */
-bool furi_hal_flash_write_dword_from(size_t address, size_t source_address);
+bool furi_hal_flash_write_row(size_t address, size_t source_address);

+ 0 - 11
firmware/targets/f7/ble-glue/app_conf.h

@@ -427,16 +427,5 @@ typedef enum
 #define DBG_TRACE_MSG_QUEUE_SIZE 4096
 #define MAX_DBG_TRACE_MSG_SIZE 1024
 
-/******************************************************************************
- * FreeRTOS
- ******************************************************************************/
-#define CFG_SHCI_USER_EVT_PROCESS_NAME        "ble_shci_evt"
-#define CFG_SHCI_USER_EVT_PROCESS_ATTR_BITS   (0)
-#define CFG_SHCI_USER_EVT_PROCESS_CB_MEM      (0)
-#define CFG_SHCI_USER_EVT_PROCESS_CB_SIZE     (0)
-#define CFG_SHCI_USER_EVT_PROCESS_STACK_MEM   (0)
-#define CFG_SHCI_USER_EVT_PROCESS_PRIORITY    osPriorityNone
-#define CFG_SHCI_USER_EVT_PROCESS_STACK_SIZE  (128 * 7)
-
 #define CFG_OTP_BASE_ADDRESS    OTP_AREA_BASE
 #define CFG_OTP_END_ADRESS      OTP_AREA_END_ADDR

+ 0 - 180
firmware/targets/f7/ble-glue/app_entry.c

@@ -1,180 +0,0 @@
-#include "app_common.h"
-#include "main.h"
-#include "app_entry.h"
-#include "ble_app.h"
-#include "ble.h"
-#include "tl.h"
-#include "cmsis_os.h"
-#include "shci_tl.h"
-#include "app_debug.h"
-#include <furi-hal.h>
-
-extern RTC_HandleTypeDef hrtc;
-
-#define POOL_SIZE (CFG_TLBLE_EVT_QUEUE_LENGTH*4U*DIVC(( sizeof(TL_PacketHeader_t) + TL_BLE_EVENT_FRAME_SIZE ), 4U))
-
-PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t EvtPool[POOL_SIZE];
-PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static TL_CmdPacket_t SystemCmdBuffer;
-PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t SystemSpareEvtBuffer[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255U];
-PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t BleSpareEvtBuffer[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255];
-
-osMutexId_t MtxShciId;
-osSemaphoreId_t SemShciId;
-osThreadId_t ShciUserEvtProcessId;
-
-volatile static BleGlueStatus ble_glue_status = BleGlueStatusUninitialized;
-
-const osThreadAttr_t ShciUserEvtProcess_attr = {
-    .name = CFG_SHCI_USER_EVT_PROCESS_NAME,
-    .attr_bits = CFG_SHCI_USER_EVT_PROCESS_ATTR_BITS,
-    .cb_mem = CFG_SHCI_USER_EVT_PROCESS_CB_MEM,
-    .cb_size = CFG_SHCI_USER_EVT_PROCESS_CB_SIZE,
-    .stack_mem = CFG_SHCI_USER_EVT_PROCESS_STACK_MEM,
-    .priority = CFG_SHCI_USER_EVT_PROCESS_PRIORITY,
-    .stack_size = CFG_SHCI_USER_EVT_PROCESS_STACK_SIZE
-};
-
-static void ShciUserEvtProcess(void *argument);
-static void SystemPower_Config( void );
-static void appe_Tl_Init( void );
-static void APPE_SysStatusNot( SHCI_TL_CmdStatus_t status );
-static void APPE_SysUserEvtRx( void * pPayload );
-
-BleGlueStatus APPE_Status() {
-  return ble_glue_status;
-}
-
-void APPE_Init() {
-  ble_glue_status = BleGlueStatusStartup;
-  SystemPower_Config(); /**< Configure the system Power Mode */
-
-  // APPD_Init();
-  furi_hal_power_insomnia_enter();
-
-  appe_Tl_Init();	/* Initialize all transport layers */
-
-  /**
-   * From now, the application is waiting for the ready event ( VS_HCI_C2_Ready )
-   * received on the system channel before starting the Stack
-   * This system event is received with APPE_SysUserEvtRx()
-   */
-}
-
-/*************************************************************
- *
- * LOCAL FUNCTIONS
- *
- *************************************************************/
-
-/**
- * @brief  Configure the system for power optimization
- *
- * @note  This API configures the system to be ready for low power mode
- *
- * @param  None
- * @retval None
- */
-static void SystemPower_Config(void) {
-  // Select HSI as system clock source after Wake Up from Stop mode
-  LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI);
-
-  /* Initialize the CPU2 reset value before starting CPU2 with C2BOOT */
-  LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
-}
-
-static void appe_Tl_Init( void ) {
-  TL_MM_Config_t tl_mm_config;
-  SHCI_TL_HciInitConf_t SHci_Tl_Init_Conf;
-  /**< Reference table initialization */
-  TL_Init();
-
-  MtxShciId = osMutexNew( NULL );
-  SemShciId = osSemaphoreNew( 1, 0, NULL ); /*< Create the semaphore and make it busy at initialization */
-
-  /** FreeRTOS system task creation */
-  ShciUserEvtProcessId = osThreadNew(ShciUserEvtProcess, NULL, &ShciUserEvtProcess_attr);
-
-  /**< System channel initialization */
-  SHci_Tl_Init_Conf.p_cmdbuffer = (uint8_t*)&SystemCmdBuffer;
-  SHci_Tl_Init_Conf.StatusNotCallBack = APPE_SysStatusNot;
-  shci_init(APPE_SysUserEvtRx, (void*) &SHci_Tl_Init_Conf);
-
-  /**< Memory Manager channel initialization */
-  tl_mm_config.p_BleSpareEvtBuffer = BleSpareEvtBuffer;
-  tl_mm_config.p_SystemSpareEvtBuffer = SystemSpareEvtBuffer;
-  tl_mm_config.p_AsynchEvtPool = EvtPool;
-  tl_mm_config.AsynchEvtPoolSize = POOL_SIZE;
-  TL_MM_Init( &tl_mm_config );
-
-  TL_Enable();
-}
-
-static void APPE_SysStatusNot( SHCI_TL_CmdStatus_t status ) {
-  switch (status) {
-    case SHCI_TL_CmdBusy:
-      osMutexAcquire( MtxShciId, osWaitForever );
-      break;
-    case SHCI_TL_CmdAvailable:
-      osMutexRelease( MtxShciId );
-      break;
-    default:
-      break;
-  }
-}
-
-/**
- * The type of the payload for a system user event is tSHCI_UserEvtRxParam
- * When the system event is both :
- *    - a ready event (subevtcode = SHCI_SUB_EVT_CODE_READY)
- *    - reported by the FUS (sysevt_ready_rsp == FUS_FW_RUNNING)
- * The buffer shall not be released
- * ( eg ((tSHCI_UserEvtRxParam*)pPayload)->status shall be set to SHCI_TL_UserEventFlow_Disable )
- * When the status is not filled, the buffer is released by default
- */
-static void APPE_SysUserEvtRx( void * pPayload ) {
-  UNUSED(pPayload);
-  /* Traces channel initialization */
-  // APPD_EnableCPU2( );
-  
-  if(ble_app_init()) {
-    FURI_LOG_I("Core2", "BLE stack started");
-    ble_glue_status = BleGlueStatusStarted;
-  } else {
-    FURI_LOG_E("Core2", "BLE stack startup failed");
-    ble_glue_status = BleGlueStatusBroken;
-  }
-  furi_hal_power_insomnia_exit();
-}
-
-/*************************************************************
- *
- * FREERTOS WRAPPER FUNCTIONS
- *
-*************************************************************/
-static void ShciUserEvtProcess(void *argument) {
-  UNUSED(argument);
-  for(;;) {
-    osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever);
-    shci_user_evt_proc();
-  }
-}
-
-/*************************************************************
- *
- * WRAP FUNCTIONS
- *
- *************************************************************/
-void shci_notify_asynch_evt(void* pdata) {
-  UNUSED(pdata);
-  osThreadFlagsSet( ShciUserEvtProcessId, 1 );
-}
-
-void shci_cmd_resp_release(uint32_t flag) {
-  UNUSED(flag);
-  osSemaphoreRelease( SemShciId );
-}
-
-void shci_cmd_resp_wait(uint32_t timeout) {
-  UNUSED(timeout);
-  osSemaphoreAcquire( SemShciId, osWaitForever );
-}

+ 0 - 20
firmware/targets/f7/ble-glue/app_entry.h

@@ -1,20 +0,0 @@
-#pragma once
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum {
-    BleGlueStatusUninitialized,
-    BleGlueStatusStartup,
-    BleGlueStatusBroken,
-    BleGlueStatusStarted
-} BleGlueStatus;
-
-void APPE_Init();
-
-BleGlueStatus APPE_Status();
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif

+ 20 - 2
firmware/targets/f7/ble-glue/ble_app.c

@@ -11,6 +11,7 @@
 #define BLE_APP_TAG "ble app"
 
 PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t ble_app_cmd_buffer;
+PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint32_t ble_app_nvm[BLE_NVM_SRAM_SIZE];
 
 typedef struct {
     osMutexId_t hci_mtx;
@@ -26,8 +27,8 @@ static void ble_app_hci_event_handler(void * pPayload);
 static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status);
 
 bool ble_app_init() {
+    SHCI_CmdStatus_t status;
     ble_app = furi_alloc(sizeof(BleApp));
-
     // Allocate semafore and mutex for ble command buffer access
     ble_app->hci_mtx = osMutexNew(NULL);
     ble_app->hci_sem = osSemaphoreNew(1, 0, NULL);
@@ -43,6 +44,18 @@ bool ble_app_init() {
     };
     hci_init(ble_app_hci_event_handler, (void*)&hci_tl_config);
 
+    // Configure NVM store for pairing data
+    SHCI_C2_CONFIG_Cmd_Param_t config_param = {
+        .PayloadCmdSize = SHCI_C2_CONFIG_PAYLOAD_CMD_SIZE,
+        .Config1 =SHCI_C2_CONFIG_CONFIG1_BIT0_BLE_NVM_DATA_TO_SRAM,
+        .BleNvmRamAddress = (uint32_t)ble_app_nvm,
+        .EvtMask1 = SHCI_C2_CONFIG_EVTMASK1_BIT1_BLE_NVM_RAM_UPDATE_ENABLE,
+    };
+    status = SHCI_C2_Config(&config_param);
+    if(status) {
+        FURI_LOG_E(BLE_APP_TAG, "Failed to configure 2nd core: %d", status);
+    }
+
     // Start ble stack on 2nd core
     SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = {
         .Header = {{0,0,0}}, // Header unused
@@ -67,13 +80,18 @@ bool ble_app_init() {
             0,
         }
     };
-    SHCI_CmdStatus_t status = SHCI_C2_BLE_Init(&ble_init_cmd_packet);
+    status = SHCI_C2_BLE_Init(&ble_init_cmd_packet);
     if(status) {
         FURI_LOG_E(BLE_APP_TAG, "Failed to start ble stack: %d", status);
     }
     return status == SHCI_Success;
 }
 
+void ble_app_get_key_storage_buff(uint8_t** addr, uint16_t* size) {
+    *addr = (uint8_t*)ble_app_nvm;
+    *size = sizeof(ble_app_nvm);
+}
+
 static void ble_app_hci_thread(void *arg) {
     while(1) {
         osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever);

+ 2 - 0
firmware/targets/f7/ble-glue/ble_app.h

@@ -5,8 +5,10 @@ extern "C" {
 #endif
 
 #include <stdbool.h>
+#include <stdint.h>
 
 bool ble_app_init();
+void ble_app_get_key_storage_buff(uint8_t** addr, uint16_t* size);
 
 #ifdef __cplusplus
 }

+ 173 - 0
firmware/targets/f7/ble-glue/ble_glue.c

@@ -0,0 +1,173 @@
+#include "ble_glue.h"
+#include "app_common.h"
+#include "main.h"
+#include "ble_app.h"
+#include "ble.h"
+#include "tl.h"
+#include "shci.h"
+#include "cmsis_os.h"
+#include "shci_tl.h"
+#include "app_debug.h"
+#include <furi-hal.h>
+
+#define POOL_SIZE (CFG_TLBLE_EVT_QUEUE_LENGTH*4U*DIVC(( sizeof(TL_PacketHeader_t) + TL_BLE_EVENT_FRAME_SIZE ), 4U))
+
+PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t ble_glue_event_pool[POOL_SIZE];
+PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static TL_CmdPacket_t ble_glue_system_cmd_buff;
+PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t ble_glue_system_spare_event_buff[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255U];
+PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static uint8_t ble_glue_ble_spare_event_buff[sizeof(TL_PacketHeader_t) + TL_EVT_HDR_SIZE + 255];
+
+typedef struct {
+    osMutexId_t shci_mtx;
+    osSemaphoreId_t shci_sem;
+    osThreadId_t shci_user_event_thread_id;
+    osThreadAttr_t shci_user_event_thread_attr;
+    BleGlueStatus status;
+    BleGlueKeyStorageChangedCallback callback;
+    void* context;
+} BleGlue;
+
+static BleGlue* ble_glue = NULL;
+
+static void ble_glue_user_event_thread(void *argument);
+static void ble_glue_sys_status_not_callback(SHCI_TL_CmdStatus_t status);
+static void ble_glue_sys_user_event_callback(void* pPayload);
+
+BleGlueStatus ble_glue_get_status() {
+    if(!ble_glue) {
+        return BleGlueStatusUninitialized;
+    }
+    return ble_glue->status;
+}
+
+void ble_glue_set_key_storage_changed_callback(BleGlueKeyStorageChangedCallback callback, void* context) {
+    furi_assert(ble_glue);
+    furi_assert(callback);
+    ble_glue->callback = callback;
+    ble_glue->context = context;
+}
+
+void ble_glue_init() {
+    ble_glue = furi_alloc(sizeof(BleGlue));
+    ble_glue->status = BleGlueStatusStartup;
+    ble_glue->shci_user_event_thread_attr.name = "ble_shci_evt";
+    ble_glue->shci_user_event_thread_attr.stack_size = 1024;
+
+    // Configure the system Power Mode
+    // Select HSI as system clock source after Wake Up from Stop mode
+    LL_RCC_SetClkAfterWakeFromStop(LL_RCC_STOP_WAKEUPCLOCK_HSI);
+    /* Initialize the CPU2 reset value before starting CPU2 with C2BOOT */
+    LL_C2_PWR_SetPowerMode(LL_PWR_MODE_SHUTDOWN);
+    furi_hal_power_insomnia_enter();
+
+    // APPD_Init();
+
+    // Initialize all transport layers
+    TL_MM_Config_t tl_mm_config;
+    SHCI_TL_HciInitConf_t SHci_Tl_Init_Conf;
+    // Reference table initialization
+    TL_Init();
+
+    ble_glue->shci_mtx = osMutexNew(NULL);
+    ble_glue->shci_sem = osSemaphoreNew(1, 0, NULL);
+
+    // FreeRTOS system task creation
+    ble_glue->shci_user_event_thread_id = osThreadNew(ble_glue_user_event_thread, NULL, &ble_glue->shci_user_event_thread_attr);
+
+    // System channel initialization
+    SHci_Tl_Init_Conf.p_cmdbuffer = (uint8_t*)&ble_glue_system_cmd_buff;
+    SHci_Tl_Init_Conf.StatusNotCallBack = ble_glue_sys_status_not_callback;
+    shci_init(ble_glue_sys_user_event_callback, (void*) &SHci_Tl_Init_Conf);
+
+    /**< Memory Manager channel initialization */
+    tl_mm_config.p_BleSpareEvtBuffer = ble_glue_ble_spare_event_buff;
+    tl_mm_config.p_SystemSpareEvtBuffer = ble_glue_system_spare_event_buff;
+    tl_mm_config.p_AsynchEvtPool = ble_glue_event_pool;
+    tl_mm_config.AsynchEvtPoolSize = POOL_SIZE;
+    TL_MM_Init( &tl_mm_config );
+    TL_Enable();
+
+    /*
+     * From now, the application is waiting for the ready event ( VS_HCI_C2_Ready )
+     * received on the system channel before starting the Stack
+     * This system event is received with ble_glue_sys_user_event_callback()
+     */
+}
+
+static void ble_glue_sys_status_not_callback(SHCI_TL_CmdStatus_t status) {
+    switch (status) {
+    case SHCI_TL_CmdBusy:
+        osMutexAcquire( ble_glue->shci_mtx, osWaitForever );
+        break;
+    case SHCI_TL_CmdAvailable:
+        osMutexRelease( ble_glue->shci_mtx );
+        break;
+    default:
+        break;
+  }
+}
+
+/*
+ * The type of the payload for a system user event is tSHCI_UserEvtRxParam
+ * When the system event is both :
+ *    - a ready event (subevtcode = SHCI_SUB_EVT_CODE_READY)
+ *    - reported by the FUS (sysevt_ready_rsp == FUS_FW_RUNNING)
+ * The buffer shall not be released
+ * ( eg ((tSHCI_UserEvtRxParam*)pPayload)->status shall be set to SHCI_TL_UserEventFlow_Disable )
+ * When the status is not filled, the buffer is released by default
+ */
+static void ble_glue_sys_user_event_callback( void * pPayload ) {
+    UNUSED(pPayload);
+    /* Traces channel initialization */
+    // APPD_EnableCPU2( );
+
+    TL_AsynchEvt_t *p_sys_event = (TL_AsynchEvt_t*)(((tSHCI_UserEvtRxParam*)pPayload)->pckt->evtserial.evt.payload);
+    
+    if(p_sys_event->subevtcode == SHCI_SUB_EVT_CODE_READY) {
+        if(ble_app_init()) {
+            FURI_LOG_I("Core2", "BLE stack started");
+            ble_glue->status = BleGlueStatusStarted;
+            if(SHCI_C2_SetFlashActivityControl(FLASH_ACTIVITY_CONTROL_SEM7) == SHCI_Success) {
+                FURI_LOG_I("Core2", "Flash activity control switched to SEM7");
+            } else {
+                FURI_LOG_E("Core2", "Failed to switch flash activity control to SEM7");
+            }
+        } else {
+            FURI_LOG_E("Core2", "BLE stack startup failed");
+            ble_glue->status = BleGlueStatusBleStackMissing;
+        }
+        furi_hal_power_insomnia_exit();
+    } else if(p_sys_event->subevtcode == SHCI_SUB_EVT_ERROR_NOTIF) {
+        FURI_LOG_E("Core2", "Error during initialization");
+        furi_hal_power_insomnia_exit();
+    } else if(p_sys_event->subevtcode == SHCI_SUB_EVT_BLE_NVM_RAM_UPDATE) {
+        SHCI_C2_BleNvmRamUpdate_Evt_t* p_sys_ble_nvm_ram_update_event = (SHCI_C2_BleNvmRamUpdate_Evt_t*)p_sys_event->payload;
+        if(ble_glue->callback) {
+            ble_glue->callback((uint8_t*)p_sys_ble_nvm_ram_update_event->StartAddress, p_sys_ble_nvm_ram_update_event->Size, ble_glue->context);
+        }
+    }
+}
+
+// Wrap functions
+static void ble_glue_user_event_thread(void *argument) {
+    UNUSED(argument);
+    for(;;) {
+        osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever);
+        shci_user_evt_proc();
+    }
+}
+
+void shci_notify_asynch_evt(void* pdata) {
+    UNUSED(pdata);
+    osThreadFlagsSet(ble_glue->shci_user_event_thread_id, 1);
+}
+
+void shci_cmd_resp_release(uint32_t flag) {
+    UNUSED(flag);
+    osSemaphoreRelease(ble_glue->shci_sem);
+}
+
+void shci_cmd_resp_wait(uint32_t timeout) {
+    UNUSED(timeout);
+    osSemaphoreAcquire(ble_glue->shci_sem, osWaitForever);
+}

+ 26 - 0
firmware/targets/f7/ble-glue/ble_glue.h

@@ -0,0 +1,26 @@
+#pragma once
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void(*BleGlueKeyStorageChangedCallback)(uint8_t* change_addr_start, uint16_t size, void* context);
+
+typedef enum {
+    BleGlueStatusUninitialized,
+    BleGlueStatusStartup,
+    BleGlueStatusBleStackMissing,
+    BleGlueStatusStarted
+} BleGlueStatus;
+
+void ble_glue_init();
+
+BleGlueStatus ble_glue_get_status();
+
+void ble_glue_set_key_storage_changed_callback(BleGlueKeyStorageChangedCallback callback, void* context);
+
+#ifdef __cplusplus
+}
+#endif

+ 1 - 2
firmware/targets/f7/ble-glue/gap.c

@@ -1,6 +1,5 @@
 #include "gap.h"
 
-#include "app_entry.h"
 #include "ble.h"
 
 #include "cmsis_os.h"
@@ -375,7 +374,7 @@ static void gap_advetise_timer_callback(void* context) {
 }
 
 bool gap_init(BleEventCallback on_event_cb, void* context) {
-    if (APPE_Status() != BleGlueStatusStarted) {
+    if (ble_glue_get_status() != BleGlueStatusStarted) {
         return false;
     }
 

+ 31 - 0
firmware/targets/f7/ble-glue/hw_conf.h

@@ -29,6 +29,37 @@
  * Semaphores
  * THIS SHALL NO BE CHANGED AS THESE SEMAPHORES ARE USED AS WELL ON THE CM0+
  *****************************************************************************/
+/**
+* Index of the semaphore used the prevent conflicts after standby sleep.
+* Each CPUs takes this semaphore at standby wakeup until conclicting elements are restored.
+*/
+#define CFG_HW_PWR_STANDBY_SEMID                               10
+/**
+*  The CPU2 may be configured to store the Thread persistent data either in internal NVM storage on CPU2 or in
+*  SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config()
+*  When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed.
+*  In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be:
+*  + CPU1 takes CFG_HW_THREAD_NVM_SRAM_SEMID semaphore
+*  + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1)
+*  + CPU1 releases CFG_HW_THREAD_NVM_SRAM_SEMID semaphore
+*  CFG_HW_THREAD_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them.
+*  There is no timing constraint on how long this semaphore can be kept.
+*/
+#define CFG_HW_THREAD_NVM_SRAM_SEMID                           9
+
+/**
+*  The CPU2 may be configured to store the BLE persistent data either in internal NVM storage on CPU2 or in
+*  SRAM2 buffer provided by the user application. This can be configured with the system command SHCI_C2_Config()
+*  When the CPU2 is requested to store persistent data in SRAM2, it can write data in this buffer at any time when needed.
+*  In order to read consistent data with the CPU1 from the SRAM2 buffer, the flow should be:
+*  + CPU1 takes CFG_HW_BLE_NVM_SRAM_SEMID semaphore
+*  + CPU1 reads all persistent data from SRAM2 (most of the time, the goal is to write these data into an NVM managed by CPU1)
+*  + CPU1 releases CFG_HW_BLE_NVM_SRAM_SEMID semaphore
+*  CFG_HW_BLE_NVM_SRAM_SEMID semaphore makes sure CPU2 does not update the persistent data in SRAM2 at the same time CPU1 is reading them.
+*  There is no timing constraint on how long this semaphore can be kept.
+*/
+#define CFG_HW_BLE_NVM_SRAM_SEMID                              8
+
 /**
 *  Index of the semaphore used by CPU2 to prevent the CPU1 to either write or erase data in flash
 *  The CPU1 shall not either write or erase in flash when this semaphore is taken by the CPU2

+ 123 - 32
firmware/targets/f7/furi-hal/furi-hal-bt.c

@@ -1,5 +1,4 @@
 #include <furi-hal-bt.h>
-#include <app_entry.h>
 #include <ble.h>
 #include <stm32wbxx.h>
 #include <shci.h>
@@ -7,11 +6,37 @@
 
 #include <furi.h>
 
+osMutexId_t furi_hal_bt_core2_mtx = NULL;
+
 void furi_hal_bt_init() {
+    furi_hal_bt_core2_mtx = osMutexNew(NULL);
+}
+
+static bool furi_hal_bt_wait_startup() {
+    uint16_t counter = 0;
+    while (!(ble_glue_get_status() == BleGlueStatusStarted || ble_glue_get_status() == BleGlueStatusBleStackMissing)) {
+        osDelay(10);
+        counter++;
+        if (counter > 1000) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool furi_hal_bt_start_core2() {
+    furi_assert(furi_hal_bt_core2_mtx);
+
+    bool ret = false;
+    osMutexAcquire(furi_hal_bt_core2_mtx, osWaitForever);
     // Explicitly tell that we are in charge of CLK48 domain
     HAL_HSEM_FastTake(CFG_HW_CLK48_CONFIG_SEMID);
-    // Start Core2, init HCI and start GAP/GATT
-    APPE_Init();
+    // Start Core2
+    ble_glue_init();
+    // Wait for Core2 start
+    ret = furi_hal_bt_wait_startup();
+    osMutexRelease(furi_hal_bt_core2_mtx);
+    return ret;
 }
 
 bool furi_hal_bt_init_app(BleEventCallback event_cb, void* context) {
@@ -45,8 +70,33 @@ bool furi_hal_bt_tx(uint8_t* data, uint16_t size) {
     return serial_svc_update_tx(data, size);
 }
 
+bool furi_hal_bt_get_key_storage_buff(uint8_t** key_buff_addr, uint16_t* key_buff_size) {
+    bool ret = false;
+    BleGlueStatus status = ble_glue_get_status();
+    if(status == BleGlueStatusUninitialized || BleGlueStatusStarted) {
+        ble_app_get_key_storage_buff(key_buff_addr, key_buff_size);
+        ret = true;
+    }
+    return ret;
+}
+
+void furi_hal_bt_set_key_storage_change_callback(BleGlueKeyStorageChangedCallback callback, void* context) {
+    furi_assert(callback);
+    ble_glue_set_key_storage_changed_callback(callback, context);
+}
+
+void furi_hal_bt_nvm_sram_sem_acquire() {
+    while(HAL_HSEM_FastTake(CFG_HW_BLE_NVM_SRAM_SEMID) != HAL_OK) {
+        osDelay(1);
+    }
+}
+
+void furi_hal_bt_nvm_sram_sem_release() {
+    HAL_HSEM_Release(CFG_HW_BLE_NVM_SRAM_SEMID, 0);
+}
+
 void furi_hal_bt_dump_state(string_t buffer) {
-    BleGlueStatus status = APPE_Status();
+    BleGlueStatus status = ble_glue_get_status();
     if (status == BleGlueStatusStarted) {
         uint8_t HCI_Version;
         uint16_t HCI_Revision;
@@ -68,56 +118,97 @@ void furi_hal_bt_dump_state(string_t buffer) {
 }
 
 bool furi_hal_bt_is_alive() {
-    BleGlueStatus status = APPE_Status();
-    return (status == BleGlueStatusBroken) || (status == BleGlueStatusStarted);
+    BleGlueStatus status = ble_glue_get_status();
+    return (status == BleGlueStatusBleStackMissing) || (status == BleGlueStatusStarted);
 }
 
 bool furi_hal_bt_is_active() {
     return gap_get_state() > GapStateIdle;
 }
 
-bool furi_hal_bt_wait_startup() {
-    uint16_t counter = 0;
-    while (!(APPE_Status() == BleGlueStatusStarted || APPE_Status() == BleGlueStatusBroken)) {
-        osDelay(10);
-        counter++;
-        if (counter > 1000) {
-            return false;
-        }
-    }
-    return true;
-}
-
-bool furi_hal_bt_lock_flash(bool erase_flag) {
-    if (!furi_hal_bt_wait_startup()) {
-        return false;
-    }
-    
+static void furi_hal_bt_lock_flash_core2(bool erase_flag) {
+    // Take flash controller ownership 
     while (HAL_HSEM_FastTake(CFG_HW_FLASH_SEMID) != HAL_OK) {
-        osDelay(1);
+        taskYIELD();
     }
 
+    // Unlock flash operation
     HAL_FLASH_Unlock();
 
+    // Erase activity notification
     if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON);
 
-    while(LL_FLASH_IsActiveFlag_OperationSuspended()) {
-        osDelay(1);
-    };
+    while(true) {
+        // Wait till flash controller become usable
+        while(LL_FLASH_IsActiveFlag_OperationSuspended()) {
+            taskYIELD();
+        };
 
-    __disable_irq();
+        // Just a little more love
+        taskENTER_CRITICAL();
 
-    return true;
+        // Actually we already have mutex for it, but specification is specification
+        if (HAL_HSEM_IsSemTaken(CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID)) {
+            taskEXIT_CRITICAL();
+            continue;
+        }
+
+        // Take sempahopre and prevent core2 from anyting funky
+        if (HAL_HSEM_FastTake(CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID) != HAL_OK) {
+            taskEXIT_CRITICAL();
+            continue;
+        }
+
+        break;
+    }
 }
 
-void furi_hal_bt_unlock_flash(bool erase_flag) {
-    __enable_irq();
+void furi_hal_bt_lock_flash(bool erase_flag) {
+    // Acquire dangerous ops mutex
+    osMutexAcquire(furi_hal_bt_core2_mtx, osWaitForever);
+
+    // If Core2 is running use IPC locking
+    BleGlueStatus status = ble_glue_get_status();
+    if(status == BleGlueStatusStarted || status == BleGlueStatusBleStackMissing) {
+        furi_hal_bt_lock_flash_core2(erase_flag);
+    } else { 
+        HAL_FLASH_Unlock();
+    }
+}
+
+static void furi_hal_bt_unlock_flash_core2(bool erase_flag) {
+    // Funky ops are ok at this point
+    HAL_HSEM_Release(CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID, 0);
+
+    // Task switching is ok
+    taskEXIT_CRITICAL();
 
+    // Doesn't make much sense, does it?
+    while (__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY)) {
+        taskYIELD();
+    }
+
+    // Erase activity over, core2 can continue
     if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF);
 
+    // Lock flash controller
     HAL_FLASH_Lock();
 
-    HAL_HSEM_Release(CFG_HW_FLASH_SEMID, HSEM_CPU1_COREID);
+    // Release flash controller ownership
+    HAL_HSEM_Release(CFG_HW_FLASH_SEMID, 0);
+}
+
+void furi_hal_bt_unlock_flash(bool erase_flag) {
+    // If Core2 is running use IPC locking
+    BleGlueStatus status = ble_glue_get_status();
+    if(status == BleGlueStatusStarted || status == BleGlueStatusBleStackMissing) {
+        furi_hal_bt_unlock_flash_core2(erase_flag);
+    } else { 
+        HAL_FLASH_Lock();
+    }
+
+    // Release dangerous ops mutex
+    osMutexRelease(furi_hal_bt_core2_mtx);
 }
 
 void furi_hal_bt_start_tone_tx(uint8_t channel, uint8_t power) {

+ 13 - 0
firmware/targets/f7/furi-hal/furi-hal-crypto.c

@@ -1,4 +1,5 @@
 #include <furi-hal-crypto.h>
+#include <furi-hal-bt.h>
 #include <furi.h>
 #include <shci.h>
 
@@ -12,6 +13,10 @@ bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot) {
     furi_assert(key);
     furi_assert(slot);
 
+    if(!furi_hal_bt_is_alive()) {
+        return false;
+    }
+
     SHCI_C2_FUS_StoreUsrKey_Cmd_Param_t pParam;
     size_t key_data_size = 0;
 
@@ -44,6 +49,10 @@ bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot) {
 bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) {
     furi_assert(slot > 0 && slot <= 100);
 
+    if(!furi_hal_bt_is_alive()) {
+        return false;
+    }
+
     crypt.Instance = AES1;
     crypt.Init.DataType = CRYP_DATATYPE_32B;
     crypt.Init.KeySize = CRYP_KEYSIZE_256B;
@@ -63,6 +72,10 @@ bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) {
 }
 
 bool furi_hal_crypto_store_unload_key(uint8_t slot) {
+    if(!furi_hal_bt_is_alive()) {
+        return false;
+    }
+
     furi_check(HAL_CRYP_DeInit(&crypt) == HAL_OK);
     return SHCI_C2_FUS_UnloadUsrKey(slot) == SHCI_Success;
 }

+ 32 - 16
firmware/targets/f7/furi-hal/furi-hal-flash.c

@@ -1,11 +1,14 @@
 #include <furi-hal-flash.h>
 #include <furi-hal-bt.h>
-#include <stm32wbxx.h>
 #include <furi.h>
 
+#include <stm32wbxx.h>
+
 /* Free flash space borders, exported by linker */
 extern const void __free_flash_start__;
 
+#define FURI_HAL_TAG "FuriHalFlash"
+#define FURI_HAL_CRITICAL_MSG "Critical flash operation fail"
 #define FURI_HAL_FLASH_READ_BLOCK 8
 #define FURI_HAL_FLASH_WRITE_BLOCK 8
 #define FURI_HAL_FLASH_PAGE_SIZE 4096
@@ -57,33 +60,46 @@ size_t furi_hal_flash_get_free_page_count() {
 }
 
 bool furi_hal_flash_erase(uint8_t page, uint8_t count) {
-    if (!furi_hal_bt_lock_flash(true)) {
-        return false;
-    }
+    furi_hal_bt_lock_flash(true);
+
     FLASH_EraseInitTypeDef erase;
     erase.TypeErase = FLASH_TYPEERASE_PAGES;
     erase.Page = page;
     erase.NbPages = count;
-    uint32_t error;
-    HAL_StatusTypeDef status = HAL_FLASHEx_Erase(&erase, &error);
+
+    uint32_t error_page = 0;
+    HAL_StatusTypeDef status = HAL_FLASHEx_Erase(&erase, &error_page);
+    if (status != HAL_OK) {
+        FURI_LOG_E(FURI_HAL_TAG, "Erase failed, ret: %d, page: %d", status, error_page);
+        furi_crash(FURI_HAL_CRITICAL_MSG);
+    }
+
     furi_hal_bt_unlock_flash(true);
-    return status == HAL_OK;
+
+    return true;
 }
 
 bool furi_hal_flash_write_dword(size_t address, uint64_t data) {
-    if (!furi_hal_bt_lock_flash(false)) {
-        return false;
-    }
+    furi_hal_bt_lock_flash(false);
+
     HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, data);
+    if (status != HAL_OK) {
+        FURI_LOG_E(FURI_HAL_TAG, "Programming failed, ret: %d, address: %p", status, address);
+        furi_crash(FURI_HAL_CRITICAL_MSG);
+    }
+
     furi_hal_bt_unlock_flash(false);
-    return status == HAL_OK;
+
+    return true;
 }
 
-bool furi_hal_flash_write_dword_from(size_t address, size_t source_address) {
-    if (!furi_hal_bt_lock_flash(false)) {
-        return false;
-    }
+bool furi_hal_flash_write_row(size_t address, size_t source_address) {
+    furi_hal_bt_lock_flash(false);
+
     HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FAST, address, source_address);
+    furi_check(status == HAL_OK);
+
     furi_hal_bt_unlock_flash(false);
-    return status == HAL_OK;
+
+    return true;
 }

+ 2 - 2
firmware/targets/f7/furi-hal/furi-hal-flash.h

@@ -80,7 +80,7 @@ bool furi_hal_flash_erase(uint8_t page, uint8_t count);
  */
 bool furi_hal_flash_write_dword(size_t address, uint64_t data);
 
-/** Write double word (64 bits) from address
+/** Write row: 64 double word (64 bits) from address
  *
  * Locking operation, uses HSEM to manage shared access.
  *
@@ -89,4 +89,4 @@ bool furi_hal_flash_write_dword(size_t address, uint64_t data);
  *
  * @return     true on success
  */
-bool furi_hal_flash_write_dword_from(size_t address, size_t source_address);
+bool furi_hal_flash_write_row(size_t address, size_t source_address);

+ 36 - 6
firmware/targets/furi-hal-include/furi-hal-bt.h

@@ -9,6 +9,9 @@
 #include <stdbool.h>
 #include <gap.h>
 #include <serial_service.h>
+#include <ble_glue.h>
+#include <ble_app.h>
+
 
 #define FURI_HAL_BT_PACKET_SIZE_MAX SERIAL_SVC_DATA_LEN_MAX
 
@@ -20,6 +23,12 @@ extern "C" {
  */
 void furi_hal_bt_init();
 
+/** Start 2nd core and BLE stack
+ *
+ * @return true on success
+ */
+bool furi_hal_bt_start_core2();
+
 /** Start BLE app
  * @param event_cb - BleEventCallback instance
  * @param context - pointer to context
@@ -52,6 +61,32 @@ void furi_hal_bt_dump_state(string_t buffer);
  */
 bool furi_hal_bt_is_alive();
 
+/** Get key storage buffer address and size
+ *
+ * @param       key_buff_addr   pointer to store buffer address
+ * @param       key_buff_size   pointer to store buffer size
+ *
+ * @return      true on success
+ */
+bool furi_hal_bt_get_key_storage_buff(uint8_t** key_buff_addr, uint16_t* key_buff_size);
+
+/** Get SRAM2 hardware semaphore
+ * @note Must be called before SRAM2 read/write operations
+ */
+void furi_hal_bt_nvm_sram_sem_acquire();
+
+/** Release SRAM2 hardware semaphore
+ * @note Must be called after SRAM2 read/write operations
+ */
+void furi_hal_bt_nvm_sram_sem_release();
+
+/** Set key storage change callback
+ *
+ * @param       callback    BleGlueKeyStorageChangedCallback instance
+ * @param       context     pointer to context
+ */
+void furi_hal_bt_set_key_storage_change_callback(BleGlueKeyStorageChangedCallback callback, void* context);
+
 /** Set data event callbacks
  * @param on_received_cb - SerialSvcDataReceivedCallback instance
  * @param on_sent_cb - SerialSvcDataSentCallback instance
@@ -65,16 +100,11 @@ void furi_hal_bt_set_data_event_callbacks(SerialSvcDataReceivedCallback on_recei
  */
 bool furi_hal_bt_tx(uint8_t* data, uint16_t size);
 
-/** Wait for Core2 startup */
-bool furi_hal_bt_wait_startup();
-
 /** Lock shared access to flash controller
  *
  * @param[in]  erase_flag  true if erase operation
- *
- * @return     true if lock was successful, false if not
  */
-bool furi_hal_bt_lock_flash(bool erase_flag);
+void furi_hal_bt_lock_flash(bool erase_flag);
 
 /** Unlock shared access to flash controller
  *

+ 187 - 0
lib/lfs_config.h

@@ -0,0 +1,187 @@
+#pragma once
+
+#include <furi.h>
+
+#ifdef FURI_NDEBUG
+#define LFS_NO_ASSERT
+#endif
+
+#define LFS_TAG "Lfs"
+
+#define LFS_TRACE(...) FURI_LOG_D(LFS_TAG, __VA_ARGS__);
+
+#define LFS_DEBUG(...) FURI_LOG_I(LFS_TAG, __VA_ARGS__);
+
+#define LFS_WARN(...) FURI_LOG_W(LFS_TAG, __VA_ARGS__);
+
+#define LFS_ERROR(...) FURI_LOG_E(LFS_TAG, __VA_ARGS__);
+
+#define LFS_ASSERT furi_assert
+
+// Because crc
+#undef LFS_CONFIG
+
+// System includes
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <inttypes.h>
+
+#ifndef LFS_NO_MALLOC
+#include <stdlib.h>
+#endif
+#ifndef LFS_NO_ASSERT
+#include <assert.h>
+#endif
+#if !defined(LFS_NO_DEBUG) || \
+        !defined(LFS_NO_WARN) || \
+        !defined(LFS_NO_ERROR) || \
+        defined(LFS_YES_TRACE)
+#include <stdio.h>
+#endif
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+// Builtin functions, these may be replaced by more efficient
+// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more
+// expensive basic C implementation for debugging purposes
+
+// Min/max functions for unsigned 32-bit numbers
+static inline uint32_t lfs_max(uint32_t a, uint32_t b) {
+    return (a > b) ? a : b;
+}
+
+static inline uint32_t lfs_min(uint32_t a, uint32_t b) {
+    return (a < b) ? a : b;
+}
+
+// Align to nearest multiple of a size
+static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) {
+    return a - (a % alignment);
+}
+
+static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) {
+    return lfs_aligndown(a + alignment-1, alignment);
+}
+
+// Find the smallest power of 2 greater than or equal to a
+static inline uint32_t lfs_npw2(uint32_t a) {
+#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
+    return 32 - __builtin_clz(a-1);
+#else
+    uint32_t r = 0;
+    uint32_t s;
+    a -= 1;
+    s = (a > 0xffff) << 4; a >>= s; r |= s;
+    s = (a > 0xff  ) << 3; a >>= s; r |= s;
+    s = (a > 0xf   ) << 2; a >>= s; r |= s;
+    s = (a > 0x3   ) << 1; a >>= s; r |= s;
+    return (r | (a >> 1)) + 1;
+#endif
+}
+
+// Count the number of trailing binary zeros in a
+// lfs_ctz(0) may be undefined
+static inline uint32_t lfs_ctz(uint32_t a) {
+#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__)
+    return __builtin_ctz(a);
+#else
+    return lfs_npw2((a & -a) + 1) - 1;
+#endif
+}
+
+// Count the number of binary ones in a
+static inline uint32_t lfs_popc(uint32_t a) {
+#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
+    return __builtin_popcount(a);
+#else
+    a = a - ((a >> 1) & 0x55555555);
+    a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
+    return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24;
+#endif
+}
+
+// Find the sequence comparison of a and b, this is the distance
+// between a and b ignoring overflow
+static inline int lfs_scmp(uint32_t a, uint32_t b) {
+    return (int)(unsigned)(a - b);
+}
+
+// Convert between 32-bit little-endian and native order
+static inline uint32_t lfs_fromle32(uint32_t a) {
+#if !defined(LFS_NO_INTRINSICS) && ( \
+    (defined(  BYTE_ORDER  ) && defined(  ORDER_LITTLE_ENDIAN  ) &&   BYTE_ORDER   ==   ORDER_LITTLE_ENDIAN  ) || \
+    (defined(__BYTE_ORDER  ) && defined(__ORDER_LITTLE_ENDIAN  ) && __BYTE_ORDER   == __ORDER_LITTLE_ENDIAN  ) || \
+    (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
+    return a;
+#elif !defined(LFS_NO_INTRINSICS) && ( \
+    (defined(  BYTE_ORDER  ) && defined(  ORDER_BIG_ENDIAN  ) &&   BYTE_ORDER   ==   ORDER_BIG_ENDIAN  ) || \
+    (defined(__BYTE_ORDER  ) && defined(__ORDER_BIG_ENDIAN  ) && __BYTE_ORDER   == __ORDER_BIG_ENDIAN  ) || \
+    (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
+    return __builtin_bswap32(a);
+#else
+    return (((uint8_t*)&a)[0] <<  0) |
+           (((uint8_t*)&a)[1] <<  8) |
+           (((uint8_t*)&a)[2] << 16) |
+           (((uint8_t*)&a)[3] << 24);
+#endif
+}
+
+static inline uint32_t lfs_tole32(uint32_t a) {
+    return lfs_fromle32(a);
+}
+
+// Convert between 32-bit big-endian and native order
+static inline uint32_t lfs_frombe32(uint32_t a) {
+#if !defined(LFS_NO_INTRINSICS) && ( \
+    (defined(  BYTE_ORDER  ) && defined(  ORDER_LITTLE_ENDIAN  ) &&   BYTE_ORDER   ==   ORDER_LITTLE_ENDIAN  ) || \
+    (defined(__BYTE_ORDER  ) && defined(__ORDER_LITTLE_ENDIAN  ) && __BYTE_ORDER   == __ORDER_LITTLE_ENDIAN  ) || \
+    (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
+    return __builtin_bswap32(a);
+#elif !defined(LFS_NO_INTRINSICS) && ( \
+    (defined(  BYTE_ORDER  ) && defined(  ORDER_BIG_ENDIAN  ) &&   BYTE_ORDER   ==   ORDER_BIG_ENDIAN  ) || \
+    (defined(__BYTE_ORDER  ) && defined(__ORDER_BIG_ENDIAN  ) && __BYTE_ORDER   == __ORDER_BIG_ENDIAN  ) || \
+    (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
+    return a;
+#else
+    return (((uint8_t*)&a)[0] << 24) |
+           (((uint8_t*)&a)[1] << 16) |
+           (((uint8_t*)&a)[2] <<  8) |
+           (((uint8_t*)&a)[3] <<  0);
+#endif
+}
+
+static inline uint32_t lfs_tobe32(uint32_t a) {
+    return lfs_frombe32(a);
+}
+
+// Calculate CRC-32 with polynomial = 0x04c11db7
+uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size);
+
+// Allocate memory, only used if buffers are not provided to littlefs
+// Note, memory must be 64-bit aligned
+static inline void *lfs_malloc(size_t size) {
+#ifndef LFS_NO_MALLOC
+    return malloc(size);
+#else
+    (void)size;
+    return NULL;
+#endif
+}
+
+// Deallocate memory, only used if buffers are not provided to littlefs
+static inline void lfs_free(void *p) {
+#ifndef LFS_NO_MALLOC
+    free(p);
+#else
+    (void)p;
+#endif
+}
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif

+ 1 - 1
lib/lib.mk

@@ -37,7 +37,7 @@ C_SOURCES		+= $(FATFS_DIR)/option/unicode.c
 
 # Little FS
 LITTLEFS_DIR	= $(LIB_DIR)/littlefs
-CFLAGS			+= -I$(LITTLEFS_DIR)
+CFLAGS			+= -I$(LITTLEFS_DIR) -DLFS_CONFIG=lfs_config.h
 C_SOURCES		+= $(LITTLEFS_DIR)/lfs.c
 C_SOURCES		+= $(LITTLEFS_DIR)/lfs_util.c
 

+ 3 - 3
make/toolchain.mk

@@ -18,11 +18,11 @@ BIN	= $(CP) -O binary -S
 DEBUG ?= 1
 COMPACT ?= 0
 ifeq ($(DEBUG), 1)
-CFLAGS += -DFURI_DEBUG -DNDEBUG -DLFS_NO_ASSERT -Og -g
+CFLAGS += -DFURI_DEBUG -DNDEBUG -Og -g
 else ifeq ($(COMPACT), 1)
-CFLAGS += -DFURI_NDEBUG -DNDEBUG -DLFS_NO_ASSERT -Os
+CFLAGS += -DFURI_NDEBUG -DNDEBUG -Os
 else
-CFLAGS += -DFURI_NDEBUG -DNDEBUG -DLFS_NO_ASSERT -Og
+CFLAGS += -DFURI_NDEBUG -DNDEBUG -Og
 endif
 
 CFLAGS		+= -fdata-sections -ffunction-sections -fno-math-errno -fstack-usage -MMD -MP -MF"$(@:%.o=%.d)"