[FL-1795] BLE GAP refactoring (#694)
* ble: remove heart rate profile * ble-glue: delete dead code * ble-glue: dis refactoring * ble-glue: add battery service * broken ble_common refactoring * ble-glue: advertise 128 bit service uid * ble-glue: remove dead code * ble: advertise service 16 bit uid depending on flipper color * ble-glue: remove debug * ble: intriduce serial service * ble: serial over ble * bt: serial echo server * bt: serial service process indicate acknowledge * bt: serial service event handler update * bt: refactore battery service * bt: add battery level apdate API * power: update battery level on change * bt: refactore device information service * app_ble: pairing configuration * bt: display pin code * bt: refactor battery service * bt: refactor device info service * bt: change advertise timer to freertos one * bt: separate app_ble to hci and gap * bt: increase max ble packet size * gap: refactoring * bt: refactor serial service * bt: support f7 target * bt: not blocking pin code show request Co-authored-by: Anna Prosvetova <anna@prosvetova.me> Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									4456982e27
								
							
						
					
					
						commit
						95d9140d24
					
				| @ -22,6 +22,17 @@ static ViewPort* bt_statusbar_view_port_alloc() { | |||||||
|     return statusbar_view_port; |     return statusbar_view_port; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void bt_pin_code_show_event_handler(Bt* bt, uint32_t pin) { | ||||||
|  |     furi_assert(bt); | ||||||
|  |     string_t pin_str; | ||||||
|  |     string_init_printf(pin_str, "%06d", pin); | ||||||
|  |     dialog_message_set_text( | ||||||
|  |         bt->dialog_message, string_get_cstr(pin_str), 64, 32, AlignCenter, AlignCenter); | ||||||
|  |     dialog_message_set_buttons(bt->dialog_message, "Back", NULL, NULL); | ||||||
|  |     dialog_message_show(bt->dialogs, bt->dialog_message); | ||||||
|  |     string_clear(pin_str); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Bt* bt_alloc() { | Bt* bt_alloc() { | ||||||
|     Bt* bt = furi_alloc(sizeof(Bt)); |     Bt* bt = furi_alloc(sizeof(Bt)); | ||||||
|     // Load settings
 |     // Load settings
 | ||||||
| @ -41,13 +52,11 @@ Bt* bt_alloc() { | |||||||
|     bt->gui = furi_record_open("gui"); |     bt->gui = furi_record_open("gui"); | ||||||
|     gui_add_view_port(bt->gui, bt->statusbar_view_port, GuiLayerStatusBarLeft); |     gui_add_view_port(bt->gui, bt->statusbar_view_port, GuiLayerStatusBarLeft); | ||||||
| 
 | 
 | ||||||
|     return bt; |     // Dialogs
 | ||||||
| } |     bt->dialogs = furi_record_open("dialogs"); | ||||||
|  |     bt->dialog_message = dialog_message_alloc(); | ||||||
| 
 | 
 | ||||||
| bool bt_update_battery_level(Bt* bt, uint8_t battery_level) { |     return bt; | ||||||
|     BtMessage message = { |  | ||||||
|         .type = BtMessageTypeUpdateBatteryLevel, .data.battery_level = battery_level}; |  | ||||||
|     return osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int32_t bt_srv() { | int32_t bt_srv() { | ||||||
| @ -76,9 +85,13 @@ int32_t bt_srv() { | |||||||
|             // Update statusbar
 |             // Update statusbar
 | ||||||
|             view_port_enabled_set(bt->statusbar_view_port, furi_hal_bt_is_alive()); |             view_port_enabled_set(bt->statusbar_view_port, furi_hal_bt_is_alive()); | ||||||
|         } else if(message.type == BtMessageTypeUpdateBatteryLevel) { |         } else if(message.type == BtMessageTypeUpdateBatteryLevel) { | ||||||
|  |             // Update battery level
 | ||||||
|             if(furi_hal_bt_is_alive()) { |             if(furi_hal_bt_is_alive()) { | ||||||
|                 battery_svc_update_level(message.data.battery_level); |                 battery_svc_update_level(message.data.battery_level); | ||||||
|             } |             } | ||||||
|  |         } else if(message.type == BtMessageTypePinCodeShow) { | ||||||
|  |             // Display PIN code
 | ||||||
|  |             bt_pin_code_show_event_handler(bt, message.data.pin_code); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return 0; |     return 0; | ||||||
|  | |||||||
| @ -9,7 +9,9 @@ extern "C" { | |||||||
| 
 | 
 | ||||||
| typedef struct Bt Bt; | typedef struct Bt Bt; | ||||||
| 
 | 
 | ||||||
| bool bt_update_battery_level(Bt* bt, uint8_t battery_level); | void bt_update_battery_level(Bt* bt, uint8_t battery_level); | ||||||
|  | 
 | ||||||
|  | bool bt_pin_code_show(Bt* bt, uint32_t pin_code); | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										15
									
								
								applications/bt/bt_service/bt_api.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										15
									
								
								applications/bt/bt_service/bt_api.c
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,15 @@ | |||||||
|  | #include "bt.h" | ||||||
|  | #include "bt_i.h" | ||||||
|  | 
 | ||||||
|  | void bt_update_battery_level(Bt* bt, uint8_t battery_level) { | ||||||
|  |     furi_assert(bt); | ||||||
|  |     BtMessage message = { | ||||||
|  |         .type = BtMessageTypeUpdateBatteryLevel, .data.battery_level = battery_level}; | ||||||
|  |     furi_check(osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool bt_pin_code_show(Bt* bt, uint32_t pin_code) { | ||||||
|  |     furi_assert(bt); | ||||||
|  |     BtMessage message = {.type = BtMessageTypePinCodeShow, .data.pin_code = pin_code}; | ||||||
|  |     return osMessageQueuePut(bt->message_queue, &message, 0, 0) == osOK; | ||||||
|  | } | ||||||
| @ -9,14 +9,18 @@ | |||||||
| #include <gui/view_port.h> | #include <gui/view_port.h> | ||||||
| #include <gui/view.h> | #include <gui/view.h> | ||||||
| 
 | 
 | ||||||
|  | #include <applications/dialogs/dialogs.h> | ||||||
|  | 
 | ||||||
| #include "../bt_settings.h" | #include "../bt_settings.h" | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     BtMessageTypeUpdateStatusbar, |     BtMessageTypeUpdateStatusbar, | ||||||
|     BtMessageTypeUpdateBatteryLevel, |     BtMessageTypeUpdateBatteryLevel, | ||||||
|  |     BtMessageTypePinCodeShow, | ||||||
| } BtMessageType; | } BtMessageType; | ||||||
| 
 | 
 | ||||||
| typedef union { | typedef union { | ||||||
|  |     uint32_t pin_code; | ||||||
|     uint8_t battery_level; |     uint8_t battery_level; | ||||||
| } BtMessageData; | } BtMessageData; | ||||||
| 
 | 
 | ||||||
| @ -31,4 +35,6 @@ struct Bt { | |||||||
|     osTimerId_t update_status_timer; |     osTimerId_t update_status_timer; | ||||||
|     Gui* gui; |     Gui* gui; | ||||||
|     ViewPort* statusbar_view_port; |     ViewPort* statusbar_view_port; | ||||||
|  |     DialogsApp* dialogs; | ||||||
|  |     DialogMessage* dialog_message; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -6,106 +6,20 @@ | |||||||
| #include "ble.h" | #include "ble.h" | ||||||
| #include "tl.h" | #include "tl.h" | ||||||
| #include "app_ble.h" | #include "app_ble.h" | ||||||
| 
 |  | ||||||
| #include "cmsis_os.h" |  | ||||||
| #include "shci.h" | #include "shci.h" | ||||||
| #include "otp.h" | #include "cmsis_os.h" | ||||||
| #include "dev_info_service.h" |  | ||||||
| #include "battery_service.h" |  | ||||||
| #include "serial_service.h" |  | ||||||
| 
 | 
 | ||||||
| #include <furi-hal.h> | #include <furi-hal.h> | ||||||
| 
 | 
 | ||||||
| typedef struct _tSecurityParams { |  | ||||||
|   uint8_t ioCapability; |  | ||||||
|   uint8_t mitm_mode; |  | ||||||
|   uint8_t bonding_mode; |  | ||||||
|   uint8_t Use_Fixed_Pin; |  | ||||||
|   uint8_t encryptionKeySizeMin; |  | ||||||
|   uint8_t encryptionKeySizeMax; |  | ||||||
|   uint32_t Fixed_Pin; |  | ||||||
|   uint8_t initiateSecurity; |  | ||||||
| } tSecurityParams; |  | ||||||
| 
 |  | ||||||
| typedef struct _tBLEProfileGlobalContext { |  | ||||||
|   tSecurityParams bleSecurityParam; |  | ||||||
|   uint16_t gapServiceHandle; |  | ||||||
|   uint16_t devNameCharHandle; |  | ||||||
|   uint16_t appearanceCharHandle; |  | ||||||
|   uint16_t connectionHandle; |  | ||||||
|   uint8_t advtServUUIDlen; |  | ||||||
|   uint8_t advtServUUID[100]; |  | ||||||
| } BleGlobalContext_t; |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|   BleGlobalContext_t BleApplicationContext_legacy; |  | ||||||
|   APP_BLE_ConnStatus_t Device_Connection_Status; |  | ||||||
|   uint8_t Advertising_mgr_timer_Id; |  | ||||||
| } BleApplicationContext_t; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| #define FAST_ADV_TIMEOUT               (30*1000*1000/CFG_TS_TICK_VAL) /**< 30s */ |  | ||||||
| #define INITIAL_ADV_TIMEOUT            (60*1000*1000/CFG_TS_TICK_VAL) /**< 60s */ |  | ||||||
| 
 |  | ||||||
| #define BD_ADDR_SIZE_LOCAL    6 |  | ||||||
| 
 |  | ||||||
| #define LED_ON_TIMEOUT                 (0.005*1000*1000/CFG_TS_TICK_VAL) /**< 5ms */ |  | ||||||
| 
 |  | ||||||
| PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer; | PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer; | ||||||
| 
 | 
 | ||||||
| static const uint8_t M_bd_addr[BD_ADDR_SIZE_LOCAL] = | // PLACE_IN_SECTION("TAG_OTA_END") const uint32_t MagicKeywordValue = 0x94448A29 ;
 | ||||||
|     { | // PLACE_IN_SECTION("TAG_OTA_START") const uint32_t MagicKeywordAddress = (uint32_t)&MagicKeywordValue;
 | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0x0000000000FF)), |  | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0x00000000FF00) >> 8), |  | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0x000000FF0000) >> 16), |  | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0x0000FF000000) >> 24), |  | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0x00FF00000000) >> 32), |  | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0xFF0000000000) >> 40) |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
| static uint8_t bd_addr_udn[BD_ADDR_SIZE_LOCAL]; |  | ||||||
| 
 |  | ||||||
| static const uint8_t BLE_CFG_IR_VALUE[16] = CFG_BLE_IRK; |  | ||||||
| static const uint8_t BLE_CFG_ER_VALUE[16] = CFG_BLE_ERK; |  | ||||||
| 
 |  | ||||||
| PLACE_IN_SECTION("TAG_OTA_END") const uint32_t MagicKeywordValue = 0x94448A29 ; |  | ||||||
| PLACE_IN_SECTION("TAG_OTA_START") const uint32_t MagicKeywordAddress = (uint32_t)&MagicKeywordValue; |  | ||||||
| 
 |  | ||||||
| PLACE_IN_SECTION("BLE_APP_CONTEXT") static BleApplicationContext_t BleApplicationContext; |  | ||||||
| PLACE_IN_SECTION("BLE_APP_CONTEXT") static uint16_t AdvIntervalMin, AdvIntervalMax; |  | ||||||
| 
 |  | ||||||
| uint8_t  manuf_data[14] = { |  | ||||||
|     sizeof(manuf_data)-1, AD_TYPE_MANUFACTURER_SPECIFIC_DATA, |  | ||||||
|     0x01/*SKD version */, |  | ||||||
|     0x00 /* Generic*/, |  | ||||||
|     0x00 /* GROUP A Feature  */, |  | ||||||
|     0x00 /* GROUP A Feature */, |  | ||||||
|     0x00 /* GROUP B Feature */, |  | ||||||
|     0x00 /* GROUP B Feature */, |  | ||||||
|     0x00, /* BLE MAC start -MSB */ |  | ||||||
|     0x00, |  | ||||||
|     0x00, |  | ||||||
|     0x00, |  | ||||||
|     0x00, |  | ||||||
|     0x00, /* BLE MAC stop */ |  | ||||||
| 
 |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| osMutexId_t MtxHciId; | osMutexId_t MtxHciId; | ||||||
| osSemaphoreId_t SemHciId; | osSemaphoreId_t SemHciId; | ||||||
| osThreadId_t AdvUpdateProcessId; |  | ||||||
| osThreadId_t HciUserEvtProcessId; | osThreadId_t HciUserEvtProcessId; | ||||||
| 
 | 
 | ||||||
| const osThreadAttr_t AdvUpdateProcess_attr = { |  | ||||||
|     .name = CFG_ADV_UPDATE_PROCESS_NAME, |  | ||||||
|     .attr_bits = CFG_ADV_UPDATE_PROCESS_ATTR_BITS, |  | ||||||
|     .cb_mem = CFG_ADV_UPDATE_PROCESS_CB_MEM, |  | ||||||
|     .cb_size = CFG_ADV_UPDATE_PROCESS_CB_SIZE, |  | ||||||
|     .stack_mem = CFG_ADV_UPDATE_PROCESS_STACK_MEM, |  | ||||||
|     .priority = CFG_ADV_UPDATE_PROCESS_PRIORITY, |  | ||||||
|     .stack_size = CFG_ADV_UPDATE_PROCESS_STACK_SIZE |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const osThreadAttr_t HciUserEvtProcess_attr = { | const osThreadAttr_t HciUserEvtProcess_attr = { | ||||||
|     .name = CFG_HCI_USER_EVT_PROCESS_NAME, |     .name = CFG_HCI_USER_EVT_PROCESS_NAME, | ||||||
|     .attr_bits = CFG_HCI_USER_EVT_PROCESS_ATTR_BITS, |     .attr_bits = CFG_HCI_USER_EVT_PROCESS_ATTR_BITS, | ||||||
| @ -121,13 +35,6 @@ static void HciUserEvtProcess(void *argument); | |||||||
| static void BLE_UserEvtRx( void * pPayload ); | static void BLE_UserEvtRx( void * pPayload ); | ||||||
| static void BLE_StatusNot( HCI_TL_CmdStatus_t status ); | static void BLE_StatusNot( HCI_TL_CmdStatus_t status ); | ||||||
| static void Ble_Tl_Init( void ); | static void Ble_Tl_Init( void ); | ||||||
| static void Ble_Hci_Gap_Gatt_Init(); |  | ||||||
| static const uint8_t* BleGetBdAddress( void ); |  | ||||||
| static void Adv_Request( APP_BLE_ConnStatus_t New_Status ); |  | ||||||
| static void Adv_Mgr( void ); |  | ||||||
| static void AdvUpdateProcess(void *argument); |  | ||||||
| static void Adv_Update( void ); |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| bool APP_BLE_Init() { | bool APP_BLE_Init() { | ||||||
|   SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { |   SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { | ||||||
| @ -160,254 +67,6 @@ bool APP_BLE_Init() { | |||||||
|   return (SHCI_C2_BLE_Init( &ble_init_cmd_packet ) == SHCI_Success); |   return (SHCI_C2_BLE_Init( &ble_init_cmd_packet ) == SHCI_Success); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void set_advertisment_service_uid(uint8_t* uid, uint8_t uin_len); |  | ||||||
| 
 |  | ||||||
| bool APP_BLE_Start() { |  | ||||||
|   if (APPE_Status() != BleGlueStatusStarted) { |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|   // Initialization of HCI & GATT & GAP layer
 |  | ||||||
|   Ble_Hci_Gap_Gatt_Init(); |  | ||||||
|   // Initialization of the BLE Services
 |  | ||||||
|   SVCCTL_Init(); |  | ||||||
|   // Initialization of the BLE App Context
 |  | ||||||
|   BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0xFFFF; |  | ||||||
|   // From here, all initialization are BLE application specific
 |  | ||||||
|   AdvUpdateProcessId = osThreadNew(AdvUpdateProcess, NULL, &AdvUpdateProcess_attr); |  | ||||||
| 
 |  | ||||||
|   // Initialization of ADV - Ad Manufacturer Element - Support OTA Bit Masks
 |  | ||||||
| #if(BLE_CFG_OTA_REBOOT_CHAR != 0) |  | ||||||
|   manuf_data[sizeof(manuf_data)-8] = CFG_FEATURE_OTA_REBOOT; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
|   // Initialize DIS Application
 |  | ||||||
|   dev_info_service_init(); |  | ||||||
|   // Initialize BAS Application
 |  | ||||||
|   battery_svc_init(); |  | ||||||
|   // Initialize Serial application
 |  | ||||||
|   serial_svc_init(); |  | ||||||
|   // Create timer to handle the connection state machine
 |  | ||||||
|   HW_TS_Create(CFG_TIM_PROC_ID_ISR, &(BleApplicationContext.Advertising_mgr_timer_Id), hw_ts_SingleShot, Adv_Mgr); |  | ||||||
|   uint8_t adv_service_uid[2]; |  | ||||||
|   adv_service_uid[0] = 0x80 | furi_hal_version_get_hw_color(); |  | ||||||
|   adv_service_uid[1] = 0x30; |  | ||||||
| 
 |  | ||||||
|   set_advertisment_service_uid(adv_service_uid, sizeof(adv_service_uid)); |  | ||||||
|   /* Initialize intervals for reconnexion without intervals update */ |  | ||||||
|   AdvIntervalMin = CFG_FAST_CONN_ADV_INTERVAL_MIN; |  | ||||||
|   AdvIntervalMax = CFG_FAST_CONN_ADV_INTERVAL_MAX; |  | ||||||
| 
 |  | ||||||
|   Adv_Request(APP_BLE_FAST_ADV); |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SVCCTL_SvcInit() { |  | ||||||
|     // Dummy function to prevent unused services initialization
 |  | ||||||
|     // TODO refactore
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt ) |  | ||||||
| { |  | ||||||
|   hci_event_pckt *event_pckt; |  | ||||||
|   evt_le_meta_event *meta_evt; |  | ||||||
|   evt_blue_aci *blue_evt; |  | ||||||
|   hci_le_phy_update_complete_event_rp0 *evt_le_phy_update_complete; |  | ||||||
|   uint8_t TX_PHY, RX_PHY; |  | ||||||
|   tBleStatus ret = BLE_STATUS_INVALID_PARAMS; |  | ||||||
| 
 |  | ||||||
|   event_pckt = (hci_event_pckt*) ((hci_uart_pckt *) pckt)->data; |  | ||||||
| 
 |  | ||||||
|   switch (event_pckt->evt) { |  | ||||||
|     case EVT_DISCONN_COMPLETE: |  | ||||||
|     { |  | ||||||
|       hci_disconnection_complete_event_rp0 *disconnection_complete_event; |  | ||||||
|       disconnection_complete_event = (hci_disconnection_complete_event_rp0 *) event_pckt->data; |  | ||||||
| 
 |  | ||||||
|       if (disconnection_complete_event->Connection_Handle == BleApplicationContext.BleApplicationContext_legacy.connectionHandle) { |  | ||||||
|         BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0; |  | ||||||
|         BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE; |  | ||||||
|         APP_DBG_MSG("\r\n\r** DISCONNECTION EVENT WITH CLIENT \r\n"); |  | ||||||
|       } |  | ||||||
|       /* restart advertising */ |  | ||||||
|       Adv_Request(APP_BLE_FAST_ADV); |  | ||||||
|       furi_hal_power_insomnia_exit(); |  | ||||||
|     } |  | ||||||
|     break; /* EVT_DISCONN_COMPLETE */ |  | ||||||
| 
 |  | ||||||
|     case EVT_LE_META_EVENT: |  | ||||||
|     { |  | ||||||
|       meta_evt = (evt_le_meta_event*) event_pckt->data; |  | ||||||
|       switch (meta_evt->subevent) |  | ||||||
|       { |  | ||||||
|         case EVT_LE_CONN_UPDATE_COMPLETE: |  | ||||||
|           APP_DBG_MSG("\r\n\r** CONNECTION UPDATE EVENT WITH CLIENT \r\n"); |  | ||||||
| 
 |  | ||||||
|           /* USER CODE BEGIN EVT_LE_CONN_UPDATE_COMPLETE */ |  | ||||||
| 
 |  | ||||||
|           /* USER CODE END EVT_LE_CONN_UPDATE_COMPLETE */ |  | ||||||
|           break; |  | ||||||
|         case EVT_LE_PHY_UPDATE_COMPLETE: |  | ||||||
|           APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE \r\n"); |  | ||||||
|           evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data; |  | ||||||
|           if (evt_le_phy_update_complete->Status == 0) |  | ||||||
|           { |  | ||||||
|             APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status ok \r\n"); |  | ||||||
|           } |  | ||||||
|           else |  | ||||||
|           { |  | ||||||
|             APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status nok \r\n"); |  | ||||||
|           } |  | ||||||
| 
 |  | ||||||
|           ret = hci_le_read_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,&TX_PHY,&RX_PHY); |  | ||||||
|           if (ret == BLE_STATUS_SUCCESS) |  | ||||||
|           { |  | ||||||
|             APP_DBG_MSG("Read_PHY success \r\n"); |  | ||||||
| 
 |  | ||||||
|             if ((TX_PHY == TX_2M) && (RX_PHY == RX_2M)) |  | ||||||
|             { |  | ||||||
|               APP_DBG_MSG("PHY Param  TX= %d, RX= %d \r\n", TX_PHY, RX_PHY); |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|               APP_DBG_MSG("PHY Param  TX= %d, RX= %d \r\n", TX_PHY, RX_PHY); |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|           else |  | ||||||
|           { |  | ||||||
|             APP_DBG_MSG("Read conf not succeess \r\n"); |  | ||||||
|           } |  | ||||||
|           break; |  | ||||||
|         case EVT_LE_CONN_COMPLETE: |  | ||||||
|         { |  | ||||||
|           furi_hal_power_insomnia_enter(); |  | ||||||
|           hci_le_connection_complete_event_rp0 *connection_complete_event; |  | ||||||
| 
 |  | ||||||
|           /**
 |  | ||||||
|            * The connection is done, there is no need anymore to schedule the LP ADV |  | ||||||
|            */ |  | ||||||
|           connection_complete_event = (hci_le_connection_complete_event_rp0 *) meta_evt->data; |  | ||||||
| 
 |  | ||||||
|           HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id); |  | ||||||
| 
 |  | ||||||
|           APP_DBG_MSG("EVT_LE_CONN_COMPLETE for connection handle 0x%x\r\n", connection_complete_event->Connection_Handle); |  | ||||||
|           if (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_CONNECTING) |  | ||||||
|           { |  | ||||||
|             /* Connection as client */ |  | ||||||
|             BleApplicationContext.Device_Connection_Status = APP_BLE_CONNECTED_CLIENT; |  | ||||||
|           } |  | ||||||
|           else |  | ||||||
|           { |  | ||||||
|             /* Connection as server */ |  | ||||||
|             BleApplicationContext.Device_Connection_Status = APP_BLE_CONNECTED_SERVER; |  | ||||||
|           } |  | ||||||
|           BleApplicationContext.BleApplicationContext_legacy.connectionHandle = connection_complete_event->Connection_Handle; |  | ||||||
|         } |  | ||||||
|         break; /* HCI_EVT_LE_CONN_COMPLETE */ |  | ||||||
|         default: |  | ||||||
|           break; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     break; /* HCI_EVT_LE_META_EVENT */ |  | ||||||
| 
 |  | ||||||
|     case EVT_VENDOR: |  | ||||||
|       blue_evt = (evt_blue_aci*) event_pckt->data; |  | ||||||
|       switch (blue_evt->ecode) { |  | ||||||
|         aci_gap_pairing_complete_event_rp0 *pairing_complete; |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_LIMITED_DISCOVERABLE:  |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_LIMITED_DISCOVERABLE \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_LIMITED_DISCOVERABLE */ |  | ||||||
|            |  | ||||||
|       case EVT_BLUE_GAP_PASS_KEY_REQUEST:   |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PASS_KEY_REQUEST \r\n"); |  | ||||||
| 
 |  | ||||||
|         aci_gap_pass_key_resp(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,123456); |  | ||||||
| 
 |  | ||||||
|         APP_DBG_MSG("\r\n\r** aci_gap_pass_key_resp \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_PASS_KEY_REQUEST */ |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_AUTHORIZATION_REQUEST:     |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_AUTHORIZATION_REQUEST \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_AUTHORIZATION_REQUEST */ |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED:    |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED */ |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_BOND_LOST:     |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_BOND_LOST \r\n"); |  | ||||||
|           aci_gap_allow_rebond(BleApplicationContext.BleApplicationContext_legacy.connectionHandle); |  | ||||||
|         APP_DBG_MSG("\r\n\r** Send allow rebond \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_BOND_LOST */ |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_DEVICE_FOUND:   |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_DEVICE_FOUND */ |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_ADDR_NOT_RESOLVED: |  | ||||||
|          APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_DEVICE_FOUND */ |  | ||||||
|        |  | ||||||
|       case (EVT_BLUE_GAP_KEYPRESS_NOTIFICATION): |  | ||||||
|          APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_KEYPRESS_NOTIFICATION \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_KEY_PRESS_NOTIFICATION */     |  | ||||||
| 
 |  | ||||||
|        case (EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE): |  | ||||||
|           APP_DBG_MSG("numeric_value = %ld\r\n", |  | ||||||
|                       ((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value); |  | ||||||
| 
 |  | ||||||
|           APP_DBG_MSG("Hex_value = %lx\r\n", |  | ||||||
|                       ((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value); |  | ||||||
| 
 |  | ||||||
|           aci_gap_numeric_comparison_value_confirm_yesno(BleApplicationContext.BleApplicationContext_legacy.connectionHandle, 1); /* CONFIRM_YES = 1 */ |  | ||||||
| 
 |  | ||||||
|           APP_DBG_MSG("\r\n\r** aci_gap_numeric_comparison_value_confirm_yesno-->YES \r\n"); |  | ||||||
|           break; |  | ||||||
| 
 |  | ||||||
|         case (EVT_BLUE_GAP_PAIRING_CMPLT): |  | ||||||
|           { |  | ||||||
|             pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data; |  | ||||||
| 
 |  | ||||||
|             APP_DBG_MSG("BLE_CTRL_App_Notification: EVT_BLUE_GAP_PAIRING_CMPLT, pairing_complete->Status = %d\r\n",pairing_complete->Status); |  | ||||||
|             if (pairing_complete->Status == 0) { |  | ||||||
|               APP_DBG_MSG("\r\n\r** Pairing OK \r\n"); |  | ||||||
|             } else { |  | ||||||
|               APP_DBG_MSG("\r\n\r** Pairing KO \r\n"); |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|           break; |  | ||||||
| 
 |  | ||||||
|       /* USER CODE END ecode */ |  | ||||||
|         case EVT_BLUE_GAP_PROCEDURE_COMPLETE: |  | ||||||
|           APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PROCEDURE_COMPLETE \r\n"); |  | ||||||
|           break; |  | ||||||
|       } |  | ||||||
|       break; /* EVT_VENDOR */ |  | ||||||
|       default: |  | ||||||
|         break; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   return (SVCCTL_UserEvtFlowEnable); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void set_advertisment_service_uid(uint8_t* uid, uint8_t uid_len) { |  | ||||||
|     BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen = 1; |  | ||||||
|     if(uid_len == 2) { |  | ||||||
|         BleApplicationContext.BleApplicationContext_legacy.advtServUUID[0] = AD_TYPE_16_BIT_SERV_UUID; |  | ||||||
|     } else if (uid_len == 4) { |  | ||||||
|         BleApplicationContext.BleApplicationContext_legacy.advtServUUID[0] = AD_TYPE_32_BIT_SERV_UUID; |  | ||||||
|     } else if(uid_len == 16) { |  | ||||||
|         BleApplicationContext.BleApplicationContext_legacy.advtServUUID[0] = AD_TYPE_128_BIT_SERV_UUID_CMPLT_LIST; |  | ||||||
|     } |  | ||||||
|     memcpy(&BleApplicationContext.BleApplicationContext_legacy.advtServUUID[1], uid, uid_len); |  | ||||||
|     BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen += uid_len; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status() { |  | ||||||
|     return BleApplicationContext.Device_Connection_Status; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void Ble_Tl_Init( void ) { | static void Ble_Tl_Init( void ) { | ||||||
|   HCI_TL_HciInitConf_t Hci_Tl_Init_Conf; |   HCI_TL_HciInitConf_t Hci_Tl_Init_Conf; | ||||||
| 
 | 
 | ||||||
| @ -419,301 +78,6 @@ static void Ble_Tl_Init( void ) { | |||||||
|   hci_init(BLE_UserEvtRx, (void*) &Hci_Tl_Init_Conf); |   hci_init(BLE_UserEvtRx, (void*) &Hci_Tl_Init_Conf); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void Ble_Hci_Gap_Gatt_Init() { |  | ||||||
|   uint8_t role; |  | ||||||
|   uint16_t gap_service_handle, gap_dev_name_char_handle, gap_appearance_char_handle; |  | ||||||
|   const uint8_t *bd_addr; |  | ||||||
|   uint32_t srd_bd_addr[2]; |  | ||||||
|   uint16_t appearance[1] = { BLE_CFG_GAP_APPEARANCE }; |  | ||||||
| 
 |  | ||||||
|   /*HCI Reset to synchronise BLE Stack*/ |  | ||||||
|   hci_reset(); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Write the BD Address |  | ||||||
|    */ |  | ||||||
|   bd_addr = BleGetBdAddress(); |  | ||||||
|   aci_hal_write_config_data(CONFIG_DATA_PUBADDR_OFFSET, |  | ||||||
|                             CONFIG_DATA_PUBADDR_LEN, |  | ||||||
|                             (uint8_t*) bd_addr); |  | ||||||
| 
 |  | ||||||
|   /* BLE MAC in ADV Packet */ |  | ||||||
|   manuf_data[ sizeof(manuf_data)-6] = bd_addr[5]; |  | ||||||
|   manuf_data[ sizeof(manuf_data)-5] = bd_addr[4]; |  | ||||||
|   manuf_data[ sizeof(manuf_data)-4] = bd_addr[3]; |  | ||||||
|   manuf_data[ sizeof(manuf_data)-3] = bd_addr[2]; |  | ||||||
|   manuf_data[ sizeof(manuf_data)-2] = bd_addr[1]; |  | ||||||
|   manuf_data[ sizeof(manuf_data)-1] = bd_addr[0]; |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Write Identity root key used to derive LTK and CSRK |  | ||||||
|    */ |  | ||||||
|     aci_hal_write_config_data(CONFIG_DATA_IR_OFFSET, |  | ||||||
|     CONFIG_DATA_IR_LEN, |  | ||||||
|                             (uint8_t*) BLE_CFG_IR_VALUE); |  | ||||||
| 
 |  | ||||||
|    /**
 |  | ||||||
|    * Write Encryption root key used to derive LTK and CSRK |  | ||||||
|    */ |  | ||||||
|     aci_hal_write_config_data(CONFIG_DATA_ER_OFFSET, |  | ||||||
|     CONFIG_DATA_ER_LEN, |  | ||||||
|                             (uint8_t*) BLE_CFG_ER_VALUE); |  | ||||||
| 
 |  | ||||||
|    /**
 |  | ||||||
|    * Write random bd_address |  | ||||||
|    */ |  | ||||||
|    /* random_bd_address = R_bd_address;
 |  | ||||||
|     aci_hal_write_config_data(CONFIG_DATA_RANDOM_ADDRESS_WR, |  | ||||||
|     CONFIG_DATA_RANDOM_ADDRESS_LEN, |  | ||||||
|                             (uint8_t*) random_bd_address); |  | ||||||
|   */ |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Static random Address |  | ||||||
|    * The two upper bits shall be set to 1 |  | ||||||
|    * The lowest 32bits is read from the UDN to differentiate between devices |  | ||||||
|    * The RNG may be used to provide a random number on each power on |  | ||||||
|    */ |  | ||||||
|   srd_bd_addr[1] =  0x0000ED6E; |  | ||||||
|   srd_bd_addr[0] =  LL_FLASH_GetUDN( ); |  | ||||||
|   aci_hal_write_config_data( CONFIG_DATA_RANDOM_ADDRESS_OFFSET, CONFIG_DATA_RANDOM_ADDRESS_LEN, (uint8_t*)srd_bd_addr ); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Write Identity root key used to derive LTK and CSRK |  | ||||||
|    */ |  | ||||||
|     aci_hal_write_config_data( CONFIG_DATA_IR_OFFSET, CONFIG_DATA_IR_LEN, (uint8_t*)BLE_CFG_IR_VALUE ); |  | ||||||
| 
 |  | ||||||
|    /**
 |  | ||||||
|    * Write Encryption root key used to derive LTK and CSRK |  | ||||||
|    */ |  | ||||||
|     aci_hal_write_config_data( CONFIG_DATA_ER_OFFSET, CONFIG_DATA_ER_LEN, (uint8_t*)BLE_CFG_ER_VALUE ); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Set TX Power to 0dBm. |  | ||||||
|    */ |  | ||||||
|   aci_hal_set_tx_power_level(1, CFG_TX_POWER); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Initialize GATT interface |  | ||||||
|    */ |  | ||||||
|   aci_gatt_init(); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Initialize GAP interface |  | ||||||
|    */ |  | ||||||
|   role = 0; |  | ||||||
| 
 |  | ||||||
| #if (BLE_CFG_PERIPHERAL == 1) |  | ||||||
|   role |= GAP_PERIPHERAL_ROLE; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #if (BLE_CFG_CENTRAL == 1) |  | ||||||
|   role |= GAP_CENTRAL_ROLE; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
|   if (role > 0) |  | ||||||
|   { |  | ||||||
|     const char *name = furi_hal_version_get_device_name_ptr(); |  | ||||||
|     aci_gap_init(role, 0, |  | ||||||
|                  strlen(name), |  | ||||||
|                  &gap_service_handle, &gap_dev_name_char_handle, &gap_appearance_char_handle); |  | ||||||
| 
 |  | ||||||
|     if (aci_gatt_update_char_value(gap_service_handle, gap_dev_name_char_handle, 0, strlen(name), (uint8_t *) name)) |  | ||||||
|     { |  | ||||||
|       BLE_DBG_SVCCTL_MSG("Device Name aci_gatt_update_char_value failed.\r\n"); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   if(aci_gatt_update_char_value(gap_service_handle, |  | ||||||
|                                 gap_appearance_char_handle, |  | ||||||
|                                 0, |  | ||||||
|                                 2, |  | ||||||
|                                 (uint8_t *)&appearance)) |  | ||||||
|   { |  | ||||||
|     BLE_DBG_SVCCTL_MSG("Appearance aci_gatt_update_char_value failed.\r\n"); |  | ||||||
|   } |  | ||||||
|   /**
 |  | ||||||
|    * Initialize Default PHY |  | ||||||
|    */ |  | ||||||
|   hci_le_set_default_phy(ALL_PHYS_PREFERENCE,TX_2M_PREFERRED,RX_2M_PREFERRED); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Initialize IO capability |  | ||||||
|    */ |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.ioCapability = CFG_IO_CAPABILITY; |  | ||||||
|   aci_gap_set_io_capability(BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.ioCapability); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Initialize authentication |  | ||||||
|    */ |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.mitm_mode = CFG_MITM_PROTECTION; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMin = CFG_ENCRYPTION_KEY_SIZE_MIN; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMax = CFG_ENCRYPTION_KEY_SIZE_MAX; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Use_Fixed_Pin = CFG_USED_FIXED_PIN; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Fixed_Pin = CFG_FIXED_PIN; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode = CFG_BONDING_MODE; |  | ||||||
| 
 |  | ||||||
|   aci_gap_set_authentication_requirement(BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode, |  | ||||||
|                                          BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.mitm_mode, |  | ||||||
|                                          CFG_SC_SUPPORT, |  | ||||||
|                                          CFG_KEYPRESS_NOTIFICATION_SUPPORT, |  | ||||||
|                                          BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMin, |  | ||||||
|                                          BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMax, |  | ||||||
|                                          BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Use_Fixed_Pin, |  | ||||||
|                                          BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Fixed_Pin, |  | ||||||
|                                          PUBLIC_ADDR |  | ||||||
|                                          ); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Initialize whitelist |  | ||||||
|    */ |  | ||||||
|    if (BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode) |  | ||||||
|    { |  | ||||||
|      aci_gap_configure_whitelist(); |  | ||||||
|    } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void Adv_Request(APP_BLE_ConnStatus_t New_Status) |  | ||||||
| { |  | ||||||
|   tBleStatus ret = BLE_STATUS_INVALID_PARAMS; |  | ||||||
|   uint16_t Min_Inter, Max_Inter; |  | ||||||
| 
 |  | ||||||
|   if (New_Status == APP_BLE_FAST_ADV) |  | ||||||
|   { |  | ||||||
|     Min_Inter = AdvIntervalMin; |  | ||||||
|     Max_Inter = AdvIntervalMax; |  | ||||||
|   } |  | ||||||
|   else |  | ||||||
|   { |  | ||||||
|     Min_Inter = CFG_LP_CONN_ADV_INTERVAL_MIN; |  | ||||||
|     Max_Inter = CFG_LP_CONN_ADV_INTERVAL_MAX; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Stop the timer, it will be restarted for a new shot |  | ||||||
|      * It does not hurt if the timer was not running |  | ||||||
|      */ |  | ||||||
|     HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id); |  | ||||||
| 
 |  | ||||||
|     APP_DBG_MSG("First index in %d state \r\n", BleApplicationContext.Device_Connection_Status); |  | ||||||
| 
 |  | ||||||
|     if ((New_Status == APP_BLE_LP_ADV) |  | ||||||
|         && ((BleApplicationContext.Device_Connection_Status == APP_BLE_FAST_ADV) |  | ||||||
|             || (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_ADV))) |  | ||||||
|     { |  | ||||||
|       /* Connection in ADVERTISE mode have to stop the current advertising */ |  | ||||||
|       ret = aci_gap_set_non_discoverable(); |  | ||||||
|       if (ret == BLE_STATUS_SUCCESS) |  | ||||||
|       { |  | ||||||
|         APP_DBG_MSG("Successfully Stopped Advertising \r\n"); |  | ||||||
|       } |  | ||||||
|       else |  | ||||||
|       { |  | ||||||
|         APP_DBG_MSG("Stop Advertising Failed , result: %d \r\n", ret); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     BleApplicationContext.Device_Connection_Status = New_Status; |  | ||||||
| 
 |  | ||||||
|     const char* name = furi_hal_version_get_ble_local_device_name_ptr(); |  | ||||||
| 
 |  | ||||||
|     /* Start Fast or Low Power Advertising */ |  | ||||||
|     ret = aci_gap_set_discoverable( |  | ||||||
|         ADV_IND, |  | ||||||
|         Min_Inter, |  | ||||||
|         Max_Inter, |  | ||||||
|         PUBLIC_ADDR, |  | ||||||
|         NO_WHITE_LIST_USE, /* use white list */ |  | ||||||
|         strlen(name), |  | ||||||
|         (uint8_t*)name, |  | ||||||
|         BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen, |  | ||||||
|         BleApplicationContext.BleApplicationContext_legacy.advtServUUID, |  | ||||||
|         0, |  | ||||||
|         0); |  | ||||||
|     if(ret) { |  | ||||||
|       FURI_LOG_E("APP ble", "Set discoverable err: %d", ret); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Update Advertising data */ |  | ||||||
|     ret = aci_gap_update_adv_data(sizeof(manuf_data), (uint8_t*) manuf_data); |  | ||||||
|     if (ret == BLE_STATUS_SUCCESS) { |  | ||||||
|       if (New_Status == APP_BLE_FAST_ADV) { |  | ||||||
|         APP_DBG_MSG("Successfully Start Fast Advertising \r\n" ); |  | ||||||
|         /* Start Timer to STOP ADV - TIMEOUT */ |  | ||||||
|         HW_TS_Start(BleApplicationContext.Advertising_mgr_timer_Id, INITIAL_ADV_TIMEOUT); |  | ||||||
|       } else { |  | ||||||
|         APP_DBG_MSG("Successfully Start Low Power Advertising \r\n"); |  | ||||||
|       } |  | ||||||
|     } else { |  | ||||||
|       if (New_Status == APP_BLE_FAST_ADV) { |  | ||||||
|         APP_DBG_MSG("Start Fast Advertising Failed , result: %d \r\n", ret); |  | ||||||
|       } else { |  | ||||||
|         APP_DBG_MSG("Start Low Power Advertising Failed , result: %d \r\n", ret); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const uint8_t* BleGetBdAddress( void ) { |  | ||||||
|   uint8_t *otp_addr; |  | ||||||
|   const uint8_t *bd_addr; |  | ||||||
|   uint32_t udn; |  | ||||||
|   uint32_t company_id; |  | ||||||
|   uint32_t device_id; |  | ||||||
| 
 |  | ||||||
|   udn = LL_FLASH_GetUDN(); |  | ||||||
| 
 |  | ||||||
|   if(udn != 0xFFFFFFFF) { |  | ||||||
|     company_id = LL_FLASH_GetSTCompanyID(); |  | ||||||
|     device_id = LL_FLASH_GetDeviceID(); |  | ||||||
| 
 |  | ||||||
|     bd_addr_udn[0] = (uint8_t)(udn & 0x000000FF); |  | ||||||
|     bd_addr_udn[1] = (uint8_t)( (udn & 0x0000FF00) >> 8 ); |  | ||||||
|     bd_addr_udn[2] = (uint8_t)( (udn & 0x00FF0000) >> 16 ); |  | ||||||
|     bd_addr_udn[3] = (uint8_t)device_id; |  | ||||||
|     bd_addr_udn[4] = (uint8_t)(company_id & 0x000000FF);; |  | ||||||
|     bd_addr_udn[5] = (uint8_t)( (company_id & 0x0000FF00) >> 8 ); |  | ||||||
| 
 |  | ||||||
|     bd_addr = (const uint8_t *)bd_addr_udn; |  | ||||||
|   } else { |  | ||||||
|     otp_addr = OTP_Read(0); |  | ||||||
|     if(otp_addr) { |  | ||||||
|       bd_addr = ((OTP_ID0_t*)otp_addr)->bd_address; |  | ||||||
|     } else { |  | ||||||
|       bd_addr = M_bd_addr; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   return bd_addr; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*************************************************************
 |  | ||||||
|  * |  | ||||||
|  *SPECIFIC FUNCTIONS |  | ||||||
|  * |  | ||||||
|  *************************************************************/ |  | ||||||
| static void Adv_Mgr( void ) { |  | ||||||
|   /**
 |  | ||||||
|    * The code shall be executed in the background as an aci command may be sent |  | ||||||
|    * The background is the only place where the application can make sure a new aci command |  | ||||||
|    * is not sent if there is a pending one |  | ||||||
|    */ |  | ||||||
|   osThreadFlagsSet( AdvUpdateProcessId, 1 ); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void AdvUpdateProcess(void *argument) { |  | ||||||
|   UNUSED(argument); |  | ||||||
| 
 |  | ||||||
|   for(;;) { |  | ||||||
|     osThreadFlagsWait( 1, osFlagsWaitAny, osWaitForever); |  | ||||||
|     Adv_Update( ); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void Adv_Update( void ) { |  | ||||||
|   Adv_Request(APP_BLE_LP_ADV); |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void HciUserEvtProcess(void *argument) { | static void HciUserEvtProcess(void *argument) { | ||||||
|   UNUSED(argument); |   UNUSED(argument); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -7,20 +7,7 @@ extern "C" { | |||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include "hci_tl.h" | #include "hci_tl.h" | ||||||
| 
 | 
 | ||||||
| typedef enum { |  | ||||||
|     APP_BLE_IDLE, |  | ||||||
|     APP_BLE_FAST_ADV, |  | ||||||
|     APP_BLE_LP_ADV, |  | ||||||
|     APP_BLE_SCAN, |  | ||||||
|     APP_BLE_LP_CONNECTING, |  | ||||||
|     APP_BLE_CONNECTED_SERVER, |  | ||||||
|     APP_BLE_CONNECTED_CLIENT |  | ||||||
| } APP_BLE_ConnStatus_t; |  | ||||||
| 
 |  | ||||||
| bool APP_BLE_Init(); | bool APP_BLE_Init(); | ||||||
| bool APP_BLE_Start(); |  | ||||||
| 
 |  | ||||||
| APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status(); |  | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
|  | |||||||
| @ -139,7 +139,7 @@ | |||||||
| /**
 | /**
 | ||||||
|  * Maximum supported ATT_MTU size |  * Maximum supported ATT_MTU size | ||||||
|  */ |  */ | ||||||
| #define CFG_BLE_MAX_ATT_MTU             (156) | #define CFG_BLE_MAX_ATT_MTU             (251) | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Size of the storage area for Attribute values |  * Size of the storage area for Attribute values | ||||||
|  | |||||||
| @ -11,21 +11,22 @@ typedef struct { | |||||||
|     uint16_t char_level_handle; |     uint16_t char_level_handle; | ||||||
| } BatterySvc; | } BatterySvc; | ||||||
| 
 | 
 | ||||||
| static BatterySvc battery_svc; | static BatterySvc* battery_svc = NULL; | ||||||
| 
 | 
 | ||||||
| bool battery_svc_init() { | static const uint16_t service_uuid = BATTERY_SERVICE_UUID; | ||||||
|  | static const uint16_t char_battery_level_uuid = BATTERY_LEVEL_CHAR_UUID; | ||||||
|  | 
 | ||||||
|  | void battery_svc_start() { | ||||||
|  |     battery_svc = furi_alloc(sizeof(BatterySvc)); | ||||||
|     tBleStatus status; |     tBleStatus status; | ||||||
|     const uint16_t service_uuid = BATTERY_SERVICE_UUID; |  | ||||||
|     const uint16_t char_battery_level_uuid = BATTERY_LEVEL_CHAR_UUID; |  | ||||||
| 
 | 
 | ||||||
|     // Add Battery service
 |     // Add Battery service
 | ||||||
|     status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&service_uuid, PRIMARY_SERVICE, 4, &battery_svc.svc_handle); |     status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&service_uuid, PRIMARY_SERVICE, 4, &battery_svc->svc_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery service: %d", status); |         FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery service: %d", status); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     // Add Battery level characteristic
 |     // Add Battery level characteristic
 | ||||||
|     status = aci_gatt_add_char(battery_svc.svc_handle, |     status = aci_gatt_add_char(battery_svc->svc_handle, | ||||||
|                                 UUID_TYPE_16, |                                 UUID_TYPE_16, | ||||||
|                                 (Char_UUID_t *) &char_battery_level_uuid, |                                 (Char_UUID_t *) &char_battery_level_uuid, | ||||||
|                                 1, |                                 1, | ||||||
| @ -34,17 +35,39 @@ bool battery_svc_init() { | |||||||
|                                 GATT_DONT_NOTIFY_EVENTS, |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|                                 10, |                                 10, | ||||||
|                                 CHAR_VALUE_LEN_CONSTANT, |                                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|                                 &battery_svc.char_level_handle); |                                 &battery_svc->char_level_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery level characteristic: %d", status); |         FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery level characteristic: %d", status); | ||||||
|     } |     } | ||||||
|     return status != BLE_STATUS_SUCCESS; | } | ||||||
|  | 
 | ||||||
|  | void battery_svc_stop() { | ||||||
|  |     tBleStatus status; | ||||||
|  |     if(battery_svc) { | ||||||
|  |         // Delete Battery level characteristic
 | ||||||
|  |         status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->char_level_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to delete Battery level characteristic: %d", status); | ||||||
|  |         } | ||||||
|  |         // Delete Battery service
 | ||||||
|  |         status = aci_gatt_del_service(battery_svc->svc_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to delete Battery service: %d", status); | ||||||
|  |         } | ||||||
|  |         free(battery_svc); | ||||||
|  |         battery_svc = NULL; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool battery_svc_update_level(uint8_t battery_charge) { | bool battery_svc_update_level(uint8_t battery_charge) { | ||||||
|  |     // Check if service was started
 | ||||||
|  |     if(battery_svc == NULL) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     // Update battery level characteristic
 | ||||||
|     FURI_LOG_I(BATTERY_SERVICE_TAG, "Updating battery level characteristic"); |     FURI_LOG_I(BATTERY_SERVICE_TAG, "Updating battery level characteristic"); | ||||||
|     tBleStatus result = aci_gatt_update_char_value(battery_svc.svc_handle, |     tBleStatus result = aci_gatt_update_char_value(battery_svc->svc_handle, | ||||||
|                                           battery_svc.char_level_handle, |                                           battery_svc->char_level_handle, | ||||||
|                                           0, |                                           0, | ||||||
|                                           1, |                                           1, | ||||||
|                                           &battery_charge); |                                           &battery_charge); | ||||||
|  | |||||||
| @ -7,7 +7,9 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| bool battery_svc_init(); | void battery_svc_start(); | ||||||
|  | 
 | ||||||
|  | void battery_svc_stop(); | ||||||
| 
 | 
 | ||||||
| bool battery_svc_update_level(uint8_t battery_level); | bool battery_svc_update_level(uint8_t battery_level); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ | |||||||
| 
 | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| 
 | 
 | ||||||
| #define DEV_INFO_SERVICE_TAG "dev info service" | #define DEV_INFO_SVC_TAG "dev info service" | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     uint16_t service_handle; |     uint16_t service_handle; | ||||||
| @ -14,108 +14,143 @@ typedef struct { | |||||||
|     uint16_t software_rev_char_handle; |     uint16_t software_rev_char_handle; | ||||||
| } DevInfoSvc; | } DevInfoSvc; | ||||||
| 
 | 
 | ||||||
| bool dev_info_service_init() { | static DevInfoSvc* dev_info_svc = NULL; | ||||||
|  | 
 | ||||||
|  | static const char dev_info_man_name[] = "Flipper Devices Inc."; | ||||||
|  | static const char dev_info_serial_num[] = "1.0"; | ||||||
|  | static const char dev_info_firmware_rev_num[] = TARGET; | ||||||
|  | static const char dev_info_software_rev_num[] = GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE; | ||||||
|  | 
 | ||||||
|  | void dev_info_svc_start() { | ||||||
|  |     dev_info_svc = furi_alloc(sizeof(DevInfoSvc)); | ||||||
|     tBleStatus status; |     tBleStatus status; | ||||||
|     DevInfoSvc dev_info_svc; |  | ||||||
| 
 | 
 | ||||||
|     // Add Device Information Service
 |     // Add Device Information Service
 | ||||||
|     uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; |     uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; | ||||||
|     status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 9, &dev_info_svc.service_handle); |     status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 9, &dev_info_svc->service_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add Device Information Service: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add Device Information Service: %d", status); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Add characteristics
 |     // Add characteristics
 | ||||||
|     uuid = MANUFACTURER_NAME_UUID; |     uuid = MANUFACTURER_NAME_UUID; | ||||||
|     status = aci_gatt_add_char(dev_info_svc.service_handle, |     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||||
|                                 UUID_TYPE_16, |                                 UUID_TYPE_16, | ||||||
|                                 (Char_UUID_t*)&uuid, |                                 (Char_UUID_t*)&uuid, | ||||||
|                                 strlen(DEV_INFO_MANUFACTURER_NAME), |                                 strlen(dev_info_man_name), | ||||||
|                                 CHAR_PROP_READ, |                                 CHAR_PROP_READ, | ||||||
|                                 ATTR_PERMISSION_NONE, |                                 ATTR_PERMISSION_NONE, | ||||||
|                                 GATT_DONT_NOTIFY_EVENTS, |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|                                 10, |                                 10, | ||||||
|                                 CHAR_VALUE_LEN_CONSTANT, |                                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|                                 &dev_info_svc.man_name_char_handle); |                                 &dev_info_svc->man_name_char_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add manufacturer name char: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add manufacturer name char: %d", status); | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
|     uuid = SERIAL_NUMBER_UUID; |     uuid = SERIAL_NUMBER_UUID; | ||||||
|     status = aci_gatt_add_char(dev_info_svc.service_handle, |     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||||
|                                 UUID_TYPE_16, |                                 UUID_TYPE_16, | ||||||
|                                 (Char_UUID_t*)&uuid, |                                 (Char_UUID_t*)&uuid, | ||||||
|                                 strlen(DEV_INFO_SERIAL_NUMBER), |                                 strlen(dev_info_serial_num), | ||||||
|                                 CHAR_PROP_READ, |                                 CHAR_PROP_READ, | ||||||
|                                 ATTR_PERMISSION_NONE, |                                 ATTR_PERMISSION_NONE, | ||||||
|                                 GATT_DONT_NOTIFY_EVENTS, |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|                                 10, |                                 10, | ||||||
|                                 CHAR_VALUE_LEN_CONSTANT, |                                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|                                 &dev_info_svc.serial_num_char_handle); |                                 &dev_info_svc->serial_num_char_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add serial number char: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add serial number char: %d", status); | ||||||
|     } |     } | ||||||
|     uuid = FIRMWARE_REVISION_UUID; |     uuid = FIRMWARE_REVISION_UUID; | ||||||
|     status = aci_gatt_add_char(dev_info_svc.service_handle, |     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||||
|                                 UUID_TYPE_16, |                                 UUID_TYPE_16, | ||||||
|                                 (Char_UUID_t*)&uuid, |                                 (Char_UUID_t*)&uuid, | ||||||
|                                 strlen(DEV_INFO_FIRMWARE_REVISION_NUMBER), |                                 strlen(dev_info_firmware_rev_num), | ||||||
|                                 CHAR_PROP_READ, |                                 CHAR_PROP_READ, | ||||||
|                                 ATTR_PERMISSION_NONE, |                                 ATTR_PERMISSION_NONE, | ||||||
|                                 GATT_DONT_NOTIFY_EVENTS, |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|                                 10, |                                 10, | ||||||
|                                 CHAR_VALUE_LEN_CONSTANT, |                                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|                                 &dev_info_svc.firmware_rev_char_handle); |                                 &dev_info_svc->firmware_rev_char_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add firmware revision char: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add firmware revision char: %d", status); | ||||||
|     } |     } | ||||||
|     uuid = SOFTWARE_REVISION_UUID; |     uuid = SOFTWARE_REVISION_UUID; | ||||||
|     status = aci_gatt_add_char(dev_info_svc.service_handle, |     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||||
|                                 UUID_TYPE_16, |                                 UUID_TYPE_16, | ||||||
|                                 (Char_UUID_t*)&uuid, |                                 (Char_UUID_t*)&uuid, | ||||||
|                                 strlen(DEV_INFO_SOFTWARE_REVISION_NUMBER), |                                 strlen(dev_info_software_rev_num), | ||||||
|                                 CHAR_PROP_READ, |                                 CHAR_PROP_READ, | ||||||
|                                 ATTR_PERMISSION_NONE, |                                 ATTR_PERMISSION_NONE, | ||||||
|                                 GATT_DONT_NOTIFY_EVENTS, |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|                                 10, |                                 10, | ||||||
|                                 CHAR_VALUE_LEN_CONSTANT, |                                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|                                 &dev_info_svc.software_rev_char_handle); |                                 &dev_info_svc->software_rev_char_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add software revision char: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add software revision char: %d", status); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Update characteristics
 |     // Update characteristics
 | ||||||
|     status = aci_gatt_update_char_value(dev_info_svc.service_handle, |     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||||
|                                         dev_info_svc.man_name_char_handle, |                                         dev_info_svc->man_name_char_handle, | ||||||
|                                         0, |                                         0, | ||||||
|                                         strlen(DEV_INFO_MANUFACTURER_NAME), |                                         strlen(dev_info_man_name), | ||||||
|                                         (uint8_t*)DEV_INFO_MANUFACTURER_NAME); |                                         (uint8_t*)dev_info_man_name); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to update manufacturer name char: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update manufacturer name char: %d", status); | ||||||
|     } |     } | ||||||
|     status = aci_gatt_update_char_value(dev_info_svc.service_handle, |     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||||
|                                         dev_info_svc.serial_num_char_handle, |                                         dev_info_svc->serial_num_char_handle, | ||||||
|                                         0, |                                         0, | ||||||
|                                         strlen(DEV_INFO_SERIAL_NUMBER), |                                         strlen(dev_info_serial_num), | ||||||
|                                         (uint8_t*)DEV_INFO_SERIAL_NUMBER); |                                         (uint8_t*)dev_info_serial_num); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to update serial number char: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update serial number char: %d", status); | ||||||
|     } |     } | ||||||
|     status = aci_gatt_update_char_value(dev_info_svc.service_handle, |     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||||
|                                         dev_info_svc.firmware_rev_char_handle, |                                         dev_info_svc->firmware_rev_char_handle, | ||||||
|                                         0, |                                         0, | ||||||
|                                         strlen(DEV_INFO_FIRMWARE_REVISION_NUMBER), |                                         strlen(dev_info_firmware_rev_num), | ||||||
|                                         (uint8_t*)DEV_INFO_FIRMWARE_REVISION_NUMBER); |                                         (uint8_t*)dev_info_firmware_rev_num); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to update firmware revision char: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update firmware revision char: %d", status); | ||||||
|     } |     } | ||||||
|     status = aci_gatt_update_char_value(dev_info_svc.service_handle, |     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||||
|                                         dev_info_svc.software_rev_char_handle, |                                         dev_info_svc->software_rev_char_handle, | ||||||
|                                         0, |                                         0, | ||||||
|                                         strlen(DEV_INFO_SOFTWARE_REVISION_NUMBER), |                                         strlen(dev_info_software_rev_num), | ||||||
|                                         (uint8_t*)DEV_INFO_SOFTWARE_REVISION_NUMBER); |                                         (uint8_t*)dev_info_software_rev_num); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to update software revision char: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update software revision char: %d", status); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dev_info_svc_stop() { | ||||||
|  |     tBleStatus status; | ||||||
|  |     if(dev_info_svc) { | ||||||
|  |         // Delete service characteristics
 | ||||||
|  |         status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->man_name_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete manufacturer name char: %d", status); | ||||||
|  |         } | ||||||
|  |         status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->serial_num_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete serial number char: %d", status); | ||||||
|  |         } | ||||||
|  |         status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->firmware_rev_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete firmware revision char: %d", status); | ||||||
|  |         } | ||||||
|  |         status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->software_rev_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete software revision char: %d", status); | ||||||
|  |         } | ||||||
|  |         // Delete service
 | ||||||
|  |         status = aci_gatt_del_service(dev_info_svc->service_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete device info service: %d", status); | ||||||
|  |         } | ||||||
|  |         free(dev_info_svc); | ||||||
|  |         dev_info_svc = NULL; | ||||||
|     } |     } | ||||||
|     return status != BLE_STATUS_SUCCESS; |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -12,8 +12,9 @@ extern "C" { | |||||||
| #define DEV_INFO_FIRMWARE_REVISION_NUMBER       TARGET | #define DEV_INFO_FIRMWARE_REVISION_NUMBER       TARGET | ||||||
| #define DEV_INFO_SOFTWARE_REVISION_NUMBER       GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE | #define DEV_INFO_SOFTWARE_REVISION_NUMBER       GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE | ||||||
| 
 | 
 | ||||||
|  | void dev_info_svc_start(); | ||||||
| 
 | 
 | ||||||
| bool dev_info_service_init(); | void dev_info_svc_stop(); | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										387
									
								
								firmware/targets/f6/ble-glue/gap.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										387
									
								
								firmware/targets/f6/ble-glue/gap.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,387 @@ | |||||||
|  | #include "gap.h" | ||||||
|  | 
 | ||||||
|  | #include "app_entry.h" | ||||||
|  | #include "ble.h" | ||||||
|  | 
 | ||||||
|  | #include "cmsis_os.h" | ||||||
|  | #include "otp.h" | ||||||
|  | #include "dev_info_service.h" | ||||||
|  | #include "battery_service.h" | ||||||
|  | #include "serial_service.h" | ||||||
|  | 
 | ||||||
|  | #include <applications/bt/bt_service/bt.h> | ||||||
|  | #include <furi-hal.h> | ||||||
|  | 
 | ||||||
|  | #define GAP_TAG "BLE" | ||||||
|  | 
 | ||||||
|  | #define FAST_ADV_TIMEOUT 30000 | ||||||
|  | #define INITIAL_ADV_TIMEOUT 60000 | ||||||
|  | 
 | ||||||
|  | #define BD_ADDR_SIZE_LOCAL 6 | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |   uint16_t gap_svc_handle; | ||||||
|  |   uint16_t dev_name_char_handle; | ||||||
|  |   uint16_t appearance_char_handle; | ||||||
|  |   uint16_t connection_handle; | ||||||
|  |   uint8_t adv_svc_uuid_len; | ||||||
|  |   uint8_t adv_svc_uuid[20]; | ||||||
|  | } GapSvc; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |   GapSvc gap_svc; | ||||||
|  |   GapState state; | ||||||
|  |   uint8_t mac_address[BD_ADDR_SIZE_LOCAL]; | ||||||
|  |   Bt* bt; | ||||||
|  |   osTimerId advertise_timer; | ||||||
|  |   osThreadAttr_t thread_attr; | ||||||
|  |   osThreadId_t thread_id; | ||||||
|  | } Gap; | ||||||
|  | 
 | ||||||
|  | // Identity root key
 | ||||||
|  | static const uint8_t gap_irk[16] = {0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0}; | ||||||
|  | // Encryption root key
 | ||||||
|  | static const uint8_t gap_erk[16] = {0xfe,0xdc,0xba,0x09,0x87,0x65,0x43,0x21,0xfe,0xdc,0xba,0x09,0x87,0x65,0x43,0x21}; | ||||||
|  | // Appearence characteristic UUID
 | ||||||
|  | static const uint8_t gap_appearence_char_uuid[] = {0x00, 0x86}; | ||||||
|  | // Default MAC address
 | ||||||
|  | static const uint8_t gap_default_mac_addr[] = {0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72}; | ||||||
|  | 
 | ||||||
|  | static Gap* gap = NULL; | ||||||
|  | 
 | ||||||
|  | static void gap_advertise(GapState new_state); | ||||||
|  | static void gap_app(void *arg); | ||||||
|  | 
 | ||||||
|  | SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt ) | ||||||
|  | { | ||||||
|  |   hci_event_pckt *event_pckt; | ||||||
|  |   evt_le_meta_event *meta_evt; | ||||||
|  |   evt_blue_aci *blue_evt; | ||||||
|  |   hci_le_phy_update_complete_event_rp0 *evt_le_phy_update_complete; | ||||||
|  |   uint8_t tx_phy; | ||||||
|  |   uint8_t rx_phy; | ||||||
|  |   tBleStatus ret = BLE_STATUS_INVALID_PARAMS; | ||||||
|  | 
 | ||||||
|  |   event_pckt = (hci_event_pckt*) ((hci_uart_pckt *) pckt)->data; | ||||||
|  | 
 | ||||||
|  |   switch (event_pckt->evt) { | ||||||
|  |     case EVT_DISCONN_COMPLETE: | ||||||
|  |     { | ||||||
|  |         hci_disconnection_complete_event_rp0 *disconnection_complete_event = (hci_disconnection_complete_event_rp0 *) event_pckt->data; | ||||||
|  |         if (disconnection_complete_event->Connection_Handle == gap->gap_svc.connection_handle) { | ||||||
|  |             gap->gap_svc.connection_handle = 0; | ||||||
|  |             gap->state = GapStateIdle; | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Disconnect from client"); | ||||||
|  |         } | ||||||
|  |         // Restart advertising
 | ||||||
|  |         gap_advertise(GapStateAdvFast); | ||||||
|  |         furi_hal_power_insomnia_exit(); | ||||||
|  |     } | ||||||
|  |     break; | ||||||
|  | 
 | ||||||
|  |     case EVT_LE_META_EVENT: | ||||||
|  |         meta_evt = (evt_le_meta_event*) event_pckt->data; | ||||||
|  |         switch (meta_evt->subevent) { | ||||||
|  |             case EVT_LE_CONN_UPDATE_COMPLETE: | ||||||
|  |             FURI_LOG_D(GAP_TAG, "Connection update event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |             case EVT_LE_PHY_UPDATE_COMPLETE: | ||||||
|  |             evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data; | ||||||
|  |             if(evt_le_phy_update_complete->Status) { | ||||||
|  |                 FURI_LOG_E(GAP_TAG, "Update PHY failed, status %d", evt_le_phy_update_complete->Status); | ||||||
|  |             } else { | ||||||
|  |                 FURI_LOG_I(GAP_TAG, "Update PHY succeed"); | ||||||
|  |             } | ||||||
|  |             ret = hci_le_read_phy(gap->gap_svc.connection_handle,&tx_phy,&rx_phy); | ||||||
|  |             if(ret) { | ||||||
|  |                 FURI_LOG_E(GAP_TAG, "Read PHY failed, status: %d", ret); | ||||||
|  |             } else { | ||||||
|  |                 FURI_LOG_I(GAP_TAG, "PHY Params TX= %d, RX= %d ", tx_phy, rx_phy); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |             case EVT_LE_CONN_COMPLETE: | ||||||
|  |             furi_hal_power_insomnia_enter(); | ||||||
|  |             hci_le_connection_complete_event_rp0* connection_complete_event = (hci_le_connection_complete_event_rp0 *) meta_evt->data; | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Connection complete for connection handle 0x%x", connection_complete_event->Connection_Handle); | ||||||
|  | 
 | ||||||
|  |             // Stop advertising as connection completed
 | ||||||
|  |             osTimerStop(gap->advertise_timer); | ||||||
|  | 
 | ||||||
|  |             // Update connection status and handle
 | ||||||
|  |             gap->state = GapStateConnected; | ||||||
|  |             gap->gap_svc.connection_handle = connection_complete_event->Connection_Handle; | ||||||
|  | 
 | ||||||
|  |             // Start pairing by sending security request
 | ||||||
|  |             aci_gap_slave_security_req(connection_complete_event->Connection_Handle); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     break; | ||||||
|  | 
 | ||||||
|  |     case EVT_VENDOR: | ||||||
|  |         blue_evt = (evt_blue_aci*) event_pckt->data; | ||||||
|  |         switch (blue_evt->ecode) { | ||||||
|  |             aci_gap_pairing_complete_event_rp0 *pairing_complete; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_LIMITED_DISCOVERABLE: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Limited discoverable event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_PASS_KEY_REQUEST: | ||||||
|  |         { | ||||||
|  |             // Generate random PIN code
 | ||||||
|  |             uint32_t pin = rand() % 999999; | ||||||
|  |             aci_gap_pass_key_resp(gap->gap_svc.connection_handle, pin); | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Pass key request event. Pin: %d", pin); | ||||||
|  |             bt_pin_code_show(gap->bt, pin); | ||||||
|  |         } | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_AUTHORIZATION_REQUEST: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Authorization request event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Slave security initiated"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_BOND_LOST: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Bond lost event. Start rebonding"); | ||||||
|  |             aci_gap_allow_rebond(gap->gap_svc.connection_handle); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_DEVICE_FOUND: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Device found event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_ADDR_NOT_RESOLVED: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Address not resolved event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_KEYPRESS_NOTIFICATION: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Key press notification event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Hex_value = %lx", | ||||||
|  |                         ((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value); | ||||||
|  |             aci_gap_numeric_comparison_value_confirm_yesno(gap->gap_svc.connection_handle, 1); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case (EVT_BLUE_GAP_PAIRING_CMPLT): | ||||||
|  |         { | ||||||
|  |             pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data; | ||||||
|  |             if (pairing_complete->Status) { | ||||||
|  |             FURI_LOG_E(GAP_TAG, "Pairing failed with status: %d. Terminating connection", pairing_complete->Status); | ||||||
|  |             aci_gap_terminate(gap->gap_svc.connection_handle, 5); | ||||||
|  |             } else { | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Pairing complete"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_PROCEDURE_COMPLETE: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Procedure complete event"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return SVCCTL_UserEvtFlowEnable; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SVCCTL_SvcInit() { | ||||||
|  |     // Dummy function to prevent unused services initialization
 | ||||||
|  |     // TODO refactor (disable all services in WPAN config)
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void set_advertisment_service_uid(uint8_t* uid, uint8_t uid_len) { | ||||||
|  |     gap->gap_svc.adv_svc_uuid_len = 1; | ||||||
|  |     if(uid_len == 2) { | ||||||
|  |         gap->gap_svc.adv_svc_uuid[0] = AD_TYPE_16_BIT_SERV_UUID; | ||||||
|  |     } else if (uid_len == 4) { | ||||||
|  |         gap->gap_svc.adv_svc_uuid[0] = AD_TYPE_32_BIT_SERV_UUID; | ||||||
|  |     } else if(uid_len == 16) { | ||||||
|  |         gap->gap_svc.adv_svc_uuid[0] = AD_TYPE_128_BIT_SERV_UUID_CMPLT_LIST; | ||||||
|  |     } | ||||||
|  |     memcpy(&gap->gap_svc.adv_svc_uuid[1], uid, uid_len); | ||||||
|  |     gap->gap_svc.adv_svc_uuid_len += uid_len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GapState gap_get_status() { | ||||||
|  |     return gap->state; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void gap_init_mac_address(Gap* gap) { | ||||||
|  |     uint8_t *otp_addr; | ||||||
|  |     uint32_t udn; | ||||||
|  |     uint32_t company_id; | ||||||
|  |     uint32_t device_id; | ||||||
|  | 
 | ||||||
|  |     udn = LL_FLASH_GetUDN(); | ||||||
|  |     if(udn != 0xFFFFFFFF) { | ||||||
|  |         company_id = LL_FLASH_GetSTCompanyID(); | ||||||
|  |         device_id = LL_FLASH_GetDeviceID(); | ||||||
|  |         gap->mac_address[0] = (uint8_t)(udn & 0x000000FF); | ||||||
|  |         gap->mac_address[1] = (uint8_t)( (udn & 0x0000FF00) >> 8 ); | ||||||
|  |         gap->mac_address[2] = (uint8_t)( (udn & 0x00FF0000) >> 16 ); | ||||||
|  |         gap->mac_address[3] = (uint8_t)device_id; | ||||||
|  |         gap->mac_address[4] = (uint8_t)(company_id & 0x000000FF);; | ||||||
|  |         gap->mac_address[5] = (uint8_t)( (company_id & 0x0000FF00) >> 8 ); | ||||||
|  |     } else { | ||||||
|  |         otp_addr = OTP_Read(0); | ||||||
|  |         if(otp_addr) { | ||||||
|  |             memcpy(gap->mac_address, ((OTP_ID0_t*)otp_addr)->bd_address, sizeof(gap->mac_address)); | ||||||
|  |         } else { | ||||||
|  |             memcpy(gap->mac_address, gap_default_mac_addr, sizeof(gap->mac_address)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gap_init_svc(Gap* gap) { | ||||||
|  |     tBleStatus status; | ||||||
|  |     uint32_t srd_bd_addr[2]; | ||||||
|  | 
 | ||||||
|  |     //HCI Reset to synchronise BLE Stack*/
 | ||||||
|  |     hci_reset(); | ||||||
|  |     // Configure mac address
 | ||||||
|  |     gap_init_mac_address(gap); | ||||||
|  |     aci_hal_write_config_data(CONFIG_DATA_PUBADDR_OFFSET, CONFIG_DATA_PUBADDR_LEN, (uint8_t*)gap->mac_address); | ||||||
|  | 
 | ||||||
|  |     /* Static random Address
 | ||||||
|  |      * The two upper bits shall be set to 1 | ||||||
|  |      * The lowest 32bits is read from the UDN to differentiate between devices | ||||||
|  |      * The RNG may be used to provide a random number on each power on | ||||||
|  |      */ | ||||||
|  |     srd_bd_addr[1] = 0x0000ED6E; | ||||||
|  |     srd_bd_addr[0] = LL_FLASH_GetUDN(); | ||||||
|  |     aci_hal_write_config_data( CONFIG_DATA_RANDOM_ADDRESS_OFFSET, CONFIG_DATA_RANDOM_ADDRESS_LEN, (uint8_t*)srd_bd_addr ); | ||||||
|  |     // Set Identity root key used to derive LTK and CSRK
 | ||||||
|  |     aci_hal_write_config_data( CONFIG_DATA_IR_OFFSET, CONFIG_DATA_IR_LEN, (uint8_t*)gap_irk ); | ||||||
|  |     // Set Encryption root key used to derive LTK and CSRK
 | ||||||
|  |     aci_hal_write_config_data( CONFIG_DATA_ER_OFFSET, CONFIG_DATA_ER_LEN, (uint8_t*)gap_erk ); | ||||||
|  |     // Set TX Power to 0 dBm
 | ||||||
|  |     aci_hal_set_tx_power_level(1, 0x19); | ||||||
|  |     // Initialize GATT interface
 | ||||||
|  |     aci_gatt_init(); | ||||||
|  |     // Initialize GAP interface
 | ||||||
|  |     const char *name = furi_hal_version_get_device_name_ptr(); | ||||||
|  |     aci_gap_init(GAP_PERIPHERAL_ROLE, 0, strlen(name), | ||||||
|  |                 &gap->gap_svc.gap_svc_handle, &gap->gap_svc.dev_name_char_handle, &gap->gap_svc.appearance_char_handle); | ||||||
|  | 
 | ||||||
|  |     // Set GAP characteristics
 | ||||||
|  |     status = aci_gatt_update_char_value(gap->gap_svc.gap_svc_handle, gap->gap_svc.dev_name_char_handle, 0, strlen(name), (uint8_t *) name); | ||||||
|  |     if (status) { | ||||||
|  |         FURI_LOG_E(GAP_TAG, "Failed updating name characteristic: %d", status); | ||||||
|  |     } | ||||||
|  |     status = aci_gatt_update_char_value(gap->gap_svc.gap_svc_handle, gap->gap_svc.appearance_char_handle, 0, 2, gap_appearence_char_uuid); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(GAP_TAG, "Failed updating appearence characteristic: %d", status); | ||||||
|  |     } | ||||||
|  |     // Set default PHY
 | ||||||
|  |     hci_le_set_default_phy(ALL_PHYS_PREFERENCE, TX_2M_PREFERRED, RX_2M_PREFERRED); | ||||||
|  |     // Set I/O capability
 | ||||||
|  |     aci_gap_set_io_capability(IO_CAP_DISPLAY_ONLY); | ||||||
|  |     // Setup  authentication
 | ||||||
|  |     aci_gap_set_authentication_requirement(1, 1, 1, 0, 8, 16, 1, 0, PUBLIC_ADDR); | ||||||
|  |     // Configure whitelist
 | ||||||
|  |     aci_gap_configure_whitelist(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gap_advertise(GapState new_state) | ||||||
|  | { | ||||||
|  |     tBleStatus status; | ||||||
|  |     uint16_t min_interval; | ||||||
|  |     uint16_t max_interval; | ||||||
|  | 
 | ||||||
|  |     if (new_state == GapStateAdvFast) { | ||||||
|  |         min_interval = 0x80; // 80 ms
 | ||||||
|  |         max_interval = 0xa0; // 100 ms
 | ||||||
|  |     } else { | ||||||
|  |         min_interval = 0x0640; // 1 s
 | ||||||
|  |         max_interval = 0x0fa0; // 2.5 s
 | ||||||
|  |     } | ||||||
|  |     // Stop advertising timer
 | ||||||
|  |     osTimerStop(gap->advertise_timer); | ||||||
|  | 
 | ||||||
|  |     if ((new_state == GapStateAdvLowPower) && ((gap->state == GapStateAdvFast) || (gap->state == GapStateAdvLowPower))) { | ||||||
|  |         // Stop advertising
 | ||||||
|  |         status = aci_gap_set_non_discoverable(); | ||||||
|  |         if (status) { | ||||||
|  |             FURI_LOG_E(GAP_TAG, "Stop Advertising Failed, result: %d", status); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     // Configure advertising
 | ||||||
|  |     gap->state = new_state; | ||||||
|  |     const char* name = furi_hal_version_get_ble_local_device_name_ptr(); | ||||||
|  |     status = aci_gap_set_discoverable(ADV_IND, min_interval, max_interval, PUBLIC_ADDR, 0, | ||||||
|  |                                         strlen(name), (uint8_t*)name, | ||||||
|  |                                         gap->gap_svc.adv_svc_uuid_len, gap->gap_svc.adv_svc_uuid, 0, 0); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(GAP_TAG, "Set discoverable err: %d", status); | ||||||
|  |     } | ||||||
|  |     osTimerStart(gap->advertise_timer, INITIAL_ADV_TIMEOUT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gap_advertise_request(Gap* gap) { | ||||||
|  |     osThreadFlagsSet(gap->thread_id, 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gap_advetise_timer_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Gap* gap = context; | ||||||
|  |     gap_advertise_request(gap); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool gap_init() { | ||||||
|  |     if (APPE_Status() != BleGlueStatusStarted) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     gap = furi_alloc(sizeof(Gap)); | ||||||
|  |     srand(DWT->CYCCNT); | ||||||
|  |     // Open Bt record
 | ||||||
|  |     gap->bt = furi_record_open("bt"); | ||||||
|  |     // Create advertising timer
 | ||||||
|  |     gap->advertise_timer = osTimerNew(gap_advetise_timer_callback, osTimerOnce, &gap, NULL); | ||||||
|  |     // Initialization of HCI & GATT & GAP layer
 | ||||||
|  |     gap_init_svc(gap); | ||||||
|  |     // Initialization of the BLE Services
 | ||||||
|  |     SVCCTL_Init(); | ||||||
|  |     // Initialization of the BLE App Context
 | ||||||
|  |     gap->state = GapStateIdle; | ||||||
|  |     gap->gap_svc.connection_handle = 0xFFFF; | ||||||
|  | 
 | ||||||
|  |     // Thread configuration
 | ||||||
|  |     gap->thread_attr.name = "BLE advertising"; | ||||||
|  |     gap->thread_attr.stack_size = 512; | ||||||
|  |     gap->thread_id = osThreadNew(gap_app, NULL, &gap->thread_attr); | ||||||
|  | 
 | ||||||
|  |     // Start Device Information service
 | ||||||
|  |     dev_info_svc_start(); | ||||||
|  |     // Start Battery service
 | ||||||
|  |     battery_svc_start(); | ||||||
|  |     // Start Serial application
 | ||||||
|  |     serial_svc_start(); | ||||||
|  |     // Configure advirtise service UUID
 | ||||||
|  |     uint8_t adv_service_uid[2]; | ||||||
|  |     adv_service_uid[0] = 0x80 | furi_hal_version_get_hw_color(); | ||||||
|  |     adv_service_uid[1] = 0x30; | ||||||
|  | 
 | ||||||
|  |     set_advertisment_service_uid(adv_service_uid, sizeof(adv_service_uid)); | ||||||
|  |     gap_advertise(GapStateAdvFast); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gap_app(void *arg) { | ||||||
|  |     // TODO Exit from app, stop service, clean memory
 | ||||||
|  |     while(1) { | ||||||
|  |         osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever); | ||||||
|  |         gap_advertise(GapStateAdvLowPower); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								firmware/targets/f6/ble-glue/gap.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								firmware/targets/f6/ble-glue/gap.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     GapStateIdle, | ||||||
|  |     GapStateAdvFast, | ||||||
|  |     GapStateAdvLowPower, | ||||||
|  |     GapStateConnected, | ||||||
|  | } GapState; | ||||||
|  | 
 | ||||||
|  | bool gap_init(); | ||||||
|  | 
 | ||||||
|  | GapState gap_get_status(); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -6,13 +6,19 @@ | |||||||
| 
 | 
 | ||||||
| #define SERIAL_SERVICE_TAG "serial service" | #define SERIAL_SERVICE_TAG "serial service" | ||||||
| 
 | 
 | ||||||
|  | #define SERIAL_SVC_DATA_LEN_MAX 245 | ||||||
|  | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     uint16_t svc_handle; |     uint16_t svc_handle; | ||||||
|     uint16_t rx_char_handle; |     uint16_t rx_char_handle; | ||||||
|     uint16_t tx_char_handle; |     uint16_t tx_char_handle; | ||||||
| } SerialSvc; | } SerialSvc; | ||||||
| 
 | 
 | ||||||
| static SerialSvc serial_svc; | static SerialSvc* serial_svc; | ||||||
|  | 
 | ||||||
|  | static const uint8_t service_uuid[] = {0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f}; | ||||||
|  | static const uint8_t char_rx_uuid[] = {0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; | ||||||
|  | static const uint8_t char_tx_uuid[] = {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; | ||||||
| 
 | 
 | ||||||
| static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) { | static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) { | ||||||
|     SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; |     SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; | ||||||
| @ -22,76 +28,90 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) { | |||||||
|     if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { |     if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { | ||||||
|         if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { |         if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { | ||||||
|             attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data; |             attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data; | ||||||
|             if(attribute_modified->Attr_Handle == serial_svc.tx_char_handle + 2) { |             if(attribute_modified->Attr_Handle == serial_svc->tx_char_handle + 2) { | ||||||
|                 // Descriptor handle
 |                 // Descriptor handle
 | ||||||
|                 ret = SVCCTL_EvtAckFlowEnable; |                 ret = SVCCTL_EvtAckFlowEnable; | ||||||
|                 FURI_LOG_D(SERIAL_SERVICE_TAG, "TX descriptor event"); |                 FURI_LOG_D(SERIAL_SERVICE_TAG, "TX descriptor event"); | ||||||
|             } else if(attribute_modified->Attr_Handle == serial_svc.tx_char_handle + 1) { |             } else if(attribute_modified->Attr_Handle == serial_svc->tx_char_handle + 1) { | ||||||
|                 FURI_LOG_I(SERIAL_SERVICE_TAG, "Data len: %d", attribute_modified->Attr_Data_Length); |                 FURI_LOG_D(SERIAL_SERVICE_TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); | ||||||
|                 for(uint8_t i = 0; i < attribute_modified->Attr_Data_Length; i++) { |  | ||||||
|                     printf("%02X ", attribute_modified->Attr_Data[i]); |  | ||||||
|                 } |  | ||||||
|                 printf("\r\n"); |  | ||||||
|                 serial_svc_update_rx(attribute_modified->Attr_Data, attribute_modified->Attr_Data_Length); |                 serial_svc_update_rx(attribute_modified->Attr_Data, attribute_modified->Attr_Data_Length); | ||||||
|                 ret = SVCCTL_EvtAckFlowEnable; |                 ret = SVCCTL_EvtAckFlowEnable; | ||||||
|             } |             } | ||||||
|         } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { |         } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { | ||||||
|             FURI_LOG_I(SERIAL_SERVICE_TAG, "Ack received", blecore_evt->ecode); |             FURI_LOG_D(SERIAL_SERVICE_TAG, "Ack received", blecore_evt->ecode); | ||||||
|             ret = SVCCTL_EvtAckFlowEnable; |             ret = SVCCTL_EvtAckFlowEnable; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool serial_svc_init() { | void serial_svc_start() { | ||||||
|     tBleStatus status; |     tBleStatus status; | ||||||
|     const uint8_t service_uuid[] = {SERIAL_SVC_UUID_128}; |     serial_svc = furi_alloc(sizeof(SerialSvc)); | ||||||
|     const uint8_t char_rx_uuid[] = {SERIAL_CHAR_RX_UUID_128}; |  | ||||||
|     const uint8_t char_tx_uuid[] = {SERIAL_CHAR_TX_UUID_128}; |  | ||||||
| 
 |  | ||||||
|     // Register event handler
 |     // Register event handler
 | ||||||
|     SVCCTL_RegisterSvcHandler(serial_svc_event_handler); |     SVCCTL_RegisterSvcHandler(serial_svc_event_handler); | ||||||
| 
 | 
 | ||||||
|     // Add service
 |     // Add service
 | ||||||
|     status = aci_gatt_add_service(UUID_TYPE_128, (Service_UUID_t *)service_uuid, PRIMARY_SERVICE, 6, &serial_svc.svc_handle); |     status = aci_gatt_add_service(UUID_TYPE_128, (Service_UUID_t *)service_uuid, PRIMARY_SERVICE, 6, &serial_svc->svc_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status); |         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Add TX characteristics
 |     // Add TX characteristics
 | ||||||
|     status = aci_gatt_add_char(serial_svc.svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_tx_uuid , |     status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_tx_uuid, | ||||||
|                                 SERIAL_SVC_DATA_LEN_MAX, |                                 SERIAL_SVC_DATA_LEN_MAX, | ||||||
|                                 CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, |                                 CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, | ||||||
|                                 ATTR_PERMISSION_NONE, |                                 ATTR_PERMISSION_NONE, | ||||||
|                                 GATT_NOTIFY_ATTRIBUTE_WRITE, |                                 GATT_NOTIFY_ATTRIBUTE_WRITE, | ||||||
|                                 10, |                                 10, | ||||||
|                                 CHAR_VALUE_LEN_VARIABLE, |                                 CHAR_VALUE_LEN_VARIABLE, | ||||||
|                                 &serial_svc.tx_char_handle); |                                 &serial_svc->tx_char_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add TX characteristic: %d", status); |         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add TX characteristic: %d", status); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Add RX characteristic
 |     // Add RX characteristic
 | ||||||
|     status = aci_gatt_add_char(serial_svc.svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_rx_uuid , |     status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_rx_uuid, | ||||||
|                                 SERIAL_SVC_DATA_LEN_MAX,                                   |                                 SERIAL_SVC_DATA_LEN_MAX,                                   | ||||||
|                                 CHAR_PROP_READ | CHAR_PROP_INDICATE, |                                 CHAR_PROP_READ | CHAR_PROP_INDICATE, | ||||||
|                                 ATTR_PERMISSION_NONE, |                                 ATTR_PERMISSION_NONE, | ||||||
|                                 GATT_DONT_NOTIFY_EVENTS, |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|                                 10, |                                 10, | ||||||
|                                 CHAR_VALUE_LEN_VARIABLE, |                                 CHAR_VALUE_LEN_VARIABLE, | ||||||
|                                 &serial_svc.rx_char_handle); |                                 &serial_svc->rx_char_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add RX characteristic: %d", status); |         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add RX characteristic: %d", status); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     return status != BLE_STATUS_SUCCESS; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void serial_svc_stop() { | ||||||
|  |     tBleStatus status; | ||||||
|  |     if(serial_svc) { | ||||||
|  |         // Delete characteristics
 | ||||||
|  |         status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->tx_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete TX characteristic: %d", status); | ||||||
|  |         } | ||||||
|  |         status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rx_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete RX characteristic: %d", status); | ||||||
|  |         } | ||||||
|  |         // Delete service
 | ||||||
|  |         status = aci_gatt_del_service(serial_svc->svc_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete Serial service: %d", status); | ||||||
|  |         } | ||||||
|  |         free(serial_svc); | ||||||
|  |         serial_svc = NULL; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| bool serial_svc_update_rx(uint8_t* data, uint8_t data_len) { | bool serial_svc_update_rx(uint8_t* data, uint8_t data_len) { | ||||||
|     furi_assert(data_len < SERIAL_SVC_DATA_LEN_MAX); |     furi_assert(data_len < SERIAL_SVC_DATA_LEN_MAX); | ||||||
| 
 | 
 | ||||||
|     tBleStatus result = aci_gatt_update_char_value(serial_svc.svc_handle, |     tBleStatus result = aci_gatt_update_char_value(serial_svc->svc_handle, | ||||||
|                                           serial_svc.rx_char_handle, |                                           serial_svc->rx_char_handle, | ||||||
|                                           0, |                                           0, | ||||||
|                                           data_len, |                                           data_len, | ||||||
|                                           data); |                                           data); | ||||||
|  | |||||||
| @ -7,13 +7,9 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #define SERIAL_SVC_DATA_LEN_MAX 255 | void serial_svc_start(); | ||||||
| 
 | 
 | ||||||
| #define SERIAL_SVC_UUID_128 0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f | void serial_svc_stop(); | ||||||
| #define SERIAL_CHAR_RX_UUID_128 0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 |  | ||||||
| #define SERIAL_CHAR_TX_UUID_128 0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 |  | ||||||
| 
 |  | ||||||
| bool serial_svc_init(); |  | ||||||
| 
 | 
 | ||||||
| bool serial_svc_update_rx(uint8_t* data, uint8_t data_len); | bool serial_svc_update_rx(uint8_t* data, uint8_t data_len); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ | |||||||
| #include <shci.h> | #include <shci.h> | ||||||
| #include <cmsis_os2.h> | #include <cmsis_os2.h> | ||||||
| #include <app_ble.h> | #include <app_ble.h> | ||||||
|  | #include <gap.h> | ||||||
| 
 | 
 | ||||||
| void furi_hal_bt_init() { | void furi_hal_bt_init() { | ||||||
|     // Explicitly tell that we are in charge of CLK48 domain
 |     // Explicitly tell that we are in charge of CLK48 domain
 | ||||||
| @ -14,7 +15,7 @@ void furi_hal_bt_init() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool furi_hal_bt_start_app() { | bool furi_hal_bt_start_app() { | ||||||
|     return APP_BLE_Start(); |     return gap_init(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_bt_dump_state(string_t buffer) { | void furi_hal_bt_dump_state(string_t buffer) { | ||||||
|  | |||||||
| @ -6,106 +6,20 @@ | |||||||
| #include "ble.h" | #include "ble.h" | ||||||
| #include "tl.h" | #include "tl.h" | ||||||
| #include "app_ble.h" | #include "app_ble.h" | ||||||
| 
 |  | ||||||
| #include "cmsis_os.h" |  | ||||||
| #include "shci.h" | #include "shci.h" | ||||||
| #include "otp.h" | #include "cmsis_os.h" | ||||||
| #include "dev_info_service.h" |  | ||||||
| #include "battery_service.h" |  | ||||||
| #include "serial_service.h" |  | ||||||
| 
 | 
 | ||||||
| #include <furi-hal.h> | #include <furi-hal.h> | ||||||
| 
 | 
 | ||||||
| typedef struct _tSecurityParams { |  | ||||||
|   uint8_t ioCapability; |  | ||||||
|   uint8_t mitm_mode; |  | ||||||
|   uint8_t bonding_mode; |  | ||||||
|   uint8_t Use_Fixed_Pin; |  | ||||||
|   uint8_t encryptionKeySizeMin; |  | ||||||
|   uint8_t encryptionKeySizeMax; |  | ||||||
|   uint32_t Fixed_Pin; |  | ||||||
|   uint8_t initiateSecurity; |  | ||||||
| } tSecurityParams; |  | ||||||
| 
 |  | ||||||
| typedef struct _tBLEProfileGlobalContext { |  | ||||||
|   tSecurityParams bleSecurityParam; |  | ||||||
|   uint16_t gapServiceHandle; |  | ||||||
|   uint16_t devNameCharHandle; |  | ||||||
|   uint16_t appearanceCharHandle; |  | ||||||
|   uint16_t connectionHandle; |  | ||||||
|   uint8_t advtServUUIDlen; |  | ||||||
|   uint8_t advtServUUID[100]; |  | ||||||
| } BleGlobalContext_t; |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|   BleGlobalContext_t BleApplicationContext_legacy; |  | ||||||
|   APP_BLE_ConnStatus_t Device_Connection_Status; |  | ||||||
|   uint8_t Advertising_mgr_timer_Id; |  | ||||||
| } BleApplicationContext_t; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| #define FAST_ADV_TIMEOUT               (30*1000*1000/CFG_TS_TICK_VAL) /**< 30s */ |  | ||||||
| #define INITIAL_ADV_TIMEOUT            (60*1000*1000/CFG_TS_TICK_VAL) /**< 60s */ |  | ||||||
| 
 |  | ||||||
| #define BD_ADDR_SIZE_LOCAL    6 |  | ||||||
| 
 |  | ||||||
| #define LED_ON_TIMEOUT                 (0.005*1000*1000/CFG_TS_TICK_VAL) /**< 5ms */ |  | ||||||
| 
 |  | ||||||
| PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer; | PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer; | ||||||
| 
 | 
 | ||||||
| static const uint8_t M_bd_addr[BD_ADDR_SIZE_LOCAL] = | // PLACE_IN_SECTION("TAG_OTA_END") const uint32_t MagicKeywordValue = 0x94448A29 ;
 | ||||||
|     { | // PLACE_IN_SECTION("TAG_OTA_START") const uint32_t MagicKeywordAddress = (uint32_t)&MagicKeywordValue;
 | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0x0000000000FF)), |  | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0x00000000FF00) >> 8), |  | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0x000000FF0000) >> 16), |  | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0x0000FF000000) >> 24), |  | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0x00FF00000000) >> 32), |  | ||||||
|         (uint8_t)((CFG_ADV_BD_ADDRESS & 0xFF0000000000) >> 40) |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
| static uint8_t bd_addr_udn[BD_ADDR_SIZE_LOCAL]; |  | ||||||
| 
 |  | ||||||
| static const uint8_t BLE_CFG_IR_VALUE[16] = CFG_BLE_IRK; |  | ||||||
| static const uint8_t BLE_CFG_ER_VALUE[16] = CFG_BLE_ERK; |  | ||||||
| 
 |  | ||||||
| PLACE_IN_SECTION("TAG_OTA_END") const uint32_t MagicKeywordValue = 0x94448A29 ; |  | ||||||
| PLACE_IN_SECTION("TAG_OTA_START") const uint32_t MagicKeywordAddress = (uint32_t)&MagicKeywordValue; |  | ||||||
| 
 |  | ||||||
| PLACE_IN_SECTION("BLE_APP_CONTEXT") static BleApplicationContext_t BleApplicationContext; |  | ||||||
| PLACE_IN_SECTION("BLE_APP_CONTEXT") static uint16_t AdvIntervalMin, AdvIntervalMax; |  | ||||||
| 
 |  | ||||||
| uint8_t  manuf_data[14] = { |  | ||||||
|     sizeof(manuf_data)-1, AD_TYPE_MANUFACTURER_SPECIFIC_DATA, |  | ||||||
|     0x01/*SKD version */, |  | ||||||
|     0x00 /* Generic*/, |  | ||||||
|     0x00 /* GROUP A Feature  */, |  | ||||||
|     0x00 /* GROUP A Feature */, |  | ||||||
|     0x00 /* GROUP B Feature */, |  | ||||||
|     0x00 /* GROUP B Feature */, |  | ||||||
|     0x00, /* BLE MAC start -MSB */ |  | ||||||
|     0x00, |  | ||||||
|     0x00, |  | ||||||
|     0x00, |  | ||||||
|     0x00, |  | ||||||
|     0x00, /* BLE MAC stop */ |  | ||||||
| 
 |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| osMutexId_t MtxHciId; | osMutexId_t MtxHciId; | ||||||
| osSemaphoreId_t SemHciId; | osSemaphoreId_t SemHciId; | ||||||
| osThreadId_t AdvUpdateProcessId; |  | ||||||
| osThreadId_t HciUserEvtProcessId; | osThreadId_t HciUserEvtProcessId; | ||||||
| 
 | 
 | ||||||
| const osThreadAttr_t AdvUpdateProcess_attr = { |  | ||||||
|     .name = CFG_ADV_UPDATE_PROCESS_NAME, |  | ||||||
|     .attr_bits = CFG_ADV_UPDATE_PROCESS_ATTR_BITS, |  | ||||||
|     .cb_mem = CFG_ADV_UPDATE_PROCESS_CB_MEM, |  | ||||||
|     .cb_size = CFG_ADV_UPDATE_PROCESS_CB_SIZE, |  | ||||||
|     .stack_mem = CFG_ADV_UPDATE_PROCESS_STACK_MEM, |  | ||||||
|     .priority = CFG_ADV_UPDATE_PROCESS_PRIORITY, |  | ||||||
|     .stack_size = CFG_ADV_UPDATE_PROCESS_STACK_SIZE |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| const osThreadAttr_t HciUserEvtProcess_attr = { | const osThreadAttr_t HciUserEvtProcess_attr = { | ||||||
|     .name = CFG_HCI_USER_EVT_PROCESS_NAME, |     .name = CFG_HCI_USER_EVT_PROCESS_NAME, | ||||||
|     .attr_bits = CFG_HCI_USER_EVT_PROCESS_ATTR_BITS, |     .attr_bits = CFG_HCI_USER_EVT_PROCESS_ATTR_BITS, | ||||||
| @ -121,13 +35,6 @@ static void HciUserEvtProcess(void *argument); | |||||||
| static void BLE_UserEvtRx( void * pPayload ); | static void BLE_UserEvtRx( void * pPayload ); | ||||||
| static void BLE_StatusNot( HCI_TL_CmdStatus_t status ); | static void BLE_StatusNot( HCI_TL_CmdStatus_t status ); | ||||||
| static void Ble_Tl_Init( void ); | static void Ble_Tl_Init( void ); | ||||||
| static void Ble_Hci_Gap_Gatt_Init(); |  | ||||||
| static const uint8_t* BleGetBdAddress( void ); |  | ||||||
| static void Adv_Request( APP_BLE_ConnStatus_t New_Status ); |  | ||||||
| static void Adv_Mgr( void ); |  | ||||||
| static void AdvUpdateProcess(void *argument); |  | ||||||
| static void Adv_Update( void ); |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| bool APP_BLE_Init() { | bool APP_BLE_Init() { | ||||||
|   SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { |   SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { | ||||||
| @ -160,254 +67,6 @@ bool APP_BLE_Init() { | |||||||
|   return (SHCI_C2_BLE_Init( &ble_init_cmd_packet ) == SHCI_Success); |   return (SHCI_C2_BLE_Init( &ble_init_cmd_packet ) == SHCI_Success); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void set_advertisment_service_uid(uint8_t* uid, uint8_t uin_len); |  | ||||||
| 
 |  | ||||||
| bool APP_BLE_Start() { |  | ||||||
|   if (APPE_Status() != BleGlueStatusStarted) { |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|   // Initialization of HCI & GATT & GAP layer
 |  | ||||||
|   Ble_Hci_Gap_Gatt_Init(); |  | ||||||
|   // Initialization of the BLE Services
 |  | ||||||
|   SVCCTL_Init(); |  | ||||||
|   // Initialization of the BLE App Context
 |  | ||||||
|   BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0xFFFF; |  | ||||||
|   // From here, all initialization are BLE application specific
 |  | ||||||
|   AdvUpdateProcessId = osThreadNew(AdvUpdateProcess, NULL, &AdvUpdateProcess_attr); |  | ||||||
| 
 |  | ||||||
|   // Initialization of ADV - Ad Manufacturer Element - Support OTA Bit Masks
 |  | ||||||
| #if(BLE_CFG_OTA_REBOOT_CHAR != 0) |  | ||||||
|   manuf_data[sizeof(manuf_data)-8] = CFG_FEATURE_OTA_REBOOT; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
|   // Initialize DIS Application
 |  | ||||||
|   dev_info_service_init(); |  | ||||||
|   // Initialize BAS Application
 |  | ||||||
|   battery_svc_init(); |  | ||||||
|   // Initialize Serial application
 |  | ||||||
|   serial_svc_init(); |  | ||||||
|   // Create timer to handle the connection state machine
 |  | ||||||
|   HW_TS_Create(CFG_TIM_PROC_ID_ISR, &(BleApplicationContext.Advertising_mgr_timer_Id), hw_ts_SingleShot, Adv_Mgr); |  | ||||||
|   uint8_t adv_service_uid[2]; |  | ||||||
|   adv_service_uid[0] = 0x80 | furi_hal_version_get_hw_color(); |  | ||||||
|   adv_service_uid[1] = 0x30; |  | ||||||
| 
 |  | ||||||
|   set_advertisment_service_uid(adv_service_uid, sizeof(adv_service_uid)); |  | ||||||
|   /* Initialize intervals for reconnexion without intervals update */ |  | ||||||
|   AdvIntervalMin = CFG_FAST_CONN_ADV_INTERVAL_MIN; |  | ||||||
|   AdvIntervalMax = CFG_FAST_CONN_ADV_INTERVAL_MAX; |  | ||||||
| 
 |  | ||||||
|   Adv_Request(APP_BLE_FAST_ADV); |  | ||||||
|   return true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void SVCCTL_SvcInit() { |  | ||||||
|     // Dummy function to prevent unused services initialization
 |  | ||||||
|     // TODO refactore
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt ) |  | ||||||
| { |  | ||||||
|   hci_event_pckt *event_pckt; |  | ||||||
|   evt_le_meta_event *meta_evt; |  | ||||||
|   evt_blue_aci *blue_evt; |  | ||||||
|   hci_le_phy_update_complete_event_rp0 *evt_le_phy_update_complete; |  | ||||||
|   uint8_t TX_PHY, RX_PHY; |  | ||||||
|   tBleStatus ret = BLE_STATUS_INVALID_PARAMS; |  | ||||||
| 
 |  | ||||||
|   event_pckt = (hci_event_pckt*) ((hci_uart_pckt *) pckt)->data; |  | ||||||
| 
 |  | ||||||
|   switch (event_pckt->evt) { |  | ||||||
|     case EVT_DISCONN_COMPLETE: |  | ||||||
|     { |  | ||||||
|       hci_disconnection_complete_event_rp0 *disconnection_complete_event; |  | ||||||
|       disconnection_complete_event = (hci_disconnection_complete_event_rp0 *) event_pckt->data; |  | ||||||
| 
 |  | ||||||
|       if (disconnection_complete_event->Connection_Handle == BleApplicationContext.BleApplicationContext_legacy.connectionHandle) { |  | ||||||
|         BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0; |  | ||||||
|         BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE; |  | ||||||
|         APP_DBG_MSG("\r\n\r** DISCONNECTION EVENT WITH CLIENT \r\n"); |  | ||||||
|       } |  | ||||||
|       /* restart advertising */ |  | ||||||
|       Adv_Request(APP_BLE_FAST_ADV); |  | ||||||
|       furi_hal_power_insomnia_exit(); |  | ||||||
|     } |  | ||||||
|     break; /* EVT_DISCONN_COMPLETE */ |  | ||||||
| 
 |  | ||||||
|     case EVT_LE_META_EVENT: |  | ||||||
|     { |  | ||||||
|       meta_evt = (evt_le_meta_event*) event_pckt->data; |  | ||||||
|       switch (meta_evt->subevent) |  | ||||||
|       { |  | ||||||
|         case EVT_LE_CONN_UPDATE_COMPLETE: |  | ||||||
|           APP_DBG_MSG("\r\n\r** CONNECTION UPDATE EVENT WITH CLIENT \r\n"); |  | ||||||
| 
 |  | ||||||
|           /* USER CODE BEGIN EVT_LE_CONN_UPDATE_COMPLETE */ |  | ||||||
| 
 |  | ||||||
|           /* USER CODE END EVT_LE_CONN_UPDATE_COMPLETE */ |  | ||||||
|           break; |  | ||||||
|         case EVT_LE_PHY_UPDATE_COMPLETE: |  | ||||||
|           APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE \r\n"); |  | ||||||
|           evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data; |  | ||||||
|           if (evt_le_phy_update_complete->Status == 0) |  | ||||||
|           { |  | ||||||
|             APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status ok \r\n"); |  | ||||||
|           } |  | ||||||
|           else |  | ||||||
|           { |  | ||||||
|             APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status nok \r\n"); |  | ||||||
|           } |  | ||||||
| 
 |  | ||||||
|           ret = hci_le_read_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,&TX_PHY,&RX_PHY); |  | ||||||
|           if (ret == BLE_STATUS_SUCCESS) |  | ||||||
|           { |  | ||||||
|             APP_DBG_MSG("Read_PHY success \r\n"); |  | ||||||
| 
 |  | ||||||
|             if ((TX_PHY == TX_2M) && (RX_PHY == RX_2M)) |  | ||||||
|             { |  | ||||||
|               APP_DBG_MSG("PHY Param  TX= %d, RX= %d \r\n", TX_PHY, RX_PHY); |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|               APP_DBG_MSG("PHY Param  TX= %d, RX= %d \r\n", TX_PHY, RX_PHY); |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|           else |  | ||||||
|           { |  | ||||||
|             APP_DBG_MSG("Read conf not succeess \r\n"); |  | ||||||
|           } |  | ||||||
|           break; |  | ||||||
|         case EVT_LE_CONN_COMPLETE: |  | ||||||
|         { |  | ||||||
|           furi_hal_power_insomnia_enter(); |  | ||||||
|           hci_le_connection_complete_event_rp0 *connection_complete_event; |  | ||||||
| 
 |  | ||||||
|           /**
 |  | ||||||
|            * The connection is done, there is no need anymore to schedule the LP ADV |  | ||||||
|            */ |  | ||||||
|           connection_complete_event = (hci_le_connection_complete_event_rp0 *) meta_evt->data; |  | ||||||
| 
 |  | ||||||
|           HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id); |  | ||||||
| 
 |  | ||||||
|           APP_DBG_MSG("EVT_LE_CONN_COMPLETE for connection handle 0x%x\r\n", connection_complete_event->Connection_Handle); |  | ||||||
|           if (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_CONNECTING) |  | ||||||
|           { |  | ||||||
|             /* Connection as client */ |  | ||||||
|             BleApplicationContext.Device_Connection_Status = APP_BLE_CONNECTED_CLIENT; |  | ||||||
|           } |  | ||||||
|           else |  | ||||||
|           { |  | ||||||
|             /* Connection as server */ |  | ||||||
|             BleApplicationContext.Device_Connection_Status = APP_BLE_CONNECTED_SERVER; |  | ||||||
|           } |  | ||||||
|           BleApplicationContext.BleApplicationContext_legacy.connectionHandle = connection_complete_event->Connection_Handle; |  | ||||||
|         } |  | ||||||
|         break; /* HCI_EVT_LE_CONN_COMPLETE */ |  | ||||||
|         default: |  | ||||||
|           break; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|     break; /* HCI_EVT_LE_META_EVENT */ |  | ||||||
| 
 |  | ||||||
|     case EVT_VENDOR: |  | ||||||
|       blue_evt = (evt_blue_aci*) event_pckt->data; |  | ||||||
|       switch (blue_evt->ecode) { |  | ||||||
|         aci_gap_pairing_complete_event_rp0 *pairing_complete; |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_LIMITED_DISCOVERABLE:  |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_LIMITED_DISCOVERABLE \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_LIMITED_DISCOVERABLE */ |  | ||||||
|            |  | ||||||
|       case EVT_BLUE_GAP_PASS_KEY_REQUEST:   |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PASS_KEY_REQUEST \r\n"); |  | ||||||
| 
 |  | ||||||
|         aci_gap_pass_key_resp(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,123456); |  | ||||||
| 
 |  | ||||||
|         APP_DBG_MSG("\r\n\r** aci_gap_pass_key_resp \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_PASS_KEY_REQUEST */ |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_AUTHORIZATION_REQUEST:     |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_AUTHORIZATION_REQUEST \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_AUTHORIZATION_REQUEST */ |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED:    |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED */ |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_BOND_LOST:     |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_BOND_LOST \r\n"); |  | ||||||
|           aci_gap_allow_rebond(BleApplicationContext.BleApplicationContext_legacy.connectionHandle); |  | ||||||
|         APP_DBG_MSG("\r\n\r** Send allow rebond \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_BOND_LOST */ |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_DEVICE_FOUND:   |  | ||||||
|         APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_DEVICE_FOUND */ |  | ||||||
| 
 |  | ||||||
|       case EVT_BLUE_GAP_ADDR_NOT_RESOLVED: |  | ||||||
|          APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_DEVICE_FOUND */ |  | ||||||
|        |  | ||||||
|       case (EVT_BLUE_GAP_KEYPRESS_NOTIFICATION): |  | ||||||
|          APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_KEYPRESS_NOTIFICATION \r\n"); |  | ||||||
|           break; /* EVT_BLUE_GAP_KEY_PRESS_NOTIFICATION */     |  | ||||||
| 
 |  | ||||||
|        case (EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE): |  | ||||||
|           APP_DBG_MSG("numeric_value = %ld\r\n", |  | ||||||
|                       ((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value); |  | ||||||
| 
 |  | ||||||
|           APP_DBG_MSG("Hex_value = %lx\r\n", |  | ||||||
|                       ((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value); |  | ||||||
| 
 |  | ||||||
|           aci_gap_numeric_comparison_value_confirm_yesno(BleApplicationContext.BleApplicationContext_legacy.connectionHandle, 1); /* CONFIRM_YES = 1 */ |  | ||||||
| 
 |  | ||||||
|           APP_DBG_MSG("\r\n\r** aci_gap_numeric_comparison_value_confirm_yesno-->YES \r\n"); |  | ||||||
|           break; |  | ||||||
| 
 |  | ||||||
|         case (EVT_BLUE_GAP_PAIRING_CMPLT): |  | ||||||
|           { |  | ||||||
|             pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data; |  | ||||||
| 
 |  | ||||||
|             APP_DBG_MSG("BLE_CTRL_App_Notification: EVT_BLUE_GAP_PAIRING_CMPLT, pairing_complete->Status = %d\r\n",pairing_complete->Status); |  | ||||||
|             if (pairing_complete->Status == 0) { |  | ||||||
|               APP_DBG_MSG("\r\n\r** Pairing OK \r\n"); |  | ||||||
|             } else { |  | ||||||
|               APP_DBG_MSG("\r\n\r** Pairing KO \r\n"); |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|           break; |  | ||||||
| 
 |  | ||||||
|       /* USER CODE END ecode */ |  | ||||||
|         case EVT_BLUE_GAP_PROCEDURE_COMPLETE: |  | ||||||
|           APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PROCEDURE_COMPLETE \r\n"); |  | ||||||
|           break; |  | ||||||
|       } |  | ||||||
|       break; /* EVT_VENDOR */ |  | ||||||
|       default: |  | ||||||
|         break; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   return (SVCCTL_UserEvtFlowEnable); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void set_advertisment_service_uid(uint8_t* uid, uint8_t uid_len) { |  | ||||||
|     BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen = 1; |  | ||||||
|     if(uid_len == 2) { |  | ||||||
|         BleApplicationContext.BleApplicationContext_legacy.advtServUUID[0] = AD_TYPE_16_BIT_SERV_UUID; |  | ||||||
|     } else if (uid_len == 4) { |  | ||||||
|         BleApplicationContext.BleApplicationContext_legacy.advtServUUID[0] = AD_TYPE_32_BIT_SERV_UUID; |  | ||||||
|     } else if(uid_len == 16) { |  | ||||||
|         BleApplicationContext.BleApplicationContext_legacy.advtServUUID[0] = AD_TYPE_128_BIT_SERV_UUID_CMPLT_LIST; |  | ||||||
|     } |  | ||||||
|     memcpy(&BleApplicationContext.BleApplicationContext_legacy.advtServUUID[1], uid, uid_len); |  | ||||||
|     BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen += uid_len; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status() { |  | ||||||
|     return BleApplicationContext.Device_Connection_Status; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void Ble_Tl_Init( void ) { | static void Ble_Tl_Init( void ) { | ||||||
|   HCI_TL_HciInitConf_t Hci_Tl_Init_Conf; |   HCI_TL_HciInitConf_t Hci_Tl_Init_Conf; | ||||||
| 
 | 
 | ||||||
| @ -419,301 +78,6 @@ static void Ble_Tl_Init( void ) { | |||||||
|   hci_init(BLE_UserEvtRx, (void*) &Hci_Tl_Init_Conf); |   hci_init(BLE_UserEvtRx, (void*) &Hci_Tl_Init_Conf); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void Ble_Hci_Gap_Gatt_Init() { |  | ||||||
|   uint8_t role; |  | ||||||
|   uint16_t gap_service_handle, gap_dev_name_char_handle, gap_appearance_char_handle; |  | ||||||
|   const uint8_t *bd_addr; |  | ||||||
|   uint32_t srd_bd_addr[2]; |  | ||||||
|   uint16_t appearance[1] = { BLE_CFG_GAP_APPEARANCE }; |  | ||||||
| 
 |  | ||||||
|   /*HCI Reset to synchronise BLE Stack*/ |  | ||||||
|   hci_reset(); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Write the BD Address |  | ||||||
|    */ |  | ||||||
|   bd_addr = BleGetBdAddress(); |  | ||||||
|   aci_hal_write_config_data(CONFIG_DATA_PUBADDR_OFFSET, |  | ||||||
|                             CONFIG_DATA_PUBADDR_LEN, |  | ||||||
|                             (uint8_t*) bd_addr); |  | ||||||
| 
 |  | ||||||
|   /* BLE MAC in ADV Packet */ |  | ||||||
|   manuf_data[ sizeof(manuf_data)-6] = bd_addr[5]; |  | ||||||
|   manuf_data[ sizeof(manuf_data)-5] = bd_addr[4]; |  | ||||||
|   manuf_data[ sizeof(manuf_data)-4] = bd_addr[3]; |  | ||||||
|   manuf_data[ sizeof(manuf_data)-3] = bd_addr[2]; |  | ||||||
|   manuf_data[ sizeof(manuf_data)-2] = bd_addr[1]; |  | ||||||
|   manuf_data[ sizeof(manuf_data)-1] = bd_addr[0]; |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Write Identity root key used to derive LTK and CSRK |  | ||||||
|    */ |  | ||||||
|     aci_hal_write_config_data(CONFIG_DATA_IR_OFFSET, |  | ||||||
|     CONFIG_DATA_IR_LEN, |  | ||||||
|                             (uint8_t*) BLE_CFG_IR_VALUE); |  | ||||||
| 
 |  | ||||||
|    /**
 |  | ||||||
|    * Write Encryption root key used to derive LTK and CSRK |  | ||||||
|    */ |  | ||||||
|     aci_hal_write_config_data(CONFIG_DATA_ER_OFFSET, |  | ||||||
|     CONFIG_DATA_ER_LEN, |  | ||||||
|                             (uint8_t*) BLE_CFG_ER_VALUE); |  | ||||||
| 
 |  | ||||||
|    /**
 |  | ||||||
|    * Write random bd_address |  | ||||||
|    */ |  | ||||||
|    /* random_bd_address = R_bd_address;
 |  | ||||||
|     aci_hal_write_config_data(CONFIG_DATA_RANDOM_ADDRESS_WR, |  | ||||||
|     CONFIG_DATA_RANDOM_ADDRESS_LEN, |  | ||||||
|                             (uint8_t*) random_bd_address); |  | ||||||
|   */ |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Static random Address |  | ||||||
|    * The two upper bits shall be set to 1 |  | ||||||
|    * The lowest 32bits is read from the UDN to differentiate between devices |  | ||||||
|    * The RNG may be used to provide a random number on each power on |  | ||||||
|    */ |  | ||||||
|   srd_bd_addr[1] =  0x0000ED6E; |  | ||||||
|   srd_bd_addr[0] =  LL_FLASH_GetUDN( ); |  | ||||||
|   aci_hal_write_config_data( CONFIG_DATA_RANDOM_ADDRESS_OFFSET, CONFIG_DATA_RANDOM_ADDRESS_LEN, (uint8_t*)srd_bd_addr ); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Write Identity root key used to derive LTK and CSRK |  | ||||||
|    */ |  | ||||||
|     aci_hal_write_config_data( CONFIG_DATA_IR_OFFSET, CONFIG_DATA_IR_LEN, (uint8_t*)BLE_CFG_IR_VALUE ); |  | ||||||
| 
 |  | ||||||
|    /**
 |  | ||||||
|    * Write Encryption root key used to derive LTK and CSRK |  | ||||||
|    */ |  | ||||||
|     aci_hal_write_config_data( CONFIG_DATA_ER_OFFSET, CONFIG_DATA_ER_LEN, (uint8_t*)BLE_CFG_ER_VALUE ); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Set TX Power to 0dBm. |  | ||||||
|    */ |  | ||||||
|   aci_hal_set_tx_power_level(1, CFG_TX_POWER); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Initialize GATT interface |  | ||||||
|    */ |  | ||||||
|   aci_gatt_init(); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Initialize GAP interface |  | ||||||
|    */ |  | ||||||
|   role = 0; |  | ||||||
| 
 |  | ||||||
| #if (BLE_CFG_PERIPHERAL == 1) |  | ||||||
|   role |= GAP_PERIPHERAL_ROLE; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| #if (BLE_CFG_CENTRAL == 1) |  | ||||||
|   role |= GAP_CENTRAL_ROLE; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
|   if (role > 0) |  | ||||||
|   { |  | ||||||
|     const char *name = furi_hal_version_get_device_name_ptr(); |  | ||||||
|     aci_gap_init(role, 0, |  | ||||||
|                  strlen(name), |  | ||||||
|                  &gap_service_handle, &gap_dev_name_char_handle, &gap_appearance_char_handle); |  | ||||||
| 
 |  | ||||||
|     if (aci_gatt_update_char_value(gap_service_handle, gap_dev_name_char_handle, 0, strlen(name), (uint8_t *) name)) |  | ||||||
|     { |  | ||||||
|       BLE_DBG_SVCCTL_MSG("Device Name aci_gatt_update_char_value failed.\r\n"); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   if(aci_gatt_update_char_value(gap_service_handle, |  | ||||||
|                                 gap_appearance_char_handle, |  | ||||||
|                                 0, |  | ||||||
|                                 2, |  | ||||||
|                                 (uint8_t *)&appearance)) |  | ||||||
|   { |  | ||||||
|     BLE_DBG_SVCCTL_MSG("Appearance aci_gatt_update_char_value failed.\r\n"); |  | ||||||
|   } |  | ||||||
|   /**
 |  | ||||||
|    * Initialize Default PHY |  | ||||||
|    */ |  | ||||||
|   hci_le_set_default_phy(ALL_PHYS_PREFERENCE,TX_2M_PREFERRED,RX_2M_PREFERRED); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Initialize IO capability |  | ||||||
|    */ |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.ioCapability = CFG_IO_CAPABILITY; |  | ||||||
|   aci_gap_set_io_capability(BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.ioCapability); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Initialize authentication |  | ||||||
|    */ |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.mitm_mode = CFG_MITM_PROTECTION; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMin = CFG_ENCRYPTION_KEY_SIZE_MIN; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMax = CFG_ENCRYPTION_KEY_SIZE_MAX; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Use_Fixed_Pin = CFG_USED_FIXED_PIN; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Fixed_Pin = CFG_FIXED_PIN; |  | ||||||
|   BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode = CFG_BONDING_MODE; |  | ||||||
| 
 |  | ||||||
|   aci_gap_set_authentication_requirement(BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode, |  | ||||||
|                                          BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.mitm_mode, |  | ||||||
|                                          CFG_SC_SUPPORT, |  | ||||||
|                                          CFG_KEYPRESS_NOTIFICATION_SUPPORT, |  | ||||||
|                                          BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMin, |  | ||||||
|                                          BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMax, |  | ||||||
|                                          BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Use_Fixed_Pin, |  | ||||||
|                                          BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Fixed_Pin, |  | ||||||
|                                          PUBLIC_ADDR |  | ||||||
|                                          ); |  | ||||||
| 
 |  | ||||||
|   /**
 |  | ||||||
|    * Initialize whitelist |  | ||||||
|    */ |  | ||||||
|    if (BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode) |  | ||||||
|    { |  | ||||||
|      aci_gap_configure_whitelist(); |  | ||||||
|    } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void Adv_Request(APP_BLE_ConnStatus_t New_Status) |  | ||||||
| { |  | ||||||
|   tBleStatus ret = BLE_STATUS_INVALID_PARAMS; |  | ||||||
|   uint16_t Min_Inter, Max_Inter; |  | ||||||
| 
 |  | ||||||
|   if (New_Status == APP_BLE_FAST_ADV) |  | ||||||
|   { |  | ||||||
|     Min_Inter = AdvIntervalMin; |  | ||||||
|     Max_Inter = AdvIntervalMax; |  | ||||||
|   } |  | ||||||
|   else |  | ||||||
|   { |  | ||||||
|     Min_Inter = CFG_LP_CONN_ADV_INTERVAL_MIN; |  | ||||||
|     Max_Inter = CFG_LP_CONN_ADV_INTERVAL_MAX; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|     /**
 |  | ||||||
|      * Stop the timer, it will be restarted for a new shot |  | ||||||
|      * It does not hurt if the timer was not running |  | ||||||
|      */ |  | ||||||
|     HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id); |  | ||||||
| 
 |  | ||||||
|     APP_DBG_MSG("First index in %d state \r\n", BleApplicationContext.Device_Connection_Status); |  | ||||||
| 
 |  | ||||||
|     if ((New_Status == APP_BLE_LP_ADV) |  | ||||||
|         && ((BleApplicationContext.Device_Connection_Status == APP_BLE_FAST_ADV) |  | ||||||
|             || (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_ADV))) |  | ||||||
|     { |  | ||||||
|       /* Connection in ADVERTISE mode have to stop the current advertising */ |  | ||||||
|       ret = aci_gap_set_non_discoverable(); |  | ||||||
|       if (ret == BLE_STATUS_SUCCESS) |  | ||||||
|       { |  | ||||||
|         APP_DBG_MSG("Successfully Stopped Advertising \r\n"); |  | ||||||
|       } |  | ||||||
|       else |  | ||||||
|       { |  | ||||||
|         APP_DBG_MSG("Stop Advertising Failed , result: %d \r\n", ret); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     BleApplicationContext.Device_Connection_Status = New_Status; |  | ||||||
| 
 |  | ||||||
|     const char* name = furi_hal_version_get_ble_local_device_name_ptr(); |  | ||||||
| 
 |  | ||||||
|     /* Start Fast or Low Power Advertising */ |  | ||||||
|     ret = aci_gap_set_discoverable( |  | ||||||
|         ADV_IND, |  | ||||||
|         Min_Inter, |  | ||||||
|         Max_Inter, |  | ||||||
|         PUBLIC_ADDR, |  | ||||||
|         NO_WHITE_LIST_USE, /* use white list */ |  | ||||||
|         strlen(name), |  | ||||||
|         (uint8_t*)name, |  | ||||||
|         BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen, |  | ||||||
|         BleApplicationContext.BleApplicationContext_legacy.advtServUUID, |  | ||||||
|         0, |  | ||||||
|         0); |  | ||||||
|     if(ret) { |  | ||||||
|       FURI_LOG_E("APP ble", "Set discoverable err: %d", ret); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /* Update Advertising data */ |  | ||||||
|     ret = aci_gap_update_adv_data(sizeof(manuf_data), (uint8_t*) manuf_data); |  | ||||||
|     if (ret == BLE_STATUS_SUCCESS) { |  | ||||||
|       if (New_Status == APP_BLE_FAST_ADV) { |  | ||||||
|         APP_DBG_MSG("Successfully Start Fast Advertising \r\n" ); |  | ||||||
|         /* Start Timer to STOP ADV - TIMEOUT */ |  | ||||||
|         HW_TS_Start(BleApplicationContext.Advertising_mgr_timer_Id, INITIAL_ADV_TIMEOUT); |  | ||||||
|       } else { |  | ||||||
|         APP_DBG_MSG("Successfully Start Low Power Advertising \r\n"); |  | ||||||
|       } |  | ||||||
|     } else { |  | ||||||
|       if (New_Status == APP_BLE_FAST_ADV) { |  | ||||||
|         APP_DBG_MSG("Start Fast Advertising Failed , result: %d \r\n", ret); |  | ||||||
|       } else { |  | ||||||
|         APP_DBG_MSG("Start Low Power Advertising Failed , result: %d \r\n", ret); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const uint8_t* BleGetBdAddress( void ) { |  | ||||||
|   uint8_t *otp_addr; |  | ||||||
|   const uint8_t *bd_addr; |  | ||||||
|   uint32_t udn; |  | ||||||
|   uint32_t company_id; |  | ||||||
|   uint32_t device_id; |  | ||||||
| 
 |  | ||||||
|   udn = LL_FLASH_GetUDN(); |  | ||||||
| 
 |  | ||||||
|   if(udn != 0xFFFFFFFF) { |  | ||||||
|     company_id = LL_FLASH_GetSTCompanyID(); |  | ||||||
|     device_id = LL_FLASH_GetDeviceID(); |  | ||||||
| 
 |  | ||||||
|     bd_addr_udn[0] = (uint8_t)(udn & 0x000000FF); |  | ||||||
|     bd_addr_udn[1] = (uint8_t)( (udn & 0x0000FF00) >> 8 ); |  | ||||||
|     bd_addr_udn[2] = (uint8_t)( (udn & 0x00FF0000) >> 16 ); |  | ||||||
|     bd_addr_udn[3] = (uint8_t)device_id; |  | ||||||
|     bd_addr_udn[4] = (uint8_t)(company_id & 0x000000FF);; |  | ||||||
|     bd_addr_udn[5] = (uint8_t)( (company_id & 0x0000FF00) >> 8 ); |  | ||||||
| 
 |  | ||||||
|     bd_addr = (const uint8_t *)bd_addr_udn; |  | ||||||
|   } else { |  | ||||||
|     otp_addr = OTP_Read(0); |  | ||||||
|     if(otp_addr) { |  | ||||||
|       bd_addr = ((OTP_ID0_t*)otp_addr)->bd_address; |  | ||||||
|     } else { |  | ||||||
|       bd_addr = M_bd_addr; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   return bd_addr; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*************************************************************
 |  | ||||||
|  * |  | ||||||
|  *SPECIFIC FUNCTIONS |  | ||||||
|  * |  | ||||||
|  *************************************************************/ |  | ||||||
| static void Adv_Mgr( void ) { |  | ||||||
|   /**
 |  | ||||||
|    * The code shall be executed in the background as an aci command may be sent |  | ||||||
|    * The background is the only place where the application can make sure a new aci command |  | ||||||
|    * is not sent if there is a pending one |  | ||||||
|    */ |  | ||||||
|   osThreadFlagsSet( AdvUpdateProcessId, 1 ); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void AdvUpdateProcess(void *argument) { |  | ||||||
|   UNUSED(argument); |  | ||||||
| 
 |  | ||||||
|   for(;;) { |  | ||||||
|     osThreadFlagsWait( 1, osFlagsWaitAny, osWaitForever); |  | ||||||
|     Adv_Update( ); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void Adv_Update( void ) { |  | ||||||
|   Adv_Request(APP_BLE_LP_ADV); |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void HciUserEvtProcess(void *argument) { | static void HciUserEvtProcess(void *argument) { | ||||||
|   UNUSED(argument); |   UNUSED(argument); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -7,20 +7,7 @@ extern "C" { | |||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include "hci_tl.h" | #include "hci_tl.h" | ||||||
| 
 | 
 | ||||||
| typedef enum { |  | ||||||
|     APP_BLE_IDLE, |  | ||||||
|     APP_BLE_FAST_ADV, |  | ||||||
|     APP_BLE_LP_ADV, |  | ||||||
|     APP_BLE_SCAN, |  | ||||||
|     APP_BLE_LP_CONNECTING, |  | ||||||
|     APP_BLE_CONNECTED_SERVER, |  | ||||||
|     APP_BLE_CONNECTED_CLIENT |  | ||||||
| } APP_BLE_ConnStatus_t; |  | ||||||
| 
 |  | ||||||
| bool APP_BLE_Init(); | bool APP_BLE_Init(); | ||||||
| bool APP_BLE_Start(); |  | ||||||
| 
 |  | ||||||
| APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status(); |  | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
|  | |||||||
| @ -139,7 +139,7 @@ | |||||||
| /**
 | /**
 | ||||||
|  * Maximum supported ATT_MTU size |  * Maximum supported ATT_MTU size | ||||||
|  */ |  */ | ||||||
| #define CFG_BLE_MAX_ATT_MTU             (156) | #define CFG_BLE_MAX_ATT_MTU             (251) | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Size of the storage area for Attribute values |  * Size of the storage area for Attribute values | ||||||
|  | |||||||
| @ -11,21 +11,22 @@ typedef struct { | |||||||
|     uint16_t char_level_handle; |     uint16_t char_level_handle; | ||||||
| } BatterySvc; | } BatterySvc; | ||||||
| 
 | 
 | ||||||
| static BatterySvc battery_svc; | static BatterySvc* battery_svc = NULL; | ||||||
| 
 | 
 | ||||||
| bool battery_svc_init() { | static const uint16_t service_uuid = BATTERY_SERVICE_UUID; | ||||||
|  | static const uint16_t char_battery_level_uuid = BATTERY_LEVEL_CHAR_UUID; | ||||||
|  | 
 | ||||||
|  | void battery_svc_start() { | ||||||
|  |     battery_svc = furi_alloc(sizeof(BatterySvc)); | ||||||
|     tBleStatus status; |     tBleStatus status; | ||||||
|     const uint16_t service_uuid = BATTERY_SERVICE_UUID; |  | ||||||
|     const uint16_t char_battery_level_uuid = BATTERY_LEVEL_CHAR_UUID; |  | ||||||
| 
 | 
 | ||||||
|     // Add Battery service
 |     // Add Battery service
 | ||||||
|     status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&service_uuid, PRIMARY_SERVICE, 4, &battery_svc.svc_handle); |     status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&service_uuid, PRIMARY_SERVICE, 4, &battery_svc->svc_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery service: %d", status); |         FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery service: %d", status); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     // Add Battery level characteristic
 |     // Add Battery level characteristic
 | ||||||
|     status = aci_gatt_add_char(battery_svc.svc_handle, |     status = aci_gatt_add_char(battery_svc->svc_handle, | ||||||
|                                 UUID_TYPE_16, |                                 UUID_TYPE_16, | ||||||
|                                 (Char_UUID_t *) &char_battery_level_uuid, |                                 (Char_UUID_t *) &char_battery_level_uuid, | ||||||
|                                 1, |                                 1, | ||||||
| @ -34,17 +35,39 @@ bool battery_svc_init() { | |||||||
|                                 GATT_DONT_NOTIFY_EVENTS, |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|                                 10, |                                 10, | ||||||
|                                 CHAR_VALUE_LEN_CONSTANT, |                                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|                                 &battery_svc.char_level_handle); |                                 &battery_svc->char_level_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery level characteristic: %d", status); |         FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery level characteristic: %d", status); | ||||||
|     } |     } | ||||||
|     return status != BLE_STATUS_SUCCESS; | } | ||||||
|  | 
 | ||||||
|  | void battery_svc_stop() { | ||||||
|  |     tBleStatus status; | ||||||
|  |     if(battery_svc) { | ||||||
|  |         // Delete Battery level characteristic
 | ||||||
|  |         status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->char_level_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to delete Battery level characteristic: %d", status); | ||||||
|  |         } | ||||||
|  |         // Delete Battery service
 | ||||||
|  |         status = aci_gatt_del_service(battery_svc->svc_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to delete Battery service: %d", status); | ||||||
|  |         } | ||||||
|  |         free(battery_svc); | ||||||
|  |         battery_svc = NULL; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool battery_svc_update_level(uint8_t battery_charge) { | bool battery_svc_update_level(uint8_t battery_charge) { | ||||||
|  |     // Check if service was started
 | ||||||
|  |     if(battery_svc == NULL) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     // Update battery level characteristic
 | ||||||
|     FURI_LOG_I(BATTERY_SERVICE_TAG, "Updating battery level characteristic"); |     FURI_LOG_I(BATTERY_SERVICE_TAG, "Updating battery level characteristic"); | ||||||
|     tBleStatus result = aci_gatt_update_char_value(battery_svc.svc_handle, |     tBleStatus result = aci_gatt_update_char_value(battery_svc->svc_handle, | ||||||
|                                           battery_svc.char_level_handle, |                                           battery_svc->char_level_handle, | ||||||
|                                           0, |                                           0, | ||||||
|                                           1, |                                           1, | ||||||
|                                           &battery_charge); |                                           &battery_charge); | ||||||
|  | |||||||
| @ -7,7 +7,9 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| bool battery_svc_init(); | void battery_svc_start(); | ||||||
|  | 
 | ||||||
|  | void battery_svc_stop(); | ||||||
| 
 | 
 | ||||||
| bool battery_svc_update_level(uint8_t battery_level); | bool battery_svc_update_level(uint8_t battery_level); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ | |||||||
| 
 | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
| 
 | 
 | ||||||
| #define DEV_INFO_SERVICE_TAG "dev info service" | #define DEV_INFO_SVC_TAG "dev info service" | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     uint16_t service_handle; |     uint16_t service_handle; | ||||||
| @ -14,108 +14,143 @@ typedef struct { | |||||||
|     uint16_t software_rev_char_handle; |     uint16_t software_rev_char_handle; | ||||||
| } DevInfoSvc; | } DevInfoSvc; | ||||||
| 
 | 
 | ||||||
| bool dev_info_service_init() { | static DevInfoSvc* dev_info_svc = NULL; | ||||||
|  | 
 | ||||||
|  | static const char dev_info_man_name[] = "Flipper Devices Inc."; | ||||||
|  | static const char dev_info_serial_num[] = "1.0"; | ||||||
|  | static const char dev_info_firmware_rev_num[] = TARGET; | ||||||
|  | static const char dev_info_software_rev_num[] = GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE; | ||||||
|  | 
 | ||||||
|  | void dev_info_svc_start() { | ||||||
|  |     dev_info_svc = furi_alloc(sizeof(DevInfoSvc)); | ||||||
|     tBleStatus status; |     tBleStatus status; | ||||||
|     DevInfoSvc dev_info_svc; |  | ||||||
| 
 | 
 | ||||||
|     // Add Device Information Service
 |     // Add Device Information Service
 | ||||||
|     uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; |     uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; | ||||||
|     status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 9, &dev_info_svc.service_handle); |     status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 9, &dev_info_svc->service_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add Device Information Service: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add Device Information Service: %d", status); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Add characteristics
 |     // Add characteristics
 | ||||||
|     uuid = MANUFACTURER_NAME_UUID; |     uuid = MANUFACTURER_NAME_UUID; | ||||||
|     status = aci_gatt_add_char(dev_info_svc.service_handle, |     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||||
|                                 UUID_TYPE_16, |                                 UUID_TYPE_16, | ||||||
|                                 (Char_UUID_t*)&uuid, |                                 (Char_UUID_t*)&uuid, | ||||||
|                                 strlen(DEV_INFO_MANUFACTURER_NAME), |                                 strlen(dev_info_man_name), | ||||||
|                                 CHAR_PROP_READ, |                                 CHAR_PROP_READ, | ||||||
|                                 ATTR_PERMISSION_NONE, |                                 ATTR_PERMISSION_NONE, | ||||||
|                                 GATT_DONT_NOTIFY_EVENTS, |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|                                 10, |                                 10, | ||||||
|                                 CHAR_VALUE_LEN_CONSTANT, |                                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|                                 &dev_info_svc.man_name_char_handle); |                                 &dev_info_svc->man_name_char_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add manufacturer name char: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add manufacturer name char: %d", status); | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
|     uuid = SERIAL_NUMBER_UUID; |     uuid = SERIAL_NUMBER_UUID; | ||||||
|     status = aci_gatt_add_char(dev_info_svc.service_handle, |     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||||
|                                 UUID_TYPE_16, |                                 UUID_TYPE_16, | ||||||
|                                 (Char_UUID_t*)&uuid, |                                 (Char_UUID_t*)&uuid, | ||||||
|                                 strlen(DEV_INFO_SERIAL_NUMBER), |                                 strlen(dev_info_serial_num), | ||||||
|                                 CHAR_PROP_READ, |                                 CHAR_PROP_READ, | ||||||
|                                 ATTR_PERMISSION_NONE, |                                 ATTR_PERMISSION_NONE, | ||||||
|                                 GATT_DONT_NOTIFY_EVENTS, |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|                                 10, |                                 10, | ||||||
|                                 CHAR_VALUE_LEN_CONSTANT, |                                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|                                 &dev_info_svc.serial_num_char_handle); |                                 &dev_info_svc->serial_num_char_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add serial number char: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add serial number char: %d", status); | ||||||
|     } |     } | ||||||
|     uuid = FIRMWARE_REVISION_UUID; |     uuid = FIRMWARE_REVISION_UUID; | ||||||
|     status = aci_gatt_add_char(dev_info_svc.service_handle, |     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||||
|                                 UUID_TYPE_16, |                                 UUID_TYPE_16, | ||||||
|                                 (Char_UUID_t*)&uuid, |                                 (Char_UUID_t*)&uuid, | ||||||
|                                 strlen(DEV_INFO_FIRMWARE_REVISION_NUMBER), |                                 strlen(dev_info_firmware_rev_num), | ||||||
|                                 CHAR_PROP_READ, |                                 CHAR_PROP_READ, | ||||||
|                                 ATTR_PERMISSION_NONE, |                                 ATTR_PERMISSION_NONE, | ||||||
|                                 GATT_DONT_NOTIFY_EVENTS, |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|                                 10, |                                 10, | ||||||
|                                 CHAR_VALUE_LEN_CONSTANT, |                                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|                                 &dev_info_svc.firmware_rev_char_handle); |                                 &dev_info_svc->firmware_rev_char_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add firmware revision char: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add firmware revision char: %d", status); | ||||||
|     } |     } | ||||||
|     uuid = SOFTWARE_REVISION_UUID; |     uuid = SOFTWARE_REVISION_UUID; | ||||||
|     status = aci_gatt_add_char(dev_info_svc.service_handle, |     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||||
|                                 UUID_TYPE_16, |                                 UUID_TYPE_16, | ||||||
|                                 (Char_UUID_t*)&uuid, |                                 (Char_UUID_t*)&uuid, | ||||||
|                                 strlen(DEV_INFO_SOFTWARE_REVISION_NUMBER), |                                 strlen(dev_info_software_rev_num), | ||||||
|                                 CHAR_PROP_READ, |                                 CHAR_PROP_READ, | ||||||
|                                 ATTR_PERMISSION_NONE, |                                 ATTR_PERMISSION_NONE, | ||||||
|                                 GATT_DONT_NOTIFY_EVENTS, |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|                                 10, |                                 10, | ||||||
|                                 CHAR_VALUE_LEN_CONSTANT, |                                 CHAR_VALUE_LEN_CONSTANT, | ||||||
|                                 &dev_info_svc.software_rev_char_handle); |                                 &dev_info_svc->software_rev_char_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add software revision char: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add software revision char: %d", status); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Update characteristics
 |     // Update characteristics
 | ||||||
|     status = aci_gatt_update_char_value(dev_info_svc.service_handle, |     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||||
|                                         dev_info_svc.man_name_char_handle, |                                         dev_info_svc->man_name_char_handle, | ||||||
|                                         0, |                                         0, | ||||||
|                                         strlen(DEV_INFO_MANUFACTURER_NAME), |                                         strlen(dev_info_man_name), | ||||||
|                                         (uint8_t*)DEV_INFO_MANUFACTURER_NAME); |                                         (uint8_t*)dev_info_man_name); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to update manufacturer name char: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update manufacturer name char: %d", status); | ||||||
|     } |     } | ||||||
|     status = aci_gatt_update_char_value(dev_info_svc.service_handle, |     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||||
|                                         dev_info_svc.serial_num_char_handle, |                                         dev_info_svc->serial_num_char_handle, | ||||||
|                                         0, |                                         0, | ||||||
|                                         strlen(DEV_INFO_SERIAL_NUMBER), |                                         strlen(dev_info_serial_num), | ||||||
|                                         (uint8_t*)DEV_INFO_SERIAL_NUMBER); |                                         (uint8_t*)dev_info_serial_num); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to update serial number char: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update serial number char: %d", status); | ||||||
|     } |     } | ||||||
|     status = aci_gatt_update_char_value(dev_info_svc.service_handle, |     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||||
|                                         dev_info_svc.firmware_rev_char_handle, |                                         dev_info_svc->firmware_rev_char_handle, | ||||||
|                                         0, |                                         0, | ||||||
|                                         strlen(DEV_INFO_FIRMWARE_REVISION_NUMBER), |                                         strlen(dev_info_firmware_rev_num), | ||||||
|                                         (uint8_t*)DEV_INFO_FIRMWARE_REVISION_NUMBER); |                                         (uint8_t*)dev_info_firmware_rev_num); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to update firmware revision char: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update firmware revision char: %d", status); | ||||||
|     } |     } | ||||||
|     status = aci_gatt_update_char_value(dev_info_svc.service_handle, |     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||||
|                                         dev_info_svc.software_rev_char_handle, |                                         dev_info_svc->software_rev_char_handle, | ||||||
|                                         0, |                                         0, | ||||||
|                                         strlen(DEV_INFO_SOFTWARE_REVISION_NUMBER), |                                         strlen(dev_info_software_rev_num), | ||||||
|                                         (uint8_t*)DEV_INFO_SOFTWARE_REVISION_NUMBER); |                                         (uint8_t*)dev_info_software_rev_num); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to update software revision char: %d", status); |         FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update software revision char: %d", status); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dev_info_svc_stop() { | ||||||
|  |     tBleStatus status; | ||||||
|  |     if(dev_info_svc) { | ||||||
|  |         // Delete service characteristics
 | ||||||
|  |         status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->man_name_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete manufacturer name char: %d", status); | ||||||
|  |         } | ||||||
|  |         status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->serial_num_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete serial number char: %d", status); | ||||||
|  |         } | ||||||
|  |         status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->firmware_rev_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete firmware revision char: %d", status); | ||||||
|  |         } | ||||||
|  |         status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->software_rev_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete software revision char: %d", status); | ||||||
|  |         } | ||||||
|  |         // Delete service
 | ||||||
|  |         status = aci_gatt_del_service(dev_info_svc->service_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete device info service: %d", status); | ||||||
|  |         } | ||||||
|  |         free(dev_info_svc); | ||||||
|  |         dev_info_svc = NULL; | ||||||
|     } |     } | ||||||
|     return status != BLE_STATUS_SUCCESS; |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -12,8 +12,9 @@ extern "C" { | |||||||
| #define DEV_INFO_FIRMWARE_REVISION_NUMBER       TARGET | #define DEV_INFO_FIRMWARE_REVISION_NUMBER       TARGET | ||||||
| #define DEV_INFO_SOFTWARE_REVISION_NUMBER       GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE | #define DEV_INFO_SOFTWARE_REVISION_NUMBER       GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE | ||||||
| 
 | 
 | ||||||
|  | void dev_info_svc_start(); | ||||||
| 
 | 
 | ||||||
| bool dev_info_service_init(); | void dev_info_svc_stop(); | ||||||
| 
 | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										387
									
								
								firmware/targets/f7/ble-glue/gap.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										387
									
								
								firmware/targets/f7/ble-glue/gap.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,387 @@ | |||||||
|  | #include "gap.h" | ||||||
|  | 
 | ||||||
|  | #include "app_entry.h" | ||||||
|  | #include "ble.h" | ||||||
|  | 
 | ||||||
|  | #include "cmsis_os.h" | ||||||
|  | #include "otp.h" | ||||||
|  | #include "dev_info_service.h" | ||||||
|  | #include "battery_service.h" | ||||||
|  | #include "serial_service.h" | ||||||
|  | 
 | ||||||
|  | #include <applications/bt/bt_service/bt.h> | ||||||
|  | #include <furi-hal.h> | ||||||
|  | 
 | ||||||
|  | #define GAP_TAG "BLE" | ||||||
|  | 
 | ||||||
|  | #define FAST_ADV_TIMEOUT 30000 | ||||||
|  | #define INITIAL_ADV_TIMEOUT 60000 | ||||||
|  | 
 | ||||||
|  | #define BD_ADDR_SIZE_LOCAL 6 | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |   uint16_t gap_svc_handle; | ||||||
|  |   uint16_t dev_name_char_handle; | ||||||
|  |   uint16_t appearance_char_handle; | ||||||
|  |   uint16_t connection_handle; | ||||||
|  |   uint8_t adv_svc_uuid_len; | ||||||
|  |   uint8_t adv_svc_uuid[20]; | ||||||
|  | } GapSvc; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |   GapSvc gap_svc; | ||||||
|  |   GapState state; | ||||||
|  |   uint8_t mac_address[BD_ADDR_SIZE_LOCAL]; | ||||||
|  |   Bt* bt; | ||||||
|  |   osTimerId advertise_timer; | ||||||
|  |   osThreadAttr_t thread_attr; | ||||||
|  |   osThreadId_t thread_id; | ||||||
|  | } Gap; | ||||||
|  | 
 | ||||||
|  | // Identity root key
 | ||||||
|  | static const uint8_t gap_irk[16] = {0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0,0x12,0x34,0x56,0x78,0x9a,0xbc,0xde,0xf0}; | ||||||
|  | // Encryption root key
 | ||||||
|  | static const uint8_t gap_erk[16] = {0xfe,0xdc,0xba,0x09,0x87,0x65,0x43,0x21,0xfe,0xdc,0xba,0x09,0x87,0x65,0x43,0x21}; | ||||||
|  | // Appearence characteristic UUID
 | ||||||
|  | static const uint8_t gap_appearence_char_uuid[] = {0x00, 0x86}; | ||||||
|  | // Default MAC address
 | ||||||
|  | static const uint8_t gap_default_mac_addr[] = {0x6c, 0x7a, 0xd8, 0xac, 0x57, 0x72}; | ||||||
|  | 
 | ||||||
|  | static Gap* gap = NULL; | ||||||
|  | 
 | ||||||
|  | static void gap_advertise(GapState new_state); | ||||||
|  | static void gap_app(void *arg); | ||||||
|  | 
 | ||||||
|  | SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt ) | ||||||
|  | { | ||||||
|  |   hci_event_pckt *event_pckt; | ||||||
|  |   evt_le_meta_event *meta_evt; | ||||||
|  |   evt_blue_aci *blue_evt; | ||||||
|  |   hci_le_phy_update_complete_event_rp0 *evt_le_phy_update_complete; | ||||||
|  |   uint8_t tx_phy; | ||||||
|  |   uint8_t rx_phy; | ||||||
|  |   tBleStatus ret = BLE_STATUS_INVALID_PARAMS; | ||||||
|  | 
 | ||||||
|  |   event_pckt = (hci_event_pckt*) ((hci_uart_pckt *) pckt)->data; | ||||||
|  | 
 | ||||||
|  |   switch (event_pckt->evt) { | ||||||
|  |     case EVT_DISCONN_COMPLETE: | ||||||
|  |     { | ||||||
|  |         hci_disconnection_complete_event_rp0 *disconnection_complete_event = (hci_disconnection_complete_event_rp0 *) event_pckt->data; | ||||||
|  |         if (disconnection_complete_event->Connection_Handle == gap->gap_svc.connection_handle) { | ||||||
|  |             gap->gap_svc.connection_handle = 0; | ||||||
|  |             gap->state = GapStateIdle; | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Disconnect from client"); | ||||||
|  |         } | ||||||
|  |         // Restart advertising
 | ||||||
|  |         gap_advertise(GapStateAdvFast); | ||||||
|  |         furi_hal_power_insomnia_exit(); | ||||||
|  |     } | ||||||
|  |     break; | ||||||
|  | 
 | ||||||
|  |     case EVT_LE_META_EVENT: | ||||||
|  |         meta_evt = (evt_le_meta_event*) event_pckt->data; | ||||||
|  |         switch (meta_evt->subevent) { | ||||||
|  |             case EVT_LE_CONN_UPDATE_COMPLETE: | ||||||
|  |             FURI_LOG_D(GAP_TAG, "Connection update event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |             case EVT_LE_PHY_UPDATE_COMPLETE: | ||||||
|  |             evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data; | ||||||
|  |             if(evt_le_phy_update_complete->Status) { | ||||||
|  |                 FURI_LOG_E(GAP_TAG, "Update PHY failed, status %d", evt_le_phy_update_complete->Status); | ||||||
|  |             } else { | ||||||
|  |                 FURI_LOG_I(GAP_TAG, "Update PHY succeed"); | ||||||
|  |             } | ||||||
|  |             ret = hci_le_read_phy(gap->gap_svc.connection_handle,&tx_phy,&rx_phy); | ||||||
|  |             if(ret) { | ||||||
|  |                 FURI_LOG_E(GAP_TAG, "Read PHY failed, status: %d", ret); | ||||||
|  |             } else { | ||||||
|  |                 FURI_LOG_I(GAP_TAG, "PHY Params TX= %d, RX= %d ", tx_phy, rx_phy); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |             case EVT_LE_CONN_COMPLETE: | ||||||
|  |             furi_hal_power_insomnia_enter(); | ||||||
|  |             hci_le_connection_complete_event_rp0* connection_complete_event = (hci_le_connection_complete_event_rp0 *) meta_evt->data; | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Connection complete for connection handle 0x%x", connection_complete_event->Connection_Handle); | ||||||
|  | 
 | ||||||
|  |             // Stop advertising as connection completed
 | ||||||
|  |             osTimerStop(gap->advertise_timer); | ||||||
|  | 
 | ||||||
|  |             // Update connection status and handle
 | ||||||
|  |             gap->state = GapStateConnected; | ||||||
|  |             gap->gap_svc.connection_handle = connection_complete_event->Connection_Handle; | ||||||
|  | 
 | ||||||
|  |             // Start pairing by sending security request
 | ||||||
|  |             aci_gap_slave_security_req(connection_complete_event->Connection_Handle); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |             default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     break; | ||||||
|  | 
 | ||||||
|  |     case EVT_VENDOR: | ||||||
|  |         blue_evt = (evt_blue_aci*) event_pckt->data; | ||||||
|  |         switch (blue_evt->ecode) { | ||||||
|  |             aci_gap_pairing_complete_event_rp0 *pairing_complete; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_LIMITED_DISCOVERABLE: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Limited discoverable event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_PASS_KEY_REQUEST: | ||||||
|  |         { | ||||||
|  |             // Generate random PIN code
 | ||||||
|  |             uint32_t pin = rand() % 999999; | ||||||
|  |             aci_gap_pass_key_resp(gap->gap_svc.connection_handle, pin); | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Pass key request event. Pin: %d", pin); | ||||||
|  |             bt_pin_code_show(gap->bt, pin); | ||||||
|  |         } | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_AUTHORIZATION_REQUEST: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Authorization request event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Slave security initiated"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_BOND_LOST: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Bond lost event. Start rebonding"); | ||||||
|  |             aci_gap_allow_rebond(gap->gap_svc.connection_handle); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_DEVICE_FOUND: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Device found event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_ADDR_NOT_RESOLVED: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Address not resolved event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_KEYPRESS_NOTIFICATION: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Key press notification event"); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Hex_value = %lx", | ||||||
|  |                         ((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value); | ||||||
|  |             aci_gap_numeric_comparison_value_confirm_yesno(gap->gap_svc.connection_handle, 1); | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case (EVT_BLUE_GAP_PAIRING_CMPLT): | ||||||
|  |         { | ||||||
|  |             pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data; | ||||||
|  |             if (pairing_complete->Status) { | ||||||
|  |             FURI_LOG_E(GAP_TAG, "Pairing failed with status: %d. Terminating connection", pairing_complete->Status); | ||||||
|  |             aci_gap_terminate(gap->gap_svc.connection_handle, 5); | ||||||
|  |             } else { | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Pairing complete"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |             break; | ||||||
|  | 
 | ||||||
|  |         case EVT_BLUE_GAP_PROCEDURE_COMPLETE: | ||||||
|  |             FURI_LOG_I(GAP_TAG, "Procedure complete event"); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return SVCCTL_UserEvtFlowEnable; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SVCCTL_SvcInit() { | ||||||
|  |     // Dummy function to prevent unused services initialization
 | ||||||
|  |     // TODO refactor (disable all services in WPAN config)
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void set_advertisment_service_uid(uint8_t* uid, uint8_t uid_len) { | ||||||
|  |     gap->gap_svc.adv_svc_uuid_len = 1; | ||||||
|  |     if(uid_len == 2) { | ||||||
|  |         gap->gap_svc.adv_svc_uuid[0] = AD_TYPE_16_BIT_SERV_UUID; | ||||||
|  |     } else if (uid_len == 4) { | ||||||
|  |         gap->gap_svc.adv_svc_uuid[0] = AD_TYPE_32_BIT_SERV_UUID; | ||||||
|  |     } else if(uid_len == 16) { | ||||||
|  |         gap->gap_svc.adv_svc_uuid[0] = AD_TYPE_128_BIT_SERV_UUID_CMPLT_LIST; | ||||||
|  |     } | ||||||
|  |     memcpy(&gap->gap_svc.adv_svc_uuid[1], uid, uid_len); | ||||||
|  |     gap->gap_svc.adv_svc_uuid_len += uid_len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GapState gap_get_status() { | ||||||
|  |     return gap->state; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void gap_init_mac_address(Gap* gap) { | ||||||
|  |     uint8_t *otp_addr; | ||||||
|  |     uint32_t udn; | ||||||
|  |     uint32_t company_id; | ||||||
|  |     uint32_t device_id; | ||||||
|  | 
 | ||||||
|  |     udn = LL_FLASH_GetUDN(); | ||||||
|  |     if(udn != 0xFFFFFFFF) { | ||||||
|  |         company_id = LL_FLASH_GetSTCompanyID(); | ||||||
|  |         device_id = LL_FLASH_GetDeviceID(); | ||||||
|  |         gap->mac_address[0] = (uint8_t)(udn & 0x000000FF); | ||||||
|  |         gap->mac_address[1] = (uint8_t)( (udn & 0x0000FF00) >> 8 ); | ||||||
|  |         gap->mac_address[2] = (uint8_t)( (udn & 0x00FF0000) >> 16 ); | ||||||
|  |         gap->mac_address[3] = (uint8_t)device_id; | ||||||
|  |         gap->mac_address[4] = (uint8_t)(company_id & 0x000000FF);; | ||||||
|  |         gap->mac_address[5] = (uint8_t)( (company_id & 0x0000FF00) >> 8 ); | ||||||
|  |     } else { | ||||||
|  |         otp_addr = OTP_Read(0); | ||||||
|  |         if(otp_addr) { | ||||||
|  |             memcpy(gap->mac_address, ((OTP_ID0_t*)otp_addr)->bd_address, sizeof(gap->mac_address)); | ||||||
|  |         } else { | ||||||
|  |             memcpy(gap->mac_address, gap_default_mac_addr, sizeof(gap->mac_address)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gap_init_svc(Gap* gap) { | ||||||
|  |     tBleStatus status; | ||||||
|  |     uint32_t srd_bd_addr[2]; | ||||||
|  | 
 | ||||||
|  |     //HCI Reset to synchronise BLE Stack*/
 | ||||||
|  |     hci_reset(); | ||||||
|  |     // Configure mac address
 | ||||||
|  |     gap_init_mac_address(gap); | ||||||
|  |     aci_hal_write_config_data(CONFIG_DATA_PUBADDR_OFFSET, CONFIG_DATA_PUBADDR_LEN, (uint8_t*)gap->mac_address); | ||||||
|  | 
 | ||||||
|  |     /* Static random Address
 | ||||||
|  |      * The two upper bits shall be set to 1 | ||||||
|  |      * The lowest 32bits is read from the UDN to differentiate between devices | ||||||
|  |      * The RNG may be used to provide a random number on each power on | ||||||
|  |      */ | ||||||
|  |     srd_bd_addr[1] = 0x0000ED6E; | ||||||
|  |     srd_bd_addr[0] = LL_FLASH_GetUDN(); | ||||||
|  |     aci_hal_write_config_data( CONFIG_DATA_RANDOM_ADDRESS_OFFSET, CONFIG_DATA_RANDOM_ADDRESS_LEN, (uint8_t*)srd_bd_addr ); | ||||||
|  |     // Set Identity root key used to derive LTK and CSRK
 | ||||||
|  |     aci_hal_write_config_data( CONFIG_DATA_IR_OFFSET, CONFIG_DATA_IR_LEN, (uint8_t*)gap_irk ); | ||||||
|  |     // Set Encryption root key used to derive LTK and CSRK
 | ||||||
|  |     aci_hal_write_config_data( CONFIG_DATA_ER_OFFSET, CONFIG_DATA_ER_LEN, (uint8_t*)gap_erk ); | ||||||
|  |     // Set TX Power to 0 dBm
 | ||||||
|  |     aci_hal_set_tx_power_level(1, 0x19); | ||||||
|  |     // Initialize GATT interface
 | ||||||
|  |     aci_gatt_init(); | ||||||
|  |     // Initialize GAP interface
 | ||||||
|  |     const char *name = furi_hal_version_get_device_name_ptr(); | ||||||
|  |     aci_gap_init(GAP_PERIPHERAL_ROLE, 0, strlen(name), | ||||||
|  |                 &gap->gap_svc.gap_svc_handle, &gap->gap_svc.dev_name_char_handle, &gap->gap_svc.appearance_char_handle); | ||||||
|  | 
 | ||||||
|  |     // Set GAP characteristics
 | ||||||
|  |     status = aci_gatt_update_char_value(gap->gap_svc.gap_svc_handle, gap->gap_svc.dev_name_char_handle, 0, strlen(name), (uint8_t *) name); | ||||||
|  |     if (status) { | ||||||
|  |         FURI_LOG_E(GAP_TAG, "Failed updating name characteristic: %d", status); | ||||||
|  |     } | ||||||
|  |     status = aci_gatt_update_char_value(gap->gap_svc.gap_svc_handle, gap->gap_svc.appearance_char_handle, 0, 2, gap_appearence_char_uuid); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(GAP_TAG, "Failed updating appearence characteristic: %d", status); | ||||||
|  |     } | ||||||
|  |     // Set default PHY
 | ||||||
|  |     hci_le_set_default_phy(ALL_PHYS_PREFERENCE, TX_2M_PREFERRED, RX_2M_PREFERRED); | ||||||
|  |     // Set I/O capability
 | ||||||
|  |     aci_gap_set_io_capability(IO_CAP_DISPLAY_ONLY); | ||||||
|  |     // Setup  authentication
 | ||||||
|  |     aci_gap_set_authentication_requirement(1, 1, 1, 0, 8, 16, 1, 0, PUBLIC_ADDR); | ||||||
|  |     // Configure whitelist
 | ||||||
|  |     aci_gap_configure_whitelist(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gap_advertise(GapState new_state) | ||||||
|  | { | ||||||
|  |     tBleStatus status; | ||||||
|  |     uint16_t min_interval; | ||||||
|  |     uint16_t max_interval; | ||||||
|  | 
 | ||||||
|  |     if (new_state == GapStateAdvFast) { | ||||||
|  |         min_interval = 0x80; // 80 ms
 | ||||||
|  |         max_interval = 0xa0; // 100 ms
 | ||||||
|  |     } else { | ||||||
|  |         min_interval = 0x0640; // 1 s
 | ||||||
|  |         max_interval = 0x0fa0; // 2.5 s
 | ||||||
|  |     } | ||||||
|  |     // Stop advertising timer
 | ||||||
|  |     osTimerStop(gap->advertise_timer); | ||||||
|  | 
 | ||||||
|  |     if ((new_state == GapStateAdvLowPower) && ((gap->state == GapStateAdvFast) || (gap->state == GapStateAdvLowPower))) { | ||||||
|  |         // Stop advertising
 | ||||||
|  |         status = aci_gap_set_non_discoverable(); | ||||||
|  |         if (status) { | ||||||
|  |             FURI_LOG_E(GAP_TAG, "Stop Advertising Failed, result: %d", status); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     // Configure advertising
 | ||||||
|  |     gap->state = new_state; | ||||||
|  |     const char* name = furi_hal_version_get_ble_local_device_name_ptr(); | ||||||
|  |     status = aci_gap_set_discoverable(ADV_IND, min_interval, max_interval, PUBLIC_ADDR, 0, | ||||||
|  |                                         strlen(name), (uint8_t*)name, | ||||||
|  |                                         gap->gap_svc.adv_svc_uuid_len, gap->gap_svc.adv_svc_uuid, 0, 0); | ||||||
|  |     if(status) { | ||||||
|  |         FURI_LOG_E(GAP_TAG, "Set discoverable err: %d", status); | ||||||
|  |     } | ||||||
|  |     osTimerStart(gap->advertise_timer, INITIAL_ADV_TIMEOUT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gap_advertise_request(Gap* gap) { | ||||||
|  |     osThreadFlagsSet(gap->thread_id, 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gap_advetise_timer_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     Gap* gap = context; | ||||||
|  |     gap_advertise_request(gap); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool gap_init() { | ||||||
|  |     if (APPE_Status() != BleGlueStatusStarted) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     gap = furi_alloc(sizeof(Gap)); | ||||||
|  |     srand(DWT->CYCCNT); | ||||||
|  |     // Open Bt record
 | ||||||
|  |     gap->bt = furi_record_open("bt"); | ||||||
|  |     // Create advertising timer
 | ||||||
|  |     gap->advertise_timer = osTimerNew(gap_advetise_timer_callback, osTimerOnce, &gap, NULL); | ||||||
|  |     // Initialization of HCI & GATT & GAP layer
 | ||||||
|  |     gap_init_svc(gap); | ||||||
|  |     // Initialization of the BLE Services
 | ||||||
|  |     SVCCTL_Init(); | ||||||
|  |     // Initialization of the BLE App Context
 | ||||||
|  |     gap->state = GapStateIdle; | ||||||
|  |     gap->gap_svc.connection_handle = 0xFFFF; | ||||||
|  | 
 | ||||||
|  |     // Thread configuration
 | ||||||
|  |     gap->thread_attr.name = "BLE advertising"; | ||||||
|  |     gap->thread_attr.stack_size = 512; | ||||||
|  |     gap->thread_id = osThreadNew(gap_app, NULL, &gap->thread_attr); | ||||||
|  | 
 | ||||||
|  |     // Start Device Information service
 | ||||||
|  |     dev_info_svc_start(); | ||||||
|  |     // Start Battery service
 | ||||||
|  |     battery_svc_start(); | ||||||
|  |     // Start Serial application
 | ||||||
|  |     serial_svc_start(); | ||||||
|  |     // Configure advirtise service UUID
 | ||||||
|  |     uint8_t adv_service_uid[2]; | ||||||
|  |     adv_service_uid[0] = 0x80 | furi_hal_version_get_hw_color(); | ||||||
|  |     adv_service_uid[1] = 0x30; | ||||||
|  | 
 | ||||||
|  |     set_advertisment_service_uid(adv_service_uid, sizeof(adv_service_uid)); | ||||||
|  |     gap_advertise(GapStateAdvFast); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void gap_app(void *arg) { | ||||||
|  |     // TODO Exit from app, stop service, clean memory
 | ||||||
|  |     while(1) { | ||||||
|  |         osThreadFlagsWait(1, osFlagsWaitAny, osWaitForever); | ||||||
|  |         gap_advertise(GapStateAdvLowPower); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								firmware/targets/f7/ble-glue/gap.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								firmware/targets/f7/ble-glue/gap.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdbool.h> | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     GapStateIdle, | ||||||
|  |     GapStateAdvFast, | ||||||
|  |     GapStateAdvLowPower, | ||||||
|  |     GapStateConnected, | ||||||
|  | } GapState; | ||||||
|  | 
 | ||||||
|  | bool gap_init(); | ||||||
|  | 
 | ||||||
|  | GapState gap_get_status(); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
| @ -6,13 +6,19 @@ | |||||||
| 
 | 
 | ||||||
| #define SERIAL_SERVICE_TAG "serial service" | #define SERIAL_SERVICE_TAG "serial service" | ||||||
| 
 | 
 | ||||||
|  | #define SERIAL_SVC_DATA_LEN_MAX 245 | ||||||
|  | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     uint16_t svc_handle; |     uint16_t svc_handle; | ||||||
|     uint16_t rx_char_handle; |     uint16_t rx_char_handle; | ||||||
|     uint16_t tx_char_handle; |     uint16_t tx_char_handle; | ||||||
| } SerialSvc; | } SerialSvc; | ||||||
| 
 | 
 | ||||||
| static SerialSvc serial_svc; | static SerialSvc* serial_svc; | ||||||
|  | 
 | ||||||
|  | static const uint8_t service_uuid[] = {0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f}; | ||||||
|  | static const uint8_t char_rx_uuid[] = {0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; | ||||||
|  | static const uint8_t char_tx_uuid[] = {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19}; | ||||||
| 
 | 
 | ||||||
| static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) { | static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) { | ||||||
|     SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; |     SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; | ||||||
| @ -22,76 +28,90 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) { | |||||||
|     if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { |     if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { | ||||||
|         if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { |         if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { | ||||||
|             attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data; |             attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data; | ||||||
|             if(attribute_modified->Attr_Handle == serial_svc.tx_char_handle + 2) { |             if(attribute_modified->Attr_Handle == serial_svc->tx_char_handle + 2) { | ||||||
|                 // Descriptor handle
 |                 // Descriptor handle
 | ||||||
|                 ret = SVCCTL_EvtAckFlowEnable; |                 ret = SVCCTL_EvtAckFlowEnable; | ||||||
|                 FURI_LOG_D(SERIAL_SERVICE_TAG, "TX descriptor event"); |                 FURI_LOG_D(SERIAL_SERVICE_TAG, "TX descriptor event"); | ||||||
|             } else if(attribute_modified->Attr_Handle == serial_svc.tx_char_handle + 1) { |             } else if(attribute_modified->Attr_Handle == serial_svc->tx_char_handle + 1) { | ||||||
|                 FURI_LOG_I(SERIAL_SERVICE_TAG, "Data len: %d", attribute_modified->Attr_Data_Length); |                 FURI_LOG_D(SERIAL_SERVICE_TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); | ||||||
|                 for(uint8_t i = 0; i < attribute_modified->Attr_Data_Length; i++) { |  | ||||||
|                     printf("%02X ", attribute_modified->Attr_Data[i]); |  | ||||||
|                 } |  | ||||||
|                 printf("\r\n"); |  | ||||||
|                 serial_svc_update_rx(attribute_modified->Attr_Data, attribute_modified->Attr_Data_Length); |                 serial_svc_update_rx(attribute_modified->Attr_Data, attribute_modified->Attr_Data_Length); | ||||||
|                 ret = SVCCTL_EvtAckFlowEnable; |                 ret = SVCCTL_EvtAckFlowEnable; | ||||||
|             } |             } | ||||||
|         } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { |         } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { | ||||||
|             FURI_LOG_I(SERIAL_SERVICE_TAG, "Ack received", blecore_evt->ecode); |             FURI_LOG_D(SERIAL_SERVICE_TAG, "Ack received", blecore_evt->ecode); | ||||||
|             ret = SVCCTL_EvtAckFlowEnable; |             ret = SVCCTL_EvtAckFlowEnable; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool serial_svc_init() { | void serial_svc_start() { | ||||||
|     tBleStatus status; |     tBleStatus status; | ||||||
|     const uint8_t service_uuid[] = {SERIAL_SVC_UUID_128}; |     serial_svc = furi_alloc(sizeof(SerialSvc)); | ||||||
|     const uint8_t char_rx_uuid[] = {SERIAL_CHAR_RX_UUID_128}; |  | ||||||
|     const uint8_t char_tx_uuid[] = {SERIAL_CHAR_TX_UUID_128}; |  | ||||||
| 
 |  | ||||||
|     // Register event handler
 |     // Register event handler
 | ||||||
|     SVCCTL_RegisterSvcHandler(serial_svc_event_handler); |     SVCCTL_RegisterSvcHandler(serial_svc_event_handler); | ||||||
| 
 | 
 | ||||||
|     // Add service
 |     // Add service
 | ||||||
|     status = aci_gatt_add_service(UUID_TYPE_128, (Service_UUID_t *)service_uuid, PRIMARY_SERVICE, 6, &serial_svc.svc_handle); |     status = aci_gatt_add_service(UUID_TYPE_128, (Service_UUID_t *)service_uuid, PRIMARY_SERVICE, 6, &serial_svc->svc_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status); |         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Add TX characteristics
 |     // Add TX characteristics
 | ||||||
|     status = aci_gatt_add_char(serial_svc.svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_tx_uuid , |     status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_tx_uuid, | ||||||
|                                 SERIAL_SVC_DATA_LEN_MAX, |                                 SERIAL_SVC_DATA_LEN_MAX, | ||||||
|                                 CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, |                                 CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, | ||||||
|                                 ATTR_PERMISSION_NONE, |                                 ATTR_PERMISSION_NONE, | ||||||
|                                 GATT_NOTIFY_ATTRIBUTE_WRITE, |                                 GATT_NOTIFY_ATTRIBUTE_WRITE, | ||||||
|                                 10, |                                 10, | ||||||
|                                 CHAR_VALUE_LEN_VARIABLE, |                                 CHAR_VALUE_LEN_VARIABLE, | ||||||
|                                 &serial_svc.tx_char_handle); |                                 &serial_svc->tx_char_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add TX characteristic: %d", status); |         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add TX characteristic: %d", status); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Add RX characteristic
 |     // Add RX characteristic
 | ||||||
|     status = aci_gatt_add_char(serial_svc.svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_rx_uuid , |     status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_rx_uuid, | ||||||
|                                 SERIAL_SVC_DATA_LEN_MAX,                                   |                                 SERIAL_SVC_DATA_LEN_MAX,                                   | ||||||
|                                 CHAR_PROP_READ | CHAR_PROP_INDICATE, |                                 CHAR_PROP_READ | CHAR_PROP_INDICATE, | ||||||
|                                 ATTR_PERMISSION_NONE, |                                 ATTR_PERMISSION_NONE, | ||||||
|                                 GATT_DONT_NOTIFY_EVENTS, |                                 GATT_DONT_NOTIFY_EVENTS, | ||||||
|                                 10, |                                 10, | ||||||
|                                 CHAR_VALUE_LEN_VARIABLE, |                                 CHAR_VALUE_LEN_VARIABLE, | ||||||
|                                 &serial_svc.rx_char_handle); |                                 &serial_svc->rx_char_handle); | ||||||
|     if(status) { |     if(status) { | ||||||
|         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add RX characteristic: %d", status); |         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add RX characteristic: %d", status); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     return status != BLE_STATUS_SUCCESS; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void serial_svc_stop() { | ||||||
|  |     tBleStatus status; | ||||||
|  |     if(serial_svc) { | ||||||
|  |         // Delete characteristics
 | ||||||
|  |         status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->tx_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete TX characteristic: %d", status); | ||||||
|  |         } | ||||||
|  |         status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rx_char_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete RX characteristic: %d", status); | ||||||
|  |         } | ||||||
|  |         // Delete service
 | ||||||
|  |         status = aci_gatt_del_service(serial_svc->svc_handle); | ||||||
|  |         if(status) { | ||||||
|  |             FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete Serial service: %d", status); | ||||||
|  |         } | ||||||
|  |         free(serial_svc); | ||||||
|  |         serial_svc = NULL; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| bool serial_svc_update_rx(uint8_t* data, uint8_t data_len) { | bool serial_svc_update_rx(uint8_t* data, uint8_t data_len) { | ||||||
|     furi_assert(data_len < SERIAL_SVC_DATA_LEN_MAX); |     furi_assert(data_len < SERIAL_SVC_DATA_LEN_MAX); | ||||||
| 
 | 
 | ||||||
|     tBleStatus result = aci_gatt_update_char_value(serial_svc.svc_handle, |     tBleStatus result = aci_gatt_update_char_value(serial_svc->svc_handle, | ||||||
|                                           serial_svc.rx_char_handle, |                                           serial_svc->rx_char_handle, | ||||||
|                                           0, |                                           0, | ||||||
|                                           data_len, |                                           data_len, | ||||||
|                                           data); |                                           data); | ||||||
|  | |||||||
| @ -7,13 +7,9 @@ | |||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #define SERIAL_SVC_DATA_LEN_MAX 255 | void serial_svc_start(); | ||||||
| 
 | 
 | ||||||
| #define SERIAL_SVC_UUID_128 0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f | void serial_svc_stop(); | ||||||
| #define SERIAL_CHAR_RX_UUID_128 0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 |  | ||||||
| #define SERIAL_CHAR_TX_UUID_128 0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19 |  | ||||||
| 
 |  | ||||||
| bool serial_svc_init(); |  | ||||||
| 
 | 
 | ||||||
| bool serial_svc_update_rx(uint8_t* data, uint8_t data_len); | bool serial_svc_update_rx(uint8_t* data, uint8_t data_len); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ | |||||||
| #include <shci.h> | #include <shci.h> | ||||||
| #include <cmsis_os2.h> | #include <cmsis_os2.h> | ||||||
| #include <app_ble.h> | #include <app_ble.h> | ||||||
|  | #include <gap.h> | ||||||
| 
 | 
 | ||||||
| void furi_hal_bt_init() { | void furi_hal_bt_init() { | ||||||
|     // Explicitly tell that we are in charge of CLK48 domain
 |     // Explicitly tell that we are in charge of CLK48 domain
 | ||||||
| @ -14,7 +15,7 @@ void furi_hal_bt_init() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool furi_hal_bt_start_app() { | bool furi_hal_bt_start_app() { | ||||||
|     return APP_BLE_Start(); |     return gap_init(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_bt_dump_state(string_t buffer) { | void furi_hal_bt_dump_state(string_t buffer) { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 gornekich
						gornekich