[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 "bt_i.h" | ||||||
| #include "battery_service.h" | #include "battery_service.h" | ||||||
|  | #include "bt_keys_storage.h" | ||||||
| 
 | 
 | ||||||
| #define BT_SERVICE_TAG "BT" | #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) { | static void bt_statusbar_update(Bt* bt) { | ||||||
|     if(bt->status == BtStatusAdvertising) { |     if(bt->status == BtStatusAdvertising) { | ||||||
|         view_port_set_width(bt->statusbar_view_port, icon_get_width(&I_Bluetooth_5x8)); |         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(); |     Bt* bt = bt_alloc(); | ||||||
|     furi_record_create("bt", bt); |     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"); |         FURI_LOG_E(BT_SERVICE_TAG, "Core2 startup failed"); | ||||||
|     } else { |     } else { | ||||||
|         view_port_enabled_set(bt->statusbar_view_port, true); |         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_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
 |     // Update statusbar
 | ||||||
|     bt_statusbar_update(bt); |     bt_statusbar_update(bt); | ||||||
| 
 | 
 | ||||||
| @ -207,6 +223,8 @@ int32_t bt_srv() { | |||||||
|         } else if(message.type == BtMessageTypePinCodeShow) { |         } else if(message.type == BtMessageTypePinCodeShow) { | ||||||
|             // Display PIN code
 |             // Display PIN code
 | ||||||
|             bt_pin_code_show_event_handler(bt, message.data.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; |     return 0; | ||||||
|  | |||||||
| @ -25,6 +25,7 @@ typedef enum { | |||||||
|     BtMessageTypeUpdateStatusbar, |     BtMessageTypeUpdateStatusbar, | ||||||
|     BtMessageTypeUpdateBatteryLevel, |     BtMessageTypeUpdateBatteryLevel, | ||||||
|     BtMessageTypePinCodeShow, |     BtMessageTypePinCodeShow, | ||||||
|  |     BtMessageTypeKeysStorageUpdated, | ||||||
| } BtMessageType; | } BtMessageType; | ||||||
| 
 | 
 | ||||||
| typedef union { | typedef union { | ||||||
| @ -38,6 +39,8 @@ typedef struct { | |||||||
| } BtMessage; | } BtMessage; | ||||||
| 
 | 
 | ||||||
| struct Bt { | struct Bt { | ||||||
|  |     uint8_t* bt_keys_addr_start; | ||||||
|  |     uint16_t bt_keys_size; | ||||||
|     BtSettings bt_settings; |     BtSettings bt_settings; | ||||||
|     BtStatus status; |     BtStatus status; | ||||||
|     osMessageQueueId_t message_queue; |     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) { |     if(!loaded) { | ||||||
|         furi_hal_lock_set(false); |         furi_hal_lock_set(false); | ||||||
|         memset(&desktop->settings, 0, sizeof(desktop->settings)); |         memset(&desktop->settings, 0, sizeof(desktop->settings)); | ||||||
|         bool saved = SAVE_DESKTOP_SETTINGS(&desktop->settings); |         SAVE_DESKTOP_SETTINGS(&desktop->settings); | ||||||
|         furi_check(saved); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); |     scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain); | ||||||
|  | |||||||
| @ -4,9 +4,10 @@ | |||||||
| #include <math.h> | #include <math.h> | ||||||
| #include <toolbox/saved_struct.h> | #include <toolbox/saved_struct.h> | ||||||
| 
 | 
 | ||||||
| #define DOLPHIN_STORE_PATH "/int/dolphin.state" | #define DOLPHIN_STATE_TAG "DolphinState" | ||||||
| #define DOLPHIN_STORE_HEADER_MAGIC 0xD0 | #define DOLPHIN_STATE_PATH "/int/dolphin.state" | ||||||
| #define DOLPHIN_STORE_HEADER_VERSION 0x01 | #define DOLPHIN_STATE_HEADER_MAGIC 0xD0 | ||||||
|  | #define DOLPHIN_STATE_HEADER_VERSION 0x01 | ||||||
| #define DOLPHIN_LVL_THRESHOLD 20.0f | #define DOLPHIN_LVL_THRESHOLD 20.0f | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
| @ -35,28 +36,42 @@ void dolphin_state_free(DolphinState* dolphin_state) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool dolphin_state_save(DolphinState* dolphin_state) { | bool dolphin_state_save(DolphinState* dolphin_state) { | ||||||
|     return saved_struct_save( |     if(!dolphin_state->dirty) { | ||||||
|         DOLPHIN_STORE_PATH, |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool result = saved_struct_save( | ||||||
|  |         DOLPHIN_STATE_PATH, | ||||||
|         &dolphin_state->data, |         &dolphin_state->data, | ||||||
|         sizeof(DolphinStoreData), |         sizeof(DolphinStoreData), | ||||||
|         DOLPHIN_STORE_HEADER_MAGIC, |         DOLPHIN_STATE_HEADER_MAGIC, | ||||||
|         DOLPHIN_STORE_HEADER_VERSION); |         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 dolphin_state_load(DolphinState* dolphin_state) { | ||||||
|     bool loaded = saved_struct_load( |     bool loaded = saved_struct_load( | ||||||
|         DOLPHIN_STORE_PATH, |         DOLPHIN_STATE_PATH, | ||||||
|         &dolphin_state->data, |         &dolphin_state->data, | ||||||
|         sizeof(DolphinStoreData), |         sizeof(DolphinStoreData), | ||||||
|         DOLPHIN_STORE_HEADER_MAGIC, |         DOLPHIN_STATE_HEADER_MAGIC, | ||||||
|         DOLPHIN_STORE_HEADER_VERSION); |         DOLPHIN_STATE_HEADER_VERSION); | ||||||
|  | 
 | ||||||
|     if(!loaded) { |     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)); |         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() { | uint64_t dolphin_state_timestamp() { | ||||||
|  | |||||||
| @ -189,7 +189,7 @@ void gui_input(Gui* gui, InputEvent* input_event) { | |||||||
|     } else if(input_event->type == InputTypePress) { |     } else if(input_event->type == InputTypePress) { | ||||||
|         gui->ongoing_input |= key_bit; |         gui->ongoing_input |= key_bit; | ||||||
|     } else if(!(gui->ongoing_input & key_bit)) { |     } else if(!(gui->ongoing_input & key_bit)) { | ||||||
|         FURI_LOG_W( |         FURI_LOG_D( | ||||||
|             "Gui", |             "Gui", | ||||||
|             "non-complementary input, discarding key: %s type: %s, sequence: %p", |             "non-complementary input, discarding key: %s type: %s, sequence: %p", | ||||||
|             input_get_key_name(input_event->key), |             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) { |     if(view_port && view_port == gui->ongoing_input_view_port) { | ||||||
|         view_port_input(view_port, input_event); |         view_port_input(view_port, input_event); | ||||||
|     } else if(gui->ongoing_input_view_port && input_event->type == InputTypeRelease) { |     } else if(gui->ongoing_input_view_port && input_event->type == InputTypeRelease) { | ||||||
|         FURI_LOG_W( |         FURI_LOG_D( | ||||||
|             "Gui", |             "Gui", | ||||||
|             "ViewPort changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port", |             "ViewPort changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port", | ||||||
|             gui->ongoing_input_view_port, |             gui->ongoing_input_view_port, | ||||||
| @ -221,7 +221,7 @@ void gui_input(Gui* gui, InputEvent* input_event) { | |||||||
|             input_event->sequence); |             input_event->sequence); | ||||||
|         view_port_input(gui->ongoing_input_view_port, input_event); |         view_port_input(gui->ongoing_input_view_port, input_event); | ||||||
|     } else { |     } else { | ||||||
|         FURI_LOG_W( |         FURI_LOG_D( | ||||||
|             "Gui", |             "Gui", | ||||||
|             "ViewPort changed while key press %p -> %p. Discarding key: %s, type: %s, sequence: %p", |             "ViewPort changed while key press %p -> %p. Discarding key: %s, type: %s, sequence: %p", | ||||||
|             gui->ongoing_input_view_port, |             gui->ongoing_input_view_port, | ||||||
|  | |||||||
| @ -236,7 +236,7 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e | |||||||
|     } else if(event->type == InputTypeRelease) { |     } else if(event->type == InputTypeRelease) { | ||||||
|         view_dispatcher->ongoing_input &= ~key_bit; |         view_dispatcher->ongoing_input &= ~key_bit; | ||||||
|     } else if(!(view_dispatcher->ongoing_input & key_bit)) { |     } else if(!(view_dispatcher->ongoing_input & key_bit)) { | ||||||
|         FURI_LOG_W( |         FURI_LOG_D( | ||||||
|             "ViewDispatcher", |             "ViewDispatcher", | ||||||
|             "non-complementary input, discarding key: %s, type: %s, sequence: %p", |             "non-complementary input, discarding key: %s, type: %s, sequence: %p", | ||||||
|             input_get_key_name(event->key), |             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) { |     } else if(view_dispatcher->ongoing_input_view && event->type == InputTypeRelease) { | ||||||
|         FURI_LOG_W( |         FURI_LOG_D( | ||||||
|             "ViewDispatcher", |             "ViewDispatcher", | ||||||
|             "View changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port", |             "View changed while key press %p -> %p. Sending key: %s, type: %s, sequence: %p to previous view port", | ||||||
|             view_dispatcher->ongoing_input_view, |             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; |     LFSData* lfs_data = c->context; | ||||||
|     size_t page = lfs_data->start_page + block; |     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)) { |     if(furi_hal_flash_erase(page, 1)) { | ||||||
|         return 0; |         return 0; | ||||||
|  | |||||||
| @ -427,16 +427,5 @@ typedef enum | |||||||
| #define DBG_TRACE_MSG_QUEUE_SIZE 4096 | #define DBG_TRACE_MSG_QUEUE_SIZE 4096 | ||||||
| #define MAX_DBG_TRACE_MSG_SIZE 1024 | #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_BASE_ADDRESS    OTP_AREA_BASE | ||||||
| #define CFG_OTP_END_ADRESS      OTP_AREA_END_ADDR | #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" | #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_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 { | typedef struct { | ||||||
|     osMutexId_t hci_mtx; |     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); | static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status); | ||||||
| 
 | 
 | ||||||
| bool ble_app_init() { | bool ble_app_init() { | ||||||
|  |     SHCI_CmdStatus_t status; | ||||||
|     ble_app = furi_alloc(sizeof(BleApp)); |     ble_app = furi_alloc(sizeof(BleApp)); | ||||||
| 
 |  | ||||||
|     // Allocate semafore and mutex for ble command buffer access
 |     // Allocate semafore and mutex for ble command buffer access
 | ||||||
|     ble_app->hci_mtx = osMutexNew(NULL); |     ble_app->hci_mtx = osMutexNew(NULL); | ||||||
|     ble_app->hci_sem = osSemaphoreNew(1, 0, 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); |     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
 |     // Start ble stack on 2nd core
 | ||||||
|     SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { |     SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { | ||||||
|         .Header = {{0,0,0}}, // Header unused
 |         .Header = {{0,0,0}}, // Header unused
 | ||||||
| @ -67,13 +80,18 @@ bool ble_app_init() { | |||||||
|             0, |             0, | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     SHCI_CmdStatus_t status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); |     status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(BLE_APP_TAG, "Failed to start ble stack: %d", status); |         FURI_LOG_E(BLE_APP_TAG, "Failed to start ble stack: %d", status); | ||||||
|     } |     } | ||||||
|     return status == SHCI_Success; |     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) { | static void ble_app_hci_thread(void *arg) { | ||||||
|     while(1) { |     while(1) { | ||||||
|         osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever); |         osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever); | ||||||
|  | |||||||
| @ -5,8 +5,10 @@ extern "C" { | |||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
|  | #include <stdint.h> | ||||||
| 
 | 
 | ||||||
| bool ble_app_init(); | bool ble_app_init(); | ||||||
|  | void ble_app_get_key_storage_buff(uint8_t** addr, uint16_t* size); | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #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 "gap.h" | ||||||
| 
 | 
 | ||||||
| #include "app_entry.h" |  | ||||||
| #include "ble.h" | #include "ble.h" | ||||||
| 
 | 
 | ||||||
| #include "cmsis_os.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) { | bool gap_init(BleEventCallback on_event_cb, void* context) { | ||||||
|     if (APPE_Status() != BleGlueStatusStarted) { |     if (ble_glue_get_status() != BleGlueStatusStarted) { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -29,6 +29,37 @@ | |||||||
|  * Semaphores |  * Semaphores | ||||||
|  * THIS SHALL NO BE CHANGED AS THESE SEMAPHORES ARE USED AS WELL ON THE CM0+ |  * 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 | *  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 | *  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 <furi-hal-bt.h> | ||||||
| #include <app_entry.h> |  | ||||||
| #include <ble.h> | #include <ble.h> | ||||||
| #include <stm32wbxx.h> | #include <stm32wbxx.h> | ||||||
| #include <shci.h> | #include <shci.h> | ||||||
| @ -7,11 +6,37 @@ | |||||||
| 
 | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| 
 | 
 | ||||||
|  | osMutexId_t furi_hal_bt_core2_mtx = NULL; | ||||||
|  | 
 | ||||||
| void furi_hal_bt_init() { | 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
 |     // Explicitly tell that we are in charge of CLK48 domain
 | ||||||
|     HAL_HSEM_FastTake(CFG_HW_CLK48_CONFIG_SEMID); |     HAL_HSEM_FastTake(CFG_HW_CLK48_CONFIG_SEMID); | ||||||
|     // Start Core2, init HCI and start GAP/GATT
 |     // Start Core2
 | ||||||
|     APPE_Init(); |     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) { | 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); |     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) { | void furi_hal_bt_dump_state(string_t buffer) { | ||||||
|     BleGlueStatus status = APPE_Status(); |     BleGlueStatus status = ble_glue_get_status(); | ||||||
|     if (status == BleGlueStatusStarted) { |     if (status == BleGlueStatusStarted) { | ||||||
|         uint8_t HCI_Version; |         uint8_t HCI_Version; | ||||||
|         uint16_t HCI_Revision; |         uint16_t HCI_Revision; | ||||||
| @ -68,56 +118,97 @@ void furi_hal_bt_dump_state(string_t buffer) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool furi_hal_bt_is_alive() { | bool furi_hal_bt_is_alive() { | ||||||
|     BleGlueStatus status = APPE_Status(); |     BleGlueStatus status = ble_glue_get_status(); | ||||||
|     return (status == BleGlueStatusBroken) || (status == BleGlueStatusStarted); |     return (status == BleGlueStatusBleStackMissing) || (status == BleGlueStatusStarted); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool furi_hal_bt_is_active() { | bool furi_hal_bt_is_active() { | ||||||
|     return gap_get_state() > GapStateIdle; |     return gap_get_state() > GapStateIdle; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool furi_hal_bt_wait_startup() { | static void furi_hal_bt_lock_flash_core2(bool erase_flag) { | ||||||
|     uint16_t counter = 0; |     // Take flash controller ownership 
 | ||||||
|     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; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     while (HAL_HSEM_FastTake(CFG_HW_FLASH_SEMID) != HAL_OK) { |     while (HAL_HSEM_FastTake(CFG_HW_FLASH_SEMID) != HAL_OK) { | ||||||
|         osDelay(1); |         taskYIELD(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // Unlock flash operation
 | ||||||
|     HAL_FLASH_Unlock(); |     HAL_FLASH_Unlock(); | ||||||
| 
 | 
 | ||||||
|  |     // Erase activity notification
 | ||||||
|     if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON); |     if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON); | ||||||
| 
 | 
 | ||||||
|     while(LL_FLASH_IsActiveFlag_OperationSuspended()) { |     while(true) { | ||||||
|         osDelay(1); |         // Wait till flash controller become usable
 | ||||||
|     }; |         while(LL_FLASH_IsActiveFlag_OperationSuspended()) { | ||||||
|  |             taskYIELD(); | ||||||
|  |         }; | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |         // Just a little more love
 | ||||||
|  |         taskENTER_CRITICAL(); | ||||||
| 
 | 
 | ||||||
|     return true; |         // Actually we already have mutex for it, but specification is specification
 | ||||||
|  |         if (HAL_HSEM_IsSemTaken(CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID)) { | ||||||
|  |             taskEXIT_CRITICAL(); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Take sempahopre and prevent core2 from anyting funky
 | ||||||
|  |         if (HAL_HSEM_FastTake(CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID) != HAL_OK) { | ||||||
|  |             taskEXIT_CRITICAL(); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void furi_hal_bt_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) { | void furi_hal_bt_unlock_flash(bool erase_flag) { | ||||||
|     __enable_irq(); |     // 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(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF); |     // Release dangerous ops mutex
 | ||||||
| 
 |     osMutexRelease(furi_hal_bt_core2_mtx); | ||||||
|     HAL_FLASH_Lock(); |  | ||||||
| 
 |  | ||||||
|     HAL_HSEM_Release(CFG_HW_FLASH_SEMID, HSEM_CPU1_COREID); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_bt_start_tone_tx(uint8_t channel, uint8_t power) { | 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-crypto.h> | ||||||
|  | #include <furi-hal-bt.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <shci.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(key); | ||||||
|     furi_assert(slot); |     furi_assert(slot); | ||||||
| 
 | 
 | ||||||
|  |     if(!furi_hal_bt_is_alive()) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     SHCI_C2_FUS_StoreUsrKey_Cmd_Param_t pParam; |     SHCI_C2_FUS_StoreUsrKey_Cmd_Param_t pParam; | ||||||
|     size_t key_data_size = 0; |     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) { | bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) { | ||||||
|     furi_assert(slot > 0 && slot <= 100); |     furi_assert(slot > 0 && slot <= 100); | ||||||
| 
 | 
 | ||||||
|  |     if(!furi_hal_bt_is_alive()) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     crypt.Instance = AES1; |     crypt.Instance = AES1; | ||||||
|     crypt.Init.DataType = CRYP_DATATYPE_32B; |     crypt.Init.DataType = CRYP_DATATYPE_32B; | ||||||
|     crypt.Init.KeySize = CRYP_KEYSIZE_256B; |     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) { | 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); |     furi_check(HAL_CRYP_DeInit(&crypt) == HAL_OK); | ||||||
|     return SHCI_C2_FUS_UnloadUsrKey(slot) == SHCI_Success; |     return SHCI_C2_FUS_UnloadUsrKey(slot) == SHCI_Success; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,11 +1,14 @@ | |||||||
| #include <furi-hal-flash.h> | #include <furi-hal-flash.h> | ||||||
| #include <furi-hal-bt.h> | #include <furi-hal-bt.h> | ||||||
| #include <stm32wbxx.h> |  | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| 
 | 
 | ||||||
|  | #include <stm32wbxx.h> | ||||||
|  | 
 | ||||||
| /* Free flash space borders, exported by linker */ | /* Free flash space borders, exported by linker */ | ||||||
| extern const void __free_flash_start__; | 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_READ_BLOCK 8 | ||||||
| #define FURI_HAL_FLASH_WRITE_BLOCK 8 | #define FURI_HAL_FLASH_WRITE_BLOCK 8 | ||||||
| #define FURI_HAL_FLASH_PAGE_SIZE 4096 | #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) { | bool furi_hal_flash_erase(uint8_t page, uint8_t count) { | ||||||
|     if (!furi_hal_bt_lock_flash(true)) { |     furi_hal_bt_lock_flash(true); | ||||||
|         return false; | 
 | ||||||
|     } |  | ||||||
|     FLASH_EraseInitTypeDef erase; |     FLASH_EraseInitTypeDef erase; | ||||||
|     erase.TypeErase = FLASH_TYPEERASE_PAGES; |     erase.TypeErase = FLASH_TYPEERASE_PAGES; | ||||||
|     erase.Page = page; |     erase.Page = page; | ||||||
|     erase.NbPages = count; |     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); |     furi_hal_bt_unlock_flash(true); | ||||||
|     return status == HAL_OK; | 
 | ||||||
|  |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool furi_hal_flash_write_dword(size_t address, uint64_t data) { | bool furi_hal_flash_write_dword(size_t address, uint64_t data) { | ||||||
|     if (!furi_hal_bt_lock_flash(false)) { |     furi_hal_bt_lock_flash(false); | ||||||
|         return false; | 
 | ||||||
|     } |  | ||||||
|     HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, data); |     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); |     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) { | bool furi_hal_flash_write_row(size_t address, size_t source_address) { | ||||||
|     if (!furi_hal_bt_lock_flash(false)) { |     furi_hal_bt_lock_flash(false); | ||||||
|         return false; | 
 | ||||||
|     } |  | ||||||
|     HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FAST, address, source_address); |     HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FAST, address, source_address); | ||||||
|  |     furi_check(status == HAL_OK); | ||||||
|  | 
 | ||||||
|     furi_hal_bt_unlock_flash(false); |     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); | 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. |  * 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 |  * @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 DBG_TRACE_MSG_QUEUE_SIZE 4096 | ||||||
| #define MAX_DBG_TRACE_MSG_SIZE 1024 | #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_BASE_ADDRESS    OTP_AREA_BASE | ||||||
| #define CFG_OTP_END_ADRESS      OTP_AREA_END_ADDR | #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" | #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_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 { | typedef struct { | ||||||
|     osMutexId_t hci_mtx; |     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); | static void ble_app_hci_status_not_handler(HCI_TL_CmdStatus_t status); | ||||||
| 
 | 
 | ||||||
| bool ble_app_init() { | bool ble_app_init() { | ||||||
|  |     SHCI_CmdStatus_t status; | ||||||
|     ble_app = furi_alloc(sizeof(BleApp)); |     ble_app = furi_alloc(sizeof(BleApp)); | ||||||
| 
 |  | ||||||
|     // Allocate semafore and mutex for ble command buffer access
 |     // Allocate semafore and mutex for ble command buffer access
 | ||||||
|     ble_app->hci_mtx = osMutexNew(NULL); |     ble_app->hci_mtx = osMutexNew(NULL); | ||||||
|     ble_app->hci_sem = osSemaphoreNew(1, 0, 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); |     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
 |     // Start ble stack on 2nd core
 | ||||||
|     SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { |     SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { | ||||||
|         .Header = {{0,0,0}}, // Header unused
 |         .Header = {{0,0,0}}, // Header unused
 | ||||||
| @ -67,13 +80,18 @@ bool ble_app_init() { | |||||||
|             0, |             0, | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|     SHCI_CmdStatus_t status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); |     status = SHCI_C2_BLE_Init(&ble_init_cmd_packet); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(BLE_APP_TAG, "Failed to start ble stack: %d", status); |         FURI_LOG_E(BLE_APP_TAG, "Failed to start ble stack: %d", status); | ||||||
|     } |     } | ||||||
|     return status == SHCI_Success; |     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) { | static void ble_app_hci_thread(void *arg) { | ||||||
|     while(1) { |     while(1) { | ||||||
|         osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever); |         osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever); | ||||||
|  | |||||||
| @ -5,8 +5,10 @@ extern "C" { | |||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
|  | #include <stdint.h> | ||||||
| 
 | 
 | ||||||
| bool ble_app_init(); | bool ble_app_init(); | ||||||
|  | void ble_app_get_key_storage_buff(uint8_t** addr, uint16_t* size); | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #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 "gap.h" | ||||||
| 
 | 
 | ||||||
| #include "app_entry.h" |  | ||||||
| #include "ble.h" | #include "ble.h" | ||||||
| 
 | 
 | ||||||
| #include "cmsis_os.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) { | bool gap_init(BleEventCallback on_event_cb, void* context) { | ||||||
|     if (APPE_Status() != BleGlueStatusStarted) { |     if (ble_glue_get_status() != BleGlueStatusStarted) { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -29,6 +29,37 @@ | |||||||
|  * Semaphores |  * Semaphores | ||||||
|  * THIS SHALL NO BE CHANGED AS THESE SEMAPHORES ARE USED AS WELL ON THE CM0+ |  * 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 | *  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 | *  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 <furi-hal-bt.h> | ||||||
| #include <app_entry.h> |  | ||||||
| #include <ble.h> | #include <ble.h> | ||||||
| #include <stm32wbxx.h> | #include <stm32wbxx.h> | ||||||
| #include <shci.h> | #include <shci.h> | ||||||
| @ -7,11 +6,37 @@ | |||||||
| 
 | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| 
 | 
 | ||||||
|  | osMutexId_t furi_hal_bt_core2_mtx = NULL; | ||||||
|  | 
 | ||||||
| void furi_hal_bt_init() { | 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
 |     // Explicitly tell that we are in charge of CLK48 domain
 | ||||||
|     HAL_HSEM_FastTake(CFG_HW_CLK48_CONFIG_SEMID); |     HAL_HSEM_FastTake(CFG_HW_CLK48_CONFIG_SEMID); | ||||||
|     // Start Core2, init HCI and start GAP/GATT
 |     // Start Core2
 | ||||||
|     APPE_Init(); |     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) { | 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); |     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) { | void furi_hal_bt_dump_state(string_t buffer) { | ||||||
|     BleGlueStatus status = APPE_Status(); |     BleGlueStatus status = ble_glue_get_status(); | ||||||
|     if (status == BleGlueStatusStarted) { |     if (status == BleGlueStatusStarted) { | ||||||
|         uint8_t HCI_Version; |         uint8_t HCI_Version; | ||||||
|         uint16_t HCI_Revision; |         uint16_t HCI_Revision; | ||||||
| @ -68,56 +118,97 @@ void furi_hal_bt_dump_state(string_t buffer) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool furi_hal_bt_is_alive() { | bool furi_hal_bt_is_alive() { | ||||||
|     BleGlueStatus status = APPE_Status(); |     BleGlueStatus status = ble_glue_get_status(); | ||||||
|     return (status == BleGlueStatusBroken) || (status == BleGlueStatusStarted); |     return (status == BleGlueStatusBleStackMissing) || (status == BleGlueStatusStarted); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool furi_hal_bt_is_active() { | bool furi_hal_bt_is_active() { | ||||||
|     return gap_get_state() > GapStateIdle; |     return gap_get_state() > GapStateIdle; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool furi_hal_bt_wait_startup() { | static void furi_hal_bt_lock_flash_core2(bool erase_flag) { | ||||||
|     uint16_t counter = 0; |     // Take flash controller ownership 
 | ||||||
|     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; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     while (HAL_HSEM_FastTake(CFG_HW_FLASH_SEMID) != HAL_OK) { |     while (HAL_HSEM_FastTake(CFG_HW_FLASH_SEMID) != HAL_OK) { | ||||||
|         osDelay(1); |         taskYIELD(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // Unlock flash operation
 | ||||||
|     HAL_FLASH_Unlock(); |     HAL_FLASH_Unlock(); | ||||||
| 
 | 
 | ||||||
|  |     // Erase activity notification
 | ||||||
|     if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON); |     if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON); | ||||||
| 
 | 
 | ||||||
|     while(LL_FLASH_IsActiveFlag_OperationSuspended()) { |     while(true) { | ||||||
|         osDelay(1); |         // Wait till flash controller become usable
 | ||||||
|     }; |         while(LL_FLASH_IsActiveFlag_OperationSuspended()) { | ||||||
|  |             taskYIELD(); | ||||||
|  |         }; | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |         // Just a little more love
 | ||||||
|  |         taskENTER_CRITICAL(); | ||||||
| 
 | 
 | ||||||
|     return true; |         // Actually we already have mutex for it, but specification is specification
 | ||||||
|  |         if (HAL_HSEM_IsSemTaken(CFG_HW_BLOCK_FLASH_REQ_BY_CPU1_SEMID)) { | ||||||
|  |             taskEXIT_CRITICAL(); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Take sempahopre and prevent core2 from anyting funky
 | ||||||
|  |         if (HAL_HSEM_FastTake(CFG_HW_BLOCK_FLASH_REQ_BY_CPU2_SEMID) != HAL_OK) { | ||||||
|  |             taskEXIT_CRITICAL(); | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void furi_hal_bt_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) { | void furi_hal_bt_unlock_flash(bool erase_flag) { | ||||||
|     __enable_irq(); |     // 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(); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     if(erase_flag) SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF); |     // Release dangerous ops mutex
 | ||||||
| 
 |     osMutexRelease(furi_hal_bt_core2_mtx); | ||||||
|     HAL_FLASH_Lock(); |  | ||||||
| 
 |  | ||||||
|     HAL_HSEM_Release(CFG_HW_FLASH_SEMID, HSEM_CPU1_COREID); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_bt_start_tone_tx(uint8_t channel, uint8_t power) { | 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-crypto.h> | ||||||
|  | #include <furi-hal-bt.h> | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| #include <shci.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(key); | ||||||
|     furi_assert(slot); |     furi_assert(slot); | ||||||
| 
 | 
 | ||||||
|  |     if(!furi_hal_bt_is_alive()) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     SHCI_C2_FUS_StoreUsrKey_Cmd_Param_t pParam; |     SHCI_C2_FUS_StoreUsrKey_Cmd_Param_t pParam; | ||||||
|     size_t key_data_size = 0; |     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) { | bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) { | ||||||
|     furi_assert(slot > 0 && slot <= 100); |     furi_assert(slot > 0 && slot <= 100); | ||||||
| 
 | 
 | ||||||
|  |     if(!furi_hal_bt_is_alive()) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     crypt.Instance = AES1; |     crypt.Instance = AES1; | ||||||
|     crypt.Init.DataType = CRYP_DATATYPE_32B; |     crypt.Init.DataType = CRYP_DATATYPE_32B; | ||||||
|     crypt.Init.KeySize = CRYP_KEYSIZE_256B; |     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) { | 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); |     furi_check(HAL_CRYP_DeInit(&crypt) == HAL_OK); | ||||||
|     return SHCI_C2_FUS_UnloadUsrKey(slot) == SHCI_Success; |     return SHCI_C2_FUS_UnloadUsrKey(slot) == SHCI_Success; | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,11 +1,14 @@ | |||||||
| #include <furi-hal-flash.h> | #include <furi-hal-flash.h> | ||||||
| #include <furi-hal-bt.h> | #include <furi-hal-bt.h> | ||||||
| #include <stm32wbxx.h> |  | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| 
 | 
 | ||||||
|  | #include <stm32wbxx.h> | ||||||
|  | 
 | ||||||
| /* Free flash space borders, exported by linker */ | /* Free flash space borders, exported by linker */ | ||||||
| extern const void __free_flash_start__; | 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_READ_BLOCK 8 | ||||||
| #define FURI_HAL_FLASH_WRITE_BLOCK 8 | #define FURI_HAL_FLASH_WRITE_BLOCK 8 | ||||||
| #define FURI_HAL_FLASH_PAGE_SIZE 4096 | #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) { | bool furi_hal_flash_erase(uint8_t page, uint8_t count) { | ||||||
|     if (!furi_hal_bt_lock_flash(true)) { |     furi_hal_bt_lock_flash(true); | ||||||
|         return false; | 
 | ||||||
|     } |  | ||||||
|     FLASH_EraseInitTypeDef erase; |     FLASH_EraseInitTypeDef erase; | ||||||
|     erase.TypeErase = FLASH_TYPEERASE_PAGES; |     erase.TypeErase = FLASH_TYPEERASE_PAGES; | ||||||
|     erase.Page = page; |     erase.Page = page; | ||||||
|     erase.NbPages = count; |     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); |     furi_hal_bt_unlock_flash(true); | ||||||
|     return status == HAL_OK; | 
 | ||||||
|  |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool furi_hal_flash_write_dword(size_t address, uint64_t data) { | bool furi_hal_flash_write_dword(size_t address, uint64_t data) { | ||||||
|     if (!furi_hal_bt_lock_flash(false)) { |     furi_hal_bt_lock_flash(false); | ||||||
|         return false; | 
 | ||||||
|     } |  | ||||||
|     HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, data); |     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); |     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) { | bool furi_hal_flash_write_row(size_t address, size_t source_address) { | ||||||
|     if (!furi_hal_bt_lock_flash(false)) { |     furi_hal_bt_lock_flash(false); | ||||||
|         return false; | 
 | ||||||
|     } |  | ||||||
|     HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FAST, address, source_address); |     HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FAST, address, source_address); | ||||||
|  |     furi_check(status == HAL_OK); | ||||||
|  | 
 | ||||||
|     furi_hal_bt_unlock_flash(false); |     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); | 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. |  * 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 |  * @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 <stdbool.h> | ||||||
| #include <gap.h> | #include <gap.h> | ||||||
| #include <serial_service.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 | #define FURI_HAL_BT_PACKET_SIZE_MAX SERIAL_SVC_DATA_LEN_MAX | ||||||
| 
 | 
 | ||||||
| @ -20,6 +23,12 @@ extern "C" { | |||||||
|  */ |  */ | ||||||
| void furi_hal_bt_init(); | void furi_hal_bt_init(); | ||||||
| 
 | 
 | ||||||
|  | /** Start 2nd core and BLE stack
 | ||||||
|  |  * | ||||||
|  |  * @return true on success | ||||||
|  |  */ | ||||||
|  | bool furi_hal_bt_start_core2(); | ||||||
|  | 
 | ||||||
| /** Start BLE app
 | /** Start BLE app
 | ||||||
|  * @param event_cb - BleEventCallback instance |  * @param event_cb - BleEventCallback instance | ||||||
|  * @param context - pointer to context |  * @param context - pointer to context | ||||||
| @ -52,6 +61,32 @@ void furi_hal_bt_dump_state(string_t buffer); | |||||||
|  */ |  */ | ||||||
| bool furi_hal_bt_is_alive(); | 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
 | /** Set data event callbacks
 | ||||||
|  * @param on_received_cb - SerialSvcDataReceivedCallback instance |  * @param on_received_cb - SerialSvcDataReceivedCallback instance | ||||||
|  * @param on_sent_cb - SerialSvcDataSentCallback 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); | 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
 | /** Lock shared access to flash controller
 | ||||||
|  * |  * | ||||||
|  * @param[in]  erase_flag  true if erase operation |  * @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
 | /** 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
 | # Little FS
 | ||||||
| LITTLEFS_DIR	= $(LIB_DIR)/littlefs | 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.c | ||||||
| C_SOURCES		+= $(LITTLEFS_DIR)/lfs_util.c | C_SOURCES		+= $(LITTLEFS_DIR)/lfs_util.c | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -18,11 +18,11 @@ BIN	= $(CP) -O binary -S | |||||||
| DEBUG ?= 1 | DEBUG ?= 1 | ||||||
| COMPACT ?= 0 | COMPACT ?= 0 | ||||||
| ifeq ($(DEBUG), 1) | ifeq ($(DEBUG), 1) | ||||||
| CFLAGS += -DFURI_DEBUG -DNDEBUG -DLFS_NO_ASSERT -Og -g | CFLAGS += -DFURI_DEBUG -DNDEBUG -Og -g | ||||||
| else ifeq ($(COMPACT), 1) | else ifeq ($(COMPACT), 1) | ||||||
| CFLAGS += -DFURI_NDEBUG -DNDEBUG -DLFS_NO_ASSERT -Os | CFLAGS += -DFURI_NDEBUG -DNDEBUG -Os | ||||||
| else | else | ||||||
| CFLAGS += -DFURI_NDEBUG -DNDEBUG -DLFS_NO_ASSERT -Og | CFLAGS += -DFURI_NDEBUG -DNDEBUG -Og | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
| CFLAGS		+= -fdata-sections -ffunction-sections -fno-math-errno -fstack-usage -MMD -MP -MF"$(@:%.o=%.d)" | 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