[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; | ||||
| } | ||||
| 
 | ||||
| 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 = furi_alloc(sizeof(Bt)); | ||||
|     // Load settings
 | ||||
| @ -41,13 +52,11 @@ Bt* bt_alloc() { | ||||
|     bt->gui = furi_record_open("gui"); | ||||
|     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) { | ||||
|     BtMessage message = { | ||||
|         .type = BtMessageTypeUpdateBatteryLevel, .data.battery_level = battery_level}; | ||||
|     return osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK; | ||||
|     return bt; | ||||
| } | ||||
| 
 | ||||
| int32_t bt_srv() { | ||||
| @ -76,9 +85,13 @@ int32_t bt_srv() { | ||||
|             // Update statusbar
 | ||||
|             view_port_enabled_set(bt->statusbar_view_port, furi_hal_bt_is_alive()); | ||||
|         } else if(message.type == BtMessageTypeUpdateBatteryLevel) { | ||||
|             // Update battery level
 | ||||
|             if(furi_hal_bt_is_alive()) { | ||||
|                 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; | ||||
|  | ||||
| @ -9,7 +9,9 @@ extern "C" { | ||||
| 
 | ||||
| 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 | ||||
| } | ||||
|  | ||||
							
								
								
									
										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.h> | ||||
| 
 | ||||
| #include <applications/dialogs/dialogs.h> | ||||
| 
 | ||||
| #include "../bt_settings.h" | ||||
| 
 | ||||
| typedef enum { | ||||
|     BtMessageTypeUpdateStatusbar, | ||||
|     BtMessageTypeUpdateBatteryLevel, | ||||
|     BtMessageTypePinCodeShow, | ||||
| } BtMessageType; | ||||
| 
 | ||||
| typedef union { | ||||
|     uint32_t pin_code; | ||||
|     uint8_t battery_level; | ||||
| } BtMessageData; | ||||
| 
 | ||||
| @ -31,4 +35,6 @@ struct Bt { | ||||
|     osTimerId_t update_status_timer; | ||||
|     Gui* gui; | ||||
|     ViewPort* statusbar_view_port; | ||||
|     DialogsApp* dialogs; | ||||
|     DialogMessage* dialog_message; | ||||
| }; | ||||
|  | ||||
| @ -6,106 +6,20 @@ | ||||
| #include "ble.h" | ||||
| #include "tl.h" | ||||
| #include "app_ble.h" | ||||
| 
 | ||||
| #include "cmsis_os.h" | ||||
| #include "shci.h" | ||||
| #include "otp.h" | ||||
| #include "dev_info_service.h" | ||||
| #include "battery_service.h" | ||||
| #include "serial_service.h" | ||||
| #include "cmsis_os.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; | ||||
| 
 | ||||
| static const uint8_t M_bd_addr[BD_ADDR_SIZE_LOCAL] = | ||||
|     { | ||||
|         (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 */ | ||||
| 
 | ||||
| }; | ||||
| // PLACE_IN_SECTION("TAG_OTA_END") const uint32_t MagicKeywordValue = 0x94448A29 ;
 | ||||
| // PLACE_IN_SECTION("TAG_OTA_START") const uint32_t MagicKeywordAddress = (uint32_t)&MagicKeywordValue;
 | ||||
| 
 | ||||
| osMutexId_t MtxHciId; | ||||
| osSemaphoreId_t SemHciId; | ||||
| osThreadId_t AdvUpdateProcessId; | ||||
| 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 = { | ||||
|     .name = CFG_HCI_USER_EVT_PROCESS_NAME, | ||||
|     .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_StatusNot( HCI_TL_CmdStatus_t status ); | ||||
| 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() { | ||||
|   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); | ||||
| } | ||||
| 
 | ||||
| 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 ) { | ||||
|   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); | ||||
| } | ||||
| 
 | ||||
| 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) { | ||||
|   UNUSED(argument); | ||||
| 
 | ||||
|  | ||||
| @ -7,20 +7,7 @@ extern "C" { | ||||
| #include <stdbool.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_Start(); | ||||
| 
 | ||||
| APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status(); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
|  | ||||
| @ -139,7 +139,7 @@ | ||||
| /**
 | ||||
|  * 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 | ||||
|  | ||||
| @ -11,21 +11,22 @@ typedef struct { | ||||
|     uint16_t char_level_handle; | ||||
| } 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; | ||||
|     const uint16_t service_uuid = BATTERY_SERVICE_UUID; | ||||
|     const uint16_t char_battery_level_uuid = BATTERY_LEVEL_CHAR_UUID; | ||||
| 
 | ||||
|     // 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) { | ||||
|         FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery service: %d", status); | ||||
|     } | ||||
| 
 | ||||
|     // 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, | ||||
|                                 (Char_UUID_t *) &char_battery_level_uuid, | ||||
|                                 1, | ||||
| @ -34,17 +35,39 @@ bool battery_svc_init() { | ||||
|                                 GATT_DONT_NOTIFY_EVENTS, | ||||
|                                 10, | ||||
|                                 CHAR_VALUE_LEN_CONSTANT, | ||||
|                                 &battery_svc.char_level_handle); | ||||
|                                 &battery_svc->char_level_handle); | ||||
|     if(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) { | ||||
|     // 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"); | ||||
|     tBleStatus result = aci_gatt_update_char_value(battery_svc.svc_handle, | ||||
|                                           battery_svc.char_level_handle, | ||||
|     tBleStatus result = aci_gatt_update_char_value(battery_svc->svc_handle, | ||||
|                                           battery_svc->char_level_handle, | ||||
|                                           0, | ||||
|                                           1, | ||||
|                                           &battery_charge); | ||||
|  | ||||
| @ -7,7 +7,9 @@ | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| bool battery_svc_init(); | ||||
| void battery_svc_start(); | ||||
| 
 | ||||
| void battery_svc_stop(); | ||||
| 
 | ||||
| bool battery_svc_update_level(uint8_t battery_level); | ||||
| 
 | ||||
|  | ||||
| @ -4,7 +4,7 @@ | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #define DEV_INFO_SERVICE_TAG "dev info service" | ||||
| #define DEV_INFO_SVC_TAG "dev info service" | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint16_t service_handle; | ||||
| @ -14,108 +14,143 @@ typedef struct { | ||||
|     uint16_t software_rev_char_handle; | ||||
| } 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; | ||||
|     DevInfoSvc dev_info_svc; | ||||
| 
 | ||||
|     // Add Device Information Service
 | ||||
|     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) { | ||||
|         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
 | ||||
|     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, | ||||
|                                 (Char_UUID_t*)&uuid, | ||||
|                                 strlen(DEV_INFO_MANUFACTURER_NAME), | ||||
|                                 strlen(dev_info_man_name), | ||||
|                                 CHAR_PROP_READ, | ||||
|                                 ATTR_PERMISSION_NONE, | ||||
|                                 GATT_DONT_NOTIFY_EVENTS, | ||||
|                                 10, | ||||
|                                 CHAR_VALUE_LEN_CONSTANT, | ||||
|                                 &dev_info_svc.man_name_char_handle); | ||||
|                                 &dev_info_svc->man_name_char_handle); | ||||
|     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; | ||||
|     status = aci_gatt_add_char(dev_info_svc.service_handle, | ||||
|     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||
|                                 UUID_TYPE_16, | ||||
|                                 (Char_UUID_t*)&uuid, | ||||
|                                 strlen(DEV_INFO_SERIAL_NUMBER), | ||||
|                                 strlen(dev_info_serial_num), | ||||
|                                 CHAR_PROP_READ, | ||||
|                                 ATTR_PERMISSION_NONE, | ||||
|                                 GATT_DONT_NOTIFY_EVENTS, | ||||
|                                 10, | ||||
|                                 CHAR_VALUE_LEN_CONSTANT, | ||||
|                                 &dev_info_svc.serial_num_char_handle); | ||||
|                                 &dev_info_svc->serial_num_char_handle); | ||||
|     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; | ||||
|     status = aci_gatt_add_char(dev_info_svc.service_handle, | ||||
|     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||
|                                 UUID_TYPE_16, | ||||
|                                 (Char_UUID_t*)&uuid, | ||||
|                                 strlen(DEV_INFO_FIRMWARE_REVISION_NUMBER), | ||||
|                                 strlen(dev_info_firmware_rev_num), | ||||
|                                 CHAR_PROP_READ, | ||||
|                                 ATTR_PERMISSION_NONE, | ||||
|                                 GATT_DONT_NOTIFY_EVENTS, | ||||
|                                 10, | ||||
|                                 CHAR_VALUE_LEN_CONSTANT, | ||||
|                                 &dev_info_svc.firmware_rev_char_handle); | ||||
|                                 &dev_info_svc->firmware_rev_char_handle); | ||||
|     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; | ||||
|     status = aci_gatt_add_char(dev_info_svc.service_handle, | ||||
|     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||
|                                 UUID_TYPE_16, | ||||
|                                 (Char_UUID_t*)&uuid, | ||||
|                                 strlen(DEV_INFO_SOFTWARE_REVISION_NUMBER), | ||||
|                                 strlen(dev_info_software_rev_num), | ||||
|                                 CHAR_PROP_READ, | ||||
|                                 ATTR_PERMISSION_NONE, | ||||
|                                 GATT_DONT_NOTIFY_EVENTS, | ||||
|                                 10, | ||||
|                                 CHAR_VALUE_LEN_CONSTANT, | ||||
|                                 &dev_info_svc.software_rev_char_handle); | ||||
|                                 &dev_info_svc->software_rev_char_handle); | ||||
|     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
 | ||||
|     status = aci_gatt_update_char_value(dev_info_svc.service_handle, | ||||
|                                         dev_info_svc.man_name_char_handle, | ||||
|     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||
|                                         dev_info_svc->man_name_char_handle, | ||||
|                                         0, | ||||
|                                         strlen(DEV_INFO_MANUFACTURER_NAME), | ||||
|                                         (uint8_t*)DEV_INFO_MANUFACTURER_NAME); | ||||
|                                         strlen(dev_info_man_name), | ||||
|                                         (uint8_t*)dev_info_man_name); | ||||
|     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, | ||||
|                                         dev_info_svc.serial_num_char_handle, | ||||
|     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||
|                                         dev_info_svc->serial_num_char_handle, | ||||
|                                         0, | ||||
|                                         strlen(DEV_INFO_SERIAL_NUMBER), | ||||
|                                         (uint8_t*)DEV_INFO_SERIAL_NUMBER); | ||||
|                                         strlen(dev_info_serial_num), | ||||
|                                         (uint8_t*)dev_info_serial_num); | ||||
|     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, | ||||
|                                         dev_info_svc.firmware_rev_char_handle, | ||||
|     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||
|                                         dev_info_svc->firmware_rev_char_handle, | ||||
|                                         0, | ||||
|                                         strlen(DEV_INFO_FIRMWARE_REVISION_NUMBER), | ||||
|                                         (uint8_t*)DEV_INFO_FIRMWARE_REVISION_NUMBER); | ||||
|                                         strlen(dev_info_firmware_rev_num), | ||||
|                                         (uint8_t*)dev_info_firmware_rev_num); | ||||
|     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, | ||||
|                                         dev_info_svc.software_rev_char_handle, | ||||
|     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||
|                                         dev_info_svc->software_rev_char_handle, | ||||
|                                         0, | ||||
|                                         strlen(DEV_INFO_SOFTWARE_REVISION_NUMBER), | ||||
|                                         (uint8_t*)DEV_INFO_SOFTWARE_REVISION_NUMBER); | ||||
|                                         strlen(dev_info_software_rev_num), | ||||
|                                         (uint8_t*)dev_info_software_rev_num); | ||||
|     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_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 | ||||
| } | ||||
|  | ||||
							
								
								
									
										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_SVC_DATA_LEN_MAX 245 | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint16_t svc_handle; | ||||
|     uint16_t rx_char_handle; | ||||
|     uint16_t tx_char_handle; | ||||
| } 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) { | ||||
|     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(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { | ||||
|             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
 | ||||
|                 ret = SVCCTL_EvtAckFlowEnable; | ||||
|                 FURI_LOG_D(SERIAL_SERVICE_TAG, "TX descriptor event"); | ||||
|             } 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); | ||||
|                 for(uint8_t i = 0; i < attribute_modified->Attr_Data_Length; i++) { | ||||
|                     printf("%02X ", attribute_modified->Attr_Data[i]); | ||||
|                 } | ||||
|                 printf("\r\n"); | ||||
|             } else if(attribute_modified->Attr_Handle == serial_svc->tx_char_handle + 1) { | ||||
|                 FURI_LOG_D(SERIAL_SERVICE_TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); | ||||
|                 serial_svc_update_rx(attribute_modified->Attr_Data, attribute_modified->Attr_Data_Length); | ||||
|                 ret = SVCCTL_EvtAckFlowEnable; | ||||
|             } | ||||
|         } 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; | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| bool serial_svc_init() { | ||||
| void serial_svc_start() { | ||||
|     tBleStatus status; | ||||
|     const uint8_t service_uuid[] = {SERIAL_SVC_UUID_128}; | ||||
|     const uint8_t char_rx_uuid[] = {SERIAL_CHAR_RX_UUID_128}; | ||||
|     const uint8_t char_tx_uuid[] = {SERIAL_CHAR_TX_UUID_128}; | ||||
| 
 | ||||
|     serial_svc = furi_alloc(sizeof(SerialSvc)); | ||||
|     // Register event handler
 | ||||
|     SVCCTL_RegisterSvcHandler(serial_svc_event_handler); | ||||
| 
 | ||||
|     // 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) { | ||||
|         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status); | ||||
|     } | ||||
| 
 | ||||
|     // 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, | ||||
|                                 CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, | ||||
|                                 ATTR_PERMISSION_NONE, | ||||
|                                 GATT_NOTIFY_ATTRIBUTE_WRITE, | ||||
|                                 10, | ||||
|                                 CHAR_VALUE_LEN_VARIABLE, | ||||
|                                 &serial_svc.tx_char_handle); | ||||
|                                 &serial_svc->tx_char_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add TX characteristic: %d", status); | ||||
|     } | ||||
| 
 | ||||
|     // 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,                                   | ||||
|                                 CHAR_PROP_READ | CHAR_PROP_INDICATE, | ||||
|                                 ATTR_PERMISSION_NONE, | ||||
|                                 GATT_DONT_NOTIFY_EVENTS, | ||||
|                                 10, | ||||
|                                 CHAR_VALUE_LEN_VARIABLE, | ||||
|                                 &serial_svc.rx_char_handle); | ||||
|                                 &serial_svc->rx_char_handle); | ||||
|     if(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) { | ||||
|     furi_assert(data_len < SERIAL_SVC_DATA_LEN_MAX); | ||||
| 
 | ||||
|     tBleStatus result = aci_gatt_update_char_value(serial_svc.svc_handle, | ||||
|                                           serial_svc.rx_char_handle, | ||||
|     tBleStatus result = aci_gatt_update_char_value(serial_svc->svc_handle, | ||||
|                                           serial_svc->rx_char_handle, | ||||
|                                           0, | ||||
|                                           data_len, | ||||
|                                           data); | ||||
|  | ||||
| @ -7,13 +7,9 @@ | ||||
| extern "C" { | ||||
| #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 | ||||
| #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(); | ||||
| void serial_svc_stop(); | ||||
| 
 | ||||
| bool serial_svc_update_rx(uint8_t* data, uint8_t data_len); | ||||
| 
 | ||||
|  | ||||
| @ -5,6 +5,7 @@ | ||||
| #include <shci.h> | ||||
| #include <cmsis_os2.h> | ||||
| #include <app_ble.h> | ||||
| #include <gap.h> | ||||
| 
 | ||||
| void furi_hal_bt_init() { | ||||
|     // 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() { | ||||
|     return APP_BLE_Start(); | ||||
|     return gap_init(); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_bt_dump_state(string_t buffer) { | ||||
|  | ||||
| @ -6,106 +6,20 @@ | ||||
| #include "ble.h" | ||||
| #include "tl.h" | ||||
| #include "app_ble.h" | ||||
| 
 | ||||
| #include "cmsis_os.h" | ||||
| #include "shci.h" | ||||
| #include "otp.h" | ||||
| #include "dev_info_service.h" | ||||
| #include "battery_service.h" | ||||
| #include "serial_service.h" | ||||
| #include "cmsis_os.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; | ||||
| 
 | ||||
| static const uint8_t M_bd_addr[BD_ADDR_SIZE_LOCAL] = | ||||
|     { | ||||
|         (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 */ | ||||
| 
 | ||||
| }; | ||||
| // PLACE_IN_SECTION("TAG_OTA_END") const uint32_t MagicKeywordValue = 0x94448A29 ;
 | ||||
| // PLACE_IN_SECTION("TAG_OTA_START") const uint32_t MagicKeywordAddress = (uint32_t)&MagicKeywordValue;
 | ||||
| 
 | ||||
| osMutexId_t MtxHciId; | ||||
| osSemaphoreId_t SemHciId; | ||||
| osThreadId_t AdvUpdateProcessId; | ||||
| 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 = { | ||||
|     .name = CFG_HCI_USER_EVT_PROCESS_NAME, | ||||
|     .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_StatusNot( HCI_TL_CmdStatus_t status ); | ||||
| 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() { | ||||
|   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); | ||||
| } | ||||
| 
 | ||||
| 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 ) { | ||||
|   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); | ||||
| } | ||||
| 
 | ||||
| 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) { | ||||
|   UNUSED(argument); | ||||
| 
 | ||||
|  | ||||
| @ -7,20 +7,7 @@ extern "C" { | ||||
| #include <stdbool.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_Start(); | ||||
| 
 | ||||
| APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status(); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
|  | ||||
| @ -139,7 +139,7 @@ | ||||
| /**
 | ||||
|  * 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 | ||||
|  | ||||
| @ -11,21 +11,22 @@ typedef struct { | ||||
|     uint16_t char_level_handle; | ||||
| } 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; | ||||
|     const uint16_t service_uuid = BATTERY_SERVICE_UUID; | ||||
|     const uint16_t char_battery_level_uuid = BATTERY_LEVEL_CHAR_UUID; | ||||
| 
 | ||||
|     // 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) { | ||||
|         FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery service: %d", status); | ||||
|     } | ||||
| 
 | ||||
|     // 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, | ||||
|                                 (Char_UUID_t *) &char_battery_level_uuid, | ||||
|                                 1, | ||||
| @ -34,17 +35,39 @@ bool battery_svc_init() { | ||||
|                                 GATT_DONT_NOTIFY_EVENTS, | ||||
|                                 10, | ||||
|                                 CHAR_VALUE_LEN_CONSTANT, | ||||
|                                 &battery_svc.char_level_handle); | ||||
|                                 &battery_svc->char_level_handle); | ||||
|     if(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) { | ||||
|     // 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"); | ||||
|     tBleStatus result = aci_gatt_update_char_value(battery_svc.svc_handle, | ||||
|                                           battery_svc.char_level_handle, | ||||
|     tBleStatus result = aci_gatt_update_char_value(battery_svc->svc_handle, | ||||
|                                           battery_svc->char_level_handle, | ||||
|                                           0, | ||||
|                                           1, | ||||
|                                           &battery_charge); | ||||
|  | ||||
| @ -7,7 +7,9 @@ | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| bool battery_svc_init(); | ||||
| void battery_svc_start(); | ||||
| 
 | ||||
| void battery_svc_stop(); | ||||
| 
 | ||||
| bool battery_svc_update_level(uint8_t battery_level); | ||||
| 
 | ||||
|  | ||||
| @ -4,7 +4,7 @@ | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #define DEV_INFO_SERVICE_TAG "dev info service" | ||||
| #define DEV_INFO_SVC_TAG "dev info service" | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint16_t service_handle; | ||||
| @ -14,108 +14,143 @@ typedef struct { | ||||
|     uint16_t software_rev_char_handle; | ||||
| } 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; | ||||
|     DevInfoSvc dev_info_svc; | ||||
| 
 | ||||
|     // Add Device Information Service
 | ||||
|     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) { | ||||
|         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
 | ||||
|     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, | ||||
|                                 (Char_UUID_t*)&uuid, | ||||
|                                 strlen(DEV_INFO_MANUFACTURER_NAME), | ||||
|                                 strlen(dev_info_man_name), | ||||
|                                 CHAR_PROP_READ, | ||||
|                                 ATTR_PERMISSION_NONE, | ||||
|                                 GATT_DONT_NOTIFY_EVENTS, | ||||
|                                 10, | ||||
|                                 CHAR_VALUE_LEN_CONSTANT, | ||||
|                                 &dev_info_svc.man_name_char_handle); | ||||
|                                 &dev_info_svc->man_name_char_handle); | ||||
|     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; | ||||
|     status = aci_gatt_add_char(dev_info_svc.service_handle, | ||||
|     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||
|                                 UUID_TYPE_16, | ||||
|                                 (Char_UUID_t*)&uuid, | ||||
|                                 strlen(DEV_INFO_SERIAL_NUMBER), | ||||
|                                 strlen(dev_info_serial_num), | ||||
|                                 CHAR_PROP_READ, | ||||
|                                 ATTR_PERMISSION_NONE, | ||||
|                                 GATT_DONT_NOTIFY_EVENTS, | ||||
|                                 10, | ||||
|                                 CHAR_VALUE_LEN_CONSTANT, | ||||
|                                 &dev_info_svc.serial_num_char_handle); | ||||
|                                 &dev_info_svc->serial_num_char_handle); | ||||
|     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; | ||||
|     status = aci_gatt_add_char(dev_info_svc.service_handle, | ||||
|     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||
|                                 UUID_TYPE_16, | ||||
|                                 (Char_UUID_t*)&uuid, | ||||
|                                 strlen(DEV_INFO_FIRMWARE_REVISION_NUMBER), | ||||
|                                 strlen(dev_info_firmware_rev_num), | ||||
|                                 CHAR_PROP_READ, | ||||
|                                 ATTR_PERMISSION_NONE, | ||||
|                                 GATT_DONT_NOTIFY_EVENTS, | ||||
|                                 10, | ||||
|                                 CHAR_VALUE_LEN_CONSTANT, | ||||
|                                 &dev_info_svc.firmware_rev_char_handle); | ||||
|                                 &dev_info_svc->firmware_rev_char_handle); | ||||
|     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; | ||||
|     status = aci_gatt_add_char(dev_info_svc.service_handle, | ||||
|     status = aci_gatt_add_char(dev_info_svc->service_handle, | ||||
|                                 UUID_TYPE_16, | ||||
|                                 (Char_UUID_t*)&uuid, | ||||
|                                 strlen(DEV_INFO_SOFTWARE_REVISION_NUMBER), | ||||
|                                 strlen(dev_info_software_rev_num), | ||||
|                                 CHAR_PROP_READ, | ||||
|                                 ATTR_PERMISSION_NONE, | ||||
|                                 GATT_DONT_NOTIFY_EVENTS, | ||||
|                                 10, | ||||
|                                 CHAR_VALUE_LEN_CONSTANT, | ||||
|                                 &dev_info_svc.software_rev_char_handle); | ||||
|                                 &dev_info_svc->software_rev_char_handle); | ||||
|     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
 | ||||
|     status = aci_gatt_update_char_value(dev_info_svc.service_handle, | ||||
|                                         dev_info_svc.man_name_char_handle, | ||||
|     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||
|                                         dev_info_svc->man_name_char_handle, | ||||
|                                         0, | ||||
|                                         strlen(DEV_INFO_MANUFACTURER_NAME), | ||||
|                                         (uint8_t*)DEV_INFO_MANUFACTURER_NAME); | ||||
|                                         strlen(dev_info_man_name), | ||||
|                                         (uint8_t*)dev_info_man_name); | ||||
|     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, | ||||
|                                         dev_info_svc.serial_num_char_handle, | ||||
|     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||
|                                         dev_info_svc->serial_num_char_handle, | ||||
|                                         0, | ||||
|                                         strlen(DEV_INFO_SERIAL_NUMBER), | ||||
|                                         (uint8_t*)DEV_INFO_SERIAL_NUMBER); | ||||
|                                         strlen(dev_info_serial_num), | ||||
|                                         (uint8_t*)dev_info_serial_num); | ||||
|     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, | ||||
|                                         dev_info_svc.firmware_rev_char_handle, | ||||
|     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||
|                                         dev_info_svc->firmware_rev_char_handle, | ||||
|                                         0, | ||||
|                                         strlen(DEV_INFO_FIRMWARE_REVISION_NUMBER), | ||||
|                                         (uint8_t*)DEV_INFO_FIRMWARE_REVISION_NUMBER); | ||||
|                                         strlen(dev_info_firmware_rev_num), | ||||
|                                         (uint8_t*)dev_info_firmware_rev_num); | ||||
|     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, | ||||
|                                         dev_info_svc.software_rev_char_handle, | ||||
|     status = aci_gatt_update_char_value(dev_info_svc->service_handle, | ||||
|                                         dev_info_svc->software_rev_char_handle, | ||||
|                                         0, | ||||
|                                         strlen(DEV_INFO_SOFTWARE_REVISION_NUMBER), | ||||
|                                         (uint8_t*)DEV_INFO_SOFTWARE_REVISION_NUMBER); | ||||
|                                         strlen(dev_info_software_rev_num), | ||||
|                                         (uint8_t*)dev_info_software_rev_num); | ||||
|     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_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 | ||||
| } | ||||
|  | ||||
							
								
								
									
										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_SVC_DATA_LEN_MAX 245 | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint16_t svc_handle; | ||||
|     uint16_t rx_char_handle; | ||||
|     uint16_t tx_char_handle; | ||||
| } 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) { | ||||
|     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(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { | ||||
|             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
 | ||||
|                 ret = SVCCTL_EvtAckFlowEnable; | ||||
|                 FURI_LOG_D(SERIAL_SERVICE_TAG, "TX descriptor event"); | ||||
|             } 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); | ||||
|                 for(uint8_t i = 0; i < attribute_modified->Attr_Data_Length; i++) { | ||||
|                     printf("%02X ", attribute_modified->Attr_Data[i]); | ||||
|                 } | ||||
|                 printf("\r\n"); | ||||
|             } else if(attribute_modified->Attr_Handle == serial_svc->tx_char_handle + 1) { | ||||
|                 FURI_LOG_D(SERIAL_SERVICE_TAG, "Received %d bytes", attribute_modified->Attr_Data_Length); | ||||
|                 serial_svc_update_rx(attribute_modified->Attr_Data, attribute_modified->Attr_Data_Length); | ||||
|                 ret = SVCCTL_EvtAckFlowEnable; | ||||
|             } | ||||
|         } 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; | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| bool serial_svc_init() { | ||||
| void serial_svc_start() { | ||||
|     tBleStatus status; | ||||
|     const uint8_t service_uuid[] = {SERIAL_SVC_UUID_128}; | ||||
|     const uint8_t char_rx_uuid[] = {SERIAL_CHAR_RX_UUID_128}; | ||||
|     const uint8_t char_tx_uuid[] = {SERIAL_CHAR_TX_UUID_128}; | ||||
| 
 | ||||
|     serial_svc = furi_alloc(sizeof(SerialSvc)); | ||||
|     // Register event handler
 | ||||
|     SVCCTL_RegisterSvcHandler(serial_svc_event_handler); | ||||
| 
 | ||||
|     // 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) { | ||||
|         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status); | ||||
|     } | ||||
| 
 | ||||
|     // 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, | ||||
|                                 CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, | ||||
|                                 ATTR_PERMISSION_NONE, | ||||
|                                 GATT_NOTIFY_ATTRIBUTE_WRITE, | ||||
|                                 10, | ||||
|                                 CHAR_VALUE_LEN_VARIABLE, | ||||
|                                 &serial_svc.tx_char_handle); | ||||
|                                 &serial_svc->tx_char_handle); | ||||
|     if(status) { | ||||
|         FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add TX characteristic: %d", status); | ||||
|     } | ||||
| 
 | ||||
|     // 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,                                   | ||||
|                                 CHAR_PROP_READ | CHAR_PROP_INDICATE, | ||||
|                                 ATTR_PERMISSION_NONE, | ||||
|                                 GATT_DONT_NOTIFY_EVENTS, | ||||
|                                 10, | ||||
|                                 CHAR_VALUE_LEN_VARIABLE, | ||||
|                                 &serial_svc.rx_char_handle); | ||||
|                                 &serial_svc->rx_char_handle); | ||||
|     if(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) { | ||||
|     furi_assert(data_len < SERIAL_SVC_DATA_LEN_MAX); | ||||
| 
 | ||||
|     tBleStatus result = aci_gatt_update_char_value(serial_svc.svc_handle, | ||||
|                                           serial_svc.rx_char_handle, | ||||
|     tBleStatus result = aci_gatt_update_char_value(serial_svc->svc_handle, | ||||
|                                           serial_svc->rx_char_handle, | ||||
|                                           0, | ||||
|                                           data_len, | ||||
|                                           data); | ||||
|  | ||||
| @ -7,13 +7,9 @@ | ||||
| extern "C" { | ||||
| #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 | ||||
| #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(); | ||||
| void serial_svc_stop(); | ||||
| 
 | ||||
| bool serial_svc_update_rx(uint8_t* data, uint8_t data_len); | ||||
| 
 | ||||
|  | ||||
| @ -5,6 +5,7 @@ | ||||
| #include <shci.h> | ||||
| #include <cmsis_os2.h> | ||||
| #include <app_ble.h> | ||||
| #include <gap.h> | ||||
| 
 | ||||
| void furi_hal_bt_init() { | ||||
|     // 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() { | ||||
|     return APP_BLE_Start(); | ||||
|     return gap_init(); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_bt_dump_state(string_t buffer) { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 gornekich
						gornekich