// **************************************************************************** // // beaconscanner_main.c //! @file //! //! @brief Ambiq Micro's demonstration of Beacon Scanner. //! //! @{ // // **************************************************************************** //***************************************************************************** // // Copyright (c) 2020, Ambiq Micro // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. Neither the name of the copyright holder nor the names of its // contributors may be used to endorse or promote products derived from this // software without specific prior written permission. // // Third party software included in this distribution is subject to the // additional license terms as defined in the /docs/licenses directory. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // This is part of revision 2.4.2 of the AmbiqSuite Development Package. // //***************************************************************************** #include #include "wsf_types.h" #include "bstream.h" #include "wsf_msg.h" #include "wsf_trace.h" #include "wsf_assert.h" #include "hci_api.h" #include "dm_api.h" #include "att_api.h" #include "smp_api.h" #include "app_cfg.h" #include "app_api.h" #include "app_main.h" #include "app_db.h" #include "app_ui.h" #include "svc_core.h" #include "svc_px.h" #include "svc_ch.h" #include "gatt_api.h" #include "gap_api.h" #include "fmpl_api.h" /************************************************************************************************** Macros **************************************************************************************************/ /*! WSF message event starting value */ #define BEACONSCANNER_MSG_START 0xA0 /*! WSF message event enumeration */ enum { BEACONSCANNER_SCAN_TIMER_IND = BEACONSCANNER_MSG_START, /*! Scan timer expired */ }; /*! Base UUID: B10D0000-0000-0000-BD59-D38B413F017D */ #define IBEACON_TENDEGREE_UUID128 0xB1, 0x0D, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, \ 0xBD, 0x59, 0xD3, 0x8B, \ 0x41, 0x3F, 0x01, 0x7D const uint8_t TenDegree_ibeacon_uuid128 [] = {IBEACON_TENDEGREE_UUID128}; // offset of 16 BYTE UUID within manufacturer data #define IBEACON_UUID128_OFFSET 6 // offset of 2 BYTE Major within manufacturer data #define IBEACON_UUID128_MAJOR (IBEACON_UUID128_OFFSET + 16) // offset of 2 BYTE Minor within manufacturer data #define IBEACON_UUID128_MINOR (IBEACON_UUID128_MAJOR + 2) /************************************************************************************************** Local Variables **************************************************************************************************/ /*! proximity reporter control block */ static struct { uint16_t hdlList[APP_DB_HDL_LIST_LEN]; wsfHandlerId_t handlerId; uint8_t discState; bdAddr_t peerAddr; /* Peer address */ uint8_t addrType; /* Peer address type */ wsfTimer_t scanTimer; /* timer for scan after connection is up */ appDbHdl_t dbHdl; /*! Device database record handle type */ } beaconScannerCb; /************************************************************************************************** Configurable Parameters **************************************************************************************************/ /*! configurable parameters for advertising */ static const appAdvCfg_t beaconScannerAdvCfg = { {5000, 0, 0}, /*! Advertising durations in ms */ { 56, 0, 0} /*! Advertising intervals in 0.625 ms units */ }; /*! configurable parameters for slave */ static const appSlaveCfg_t beaconScannerSlaveCfg = { 1, /*! Maximum connections */ }; /*! configurable parameters for security */ static const appSecCfg_t beaconScannerSecCfg = { DM_AUTH_BOND_FLAG, /*! Authentication and bonding flags */ DM_KEY_DIST_IRK, /*! Initiator key distribution flags */ DM_KEY_DIST_LTK | DM_KEY_DIST_IRK, /*! Responder key distribution flags */ FALSE, /*! TRUE if Out-of-band pairing data is present */ FALSE /*! TRUE to initiate security upon connection */ }; /*! configurable parameters for connection parameter update */ static const appUpdateCfg_t beaconScannerUpdateCfg = { 6000, /*! Connection idle period in ms before attempting connection parameter update; set to zero to disable */ 640, /*! Minimum connection interval in 1.25ms units */ 800, /*! Maximum connection interval in 1.25ms units */ 0, /*! Connection latency */ 600, /*! Supervision timeout in 10ms units */ 5 /*! Number of update attempts before giving up */ }; /*! Configurable parameters for service and characteristic discovery */ static const appDiscCfg_t beaconScannerDiscCfg = { FALSE /*! TRUE to wait for a secure connection before initiating discovery */ }; /*! SMP security parameter configuration */ static const smpCfg_t beaconScannerSmpCfg = { 3000, /*! 'Repeated attempts' timeout in msec */ SMP_IO_NO_IN_NO_OUT, /*! I/O Capability */ 7, /*! Minimum encryption key length */ 16, /*! Maximum encryption key length */ 3, /*! Attempts to trigger 'repeated attempts' timeout */ 0 /*! Device authentication requirements */ }; static const appCfg_t beaconScannerAppCfg = { TRUE, /*! TRUE to abort service discovery if service not found */ TRUE /*! TRUE to disconnect if ATT transaction times out */ }; /*! local IRK */ static uint8_t localIrk[] = { 0x95, 0xC8, 0xEE, 0x6F, 0xC5, 0x0D, 0xEF, 0x93, 0x35, 0x4E, 0x7C, 0x57, 0x08, 0xE2, 0xA3, 0x85 }; /************************************************************************************************** Advertising Data **************************************************************************************************/ /*! advertising data, discoverable mode */ static const uint8_t beaconScannerAdvDataDisc[] = { /*! flags */ 2, /*! length */ DM_ADV_TYPE_FLAGS, /*! AD type */ DM_FLAG_LE_LIMITED_DISC | /*! flags */ DM_FLAG_LE_BREDR_NOT_SUP, /*! tx power */ 2, /*! length */ DM_ADV_TYPE_TX_POWER, /*! AD type */ 0, /*! tx power */ /*! device name */ 14, /*! length */ DM_ADV_TYPE_LOCAL_NAME, /*! AD type */ 'B', 'e', 'a', 'c', 'o', 'n', 'S', 'c', 'a', 'n', 'n', 'e', 'r', }; /*! scan data */ static const uint8_t beaconScannerScanData[] = { /*! service UUID list */ 7, /*! length */ DM_ADV_TYPE_16_UUID, /*! AD type */ UINT16_TO_BYTES(ATT_UUID_LINK_LOSS_SERVICE), UINT16_TO_BYTES(ATT_UUID_IMMEDIATE_ALERT_SERVICE), UINT16_TO_BYTES(ATT_UUID_TX_POWER_SERVICE) }; /*! configurable parameters for master */ static const appMasterCfg_t beaconScannerMasterCfg = { 96, /*! The scan interval, in 0.625 ms units */ 48, /*! The scan window, in 0.625 ms units */ 200, /*! The scan duration in ms */ DM_DISC_MODE_NONE, /*! The GAP discovery mode */ DM_SCAN_TYPE_PASSIVE /*! The scan type (active or passive) */ }; /*! Scan interval in seconds */ #define BEACONSCANNER_SCAN_INTERVAL 1 /*! Scan delay in seconds afer connection is being setup. */ #define BEACONSCANNER_SCAN_DELAY 3 /************************************************************************************************** ATT Client Discovery Data **************************************************************************************************/ /*! Discovery states: enumeration of services to be discovered */ enum { BEACONSCANNER_DISC_IAS_SVC, /* Immediate Alert service */ BEACONSCANNER_DISC_GATT_SVC, /* GATT service */ BEACONSCANNER_DISC_GAP_SVC, /* GAP service */ BEACONSCANNER_DISC_SVC_MAX /* Discovery complete */ }; /*! the Client handle list, beaconScannerCb.hdlList[], is set as follows: * * ------------------------------- <- BEACONSCANNER_DISC_IAS_START * | IAS alert level handle | * ------------------------------- <- BEACONSCANNER_DISC_GATT_START * | GATT svc changed handle | * ------------------------------- * | GATT svc changed ccc handle | * ------------------------------- <- BEACONSCANNER_DISC_GAP_START * | GAP central addr res handle | * ------------------------------- * | GAP RPA Only handle | * ------------------------------- */ /*! Start of each service's handles in the the handle list */ #define BEACONSCANNER_DISC_IAS_START 0 #define BEACONSCANNER_DISC_GATT_START (BEACONSCANNER_DISC_IAS_START + FMPL_IAS_HDL_LIST_LEN) #define BEACONSCANNER_DISC_GAP_START (BEACONSCANNER_DISC_GATT_START + GATT_HDL_LIST_LEN) #define BEACONSCANNER_DISC_HDL_LIST_LEN (BEACONSCANNER_DISC_GAP_START + GAP_HDL_LIST_LEN) /*! Pointers into handle list for each service's handles */ static uint16_t *pBeaconScanIasHdlList = &beaconScannerCb.hdlList[BEACONSCANNER_DISC_IAS_START]; static uint16_t *pBeaconScanGattHdlList = &beaconScannerCb.hdlList[BEACONSCANNER_DISC_GATT_START]; static uint16_t *pBeaconScanGapHdlList = &beaconScannerCb.hdlList[BEACONSCANNER_DISC_GAP_START]; /* sanity check: make sure handle list length is <= app db handle list length */ WSF_CT_ASSERT(BEACONSCANNER_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN); /************************************************************************************************** ATT Client Data **************************************************************************************************/ /* Default value for GATT service changed ccc descriptor */ static const uint8_t beaconScannerGattScCccVal[] = {UINT16_TO_BYTES(ATT_CLIENT_CFG_INDICATE)}; /* List of characteristics to configure */ static const attcDiscCfg_t beaconScannerDiscCfgList[] = { /* Write: GATT service changed ccc descriptor */ {beaconScannerGattScCccVal, sizeof(beaconScannerGattScCccVal), (GATT_SC_CCC_HDL_IDX + BEACONSCANNER_DISC_GATT_START)}, /* Read: GAP central address resolution attribute */ {NULL, 0, (GAP_CAR_HDL_IDX + BEACONSCANNER_DISC_GAP_START)}, }; /* Characteristic configuration list length */ #define BEACONSCANNER_DISC_CFG_LIST_LEN (sizeof(beaconScannerDiscCfgList) / sizeof(attcDiscCfg_t)) /* sanity check: make sure configuration list length is <= handle list length */ WSF_CT_ASSERT(BEACONSCANNER_DISC_CFG_LIST_LEN <= BEACONSCANNER_DISC_HDL_LIST_LEN); /************************************************************************************************** ATT Server Data **************************************************************************************************/ /*! enumeration of client characteristic configuration descriptors used in local ATT server */ enum { BEACONSCANNER_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */ BEACONSCANNER_NUM_CCC_IDX /*! Number of ccc's */ }; /*! client characteristic configuration descriptors settings, indexed by ccc enumeration */ static const attsCccSet_t beaconScannerCccSet[BEACONSCANNER_NUM_CCC_IDX] = { /* cccd handle value range security level */ {GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_ENC} /* BEACONSCANNER_GATT_SC_CCC_IDX */ }; /*************************************************************************************************/ /*! * \fn beaconScannerAlert * * \brief Perform an alert based on the alert characteristic value * * \param alert alert characteristic value * * \return None. */ /*************************************************************************************************/ static void beaconScannerAlert(uint8_t alert) { /* perform alert according to setting of alert alert */ if (alert == CH_ALERT_LVL_NONE) { AppUiAction(APP_UI_ALERT_CANCEL); } else if (alert == CH_ALERT_LVL_MILD) { AppUiAction(APP_UI_ALERT_LOW); } else if (alert == CH_ALERT_LVL_HIGH) { AppUiAction(APP_UI_ALERT_HIGH); } } /*************************************************************************************************/ /*! * \fn beaconScannerDmCback * * \brief Application DM callback. * * \param pDmEvt DM callback event * * \return None. */ /*************************************************************************************************/ static void beaconScannerDmCback(dmEvt_t *pDmEvt) { dmEvt_t *pMsg; uint16_t len; if (pDmEvt->hdr.event == DM_SEC_ECC_KEY_IND) { DmSecSetEccKey(&pDmEvt->eccMsg.data.key); } else { len = DmSizeOfEvt(pDmEvt); if ((pMsg = WsfMsgAlloc(len)) != NULL) { memcpy(pMsg, pDmEvt, len); WsfMsgSend(beaconScannerCb.handlerId, pMsg); } } } /*************************************************************************************************/ /*! * \fn beaconScannerAttCback * * \brief Application ATT callback. * * \param pEvt ATT callback event * * \return None. */ /*************************************************************************************************/ static void beaconScannerAttCback(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(beaconScannerCb.handlerId, pMsg); } return; } /*************************************************************************************************/ /*! * \fn beaconScannerCccCback * * \brief Application ATTS client characteristic configuration callback. * * \param pDmEvt DM callback event * * \return None. */ /*************************************************************************************************/ static void beaconScannerCccCback(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(beaconScannerCb.handlerId, pMsg); } } /*************************************************************************************************/ /*! * \fn beaconScannerIasWriteCback * * \brief ATTS write callback for Immediate Alert service. * * \return ATT status. */ /*************************************************************************************************/ static uint8_t beaconScannerIasWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation, uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr) { ATT_TRACE_INFO3("beaconScannerIasWriteCback connId:%d handle:0x%04x op:0x%02x", connId, handle, operation); ATT_TRACE_INFO2(" offset:0x%04x len:0x%04x", offset, len); beaconScannerAlert(*pValue); return ATT_SUCCESS; } /*************************************************************************************************/ /*! * \fn beaconScannerOpen * * \brief Perform actions on connection open. * * \param pMsg Pointer to DM callback event message. * * \return None. */ /*************************************************************************************************/ static void beaconScannerOpen(dmEvt_t *pMsg) { /* Update peer address info */ beaconScannerCb.addrType = pMsg->connOpen.addrType; BdaCpy(beaconScannerCb.peerAddr, pMsg->connOpen.peerAddr); } /*************************************************************************************************/ /*! * \fn beaconScannerClose * * \brief Perform UI actions on connection close. * * \param pMsg Pointer to DM callback event message. * * \return None. */ /*************************************************************************************************/ static void beaconScannerClose(dmEvt_t *pMsg) { uint8_t *pVal; uint16_t len; /* perform alert according to setting of link loss alert */ if (AttsGetAttr(LLS_AL_HDL, &len, &pVal) == ATT_SUCCESS) { if (*pVal == CH_ALERT_LVL_MILD || *pVal == CH_ALERT_LVL_HIGH) { beaconScannerAlert(*pVal); } } } /*************************************************************************************************/ /*! * \fn beaconScannerSecPairCmpl * * \brief Handle pairing complete. * * \param pMsg Pointer to DM callback event message. * * \return None. */ /*************************************************************************************************/ static void beaconScannerSecPairCmpl(dmEvt_t *pMsg) { appConnCb_t *pCb; dmSecKey_t *pPeerKey; /* if LL Privacy has been enabled */ if (DmLlPrivEnabled()) { /* look up app connection control block from DM connection ID */ pCb = &appConnCb[pMsg->hdr.param - 1]; /* if database record handle valid */ if ((pCb->dbHdl != APP_DB_HDL_NONE) && ((pPeerKey = AppDbGetKey(pCb->dbHdl, DM_KEY_IRK, NULL)) != NULL)) { /* store peer identity info */ BdaCpy(beaconScannerCb.peerAddr, pPeerKey->irk.bdAddr); beaconScannerCb.addrType = pPeerKey->irk.addrType; } } } /*************************************************************************************************/ /*! * \fn beaconScannerSetup * * \brief Set up advertising and other procedures that need to be performed after * device reset. * * \param pMsg Pointer to DM callback event message. * * \return None. */ /*************************************************************************************************/ static void beaconScannerSetup(dmEvt_t *pMsg) { /* set advertising and scan response data for discoverable mode */ AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(beaconScannerAdvDataDisc), (uint8_t *) beaconScannerAdvDataDisc); AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(beaconScannerScanData), (uint8_t *) beaconScannerScanData); /* set advertising and scan response data for connectable mode */ AppAdvSetData(APP_ADV_DATA_CONNECTABLE, sizeof(beaconScannerAdvDataDisc), (uint8_t *) beaconScannerAdvDataDisc); AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, sizeof(beaconScannerScanData), (uint8_t *) beaconScannerScanData); /* start advertising; automatically set connectable/discoverable mode and bondable mode */ AppAdvStart(APP_MODE_AUTO_INIT); } /*************************************************************************************************/ /*! * \fn beaconScannerValueUpdate * * \brief Process a received ATT indication. * * \param pMsg Pointer to ATT callback event message. * * \return None. */ /*************************************************************************************************/ static void beaconScannerValueUpdate(attEvt_t *pMsg) { if (pMsg->hdr.status == ATT_SUCCESS) { /* determine which profile the handle belongs to */ /* GATT */ if (GattValueUpdate(pBeaconScanGattHdlList, pMsg) == ATT_SUCCESS) { return; } /* GAP */ if (GapValueUpdate(pBeaconScanGapHdlList, pMsg) == ATT_SUCCESS) { return; } } } /*************************************************************************************************/ /*! * \fn beaconScannerDiscGapCmpl * * \brief GAP service discovery has completed. * * \param connId Connection identifier. * * \return None. */ /*************************************************************************************************/ static void beaconScannerDiscGapCmpl(dmConnId_t connId) { appDbHdl_t dbHdl; /* if RPA Only attribute found on peer device */ if ((pBeaconScanGapHdlList[GAP_RPAO_HDL_IDX] != ATT_HANDLE_NONE) && ((dbHdl = AppDbGetHdl(connId)) != APP_DB_HDL_NONE)) { /* update DB */ AppDbSetPeerRpao(dbHdl, TRUE); } } /*************************************************************************************************/ /*! * \fn beaconScannerBtnCback * * \brief Button press callback. * * \param btn Button press. * * \return None. */ /*************************************************************************************************/ static void beaconScannerBtnCback(uint8_t btn) { dmConnId_t connId; /* button actions when connected */ if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE) { switch (btn) { case APP_UI_BTN_1_SHORT: /* send immediate alert, high */ FmplSendAlert(connId, pBeaconScanIasHdlList[FMPL_IAS_AL_HDL_IDX], CH_ALERT_LVL_HIGH); break; case APP_UI_BTN_1_MED: /* send immediate alert, none */ FmplSendAlert(connId, pBeaconScanIasHdlList[FMPL_IAS_AL_HDL_IDX], CH_ALERT_LVL_NONE); break; case APP_UI_BTN_1_LONG: /* disconnect */ AppConnClose(connId); break; case APP_UI_BTN_2_SHORT: break; case APP_UI_BTN_2_MED: { uint8_t addrType = DmConnPeerAddrType(connId); /* if peer is using a public address */ if (addrType == DM_ADDR_PUBLIC) { /* add peer to the white list */ DmDevWhiteListAdd(addrType, DmConnPeerAddr(connId)); /* set Advertising filter policy to All */ DmDevSetFilterPolicy(DM_FILT_POLICY_MODE_ADV, HCI_ADV_FILT_ALL); } } break; default: break; } } /* button actions when not connected */ else { switch (btn) { case APP_UI_BTN_1_SHORT: /* start or restart advertising */ AppAdvStart(APP_MODE_AUTO_INIT); break; case APP_UI_BTN_1_MED: /* enter discoverable and bondable mode mode */ AppSetBondable(TRUE); AppAdvStart(APP_MODE_DISCOVERABLE); break; case APP_UI_BTN_1_LONG: /* clear bonded device info */ AppDbDeleteAllRecords(); /* if LL Privacy is supported */ if (HciLlPrivacySupported()) { /* if LL Privacy has been enabled */ if (DmLlPrivEnabled()) { /* make sure LL Privacy is disabled before restarting advertising */ DmPrivSetAddrResEnable(FALSE); } /* clear resolving list */ DmPrivClearResList(); } /* restart advertising */ AppAdvStart(APP_MODE_AUTO_INIT); break; case APP_UI_BTN_1_EX_LONG: /* add RPAO characteristic to GAP service -- needed only when DM Privacy enabled */ SvcCoreGapAddRpaoCh(); break; case APP_UI_BTN_2_MED: /* clear the white list */ DmDevWhiteListClear(); /* set Advertising filter policy to None */ DmDevSetFilterPolicy(DM_FILT_POLICY_MODE_ADV, HCI_ADV_FILT_NONE); break; case APP_UI_BTN_2_SHORT: /* stop advertising */ AppAdvStop(); break; case APP_UI_BTN_2_LONG: /* start directed advertising using peer address */ AppConnAccept(DM_ADV_CONN_DIRECT_LO_DUTY, beaconScannerCb.addrType, beaconScannerCb.peerAddr, beaconScannerCb.dbHdl); break; case APP_UI_BTN_2_EX_LONG: /* enable device privacy -- start generating local RPAs every 15 minutes */ DmDevPrivStart(15 * 60); break; default: break; } } } /*************************************************************************************************/ /*! * \fn beaconScannerDiscCback * * \brief Discovery callback. * * \param connId Connection identifier. * \param status Service or configuration status. * * \return None. */ /*************************************************************************************************/ static void beaconScannerDiscCback(dmConnId_t connId, uint8_t status) { switch(status) { case APP_DISC_INIT: /* set handle list when initialization requested */ AppDiscSetHdlList(connId, BEACONSCANNER_DISC_HDL_LIST_LEN, beaconScannerCb.hdlList); break; case APP_DISC_SEC_REQUIRED: /* request security */ AppSlaveSecurityReq(connId); break; case APP_DISC_START: /* initialize discovery state */ beaconScannerCb.discState = BEACONSCANNER_DISC_IAS_SVC; /* discover immediate alert service */ FmplIasDiscover(connId, pBeaconScanIasHdlList); break; case APP_DISC_FAILED: if (pAppCfg->abortDisc) { /* if immediate alert service not found */ if (beaconScannerCb.discState == BEACONSCANNER_DISC_IAS_SVC) { /* discovery failed */ AppDiscComplete(connId, APP_DISC_FAILED); break; } } /* else fall through to continue discovery */ case APP_DISC_CMPL: /* next discovery state */ beaconScannerCb.discState++; if (beaconScannerCb.discState == BEACONSCANNER_DISC_GATT_SVC) { /* discover GATT service */ GattDiscover(connId, pBeaconScanGattHdlList); } else if (beaconScannerCb.discState == BEACONSCANNER_DISC_GAP_SVC) { /* discover GAP service */ GapDiscover(connId, pBeaconScanGapHdlList); } else { /* discovery complete */ AppDiscComplete(connId, APP_DISC_CMPL); /* GAP service discovery completed */ beaconScannerDiscGapCmpl(connId); /* start configuration */ AppDiscConfigure(connId, APP_DISC_CFG_START, BEACONSCANNER_DISC_CFG_LIST_LEN, (attcDiscCfg_t *) beaconScannerDiscCfgList, BEACONSCANNER_DISC_HDL_LIST_LEN, beaconScannerCb.hdlList); } break; case APP_DISC_CFG_START: /* start configuration */ AppDiscConfigure(connId, APP_DISC_CFG_START, BEACONSCANNER_DISC_CFG_LIST_LEN, (attcDiscCfg_t *) beaconScannerDiscCfgList, BEACONSCANNER_DISC_HDL_LIST_LEN, beaconScannerCb.hdlList); break; case APP_DISC_CFG_CMPL: AppDiscComplete(connId, APP_DISC_CFG_CMPL); break; case APP_DISC_CFG_CONN_START: /* no connection setup configuration for this application */ break; default: break; } } /*************************************************************************************************/ /*! * \fn beaconScannerProcMsg * * \brief Process messages from the event handler. * * \param pMsg Pointer to message. * * \return None. */ /*************************************************************************************************/ static void beaconScannerProcMsg(dmEvt_t *pMsg) { uint8_t uiEvent = APP_UI_NONE; switch(pMsg->hdr.event) { case ATTC_READ_RSP: case ATTC_HANDLE_VALUE_IND: beaconScannerValueUpdate((attEvt_t *) pMsg); break; case ATT_MTU_UPDATE_IND: APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu); break; case DM_RESET_CMPL_IND: AttsCalculateDbHash(); DmSecGenerateEccKeyReq(); beaconScannerSetup(pMsg); uiEvent = APP_UI_RESET_CMPL; break; case DM_ADV_START_IND: uiEvent = APP_UI_ADV_START; break; case DM_ADV_STOP_IND: uiEvent = APP_UI_ADV_STOP; // almost immediately start SCAN. WsfTimerStartMs(&beaconScannerCb.scanTimer, 10); break; case DM_SCAN_START_IND: break; case DM_SCAN_STOP_IND: { // advertising again if there's no connection. if (AppConnIsOpen() == DM_CONN_ID_NONE) { AppAdvStart(APP_MODE_AUTO_INIT); } else { // during connection, we'll scan every BEACONSCANNER_SCAN_INTERVAL seconds. WsfTimerStartSec(&beaconScannerCb.scanTimer, BEACONSCANNER_SCAN_INTERVAL); } } break; case DM_SCAN_REPORT_IND: { uint8_t *pData; pData = DmFindAdType(DM_ADV_TYPE_MANUFACTURER, pMsg->scanReport.len, pMsg->scanReport.pData); if ( pData ) { if ( !memcmp(pData + IBEACON_UUID128_OFFSET, TenDegree_ibeacon_uuid128, 16) ) { uint16_t major, minor; APP_TRACE_INFO1("eventType 0x%x", pMsg->scanReport.eventType); APP_TRACE_INFO1("addrType 0x%x", pMsg->scanReport.addrType); APP_TRACE_INFO3("addr[0-2] 0x%02x %02x %02x", pMsg->scanReport.addr[0], pMsg->scanReport.addr[1], pMsg->scanReport.addr[2] ); APP_TRACE_INFO3("addr[3-5] 0x%02x %02x %02x", pMsg->scanReport.addr[3], pMsg->scanReport.addr[4], pMsg->scanReport.addr[5] ); APP_TRACE_INFO1("rssi %d", pMsg->scanReport.rssi); major = pData[IBEACON_UUID128_MAJOR] << 8; major += pData[IBEACON_UUID128_MAJOR + 1]; APP_TRACE_INFO1("Major %d", major); minor = pData[IBEACON_UUID128_MINOR] << 8; minor += pData[IBEACON_UUID128_MINOR + 1]; APP_TRACE_INFO1("Minor %d", minor); } } } break; case DM_CONN_OPEN_IND: beaconScannerOpen(pMsg); uiEvent = APP_UI_CONN_OPEN; // here we purposely delay scan until the interaction at higher layer // between master and slave is done. WsfTimerStartSec(&beaconScannerCb.scanTimer, BEACONSCANNER_SCAN_DELAY); break; case DM_CONN_CLOSE_IND: beaconScannerClose(pMsg); uiEvent = APP_UI_CONN_CLOSE; AppScanStop(); break; case DM_SEC_PAIR_CMPL_IND: beaconScannerSecPairCmpl(pMsg); uiEvent = APP_UI_SEC_PAIR_CMPL; break; case DM_SEC_PAIR_FAIL_IND: uiEvent = APP_UI_SEC_PAIR_FAIL; break; case DM_SEC_ENCRYPT_IND: uiEvent = APP_UI_SEC_ENCRYPT; break; case DM_SEC_ENCRYPT_FAIL_IND: uiEvent = APP_UI_SEC_ENCRYPT_FAIL; break; case DM_SEC_AUTH_REQ_IND: AppHandlePasskey(&pMsg->authReq); break; case DM_SEC_COMPARE_IND: AppHandleNumericComparison(&pMsg->cnfInd); break; case DM_ADV_NEW_ADDR_IND: break; case BEACONSCANNER_SCAN_TIMER_IND: AppScanStart(beaconScannerMasterCfg.discMode, beaconScannerMasterCfg.scanType, beaconScannerMasterCfg.scanDuration); break; case DM_CONN_READ_RSSI_IND: /* if successful */ if (pMsg->hdr.status == HCI_SUCCESS) { /* display RSSI value */ AppUiDisplayRssi(pMsg->readRssi.rssi); } break; case DM_VENDOR_SPEC_CMD_CMPL_IND: { #if defined(AM_PART_APOLLO) || defined(AM_PART_APOLLO2) uint8_t *param_ptr = &pMsg->vendorSpecCmdCmpl.param[0]; switch (pMsg->vendorSpecCmdCmpl.opcode) { case 0xFC20: //read at address { uint32_t read_value; BSTREAM_TO_UINT32(read_value, param_ptr); APP_TRACE_INFO3("VSC 0x%0x complete status %x param %x", pMsg->vendorSpecCmdCmpl.opcode, pMsg->hdr.status, read_value); } break; default: APP_TRACE_INFO2("VSC 0x%0x complete status %x", pMsg->vendorSpecCmdCmpl.opcode, pMsg->hdr.status); break; } #endif } break; default: break; } if (uiEvent != APP_UI_NONE) { AppUiAction(uiEvent); } } /*************************************************************************************************/ /*! * \fn BeaconScannerHandlerInit * * \brief Proximity reporter handler init function called during system initialization. * * \param handlerID WSF handler ID. * * \return None. */ /*************************************************************************************************/ void BeaconScannerHandlerInit(wsfHandlerId_t handlerId) { APP_TRACE_INFO0("BeaconScannerHandlerInit"); /* store handler ID */ beaconScannerCb.handlerId = handlerId; /* initialize control block */ beaconScannerCb.scanTimer.handlerId = handlerId; beaconScannerCb.scanTimer.msg.event = BEACONSCANNER_SCAN_TIMER_IND; /* Set configuration pointers */ pAppMasterCfg = (appMasterCfg_t *) &beaconScannerMasterCfg; pAppSlaveCfg = (appSlaveCfg_t *) &beaconScannerSlaveCfg; pAppAdvCfg = (appAdvCfg_t *) &beaconScannerAdvCfg; pAppSecCfg = (appSecCfg_t *) &beaconScannerSecCfg; pAppUpdateCfg = (appUpdateCfg_t *) &beaconScannerUpdateCfg; pAppDiscCfg = (appDiscCfg_t *) &beaconScannerDiscCfg; pAppCfg = (appCfg_t *) &beaconScannerAppCfg; /* Set stack configuration pointers */ pSmpCfg = (smpCfg_t *)&beaconScannerSmpCfg; /* Initialize application framework */ AppMasterInit(); AppSlaveInit(); AppDiscInit(); /* Set IRK for the local device */ DmSecSetLocalIrk(localIrk); } /*************************************************************************************************/ /*! * \fn BeaconScannerHandler * * \brief WSF event handler for proximity reporter. * * \param event WSF event mask. * \param pMsg WSF message. * * \return None. */ /*************************************************************************************************/ void BeaconScannerHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg) { if (pMsg != NULL) { APP_TRACE_INFO1("BeaconScanner got evt %d", pMsg->event); /* process ATT messages */ if (pMsg->event <= ATT_CBACK_END) { /* process discovery-related ATT messages */ AppDiscProcAttMsg((attEvt_t *) pMsg); } /* process DM messages */ else if (pMsg->event <= DM_CBACK_END) { /* 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); } /* perform profile and user interface-related operations */ beaconScannerProcMsg((dmEvt_t *) pMsg); } } /*************************************************************************************************/ /*! * \fn BeaconScannerStart * * \brief Start the proximity profile reporter application. * * \return None. */ /*************************************************************************************************/ void BeaconScannerStart(void) { /* Register for stack callbacks */ DmRegister(beaconScannerDmCback); DmConnRegister(DM_CLIENT_ID_APP, beaconScannerDmCback); AttRegister(beaconScannerAttCback); AttConnRegister(AppServerConnCback); AttsCccRegister(BEACONSCANNER_NUM_CCC_IDX, (attsCccSet_t *) beaconScannerCccSet, beaconScannerCccCback); /* Register for app framework button callbacks */ AppUiBtnRegister(beaconScannerBtnCback); /* Register for app framework discovery callbacks */ AppDiscRegister(beaconScannerDiscCback); /* Initialize attribute server database */ SvcCoreAddGroup(); SvcPxCbackRegister(NULL, beaconScannerIasWriteCback); SvcPxAddGroup(); /* Reset the device */ DmDevReset(); }