[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>
This commit is contained in:
		
							parent
							
								
									bb9c464a13
								
							
						
					
					
						commit
						3225f40870
					
				| @ -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; | ||||
|  | ||||
| @ -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
									
								
								applications/bt/bt_service/bt_keys_storage.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								applications/bt/bt_service/bt_keys_storage.c
									
									
									
									
									
										Normal file
									
								
							| @ -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
									
								
								applications/bt/bt_service/bt_keys_storage.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								applications/bt/bt_service/bt_keys_storage.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "bt_i.h" | ||||
| 
 | ||||
| bool bt_load_key_storage(Bt* bt); | ||||
| 
 | ||||
| bool bt_save_key_storage(Bt* bt); | ||||
| @ -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); | ||||
|  | ||||
| @ -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() { | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 ); | ||||
| } | ||||
| @ -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 | ||||
| @ -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); | ||||
|  | ||||
| @ -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
									
								
								firmware/targets/f6/ble-glue/ble_glue.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								firmware/targets/f6/ble-glue/ble_glue.c
									
									
									
									
									
										Normal file
									
								
							| @ -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
									
								
								firmware/targets/f6/ble-glue/ble_glue.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								firmware/targets/f6/ble-glue/ble_glue.h
									
									
									
									
									
										Normal file
									
								
							| @ -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,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; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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(true) { | ||||
|         // Wait till flash controller become usable
 | ||||
|         while(LL_FLASH_IsActiveFlag_OperationSuspended()) { | ||||
|         osDelay(1); | ||||
|             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_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(); | ||||
| 
 | ||||
|     // Release flash controller ownership
 | ||||
|     HAL_HSEM_Release(CFG_HW_FLASH_SEMID, 0); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_bt_unlock_flash(bool erase_flag) { | ||||
|     __enable_irq(); | ||||
| 
 | ||||
|     if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF); | ||||
| 
 | ||||
|     // 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(); | ||||
|     } | ||||
| 
 | ||||
|     HAL_HSEM_Release(CFG_HW_FLASH_SEMID, HSEM_CPU1_COREID); | ||||
|     // Release dangerous ops mutex
 | ||||
|     osMutexRelease(furi_hal_bt_core2_mtx); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_bt_start_tone_tx(uint8_t channel, uint8_t power) { | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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 ); | ||||
| } | ||||
| @ -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 | ||||
| @ -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); | ||||
|  | ||||
| @ -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
									
								
								firmware/targets/f7/ble-glue/ble_glue.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								firmware/targets/f7/ble-glue/ble_glue.c
									
									
									
									
									
										Normal file
									
								
							| @ -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
									
								
								firmware/targets/f7/ble-glue/ble_glue.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								firmware/targets/f7/ble-glue/ble_glue.h
									
									
									
									
									
										Normal file
									
								
							| @ -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,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; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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(true) { | ||||
|         // Wait till flash controller become usable
 | ||||
|         while(LL_FLASH_IsActiveFlag_OperationSuspended()) { | ||||
|         osDelay(1); | ||||
|             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_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(); | ||||
| 
 | ||||
|     // Release flash controller ownership
 | ||||
|     HAL_HSEM_Release(CFG_HW_FLASH_SEMID, 0); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_bt_unlock_flash(bool erase_flag) { | ||||
|     __enable_irq(); | ||||
| 
 | ||||
|     if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF); | ||||
| 
 | ||||
|     // 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(); | ||||
|     } | ||||
| 
 | ||||
|     HAL_HSEM_Release(CFG_HW_FLASH_SEMID, HSEM_CPU1_COREID); | ||||
|     // Release dangerous ops mutex
 | ||||
|     osMutexRelease(furi_hal_bt_core2_mtx); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_bt_start_tone_tx(uint8_t channel, uint8_t power) { | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -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
									
								
								lib/lfs_config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								lib/lfs_config.h
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||
| @ -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 | ||||
| 
 | ||||
|  | ||||
| @ -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)" | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 gornekich
						gornekich