vos/ambiq-hal-sys/ambiq-sparkfun-sdk/ambiq_ble/apps/beaconscanner/beaconscanner_main.c
2022-10-23 23:45:43 -07:00

1177 lines
37 KiB
C

// ****************************************************************************
//
// 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 <string.h>
#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();
}