[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:
gornekich 2021-09-13 14:25:37 +03:00 committed by GitHub
parent 4456982e27
commit 95d9140d24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1197 additions and 1485 deletions

View File

@ -22,6 +22,17 @@ static ViewPort* bt_statusbar_view_port_alloc() {
return statusbar_view_port; return statusbar_view_port;
} }
static void bt_pin_code_show_event_handler(Bt* bt, uint32_t pin) {
furi_assert(bt);
string_t pin_str;
string_init_printf(pin_str, "%06d", pin);
dialog_message_set_text(
bt->dialog_message, string_get_cstr(pin_str), 64, 32, AlignCenter, AlignCenter);
dialog_message_set_buttons(bt->dialog_message, "Back", NULL, NULL);
dialog_message_show(bt->dialogs, bt->dialog_message);
string_clear(pin_str);
}
Bt* bt_alloc() { Bt* bt_alloc() {
Bt* bt = furi_alloc(sizeof(Bt)); Bt* bt = furi_alloc(sizeof(Bt));
// Load settings // Load settings
@ -41,13 +52,11 @@ Bt* bt_alloc() {
bt->gui = furi_record_open("gui"); bt->gui = furi_record_open("gui");
gui_add_view_port(bt->gui, bt->statusbar_view_port, GuiLayerStatusBarLeft); gui_add_view_port(bt->gui, bt->statusbar_view_port, GuiLayerStatusBarLeft);
return bt; // Dialogs
} bt->dialogs = furi_record_open("dialogs");
bt->dialog_message = dialog_message_alloc();
bool bt_update_battery_level(Bt* bt, uint8_t battery_level) { return bt;
BtMessage message = {
.type = BtMessageTypeUpdateBatteryLevel, .data.battery_level = battery_level};
return osMessageQueuePut(bt->message_queue, &message, 0, osWaitForever) == osOK;
} }
int32_t bt_srv() { int32_t bt_srv() {
@ -76,9 +85,13 @@ int32_t bt_srv() {
// Update statusbar // Update statusbar
view_port_enabled_set(bt->statusbar_view_port, furi_hal_bt_is_alive()); view_port_enabled_set(bt->statusbar_view_port, furi_hal_bt_is_alive());
} else if(message.type == BtMessageTypeUpdateBatteryLevel) { } else if(message.type == BtMessageTypeUpdateBatteryLevel) {
// Update battery level
if(furi_hal_bt_is_alive()) { if(furi_hal_bt_is_alive()) {
battery_svc_update_level(message.data.battery_level); battery_svc_update_level(message.data.battery_level);
} }
} else if(message.type == BtMessageTypePinCodeShow) {
// Display PIN code
bt_pin_code_show_event_handler(bt, message.data.pin_code);
} }
} }
return 0; return 0;

View File

@ -9,7 +9,9 @@ extern "C" {
typedef struct Bt Bt; typedef struct Bt Bt;
bool bt_update_battery_level(Bt* bt, uint8_t battery_level); void bt_update_battery_level(Bt* bt, uint8_t battery_level);
bool bt_pin_code_show(Bt* bt, uint32_t pin_code);
#ifdef __cplusplus #ifdef __cplusplus
} }

View 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;
}

View File

@ -9,14 +9,18 @@
#include <gui/view_port.h> #include <gui/view_port.h>
#include <gui/view.h> #include <gui/view.h>
#include <applications/dialogs/dialogs.h>
#include "../bt_settings.h" #include "../bt_settings.h"
typedef enum { typedef enum {
BtMessageTypeUpdateStatusbar, BtMessageTypeUpdateStatusbar,
BtMessageTypeUpdateBatteryLevel, BtMessageTypeUpdateBatteryLevel,
BtMessageTypePinCodeShow,
} BtMessageType; } BtMessageType;
typedef union { typedef union {
uint32_t pin_code;
uint8_t battery_level; uint8_t battery_level;
} BtMessageData; } BtMessageData;
@ -31,4 +35,6 @@ struct Bt {
osTimerId_t update_status_timer; osTimerId_t update_status_timer;
Gui* gui; Gui* gui;
ViewPort* statusbar_view_port; ViewPort* statusbar_view_port;
DialogsApp* dialogs;
DialogMessage* dialog_message;
}; };

View File

@ -6,106 +6,20 @@
#include "ble.h" #include "ble.h"
#include "tl.h" #include "tl.h"
#include "app_ble.h" #include "app_ble.h"
#include "cmsis_os.h"
#include "shci.h" #include "shci.h"
#include "otp.h" #include "cmsis_os.h"
#include "dev_info_service.h"
#include "battery_service.h"
#include "serial_service.h"
#include <furi-hal.h> #include <furi-hal.h>
typedef struct _tSecurityParams {
uint8_t ioCapability;
uint8_t mitm_mode;
uint8_t bonding_mode;
uint8_t Use_Fixed_Pin;
uint8_t encryptionKeySizeMin;
uint8_t encryptionKeySizeMax;
uint32_t Fixed_Pin;
uint8_t initiateSecurity;
} tSecurityParams;
typedef struct _tBLEProfileGlobalContext {
tSecurityParams bleSecurityParam;
uint16_t gapServiceHandle;
uint16_t devNameCharHandle;
uint16_t appearanceCharHandle;
uint16_t connectionHandle;
uint8_t advtServUUIDlen;
uint8_t advtServUUID[100];
} BleGlobalContext_t;
typedef struct {
BleGlobalContext_t BleApplicationContext_legacy;
APP_BLE_ConnStatus_t Device_Connection_Status;
uint8_t Advertising_mgr_timer_Id;
} BleApplicationContext_t;
#define FAST_ADV_TIMEOUT (30*1000*1000/CFG_TS_TICK_VAL) /**< 30s */
#define INITIAL_ADV_TIMEOUT (60*1000*1000/CFG_TS_TICK_VAL) /**< 60s */
#define BD_ADDR_SIZE_LOCAL 6
#define LED_ON_TIMEOUT (0.005*1000*1000/CFG_TS_TICK_VAL) /**< 5ms */
PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer; PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer;
static const uint8_t M_bd_addr[BD_ADDR_SIZE_LOCAL] = // PLACE_IN_SECTION("TAG_OTA_END") const uint32_t MagicKeywordValue = 0x94448A29 ;
{ // PLACE_IN_SECTION("TAG_OTA_START") const uint32_t MagicKeywordAddress = (uint32_t)&MagicKeywordValue;
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x0000000000FF)),
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x00000000FF00) >> 8),
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x000000FF0000) >> 16),
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x0000FF000000) >> 24),
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x00FF00000000) >> 32),
(uint8_t)((CFG_ADV_BD_ADDRESS & 0xFF0000000000) >> 40)
};
static uint8_t bd_addr_udn[BD_ADDR_SIZE_LOCAL];
static const uint8_t BLE_CFG_IR_VALUE[16] = CFG_BLE_IRK;
static const uint8_t BLE_CFG_ER_VALUE[16] = CFG_BLE_ERK;
PLACE_IN_SECTION("TAG_OTA_END") const uint32_t MagicKeywordValue = 0x94448A29 ;
PLACE_IN_SECTION("TAG_OTA_START") const uint32_t MagicKeywordAddress = (uint32_t)&MagicKeywordValue;
PLACE_IN_SECTION("BLE_APP_CONTEXT") static BleApplicationContext_t BleApplicationContext;
PLACE_IN_SECTION("BLE_APP_CONTEXT") static uint16_t AdvIntervalMin, AdvIntervalMax;
uint8_t manuf_data[14] = {
sizeof(manuf_data)-1, AD_TYPE_MANUFACTURER_SPECIFIC_DATA,
0x01/*SKD version */,
0x00 /* Generic*/,
0x00 /* GROUP A Feature */,
0x00 /* GROUP A Feature */,
0x00 /* GROUP B Feature */,
0x00 /* GROUP B Feature */,
0x00, /* BLE MAC start -MSB */
0x00,
0x00,
0x00,
0x00,
0x00, /* BLE MAC stop */
};
osMutexId_t MtxHciId; osMutexId_t MtxHciId;
osSemaphoreId_t SemHciId; osSemaphoreId_t SemHciId;
osThreadId_t AdvUpdateProcessId;
osThreadId_t HciUserEvtProcessId; osThreadId_t HciUserEvtProcessId;
const osThreadAttr_t AdvUpdateProcess_attr = {
.name = CFG_ADV_UPDATE_PROCESS_NAME,
.attr_bits = CFG_ADV_UPDATE_PROCESS_ATTR_BITS,
.cb_mem = CFG_ADV_UPDATE_PROCESS_CB_MEM,
.cb_size = CFG_ADV_UPDATE_PROCESS_CB_SIZE,
.stack_mem = CFG_ADV_UPDATE_PROCESS_STACK_MEM,
.priority = CFG_ADV_UPDATE_PROCESS_PRIORITY,
.stack_size = CFG_ADV_UPDATE_PROCESS_STACK_SIZE
};
const osThreadAttr_t HciUserEvtProcess_attr = { const osThreadAttr_t HciUserEvtProcess_attr = {
.name = CFG_HCI_USER_EVT_PROCESS_NAME, .name = CFG_HCI_USER_EVT_PROCESS_NAME,
.attr_bits = CFG_HCI_USER_EVT_PROCESS_ATTR_BITS, .attr_bits = CFG_HCI_USER_EVT_PROCESS_ATTR_BITS,
@ -121,13 +35,6 @@ static void HciUserEvtProcess(void *argument);
static void BLE_UserEvtRx( void * pPayload ); static void BLE_UserEvtRx( void * pPayload );
static void BLE_StatusNot( HCI_TL_CmdStatus_t status ); static void BLE_StatusNot( HCI_TL_CmdStatus_t status );
static void Ble_Tl_Init( void ); static void Ble_Tl_Init( void );
static void Ble_Hci_Gap_Gatt_Init();
static const uint8_t* BleGetBdAddress( void );
static void Adv_Request( APP_BLE_ConnStatus_t New_Status );
static void Adv_Mgr( void );
static void AdvUpdateProcess(void *argument);
static void Adv_Update( void );
bool APP_BLE_Init() { bool APP_BLE_Init() {
SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = {
@ -160,254 +67,6 @@ bool APP_BLE_Init() {
return (SHCI_C2_BLE_Init( &ble_init_cmd_packet ) == SHCI_Success); return (SHCI_C2_BLE_Init( &ble_init_cmd_packet ) == SHCI_Success);
} }
static void set_advertisment_service_uid(uint8_t* uid, uint8_t uin_len);
bool APP_BLE_Start() {
if (APPE_Status() != BleGlueStatusStarted) {
return false;
}
// Initialization of HCI & GATT & GAP layer
Ble_Hci_Gap_Gatt_Init();
// Initialization of the BLE Services
SVCCTL_Init();
// Initialization of the BLE App Context
BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE;
BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0xFFFF;
// From here, all initialization are BLE application specific
AdvUpdateProcessId = osThreadNew(AdvUpdateProcess, NULL, &AdvUpdateProcess_attr);
// Initialization of ADV - Ad Manufacturer Element - Support OTA Bit Masks
#if(BLE_CFG_OTA_REBOOT_CHAR != 0)
manuf_data[sizeof(manuf_data)-8] = CFG_FEATURE_OTA_REBOOT;
#endif
// Initialize DIS Application
dev_info_service_init();
// Initialize BAS Application
battery_svc_init();
// Initialize Serial application
serial_svc_init();
// Create timer to handle the connection state machine
HW_TS_Create(CFG_TIM_PROC_ID_ISR, &(BleApplicationContext.Advertising_mgr_timer_Id), hw_ts_SingleShot, Adv_Mgr);
uint8_t adv_service_uid[2];
adv_service_uid[0] = 0x80 | furi_hal_version_get_hw_color();
adv_service_uid[1] = 0x30;
set_advertisment_service_uid(adv_service_uid, sizeof(adv_service_uid));
/* Initialize intervals for reconnexion without intervals update */
AdvIntervalMin = CFG_FAST_CONN_ADV_INTERVAL_MIN;
AdvIntervalMax = CFG_FAST_CONN_ADV_INTERVAL_MAX;
Adv_Request(APP_BLE_FAST_ADV);
return true;
}
void SVCCTL_SvcInit() {
// Dummy function to prevent unused services initialization
// TODO refactore
}
SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt )
{
hci_event_pckt *event_pckt;
evt_le_meta_event *meta_evt;
evt_blue_aci *blue_evt;
hci_le_phy_update_complete_event_rp0 *evt_le_phy_update_complete;
uint8_t TX_PHY, RX_PHY;
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
event_pckt = (hci_event_pckt*) ((hci_uart_pckt *) pckt)->data;
switch (event_pckt->evt) {
case EVT_DISCONN_COMPLETE:
{
hci_disconnection_complete_event_rp0 *disconnection_complete_event;
disconnection_complete_event = (hci_disconnection_complete_event_rp0 *) event_pckt->data;
if (disconnection_complete_event->Connection_Handle == BleApplicationContext.BleApplicationContext_legacy.connectionHandle) {
BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0;
BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE;
APP_DBG_MSG("\r\n\r** DISCONNECTION EVENT WITH CLIENT \r\n");
}
/* restart advertising */
Adv_Request(APP_BLE_FAST_ADV);
furi_hal_power_insomnia_exit();
}
break; /* EVT_DISCONN_COMPLETE */
case EVT_LE_META_EVENT:
{
meta_evt = (evt_le_meta_event*) event_pckt->data;
switch (meta_evt->subevent)
{
case EVT_LE_CONN_UPDATE_COMPLETE:
APP_DBG_MSG("\r\n\r** CONNECTION UPDATE EVENT WITH CLIENT \r\n");
/* USER CODE BEGIN EVT_LE_CONN_UPDATE_COMPLETE */
/* USER CODE END EVT_LE_CONN_UPDATE_COMPLETE */
break;
case EVT_LE_PHY_UPDATE_COMPLETE:
APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE \r\n");
evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data;
if (evt_le_phy_update_complete->Status == 0)
{
APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status ok \r\n");
}
else
{
APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status nok \r\n");
}
ret = hci_le_read_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,&TX_PHY,&RX_PHY);
if (ret == BLE_STATUS_SUCCESS)
{
APP_DBG_MSG("Read_PHY success \r\n");
if ((TX_PHY == TX_2M) && (RX_PHY == RX_2M))
{
APP_DBG_MSG("PHY Param TX= %d, RX= %d \r\n", TX_PHY, RX_PHY);
}
else
{
APP_DBG_MSG("PHY Param TX= %d, RX= %d \r\n", TX_PHY, RX_PHY);
}
}
else
{
APP_DBG_MSG("Read conf not succeess \r\n");
}
break;
case EVT_LE_CONN_COMPLETE:
{
furi_hal_power_insomnia_enter();
hci_le_connection_complete_event_rp0 *connection_complete_event;
/**
* The connection is done, there is no need anymore to schedule the LP ADV
*/
connection_complete_event = (hci_le_connection_complete_event_rp0 *) meta_evt->data;
HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id);
APP_DBG_MSG("EVT_LE_CONN_COMPLETE for connection handle 0x%x\r\n", connection_complete_event->Connection_Handle);
if (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_CONNECTING)
{
/* Connection as client */
BleApplicationContext.Device_Connection_Status = APP_BLE_CONNECTED_CLIENT;
}
else
{
/* Connection as server */
BleApplicationContext.Device_Connection_Status = APP_BLE_CONNECTED_SERVER;
}
BleApplicationContext.BleApplicationContext_legacy.connectionHandle = connection_complete_event->Connection_Handle;
}
break; /* HCI_EVT_LE_CONN_COMPLETE */
default:
break;
}
}
break; /* HCI_EVT_LE_META_EVENT */
case EVT_VENDOR:
blue_evt = (evt_blue_aci*) event_pckt->data;
switch (blue_evt->ecode) {
aci_gap_pairing_complete_event_rp0 *pairing_complete;
case EVT_BLUE_GAP_LIMITED_DISCOVERABLE:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_LIMITED_DISCOVERABLE \r\n");
break; /* EVT_BLUE_GAP_LIMITED_DISCOVERABLE */
case EVT_BLUE_GAP_PASS_KEY_REQUEST:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PASS_KEY_REQUEST \r\n");
aci_gap_pass_key_resp(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,123456);
APP_DBG_MSG("\r\n\r** aci_gap_pass_key_resp \r\n");
break; /* EVT_BLUE_GAP_PASS_KEY_REQUEST */
case EVT_BLUE_GAP_AUTHORIZATION_REQUEST:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_AUTHORIZATION_REQUEST \r\n");
break; /* EVT_BLUE_GAP_AUTHORIZATION_REQUEST */
case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED \r\n");
break; /* EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED */
case EVT_BLUE_GAP_BOND_LOST:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_BOND_LOST \r\n");
aci_gap_allow_rebond(BleApplicationContext.BleApplicationContext_legacy.connectionHandle);
APP_DBG_MSG("\r\n\r** Send allow rebond \r\n");
break; /* EVT_BLUE_GAP_BOND_LOST */
case EVT_BLUE_GAP_DEVICE_FOUND:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \r\n");
break; /* EVT_BLUE_GAP_DEVICE_FOUND */
case EVT_BLUE_GAP_ADDR_NOT_RESOLVED:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \r\n");
break; /* EVT_BLUE_GAP_DEVICE_FOUND */
case (EVT_BLUE_GAP_KEYPRESS_NOTIFICATION):
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_KEYPRESS_NOTIFICATION \r\n");
break; /* EVT_BLUE_GAP_KEY_PRESS_NOTIFICATION */
case (EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE):
APP_DBG_MSG("numeric_value = %ld\r\n",
((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value);
APP_DBG_MSG("Hex_value = %lx\r\n",
((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value);
aci_gap_numeric_comparison_value_confirm_yesno(BleApplicationContext.BleApplicationContext_legacy.connectionHandle, 1); /* CONFIRM_YES = 1 */
APP_DBG_MSG("\r\n\r** aci_gap_numeric_comparison_value_confirm_yesno-->YES \r\n");
break;
case (EVT_BLUE_GAP_PAIRING_CMPLT):
{
pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data;
APP_DBG_MSG("BLE_CTRL_App_Notification: EVT_BLUE_GAP_PAIRING_CMPLT, pairing_complete->Status = %d\r\n",pairing_complete->Status);
if (pairing_complete->Status == 0) {
APP_DBG_MSG("\r\n\r** Pairing OK \r\n");
} else {
APP_DBG_MSG("\r\n\r** Pairing KO \r\n");
}
}
break;
/* USER CODE END ecode */
case EVT_BLUE_GAP_PROCEDURE_COMPLETE:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PROCEDURE_COMPLETE \r\n");
break;
}
break; /* EVT_VENDOR */
default:
break;
}
return (SVCCTL_UserEvtFlowEnable);
}
static void set_advertisment_service_uid(uint8_t* uid, uint8_t uid_len) {
BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen = 1;
if(uid_len == 2) {
BleApplicationContext.BleApplicationContext_legacy.advtServUUID[0] = AD_TYPE_16_BIT_SERV_UUID;
} else if (uid_len == 4) {
BleApplicationContext.BleApplicationContext_legacy.advtServUUID[0] = AD_TYPE_32_BIT_SERV_UUID;
} else if(uid_len == 16) {
BleApplicationContext.BleApplicationContext_legacy.advtServUUID[0] = AD_TYPE_128_BIT_SERV_UUID_CMPLT_LIST;
}
memcpy(&BleApplicationContext.BleApplicationContext_legacy.advtServUUID[1], uid, uid_len);
BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen += uid_len;
}
APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status() {
return BleApplicationContext.Device_Connection_Status;
}
static void Ble_Tl_Init( void ) { static void Ble_Tl_Init( void ) {
HCI_TL_HciInitConf_t Hci_Tl_Init_Conf; HCI_TL_HciInitConf_t Hci_Tl_Init_Conf;
@ -419,301 +78,6 @@ static void Ble_Tl_Init( void ) {
hci_init(BLE_UserEvtRx, (void*) &Hci_Tl_Init_Conf); hci_init(BLE_UserEvtRx, (void*) &Hci_Tl_Init_Conf);
} }
static void Ble_Hci_Gap_Gatt_Init() {
uint8_t role;
uint16_t gap_service_handle, gap_dev_name_char_handle, gap_appearance_char_handle;
const uint8_t *bd_addr;
uint32_t srd_bd_addr[2];
uint16_t appearance[1] = { BLE_CFG_GAP_APPEARANCE };
/*HCI Reset to synchronise BLE Stack*/
hci_reset();
/**
* Write the BD Address
*/
bd_addr = BleGetBdAddress();
aci_hal_write_config_data(CONFIG_DATA_PUBADDR_OFFSET,
CONFIG_DATA_PUBADDR_LEN,
(uint8_t*) bd_addr);
/* BLE MAC in ADV Packet */
manuf_data[ sizeof(manuf_data)-6] = bd_addr[5];
manuf_data[ sizeof(manuf_data)-5] = bd_addr[4];
manuf_data[ sizeof(manuf_data)-4] = bd_addr[3];
manuf_data[ sizeof(manuf_data)-3] = bd_addr[2];
manuf_data[ sizeof(manuf_data)-2] = bd_addr[1];
manuf_data[ sizeof(manuf_data)-1] = bd_addr[0];
/**
* Write Identity root key used to derive LTK and CSRK
*/
aci_hal_write_config_data(CONFIG_DATA_IR_OFFSET,
CONFIG_DATA_IR_LEN,
(uint8_t*) BLE_CFG_IR_VALUE);
/**
* Write Encryption root key used to derive LTK and CSRK
*/
aci_hal_write_config_data(CONFIG_DATA_ER_OFFSET,
CONFIG_DATA_ER_LEN,
(uint8_t*) BLE_CFG_ER_VALUE);
/**
* Write random bd_address
*/
/* random_bd_address = R_bd_address;
aci_hal_write_config_data(CONFIG_DATA_RANDOM_ADDRESS_WR,
CONFIG_DATA_RANDOM_ADDRESS_LEN,
(uint8_t*) random_bd_address);
*/
/**
* Static random Address
* The two upper bits shall be set to 1
* The lowest 32bits is read from the UDN to differentiate between devices
* The RNG may be used to provide a random number on each power on
*/
srd_bd_addr[1] = 0x0000ED6E;
srd_bd_addr[0] = LL_FLASH_GetUDN( );
aci_hal_write_config_data( CONFIG_DATA_RANDOM_ADDRESS_OFFSET, CONFIG_DATA_RANDOM_ADDRESS_LEN, (uint8_t*)srd_bd_addr );
/**
* Write Identity root key used to derive LTK and CSRK
*/
aci_hal_write_config_data( CONFIG_DATA_IR_OFFSET, CONFIG_DATA_IR_LEN, (uint8_t*)BLE_CFG_IR_VALUE );
/**
* Write Encryption root key used to derive LTK and CSRK
*/
aci_hal_write_config_data( CONFIG_DATA_ER_OFFSET, CONFIG_DATA_ER_LEN, (uint8_t*)BLE_CFG_ER_VALUE );
/**
* Set TX Power to 0dBm.
*/
aci_hal_set_tx_power_level(1, CFG_TX_POWER);
/**
* Initialize GATT interface
*/
aci_gatt_init();
/**
* Initialize GAP interface
*/
role = 0;
#if (BLE_CFG_PERIPHERAL == 1)
role |= GAP_PERIPHERAL_ROLE;
#endif
#if (BLE_CFG_CENTRAL == 1)
role |= GAP_CENTRAL_ROLE;
#endif
if (role > 0)
{
const char *name = furi_hal_version_get_device_name_ptr();
aci_gap_init(role, 0,
strlen(name),
&gap_service_handle, &gap_dev_name_char_handle, &gap_appearance_char_handle);
if (aci_gatt_update_char_value(gap_service_handle, gap_dev_name_char_handle, 0, strlen(name), (uint8_t *) name))
{
BLE_DBG_SVCCTL_MSG("Device Name aci_gatt_update_char_value failed.\r\n");
}
}
if(aci_gatt_update_char_value(gap_service_handle,
gap_appearance_char_handle,
0,
2,
(uint8_t *)&appearance))
{
BLE_DBG_SVCCTL_MSG("Appearance aci_gatt_update_char_value failed.\r\n");
}
/**
* Initialize Default PHY
*/
hci_le_set_default_phy(ALL_PHYS_PREFERENCE,TX_2M_PREFERRED,RX_2M_PREFERRED);
/**
* Initialize IO capability
*/
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.ioCapability = CFG_IO_CAPABILITY;
aci_gap_set_io_capability(BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.ioCapability);
/**
* Initialize authentication
*/
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.mitm_mode = CFG_MITM_PROTECTION;
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMin = CFG_ENCRYPTION_KEY_SIZE_MIN;
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMax = CFG_ENCRYPTION_KEY_SIZE_MAX;
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Use_Fixed_Pin = CFG_USED_FIXED_PIN;
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Fixed_Pin = CFG_FIXED_PIN;
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode = CFG_BONDING_MODE;
aci_gap_set_authentication_requirement(BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode,
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.mitm_mode,
CFG_SC_SUPPORT,
CFG_KEYPRESS_NOTIFICATION_SUPPORT,
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMin,
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMax,
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Use_Fixed_Pin,
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Fixed_Pin,
PUBLIC_ADDR
);
/**
* Initialize whitelist
*/
if (BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode)
{
aci_gap_configure_whitelist();
}
}
static void Adv_Request(APP_BLE_ConnStatus_t New_Status)
{
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
uint16_t Min_Inter, Max_Inter;
if (New_Status == APP_BLE_FAST_ADV)
{
Min_Inter = AdvIntervalMin;
Max_Inter = AdvIntervalMax;
}
else
{
Min_Inter = CFG_LP_CONN_ADV_INTERVAL_MIN;
Max_Inter = CFG_LP_CONN_ADV_INTERVAL_MAX;
}
/**
* Stop the timer, it will be restarted for a new shot
* It does not hurt if the timer was not running
*/
HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id);
APP_DBG_MSG("First index in %d state \r\n", BleApplicationContext.Device_Connection_Status);
if ((New_Status == APP_BLE_LP_ADV)
&& ((BleApplicationContext.Device_Connection_Status == APP_BLE_FAST_ADV)
|| (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_ADV)))
{
/* Connection in ADVERTISE mode have to stop the current advertising */
ret = aci_gap_set_non_discoverable();
if (ret == BLE_STATUS_SUCCESS)
{
APP_DBG_MSG("Successfully Stopped Advertising \r\n");
}
else
{
APP_DBG_MSG("Stop Advertising Failed , result: %d \r\n", ret);
}
}
BleApplicationContext.Device_Connection_Status = New_Status;
const char* name = furi_hal_version_get_ble_local_device_name_ptr();
/* Start Fast or Low Power Advertising */
ret = aci_gap_set_discoverable(
ADV_IND,
Min_Inter,
Max_Inter,
PUBLIC_ADDR,
NO_WHITE_LIST_USE, /* use white list */
strlen(name),
(uint8_t*)name,
BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen,
BleApplicationContext.BleApplicationContext_legacy.advtServUUID,
0,
0);
if(ret) {
FURI_LOG_E("APP ble", "Set discoverable err: %d", ret);
}
/* Update Advertising data */
ret = aci_gap_update_adv_data(sizeof(manuf_data), (uint8_t*) manuf_data);
if (ret == BLE_STATUS_SUCCESS) {
if (New_Status == APP_BLE_FAST_ADV) {
APP_DBG_MSG("Successfully Start Fast Advertising \r\n" );
/* Start Timer to STOP ADV - TIMEOUT */
HW_TS_Start(BleApplicationContext.Advertising_mgr_timer_Id, INITIAL_ADV_TIMEOUT);
} else {
APP_DBG_MSG("Successfully Start Low Power Advertising \r\n");
}
} else {
if (New_Status == APP_BLE_FAST_ADV) {
APP_DBG_MSG("Start Fast Advertising Failed , result: %d \r\n", ret);
} else {
APP_DBG_MSG("Start Low Power Advertising Failed , result: %d \r\n", ret);
}
}
}
const uint8_t* BleGetBdAddress( void ) {
uint8_t *otp_addr;
const uint8_t *bd_addr;
uint32_t udn;
uint32_t company_id;
uint32_t device_id;
udn = LL_FLASH_GetUDN();
if(udn != 0xFFFFFFFF) {
company_id = LL_FLASH_GetSTCompanyID();
device_id = LL_FLASH_GetDeviceID();
bd_addr_udn[0] = (uint8_t)(udn & 0x000000FF);
bd_addr_udn[1] = (uint8_t)( (udn & 0x0000FF00) >> 8 );
bd_addr_udn[2] = (uint8_t)( (udn & 0x00FF0000) >> 16 );
bd_addr_udn[3] = (uint8_t)device_id;
bd_addr_udn[4] = (uint8_t)(company_id & 0x000000FF);;
bd_addr_udn[5] = (uint8_t)( (company_id & 0x0000FF00) >> 8 );
bd_addr = (const uint8_t *)bd_addr_udn;
} else {
otp_addr = OTP_Read(0);
if(otp_addr) {
bd_addr = ((OTP_ID0_t*)otp_addr)->bd_address;
} else {
bd_addr = M_bd_addr;
}
}
return bd_addr;
}
/*************************************************************
*
*SPECIFIC FUNCTIONS
*
*************************************************************/
static void Adv_Mgr( void ) {
/**
* The code shall be executed in the background as an aci command may be sent
* The background is the only place where the application can make sure a new aci command
* is not sent if there is a pending one
*/
osThreadFlagsSet( AdvUpdateProcessId, 1 );
}
static void AdvUpdateProcess(void *argument) {
UNUSED(argument);
for(;;) {
osThreadFlagsWait( 1, osFlagsWaitAny, osWaitForever);
Adv_Update( );
}
}
static void Adv_Update( void ) {
Adv_Request(APP_BLE_LP_ADV);
}
static void HciUserEvtProcess(void *argument) { static void HciUserEvtProcess(void *argument) {
UNUSED(argument); UNUSED(argument);

View File

@ -7,20 +7,7 @@ extern "C" {
#include <stdbool.h> #include <stdbool.h>
#include "hci_tl.h" #include "hci_tl.h"
typedef enum {
APP_BLE_IDLE,
APP_BLE_FAST_ADV,
APP_BLE_LP_ADV,
APP_BLE_SCAN,
APP_BLE_LP_CONNECTING,
APP_BLE_CONNECTED_SERVER,
APP_BLE_CONNECTED_CLIENT
} APP_BLE_ConnStatus_t;
bool APP_BLE_Init(); bool APP_BLE_Init();
bool APP_BLE_Start();
APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status();
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -139,7 +139,7 @@
/** /**
* Maximum supported ATT_MTU size * Maximum supported ATT_MTU size
*/ */
#define CFG_BLE_MAX_ATT_MTU (156) #define CFG_BLE_MAX_ATT_MTU (251)
/** /**
* Size of the storage area for Attribute values * Size of the storage area for Attribute values

View File

@ -11,21 +11,22 @@ typedef struct {
uint16_t char_level_handle; uint16_t char_level_handle;
} BatterySvc; } BatterySvc;
static BatterySvc battery_svc; static BatterySvc* battery_svc = NULL;
bool battery_svc_init() { static const uint16_t service_uuid = BATTERY_SERVICE_UUID;
static const uint16_t char_battery_level_uuid = BATTERY_LEVEL_CHAR_UUID;
void battery_svc_start() {
battery_svc = furi_alloc(sizeof(BatterySvc));
tBleStatus status; tBleStatus status;
const uint16_t service_uuid = BATTERY_SERVICE_UUID;
const uint16_t char_battery_level_uuid = BATTERY_LEVEL_CHAR_UUID;
// Add Battery service // Add Battery service
status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&service_uuid, PRIMARY_SERVICE, 4, &battery_svc.svc_handle); status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&service_uuid, PRIMARY_SERVICE, 4, &battery_svc->svc_handle);
if(status) { if(status) {
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery service: %d", status); FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery service: %d", status);
} }
// Add Battery level characteristic // Add Battery level characteristic
status = aci_gatt_add_char(battery_svc.svc_handle, status = aci_gatt_add_char(battery_svc->svc_handle,
UUID_TYPE_16, UUID_TYPE_16,
(Char_UUID_t *) &char_battery_level_uuid, (Char_UUID_t *) &char_battery_level_uuid,
1, 1,
@ -34,17 +35,39 @@ bool battery_svc_init() {
GATT_DONT_NOTIFY_EVENTS, GATT_DONT_NOTIFY_EVENTS,
10, 10,
CHAR_VALUE_LEN_CONSTANT, CHAR_VALUE_LEN_CONSTANT,
&battery_svc.char_level_handle); &battery_svc->char_level_handle);
if(status) { if(status) {
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery level characteristic: %d", status); FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery level characteristic: %d", status);
} }
return status != BLE_STATUS_SUCCESS; }
void battery_svc_stop() {
tBleStatus status;
if(battery_svc) {
// Delete Battery level characteristic
status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->char_level_handle);
if(status) {
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to delete Battery level characteristic: %d", status);
}
// Delete Battery service
status = aci_gatt_del_service(battery_svc->svc_handle);
if(status) {
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to delete Battery service: %d", status);
}
free(battery_svc);
battery_svc = NULL;
}
} }
bool battery_svc_update_level(uint8_t battery_charge) { bool battery_svc_update_level(uint8_t battery_charge) {
// Check if service was started
if(battery_svc == NULL) {
return false;
}
// Update battery level characteristic
FURI_LOG_I(BATTERY_SERVICE_TAG, "Updating battery level characteristic"); FURI_LOG_I(BATTERY_SERVICE_TAG, "Updating battery level characteristic");
tBleStatus result = aci_gatt_update_char_value(battery_svc.svc_handle, tBleStatus result = aci_gatt_update_char_value(battery_svc->svc_handle,
battery_svc.char_level_handle, battery_svc->char_level_handle,
0, 0,
1, 1,
&battery_charge); &battery_charge);

View File

@ -7,7 +7,9 @@
extern "C" { extern "C" {
#endif #endif
bool battery_svc_init(); void battery_svc_start();
void battery_svc_stop();
bool battery_svc_update_level(uint8_t battery_level); bool battery_svc_update_level(uint8_t battery_level);

View File

@ -4,7 +4,7 @@
#include <furi.h> #include <furi.h>
#define DEV_INFO_SERVICE_TAG "dev info service" #define DEV_INFO_SVC_TAG "dev info service"
typedef struct { typedef struct {
uint16_t service_handle; uint16_t service_handle;
@ -14,108 +14,143 @@ typedef struct {
uint16_t software_rev_char_handle; uint16_t software_rev_char_handle;
} DevInfoSvc; } DevInfoSvc;
bool dev_info_service_init() { static DevInfoSvc* dev_info_svc = NULL;
static const char dev_info_man_name[] = "Flipper Devices Inc.";
static const char dev_info_serial_num[] = "1.0";
static const char dev_info_firmware_rev_num[] = TARGET;
static const char dev_info_software_rev_num[] = GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE;
void dev_info_svc_start() {
dev_info_svc = furi_alloc(sizeof(DevInfoSvc));
tBleStatus status; tBleStatus status;
DevInfoSvc dev_info_svc;
// Add Device Information Service // Add Device Information Service
uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID;
status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 9, &dev_info_svc.service_handle); status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 9, &dev_info_svc->service_handle);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add Device Information Service: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add Device Information Service: %d", status);
} }
// Add characteristics // Add characteristics
uuid = MANUFACTURER_NAME_UUID; uuid = MANUFACTURER_NAME_UUID;
status = aci_gatt_add_char(dev_info_svc.service_handle, status = aci_gatt_add_char(dev_info_svc->service_handle,
UUID_TYPE_16, UUID_TYPE_16,
(Char_UUID_t*)&uuid, (Char_UUID_t*)&uuid,
strlen(DEV_INFO_MANUFACTURER_NAME), strlen(dev_info_man_name),
CHAR_PROP_READ, CHAR_PROP_READ,
ATTR_PERMISSION_NONE, ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS, GATT_DONT_NOTIFY_EVENTS,
10, 10,
CHAR_VALUE_LEN_CONSTANT, CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc.man_name_char_handle); &dev_info_svc->man_name_char_handle);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add manufacturer name char: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add manufacturer name char: %d", status);
} }
uuid = SERIAL_NUMBER_UUID; uuid = SERIAL_NUMBER_UUID;
status = aci_gatt_add_char(dev_info_svc.service_handle, status = aci_gatt_add_char(dev_info_svc->service_handle,
UUID_TYPE_16, UUID_TYPE_16,
(Char_UUID_t*)&uuid, (Char_UUID_t*)&uuid,
strlen(DEV_INFO_SERIAL_NUMBER), strlen(dev_info_serial_num),
CHAR_PROP_READ, CHAR_PROP_READ,
ATTR_PERMISSION_NONE, ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS, GATT_DONT_NOTIFY_EVENTS,
10, 10,
CHAR_VALUE_LEN_CONSTANT, CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc.serial_num_char_handle); &dev_info_svc->serial_num_char_handle);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add serial number char: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add serial number char: %d", status);
} }
uuid = FIRMWARE_REVISION_UUID; uuid = FIRMWARE_REVISION_UUID;
status = aci_gatt_add_char(dev_info_svc.service_handle, status = aci_gatt_add_char(dev_info_svc->service_handle,
UUID_TYPE_16, UUID_TYPE_16,
(Char_UUID_t*)&uuid, (Char_UUID_t*)&uuid,
strlen(DEV_INFO_FIRMWARE_REVISION_NUMBER), strlen(dev_info_firmware_rev_num),
CHAR_PROP_READ, CHAR_PROP_READ,
ATTR_PERMISSION_NONE, ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS, GATT_DONT_NOTIFY_EVENTS,
10, 10,
CHAR_VALUE_LEN_CONSTANT, CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc.firmware_rev_char_handle); &dev_info_svc->firmware_rev_char_handle);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add firmware revision char: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add firmware revision char: %d", status);
} }
uuid = SOFTWARE_REVISION_UUID; uuid = SOFTWARE_REVISION_UUID;
status = aci_gatt_add_char(dev_info_svc.service_handle, status = aci_gatt_add_char(dev_info_svc->service_handle,
UUID_TYPE_16, UUID_TYPE_16,
(Char_UUID_t*)&uuid, (Char_UUID_t*)&uuid,
strlen(DEV_INFO_SOFTWARE_REVISION_NUMBER), strlen(dev_info_software_rev_num),
CHAR_PROP_READ, CHAR_PROP_READ,
ATTR_PERMISSION_NONE, ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS, GATT_DONT_NOTIFY_EVENTS,
10, 10,
CHAR_VALUE_LEN_CONSTANT, CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc.software_rev_char_handle); &dev_info_svc->software_rev_char_handle);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add software revision char: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add software revision char: %d", status);
} }
// Update characteristics // Update characteristics
status = aci_gatt_update_char_value(dev_info_svc.service_handle, status = aci_gatt_update_char_value(dev_info_svc->service_handle,
dev_info_svc.man_name_char_handle, dev_info_svc->man_name_char_handle,
0, 0,
strlen(DEV_INFO_MANUFACTURER_NAME), strlen(dev_info_man_name),
(uint8_t*)DEV_INFO_MANUFACTURER_NAME); (uint8_t*)dev_info_man_name);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to update manufacturer name char: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update manufacturer name char: %d", status);
} }
status = aci_gatt_update_char_value(dev_info_svc.service_handle, status = aci_gatt_update_char_value(dev_info_svc->service_handle,
dev_info_svc.serial_num_char_handle, dev_info_svc->serial_num_char_handle,
0, 0,
strlen(DEV_INFO_SERIAL_NUMBER), strlen(dev_info_serial_num),
(uint8_t*)DEV_INFO_SERIAL_NUMBER); (uint8_t*)dev_info_serial_num);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to update serial number char: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update serial number char: %d", status);
} }
status = aci_gatt_update_char_value(dev_info_svc.service_handle, status = aci_gatt_update_char_value(dev_info_svc->service_handle,
dev_info_svc.firmware_rev_char_handle, dev_info_svc->firmware_rev_char_handle,
0, 0,
strlen(DEV_INFO_FIRMWARE_REVISION_NUMBER), strlen(dev_info_firmware_rev_num),
(uint8_t*)DEV_INFO_FIRMWARE_REVISION_NUMBER); (uint8_t*)dev_info_firmware_rev_num);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to update firmware revision char: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update firmware revision char: %d", status);
} }
status = aci_gatt_update_char_value(dev_info_svc.service_handle, status = aci_gatt_update_char_value(dev_info_svc->service_handle,
dev_info_svc.software_rev_char_handle, dev_info_svc->software_rev_char_handle,
0, 0,
strlen(DEV_INFO_SOFTWARE_REVISION_NUMBER), strlen(dev_info_software_rev_num),
(uint8_t*)DEV_INFO_SOFTWARE_REVISION_NUMBER); (uint8_t*)dev_info_software_rev_num);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to update software revision char: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update software revision char: %d", status);
}
}
void dev_info_svc_stop() {
tBleStatus status;
if(dev_info_svc) {
// Delete service characteristics
status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->man_name_char_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete manufacturer name char: %d", status);
}
status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->serial_num_char_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete serial number char: %d", status);
}
status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->firmware_rev_char_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete firmware revision char: %d", status);
}
status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->software_rev_char_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete software revision char: %d", status);
}
// Delete service
status = aci_gatt_del_service(dev_info_svc->service_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete device info service: %d", status);
}
free(dev_info_svc);
dev_info_svc = NULL;
} }
return status != BLE_STATUS_SUCCESS;
} }

View File

@ -12,8 +12,9 @@ extern "C" {
#define DEV_INFO_FIRMWARE_REVISION_NUMBER TARGET #define DEV_INFO_FIRMWARE_REVISION_NUMBER TARGET
#define DEV_INFO_SOFTWARE_REVISION_NUMBER GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE #define DEV_INFO_SOFTWARE_REVISION_NUMBER GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE
void dev_info_svc_start();
bool dev_info_service_init(); void dev_info_svc_stop();
#ifdef __cplusplus #ifdef __cplusplus
} }

View 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);
}
}

View 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

View File

@ -6,13 +6,19 @@
#define SERIAL_SERVICE_TAG "serial service" #define SERIAL_SERVICE_TAG "serial service"
#define SERIAL_SVC_DATA_LEN_MAX 245
typedef struct { typedef struct {
uint16_t svc_handle; uint16_t svc_handle;
uint16_t rx_char_handle; uint16_t rx_char_handle;
uint16_t tx_char_handle; uint16_t tx_char_handle;
} SerialSvc; } SerialSvc;
static SerialSvc serial_svc; static SerialSvc* serial_svc;
static const uint8_t service_uuid[] = {0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f};
static const uint8_t char_rx_uuid[] = {0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
static const uint8_t char_tx_uuid[] = {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) { static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) {
SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck;
@ -22,76 +28,90 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) {
if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) {
if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) {
attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data; attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data;
if(attribute_modified->Attr_Handle == serial_svc.tx_char_handle + 2) { if(attribute_modified->Attr_Handle == serial_svc->tx_char_handle + 2) {
// Descriptor handle // Descriptor handle
ret = SVCCTL_EvtAckFlowEnable; ret = SVCCTL_EvtAckFlowEnable;
FURI_LOG_D(SERIAL_SERVICE_TAG, "TX descriptor event"); FURI_LOG_D(SERIAL_SERVICE_TAG, "TX descriptor event");
} else if(attribute_modified->Attr_Handle == serial_svc.tx_char_handle + 1) { } else if(attribute_modified->Attr_Handle == serial_svc->tx_char_handle + 1) {
FURI_LOG_I(SERIAL_SERVICE_TAG, "Data len: %d", attribute_modified->Attr_Data_Length); FURI_LOG_D(SERIAL_SERVICE_TAG, "Received %d bytes", attribute_modified->Attr_Data_Length);
for(uint8_t i = 0; i < attribute_modified->Attr_Data_Length; i++) {
printf("%02X ", attribute_modified->Attr_Data[i]);
}
printf("\r\n");
serial_svc_update_rx(attribute_modified->Attr_Data, attribute_modified->Attr_Data_Length); serial_svc_update_rx(attribute_modified->Attr_Data, attribute_modified->Attr_Data_Length);
ret = SVCCTL_EvtAckFlowEnable; ret = SVCCTL_EvtAckFlowEnable;
} }
} else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) {
FURI_LOG_I(SERIAL_SERVICE_TAG, "Ack received", blecore_evt->ecode); FURI_LOG_D(SERIAL_SERVICE_TAG, "Ack received", blecore_evt->ecode);
ret = SVCCTL_EvtAckFlowEnable; ret = SVCCTL_EvtAckFlowEnable;
} }
} }
return ret; return ret;
} }
bool serial_svc_init() { void serial_svc_start() {
tBleStatus status; tBleStatus status;
const uint8_t service_uuid[] = {SERIAL_SVC_UUID_128}; serial_svc = furi_alloc(sizeof(SerialSvc));
const uint8_t char_rx_uuid[] = {SERIAL_CHAR_RX_UUID_128};
const uint8_t char_tx_uuid[] = {SERIAL_CHAR_TX_UUID_128};
// Register event handler // Register event handler
SVCCTL_RegisterSvcHandler(serial_svc_event_handler); SVCCTL_RegisterSvcHandler(serial_svc_event_handler);
// Add service // Add service
status = aci_gatt_add_service(UUID_TYPE_128, (Service_UUID_t *)service_uuid, PRIMARY_SERVICE, 6, &serial_svc.svc_handle); status = aci_gatt_add_service(UUID_TYPE_128, (Service_UUID_t *)service_uuid, PRIMARY_SERVICE, 6, &serial_svc->svc_handle);
if(status) { if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status); FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status);
} }
// Add TX characteristics // Add TX characteristics
status = aci_gatt_add_char(serial_svc.svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_tx_uuid , status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_tx_uuid,
SERIAL_SVC_DATA_LEN_MAX, SERIAL_SVC_DATA_LEN_MAX,
CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ,
ATTR_PERMISSION_NONE, ATTR_PERMISSION_NONE,
GATT_NOTIFY_ATTRIBUTE_WRITE, GATT_NOTIFY_ATTRIBUTE_WRITE,
10, 10,
CHAR_VALUE_LEN_VARIABLE, CHAR_VALUE_LEN_VARIABLE,
&serial_svc.tx_char_handle); &serial_svc->tx_char_handle);
if(status) { if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add TX characteristic: %d", status); FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add TX characteristic: %d", status);
} }
// Add RX characteristic // Add RX characteristic
status = aci_gatt_add_char(serial_svc.svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_rx_uuid , status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_rx_uuid,
SERIAL_SVC_DATA_LEN_MAX, SERIAL_SVC_DATA_LEN_MAX,
CHAR_PROP_READ | CHAR_PROP_INDICATE, CHAR_PROP_READ | CHAR_PROP_INDICATE,
ATTR_PERMISSION_NONE, ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS, GATT_DONT_NOTIFY_EVENTS,
10, 10,
CHAR_VALUE_LEN_VARIABLE, CHAR_VALUE_LEN_VARIABLE,
&serial_svc.rx_char_handle); &serial_svc->rx_char_handle);
if(status) { if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add RX characteristic: %d", status); FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add RX characteristic: %d", status);
} }
return status != BLE_STATUS_SUCCESS;
} }
void serial_svc_stop() {
tBleStatus status;
if(serial_svc) {
// Delete characteristics
status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->tx_char_handle);
if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete TX characteristic: %d", status);
}
status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rx_char_handle);
if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete RX characteristic: %d", status);
}
// Delete service
status = aci_gatt_del_service(serial_svc->svc_handle);
if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete Serial service: %d", status);
}
free(serial_svc);
serial_svc = NULL;
}
}
bool serial_svc_update_rx(uint8_t* data, uint8_t data_len) { bool serial_svc_update_rx(uint8_t* data, uint8_t data_len) {
furi_assert(data_len < SERIAL_SVC_DATA_LEN_MAX); furi_assert(data_len < SERIAL_SVC_DATA_LEN_MAX);
tBleStatus result = aci_gatt_update_char_value(serial_svc.svc_handle, tBleStatus result = aci_gatt_update_char_value(serial_svc->svc_handle,
serial_svc.rx_char_handle, serial_svc->rx_char_handle,
0, 0,
data_len, data_len,
data); data);

View File

@ -7,13 +7,9 @@
extern "C" { extern "C" {
#endif #endif
#define SERIAL_SVC_DATA_LEN_MAX 255 void serial_svc_start();
#define SERIAL_SVC_UUID_128 0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f void serial_svc_stop();
#define SERIAL_CHAR_RX_UUID_128 0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19
#define SERIAL_CHAR_TX_UUID_128 0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19
bool serial_svc_init();
bool serial_svc_update_rx(uint8_t* data, uint8_t data_len); bool serial_svc_update_rx(uint8_t* data, uint8_t data_len);

View File

@ -5,6 +5,7 @@
#include <shci.h> #include <shci.h>
#include <cmsis_os2.h> #include <cmsis_os2.h>
#include <app_ble.h> #include <app_ble.h>
#include <gap.h>
void furi_hal_bt_init() { void furi_hal_bt_init() {
// Explicitly tell that we are in charge of CLK48 domain // Explicitly tell that we are in charge of CLK48 domain
@ -14,7 +15,7 @@ void furi_hal_bt_init() {
} }
bool furi_hal_bt_start_app() { bool furi_hal_bt_start_app() {
return APP_BLE_Start(); return gap_init();
} }
void furi_hal_bt_dump_state(string_t buffer) { void furi_hal_bt_dump_state(string_t buffer) {

View File

@ -6,106 +6,20 @@
#include "ble.h" #include "ble.h"
#include "tl.h" #include "tl.h"
#include "app_ble.h" #include "app_ble.h"
#include "cmsis_os.h"
#include "shci.h" #include "shci.h"
#include "otp.h" #include "cmsis_os.h"
#include "dev_info_service.h"
#include "battery_service.h"
#include "serial_service.h"
#include <furi-hal.h> #include <furi-hal.h>
typedef struct _tSecurityParams {
uint8_t ioCapability;
uint8_t mitm_mode;
uint8_t bonding_mode;
uint8_t Use_Fixed_Pin;
uint8_t encryptionKeySizeMin;
uint8_t encryptionKeySizeMax;
uint32_t Fixed_Pin;
uint8_t initiateSecurity;
} tSecurityParams;
typedef struct _tBLEProfileGlobalContext {
tSecurityParams bleSecurityParam;
uint16_t gapServiceHandle;
uint16_t devNameCharHandle;
uint16_t appearanceCharHandle;
uint16_t connectionHandle;
uint8_t advtServUUIDlen;
uint8_t advtServUUID[100];
} BleGlobalContext_t;
typedef struct {
BleGlobalContext_t BleApplicationContext_legacy;
APP_BLE_ConnStatus_t Device_Connection_Status;
uint8_t Advertising_mgr_timer_Id;
} BleApplicationContext_t;
#define FAST_ADV_TIMEOUT (30*1000*1000/CFG_TS_TICK_VAL) /**< 30s */
#define INITIAL_ADV_TIMEOUT (60*1000*1000/CFG_TS_TICK_VAL) /**< 60s */
#define BD_ADDR_SIZE_LOCAL 6
#define LED_ON_TIMEOUT (0.005*1000*1000/CFG_TS_TICK_VAL) /**< 5ms */
PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer; PLACE_IN_SECTION("MB_MEM1") ALIGN(4) static TL_CmdPacket_t BleCmdBuffer;
static const uint8_t M_bd_addr[BD_ADDR_SIZE_LOCAL] = // PLACE_IN_SECTION("TAG_OTA_END") const uint32_t MagicKeywordValue = 0x94448A29 ;
{ // PLACE_IN_SECTION("TAG_OTA_START") const uint32_t MagicKeywordAddress = (uint32_t)&MagicKeywordValue;
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x0000000000FF)),
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x00000000FF00) >> 8),
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x000000FF0000) >> 16),
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x0000FF000000) >> 24),
(uint8_t)((CFG_ADV_BD_ADDRESS & 0x00FF00000000) >> 32),
(uint8_t)((CFG_ADV_BD_ADDRESS & 0xFF0000000000) >> 40)
};
static uint8_t bd_addr_udn[BD_ADDR_SIZE_LOCAL];
static const uint8_t BLE_CFG_IR_VALUE[16] = CFG_BLE_IRK;
static const uint8_t BLE_CFG_ER_VALUE[16] = CFG_BLE_ERK;
PLACE_IN_SECTION("TAG_OTA_END") const uint32_t MagicKeywordValue = 0x94448A29 ;
PLACE_IN_SECTION("TAG_OTA_START") const uint32_t MagicKeywordAddress = (uint32_t)&MagicKeywordValue;
PLACE_IN_SECTION("BLE_APP_CONTEXT") static BleApplicationContext_t BleApplicationContext;
PLACE_IN_SECTION("BLE_APP_CONTEXT") static uint16_t AdvIntervalMin, AdvIntervalMax;
uint8_t manuf_data[14] = {
sizeof(manuf_data)-1, AD_TYPE_MANUFACTURER_SPECIFIC_DATA,
0x01/*SKD version */,
0x00 /* Generic*/,
0x00 /* GROUP A Feature */,
0x00 /* GROUP A Feature */,
0x00 /* GROUP B Feature */,
0x00 /* GROUP B Feature */,
0x00, /* BLE MAC start -MSB */
0x00,
0x00,
0x00,
0x00,
0x00, /* BLE MAC stop */
};
osMutexId_t MtxHciId; osMutexId_t MtxHciId;
osSemaphoreId_t SemHciId; osSemaphoreId_t SemHciId;
osThreadId_t AdvUpdateProcessId;
osThreadId_t HciUserEvtProcessId; osThreadId_t HciUserEvtProcessId;
const osThreadAttr_t AdvUpdateProcess_attr = {
.name = CFG_ADV_UPDATE_PROCESS_NAME,
.attr_bits = CFG_ADV_UPDATE_PROCESS_ATTR_BITS,
.cb_mem = CFG_ADV_UPDATE_PROCESS_CB_MEM,
.cb_size = CFG_ADV_UPDATE_PROCESS_CB_SIZE,
.stack_mem = CFG_ADV_UPDATE_PROCESS_STACK_MEM,
.priority = CFG_ADV_UPDATE_PROCESS_PRIORITY,
.stack_size = CFG_ADV_UPDATE_PROCESS_STACK_SIZE
};
const osThreadAttr_t HciUserEvtProcess_attr = { const osThreadAttr_t HciUserEvtProcess_attr = {
.name = CFG_HCI_USER_EVT_PROCESS_NAME, .name = CFG_HCI_USER_EVT_PROCESS_NAME,
.attr_bits = CFG_HCI_USER_EVT_PROCESS_ATTR_BITS, .attr_bits = CFG_HCI_USER_EVT_PROCESS_ATTR_BITS,
@ -121,13 +35,6 @@ static void HciUserEvtProcess(void *argument);
static void BLE_UserEvtRx( void * pPayload ); static void BLE_UserEvtRx( void * pPayload );
static void BLE_StatusNot( HCI_TL_CmdStatus_t status ); static void BLE_StatusNot( HCI_TL_CmdStatus_t status );
static void Ble_Tl_Init( void ); static void Ble_Tl_Init( void );
static void Ble_Hci_Gap_Gatt_Init();
static const uint8_t* BleGetBdAddress( void );
static void Adv_Request( APP_BLE_ConnStatus_t New_Status );
static void Adv_Mgr( void );
static void AdvUpdateProcess(void *argument);
static void Adv_Update( void );
bool APP_BLE_Init() { bool APP_BLE_Init() {
SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = { SHCI_C2_Ble_Init_Cmd_Packet_t ble_init_cmd_packet = {
@ -160,254 +67,6 @@ bool APP_BLE_Init() {
return (SHCI_C2_BLE_Init( &ble_init_cmd_packet ) == SHCI_Success); return (SHCI_C2_BLE_Init( &ble_init_cmd_packet ) == SHCI_Success);
} }
static void set_advertisment_service_uid(uint8_t* uid, uint8_t uin_len);
bool APP_BLE_Start() {
if (APPE_Status() != BleGlueStatusStarted) {
return false;
}
// Initialization of HCI & GATT & GAP layer
Ble_Hci_Gap_Gatt_Init();
// Initialization of the BLE Services
SVCCTL_Init();
// Initialization of the BLE App Context
BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE;
BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0xFFFF;
// From here, all initialization are BLE application specific
AdvUpdateProcessId = osThreadNew(AdvUpdateProcess, NULL, &AdvUpdateProcess_attr);
// Initialization of ADV - Ad Manufacturer Element - Support OTA Bit Masks
#if(BLE_CFG_OTA_REBOOT_CHAR != 0)
manuf_data[sizeof(manuf_data)-8] = CFG_FEATURE_OTA_REBOOT;
#endif
// Initialize DIS Application
dev_info_service_init();
// Initialize BAS Application
battery_svc_init();
// Initialize Serial application
serial_svc_init();
// Create timer to handle the connection state machine
HW_TS_Create(CFG_TIM_PROC_ID_ISR, &(BleApplicationContext.Advertising_mgr_timer_Id), hw_ts_SingleShot, Adv_Mgr);
uint8_t adv_service_uid[2];
adv_service_uid[0] = 0x80 | furi_hal_version_get_hw_color();
adv_service_uid[1] = 0x30;
set_advertisment_service_uid(adv_service_uid, sizeof(adv_service_uid));
/* Initialize intervals for reconnexion without intervals update */
AdvIntervalMin = CFG_FAST_CONN_ADV_INTERVAL_MIN;
AdvIntervalMax = CFG_FAST_CONN_ADV_INTERVAL_MAX;
Adv_Request(APP_BLE_FAST_ADV);
return true;
}
void SVCCTL_SvcInit() {
// Dummy function to prevent unused services initialization
// TODO refactore
}
SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification( void *pckt )
{
hci_event_pckt *event_pckt;
evt_le_meta_event *meta_evt;
evt_blue_aci *blue_evt;
hci_le_phy_update_complete_event_rp0 *evt_le_phy_update_complete;
uint8_t TX_PHY, RX_PHY;
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
event_pckt = (hci_event_pckt*) ((hci_uart_pckt *) pckt)->data;
switch (event_pckt->evt) {
case EVT_DISCONN_COMPLETE:
{
hci_disconnection_complete_event_rp0 *disconnection_complete_event;
disconnection_complete_event = (hci_disconnection_complete_event_rp0 *) event_pckt->data;
if (disconnection_complete_event->Connection_Handle == BleApplicationContext.BleApplicationContext_legacy.connectionHandle) {
BleApplicationContext.BleApplicationContext_legacy.connectionHandle = 0;
BleApplicationContext.Device_Connection_Status = APP_BLE_IDLE;
APP_DBG_MSG("\r\n\r** DISCONNECTION EVENT WITH CLIENT \r\n");
}
/* restart advertising */
Adv_Request(APP_BLE_FAST_ADV);
furi_hal_power_insomnia_exit();
}
break; /* EVT_DISCONN_COMPLETE */
case EVT_LE_META_EVENT:
{
meta_evt = (evt_le_meta_event*) event_pckt->data;
switch (meta_evt->subevent)
{
case EVT_LE_CONN_UPDATE_COMPLETE:
APP_DBG_MSG("\r\n\r** CONNECTION UPDATE EVENT WITH CLIENT \r\n");
/* USER CODE BEGIN EVT_LE_CONN_UPDATE_COMPLETE */
/* USER CODE END EVT_LE_CONN_UPDATE_COMPLETE */
break;
case EVT_LE_PHY_UPDATE_COMPLETE:
APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE \r\n");
evt_le_phy_update_complete = (hci_le_phy_update_complete_event_rp0*)meta_evt->data;
if (evt_le_phy_update_complete->Status == 0)
{
APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status ok \r\n");
}
else
{
APP_DBG_MSG("EVT_UPDATE_PHY_COMPLETE, status nok \r\n");
}
ret = hci_le_read_phy(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,&TX_PHY,&RX_PHY);
if (ret == BLE_STATUS_SUCCESS)
{
APP_DBG_MSG("Read_PHY success \r\n");
if ((TX_PHY == TX_2M) && (RX_PHY == RX_2M))
{
APP_DBG_MSG("PHY Param TX= %d, RX= %d \r\n", TX_PHY, RX_PHY);
}
else
{
APP_DBG_MSG("PHY Param TX= %d, RX= %d \r\n", TX_PHY, RX_PHY);
}
}
else
{
APP_DBG_MSG("Read conf not succeess \r\n");
}
break;
case EVT_LE_CONN_COMPLETE:
{
furi_hal_power_insomnia_enter();
hci_le_connection_complete_event_rp0 *connection_complete_event;
/**
* The connection is done, there is no need anymore to schedule the LP ADV
*/
connection_complete_event = (hci_le_connection_complete_event_rp0 *) meta_evt->data;
HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id);
APP_DBG_MSG("EVT_LE_CONN_COMPLETE for connection handle 0x%x\r\n", connection_complete_event->Connection_Handle);
if (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_CONNECTING)
{
/* Connection as client */
BleApplicationContext.Device_Connection_Status = APP_BLE_CONNECTED_CLIENT;
}
else
{
/* Connection as server */
BleApplicationContext.Device_Connection_Status = APP_BLE_CONNECTED_SERVER;
}
BleApplicationContext.BleApplicationContext_legacy.connectionHandle = connection_complete_event->Connection_Handle;
}
break; /* HCI_EVT_LE_CONN_COMPLETE */
default:
break;
}
}
break; /* HCI_EVT_LE_META_EVENT */
case EVT_VENDOR:
blue_evt = (evt_blue_aci*) event_pckt->data;
switch (blue_evt->ecode) {
aci_gap_pairing_complete_event_rp0 *pairing_complete;
case EVT_BLUE_GAP_LIMITED_DISCOVERABLE:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_LIMITED_DISCOVERABLE \r\n");
break; /* EVT_BLUE_GAP_LIMITED_DISCOVERABLE */
case EVT_BLUE_GAP_PASS_KEY_REQUEST:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PASS_KEY_REQUEST \r\n");
aci_gap_pass_key_resp(BleApplicationContext.BleApplicationContext_legacy.connectionHandle,123456);
APP_DBG_MSG("\r\n\r** aci_gap_pass_key_resp \r\n");
break; /* EVT_BLUE_GAP_PASS_KEY_REQUEST */
case EVT_BLUE_GAP_AUTHORIZATION_REQUEST:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_AUTHORIZATION_REQUEST \r\n");
break; /* EVT_BLUE_GAP_AUTHORIZATION_REQUEST */
case EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED \r\n");
break; /* EVT_BLUE_GAP_SLAVE_SECURITY_INITIATED */
case EVT_BLUE_GAP_BOND_LOST:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_BOND_LOST \r\n");
aci_gap_allow_rebond(BleApplicationContext.BleApplicationContext_legacy.connectionHandle);
APP_DBG_MSG("\r\n\r** Send allow rebond \r\n");
break; /* EVT_BLUE_GAP_BOND_LOST */
case EVT_BLUE_GAP_DEVICE_FOUND:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \r\n");
break; /* EVT_BLUE_GAP_DEVICE_FOUND */
case EVT_BLUE_GAP_ADDR_NOT_RESOLVED:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_DEVICE_FOUND \r\n");
break; /* EVT_BLUE_GAP_DEVICE_FOUND */
case (EVT_BLUE_GAP_KEYPRESS_NOTIFICATION):
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_KEYPRESS_NOTIFICATION \r\n");
break; /* EVT_BLUE_GAP_KEY_PRESS_NOTIFICATION */
case (EVT_BLUE_GAP_NUMERIC_COMPARISON_VALUE):
APP_DBG_MSG("numeric_value = %ld\r\n",
((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value);
APP_DBG_MSG("Hex_value = %lx\r\n",
((aci_gap_numeric_comparison_value_event_rp0 *)(blue_evt->data))->Numeric_Value);
aci_gap_numeric_comparison_value_confirm_yesno(BleApplicationContext.BleApplicationContext_legacy.connectionHandle, 1); /* CONFIRM_YES = 1 */
APP_DBG_MSG("\r\n\r** aci_gap_numeric_comparison_value_confirm_yesno-->YES \r\n");
break;
case (EVT_BLUE_GAP_PAIRING_CMPLT):
{
pairing_complete = (aci_gap_pairing_complete_event_rp0*)blue_evt->data;
APP_DBG_MSG("BLE_CTRL_App_Notification: EVT_BLUE_GAP_PAIRING_CMPLT, pairing_complete->Status = %d\r\n",pairing_complete->Status);
if (pairing_complete->Status == 0) {
APP_DBG_MSG("\r\n\r** Pairing OK \r\n");
} else {
APP_DBG_MSG("\r\n\r** Pairing KO \r\n");
}
}
break;
/* USER CODE END ecode */
case EVT_BLUE_GAP_PROCEDURE_COMPLETE:
APP_DBG_MSG("\r\n\r** EVT_BLUE_GAP_PROCEDURE_COMPLETE \r\n");
break;
}
break; /* EVT_VENDOR */
default:
break;
}
return (SVCCTL_UserEvtFlowEnable);
}
static void set_advertisment_service_uid(uint8_t* uid, uint8_t uid_len) {
BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen = 1;
if(uid_len == 2) {
BleApplicationContext.BleApplicationContext_legacy.advtServUUID[0] = AD_TYPE_16_BIT_SERV_UUID;
} else if (uid_len == 4) {
BleApplicationContext.BleApplicationContext_legacy.advtServUUID[0] = AD_TYPE_32_BIT_SERV_UUID;
} else if(uid_len == 16) {
BleApplicationContext.BleApplicationContext_legacy.advtServUUID[0] = AD_TYPE_128_BIT_SERV_UUID_CMPLT_LIST;
}
memcpy(&BleApplicationContext.BleApplicationContext_legacy.advtServUUID[1], uid, uid_len);
BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen += uid_len;
}
APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status() {
return BleApplicationContext.Device_Connection_Status;
}
static void Ble_Tl_Init( void ) { static void Ble_Tl_Init( void ) {
HCI_TL_HciInitConf_t Hci_Tl_Init_Conf; HCI_TL_HciInitConf_t Hci_Tl_Init_Conf;
@ -419,301 +78,6 @@ static void Ble_Tl_Init( void ) {
hci_init(BLE_UserEvtRx, (void*) &Hci_Tl_Init_Conf); hci_init(BLE_UserEvtRx, (void*) &Hci_Tl_Init_Conf);
} }
static void Ble_Hci_Gap_Gatt_Init() {
uint8_t role;
uint16_t gap_service_handle, gap_dev_name_char_handle, gap_appearance_char_handle;
const uint8_t *bd_addr;
uint32_t srd_bd_addr[2];
uint16_t appearance[1] = { BLE_CFG_GAP_APPEARANCE };
/*HCI Reset to synchronise BLE Stack*/
hci_reset();
/**
* Write the BD Address
*/
bd_addr = BleGetBdAddress();
aci_hal_write_config_data(CONFIG_DATA_PUBADDR_OFFSET,
CONFIG_DATA_PUBADDR_LEN,
(uint8_t*) bd_addr);
/* BLE MAC in ADV Packet */
manuf_data[ sizeof(manuf_data)-6] = bd_addr[5];
manuf_data[ sizeof(manuf_data)-5] = bd_addr[4];
manuf_data[ sizeof(manuf_data)-4] = bd_addr[3];
manuf_data[ sizeof(manuf_data)-3] = bd_addr[2];
manuf_data[ sizeof(manuf_data)-2] = bd_addr[1];
manuf_data[ sizeof(manuf_data)-1] = bd_addr[0];
/**
* Write Identity root key used to derive LTK and CSRK
*/
aci_hal_write_config_data(CONFIG_DATA_IR_OFFSET,
CONFIG_DATA_IR_LEN,
(uint8_t*) BLE_CFG_IR_VALUE);
/**
* Write Encryption root key used to derive LTK and CSRK
*/
aci_hal_write_config_data(CONFIG_DATA_ER_OFFSET,
CONFIG_DATA_ER_LEN,
(uint8_t*) BLE_CFG_ER_VALUE);
/**
* Write random bd_address
*/
/* random_bd_address = R_bd_address;
aci_hal_write_config_data(CONFIG_DATA_RANDOM_ADDRESS_WR,
CONFIG_DATA_RANDOM_ADDRESS_LEN,
(uint8_t*) random_bd_address);
*/
/**
* Static random Address
* The two upper bits shall be set to 1
* The lowest 32bits is read from the UDN to differentiate between devices
* The RNG may be used to provide a random number on each power on
*/
srd_bd_addr[1] = 0x0000ED6E;
srd_bd_addr[0] = LL_FLASH_GetUDN( );
aci_hal_write_config_data( CONFIG_DATA_RANDOM_ADDRESS_OFFSET, CONFIG_DATA_RANDOM_ADDRESS_LEN, (uint8_t*)srd_bd_addr );
/**
* Write Identity root key used to derive LTK and CSRK
*/
aci_hal_write_config_data( CONFIG_DATA_IR_OFFSET, CONFIG_DATA_IR_LEN, (uint8_t*)BLE_CFG_IR_VALUE );
/**
* Write Encryption root key used to derive LTK and CSRK
*/
aci_hal_write_config_data( CONFIG_DATA_ER_OFFSET, CONFIG_DATA_ER_LEN, (uint8_t*)BLE_CFG_ER_VALUE );
/**
* Set TX Power to 0dBm.
*/
aci_hal_set_tx_power_level(1, CFG_TX_POWER);
/**
* Initialize GATT interface
*/
aci_gatt_init();
/**
* Initialize GAP interface
*/
role = 0;
#if (BLE_CFG_PERIPHERAL == 1)
role |= GAP_PERIPHERAL_ROLE;
#endif
#if (BLE_CFG_CENTRAL == 1)
role |= GAP_CENTRAL_ROLE;
#endif
if (role > 0)
{
const char *name = furi_hal_version_get_device_name_ptr();
aci_gap_init(role, 0,
strlen(name),
&gap_service_handle, &gap_dev_name_char_handle, &gap_appearance_char_handle);
if (aci_gatt_update_char_value(gap_service_handle, gap_dev_name_char_handle, 0, strlen(name), (uint8_t *) name))
{
BLE_DBG_SVCCTL_MSG("Device Name aci_gatt_update_char_value failed.\r\n");
}
}
if(aci_gatt_update_char_value(gap_service_handle,
gap_appearance_char_handle,
0,
2,
(uint8_t *)&appearance))
{
BLE_DBG_SVCCTL_MSG("Appearance aci_gatt_update_char_value failed.\r\n");
}
/**
* Initialize Default PHY
*/
hci_le_set_default_phy(ALL_PHYS_PREFERENCE,TX_2M_PREFERRED,RX_2M_PREFERRED);
/**
* Initialize IO capability
*/
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.ioCapability = CFG_IO_CAPABILITY;
aci_gap_set_io_capability(BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.ioCapability);
/**
* Initialize authentication
*/
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.mitm_mode = CFG_MITM_PROTECTION;
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMin = CFG_ENCRYPTION_KEY_SIZE_MIN;
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMax = CFG_ENCRYPTION_KEY_SIZE_MAX;
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Use_Fixed_Pin = CFG_USED_FIXED_PIN;
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Fixed_Pin = CFG_FIXED_PIN;
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode = CFG_BONDING_MODE;
aci_gap_set_authentication_requirement(BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode,
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.mitm_mode,
CFG_SC_SUPPORT,
CFG_KEYPRESS_NOTIFICATION_SUPPORT,
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMin,
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.encryptionKeySizeMax,
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Use_Fixed_Pin,
BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.Fixed_Pin,
PUBLIC_ADDR
);
/**
* Initialize whitelist
*/
if (BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode)
{
aci_gap_configure_whitelist();
}
}
static void Adv_Request(APP_BLE_ConnStatus_t New_Status)
{
tBleStatus ret = BLE_STATUS_INVALID_PARAMS;
uint16_t Min_Inter, Max_Inter;
if (New_Status == APP_BLE_FAST_ADV)
{
Min_Inter = AdvIntervalMin;
Max_Inter = AdvIntervalMax;
}
else
{
Min_Inter = CFG_LP_CONN_ADV_INTERVAL_MIN;
Max_Inter = CFG_LP_CONN_ADV_INTERVAL_MAX;
}
/**
* Stop the timer, it will be restarted for a new shot
* It does not hurt if the timer was not running
*/
HW_TS_Stop(BleApplicationContext.Advertising_mgr_timer_Id);
APP_DBG_MSG("First index in %d state \r\n", BleApplicationContext.Device_Connection_Status);
if ((New_Status == APP_BLE_LP_ADV)
&& ((BleApplicationContext.Device_Connection_Status == APP_BLE_FAST_ADV)
|| (BleApplicationContext.Device_Connection_Status == APP_BLE_LP_ADV)))
{
/* Connection in ADVERTISE mode have to stop the current advertising */
ret = aci_gap_set_non_discoverable();
if (ret == BLE_STATUS_SUCCESS)
{
APP_DBG_MSG("Successfully Stopped Advertising \r\n");
}
else
{
APP_DBG_MSG("Stop Advertising Failed , result: %d \r\n", ret);
}
}
BleApplicationContext.Device_Connection_Status = New_Status;
const char* name = furi_hal_version_get_ble_local_device_name_ptr();
/* Start Fast or Low Power Advertising */
ret = aci_gap_set_discoverable(
ADV_IND,
Min_Inter,
Max_Inter,
PUBLIC_ADDR,
NO_WHITE_LIST_USE, /* use white list */
strlen(name),
(uint8_t*)name,
BleApplicationContext.BleApplicationContext_legacy.advtServUUIDlen,
BleApplicationContext.BleApplicationContext_legacy.advtServUUID,
0,
0);
if(ret) {
FURI_LOG_E("APP ble", "Set discoverable err: %d", ret);
}
/* Update Advertising data */
ret = aci_gap_update_adv_data(sizeof(manuf_data), (uint8_t*) manuf_data);
if (ret == BLE_STATUS_SUCCESS) {
if (New_Status == APP_BLE_FAST_ADV) {
APP_DBG_MSG("Successfully Start Fast Advertising \r\n" );
/* Start Timer to STOP ADV - TIMEOUT */
HW_TS_Start(BleApplicationContext.Advertising_mgr_timer_Id, INITIAL_ADV_TIMEOUT);
} else {
APP_DBG_MSG("Successfully Start Low Power Advertising \r\n");
}
} else {
if (New_Status == APP_BLE_FAST_ADV) {
APP_DBG_MSG("Start Fast Advertising Failed , result: %d \r\n", ret);
} else {
APP_DBG_MSG("Start Low Power Advertising Failed , result: %d \r\n", ret);
}
}
}
const uint8_t* BleGetBdAddress( void ) {
uint8_t *otp_addr;
const uint8_t *bd_addr;
uint32_t udn;
uint32_t company_id;
uint32_t device_id;
udn = LL_FLASH_GetUDN();
if(udn != 0xFFFFFFFF) {
company_id = LL_FLASH_GetSTCompanyID();
device_id = LL_FLASH_GetDeviceID();
bd_addr_udn[0] = (uint8_t)(udn & 0x000000FF);
bd_addr_udn[1] = (uint8_t)( (udn & 0x0000FF00) >> 8 );
bd_addr_udn[2] = (uint8_t)( (udn & 0x00FF0000) >> 16 );
bd_addr_udn[3] = (uint8_t)device_id;
bd_addr_udn[4] = (uint8_t)(company_id & 0x000000FF);;
bd_addr_udn[5] = (uint8_t)( (company_id & 0x0000FF00) >> 8 );
bd_addr = (const uint8_t *)bd_addr_udn;
} else {
otp_addr = OTP_Read(0);
if(otp_addr) {
bd_addr = ((OTP_ID0_t*)otp_addr)->bd_address;
} else {
bd_addr = M_bd_addr;
}
}
return bd_addr;
}
/*************************************************************
*
*SPECIFIC FUNCTIONS
*
*************************************************************/
static void Adv_Mgr( void ) {
/**
* The code shall be executed in the background as an aci command may be sent
* The background is the only place where the application can make sure a new aci command
* is not sent if there is a pending one
*/
osThreadFlagsSet( AdvUpdateProcessId, 1 );
}
static void AdvUpdateProcess(void *argument) {
UNUSED(argument);
for(;;) {
osThreadFlagsWait( 1, osFlagsWaitAny, osWaitForever);
Adv_Update( );
}
}
static void Adv_Update( void ) {
Adv_Request(APP_BLE_LP_ADV);
}
static void HciUserEvtProcess(void *argument) { static void HciUserEvtProcess(void *argument) {
UNUSED(argument); UNUSED(argument);

View File

@ -7,20 +7,7 @@ extern "C" {
#include <stdbool.h> #include <stdbool.h>
#include "hci_tl.h" #include "hci_tl.h"
typedef enum {
APP_BLE_IDLE,
APP_BLE_FAST_ADV,
APP_BLE_LP_ADV,
APP_BLE_SCAN,
APP_BLE_LP_CONNECTING,
APP_BLE_CONNECTED_SERVER,
APP_BLE_CONNECTED_CLIENT
} APP_BLE_ConnStatus_t;
bool APP_BLE_Init(); bool APP_BLE_Init();
bool APP_BLE_Start();
APP_BLE_ConnStatus_t APP_BLE_Get_Server_Connection_Status();
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -139,7 +139,7 @@
/** /**
* Maximum supported ATT_MTU size * Maximum supported ATT_MTU size
*/ */
#define CFG_BLE_MAX_ATT_MTU (156) #define CFG_BLE_MAX_ATT_MTU (251)
/** /**
* Size of the storage area for Attribute values * Size of the storage area for Attribute values

View File

@ -11,21 +11,22 @@ typedef struct {
uint16_t char_level_handle; uint16_t char_level_handle;
} BatterySvc; } BatterySvc;
static BatterySvc battery_svc; static BatterySvc* battery_svc = NULL;
bool battery_svc_init() { static const uint16_t service_uuid = BATTERY_SERVICE_UUID;
static const uint16_t char_battery_level_uuid = BATTERY_LEVEL_CHAR_UUID;
void battery_svc_start() {
battery_svc = furi_alloc(sizeof(BatterySvc));
tBleStatus status; tBleStatus status;
const uint16_t service_uuid = BATTERY_SERVICE_UUID;
const uint16_t char_battery_level_uuid = BATTERY_LEVEL_CHAR_UUID;
// Add Battery service // Add Battery service
status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&service_uuid, PRIMARY_SERVICE, 4, &battery_svc.svc_handle); status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&service_uuid, PRIMARY_SERVICE, 4, &battery_svc->svc_handle);
if(status) { if(status) {
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery service: %d", status); FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery service: %d", status);
} }
// Add Battery level characteristic // Add Battery level characteristic
status = aci_gatt_add_char(battery_svc.svc_handle, status = aci_gatt_add_char(battery_svc->svc_handle,
UUID_TYPE_16, UUID_TYPE_16,
(Char_UUID_t *) &char_battery_level_uuid, (Char_UUID_t *) &char_battery_level_uuid,
1, 1,
@ -34,17 +35,39 @@ bool battery_svc_init() {
GATT_DONT_NOTIFY_EVENTS, GATT_DONT_NOTIFY_EVENTS,
10, 10,
CHAR_VALUE_LEN_CONSTANT, CHAR_VALUE_LEN_CONSTANT,
&battery_svc.char_level_handle); &battery_svc->char_level_handle);
if(status) { if(status) {
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery level characteristic: %d", status); FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to add Battery level characteristic: %d", status);
} }
return status != BLE_STATUS_SUCCESS; }
void battery_svc_stop() {
tBleStatus status;
if(battery_svc) {
// Delete Battery level characteristic
status = aci_gatt_del_char(battery_svc->svc_handle, battery_svc->char_level_handle);
if(status) {
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to delete Battery level characteristic: %d", status);
}
// Delete Battery service
status = aci_gatt_del_service(battery_svc->svc_handle);
if(status) {
FURI_LOG_E(BATTERY_SERVICE_TAG, "Failed to delete Battery service: %d", status);
}
free(battery_svc);
battery_svc = NULL;
}
} }
bool battery_svc_update_level(uint8_t battery_charge) { bool battery_svc_update_level(uint8_t battery_charge) {
// Check if service was started
if(battery_svc == NULL) {
return false;
}
// Update battery level characteristic
FURI_LOG_I(BATTERY_SERVICE_TAG, "Updating battery level characteristic"); FURI_LOG_I(BATTERY_SERVICE_TAG, "Updating battery level characteristic");
tBleStatus result = aci_gatt_update_char_value(battery_svc.svc_handle, tBleStatus result = aci_gatt_update_char_value(battery_svc->svc_handle,
battery_svc.char_level_handle, battery_svc->char_level_handle,
0, 0,
1, 1,
&battery_charge); &battery_charge);

View File

@ -7,7 +7,9 @@
extern "C" { extern "C" {
#endif #endif
bool battery_svc_init(); void battery_svc_start();
void battery_svc_stop();
bool battery_svc_update_level(uint8_t battery_level); bool battery_svc_update_level(uint8_t battery_level);

View File

@ -4,7 +4,7 @@
#include <furi.h> #include <furi.h>
#define DEV_INFO_SERVICE_TAG "dev info service" #define DEV_INFO_SVC_TAG "dev info service"
typedef struct { typedef struct {
uint16_t service_handle; uint16_t service_handle;
@ -14,108 +14,143 @@ typedef struct {
uint16_t software_rev_char_handle; uint16_t software_rev_char_handle;
} DevInfoSvc; } DevInfoSvc;
bool dev_info_service_init() { static DevInfoSvc* dev_info_svc = NULL;
static const char dev_info_man_name[] = "Flipper Devices Inc.";
static const char dev_info_serial_num[] = "1.0";
static const char dev_info_firmware_rev_num[] = TARGET;
static const char dev_info_software_rev_num[] = GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE;
void dev_info_svc_start() {
dev_info_svc = furi_alloc(sizeof(DevInfoSvc));
tBleStatus status; tBleStatus status;
DevInfoSvc dev_info_svc;
// Add Device Information Service // Add Device Information Service
uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID; uint16_t uuid = DEVICE_INFORMATION_SERVICE_UUID;
status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 9, &dev_info_svc.service_handle); status = aci_gatt_add_service(UUID_TYPE_16, (Service_UUID_t*)&uuid, PRIMARY_SERVICE, 9, &dev_info_svc->service_handle);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add Device Information Service: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add Device Information Service: %d", status);
} }
// Add characteristics // Add characteristics
uuid = MANUFACTURER_NAME_UUID; uuid = MANUFACTURER_NAME_UUID;
status = aci_gatt_add_char(dev_info_svc.service_handle, status = aci_gatt_add_char(dev_info_svc->service_handle,
UUID_TYPE_16, UUID_TYPE_16,
(Char_UUID_t*)&uuid, (Char_UUID_t*)&uuid,
strlen(DEV_INFO_MANUFACTURER_NAME), strlen(dev_info_man_name),
CHAR_PROP_READ, CHAR_PROP_READ,
ATTR_PERMISSION_NONE, ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS, GATT_DONT_NOTIFY_EVENTS,
10, 10,
CHAR_VALUE_LEN_CONSTANT, CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc.man_name_char_handle); &dev_info_svc->man_name_char_handle);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add manufacturer name char: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add manufacturer name char: %d", status);
} }
uuid = SERIAL_NUMBER_UUID; uuid = SERIAL_NUMBER_UUID;
status = aci_gatt_add_char(dev_info_svc.service_handle, status = aci_gatt_add_char(dev_info_svc->service_handle,
UUID_TYPE_16, UUID_TYPE_16,
(Char_UUID_t*)&uuid, (Char_UUID_t*)&uuid,
strlen(DEV_INFO_SERIAL_NUMBER), strlen(dev_info_serial_num),
CHAR_PROP_READ, CHAR_PROP_READ,
ATTR_PERMISSION_NONE, ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS, GATT_DONT_NOTIFY_EVENTS,
10, 10,
CHAR_VALUE_LEN_CONSTANT, CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc.serial_num_char_handle); &dev_info_svc->serial_num_char_handle);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add serial number char: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add serial number char: %d", status);
} }
uuid = FIRMWARE_REVISION_UUID; uuid = FIRMWARE_REVISION_UUID;
status = aci_gatt_add_char(dev_info_svc.service_handle, status = aci_gatt_add_char(dev_info_svc->service_handle,
UUID_TYPE_16, UUID_TYPE_16,
(Char_UUID_t*)&uuid, (Char_UUID_t*)&uuid,
strlen(DEV_INFO_FIRMWARE_REVISION_NUMBER), strlen(dev_info_firmware_rev_num),
CHAR_PROP_READ, CHAR_PROP_READ,
ATTR_PERMISSION_NONE, ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS, GATT_DONT_NOTIFY_EVENTS,
10, 10,
CHAR_VALUE_LEN_CONSTANT, CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc.firmware_rev_char_handle); &dev_info_svc->firmware_rev_char_handle);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add firmware revision char: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add firmware revision char: %d", status);
} }
uuid = SOFTWARE_REVISION_UUID; uuid = SOFTWARE_REVISION_UUID;
status = aci_gatt_add_char(dev_info_svc.service_handle, status = aci_gatt_add_char(dev_info_svc->service_handle,
UUID_TYPE_16, UUID_TYPE_16,
(Char_UUID_t*)&uuid, (Char_UUID_t*)&uuid,
strlen(DEV_INFO_SOFTWARE_REVISION_NUMBER), strlen(dev_info_software_rev_num),
CHAR_PROP_READ, CHAR_PROP_READ,
ATTR_PERMISSION_NONE, ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS, GATT_DONT_NOTIFY_EVENTS,
10, 10,
CHAR_VALUE_LEN_CONSTANT, CHAR_VALUE_LEN_CONSTANT,
&dev_info_svc.software_rev_char_handle); &dev_info_svc->software_rev_char_handle);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to add software revision char: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to add software revision char: %d", status);
} }
// Update characteristics // Update characteristics
status = aci_gatt_update_char_value(dev_info_svc.service_handle, status = aci_gatt_update_char_value(dev_info_svc->service_handle,
dev_info_svc.man_name_char_handle, dev_info_svc->man_name_char_handle,
0, 0,
strlen(DEV_INFO_MANUFACTURER_NAME), strlen(dev_info_man_name),
(uint8_t*)DEV_INFO_MANUFACTURER_NAME); (uint8_t*)dev_info_man_name);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to update manufacturer name char: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update manufacturer name char: %d", status);
} }
status = aci_gatt_update_char_value(dev_info_svc.service_handle, status = aci_gatt_update_char_value(dev_info_svc->service_handle,
dev_info_svc.serial_num_char_handle, dev_info_svc->serial_num_char_handle,
0, 0,
strlen(DEV_INFO_SERIAL_NUMBER), strlen(dev_info_serial_num),
(uint8_t*)DEV_INFO_SERIAL_NUMBER); (uint8_t*)dev_info_serial_num);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to update serial number char: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update serial number char: %d", status);
} }
status = aci_gatt_update_char_value(dev_info_svc.service_handle, status = aci_gatt_update_char_value(dev_info_svc->service_handle,
dev_info_svc.firmware_rev_char_handle, dev_info_svc->firmware_rev_char_handle,
0, 0,
strlen(DEV_INFO_FIRMWARE_REVISION_NUMBER), strlen(dev_info_firmware_rev_num),
(uint8_t*)DEV_INFO_FIRMWARE_REVISION_NUMBER); (uint8_t*)dev_info_firmware_rev_num);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to update firmware revision char: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update firmware revision char: %d", status);
} }
status = aci_gatt_update_char_value(dev_info_svc.service_handle, status = aci_gatt_update_char_value(dev_info_svc->service_handle,
dev_info_svc.software_rev_char_handle, dev_info_svc->software_rev_char_handle,
0, 0,
strlen(DEV_INFO_SOFTWARE_REVISION_NUMBER), strlen(dev_info_software_rev_num),
(uint8_t*)DEV_INFO_SOFTWARE_REVISION_NUMBER); (uint8_t*)dev_info_software_rev_num);
if(status) { if(status) {
FURI_LOG_E(DEV_INFO_SERVICE_TAG, "Failed to update software revision char: %d", status); FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to update software revision char: %d", status);
}
}
void dev_info_svc_stop() {
tBleStatus status;
if(dev_info_svc) {
// Delete service characteristics
status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->man_name_char_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete manufacturer name char: %d", status);
}
status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->serial_num_char_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete serial number char: %d", status);
}
status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->firmware_rev_char_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete firmware revision char: %d", status);
}
status = aci_gatt_del_char(dev_info_svc->service_handle, dev_info_svc->software_rev_char_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete software revision char: %d", status);
}
// Delete service
status = aci_gatt_del_service(dev_info_svc->service_handle);
if(status) {
FURI_LOG_E(DEV_INFO_SVC_TAG, "Failed to delete device info service: %d", status);
}
free(dev_info_svc);
dev_info_svc = NULL;
} }
return status != BLE_STATUS_SUCCESS;
} }

View File

@ -12,8 +12,9 @@ extern "C" {
#define DEV_INFO_FIRMWARE_REVISION_NUMBER TARGET #define DEV_INFO_FIRMWARE_REVISION_NUMBER TARGET
#define DEV_INFO_SOFTWARE_REVISION_NUMBER GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE #define DEV_INFO_SOFTWARE_REVISION_NUMBER GIT_COMMIT " " GIT_BRANCH " " GIT_BRANCH_NUM " " BUILD_DATE
void dev_info_svc_start();
bool dev_info_service_init(); void dev_info_svc_stop();
#ifdef __cplusplus #ifdef __cplusplus
} }

View 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);
}
}

View 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

View File

@ -6,13 +6,19 @@
#define SERIAL_SERVICE_TAG "serial service" #define SERIAL_SERVICE_TAG "serial service"
#define SERIAL_SVC_DATA_LEN_MAX 245
typedef struct { typedef struct {
uint16_t svc_handle; uint16_t svc_handle;
uint16_t rx_char_handle; uint16_t rx_char_handle;
uint16_t tx_char_handle; uint16_t tx_char_handle;
} SerialSvc; } SerialSvc;
static SerialSvc serial_svc; static SerialSvc* serial_svc;
static const uint8_t service_uuid[] = {0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f};
static const uint8_t char_rx_uuid[] = {0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
static const uint8_t char_tx_uuid[] = {0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19};
static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) { static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) {
SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck;
@ -22,76 +28,90 @@ static SVCCTL_EvtAckStatus_t serial_svc_event_handler(void *event) {
if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) {
if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) {
attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data; attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blecore_evt->data;
if(attribute_modified->Attr_Handle == serial_svc.tx_char_handle + 2) { if(attribute_modified->Attr_Handle == serial_svc->tx_char_handle + 2) {
// Descriptor handle // Descriptor handle
ret = SVCCTL_EvtAckFlowEnable; ret = SVCCTL_EvtAckFlowEnable;
FURI_LOG_D(SERIAL_SERVICE_TAG, "TX descriptor event"); FURI_LOG_D(SERIAL_SERVICE_TAG, "TX descriptor event");
} else if(attribute_modified->Attr_Handle == serial_svc.tx_char_handle + 1) { } else if(attribute_modified->Attr_Handle == serial_svc->tx_char_handle + 1) {
FURI_LOG_I(SERIAL_SERVICE_TAG, "Data len: %d", attribute_modified->Attr_Data_Length); FURI_LOG_D(SERIAL_SERVICE_TAG, "Received %d bytes", attribute_modified->Attr_Data_Length);
for(uint8_t i = 0; i < attribute_modified->Attr_Data_Length; i++) {
printf("%02X ", attribute_modified->Attr_Data[i]);
}
printf("\r\n");
serial_svc_update_rx(attribute_modified->Attr_Data, attribute_modified->Attr_Data_Length); serial_svc_update_rx(attribute_modified->Attr_Data, attribute_modified->Attr_Data_Length);
ret = SVCCTL_EvtAckFlowEnable; ret = SVCCTL_EvtAckFlowEnable;
} }
} else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) {
FURI_LOG_I(SERIAL_SERVICE_TAG, "Ack received", blecore_evt->ecode); FURI_LOG_D(SERIAL_SERVICE_TAG, "Ack received", blecore_evt->ecode);
ret = SVCCTL_EvtAckFlowEnable; ret = SVCCTL_EvtAckFlowEnable;
} }
} }
return ret; return ret;
} }
bool serial_svc_init() { void serial_svc_start() {
tBleStatus status; tBleStatus status;
const uint8_t service_uuid[] = {SERIAL_SVC_UUID_128}; serial_svc = furi_alloc(sizeof(SerialSvc));
const uint8_t char_rx_uuid[] = {SERIAL_CHAR_RX_UUID_128};
const uint8_t char_tx_uuid[] = {SERIAL_CHAR_TX_UUID_128};
// Register event handler // Register event handler
SVCCTL_RegisterSvcHandler(serial_svc_event_handler); SVCCTL_RegisterSvcHandler(serial_svc_event_handler);
// Add service // Add service
status = aci_gatt_add_service(UUID_TYPE_128, (Service_UUID_t *)service_uuid, PRIMARY_SERVICE, 6, &serial_svc.svc_handle); status = aci_gatt_add_service(UUID_TYPE_128, (Service_UUID_t *)service_uuid, PRIMARY_SERVICE, 6, &serial_svc->svc_handle);
if(status) { if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status); FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add Serial service: %d", status);
} }
// Add TX characteristics // Add TX characteristics
status = aci_gatt_add_char(serial_svc.svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_tx_uuid , status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_tx_uuid,
SERIAL_SVC_DATA_LEN_MAX, SERIAL_SVC_DATA_LEN_MAX,
CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ, CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE | CHAR_PROP_READ,
ATTR_PERMISSION_NONE, ATTR_PERMISSION_NONE,
GATT_NOTIFY_ATTRIBUTE_WRITE, GATT_NOTIFY_ATTRIBUTE_WRITE,
10, 10,
CHAR_VALUE_LEN_VARIABLE, CHAR_VALUE_LEN_VARIABLE,
&serial_svc.tx_char_handle); &serial_svc->tx_char_handle);
if(status) { if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add TX characteristic: %d", status); FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add TX characteristic: %d", status);
} }
// Add RX characteristic // Add RX characteristic
status = aci_gatt_add_char(serial_svc.svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_rx_uuid , status = aci_gatt_add_char(serial_svc->svc_handle, UUID_TYPE_128, (const Char_UUID_t*)char_rx_uuid,
SERIAL_SVC_DATA_LEN_MAX, SERIAL_SVC_DATA_LEN_MAX,
CHAR_PROP_READ | CHAR_PROP_INDICATE, CHAR_PROP_READ | CHAR_PROP_INDICATE,
ATTR_PERMISSION_NONE, ATTR_PERMISSION_NONE,
GATT_DONT_NOTIFY_EVENTS, GATT_DONT_NOTIFY_EVENTS,
10, 10,
CHAR_VALUE_LEN_VARIABLE, CHAR_VALUE_LEN_VARIABLE,
&serial_svc.rx_char_handle); &serial_svc->rx_char_handle);
if(status) { if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add RX characteristic: %d", status); FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to add RX characteristic: %d", status);
} }
return status != BLE_STATUS_SUCCESS;
} }
void serial_svc_stop() {
tBleStatus status;
if(serial_svc) {
// Delete characteristics
status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->tx_char_handle);
if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete TX characteristic: %d", status);
}
status = aci_gatt_del_char(serial_svc->svc_handle, serial_svc->rx_char_handle);
if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete RX characteristic: %d", status);
}
// Delete service
status = aci_gatt_del_service(serial_svc->svc_handle);
if(status) {
FURI_LOG_E(SERIAL_SERVICE_TAG, "Failed to delete Serial service: %d", status);
}
free(serial_svc);
serial_svc = NULL;
}
}
bool serial_svc_update_rx(uint8_t* data, uint8_t data_len) { bool serial_svc_update_rx(uint8_t* data, uint8_t data_len) {
furi_assert(data_len < SERIAL_SVC_DATA_LEN_MAX); furi_assert(data_len < SERIAL_SVC_DATA_LEN_MAX);
tBleStatus result = aci_gatt_update_char_value(serial_svc.svc_handle, tBleStatus result = aci_gatt_update_char_value(serial_svc->svc_handle,
serial_svc.rx_char_handle, serial_svc->rx_char_handle,
0, 0,
data_len, data_len,
data); data);

View File

@ -7,13 +7,9 @@
extern "C" { extern "C" {
#endif #endif
#define SERIAL_SVC_DATA_LEN_MAX 255 void serial_svc_start();
#define SERIAL_SVC_UUID_128 0x00, 0x00, 0xfe, 0x60, 0xcc, 0x7a, 0x48, 0x2a, 0x98, 0x4a, 0x7f, 0x2e, 0xd5, 0xb3, 0xe5, 0x8f void serial_svc_stop();
#define SERIAL_CHAR_RX_UUID_128 0x00, 0x00, 0xfe, 0x61, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19
#define SERIAL_CHAR_TX_UUID_128 0x00, 0x00, 0xfe, 0x62, 0x8e, 0x22, 0x45, 0x41, 0x9d, 0x4c, 0x21, 0xed, 0xae, 0x82, 0xed, 0x19
bool serial_svc_init();
bool serial_svc_update_rx(uint8_t* data, uint8_t data_len); bool serial_svc_update_rx(uint8_t* data, uint8_t data_len);

View File

@ -5,6 +5,7 @@
#include <shci.h> #include <shci.h>
#include <cmsis_os2.h> #include <cmsis_os2.h>
#include <app_ble.h> #include <app_ble.h>
#include <gap.h>
void furi_hal_bt_init() { void furi_hal_bt_init() {
// Explicitly tell that we are in charge of CLK48 domain // Explicitly tell that we are in charge of CLK48 domain
@ -14,7 +15,7 @@ void furi_hal_bt_init() {
} }
bool furi_hal_bt_start_app() { bool furi_hal_bt_start_app() {
return APP_BLE_Start(); return gap_init();
} }
void furi_hal_bt_dump_state(string_t buffer) { void furi_hal_bt_dump_state(string_t buffer) {