/* 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 "wsf_msg.h" #include "wsf_trace.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 "svc_ch.h" #include "svc_core.h" #include "svc_dis.h" #include "svc_batt.h" #include "svc_hid.h" #include "profiles/gap_api.h" #include "cccd.h" #include "ess.h" #include "hid.h" #include "uart.h" #include "ble_api.h" #include "epicardium.h" #include "modules/log.h" #include "modules/config.h" #define SCAN_REPORTS_NUM 16 static bool active; static uint8_t advertising_mode = APP_MODE_NONE; static uint8_t advertising_mode_target = APP_MODE_NONE; static struct epic_scan_report scan_reports[SCAN_REPORTS_NUM]; static int scan_reports_head; static int scan_reports_tail; /************************************************************************************************** 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 */ }; /*! 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 */ }; /*! Configurable parameters for service and characteristic discovery */ static const appDiscCfg_t bleDiscCfg = { FALSE /*! TRUE to wait for a secure connection before initiating discovery */ }; /* 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_LIMITED_DISC | /*! flags */ DM_FLAG_LE_BREDR_NOT_SUP, 3, DM_ADV_TYPE_APPEARANCE, UINT16_TO_BYTES(CH_APPEAR_WATCH), /*! service UUID list */ 17, DM_ADV_TYPE_128_UUID_PART, CARD10_UUID_SUFFIX, 0x0, CARD10_UUID_PREFIX, 2, /*! length */ DM_ADV_TYPE_TX_POWER, /*! AD type */ 0, /*! tx power */ }; /*! advertising data, discoverable mode with HID service*/ static const uint8_t bleAdvDataDiscHID[] = { /*! flags */ 2, /*! length */ DM_ADV_TYPE_FLAGS, /*! AD type */ DM_FLAG_LE_LIMITED_DISC | /*! flags */ DM_FLAG_LE_BREDR_NOT_SUP, 3, DM_ADV_TYPE_APPEARANCE, UINT16_TO_BYTES(CH_APPEAR_WATCH), /*! service UUID list */ 17, DM_ADV_TYPE_128_UUID_PART, CARD10_UUID_SUFFIX, 0x0, CARD10_UUID_PREFIX, 3, /*! length */ DM_ADV_TYPE_16_UUID_PART, /*! AD type */ UINT16_TO_BYTES(ATT_UUID_HID_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', 3, /*! length */ DM_ADV_TYPE_16_SOLICIT, /*! AD type */ UINT16_TO_BYTES(ATT_UUID_CURRENT_TIME_SERVICE), }; /*! advertising data, connectable mode */ static const uint8_t bleAdvDataConn[] = { /*! flags */ 2, /*! length */ DM_ADV_TYPE_FLAGS, /*! AD type */ DM_FLAG_LE_BREDR_NOT_SUP, }; static const appMasterCfg_t scannerMasterCfg = { 420, /*! The scan interval, in 0.625 ms units */ 420, /*! The scan window, in 0.625 ms units */ 0, /*! The scan duration in ms */ DM_DISC_MODE_NONE, /*! The GAP discovery mode */ DM_SCAN_TYPE_PASSIVE /*!< The scan type (active or passive) */ }; /************************************************************************************************** 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 */ {ESS_TEMP_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* BLE_ESS_TEMP_CCC_IDX */ {ESS_HUMI_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* BLE_ESS_HUMI_CCC_IDX */ {ESS_PRES_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* BLE_ESS_PRES_CCC_IDX */ {HID_MOUSE_BOOT_IN_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* HIDAPP_MBI_CCC_HDL */ {HID_KEYBOARD_BOOT_IN_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* HIDAPP_KBI_CCC_HDL */ {HID_INPUT_REPORT_1_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* HIDAPP_IN_KEYBOARD_CCC_HDL */ {HID_INPUT_REPORT_2_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* HIDAPP_IN_MOUSE_CCC_HDL */ {HID_INPUT_REPORT_3_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* HIDAPP_IN_CONSUMER_CCC_HDL */ {ESS_IAQ_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* BLE_ESS_IAQ_CCC_IDX */ {UART_TX_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* BLE_ESS_IAQ_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 appDbHdl_t last_pairing = NULL; 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); AppDbNvmStoreCccTbl(dbHdl); } 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 */ switch(pMsg->ccc.idx) { case BLE_ESS_TEMP_CCC_IDX: case BLE_ESS_HUMI_CCC_IDX: case BLE_ESS_PRES_CCC_IDX: case BLE_ESS_IAQ_CCC_IDX: bleESS_ccc_update(); break; }; } /*************************************************************************************************/ /*! * \brief Perform UI actions on connection close. * * \param pMsg Pointer to message. * * \return None. */ /*************************************************************************************************/ static void bleClose(bleMsg_t *pMsg) { /* stop battery measurement */ bleESS_ccc_update(); GapClearDeviceName(); } /*************************************************************************************************/ /*! * \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; int result = epic_config_get_string("ble_mac", buf, sizeof(buf)); if (result == 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 */ if(config_get_boolean_with_default("ble_hid_enable", false)) { AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(bleAdvDataDiscHID), (uint8_t *) bleAdvDataDiscHID); } else { 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, sizeof(bleAdvDataConn), (uint8_t *) bleAdvDataConn); AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, 0, NULL); active = true; /* TODO: Sadly, not advertising leads to a higher current consumption... */ epic_ble_set_mode(false, false); } void epic_ble_set_mode(bool bondable, bool scanner) { if(!active) { return; } if(scanner && bondable) { /* TODO: return error */ return; } if(scanner) { if(advertising_mode != APP_MODE_NONE) { advertising_mode_target = APP_MODE_NONE; advertising_mode = APP_MODE_NONE; AppAdvStop(); } dmConnId_t connId; if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE) { AppConnClose(connId); } /* Normal scanning filters out duplicates. We don't * want that for now... */ //AppScanStart(scannerMasterCfg.discMode, scannerMasterCfg.scanType, scannerMasterCfg.scanDuration); DmScanSetInterval(HCI_SCAN_PHY_LE_1M_BIT, &pAppMasterCfg->scanInterval, &pAppMasterCfg->scanWindow); DmScanStart(HCI_SCAN_PHY_LE_1M_BIT, scannerMasterCfg.discMode, &scannerMasterCfg.scanType, FALSE, scannerMasterCfg.scanDuration, 0); return; } else { AppScanStop(); } 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; } int epic_ble_get_peer_device_name(char *buf, size_t buf_size) { if (AppConnIsOpen() != DM_CONN_ID_NONE) { return GapGetDeviceName(buf, buf_size); } else { return -ENOENT; } } int epic_ble_get_last_pairing_name(char *buf, size_t buf_size) { if(last_pairing == NULL) { return -ENOENT; } return AppDbGetPairingName(last_pairing, buf, buf_size); } 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 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); ble_epic_ble_api_trigger_event(BLE_EVENT_HANDLE_NUMERIC_COMPARISON, NULL); } int epic_ble_get_scan_report(struct epic_scan_report *rpt) { if(scan_reports_head == scan_reports_tail) { return -ENOENT; } int new_tail = (scan_reports_tail + 1) % SCAN_REPORTS_NUM; *rpt = scan_reports[new_tail]; scan_reports_tail = new_tail; return 0; } static void scannerScanReport(dmEvt_t *pMsg) { struct epic_scan_report *scan_report; int next_head = (scan_reports_head + 1) % SCAN_REPORTS_NUM; if(next_head == scan_reports_tail) { ble_epic_ble_api_trigger_event(BLE_EVENT_SCAN_REPORT, NULL); return; } scan_reports_head = next_head; scan_report = &scan_reports[scan_reports_head]; memset(scan_report->data, 0, sizeof(scan_report->data)); memccpy(scan_report->data, pMsg->scanReport.pData, pMsg->scanReport.len, sizeof(scan_report->data)); scan_report->len = pMsg->scanReport.len; scan_report->rssi = pMsg->scanReport.rssi; scan_report->eventType = pMsg->scanReport.eventType; scan_report->addrType = pMsg->scanReport.addrType; memcpy(scan_report->addr, pMsg->scanReport.addr, BDA_ADDR_LEN); scan_report->directAddrType = pMsg->scanReport.directAddrType; memcpy(scan_report->directAddr, pMsg->scanReport.directAddr, BDA_ADDR_LEN); ble_epic_ble_api_trigger_event(BLE_EVENT_SCAN_REPORT, NULL); if((scan_reports_head + 1) % SCAN_REPORTS_NUM == scan_reports_tail) { LOG_WARN("ble", "Application missing scan results"); } } /*************************************************************************************************/ /*! * \brief Process messages from the event handler. * * \param pMsg Pointer to message. * * \return None. */ /*************************************************************************************************/ #define ATT_CONNECTION_OPENED 0x81 #define ATT_CONNECTION_CLOSED 0x82 static void bleProcMsg(bleMsg_t *pMsg) { hciLeConnCmplEvt_t *connOpen; switch(pMsg->hdr.event) { case ATTC_READ_RSP: case ATTC_HANDLE_VALUE_IND: bleValueUpdate((attEvt_t *) pMsg); break; case ATTS_HANDLE_VALUE_CNF: HidProcMsg(&pMsg->hdr); UartProcMsg(pMsg); 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 %u %u", advertising_mode, advertising_mode_target); if(advertising_mode != advertising_mode_target || advertising_mode_target == APP_MODE_NONE) { AppAdvStop(); } 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]); bleESS_ccc_update(); HidProcMsg(&pMsg->hdr); UartProcMsg(pMsg); 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; } /* Stack overwrites advertising mode after connection close. * Force our desired mode. */ advertising_mode = APP_MODE_NONE; AppAdvStop(); bleClose(pMsg); break; case DM_SEC_PAIR_CMPL_IND: LOG_INFO("ble", "Secure pairing successful, auth: 0x%02X", pMsg->dm.pairCmpl.auth); DmSecGenerateEccKeyReq(); last_pairing = AppDbGetHdl((dmConnId_t) pMsg->hdr.param); AppDbNvmStoreBond(last_pairing); pair_connId = DM_CONN_ID_NONE; ble_epic_ble_api_trigger_event(BLE_EVENT_PAIRING_COMPLETE, NULL); /* 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; } DmSecGenerateEccKeyReq(); pair_connId = DM_CONN_ID_NONE; ble_epic_ble_api_trigger_event(BLE_EVENT_PAIRING_FAILED, NULL); 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_SCAN_REPORT_IND: scannerScanReport((dmEvt_t *)pMsg); 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; pAppDiscCfg = (appDiscCfg_t *) &bleDiscCfg; pAppMasterCfg = (appMasterCfg_t *) &scannerMasterCfg; /* Initialize application framework */ AppSlaveInit(); AppDiscInit(); /* Set stack configuration pointers */ pSmpCfg = (smpCfg_t *) &bleSmpCfg; pAttCfg = (attCfg_t *) &bleAttCfg; } /*************************************************************************************************/ /*! * \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) { if(pMsg->event != DM_SCAN_REPORT_IND) 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); /* process discovery-related messages */ AppDiscProcDmMsg((dmEvt_t *) pMsg); ble_epic_dm_api_event((dmEvt_t *)pMsg); } else if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END) { //if(!(pMsg->event == ATTS_HANDLE_VALUE_CNF && pMsg->status == ATT_SUCCESS) ) LOG_INFO("ble", "Ble got evt %d (%s): %d", pMsg->event, att_events[pMsg->event - ATT_CBACK_START], pMsg->status); /* process discovery-related ATT messages */ AppDiscProcAttMsg((attEvt_t *) pMsg); ble_epic_att_api_event((attEvt_t *)pMsg); } 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); /* Register for app framework discovery callbacks */ AppDiscRegister(bleDiscCback); /* Initialize attribute server database */ SvcCoreAddGroup(); SvcDisAddGroup(); // Device Information Service if(config_get_boolean_with_default("ble_hid_enable", false)) { hid_init(); } ble_epic_ble_api_init(); /* Reset the device */ DmDevReset(); } /* clang-format on */