/* card10: * Copied from lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/apps/fit/fit_main.c * * Also have a look at lib/sdk/Applications/EvKitExamples/BLE_fit/fit_main.c which has some changes * to this file regarding handling of OOB paring data * * This file contains some application logic taken from the "fit" example. * * Things have been renamed: * fit -> ble * Fit -> Ble * FIT -> BLE */ /* clang-format off */ /* clang-formet turned off for easier diffing against orginal file */ #include <stdio.h> #include <string.h> #include "wsf_types.h" #include "util/bstream.h" #include "fs/fs_util.h" #include "wsf_msg.h" #include "wsf_trace.h" #include "hci_api.h" #include "l2c_api.h" #include "dm_api.h" #include "att_api.h" #include "smp_api.h" #include "app_api.h" #include "app_db.h" #include "app_ui.h" #include "app_hw.h" #include "svc_ch.h" #include "svc_core.h" #include "svc_hrs.h" #include "svc_dis.h" #include "svc_batt.h" #include "svc_rscs.h" #include "bas/bas_api.h" #include "hrps/hrps_api.h" #include "rscp/rscp_api.h" #include "cccd.h" #include "epicardium.h" #include "api/interrupt-sender.h" #include "modules/log.h" static bool active; static uint8_t advertising_mode = APP_MODE_NONE; static uint8_t advertising_mode_target = APP_MODE_NONE; static enum ble_event_type ble_event; /************************************************************************************************** Macros **************************************************************************************************/ /*! WSF message event starting value */ #define BLE_MSG_START 0xA0 /*! WSF message event enumeration */ enum { BLE_BATT_TIMER_IND = BLE_MSG_START, /*! Battery measurement timer expired */ }; /************************************************************************************************** Data Types **************************************************************************************************/ /*! Application message type */ typedef union { wsfMsgHdr_t hdr; dmEvt_t dm; attsCccEvt_t ccc; attEvt_t att; } bleMsg_t; /************************************************************************************************** Configurable Parameters **************************************************************************************************/ /*! configurable parameters for advertising */ static const appAdvCfg_t bleAdvCfg = { {0, 0}, /*! Advertising durations in ms */ {500/0.625, 0} /*! Advertising intervals in 0.625 ms units */ }; /*! configurable parameters for slave */ static const appSlaveCfg_t bleSlaveCfg = { 1, /*! Maximum connections */ }; /*! configurable parameters for security */ static const appSecCfg_t bleSecCfg = { .auth = DM_AUTH_MITM_FLAG | DM_AUTH_BOND_FLAG | DM_AUTH_SC_FLAG, /*! Authentication and bonding flags */ .iKeyDist = 0, /*! Initiator key distribution flags */ .rKeyDist = DM_KEY_DIST_LTK, /*! Responder key distribution flags */ .oob=FALSE, /*! TRUE if Out-of-band pairing data is present */ .initiateSec = TRUE /*! TRUE to initiate security upon connection */ }; /*! configurable parameters for connection parameter update */ static const appUpdateCfg_t bleUpdateCfg = { 6000, /*! Connection idle period in ms before attempting connection parameter update; set to zero to disable */ 30/1.25, /*! Minimum connection interval in 1.25ms units. Values < 8 didn't work with my Tinkpad T470 */ 40/1.25, /*! Maximum connection interval in 1.25ms units */ 0, /*! Connection latency */ 9000/10, /*! Supervision timeout in 10ms units */ 5 /*! Number of update attempts before giving up */ }; /*! battery measurement configuration */ static const basCfg_t bleBasCfg = { 30, /*! Battery measurement timer expiration period in seconds */ 1, /*! Perform battery measurement after this many timer periods */ 100 /*! Send battery level notification to peer when below this level. */ }; /*! SMP security parameter configuration */ static const smpCfg_t bleSmpCfg = { .attemptTimeout = 3000, /*! 'Repeated attempts' timeout in msec */ .ioCap = SMP_IO_DISP_YES_NO, /*! I/O Capability */ .minKeyLen = 16, /*! Minimum encryption key length */ .maxKeyLen = 16, /*! Maximum encryption key length */ .maxAttempts = 3, /*! Attempts to trigger 'repeated attempts' timeout */ .auth = DM_AUTH_MITM_FLAG | DM_AUTH_SC_FLAG, /*! Device authentication requirements */ .maxAttemptTimeout = 64000, /*! Maximum 'Repeated attempts' timeout in msec */ .attemptDecTimeout = 64000, /*! Time msec before attemptExp decreases */ .attemptExp = 2, /*! Exponent to raise attemptTimeout on maxAttempts */ }; /* Configuration structure */ static const attCfg_t bleAttCfg = { 15, /* ATT server service discovery connection idle timeout in seconds */ 241, /* desired ATT MTU */ ATT_MAX_TRANS_TIMEOUT, /* transaction timeout in seconds */ 1 /* number of queued prepare writes supported by server */ }; /************************************************************************************************** Advertising Data **************************************************************************************************/ /*! advertising data, discoverable mode */ static const uint8_t bleAdvDataDisc[] = { /*! flags */ 2, /*! length */ DM_ADV_TYPE_FLAGS, /*! AD type */ DM_FLAG_LE_GENERAL_DISC | /*! flags */ DM_FLAG_LE_BREDR_NOT_SUP, /*! tx power */ 2, /*! length */ DM_ADV_TYPE_TX_POWER, /*! AD type */ 0, /*! tx power */ /*! service UUID list */ 5, /*! length */ DM_ADV_TYPE_16_UUID, /*! AD type */ UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE), UINT16_TO_BYTES(ATT_UUID_BATTERY_SERVICE) }; /*! scan data, discoverable mode */ uint8_t bleScanDataDisc[] = { /*! device name */ 14, /*! length */ DM_ADV_TYPE_LOCAL_NAME, /*! AD type */ 'c','a','r','d','1','0','-','0','0','0','0','0','0' }; /************************************************************************************************** Client Characteristic Configuration Descriptors **************************************************************************************************/ /*! client characteristic configuration descriptors settings, indexed by above enumeration */ static const attsCccSet_t bleCccSet[BLE_NUM_CCC_IDX] = { /* cccd handle value range security level */ {GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE}, /* BLE_GATT_SC_CCC_IDX */ {BATT_LVL_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* BLE_BATT_LVL_CCC_IDX */ }; /************************************************************************************************** Global Variables **************************************************************************************************/ /*! WSF handler ID */ wsfHandlerId_t bleHandlerId; static dmConnId_t pair_connId = DM_CONN_ID_NONE; static uint32_t pair_confirm_value; static void BleHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg); static const char * const att_events[] = { "ATTC_FIND_INFO_RSP", "ATTC_FIND_BY_TYPE_VALUE_RSP", "ATTC_READ_BY_TYPE_RSP", "ATTC_READ_RSP", "ATTC_READ_LONG_RSP", "ATTC_READ_MULTIPLE_RSP", "ATTC_READ_BY_GROUP_TYPE_RSP", "ATTC_WRITE_RSP", "ATTC_WRITE_CMD_RSP", "ATTC_PREPARE_WRITE_RSP", "ATTC_EXECUTE_WRITE_RSP", "ATTC_HANDLE_VALUE_NTF", "ATTC_HANDLE_VALUE_IND", /* ATT server callback events */ "ATTS_HANDLE_VALUE_CNF", "ATTS_CCC_STATE_IND", "ATTS_DB_HASH_CALC_CMPL_IND", /* ATT common callback events */ "ATT_MTU_UPDATE_IND" }; static const char * const dm_events[] = { "DM_RESET_CMPL_IND", "DM_ADV_START_IND", "DM_ADV_STOP_IND", "DM_ADV_NEW_ADDR_IND", "DM_SCAN_START_IND", "DM_SCAN_STOP_IND", "DM_SCAN_REPORT_IND", "DM_CONN_OPEN_IND", "DM_CONN_CLOSE_IND", "DM_CONN_UPDATE_IND", "DM_SEC_PAIR_CMPL_IND", "DM_SEC_PAIR_FAIL_IND", "DM_SEC_ENCRYPT_IND", "DM_SEC_ENCRYPT_FAIL_IND", "DM_SEC_AUTH_REQ_IND", "DM_SEC_KEY_IND", "DM_SEC_LTK_REQ_IND", "DM_SEC_PAIR_IND", "DM_SEC_SLAVE_REQ_IND", "DM_SEC_CALC_OOB_IND", "DM_SEC_ECC_KEY_IND", "DM_SEC_COMPARE_IND", "DM_SEC_KEYPRESS_IND", "DM_PRIV_RESOLVED_ADDR_IND", "DM_PRIV_GENERATE_ADDR_IND", "DM_CONN_READ_RSSI_IND", "DM_PRIV_ADD_DEV_TO_RES_LIST_IND", "DM_PRIV_REM_DEV_FROM_RES_LIST_IND", "DM_PRIV_CLEAR_RES_LIST_IND", "DM_PRIV_READ_PEER_RES_ADDR_IND", "DM_PRIV_READ_LOCAL_RES_ADDR_IND", "DM_PRIV_SET_ADDR_RES_ENABLE_IND", "DM_REM_CONN_PARAM_REQ_IND", "DM_CONN_DATA_LEN_CHANGE_IND", "DM_CONN_WRITE_AUTH_TO_IND", "DM_CONN_AUTH_TO_EXPIRED_IND", "DM_PHY_READ_IND", "DM_PHY_SET_DEF_IND", "DM_PHY_UPDATE_IND", "DM_ADV_SET_START_IND", "DM_ADV_SET_STOP_IND", "DM_SCAN_REQ_RCVD_IND", "DM_EXT_SCAN_START_IND", "DM_EXT_SCAN_STOP_IND", "DM_EXT_SCAN_REPORT_IND", "DM_PER_ADV_SET_START_IND", "DM_PER_ADV_SET_STOP_IND", "DM_PER_ADV_SYNC_EST_IND", "DM_PER_ADV_SYNC_EST_FAIL_IND", "DM_PER_ADV_SYNC_LOST_IND", "DM_PER_ADV_SYNC_TRSF_EST_IND", "DM_PER_ADV_SYNC_TRSF_EST_FAIL_IND", "DM_PER_ADV_SYNC_TRSF_IND", "DM_PER_ADV_SET_INFO_TRSF_IND", "DM_PER_ADV_REPORT_IND", "DM_REMOTE_FEATURES_IND", "DM_READ_REMOTE_VER_INFO_IND", "DM_CONN_IQ_REPORT_IND", "DM_CTE_REQ_FAIL_IND", "DM_CONN_CTE_RX_SAMPLE_START_IND", "DM_CONN_CTE_RX_SAMPLE_STOP_IND", "DM_CONN_CTE_TX_CFG_IND", "DM_CONN_CTE_REQ_START_IND", "DM_CONN_CTE_REQ_STOP_IND", "DM_CONN_CTE_RSP_START_IND", "DM_CONN_CTE_RSP_STOP_IND", "DM_READ_ANTENNA_INFO_IND", "DM_L2C_CMD_REJ_IND", "DM_ERROR_IND", "DM_HW_ERROR_IND", "DM_VENDOR_SPEC_IND" }; static const char * const l2c_coc_events[] = { "L2C_COC_CONNECT_IND", "L2C_COC_DISCONNECT_IND", "L2C_COC_DATA_IND", "L2C_COC_DATA_CNF" }; /*************************************************************************************************/ /*! * \brief Application DM callback. * * \param pDmEvt DM callback event * * \return None. */ /*************************************************************************************************/ static void bleDmCback(dmEvt_t *pDmEvt) { dmEvt_t *pMsg; uint16_t len; len = DmSizeOfEvt(pDmEvt); if ((pMsg = WsfMsgAlloc(len)) != NULL) { memcpy(pMsg, pDmEvt, len); WsfMsgSend(bleHandlerId, pMsg); } } /*************************************************************************************************/ /*! * \brief Application ATT callback. * * \param pEvt ATT callback event * * \return None. */ /*************************************************************************************************/ static void bleAttCback(attEvt_t *pEvt) { attEvt_t *pMsg; if ((pMsg = WsfMsgAlloc(sizeof(attEvt_t) + pEvt->valueLen)) != NULL) { memcpy(pMsg, pEvt, sizeof(attEvt_t)); pMsg->pValue = (uint8_t *) (pMsg + 1); memcpy(pMsg->pValue, pEvt->pValue, pEvt->valueLen); WsfMsgSend(bleHandlerId, pMsg); } } /*************************************************************************************************/ /*! * \brief Application ATTS client characteristic configuration callback. * * \param pDmEvt DM callback event * * \return None. */ /*************************************************************************************************/ static void bleCccCback(attsCccEvt_t *pEvt) { attsCccEvt_t *pMsg; appDbHdl_t dbHdl; /* if CCC not set from initialization and there's a device record */ if ((pEvt->handle != ATT_HANDLE_NONE) && ((dbHdl = AppDbGetHdl((dmConnId_t) pEvt->hdr.param)) != APP_DB_HDL_NONE)) { /* store value in device database */ AppDbSetCccTblValue(dbHdl, pEvt->idx, pEvt->value); } if ((pMsg = WsfMsgAlloc(sizeof(attsCccEvt_t))) != NULL) { memcpy(pMsg, pEvt, sizeof(attsCccEvt_t)); WsfMsgSend(bleHandlerId, pMsg); } } /*************************************************************************************************/ /*! * \brief Process CCC state change. * * \param pMsg Pointer to message. * * \return None. */ /*************************************************************************************************/ static void bleProcCccState(bleMsg_t *pMsg) { APP_TRACE_INFO3("ccc state ind value:%d handle:%d idx:%d", pMsg->ccc.value, pMsg->ccc.handle, pMsg->ccc.idx); /* handle battery level CCC */ if (pMsg->ccc.idx == BLE_BATT_LVL_CCC_IDX) { if (pMsg->ccc.value == ATT_CLIENT_CFG_NOTIFY) { BasMeasBattStart((dmConnId_t) pMsg->ccc.hdr.param, BLE_BATT_TIMER_IND, BLE_BATT_LVL_CCC_IDX); } else { BasMeasBattStop((dmConnId_t) pMsg->ccc.hdr.param); } return; } } /*************************************************************************************************/ /*! * \brief Perform UI actions on connection close. * * \param pMsg Pointer to message. * * \return None. */ /*************************************************************************************************/ static void bleClose(bleMsg_t *pMsg) { /* stop battery measurement */ BasMeasBattStop((dmConnId_t) pMsg->hdr.param); } /*************************************************************************************************/ /*! * \brief Set up advertising and other procedures that need to be performed after * device reset. * * \param pMsg Pointer to message. * * \return None. */ /*************************************************************************************************/ static void bleSetup(bleMsg_t *pMsg) { char buf[32]; char a, b, c, d, e, f, K; if (fs_read_text_file("mac.txt", buf, sizeof(buf)) > 0) { if (sscanf(buf, "%c%c:%c%c:%c%c:%c%c:%c%c:%c%c", &K,&K,&K,&K,&K,&K, &a, &b, &c, &d, &e, &f) == 12) { bleScanDataDisc[9] = a; bleScanDataDisc[10] = b; bleScanDataDisc[11] = c; bleScanDataDisc[12] = d; bleScanDataDisc[13] = e; bleScanDataDisc[14] = f; } } /* set advertising and scan response data for discoverable mode */ AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(bleAdvDataDisc), (uint8_t *) bleAdvDataDisc); AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(bleScanDataDisc), (uint8_t *) bleScanDataDisc); /* set advertising and scan response data for connectable mode */ AppAdvSetData(APP_ADV_DATA_CONNECTABLE, 0, NULL); AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, 0, NULL); active = true; /* TODO: Sadly, not advertising leads to a higher current consumption... */ epic_ble_set_bondable(false); } void epic_ble_set_bondable(bool bondable) { if(!active) { return; } if(bondable) { /* We need to stop advertising in between or the * adv set will not be changed. * Also need to wait for the stop operation to finish * before we can start again * Also need to set the variables first as we don't * have a lock on the stack.*/ AppSetBondable(TRUE); if(advertising_mode != APP_MODE_DISCOVERABLE) { LOG_INFO("ble", "Making bondable and discoverable"); if(advertising_mode != APP_MODE_NONE) { advertising_mode_target = APP_MODE_DISCOVERABLE; advertising_mode = APP_MODE_NONE; AppAdvStop(); } else { advertising_mode = APP_MODE_DISCOVERABLE; advertising_mode_target = APP_MODE_DISCOVERABLE; AppAdvStart(advertising_mode); } } } else { AppSetBondable(FALSE); if(AppDbCheckBonded()) { if(advertising_mode != APP_MODE_CONNECTABLE) { LOG_INFO("ble", "Bonded. Making connectable"); if(advertising_mode != APP_MODE_NONE) { advertising_mode_target = APP_MODE_CONNECTABLE; advertising_mode = APP_MODE_NONE; AppAdvStop(); } else { advertising_mode = APP_MODE_CONNECTABLE; advertising_mode_target = APP_MODE_CONNECTABLE; AppAdvStart(advertising_mode); } } } else { if(advertising_mode != APP_MODE_NONE) { LOG_INFO("ble", "Not bonded. Stop advertising"); advertising_mode = APP_MODE_NONE; advertising_mode_target = APP_MODE_NONE; AppAdvStop(); } } } } uint32_t epic_ble_get_compare_value(void) { return pair_confirm_value; } void epic_ble_compare_response(bool confirmed) { if(!active) { return; } if(pair_connId != DM_CONN_ID_NONE) { LOG_INFO("ble", "Value confirmed: %u", confirmed); DmSecCompareRsp(pair_connId, confirmed); } else { /* error condition */ } } static void trigger_event(enum ble_event_type event) { bool enabled; epic_interrupt_is_enabled(EPIC_INT_BLE, &enabled); if(ble_event && enabled) { LOG_WARN("ble", "Application missed event %u", ble_event); } ble_event = event; api_interrupt_trigger(EPIC_INT_BLE); } enum ble_event_type epic_ble_get_event(void) { enum ble_event_type event = ble_event; ble_event = 0; return event; } static void bleHandleNumericComparison(dmSecCnfIndEvt_t *pCnfInd) { if(!active) { return; } pair_connId = (dmConnId_t)pCnfInd->hdr.param; pair_confirm_value = DmSecGetCompareValue(pCnfInd->confirm); LOG_INFO("ble", "Confirm Value: %ld", pair_confirm_value); trigger_event(BLE_EVENT_HANDLE_NUMERIC_COMPARISON); } /*************************************************************************************************/ /*! * \brief Process messages from the event handler. * * \param pMsg Pointer to message. * * \return None. */ /*************************************************************************************************/ static void bleProcMsg(bleMsg_t *pMsg) { hciLeConnCmplEvt_t *connOpen; switch(pMsg->hdr.event) { case BLE_BATT_TIMER_IND: BasProcMsg(&pMsg->hdr); break; case ATTS_HANDLE_VALUE_CNF: BasProcMsg(&pMsg->hdr); break; case ATTS_CCC_STATE_IND: bleProcCccState(pMsg); break; case DM_RESET_CMPL_IND: DmSecGenerateEccKeyReq(); bleSetup(pMsg); break; case DM_ADV_START_IND: LOG_INFO("ble", "Advertisement started"); break; case DM_ADV_STOP_IND: LOG_INFO("ble", "Advertisement stopped %u %u", advertising_mode, advertising_mode_target); if(advertising_mode != advertising_mode_target) { advertising_mode = advertising_mode_target; AppAdvStart(advertising_mode); } break; case DM_CONN_OPEN_IND: connOpen = &pMsg->dm.connOpen; LOG_INFO("ble", "connection from %02X:%02X:%02X:%02X:%02X:%02X opened", connOpen->peerAddr[5], connOpen->peerAddr[4], connOpen->peerAddr[3], connOpen->peerAddr[2], connOpen->peerAddr[1], connOpen->peerAddr[0]); BasProcMsg(&pMsg->hdr); break; case DM_CONN_CLOSE_IND: switch (pMsg->dm.connClose.reason) { case HCI_ERR_CONN_TIMEOUT: LOG_INFO("ble", "Connection closed (0x%02X), Connection timeout", pMsg->dm.connClose.reason); break; case HCI_ERR_LOCAL_TERMINATED: LOG_INFO("ble", "Connection closed (0x%02X), Connection terminated by local host", pMsg->dm.connClose.reason); break; case HCI_ERR_REMOTE_TERMINATED: LOG_INFO("ble", "Connection closed (0x%02X), Remote user terminated connection", pMsg->dm.connClose.reason); break; case HCI_ERR_CONN_FAIL: LOG_INFO("ble", "Connection closed (0x%02X), Connection failed to be established", pMsg->dm.connClose.reason); break; case HCI_ERR_MIC_FAILURE: LOG_INFO("ble", "Connection closed (0x%02X), Connection terminated due to MIC failure", pMsg->dm.connClose.reason); break; default: LOG_INFO("ble", "Connection closed (0x%02X)", pMsg->dm.connClose.reason); break; } bleClose(pMsg); break; case DM_SEC_PAIR_CMPL_IND: LOG_INFO("ble", "Secure pairing successful, auth: 0x%02X", pMsg->dm.pairCmpl.auth); pair_connId = DM_CONN_ID_NONE; trigger_event(BLE_EVENT_PAIRING_COMPLETE); /* After a successful pairing, bonding is disabled again. * We don't want that for now. */ AppSetBondable(TRUE); break; case DM_SEC_PAIR_FAIL_IND: switch (pMsg->hdr.status) { case SMP_ERR_TIMEOUT: LOG_INFO("ble", "Secure pairing failed (0x%02X), Transaction timeout", pMsg->hdr.status); break; case SMP_ERR_ATTEMPTS: LOG_INFO("ble", "Secure pairing failed (0x%02X), Repeated attempts", pMsg->hdr.status); break; default: LOG_INFO("ble", "Secure pairing failed (0x%02X)", pMsg->hdr.status); break; } pair_connId = DM_CONN_ID_NONE; trigger_event(BLE_EVENT_PAIRING_FAILED); break; case DM_SEC_ENCRYPT_IND: LOG_INFO("ble", "Encrypted handshake successful"); break; case DM_SEC_ENCRYPT_FAIL_IND: LOG_INFO("ble", "Encrypted handshake failed"); break; case DM_SEC_AUTH_REQ_IND: AppHandlePasskey(&pMsg->dm.authReq); break; case DM_SEC_ECC_KEY_IND: DmSecSetEccKey(&pMsg->dm.eccMsg.data.key); break; case DM_SEC_COMPARE_IND: bleHandleNumericComparison(&pMsg->dm.cnfInd); break; case DM_HW_ERROR_IND: LOG_ERR("ble", "HW Error"); break; default: break; } } /*************************************************************************************************/ /*! * \brief Application handler init function called during system initialization. * * \param handlerID WSF handler ID. * * \return None. */ /*************************************************************************************************/ static void BleHandlerInit(void) { APP_TRACE_INFO0("BleHandlerInit"); /* store handler ID */ bleHandlerId =WsfOsSetNextHandler(BleHandler); /* Set configuration pointers */ pAppAdvCfg = (appAdvCfg_t *) &bleAdvCfg; pAppSlaveCfg = (appSlaveCfg_t *) &bleSlaveCfg; pAppSecCfg = (appSecCfg_t *) &bleSecCfg; pAppUpdateCfg = (appUpdateCfg_t *) &bleUpdateCfg; /* Initialize application framework */ AppSlaveInit(); /* Set stack configuration pointers */ pSmpCfg = (smpCfg_t *) &bleSmpCfg; pAttCfg = (attCfg_t *) &bleAttCfg; /* initialize battery service server */ BasInit(bleHandlerId, (basCfg_t *) &bleBasCfg); } /*************************************************************************************************/ /*! * \brief WSF event handler for application. * * \param event WSF event mask. * \param pMsg WSF message. * * \return None. */ /*************************************************************************************************/ static void BleHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg) { if (pMsg != NULL) { APP_TRACE_INFO1("Ble got evt %d", pMsg->event); if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END) { LOG_INFO("ble", "Ble got evt %d: %s", pMsg->event, dm_events[pMsg->event - DM_CBACK_START]); /* process advertising and connection-related messages */ AppSlaveProcDmMsg((dmEvt_t *) pMsg); /* process security-related messages */ AppSlaveSecProcDmMsg((dmEvt_t *) pMsg); } else if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END) { LOG_INFO("ble", "Ble got evt %d: %s", pMsg->event, att_events[pMsg->event - ATT_CBACK_START]); } else if (pMsg->event >= L2C_COC_CBACK_START && pMsg->event <= L2C_COC_CBACK_CBACK_END) { LOG_INFO("ble", "Ble got evt %d: %s", pMsg->event, l2c_coc_events[pMsg->event - L2C_COC_CBACK_START]); } else { LOG_INFO("ble", "Ble got evt %d", pMsg->event); } /* perform profile and user interface-related operations */ bleProcMsg((bleMsg_t *) pMsg); } } /*************************************************************************************************/ /*! * \brief Start the application. * * \return None. */ /*************************************************************************************************/ void BleStart(void) { BleHandlerInit(); /* Register for stack callbacks */ DmRegister(bleDmCback); DmConnRegister(DM_CLIENT_ID_APP, bleDmCback); AttRegister(bleAttCback); AttConnRegister(AppServerConnCback); AttsCccRegister(BLE_NUM_CCC_IDX, (attsCccSet_t *) bleCccSet, bleCccCback); /* Initialize attribute server database */ SvcCoreAddGroup(); SvcDisAddGroup(); // Device Information Service SvcBattCbackRegister(BasReadCback, NULL); SvcBattAddGroup(); /* Reset the device */ DmDevReset(); } /* clang-format on */