initial commit
This commit is contained in:
Vendored
+962
@@ -0,0 +1,962 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Application framework service discovery and configuration.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/bstream.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "svc_ch.h"
|
||||
#include "app_api.h"
|
||||
#include "app_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! "In progress" values */
|
||||
#define APP_DISC_IDLE 0
|
||||
#define APP_DISC_SVC_DISC_IN_PROGRESS 1
|
||||
#define APP_DISC_CFG_IN_PROGRESS 2
|
||||
#define APP_DISC_READ_DBH_IN_PROGRESS 3
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! \brief Application Discovery controler block */
|
||||
typedef struct
|
||||
{
|
||||
attcDiscCb_t *pDiscCb; /*! ATT discovery control block */
|
||||
uint16_t *pHdlList; /*! Handle list */
|
||||
uint8_t connCfgStatus; /*! Connection setup configuration status */
|
||||
uint8_t cmplStatus; /*! Discovery or configuration complete status */
|
||||
uint8_t hdlListLen; /*! Handle list length */
|
||||
uint8_t inProgress; /*! Discovery or configuration in progress */
|
||||
bool_t alreadySecure; /*! TRUE if connection was already secure */
|
||||
bool_t secRequired; /*! TRUE if security is required for configuration */
|
||||
bool_t scPending; /*! TRUE if service changed from peer is pending */
|
||||
} appDiscCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Discovery connection control blocks */
|
||||
static appDiscCb_t appDiscCb[DM_CONN_MAX];
|
||||
|
||||
/*! Discovery callback */
|
||||
static appDiscCback_t appDiscCback;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start discovery or configuration
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
* \param status Status of discovery process.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appDiscCfgStart(dmConnId_t connId, uint8_t status)
|
||||
{
|
||||
appDiscCb_t *pAppDiscCb = &appDiscCb[connId - 1];
|
||||
|
||||
/* if configuration not complete */
|
||||
if (status < APP_DISC_CFG_CMPL)
|
||||
{
|
||||
/* notify application to start configuration */
|
||||
(*appDiscCback)(connId, APP_DISC_CFG_START);
|
||||
}
|
||||
/* else if configuration complete start connection setup configuration */
|
||||
else if (status == APP_DISC_CFG_CMPL && pAppDiscCb->connCfgStatus == APP_DISC_INIT)
|
||||
{
|
||||
(*appDiscCback)(connId, APP_DISC_CFG_CONN_START);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start discovery or configuration
|
||||
*
|
||||
* \param dmConnId_t Connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void appDiscStart(dmConnId_t connId)
|
||||
{
|
||||
appDbHdl_t hdl;
|
||||
uint8_t status;
|
||||
appDiscCb_t *pAppDiscCb = &appDiscCb[connId - 1];
|
||||
|
||||
if (pAppDiscCb->inProgress == APP_DISC_IDLE)
|
||||
{
|
||||
/* get discovery status */
|
||||
if ((hdl = AppDbGetHdl(connId)) != APP_DB_HDL_NONE)
|
||||
{
|
||||
status = AppDbGetDiscStatus(hdl);
|
||||
}
|
||||
else
|
||||
{
|
||||
status = pAppDiscCb->cmplStatus;
|
||||
}
|
||||
|
||||
/* if discovery not complete */
|
||||
if (status < APP_DISC_CMPL)
|
||||
{
|
||||
/* Read database hash first if not bonded or if secure but without bond. */
|
||||
if ((!pAppDiscCb->alreadySecure) || (pAppDiscCb->alreadySecure && !AppCheckBonded(connId)))
|
||||
{
|
||||
/* notify application to start discovery */
|
||||
(*appDiscCback)(connId, APP_DISC_READ_DATABASE_HASH);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* notify application to start discovery */
|
||||
(*appDiscCback)(connId, APP_DISC_START);
|
||||
}
|
||||
}
|
||||
/* else if discovery was completed successfully */
|
||||
else if (status != APP_DISC_FAILED)
|
||||
{
|
||||
/* get stored handle list if present */
|
||||
if (hdl != APP_DB_HDL_NONE && pAppDiscCb->pHdlList != NULL)
|
||||
{
|
||||
/* Read hash before using handles */
|
||||
if (AppDbIsCacheCheckedByHash(hdl))
|
||||
{
|
||||
pAppDiscCb->inProgress = APP_DISC_READ_DBH_IN_PROGRESS;
|
||||
|
||||
/* Read the database hash. */
|
||||
AttcReadByTypeReq(connId, ATT_HANDLE_START, ATT_HANDLE_MAX, ATT_16_UUID_LEN,
|
||||
(uint8_t *)attGattDbhChUuid, FALSE);
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(pAppDiscCb->pHdlList, AppDbGetHdlList(hdl), (pAppDiscCb->hdlListLen * sizeof(uint16_t)));
|
||||
}
|
||||
}
|
||||
|
||||
appDiscCfgStart(connId, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Reset service discovery.
|
||||
*
|
||||
* \param connId DM Connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void appDiscRestartDiscovery(dmConnId_t connId)
|
||||
{
|
||||
appDiscCb_t *pAppDiscCb = &appDiscCb[connId - 1];
|
||||
appDbHdl_t dbHdl;
|
||||
|
||||
/* otherwise initialize discovery and configuration status */
|
||||
pAppDiscCb->connCfgStatus = APP_DISC_INIT;
|
||||
pAppDiscCb->cmplStatus = APP_DISC_INIT;
|
||||
pAppDiscCb->secRequired = FALSE;
|
||||
pAppDiscCb->scPending = FALSE;
|
||||
|
||||
/* initialize handle list */
|
||||
if (pAppDiscCb->pHdlList != NULL)
|
||||
{
|
||||
memset(pAppDiscCb->pHdlList, 0, (pAppDiscCb->hdlListLen * sizeof(uint16_t)));
|
||||
|
||||
/* clear stored discovery status and handle list */
|
||||
if ((dbHdl = AppDbGetHdl(connId)) != APP_DB_HDL_NONE)
|
||||
{
|
||||
AppDbSetDiscStatus(dbHdl, APP_DISC_INIT);
|
||||
AppDbSetHdlList(dbHdl, pAppDiscCb->pHdlList);
|
||||
}
|
||||
}
|
||||
|
||||
/* if configuration in progress */
|
||||
if (pAppDiscCb->inProgress == APP_DISC_CFG_IN_PROGRESS)
|
||||
{
|
||||
/* set pending status to set up abort of configuration */
|
||||
pAppDiscCb->scPending = TRUE;
|
||||
}
|
||||
/* else no procedure in progress */
|
||||
else
|
||||
{
|
||||
/* if not waiting for security or connection is already secure, then
|
||||
* initiate discovery now; otherwise discovery will be initiated after
|
||||
* security is done
|
||||
*/
|
||||
if (!pAppDiscCfg->waitForSec || pAppDiscCb->alreadySecure)
|
||||
{
|
||||
appDiscStart(connId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a DM_CONN_OPEN_IND event.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appDiscConnOpen(dmEvt_t *pMsg)
|
||||
{
|
||||
appDiscCb_t *pAppDiscCb = &appDiscCb[(dmConnId_t) pMsg->hdr.param - 1];
|
||||
|
||||
pAppDiscCb->alreadySecure = FALSE;
|
||||
pAppDiscCb->connCfgStatus = APP_DISC_INIT;
|
||||
pAppDiscCb->cmplStatus = APP_DISC_INIT;
|
||||
pAppDiscCb->secRequired = FALSE;
|
||||
pAppDiscCb->scPending = FALSE;
|
||||
|
||||
/* tell app to set up handle list */
|
||||
(*appDiscCback)((dmConnId_t) pMsg->hdr.param, APP_DISC_INIT);
|
||||
|
||||
/* initialize handle list */
|
||||
if (pAppDiscCb->pHdlList != NULL)
|
||||
{
|
||||
memset(pAppDiscCb->pHdlList, 0, (pAppDiscCb->hdlListLen * sizeof(uint16_t)));
|
||||
}
|
||||
|
||||
/* if not waiting for security start discovery/configuration */
|
||||
if (!pAppDiscCfg->waitForSec)
|
||||
{
|
||||
appDiscStart((dmConnId_t) pMsg->hdr.param);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a DM_CONN_CLOSE_IND event.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appDiscConnClose(dmEvt_t *pMsg)
|
||||
{
|
||||
appDiscCb_t *pAppDiscCb = &appDiscCb[(dmConnId_t) pMsg->hdr.param - 1];
|
||||
|
||||
pAppDiscCb->inProgress = APP_DISC_IDLE;
|
||||
|
||||
appDbHdl_t hdl;
|
||||
|
||||
if ((hdl = AppDbGetHdl((dmConnId_t) pMsg->hdr.param)) != APP_DB_HDL_NONE)
|
||||
{
|
||||
// reset discovery status
|
||||
AppDbSetDiscStatus(hdl, APP_DISC_INIT);
|
||||
}
|
||||
|
||||
|
||||
if (pAppDiscCb->pDiscCb != NULL)
|
||||
{
|
||||
WsfBufFree(pAppDiscCb->pDiscCb);
|
||||
pAppDiscCb->pDiscCb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle pairing complete.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appDiscPairCmpl(dmEvt_t *pMsg)
|
||||
{
|
||||
appDiscCb_t *pAppDiscCb = &appDiscCb[(dmConnId_t) pMsg->hdr.param - 1];
|
||||
appDbHdl_t hdl;
|
||||
|
||||
/* procedures triggered by security are only executed once */
|
||||
if (pAppDiscCb->alreadySecure)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* if bonded, disable hash check on cache if not already disabled */
|
||||
if (((hdl = AppDbGetHdl((dmConnId_t)pMsg->hdr.param)) != APP_DB_HDL_NONE) &&
|
||||
AppCheckBonded((dmConnId_t) pMsg->hdr.param) &&
|
||||
AppDbIsCacheCheckedByHash(hdl))
|
||||
{
|
||||
AppDbSetCacheByHash(appConnCb[pMsg->hdr.param - 1].dbHdl, FALSE);
|
||||
}
|
||||
|
||||
|
||||
/* if we are now bonded and discovery/configuration was performed before bonding */
|
||||
if (AppCheckBonded((dmConnId_t) pMsg->hdr.param) && (pAppDiscCb->cmplStatus != APP_DISC_INIT))
|
||||
{
|
||||
if (hdl != APP_DB_HDL_NONE)
|
||||
{
|
||||
/* store discovery status */
|
||||
AppDbSetDiscStatus(hdl, pAppDiscCb->cmplStatus);
|
||||
|
||||
/* store handle list */
|
||||
if (pAppDiscCb->cmplStatus == APP_DISC_CMPL || pAppDiscCb->cmplStatus == APP_DISC_CFG_CMPL)
|
||||
{
|
||||
if (pAppDiscCb->pHdlList != NULL)
|
||||
{
|
||||
AppDbSetHdlList(hdl, pAppDiscCb->pHdlList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if configuration was waiting for security */
|
||||
if (pAppDiscCb->secRequired)
|
||||
{
|
||||
pAppDiscCb->secRequired = FALSE;
|
||||
|
||||
/* resume configuration */
|
||||
if (pAppDiscCb->pDiscCb != NULL)
|
||||
{
|
||||
AttcDiscConfigResume((dmConnId_t) pMsg->hdr.param, pAppDiscCb->pDiscCb);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
/* if waiting for security start discovery now that connection is secure */
|
||||
if (pAppDiscCfg->waitForSec)
|
||||
{
|
||||
appDiscStart((dmConnId_t) pMsg->hdr.param);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pAppDiscCb->alreadySecure = TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle encryption indication
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appDiscEncryptInd(dmEvt_t *pMsg)
|
||||
{
|
||||
appDiscCb_t *pAppDiscCb = &appDiscCb[(dmConnId_t) pMsg->hdr.param - 1];
|
||||
|
||||
/* if encrypted with ltk */
|
||||
if (pMsg->encryptInd.usingLtk)
|
||||
{
|
||||
/* procedures triggered by security are only executed once */
|
||||
if (pAppDiscCb->alreadySecure)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* if we waiting for security start discovery now that connection is secure */
|
||||
if (pAppDiscCfg->waitForSec)
|
||||
{
|
||||
appDiscStart((dmConnId_t) pMsg->hdr.param);
|
||||
}
|
||||
/* else if configuration was waiting for security */
|
||||
else if (pAppDiscCb->secRequired)
|
||||
{
|
||||
pAppDiscCb->secRequired = FALSE;
|
||||
|
||||
/* resume configuration */
|
||||
if (pAppDiscCb->pDiscCb != NULL)
|
||||
{
|
||||
AttcDiscConfigResume((dmConnId_t) pMsg->hdr.param, pAppDiscCb->pDiscCb);
|
||||
}
|
||||
}
|
||||
|
||||
pAppDiscCb->alreadySecure = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle pairing failure
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appDiscPairFail(dmEvt_t *pMsg)
|
||||
{
|
||||
appDiscCb_t *pAppDiscCb = &appDiscCb[(dmConnId_t)pMsg->hdr.param - 1];
|
||||
|
||||
/* Procedures triggered by security are only executed once. */
|
||||
if (pAppDiscCb->alreadySecure)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fall back to relying on database hash to verify handles if configured to do so. */
|
||||
if (pAppDiscCfg->readDbHash)
|
||||
{
|
||||
pAppDiscCb->inProgress = APP_DISC_READ_DBH_IN_PROGRESS;
|
||||
|
||||
/* Read the database hash instead of re-performing service discovery. */
|
||||
AttcReadByTypeReq((dmConnId_t) pMsg->hdr.param, ATT_HANDLE_START, ATT_HANDLE_MAX, ATT_16_UUID_LEN,
|
||||
(uint8_t *) attGattDbhChUuid, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process discovery-related DM messages. This function should be called
|
||||
* from the application's event handler.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDiscProcDmMsg(dmEvt_t *pMsg)
|
||||
{
|
||||
switch(pMsg->hdr.event)
|
||||
{
|
||||
case DM_CONN_OPEN_IND:
|
||||
appDiscConnOpen(pMsg);
|
||||
break;
|
||||
|
||||
case DM_CONN_CLOSE_IND:
|
||||
appDiscConnClose(pMsg);
|
||||
break;
|
||||
|
||||
case DM_SEC_PAIR_CMPL_IND:
|
||||
appDiscPairCmpl(pMsg);
|
||||
break;
|
||||
|
||||
case DM_SEC_PAIR_FAIL_IND:
|
||||
appDiscPairFail(pMsg);
|
||||
break;
|
||||
|
||||
case DM_SEC_ENCRYPT_IND:
|
||||
appDiscEncryptInd(pMsg);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process discovery-related ATT messages. This function should be called
|
||||
* from the application's event handler.
|
||||
*
|
||||
* \param pMsg Pointer to ATT callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDiscProcAttMsg(attEvt_t *pMsg)
|
||||
{
|
||||
appDiscCb_t *pAppDiscCb = &appDiscCb[(dmConnId_t)pMsg->hdr.param - 1];
|
||||
uint8_t status;
|
||||
|
||||
/* Check status */
|
||||
if (pMsg->hdr.status == ATT_ERR_DATABASE_OUT_OF_SYNC)
|
||||
{
|
||||
/* Restart discovery as cached handle list is out of sync with server's database. */
|
||||
appDiscRestartDiscovery((dmConnId_t)pMsg->hdr.param);
|
||||
}
|
||||
|
||||
if (pAppDiscCb->inProgress == APP_DISC_READ_DBH_IN_PROGRESS)
|
||||
{
|
||||
if (pMsg->hdr.event == ATTC_READ_BY_TYPE_RSP)
|
||||
{
|
||||
dmConnId_t connId = (dmConnId_t)pMsg->hdr.param;
|
||||
|
||||
if (pMsg->hdr.status != ATT_SUCCESS)
|
||||
{
|
||||
/* No Database hash found on peer, notify application to start discovery */
|
||||
(*appDiscCback)(connId, APP_DISC_START);
|
||||
}
|
||||
else
|
||||
{
|
||||
appDbHdl_t hdl;
|
||||
|
||||
pAppDiscCb->inProgress = APP_DISC_IDLE;
|
||||
|
||||
/* If there is no existing record, create one now. The cached handles will be
|
||||
* validated across connections by the peer's database hash.
|
||||
*/
|
||||
if ((hdl = AppDbGetHdl(connId)) == APP_DB_HDL_NONE)
|
||||
{
|
||||
hdl = appConnCb[connId - 1].dbHdl = AppDbNewRecord(DmConnPeerAddrType(connId),
|
||||
DmConnPeerAddr(connId),
|
||||
(DmConnRole(connId)==DM_ROLE_MASTER)?TRUE:FALSE);
|
||||
}
|
||||
|
||||
/* Compare with existing database hash for this server.
|
||||
* If they do not match, perform service discovery.
|
||||
*/
|
||||
if (memcmp(AppDbGetPeerDbHash(hdl), pMsg->pValue + 3, ATT_DATABASE_HASH_LEN))
|
||||
{
|
||||
/* The new hash is different. Store it. */
|
||||
AppDbSetPeerDbHash(hdl, pMsg->pValue + 3);
|
||||
|
||||
/* Note: it is possible this record was created without a pairing or after
|
||||
* a pairing failed, validate record now so that it can be stored persistently.
|
||||
*/
|
||||
/* AppDbValidateRecord(hdl, 0); */
|
||||
|
||||
/* The validity of the cached handles is checked by the hash and not necessarily by
|
||||
* service changed indications.
|
||||
*/
|
||||
AppDbSetCacheByHash(hdl, TRUE);
|
||||
|
||||
/* notify application to start discovery */
|
||||
(*appDiscCback)(connId, APP_DISC_START);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Hash is the same, cached handles may be reused. */
|
||||
memcpy(pAppDiscCb->pHdlList, AppDbGetHdlList(hdl),
|
||||
(pAppDiscCb->hdlListLen * sizeof(uint16_t)));
|
||||
|
||||
/* get discovery status */
|
||||
status = AppDbGetDiscStatus(hdl);
|
||||
|
||||
appDiscCfgStart(connId, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pAppDiscCb->inProgress == APP_DISC_SVC_DISC_IN_PROGRESS)
|
||||
{
|
||||
/* service discovery */
|
||||
if (pMsg->hdr.event == ATTC_FIND_BY_TYPE_VALUE_RSP)
|
||||
{
|
||||
/* continue with service discovery */
|
||||
status = AttcDiscServiceCmpl(pAppDiscCb->pDiscCb, pMsg);
|
||||
|
||||
APP_TRACE_INFO1("AttcDiscServiceCmpl status 0x%02x", status);
|
||||
|
||||
/* if discovery complete and successful */
|
||||
if (status == ATT_SUCCESS)
|
||||
{
|
||||
/* proceed with characteristic discovery */
|
||||
AttcDiscCharStart((dmConnId_t) pMsg->hdr.param, pAppDiscCb->pDiscCb);
|
||||
}
|
||||
/* else if failed */
|
||||
else if (status != ATT_CONTINUING)
|
||||
{
|
||||
/* notify application of discovery failure */
|
||||
(*appDiscCback)((dmConnId_t) pMsg->hdr.param, APP_DISC_FAILED);
|
||||
}
|
||||
}
|
||||
/* characteristic discovery */
|
||||
else if (pMsg->hdr.event == ATTC_READ_BY_TYPE_RSP ||
|
||||
pMsg->hdr.event == ATTC_FIND_INFO_RSP)
|
||||
{
|
||||
/* continue with characteristic discovery */
|
||||
status = AttcDiscCharCmpl(pAppDiscCb->pDiscCb, pMsg);
|
||||
|
||||
APP_TRACE_INFO1("AttcDiscCharCmpl status 0x%02x", status);
|
||||
|
||||
/* if discovery complete and successful */
|
||||
if (status == ATT_SUCCESS)
|
||||
{
|
||||
/* notify application of discovery success */
|
||||
(*appDiscCback)((dmConnId_t) pMsg->hdr.param, APP_DISC_CMPL);
|
||||
}
|
||||
/* else if failed */
|
||||
else if (status != ATT_CONTINUING)
|
||||
{
|
||||
/* notify application of discovery failure */
|
||||
(*appDiscCback)((dmConnId_t) pMsg->hdr.param, APP_DISC_FAILED);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* characteristic configuration */
|
||||
else if ((pAppDiscCb->inProgress == APP_DISC_CFG_IN_PROGRESS) &&
|
||||
(pMsg->hdr.event == ATTC_READ_RSP || pMsg->hdr.event == ATTC_WRITE_RSP))
|
||||
{
|
||||
/* if service changed is pending */
|
||||
if (pAppDiscCb->scPending)
|
||||
{
|
||||
/* clear pending flag */
|
||||
pAppDiscCb->scPending = FALSE;
|
||||
|
||||
/* start discovery */
|
||||
pAppDiscCb->inProgress = APP_DISC_IDLE;
|
||||
appDiscStart((dmConnId_t) pMsg->hdr.param);
|
||||
}
|
||||
/* else if security failure */
|
||||
else if ((pMsg->hdr.status == ATT_ERR_AUTH || pMsg->hdr.status == ATT_ERR_ENC) &&
|
||||
(DmConnSecLevel((dmConnId_t) pMsg->hdr.param) == DM_SEC_LEVEL_NONE))
|
||||
{
|
||||
/* tell application to request security */
|
||||
pAppDiscCb->secRequired = TRUE;
|
||||
(*appDiscCback)((dmConnId_t) pMsg->hdr.param, APP_DISC_SEC_REQUIRED);
|
||||
}
|
||||
else
|
||||
{
|
||||
status = AttcDiscConfigCmpl((dmConnId_t) pMsg->hdr.param, pAppDiscCb->pDiscCb);
|
||||
|
||||
APP_TRACE_INFO1("AttcDiscConfigCmpl status 0x%02x", status);
|
||||
|
||||
/* if configuration complete */
|
||||
if (status != ATT_CONTINUING)
|
||||
{
|
||||
/* notify application of config success */
|
||||
(*appDiscCback)((dmConnId_t) pMsg->hdr.param, APP_DISC_CFG_CMPL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize app framework discovery.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDiscInit(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < DM_CONN_MAX; i++)
|
||||
{
|
||||
appDiscCb[i].inProgress = APP_DISC_IDLE;
|
||||
appDiscCb[i].pDiscCb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a callback function to service discovery status.
|
||||
*
|
||||
* \param cback Application service discovery callback function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDiscRegister(appDiscCback_t cback)
|
||||
{
|
||||
appDiscCback = cback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the discovery cached handle list for a given connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param listLen Length of characteristic and handle lists.
|
||||
* \param pHdlList Characteristic handle list.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDiscSetHdlList(dmConnId_t connId, uint8_t hdlListLen, uint16_t *pHdlList)
|
||||
{
|
||||
appDiscCb_t *pAppDiscCb = &appDiscCb[connId - 1];
|
||||
|
||||
pAppDiscCb->hdlListLen = hdlListLen;
|
||||
pAppDiscCb->pHdlList = pHdlList;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Service discovery or configuration procedure complete.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param status Service or configuration status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDiscComplete(dmConnId_t connId, uint8_t status)
|
||||
{
|
||||
appDiscCb_t *pAppDiscCb = &appDiscCb[connId - 1];
|
||||
appDbHdl_t hdl;
|
||||
|
||||
/* set connection as idle */
|
||||
DmConnSetIdle(connId, DM_IDLE_APP_DISC, DM_CONN_IDLE);
|
||||
|
||||
/* store status if not doing connection setup configuration */
|
||||
if (!(status == APP_DISC_CFG_CMPL && pAppDiscCb->connCfgStatus == APP_DISC_CFG_CONN_START))
|
||||
{
|
||||
pAppDiscCb->cmplStatus = status;
|
||||
}
|
||||
|
||||
/* initialize control block */
|
||||
pAppDiscCb->inProgress = APP_DISC_IDLE;
|
||||
if (pAppDiscCb->pDiscCb != NULL)
|
||||
{
|
||||
WsfBufFree(pAppDiscCb->pDiscCb);
|
||||
pAppDiscCb->pDiscCb = NULL;
|
||||
}
|
||||
|
||||
if ((hdl = AppDbGetHdl(connId)) != APP_DB_HDL_NONE)
|
||||
{
|
||||
/* Don't store configuration complete if not bonded - it must be re-done on reconnection. */
|
||||
uint8_t discComplete = AppCheckBonded(connId) ? APP_DISC_CFG_CMPL : APP_DISC_CMPL;
|
||||
|
||||
/* store discovery status if not doing connection setup configuration */
|
||||
if (!(status == APP_DISC_CFG_CMPL && pAppDiscCb->connCfgStatus == APP_DISC_CFG_CONN_START) && (status <= discComplete))
|
||||
{
|
||||
AppDbSetDiscStatus(hdl, status);
|
||||
}
|
||||
|
||||
if (pAppDiscCb->pHdlList != NULL)
|
||||
{
|
||||
/* if discovery complete store handles */
|
||||
if (status == APP_DISC_CMPL)
|
||||
{
|
||||
AppDbSetHdlList(hdl, pAppDiscCb->pHdlList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* set connection setup configuration status as complete if either discovery-initiated
|
||||
* configuration is complete or connection setup configuration is complete
|
||||
*/
|
||||
if (status == APP_DISC_CFG_CMPL)
|
||||
{
|
||||
pAppDiscCb->connCfgStatus = APP_DISC_CFG_CMPL;
|
||||
}
|
||||
|
||||
APP_TRACE_INFO2("AppDiscComplete connId:%d status:0x%02x", connId, status);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform service and characteristic discovery for a given service.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param uuidLen Length of UUID (2 or 16).
|
||||
* \param pUuid Pointer to UUID data.
|
||||
* \param listLen Length of characteristic and handle lists.
|
||||
* \param pCharList Characterisic list for discovery.
|
||||
* \param pHdlList Characteristic handle list.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDiscFindService(dmConnId_t connId, uint8_t uuidLen, uint8_t *pUuid, uint8_t listLen,
|
||||
attcDiscChar_t **pCharList, uint16_t *pHdlList)
|
||||
{
|
||||
appDiscCb_t *pAppDiscCb = &appDiscCb[connId - 1];
|
||||
|
||||
if (pAppDiscCb->pDiscCb == NULL)
|
||||
{
|
||||
pAppDiscCb->pDiscCb = WsfBufAlloc(sizeof(attcDiscCb_t));
|
||||
}
|
||||
|
||||
if (pAppDiscCb->pDiscCb != NULL)
|
||||
{
|
||||
/* set connection as busy */
|
||||
DmConnSetIdle(connId, DM_IDLE_APP_DISC, DM_CONN_BUSY);
|
||||
|
||||
pAppDiscCb->inProgress = APP_DISC_SVC_DISC_IN_PROGRESS;
|
||||
|
||||
pAppDiscCb->pDiscCb->pCharList = pCharList;
|
||||
pAppDiscCb->pDiscCb->pHdlList = pHdlList;
|
||||
pAppDiscCb->pDiscCb->charListLen = listLen;
|
||||
AttcDiscService(connId, pAppDiscCb->pDiscCb, uuidLen, pUuid);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Configure characteristics for discovered services.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
|
||||
* \param cfgListLen Length of characteristic configuration list.
|
||||
* \param pCfgList Characteristic configuration list.
|
||||
* \param hdlListLen Length of characteristic handle list.
|
||||
* \param pHdlList Characteristic handle list.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDiscConfigure(dmConnId_t connId, uint8_t status, uint8_t cfgListLen,
|
||||
attcDiscCfg_t *pCfgList, uint8_t hdlListLen, uint16_t *pHdlList)
|
||||
{
|
||||
appDiscCb_t *pAppDiscCb = &appDiscCb[connId - 1];
|
||||
uint8_t ret;
|
||||
|
||||
if (pAppDiscCb->pDiscCb == NULL)
|
||||
{
|
||||
pAppDiscCb->pDiscCb = WsfBufAlloc(sizeof(attcDiscCb_t));
|
||||
}
|
||||
|
||||
if (pAppDiscCb->pDiscCb != NULL)
|
||||
{
|
||||
/* set connection as busy */
|
||||
DmConnSetIdle(connId, DM_IDLE_APP_DISC, DM_CONN_BUSY);
|
||||
|
||||
pAppDiscCb->inProgress = APP_DISC_CFG_IN_PROGRESS;
|
||||
|
||||
if (status == APP_DISC_CFG_CONN_START)
|
||||
{
|
||||
pAppDiscCb->connCfgStatus = APP_DISC_CFG_CONN_START;
|
||||
}
|
||||
|
||||
/* start configuration */
|
||||
pAppDiscCb->pDiscCb->pCfgList = pCfgList;
|
||||
pAppDiscCb->pDiscCb->cfgListLen = cfgListLen;
|
||||
pAppDiscCb->pDiscCb->pHdlList = pHdlList;
|
||||
pAppDiscCb->pDiscCb->charListLen = hdlListLen;
|
||||
ret = AttcDiscConfigStart(connId, pAppDiscCb->pDiscCb);
|
||||
|
||||
/* nothing to configure; configuration complete */
|
||||
if (ret == ATT_SUCCESS)
|
||||
{
|
||||
(*appDiscCback)(connId, APP_DISC_CFG_CMPL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform the GATT service changed procedure. This function is called when an
|
||||
* indication is received containing the GATT service changed characteristic. This
|
||||
* function may initialize the discovery state and initiate service discovery
|
||||
* and configuration.
|
||||
*
|
||||
* \param pMsg Pointer to ATT callback event message containing received indication.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDiscServiceChanged(attEvt_t *pMsg)
|
||||
{
|
||||
appDiscCb_t *pAppDiscCb = &appDiscCb[pMsg->hdr.param - 1];
|
||||
uint16_t startHdl;
|
||||
uint16_t endHdl;
|
||||
uint8_t *p;
|
||||
uint16_t *pHdl;
|
||||
uint8_t i;
|
||||
bool_t foundHdl;
|
||||
|
||||
/* verify characteristic length */
|
||||
if (pMsg->valueLen != CH_SC_LEN)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse and verify handles */
|
||||
p = pMsg->pValue;
|
||||
BSTREAM_TO_UINT16(startHdl, p);
|
||||
BSTREAM_TO_UINT16(endHdl, p);
|
||||
if (startHdl == 0 || endHdl < startHdl)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* if we don't have any stored handles within service changed handle range, ignore */
|
||||
foundHdl = FALSE;
|
||||
if (pAppDiscCb->pHdlList != NULL)
|
||||
{
|
||||
pHdl = pAppDiscCb->pHdlList;
|
||||
for (i = pAppDiscCb->hdlListLen; i > 0; i--, pHdl++)
|
||||
{
|
||||
if (*pHdl >= startHdl && *pHdl <= endHdl)
|
||||
{
|
||||
foundHdl = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (foundHdl == FALSE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* if discovery procedure already in progress */
|
||||
if (pAppDiscCb->inProgress == APP_DISC_SVC_DISC_IN_PROGRESS)
|
||||
{
|
||||
/* ignore service changed */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prepare to restart service discovery*/
|
||||
appDiscRestartDiscovery((dmConnId_t) pMsg->hdr.param);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read peer's database hash
|
||||
*
|
||||
* \param dmConnId_t Connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDiscReadDatabaseHash(dmConnId_t connId)
|
||||
{
|
||||
appDiscCb_t *pAppDiscCb = &appDiscCb[connId - 1];
|
||||
|
||||
/* Security/bonding not used, rely on database hash for cached handles. */
|
||||
pAppDiscCb->inProgress = APP_DISC_READ_DBH_IN_PROGRESS;
|
||||
|
||||
/* Read the database hash. */
|
||||
AttcReadByTypeReq(connId, ATT_HANDLE_START, ATT_HANDLE_MAX, ATT_16_UUID_LEN,
|
||||
(uint8_t *)attGattDbhChUuid, FALSE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the handle range of the latest service discovery operation.
|
||||
*
|
||||
* May be called after receiving a \ref APP_DISC_CMPL event, but before calling AppDiscComplete().
|
||||
*
|
||||
* \param connId connection identifier.
|
||||
* \param pStartHdl output parameter for start handle.
|
||||
* \param pEndHdl output parameter for end handle.
|
||||
*
|
||||
* \return \ref TRUE if handles were set, \ref FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AppDiscGetHandleRange(dmConnId_t connId, uint16_t *pStartHdl, uint16_t *pEndHdl)
|
||||
{
|
||||
appDiscCb_t *pAppDiscCb = &appDiscCb[connId - 1];
|
||||
|
||||
if (pAppDiscCb->pDiscCb != NULL)
|
||||
{
|
||||
*pStartHdl = pAppDiscCb->pDiscCb->svcStartHdl;
|
||||
*pEndHdl = pAppDiscCb->pDiscCb->svcEndHdl;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
Vendored
+456
@@ -0,0 +1,456 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Application framework main module.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "sec_api.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "util/bstream.h"
|
||||
#include "dm_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_main.h"
|
||||
#include "app_ui.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Configuration pointer for advertising */
|
||||
appAdvCfg_t *pAppAdvCfg;
|
||||
|
||||
/*! Configuration pointer for extended and periodic advertising */
|
||||
appExtAdvCfg_t *pAppExtAdvCfg;
|
||||
|
||||
/*! Configuration pointer for slave */
|
||||
appSlaveCfg_t *pAppSlaveCfg;
|
||||
|
||||
/*! Configuration pointer for master */
|
||||
appMasterCfg_t *pAppMasterCfg;
|
||||
|
||||
/*! Configuration pointer for extended master */
|
||||
appExtMasterCfg_t *pAppExtMasterCfg;
|
||||
|
||||
/*! Configuration pointer for security */
|
||||
appSecCfg_t *pAppSecCfg;
|
||||
|
||||
/*! Configuration pointer for connection parameter update */
|
||||
appUpdateCfg_t *pAppUpdateCfg;
|
||||
|
||||
/*! Configuration pointer for discovery */
|
||||
appDiscCfg_t *pAppDiscCfg;
|
||||
|
||||
/*! Configuration pointer for application */
|
||||
appCfg_t *pAppCfg;
|
||||
|
||||
/*! Connection control block array */
|
||||
appConnCb_t appConnCb[DM_CONN_MAX];
|
||||
|
||||
/*! WSF handler ID */
|
||||
wsfHandlerId_t appHandlerId;
|
||||
|
||||
/*! Main control block */
|
||||
appCb_t appCb;
|
||||
|
||||
/*! Configuration structure for incoming request actions */
|
||||
const appReqActCfg_t appReqActCfg =
|
||||
{
|
||||
APP_ACT_ACCEPT /*! Action for the remote connection parameter request */
|
||||
};
|
||||
|
||||
/*! Configuration pointer for incoming request actions on master */
|
||||
appReqActCfg_t *pAppMasterReqActCfg = (appReqActCfg_t *) &appReqActCfg;
|
||||
|
||||
/*! Configurable pointer for incoming request actions on slave */
|
||||
appReqActCfg_t *pAppSlaveReqActCfg = (appReqActCfg_t *) &appReqActCfg;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appProcMsg(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
switch(pMsg->event)
|
||||
{
|
||||
case APP_BTN_POLL_IND:
|
||||
appUiBtnPoll();
|
||||
break;
|
||||
|
||||
case APP_UI_TIMER_IND:
|
||||
appUiTimerExpired(pMsg);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check the bond-by-LTK state of a connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return Bond-by-LTK state.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t appCheckBondByLtk(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
return appConnCb[connId - 1].bondByLtk;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Return the number of existing connections of the given role.
|
||||
*
|
||||
* \param role Connection role
|
||||
*
|
||||
* \return Number of connections.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t appNumConns(uint8_t role)
|
||||
{
|
||||
appConnCb_t *pCcb = appConnCb;
|
||||
uint8_t i, j;
|
||||
|
||||
for (i = DM_CONN_MAX, j = 0; i > 0; i--, pCcb++)
|
||||
{
|
||||
if ((pCcb->connId != DM_CONN_ID_NONE) && (DmConnRole(pCcb->connId) == role))
|
||||
{
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check the bonded state of a connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return Bonded state.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AppCheckBonded(dmConnId_t connId)
|
||||
{
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
return appConnCb[connId - 1].bonded;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief App framework handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for App.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
appHandlerId = handlerId;
|
||||
|
||||
AppDbInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for app framework.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
APP_TRACE_INFO1("App got evt %d", pMsg->event);
|
||||
|
||||
if (pMsg->event >= APP_MASTER_MSG_START)
|
||||
{
|
||||
/* pass event to master handler */
|
||||
(*appCb.masterCback)(pMsg);
|
||||
}
|
||||
else if (pMsg->event >= APP_SLAVE_MSG_START)
|
||||
{
|
||||
/* pass event to slave handler */
|
||||
(*appCb.slaveCback)(pMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
appProcMsg(pMsg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (event & APP_BTN_DOWN_EVT)
|
||||
{
|
||||
AppUiBtnPressed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a passkey request during pairing. If the passkey is to displayed, a
|
||||
* random passkey is generated and displayed. If the passkey is to be entered
|
||||
* the user is prompted to enter the passkey.
|
||||
*
|
||||
* \param pAuthReq DM authentication requested event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppHandlePasskey(dmSecAuthReqIndEvt_t *pAuthReq)
|
||||
{
|
||||
uint32_t passkey;
|
||||
uint8_t buf[SMP_PIN_LEN];
|
||||
|
||||
if (pAuthReq->display)
|
||||
{
|
||||
/* generate random passkey, limit to 6 digit max */
|
||||
SecRand((uint8_t *) &passkey, sizeof(uint32_t));
|
||||
passkey %= 1000000;
|
||||
|
||||
/* convert to byte buffer */
|
||||
buf[0] = UINT32_TO_BYTE0(passkey);
|
||||
buf[1] = UINT32_TO_BYTE1(passkey);
|
||||
buf[2] = UINT32_TO_BYTE2(passkey);
|
||||
|
||||
/* send authentication response to DM */
|
||||
DmSecAuthRsp((dmConnId_t) pAuthReq->hdr.param, SMP_PIN_LEN, buf);
|
||||
|
||||
/* display passkey */
|
||||
AppUiDisplayPasskey(passkey);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* prompt user to enter passkey */
|
||||
AppUiAction(APP_UI_PASSKEY_PROMPT);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a numeric comparison indication during pairing. The confirmation value is
|
||||
* displayed and the user is prompted to verify that the local and peer confirmation
|
||||
* values match.
|
||||
*
|
||||
* \param pCnfInd DM confirmation indication event structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppHandleNumericComparison(dmSecCnfIndEvt_t *pCnfInd)
|
||||
{
|
||||
uint32_t confirm = DmSecGetCompareValue(pCnfInd->confirm);
|
||||
|
||||
/* display confirmation value */
|
||||
AppUiDisplayConfirmValue(confirm);
|
||||
|
||||
/* TODO: Verify that local and peer confirmation values match */
|
||||
DmSecCompareRsp((dmConnId_t)pCnfInd->hdr.param, TRUE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Close the connection with the give connection identifier.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppConnClose(dmConnId_t connId)
|
||||
{
|
||||
DmConnClose(DM_CLIENT_ID_APP, connId, HCI_ERR_REMOTE_TERMINATED);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get a list of connection identifiers of open connections.
|
||||
*
|
||||
* \param pConnIdList Buffer to hold connection IDs (must be DM_CONN_MAX bytes).
|
||||
*
|
||||
* \return Number of open connections.
|
||||
*
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AppConnOpenList(dmConnId_t *pConnIdList)
|
||||
{
|
||||
appConnCb_t *pCcb = appConnCb;
|
||||
uint8_t i;
|
||||
uint8_t pos = 0;
|
||||
|
||||
memset(pConnIdList, DM_CONN_ID_NONE, DM_CONN_MAX);
|
||||
|
||||
for (i = DM_CONN_MAX; i > 0; i--, pCcb++)
|
||||
{
|
||||
if (pCcb->connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
pConnIdList[pos++] = pCcb->connId;
|
||||
}
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if a connection is open.
|
||||
*
|
||||
* \return Connection ID of open connection or DM_CONN_ID_NONE if no open connections.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
dmConnId_t AppConnIsOpen(void)
|
||||
{
|
||||
appConnCb_t *pCcb = appConnCb;
|
||||
uint8_t i;
|
||||
|
||||
for (i = DM_CONN_MAX; i > 0; i--, pCcb++)
|
||||
{
|
||||
if (pCcb->connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
return pCcb->connId;
|
||||
}
|
||||
}
|
||||
|
||||
return DM_CONN_ID_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the device database record handle associated with an open connection.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return Database record handle or APP_DB_HDL_NONE.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
appDbHdl_t AppDbGetHdl(dmConnId_t connId)
|
||||
{
|
||||
return appConnCb[connId-1].dbHdl;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Add device to resolving list.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppAddDevToResList(dmEvt_t *pMsg, dmConnId_t connId)
|
||||
{
|
||||
dmSecKey_t *pPeerKey;
|
||||
appDbHdl_t hdl = appConnCb[connId - 1].dbHdl;
|
||||
|
||||
/* if LL Privacy is supported and the peer device has distributed its IRK */
|
||||
if (HciLlPrivacySupported() && ((pPeerKey = AppDbGetKey(hdl, DM_KEY_IRK, NULL))!= NULL))
|
||||
{
|
||||
/* add peer device to resolving list. If all-zero local or peer IRK is used then
|
||||
LL will only use or accept local or peer identity address respectively. */
|
||||
DmPrivAddDevToResList(pPeerKey->irk.addrType, pPeerKey->irk.bdAddr, pPeerKey->irk.key,
|
||||
DmSecGetLocalIrk(), TRUE, pMsg->hdr.param);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear all bonding information.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \Note This API should not be used when:
|
||||
* - Advertising (other than periodic advertising) is enabled,
|
||||
* - Scanning is enabled, or
|
||||
* - (Extended) Create connection or Create Sync command is outstanding.
|
||||
*
|
||||
* Otherwise, clearing the resolving list in the Controller may fail.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppClearAllBondingInfo(void)
|
||||
{
|
||||
APP_TRACE_INFO0("Clear bonding info");
|
||||
|
||||
/* 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 clearing resolving list */
|
||||
DmPrivSetAddrResEnable(FALSE);
|
||||
}
|
||||
|
||||
/* clear resolving list */
|
||||
DmPrivClearResList();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Update privacy mode for a given peer device.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppUpdatePrivacyMode(appDbHdl_t hdl)
|
||||
{
|
||||
/* if peer device's been added to resolving list but RPA Only attribute not found on peer device */
|
||||
if ((hdl != APP_DB_HDL_NONE) && AppDbGetPeerAddedToRl(hdl) && !AppDbGetPeerRpao(hdl))
|
||||
{
|
||||
dmSecKey_t *pPeerKey = AppDbGetKey(hdl, DM_KEY_IRK, NULL);
|
||||
if (pPeerKey != NULL)
|
||||
{
|
||||
/* set device privacy mode for this peer device */
|
||||
DmPrivSetPrivacyMode(pPeerKey->irk.addrType, pPeerKey->irk.bdAddr, DM_PRIV_MODE_DEVICE);
|
||||
|
||||
/* make sure resolving list flag cleared */
|
||||
AppDbSetPeerAddedToRl(hdl, FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+196
@@ -0,0 +1,196 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Application framework main module.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef APP_MAIN_H
|
||||
#define APP_MAIN_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_db.h"
|
||||
#include "app_cfg.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! \brief No security record handle */
|
||||
#define APP_DB_HDL_NONE NULL
|
||||
|
||||
/*! \brief Scanning mode types */
|
||||
#define APP_SCAN_MODE_LEG 0 /*!< Legacy scanning mode */
|
||||
#define APP_SCAN_MODE_EXT 1 /*!< Extended scanning mode */
|
||||
#define APP_SCAN_MODE_NONE 255 /*!< Unknown scanning mode */
|
||||
|
||||
/*! \brief App WSF handler event bitmasks */
|
||||
#define APP_BTN_DOWN_EVT 0x10 /*!< Button pressed down event */
|
||||
|
||||
/*! \brief App WSF message event starting values */
|
||||
#define APP_MSG_START 0x00
|
||||
#define APP_SLAVE_MSG_START 0x10
|
||||
#define APP_MASTER_MSG_START 0x20
|
||||
|
||||
/*! \brief App WSF message event enumeration */
|
||||
enum
|
||||
{
|
||||
APP_BTN_POLL_IND = APP_MSG_START, /*! Button poll timer expired */
|
||||
APP_UI_TIMER_IND /*! UI timer expired */
|
||||
};
|
||||
|
||||
/*! App slave WSF message event enumeration */
|
||||
enum
|
||||
{
|
||||
APP_CONN_UPDATE_TIMEOUT_IND = APP_SLAVE_MSG_START /*! Connection parameter update timer expired */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! \brief Message handling function type */
|
||||
typedef void (*appMsgHandler_t)(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/*! \brief Advertising callback function type */
|
||||
typedef void(*appAdvCback_t)(dmEvt_t *pMsg);
|
||||
|
||||
/*! \brief Master control block */
|
||||
typedef struct
|
||||
{
|
||||
appDevInfo_t scanResults[APP_SCAN_RESULT_MAX]; /*! Scan result storage */
|
||||
uint8_t numScanResults; /*! Number of scan results */
|
||||
uint8_t idx; /*! Index of address being resolved in scan result list */
|
||||
appDbHdl_t dbHdl; /*! Database record handle for address being resolved */
|
||||
bool_t inProgress; /*! TRUE if address resolution is in progress */
|
||||
uint8_t scanMode; /*! Scan and connect mode in use */
|
||||
} appMasterCb_t;
|
||||
|
||||
/*! Slave control block */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t *pAdvData[DM_NUM_ADV_SETS][APP_NUM_DATA_LOCATIONS]; /*! Advertising data pointers */
|
||||
uint16_t advDataLen[DM_NUM_ADV_SETS][APP_NUM_DATA_LOCATIONS]; /*! Advertising data lengths */
|
||||
uint16_t advDataBufLen[DM_NUM_ADV_SETS][APP_NUM_DATA_LOCATIONS]; /*! Length of advertising data buffer maintained by Application */
|
||||
uint16_t advDataOffset[DM_NUM_ADV_SETS][APP_NUM_DATA_LOCATIONS]; /*! Advertising data offsets */
|
||||
uint16_t maxAdvDataLen[DM_NUM_ADV_SETS]; /*! Maximum advertising data length supported by Controller */
|
||||
bool_t bondable; /*! TRUE if in bondable mode */
|
||||
bool_t advDataSynced[DM_NUM_ADV_SETS]; /*! TRUE if advertising/scan data is synced */
|
||||
uint8_t advState[DM_NUM_ADV_SETS]; /*! Advertising state */
|
||||
uint8_t advType[DM_NUM_ADV_SETS]; /*! Advertising type */
|
||||
bool_t advTypeChanged[DM_NUM_ADV_SETS]; /*! TRUE if advertising type is changed */
|
||||
uint8_t discMode; /*! Discoverable/connectable mode */
|
||||
bdAddr_t peerAddr[DM_NUM_ADV_SETS]; /*! Peer address */
|
||||
uint8_t peerAddrType[DM_NUM_ADV_SETS]; /*! Peer address type */
|
||||
bool_t findLtk; /*! TRUE if LTK request received while resolving address */
|
||||
appDbHdl_t dbHdl; /*! Database record handle for address being resolved */
|
||||
bool_t inProgress; /*! TRUE if address resolution is in progress */
|
||||
bool_t advDirected; /*! TRUE if legacy directed advertising is in progress */
|
||||
appAdvCback_t advStopCback; /*! Advertising stopped callback */
|
||||
appAdvCback_t advRestartCback; /*! Advertising restart callback */
|
||||
} appSlaveCb_t;
|
||||
|
||||
/*! Connection control block */
|
||||
typedef struct
|
||||
{
|
||||
appDbHdl_t dbHdl; /*! Device database handle */
|
||||
dmConnId_t connId; /*! Connection ID */
|
||||
bool_t bonded; /*! TRUE if bonded with peer device */
|
||||
bool_t bondByLtk; /*! TRUE if bonded state being determined by LTK */
|
||||
bool_t bondByPairing; /*! TRUE if bonded state being determined by pairing */
|
||||
bool_t initiatingSec; /*! TRUE if initiating security */
|
||||
bool_t setConnectable; /*! TRUE if switching to connectable mode */
|
||||
bool_t connWasIdle; /*! TRUE if connection was idle at last check */
|
||||
uint8_t rcvdKeys; /*! Bitmask of keys received during pairing */
|
||||
uint8_t attempts; /*! Connection parameter update attempts */
|
||||
uint8_t updateState; /*! Connection Update State */
|
||||
wsfTimer_t updateTimer; /*! Connection parameter update timer */
|
||||
} appConnCb_t;
|
||||
|
||||
/*! Main control block */
|
||||
typedef struct
|
||||
{
|
||||
appMsgHandler_t slaveCback; /*! Slave message handler callback */
|
||||
appMsgHandler_t masterCback; /*! Slave message handler callback */
|
||||
} appCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! \brief Connection control block array */
|
||||
extern appConnCb_t appConnCb[DM_CONN_MAX];
|
||||
|
||||
/*! \brief WSF handler ID */
|
||||
extern wsfHandlerId_t appHandlerId;
|
||||
|
||||
/*! \brief Main control block */
|
||||
extern appCb_t appCb;
|
||||
|
||||
/*! \brief Master control block */
|
||||
extern appMasterCb_t appMasterCb;
|
||||
|
||||
/*! \brief Slave control block */
|
||||
extern appSlaveCb_t appSlaveCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
bool_t AppCheckBonded(dmConnId_t connId);
|
||||
bool_t appCheckBondByLtk(dmConnId_t connId);
|
||||
uint8_t appNumConns(uint8_t role);
|
||||
|
||||
void appUiBtnPoll(void);
|
||||
void appUiTimerExpired(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/* slave utility functions */
|
||||
extern void appSlaveResetAdvDataOffset(uint8_t advHandle);
|
||||
extern void appAdvStart(uint8_t numSets, uint8_t *pAdvHandles, uint16_t *pInterval, uint16_t *pDuration,
|
||||
uint8_t *pMaxEaEvents, bool_t cfgAdvParam);
|
||||
extern void appAdvSetData(uint8_t advHandle, uint8_t location, uint16_t len, uint8_t *pData, uint16_t bufLen,
|
||||
uint16_t maxLen);
|
||||
extern void appSlaveAdvStart(uint8_t numSets, uint8_t *pAdvHandles, uint16_t *pInterval, uint16_t *pDuration,
|
||||
uint8_t *pMaxEaEvents, bool_t cfgAdvParam, uint8_t mode);
|
||||
extern void appAdvStop(uint8_t numSets, uint8_t *pAdvHandles);
|
||||
extern bool_t appAdvSetAdValue(uint8_t advHandle, uint8_t location, uint8_t adType, uint8_t len,
|
||||
uint8_t *pValue);
|
||||
extern void appSetAdvType(uint8_t advHandle, uint8_t advType, uint16_t interval, uint16_t duration,
|
||||
uint8_t maxEaEvents, bool_t cfgAdvParam);
|
||||
extern dmConnId_t appConnAccept(uint8_t advHandle, uint8_t advType, uint16_t interval, uint16_t duration,
|
||||
uint8_t maxEaEvents, uint8_t addrType, uint8_t *pAddr, appDbHdl_t dbHdl,
|
||||
bool_t cfgAdvData);
|
||||
|
||||
/* master utility functions */
|
||||
extern dmConnId_t appConnOpen(uint8_t initPhys, uint8_t addrType, uint8_t *pAddr, appDbHdl_t dbHdl);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* APP_MAIN_H */
|
||||
Vendored
+921
@@ -0,0 +1,921 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Application framework module for master.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "svc_core.h"
|
||||
#include "app_api.h"
|
||||
#include "app_main.h"
|
||||
#include "app_cfg.h"
|
||||
#include <stdbool.h>
|
||||
#include "hci_drv_apollo.h"
|
||||
#include "dm_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Constant used in the address type indicating value not present */
|
||||
#define APP_ADDR_NONE 0xFF
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Master control block */
|
||||
appMasterCb_t appMasterCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate security
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
* \param initiatePairing TRUE to initiate pairing.
|
||||
* \param pCb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appMasterInitiateSec(dmConnId_t connId, bool_t initiatePairing, appConnCb_t *pCb)
|
||||
{
|
||||
uint8_t rKeyDist;
|
||||
uint8_t secLevel;
|
||||
dmSecKey_t *pKey;
|
||||
|
||||
/* if we have an LTK for peer device */
|
||||
if ((pCb->dbHdl != APP_DB_HDL_NONE) &&
|
||||
((pKey = AppDbGetKey(pCb->dbHdl, DM_KEY_PEER_LTK, &secLevel)) != NULL))
|
||||
{
|
||||
pCb->bondByLtk = TRUE;
|
||||
pCb->initiatingSec = TRUE;
|
||||
|
||||
/* encrypt with LTK */
|
||||
DmSecEncryptReq(connId, secLevel, &pKey->ltk);
|
||||
}
|
||||
/* no key; initiate pairing only if requested */
|
||||
else if (initiatePairing)
|
||||
{
|
||||
/* store bonding state */
|
||||
pCb->bondByPairing = (pAppSecCfg->auth & DM_AUTH_BOND_FLAG) == DM_AUTH_BOND_FLAG;
|
||||
|
||||
/* if bonding and no device record */
|
||||
if (pCb->bondByPairing && pCb->dbHdl == APP_DB_HDL_NONE)
|
||||
{
|
||||
/* create a device record if none exists */
|
||||
pCb->dbHdl = AppDbNewRecord(DmConnPeerAddrType(connId), DmConnPeerAddr(connId), TRUE);
|
||||
}
|
||||
|
||||
/* initialize stored keys */
|
||||
pCb->rcvdKeys = 0;
|
||||
|
||||
/* if peer is using random address request IRK */
|
||||
rKeyDist = pAppSecCfg->rKeyDist;
|
||||
if (DmConnPeerAddrType(connId) == DM_ADDR_RANDOM)
|
||||
{
|
||||
rKeyDist |= DM_KEY_DIST_IRK;
|
||||
}
|
||||
|
||||
pCb->initiatingSec = TRUE;
|
||||
|
||||
/* initiate pairing */
|
||||
DmSecPairReq(connId, pAppSecCfg->oob, pAppSecCfg->auth, pAppSecCfg->iKeyDist, rKeyDist);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Clear all scan results.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appScanResultsClear(void)
|
||||
{
|
||||
uint8_t i;
|
||||
appDevInfo_t *pDev = appMasterCb.scanResults;
|
||||
|
||||
appMasterCb.numScanResults = 0;
|
||||
for (i = APP_SCAN_RESULT_MAX; i > 0; i--, pDev++)
|
||||
{
|
||||
pDev->addrType = APP_ADDR_NONE;
|
||||
}
|
||||
|
||||
/* end address resolution */
|
||||
appMasterCb.inProgress = FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Add a scan report to the scan result list.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appScanResultAdd(dmEvt_t *pMsg)
|
||||
{
|
||||
uint8_t i;
|
||||
appDevInfo_t *pDev = appMasterCb.scanResults;
|
||||
|
||||
/* see if device is in list already */
|
||||
for (i = 0; i < APP_SCAN_RESULT_MAX; i++, pDev++)
|
||||
{
|
||||
/* if address matches list entry */
|
||||
if ((pDev->addrType == pMsg->scanReport.addrType) &&
|
||||
BdaCmp(pDev->addr, pMsg->scanReport.addr))
|
||||
{
|
||||
/* device already exists in list; we are done */
|
||||
break;
|
||||
}
|
||||
/* if entry is free end then of list has been reached */
|
||||
else if (pDev->addrType == APP_ADDR_NONE)
|
||||
{
|
||||
/* add device to list */
|
||||
pDev->addrType = pMsg->scanReport.addrType;
|
||||
BdaCpy(pDev->addr, pMsg->scanReport.addr);
|
||||
pDev->directAddrType = pMsg->scanReport.directAddrType;
|
||||
BdaCpy(pDev->directAddr, pMsg->scanReport.directAddr);
|
||||
appMasterCb.numScanResults++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find a scan report in the scan result list.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return Index of result in scan result list. APP_SCAN_RESULT_MAX, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t appScanResultFind(dmEvt_t *pMsg)
|
||||
{
|
||||
uint8_t i;
|
||||
appDevInfo_t *pDev = appMasterCb.scanResults;
|
||||
|
||||
/* see if device is in list already */
|
||||
for (i = 0; i < APP_SCAN_RESULT_MAX; i++, pDev++)
|
||||
{
|
||||
/* if address matches list entry */
|
||||
if ((pDev->addrType == pMsg->scanReport.addrType) &&
|
||||
BdaCmp(pDev->addr, pMsg->scanReport.addr))
|
||||
{
|
||||
/* device already exists in list; we are done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a DM_SCAN_START_IND event.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appMasterScanStart(dmEvt_t *pMsg)
|
||||
{
|
||||
if (pMsg->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
/* clear current scan results */
|
||||
appScanResultsClear();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a DM_SCAN_STOP_IND event.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appMasterScanStop(dmEvt_t *pMsg)
|
||||
{
|
||||
if (pMsg->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
APP_TRACE_INFO1("Scan results: %d", AppScanGetNumResults());
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a DM_SCAN_REPORT_IND event.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appMasterScanReport(dmEvt_t *pMsg)
|
||||
{
|
||||
/* add to scan result list */
|
||||
appScanResultAdd(pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a DM_CONN_OPEN_IND event.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
* \param pCb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appMasterConnOpen(dmEvt_t *pMsg, appConnCb_t *pCb)
|
||||
{
|
||||
DmReadRemoteFeatures((dmConnId_t) pMsg->hdr.param);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a DM_CONN_CLOSE_IND event.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
* \param pCb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appMasterConnClose(dmEvt_t *pMsg, appConnCb_t *pCb)
|
||||
{
|
||||
/* update privacy mode for peer device */
|
||||
AppUpdatePrivacyMode(pCb->dbHdl);
|
||||
|
||||
/* clear connection ID */
|
||||
pCb->connId = DM_CONN_ID_NONE;
|
||||
|
||||
/* cancel any address resolution in progress */
|
||||
appMasterCb.inProgress = FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform master security procedures on connection open.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
* \param pCb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appMasterSecConnOpen(dmEvt_t *pMsg, appConnCb_t *pCb)
|
||||
{
|
||||
/* initialize state variables */
|
||||
pCb->bonded = FALSE;
|
||||
pCb->bondByLtk = FALSE;
|
||||
pCb->bondByPairing = FALSE;
|
||||
pCb->initiatingSec = FALSE;
|
||||
|
||||
/* if master initiates security on connection open */
|
||||
appMasterInitiateSec((dmConnId_t) pMsg->hdr.param, pAppSecCfg->initiateSec, pCb);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform security procedures on connection close.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
* \param pCb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appMasterSecConnClose(dmEvt_t *pMsg, appConnCb_t *pCb)
|
||||
{
|
||||
/* if a device record was created check if it is valid */
|
||||
if (pCb->dbHdl != APP_DB_HDL_NONE)
|
||||
{
|
||||
AppDbCheckValidRecord(pCb->dbHdl);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a slave security request.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
* \param pCb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appMasterSecSlaveReq(dmEvt_t *pMsg, appConnCb_t *pCb)
|
||||
{
|
||||
/* if master is not initiating security and not already secure */
|
||||
if (!pAppSecCfg->initiateSec && !pCb->initiatingSec &&
|
||||
(DmConnSecLevel((dmConnId_t) pMsg->hdr.param) == DM_SEC_LEVEL_NONE))
|
||||
{
|
||||
appMasterInitiateSec((dmConnId_t) pMsg->hdr.param, TRUE, pCb);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle set address resolution enable indication.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appPrivSetAddrResEnableInd(dmEvt_t *pMsg)
|
||||
{
|
||||
if (pMsg->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
SvcCoreGapCentAddrResUpdate(DmLlPrivEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle add device to resolving list indication.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
* \param pCb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appPrivAddDevToResListInd(dmEvt_t *pMsg, appConnCb_t *pCb)
|
||||
{
|
||||
if ((pMsg->hdr.status == HCI_SUCCESS) && (pCb->dbHdl != APP_DB_HDL_NONE))
|
||||
{
|
||||
/* peer device's been added to resolving list */
|
||||
AppDbSetPeerAddedToRl(pCb->dbHdl, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle remove device from resolving list indication.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
* \param pCb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appPrivRemDevFromResListInd(dmEvt_t *pMsg, appConnCb_t *pCb)
|
||||
{
|
||||
if ((pMsg->hdr.status == HCI_SUCCESS) && (pCb->dbHdl != APP_DB_HDL_NONE))
|
||||
{
|
||||
/* peer device's been removed from resolving list */
|
||||
AppDbSetPeerAddedToRl(pCb->dbHdl, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Store security key.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
* \param pCb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appMasterSecStoreKey(dmEvt_t *pMsg, appConnCb_t *pCb)
|
||||
{
|
||||
if (pCb->bondByPairing && pCb->dbHdl != APP_DB_HDL_NONE)
|
||||
{
|
||||
/* key was received */
|
||||
pCb->rcvdKeys |= pMsg->keyInd.type;
|
||||
|
||||
/* store key in record */
|
||||
AppDbSetKey(pCb->dbHdl, &pMsg->keyInd);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle pairing complete.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
* \param pCb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appMasterSecPairCmpl(dmEvt_t *pMsg, appConnCb_t *pCb)
|
||||
{
|
||||
/* if bonding */
|
||||
if (pMsg->pairCmpl.auth & DM_AUTH_BOND_FLAG)
|
||||
{
|
||||
/* set bonded state */
|
||||
pCb->bonded = TRUE;
|
||||
|
||||
/* validate record and received keys */
|
||||
if (pCb->dbHdl != APP_DB_HDL_NONE)
|
||||
{
|
||||
AppDbValidateRecord(pCb->dbHdl, pCb->rcvdKeys);
|
||||
}
|
||||
|
||||
/* if bonded, add device to resolving list */
|
||||
if (pCb->dbHdl != APP_DB_HDL_NONE)
|
||||
{
|
||||
AppAddDevToResList(pMsg, pCb->connId);
|
||||
}
|
||||
}
|
||||
|
||||
pCb->initiatingSec = FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle pairing failed
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
* \param pCb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appMasterSecPairFailed(dmEvt_t *pMsg, appConnCb_t *pCb)
|
||||
{
|
||||
pCb->initiatingSec = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle encryption indication
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
* \param pCb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appMasterSecEncryptInd(dmEvt_t *pMsg, appConnCb_t *pCb)
|
||||
{
|
||||
/* check if bonding state should be set */
|
||||
if (pCb->bondByLtk && pMsg->encryptInd.usingLtk)
|
||||
{
|
||||
pCb->bonded = TRUE;
|
||||
pCb->bondByLtk = FALSE;
|
||||
pCb->initiatingSec = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process app framework messages for a master.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void appMasterProcMsg(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
switch(pMsg->event)
|
||||
{
|
||||
case APP_CONN_UPDATE_TIMEOUT_IND:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a received privacy resolved address indication.
|
||||
*
|
||||
* \param pMsg Pointer to DM message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appMasterResolvedAddrInd(dmEvt_t *pMsg)
|
||||
{
|
||||
appDevInfo_t *pDev;
|
||||
dmSecKey_t *pPeerKey;
|
||||
|
||||
/* if address resolution is not in progress */
|
||||
if (!appMasterCb.inProgress)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* get device record */
|
||||
pDev = &appMasterCb.scanResults[appMasterCb.idx];
|
||||
|
||||
/* if RPA resolved */
|
||||
if (pMsg->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
/* if resolved advertising was directed with an RPA initiator address */
|
||||
if ((pMsg->hdr.param == APP_RESOLVE_ADV_RPA) && DM_RAND_ADDR_RPA(pDev->directAddr, pDev->directAddrType))
|
||||
{
|
||||
/* resolve initiator's RPA to see if directed advertisement was addressed to us */
|
||||
DmPrivResolveAddr(pDev->directAddr, DmSecGetLocalIrk(), APP_RESOLVE_DIRECT_RPA);
|
||||
|
||||
/* not done yet */
|
||||
return;
|
||||
}
|
||||
|
||||
/* stop scanning */
|
||||
AppScanStop();
|
||||
|
||||
/* connect to peer device using its advertising address */
|
||||
AppConnOpen(pDev->addrType, pDev->addr, appMasterCb.dbHdl);
|
||||
}
|
||||
/* if RPA did not resolve and there're more bonded records to go through */
|
||||
else if ((pMsg->hdr.status == HCI_ERR_AUTH_FAILURE) && (appMasterCb.dbHdl != APP_DB_HDL_NONE))
|
||||
{
|
||||
/* get the next database record */
|
||||
appMasterCb.dbHdl = AppDbGetNextRecord(appMasterCb.dbHdl);
|
||||
|
||||
/* if there's another bond record */
|
||||
if ((appMasterCb.dbHdl != APP_DB_HDL_NONE) &&
|
||||
((pPeerKey = AppDbGetKey(appMasterCb.dbHdl, DM_KEY_IRK, NULL)) != NULL))
|
||||
{
|
||||
/* resolve RPA using the next stored IRK */
|
||||
DmPrivResolveAddr(pDev->addr, pPeerKey->irk.key, APP_RESOLVE_ADV_RPA);
|
||||
|
||||
/* not done yet */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* done with this address resolution */
|
||||
appMasterCb.inProgress = FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a DM_REM_CONN_PARAM_REQ_IND event.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
* \param pCb Connection control block.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appMasterRemoteConnParamReq(dmEvt_t *pMsg, appConnCb_t *pCb)
|
||||
{
|
||||
/* if configured to accept the remote connection parameter request */
|
||||
if (pAppMasterReqActCfg->remConnParamReqAct == APP_ACT_ACCEPT)
|
||||
{
|
||||
hciConnSpec_t connSpec;
|
||||
|
||||
connSpec.connIntervalMin = pMsg->remConnParamReq.intervalMin;
|
||||
connSpec.connIntervalMax = pMsg->remConnParamReq.intervalMax;
|
||||
connSpec.connLatency = pMsg->remConnParamReq.latency;
|
||||
connSpec.supTimeout = pMsg->remConnParamReq.timeout;
|
||||
connSpec.minCeLen = connSpec.maxCeLen = 0;
|
||||
|
||||
/* accept the remote device's request to change connection parameters */
|
||||
DmRemoteConnParamReqReply(pCb->connId, &connSpec);
|
||||
}
|
||||
/* if configured to reject the remote connection parameter request */
|
||||
else if (pAppMasterReqActCfg->remConnParamReqAct == APP_ACT_REJECT)
|
||||
{
|
||||
/* reject the remote device's request to change connection parameters */
|
||||
DmRemoteConnParamReqNegReply(pCb->connId, HCI_ERR_UNSUP_FEAT);
|
||||
}
|
||||
/* else - app will handle the remote connection parameter request */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize app framework master.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppMasterInit(void)
|
||||
{
|
||||
appMasterCb.inProgress = FALSE;
|
||||
|
||||
/* initialize scan mode */
|
||||
appMasterCb.scanMode = APP_SCAN_MODE_NONE;
|
||||
|
||||
/* set up callback from main */
|
||||
appCb.masterCback = appMasterProcMsg;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process connection-related DM messages for a master. This function should be called
|
||||
* from the application's event handler.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppMasterProcDmMsg(dmEvt_t *pMsg)
|
||||
{
|
||||
appConnCb_t *pCb = NULL;
|
||||
|
||||
/* look up app connection control block from DM connection ID */
|
||||
if (pMsg->hdr.event == DM_CONN_OPEN_IND ||
|
||||
pMsg->hdr.event == DM_CONN_CLOSE_IND ||
|
||||
pMsg->hdr.event == DM_REM_CONN_PARAM_REQ_IND)
|
||||
{
|
||||
pCb = &appConnCb[pMsg->hdr.param - 1];
|
||||
}
|
||||
|
||||
switch(pMsg->hdr.event)
|
||||
{
|
||||
case DM_RESET_CMPL_IND:
|
||||
/* reset scan mode */
|
||||
appMasterCb.scanMode = APP_SCAN_MODE_NONE;
|
||||
break;
|
||||
|
||||
case DM_SCAN_START_IND:
|
||||
appMasterScanStart(pMsg);
|
||||
break;
|
||||
|
||||
case DM_SCAN_STOP_IND:
|
||||
appMasterScanStop(pMsg);
|
||||
break;
|
||||
|
||||
case DM_SCAN_REPORT_IND:
|
||||
appMasterScanReport(pMsg);
|
||||
break;
|
||||
|
||||
case DM_CONN_OPEN_IND:
|
||||
appMasterConnOpen(pMsg, pCb);
|
||||
break;
|
||||
|
||||
case DM_CONN_CLOSE_IND:
|
||||
appMasterConnClose(pMsg, pCb);
|
||||
break;
|
||||
|
||||
case DM_PRIV_RESOLVED_ADDR_IND:
|
||||
appMasterResolvedAddrInd(pMsg);
|
||||
break;
|
||||
|
||||
case DM_REM_CONN_PARAM_REQ_IND:
|
||||
appMasterRemoteConnParamReq(pMsg, pCb);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process security-related DM messages for a master. This function should be called
|
||||
* from the application's event handler.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppMasterSecProcDmMsg(dmEvt_t *pMsg)
|
||||
{
|
||||
appConnCb_t *pCb;
|
||||
|
||||
/* look up app connection control block from DM connection ID */
|
||||
pCb = &appConnCb[pMsg->hdr.param - 1];
|
||||
|
||||
switch(pMsg->hdr.event)
|
||||
{
|
||||
case DM_CONN_OPEN_IND:
|
||||
appMasterSecConnOpen(pMsg, pCb);
|
||||
break;
|
||||
|
||||
case DM_CONN_CLOSE_IND:
|
||||
appMasterSecConnClose(pMsg, pCb);
|
||||
break;
|
||||
|
||||
case DM_SEC_PAIR_CMPL_IND:
|
||||
appMasterSecPairCmpl(pMsg, pCb);
|
||||
break;
|
||||
|
||||
case DM_SEC_PAIR_FAIL_IND:
|
||||
appMasterSecPairFailed(pMsg, pCb);
|
||||
break;
|
||||
|
||||
case DM_SEC_ENCRYPT_IND:
|
||||
appMasterSecEncryptInd(pMsg, pCb);
|
||||
break;
|
||||
|
||||
case DM_SEC_ENCRYPT_FAIL_IND:
|
||||
break;
|
||||
|
||||
case DM_SEC_KEY_IND:
|
||||
appMasterSecStoreKey(pMsg, pCb);
|
||||
break;
|
||||
|
||||
case DM_SEC_SLAVE_REQ_IND:
|
||||
appMasterSecSlaveReq(pMsg, pCb);
|
||||
break;
|
||||
|
||||
case DM_PRIV_SET_ADDR_RES_ENABLE_IND:
|
||||
appPrivSetAddrResEnableInd(pMsg);
|
||||
break;
|
||||
|
||||
case DM_PRIV_ADD_DEV_TO_RES_LIST_IND:
|
||||
appPrivAddDevToResListInd(pMsg, pCb);
|
||||
break;
|
||||
|
||||
case DM_PRIV_REM_DEV_FROM_RES_LIST_IND:
|
||||
appPrivRemDevFromResListInd(pMsg, pCb);
|
||||
break;
|
||||
|
||||
case DM_HW_ERROR_IND:
|
||||
HciDrvRadioBoot(0);
|
||||
DmDevReset();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get a stored scan result from the scan result list.
|
||||
*
|
||||
* \param idx Index of result in scan result list.
|
||||
*
|
||||
* \return Pointer to scan result device info or NULL if index contains no result.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
appDevInfo_t *AppScanGetResult(uint8_t idx)
|
||||
{
|
||||
if (idx < APP_SCAN_RESULT_MAX && appMasterCb.scanResults[idx].addrType != APP_ADDR_NONE)
|
||||
{
|
||||
return &appMasterCb.scanResults[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the number of stored scan results.
|
||||
*
|
||||
* \return Number of stored scan results.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AppScanGetNumResults(void)
|
||||
{
|
||||
return appMasterCb.numScanResults;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Open a connection to a peer device with the given initiator PHYs, and address.
|
||||
*
|
||||
* \param initPhys Initiator PHYs.
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
* \param dbHdl Device database handle.
|
||||
*
|
||||
* \return Connection identifier.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
dmConnId_t appConnOpen(uint8_t initPhys, uint8_t addrType, uint8_t *pAddr, appDbHdl_t dbHdl)
|
||||
{
|
||||
dmConnId_t connId;
|
||||
appConnCb_t *pCb;
|
||||
|
||||
/* open connection */
|
||||
connId = DmConnOpen(DM_CLIENT_ID_APP, initPhys, addrType, pAddr);
|
||||
|
||||
if (connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
/* set up conn. control block */
|
||||
pCb = &appConnCb[connId - 1];
|
||||
|
||||
pCb->connId = connId;
|
||||
|
||||
/* if database record handle is in use */
|
||||
if ((dbHdl != APP_DB_HDL_NONE) && AppDbRecordInUse(dbHdl))
|
||||
{
|
||||
pCb->dbHdl = dbHdl;
|
||||
}
|
||||
else
|
||||
{
|
||||
pCb->dbHdl = AppDbFindByAddr(addrType, pAddr);
|
||||
}
|
||||
}
|
||||
|
||||
return connId;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initiate security as a master device. If there is a stored encryption key
|
||||
* for the peer device this function will initiate encryption, otherwise it will
|
||||
* initiate pairing.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppMasterSecurityReq(dmConnId_t connId)
|
||||
{
|
||||
appConnCb_t *pCb;
|
||||
|
||||
WSF_ASSERT((connId > 0) && (connId <= DM_CONN_MAX));
|
||||
|
||||
/* look up app connection control block from DM connection ID */
|
||||
pCb = &appConnCb[connId - 1];
|
||||
|
||||
/* if master is not initiating security and not already secure */
|
||||
if (!pAppSecCfg->initiateSec && !pCb->initiatingSec &&
|
||||
(DmConnSecLevel(connId) == DM_SEC_LEVEL_NONE))
|
||||
{
|
||||
appMasterInitiateSec(connId, TRUE, pCb);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Resolve the advertiser's RPA (AdvA) or the initiator's RPA (InitA) of a directed
|
||||
* advertisement report. If the addresses resolved then a connection will be initiated.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
* \param dbHdl Database record handle.
|
||||
* \param resolveType Which address in advertising report to be resolved.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppMasterResolveAddr(dmEvt_t *pMsg, appDbHdl_t dbHdl, uint8_t resolveType)
|
||||
{
|
||||
uint8_t idx;
|
||||
|
||||
/* if address resolution's in progress or scan record is not found */
|
||||
if ((appMasterCb.inProgress) || ((idx = appScanResultFind(pMsg)) >= APP_SCAN_RESULT_MAX))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* if asked to resolve direct address */
|
||||
if (resolveType == APP_RESOLVE_DIRECT_RPA)
|
||||
{
|
||||
/* resolve initiator's RPA to see if the directed advertisement is addressed to us */
|
||||
DmPrivResolveAddr(pMsg->scanReport.directAddr, DmSecGetLocalIrk(), APP_RESOLVE_DIRECT_RPA);
|
||||
|
||||
/* store scan record index and database record handle for later */
|
||||
appMasterCb.idx = idx;
|
||||
appMasterCb.dbHdl = dbHdl;
|
||||
appMasterCb.inProgress = TRUE;
|
||||
}
|
||||
/* if asked to resolve advertiser's address */
|
||||
else if (resolveType == APP_RESOLVE_ADV_RPA)
|
||||
{
|
||||
dmSecKey_t *pPeerKey;
|
||||
appDbHdl_t hdl = AppDbGetNextRecord(APP_DB_HDL_NONE);
|
||||
|
||||
/* if we have any bond records */
|
||||
if ((hdl != APP_DB_HDL_NONE) && ((pPeerKey = AppDbGetKey(hdl, DM_KEY_IRK, NULL)) != NULL))
|
||||
{
|
||||
/* resolve advertiser's RPA to see if we already have a bond with this device */
|
||||
DmPrivResolveAddr(pMsg->scanReport.addr, pPeerKey->irk.key, APP_RESOLVE_ADV_RPA);
|
||||
|
||||
/* store scan record index and database record handle for later */
|
||||
appMasterCb.idx = idx;
|
||||
appMasterCb.dbHdl = hdl;
|
||||
appMasterCb.inProgress = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+190
@@ -0,0 +1,190 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Application framework module for extended master.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if current scanning mode is extended scanning.
|
||||
*
|
||||
* \return TRUE if extended scanning mode. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t appMasterExtScanMode(void)
|
||||
{
|
||||
/* if DM extended scanning */
|
||||
if (DmScanModeExt())
|
||||
{
|
||||
/* if first time since last power-on or reset */
|
||||
if (appMasterCb.scanMode == APP_SCAN_MODE_NONE)
|
||||
{
|
||||
/* set scanning mode to extended */
|
||||
appMasterCb.scanMode = APP_SCAN_MODE_EXT;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (appMasterCb.scanMode == APP_SCAN_MODE_EXT)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
APP_TRACE_WARN0("Invalid DM scanning mode; mode configured as legacy");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start scanning. A scan is performed using the given scanner PHYs, discoverability
|
||||
* mode, scan type, duration, and period.
|
||||
*
|
||||
* \param scanPhys Scanner PHYs.
|
||||
* \param mode Discoverability mode.
|
||||
* \param pScanType Scan type array.
|
||||
* \param duration The scan duration, in milliseconds. If set to zero or both duration and
|
||||
* period set to non-zero, scanning will continue until DmExtScanStop() is
|
||||
* called.
|
||||
* \param period The scan period, in 1.28 sec units. Set to zero to disable periodic scanning.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppExtScanStart(uint8_t scanPhys, uint8_t mode, const uint8_t *pScanType, uint16_t duration,
|
||||
uint16_t period)
|
||||
{
|
||||
uint8_t i; /* scanPhy bit position */
|
||||
uint8_t idx; /* array index */
|
||||
uint16_t scanInterval[DM_NUM_PHYS];
|
||||
uint16_t scanWindow[DM_NUM_PHYS];
|
||||
uint8_t scanType[DM_NUM_PHYS];
|
||||
|
||||
if (appMasterExtScanMode())
|
||||
{
|
||||
for (i = 0, idx = 0; (i < 8) && (idx < DM_NUM_PHYS); i++)
|
||||
{
|
||||
if (scanPhys & (1 << i))
|
||||
{
|
||||
scanInterval[idx] = pAppExtMasterCfg->scanInterval[i];
|
||||
scanWindow[idx] = pAppExtMasterCfg->scanWindow[i];
|
||||
scanType[idx] = pScanType[i];
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
DmScanSetInterval(scanPhys, scanInterval, scanWindow);
|
||||
|
||||
DmScanStart(scanPhys, mode, scanType, TRUE, duration, period);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop scanning.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppExtScanStop(void)
|
||||
{
|
||||
if (appMasterExtScanMode())
|
||||
{
|
||||
/* stop address resolution */
|
||||
appMasterCb.inProgress = FALSE;
|
||||
|
||||
DmScanStop();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Synchronize with periodic advertising from the given advertiser, and start receiving
|
||||
* periodic advertising packets.
|
||||
*
|
||||
* \param advSid Advertising SID.
|
||||
* \param advAddrType Advertiser address type.
|
||||
* \param pAdvAddr Advertiser address.
|
||||
* \param skip Number of periodic advertising packets that can be skipped after
|
||||
* successful receive.
|
||||
* \param syncTimeout Synchronization timeout.
|
||||
*
|
||||
* \return Sync identifier.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
dmSyncId_t AppSyncStart(uint8_t advSid, uint8_t advAddrType, const uint8_t *pAdvAddr, uint16_t skip,
|
||||
uint16_t syncTimeout)
|
||||
{
|
||||
if (appMasterExtScanMode())
|
||||
{
|
||||
return DmSyncStart(advSid, advAddrType, pAdvAddr, skip, syncTimeout);
|
||||
}
|
||||
|
||||
/* wrong scan mode */
|
||||
return DM_SYNC_ID_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop reception of the periodic advertising identified by the given sync identifier.
|
||||
*
|
||||
* \param syncId Sync identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppSyncStop(dmSyncId_t syncId)
|
||||
{
|
||||
if (appMasterExtScanMode())
|
||||
{
|
||||
DmSyncStop(syncId);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Open a connection to a peer device with the given address.
|
||||
*
|
||||
* \param initPhys Initiator PHYs.
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
* \param dbHdl Device database handle.
|
||||
*
|
||||
* \return Connection identifier.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
dmConnId_t AppExtConnOpen(uint8_t initPhys, uint8_t addrType, uint8_t *pAddr, appDbHdl_t dbHdl)
|
||||
{
|
||||
if (appMasterExtScanMode())
|
||||
{
|
||||
return appConnOpen(initPhys, addrType, pAddr, dbHdl);
|
||||
}
|
||||
|
||||
/* wrong connect mode */
|
||||
return DM_CONN_ID_NONE;
|
||||
}
|
||||
Vendored
+123
@@ -0,0 +1,123 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Application framework module for legacy master. This module can be used with both
|
||||
* DM legacy and extended scanning and connect.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if current scanning mode is legacy scanning.
|
||||
*
|
||||
* \return TRUE if legacy scanning mode. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t appMasterScanMode(void)
|
||||
{
|
||||
/* legacy master app works with both DM legacy and extended scanning */
|
||||
|
||||
/* if first time since last power-on or reset */
|
||||
if (appMasterCb.scanMode == APP_SCAN_MODE_NONE)
|
||||
{
|
||||
/* set scanning mode to legacy */
|
||||
appMasterCb.scanMode = APP_SCAN_MODE_LEG;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (appMasterCb.scanMode == APP_SCAN_MODE_LEG)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
APP_TRACE_WARN0("Invalid DM scanning mode; mode configured as extended");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start scanning. A scan is performed using the given discoverability mode,
|
||||
* scan type, and duration.
|
||||
*
|
||||
* \param mode Discoverability mode.
|
||||
* \param scanType Scan type.
|
||||
* \param duration The scan duration, in milliseconds. If set to zero, scanning will
|
||||
* continue until AppScanStop() is called.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppScanStart(uint8_t mode, uint8_t scanType, uint16_t duration)
|
||||
{
|
||||
if (appMasterScanMode())
|
||||
{
|
||||
DmScanSetInterval(HCI_SCAN_PHY_LE_1M_BIT, &pAppMasterCfg->scanInterval, &pAppMasterCfg->scanWindow);
|
||||
|
||||
DmScanStart(HCI_SCAN_PHY_LE_1M_BIT, mode, &scanType, TRUE, duration, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop scanning.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppScanStop(void)
|
||||
{
|
||||
if (appMasterScanMode())
|
||||
{
|
||||
/* stop address resolution */
|
||||
appMasterCb.inProgress = FALSE;
|
||||
|
||||
DmScanStop();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Open a connection to a peer device with the given address.
|
||||
*
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
* \param dbHdl Device database handle.
|
||||
*
|
||||
* \return Connection identifier.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
dmConnId_t AppConnOpen(uint8_t addrType, uint8_t *pAddr, appDbHdl_t dbHdl)
|
||||
{
|
||||
if (appMasterScanMode())
|
||||
{
|
||||
return appConnOpen(HCI_INIT_PHY_LE_1M_BIT, addrType, pAddr, dbHdl);
|
||||
}
|
||||
|
||||
/* wrong connect mode */
|
||||
return DM_CONN_ID_NONE;
|
||||
}
|
||||
Vendored
+283
@@ -0,0 +1,283 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Application framework module for attribute server.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/wstr.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_main.h"
|
||||
#include "svc_core.h"
|
||||
#include "gatt/gatt_api.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the peer's CSRK and sign counter on this connection.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appServerSetSigningInfo(dmConnId_t connId)
|
||||
{
|
||||
appDbHdl_t dbHdl;
|
||||
dmSecKey_t *pPeerKey;
|
||||
|
||||
/* if peer's CSRK is available */
|
||||
if (((dbHdl = AppDbGetHdl(connId)) != APP_DB_HDL_NONE) &&
|
||||
((pPeerKey = AppDbGetKey(dbHdl, DM_KEY_CSRK, NULL)) != NULL))
|
||||
{
|
||||
/* set peer's CSRK and sign counter on this connection */
|
||||
AttsSetCsrk(connId, pPeerKey->csrk.key, FALSE);
|
||||
AttsSetSignCounter(connId, AppDbGetPeerSignCounter(dbHdl));
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief ATT connection callback for app framework.
|
||||
*
|
||||
* \param pDmEvt DM callback event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppServerConnCback(dmEvt_t *pDmEvt)
|
||||
{
|
||||
appDbHdl_t dbHdl;
|
||||
dmConnId_t connId = (dmConnId_t) pDmEvt->hdr.param;
|
||||
|
||||
if (pDmEvt->hdr.event == DM_CONN_OPEN_IND)
|
||||
{
|
||||
/* apply the peer's CCC table - values are persistant across connection when bonded */
|
||||
if ((dbHdl = AppDbGetHdl(connId)) != APP_DB_HDL_NONE)
|
||||
{
|
||||
uint8_t changeAwareState;
|
||||
uint8_t *pCsf;
|
||||
|
||||
AppDbGetCsfRecord(dbHdl, &changeAwareState, &pCsf);
|
||||
|
||||
/* Apply peer's client supported features. */
|
||||
AttsCsfConnOpen(connId, changeAwareState, pCsf);
|
||||
|
||||
AttsCccInitTable(connId, AppDbGetCccTbl(dbHdl));
|
||||
|
||||
/* If database has changed and peer configured service indications, send one now. */
|
||||
if (changeAwareState == ATTS_CLIENT_CHANGE_UNAWARE)
|
||||
{
|
||||
GattSendServiceChangedInd(connId, ATT_HANDLE_START, ATT_HANDLE_MAX);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* set up CCC table with uninitialized (all zero) values. */
|
||||
AttsCccInitTable(connId, NULL);
|
||||
|
||||
/* set CSF values to default */
|
||||
AttsCsfConnOpen(connId, TRUE, NULL);
|
||||
}
|
||||
|
||||
/* set peer's data signing info */
|
||||
appServerSetSigningInfo(connId);
|
||||
}
|
||||
else if (pDmEvt->hdr.event == DM_SEC_PAIR_CMPL_IND)
|
||||
{
|
||||
bool_t bonded;
|
||||
|
||||
bonded = ((pDmEvt->pairCmpl.auth & DM_AUTH_BOND_FLAG) == DM_AUTH_BOND_FLAG);
|
||||
|
||||
/* if a record exists but it is a new record, synchronize ATT CCC info into record. */
|
||||
if (bonded && (AppCheckBonded(connId) == FALSE) &&
|
||||
((dbHdl = AppDbGetHdl(connId)) != APP_DB_HDL_NONE))
|
||||
{
|
||||
uint8_t tableLen, idx;
|
||||
uint8_t csf[ATT_CSF_LEN];
|
||||
|
||||
tableLen = AttsGetCccTableLen();
|
||||
|
||||
for (idx = 0; idx < tableLen; idx++)
|
||||
{
|
||||
uint16_t cccValue;
|
||||
|
||||
if ((cccValue = AttsCccGet(connId, idx)) != 0)
|
||||
{
|
||||
AppDbSetCccTblValue(dbHdl, idx, cccValue);
|
||||
}
|
||||
}
|
||||
|
||||
/* Store cached CSF information. */
|
||||
AttsCsfGetFeatures(connId, csf, sizeof(csf));
|
||||
AppDbSetCsfRecord(dbHdl, AttsCsfGetChangeAwareState(connId), csf);
|
||||
}
|
||||
|
||||
/* set peer's data signing info */
|
||||
appServerSetSigningInfo(connId);
|
||||
}
|
||||
else if (pDmEvt->hdr.event == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* clear CCC table on connection close */
|
||||
AttsCccClearTable(connId);
|
||||
|
||||
if ((dbHdl = AppDbGetHdl(connId)) != APP_DB_HDL_NONE)
|
||||
{
|
||||
/* remember peer's sign counter */
|
||||
AppDbSetPeerSignCounter(dbHdl, AttsGetSignCounter(connId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a database hash update.
|
||||
*
|
||||
* \param pMsg message containing new hash.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void appServerHandleDbHashUpdate(attEvt_t *pMsg)
|
||||
{
|
||||
uint8_t *pCurrentHash = AppDbGetDbHash();
|
||||
|
||||
/* Compare new hash with old. */
|
||||
if (pCurrentHash != NULL)
|
||||
{
|
||||
if (memcmp(pMsg->pValue, pCurrentHash, ATT_DATABASE_HASH_LEN))
|
||||
{
|
||||
/* hash has changed, set to NULL. */
|
||||
pCurrentHash = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (pCurrentHash == NULL)
|
||||
{
|
||||
/* Update App database. */
|
||||
AppDbSetDbHash(pMsg->pValue);
|
||||
|
||||
/* Make all bonded clients change-unaware. */
|
||||
AppDbSetClientsChangeAwareState(APP_DB_HDL_NONE, ATTS_CLIENT_CHANGE_UNAWARE);
|
||||
|
||||
/* Make all active clients change-unaware. */
|
||||
AttsCsfSetClientsChangeAwarenessState(DM_CONN_ID_NONE, ATTS_CLIENT_CHANGE_UNAWARE);
|
||||
|
||||
APP_TRACE_INFO0("Database hash updated");
|
||||
|
||||
/* Send all connect clients configured to receive Service Changed Indications one now. */
|
||||
GattSendServiceChangedInd(DM_CONN_ID_NONE, ATT_HANDLE_START, ATT_HANDLE_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a service change confirmation.
|
||||
*
|
||||
* \param pMsg message containing handle value confirmation.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void appServerHandleSvcChangeCnf(attEvt_t *pMsg)
|
||||
{
|
||||
/* Check if this is a confirmation on the Service Changed Indication. */
|
||||
if (pMsg->handle == GATT_SC_HDL)
|
||||
{
|
||||
appDbHdl_t dbHdl;
|
||||
dmConnId_t connId = (dmConnId_t)pMsg->hdr.param;
|
||||
|
||||
if ((dbHdl = AppDbGetHdl(connId)) != APP_DB_HDL_NONE)
|
||||
{
|
||||
/* store update in device database */
|
||||
AppDbSetClientsChangeAwareState(dbHdl, ATTS_CLIENT_CHANGE_AWARE);
|
||||
}
|
||||
|
||||
/* Client is now change-aware. */
|
||||
AttsCsfSetClientsChangeAwarenessState(connId, ATTS_CLIENT_CHANGE_AWARE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATTS client supported features changed callback.
|
||||
*
|
||||
* \param connId DM Connection ID
|
||||
* \param changeAwareState The state of awareness to a change, see ::attClientAwareStates.
|
||||
* \param pCsf Pointer to the client supported features value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void appServerCsfWriteCback(dmConnId_t connId, uint8_t changeAwareState, uint8_t *pCsf)
|
||||
{
|
||||
appDbHdl_t dbHdl;
|
||||
|
||||
if ((dbHdl = AppDbGetHdl(connId)) != APP_DB_HDL_NONE)
|
||||
{
|
||||
/* store update in device database */
|
||||
AppDbSetCsfRecord(dbHdl, changeAwareState, pCsf);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process ATT messages.
|
||||
*
|
||||
* \param pMsg message containing event.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppServerProcAttMsg(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
switch(pMsg->event)
|
||||
{
|
||||
case ATTS_DB_HASH_CALC_CMPL_IND:
|
||||
appServerHandleDbHashUpdate((attEvt_t *)pMsg);
|
||||
break;
|
||||
|
||||
case ATTS_HANDLE_VALUE_CNF:
|
||||
appServerHandleSvcChangeCnf((attEvt_t *)pMsg);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application Server initialization routine.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppServerInit(void)
|
||||
{
|
||||
/* register callback with caching state machine */
|
||||
AttsCsfRegister(appServerCsfWriteCback);
|
||||
}
|
||||
Vendored
+1706
File diff suppressed because it is too large
Load Diff
Vendored
+749
@@ -0,0 +1,749 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Application framework module for extended slave.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Extended slave control block */
|
||||
typedef struct
|
||||
{
|
||||
uint8_t *pPerAdvData[DM_NUM_ADV_SETS]; /*! Periodic advertising data pointers */
|
||||
uint16_t perAdvDataLen[DM_NUM_ADV_SETS]; /*! Periodic advertising data lengths */
|
||||
uint16_t perAdvDataBufLen[DM_NUM_ADV_SETS]; /*! Length of periodic advertising data buffer maintained by Application */
|
||||
uint16_t perAdvDataOffset[DM_NUM_ADV_SETS]; /*! Periodic advertising data offsets */
|
||||
bool_t perAdvDataSynced[DM_NUM_ADV_SETS]; /*! TRUE if periodic advertising data is synced */
|
||||
bool_t perAdvParamsCfg[DM_NUM_ADV_SETS]; /*! TRUE if periodic advertising parameters are configured */
|
||||
uint8_t perAdvState[DM_NUM_ADV_SETS]; /*! Periodic advertising state */
|
||||
} appExtSlaveCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Extended slave control block */
|
||||
static appExtSlaveCb_t appExtSlaveCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Utility function to start extended advertising.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param cfgAdvParam Whether to configure advertising parameters
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appSlaveExtAdvStart(uint8_t advHandle, bool_t setAdvParam)
|
||||
{
|
||||
appAdvStart(1, &advHandle, pAppExtAdvCfg->advInterval, pAppExtAdvCfg->advDuration,
|
||||
pAppExtAdvCfg->maxEaEvents, setAdvParam);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Utility function to handle change in advertising type.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appSlaveExtAdvTypeChanged(dmEvt_t *pMsg)
|
||||
{
|
||||
/* clear advertising type changed flag */
|
||||
appSlaveCb.advTypeChanged[pMsg->advSetStop.advHandle] = FALSE;
|
||||
|
||||
/* set advertising state */
|
||||
appSlaveCb.advState[pMsg->advSetStop.advHandle] = APP_ADV_STATE1;
|
||||
|
||||
/* restart advertising */
|
||||
appSlaveExtAdvStart(pMsg->advSetStop.advHandle, TRUE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the next advertising state.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appSlaveNextExtAdvState(dmEvt_t *pMsg)
|
||||
{
|
||||
/* if adv hasn't been stopped and all adv/scan data haven't been sent */
|
||||
if ((appSlaveCb.advState[pMsg->advSetStop.advHandle] != APP_ADV_STOPPED) &&
|
||||
!appSlaveCb.advDataSynced[pMsg->advSetStop.advHandle])
|
||||
{
|
||||
/* set advertising state */
|
||||
appSlaveCb.advState[pMsg->advSetStop.advHandle] = APP_ADV_STATE1;
|
||||
|
||||
/* restart advertising with rest of adv/scan data */
|
||||
appSlaveExtAdvStart(pMsg->advSetStop.advHandle, FALSE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* done with this advertising set */
|
||||
appSlaveCb.advState[pMsg->advSetStop.advHandle] = APP_ADV_STOPPED;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a DM_ADV_SET_STOP_IND event.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appSlaveExtAdvStop(dmEvt_t *pMsg)
|
||||
{
|
||||
/* if advertising set was terminated */
|
||||
if (pMsg->hdr.event == DM_ADV_SET_STOP_IND)
|
||||
{
|
||||
/* if advertising was stopped for change to advertising type */
|
||||
if (appSlaveCb.advTypeChanged[pMsg->advSetStop.advHandle])
|
||||
{
|
||||
appSlaveExtAdvTypeChanged(pMsg);
|
||||
}
|
||||
/* else if advertising successfully ended with connection being created */
|
||||
else if (pMsg->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
/* advertising is stopped once a connection is opened */
|
||||
appSlaveCb.advState[pMsg->advSetStop.advHandle] = APP_ADV_STOPPED;
|
||||
}
|
||||
/* else advertising ended for other reasons */
|
||||
else
|
||||
{
|
||||
appSlaveNextExtAdvState(pMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set periodic advertising data fragment.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appSetPerAdvDataFrag(uint8_t advHandle)
|
||||
{
|
||||
uint8_t op;
|
||||
uint16_t fragLen;
|
||||
uint16_t remainLen;
|
||||
uint8_t *pAdvData;
|
||||
bool_t firstFrag = TRUE;
|
||||
|
||||
/* get data pointer and remaining data length */
|
||||
pAdvData = appExtSlaveCb.pPerAdvData[advHandle];
|
||||
remainLen = appExtSlaveCb.perAdvDataLen[advHandle] - appExtSlaveCb.perAdvDataOffset[advHandle];
|
||||
|
||||
/* if remaing data length > max adv data length supported by Controller */
|
||||
if (remainLen > appSlaveCb.maxAdvDataLen[advHandle])
|
||||
{
|
||||
remainLen = appSlaveCb.maxAdvDataLen[advHandle];
|
||||
}
|
||||
|
||||
/* while there remains data to be sent */
|
||||
while (remainLen > 0)
|
||||
{
|
||||
/* if remaing data length > max length of periodic advertising data (per set adv data command) */
|
||||
if (remainLen > HCI_PER_ADV_DATA_LEN)
|
||||
{
|
||||
/* data needs to be fragmented */
|
||||
fragLen = HCI_PER_ADV_DATA_LEN;
|
||||
op = firstFrag ? HCI_ADV_DATA_OP_FRAG_FIRST : HCI_ADV_DATA_OP_FRAG_INTER;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/* no fragmentation needed */
|
||||
fragLen = remainLen;
|
||||
op = firstFrag ? HCI_ADV_DATA_OP_COMP_FRAG : HCI_ADV_DATA_OP_FRAG_LAST;
|
||||
}
|
||||
|
||||
/* send periodic adv data */
|
||||
DmPerAdvSetData(advHandle, op, (uint8_t)fragLen,
|
||||
&(pAdvData[appExtSlaveCb.perAdvDataOffset[advHandle]]));
|
||||
|
||||
/* store periodic adv data offset */
|
||||
appExtSlaveCb.perAdvDataOffset[advHandle] += fragLen;
|
||||
|
||||
/* update remaining data length */
|
||||
remainLen -= fragLen;
|
||||
firstFrag = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set periodic advertising data.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appSetPerAdvData(uint8_t advHandle)
|
||||
{
|
||||
/* set periodic advertising data */
|
||||
if (appExtSlaveCb.perAdvDataOffset[advHandle] < appExtSlaveCb.perAdvDataLen[advHandle])
|
||||
{
|
||||
appSetPerAdvDataFrag(advHandle);
|
||||
}
|
||||
|
||||
/* if all periodic advertising data have been sent */
|
||||
if ((appExtSlaveCb.perAdvDataOffset[advHandle] >= appExtSlaveCb.perAdvDataLen[advHandle]))
|
||||
{
|
||||
appExtSlaveCb.perAdvDataSynced[advHandle] = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Utility function to start periodic advertising.
|
||||
*
|
||||
* \param avHandle Advertising handles array.
|
||||
* \param advInterval Advertising interval (in 0.625 ms units).
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void appPerAdvStart(uint8_t advHandle, uint16_t advInterval)
|
||||
{
|
||||
/* if advertising parameters to be configured */
|
||||
if (!appExtSlaveCb.perAdvParamsCfg[advHandle])
|
||||
{
|
||||
/* set min and max interval */
|
||||
DmPerAdvSetInterval(advHandle, advInterval, advInterval);
|
||||
|
||||
/* set advertising parameters */
|
||||
DmPerAdvConfig(advHandle);
|
||||
appExtSlaveCb.perAdvParamsCfg[advHandle] = TRUE;
|
||||
}
|
||||
|
||||
/* if periodic adv data to be synced */
|
||||
if (!appExtSlaveCb.perAdvDataSynced[advHandle])
|
||||
{
|
||||
/* set periodic advertising data */
|
||||
appSetPerAdvData(advHandle);
|
||||
}
|
||||
|
||||
/* start periodic advertising */
|
||||
DmPerAdvStart(advHandle);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set periodic advertising data for a given advertising set.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param len Length of the data.
|
||||
* \param pData Pointer to the data.
|
||||
* \param bufLen Length of the data buffer maintained by Application. Minimum length is
|
||||
* 31 bytes.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void appPerAdvSetData(uint8_t advHandle, uint16_t len, uint8_t *pData, uint16_t bufLen)
|
||||
{
|
||||
/* store data for location */
|
||||
appExtSlaveCb.pPerAdvData[advHandle] = pData;
|
||||
appExtSlaveCb.perAdvDataLen[advHandle] = len;
|
||||
|
||||
/* set length of advertising data buffer maintained by Application */
|
||||
appExtSlaveCb.perAdvDataBufLen[advHandle] = bufLen;
|
||||
|
||||
/* set maximum advertising data length supported by Controller */
|
||||
appSlaveCb.maxAdvDataLen[advHandle] = HciGetMaxAdvDataLen();
|
||||
|
||||
/* reset data offset */
|
||||
appExtSlaveCb.perAdvDataOffset[advHandle] = 0;
|
||||
|
||||
/* Set the data now if we are in the right mode and the data is complete (no fragmentation's required) */
|
||||
if ((appExtSlaveCb.perAdvState[advHandle] != APP_ADV_STOPPED) &&
|
||||
(appExtSlaveCb.perAdvParamsCfg[advHandle] == TRUE) &&
|
||||
(len <= HCI_PER_ADV_DATA_LEN) &&
|
||||
(len <= appSlaveCb.maxAdvDataLen[advHandle]))
|
||||
{
|
||||
appSetPerAdvData(advHandle);
|
||||
}
|
||||
/* Otherwise set it when advertising is started or mode changes */
|
||||
else
|
||||
{
|
||||
appExtSlaveCb.perAdvDataSynced[advHandle] = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the value of an advertising data element in the peroidic advertising data.
|
||||
* If the element already exists in the data then it is replaced with the new value.
|
||||
* If the element does not exist in the data it is appended to it, space permitting.
|
||||
*
|
||||
* There is special handling for the device name (AD type DM_ADV_TYPE_LOCAL_NAME).
|
||||
* If the name can only fit in the data if it is shortened, the name is shortened
|
||||
* and the AD type is changed to DM_ADV_TYPE_SHORT_NAME.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param adType Advertising data element type.
|
||||
* \param len Length of the value. Maximum length is 31 bytes.
|
||||
* \param pValue Pointer to the value.
|
||||
*
|
||||
* \return TRUE if the element was successfully added to the data, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t appPerAdvSetAdValue(uint8_t advHandle, uint8_t adType, uint8_t len, uint8_t *pValue)
|
||||
{
|
||||
uint8_t *pAdvData;
|
||||
uint16_t advDataLen;
|
||||
uint16_t advDataBufLen;
|
||||
bool_t valueSet;
|
||||
|
||||
/* get pointer and length for location */
|
||||
pAdvData = appExtSlaveCb.pPerAdvData[advHandle];
|
||||
advDataLen = appExtSlaveCb.perAdvDataLen[advHandle];
|
||||
advDataBufLen = appExtSlaveCb.perAdvDataBufLen[advHandle];
|
||||
|
||||
if (pAdvData != NULL)
|
||||
{
|
||||
/* set the new element value in the advertising data */
|
||||
if (adType == DM_ADV_TYPE_LOCAL_NAME)
|
||||
{
|
||||
valueSet = DmAdvSetName(len, pValue, &advDataLen, pAdvData, advDataBufLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
valueSet = DmAdvSetAdValue(adType, len, pValue, &advDataLen, pAdvData, advDataBufLen);
|
||||
}
|
||||
|
||||
if (valueSet)
|
||||
{
|
||||
/* if new value set update periodic advertising data */
|
||||
appPerAdvSetData(advHandle, advDataLen, pAdvData, advDataBufLen);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if current advertising mode is extended advertising.
|
||||
*
|
||||
* \return TRUE if extended advertising mode. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t appSlaveExtAdvMode(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* if DM extended advertising */
|
||||
if (DmAdvModeExt())
|
||||
{
|
||||
/* if first time since last power-on or reset */
|
||||
if (appSlaveCb.advStopCback == NULL)
|
||||
{
|
||||
appSlaveCb.advStopCback = appSlaveExtAdvStop;
|
||||
appSlaveCb.advRestartCback = NULL;
|
||||
|
||||
/* for each advertising set */
|
||||
for (i = 0; i < DM_NUM_ADV_SETS; i++)
|
||||
{
|
||||
/* configure whether to use legacy advertising PDUs */
|
||||
DmAdvUseLegacyPdu(i, pAppExtAdvCfg->useLegacyPdu[i]);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (appSlaveCb.advStopCback == appSlaveExtAdvStop)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
APP_TRACE_WARN0("Invalid DM advertising mode; mode configured as legacy");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set extended advertising data.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param location Data location.
|
||||
* \param len Length of the data. Maximum length is 31 bytes if advertising set uses
|
||||
* legacy advertising PDUs with extended advertising.
|
||||
* \param pData Pointer to the data.
|
||||
* \param bufLen Length of the data buffer maintained by Application. Minimum length is
|
||||
* 31 bytes.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppExtAdvSetData(uint8_t advHandle, uint8_t location, uint16_t len, uint8_t *pData, uint16_t bufLen)
|
||||
{
|
||||
uint16_t maxLen;
|
||||
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
if (appSlaveExtAdvMode())
|
||||
{
|
||||
/* if advertising set uses legacy advertising PDUs with extended advertising */
|
||||
if (pAppExtAdvCfg->useLegacyPdu[advHandle])
|
||||
{
|
||||
/* maximum advertising data length supported by Controller is 31 bytes */
|
||||
maxLen = HCI_ADV_DATA_LEN;
|
||||
|
||||
/* legacy advertising data length cannot exceed 31 bytes */
|
||||
if (len > HCI_ADV_DATA_LEN)
|
||||
{
|
||||
len = HCI_ADV_DATA_LEN;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* get maximum advertising data length supported by Controller */
|
||||
maxLen = HciGetMaxAdvDataLen();
|
||||
}
|
||||
|
||||
appAdvSetData(advHandle, location, len, pData, bufLen, maxLen);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start extended advertising using the parameters for the given advertising set and mode.
|
||||
*
|
||||
* \param numSets Number of advertising sets.
|
||||
* \param pAdvHandles Advertising handles array.
|
||||
* \param mode Discoverable/connectable mode.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppExtAdvStart(uint8_t numSets, uint8_t *pAdvHandles, uint8_t mode)
|
||||
{
|
||||
uint8_t i;
|
||||
uint16_t advInterval[DM_NUM_ADV_SETS] = {0};
|
||||
uint16_t advDuration[DM_NUM_ADV_SETS] = {0};
|
||||
uint8_t maxEaEvents[DM_NUM_ADV_SETS] = {0};
|
||||
|
||||
WSF_ASSERT(numSets <= DM_NUM_ADV_SETS);
|
||||
|
||||
if (appSlaveExtAdvMode())
|
||||
{
|
||||
for (i = 0; i < numSets; i++)
|
||||
{
|
||||
uint8_t advHandle = pAdvHandles[i];
|
||||
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
/* initialize advertising state */
|
||||
appSlaveCb.advState[advHandle] = APP_ADV_STATE1;
|
||||
|
||||
if (appSlaveCb.discMode != APP_MODE_NONE)
|
||||
{
|
||||
/* start advertising from beginning of advertising data */
|
||||
appSlaveResetAdvDataOffset(advHandle);
|
||||
}
|
||||
|
||||
/* set maximum advertising data length allowed by Controller for this advertising type */
|
||||
appSlaveCb.maxAdvDataLen[advHandle] = DmExtMaxAdvDataLen(appSlaveCb.advType[advHandle],
|
||||
pAppExtAdvCfg->useLegacyPdu[advHandle]);
|
||||
|
||||
/* build advertising parameters */
|
||||
advInterval[i] = pAppExtAdvCfg->advInterval[advHandle];
|
||||
advDuration[i] = pAppExtAdvCfg->advDuration[advHandle];
|
||||
maxEaEvents[i] = pAppExtAdvCfg->maxEaEvents[advHandle];
|
||||
}
|
||||
|
||||
appSlaveAdvStart(numSets, pAdvHandles, advInterval, advDuration, maxEaEvents, TRUE, mode);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop extended advertising. If the number of sets is set to 0 then all advertising
|
||||
* sets are disabled.
|
||||
*
|
||||
* \param numSets Number of advertising sets.
|
||||
* \param advHandle Advertising handle array.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppExtAdvStop(uint8_t numSets, uint8_t *pAdvHandles)
|
||||
{
|
||||
WSF_ASSERT(numSets <= DM_NUM_ADV_SETS);
|
||||
|
||||
if (appSlaveExtAdvMode())
|
||||
{
|
||||
appAdvStop(numSets, pAdvHandles);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the value of an advertising data element in the extended advertising or scan
|
||||
* response data. If the element already exists in the data then it is replaced
|
||||
* with the new value. If the element does not exist in the data it is appended
|
||||
* to it, space permitting.
|
||||
*
|
||||
* There is special handling for the device name (AD type DM_ADV_TYPE_LOCAL_NAME).
|
||||
* If the name can only fit in the data if it is shortened, the name is shortened
|
||||
* and the AD type is changed to DM_ADV_TYPE_SHORT_NAME.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param location Data location.
|
||||
* \param adType Advertising data element type.
|
||||
* \param len Length of the value. Maximum length is 31 bytes if advertising set uses
|
||||
* legacy advertising PDUs with extended advertising.
|
||||
* \param pValue Pointer to the value.
|
||||
*
|
||||
* \return TRUE if the element was successfully added to the data, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AppExtAdvSetAdValue(uint8_t advHandle, uint8_t location, uint8_t adType, uint8_t len,
|
||||
uint8_t *pValue)
|
||||
{
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
if (appSlaveExtAdvMode())
|
||||
{
|
||||
return appAdvSetAdValue(advHandle, location, adType, len, pValue);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set extended advertising type.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param advType Advertising type.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppExtSetAdvType(uint8_t advHandle, uint8_t advType)
|
||||
{
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
if (appSlaveExtAdvMode())
|
||||
{
|
||||
/* set maximum advertising data length allowed by Controller for this advertising type */
|
||||
appSlaveCb.maxAdvDataLen[advHandle] = DmExtMaxAdvDataLen(advType,
|
||||
pAppExtAdvCfg->useLegacyPdu[advHandle]);
|
||||
|
||||
appSetAdvType(advHandle, advType, pAppExtAdvCfg->advInterval[advHandle],
|
||||
pAppExtAdvCfg->advDuration[advHandle], pAppExtAdvCfg->maxEaEvents[advHandle],
|
||||
TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set advertising peer address and its type for the given advertising set.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param peerAddrType Peer address type.
|
||||
* \param pPeerAddr Peer address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppExtSetAdvPeerAddr(uint8_t advHandle, uint8_t peerAddrType, uint8_t *pPeerAddr)
|
||||
{
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
appSlaveCb.peerAddrType[advHandle] = peerAddrType;
|
||||
BdaCpy(appSlaveCb.peerAddr[advHandle], pPeerAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Accept a connection to a peer device with the given address using a given advertising
|
||||
* set.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param advType Advertising type.
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
* \param dbHdl Device database handle.
|
||||
*
|
||||
* \return Connection identifier.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
dmConnId_t AppExtConnAccept(uint8_t advHandle, uint8_t advType, uint8_t addrType, uint8_t *pAddr,
|
||||
appDbHdl_t dbHdl)
|
||||
{
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
if (appSlaveExtAdvMode())
|
||||
{
|
||||
/* set maximum advertising data length allowed by Controller for this advertising type */
|
||||
appSlaveCb.maxAdvDataLen[advHandle] = DmExtMaxAdvDataLen(advType,
|
||||
pAppExtAdvCfg->useLegacyPdu[advHandle]);
|
||||
|
||||
/* start connectable directed advertising (advertising data is supported only with extended
|
||||
low-duty cycle connectable directed advertising) */
|
||||
return appConnAccept(advHandle, advType, pAppExtAdvCfg->advInterval[advHandle],
|
||||
pAppExtAdvCfg->advDuration[advHandle],
|
||||
pAppExtAdvCfg->maxEaEvents[advHandle], addrType, pAddr, dbHdl,
|
||||
(pAppExtAdvCfg->useLegacyPdu[advHandle] ? FALSE : \
|
||||
(advType == DM_ADV_CONN_DIRECT) ? FALSE : TRUE));
|
||||
}
|
||||
|
||||
/* wrong advertising mode */
|
||||
return DM_CONN_ID_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set periodic advertising data.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param len Length of the data.
|
||||
* \param pData Pointer to the data.
|
||||
* \param bufLen Length of the data buffer maintained by Application. Minimum length is
|
||||
* 31 bytes.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppPerAdvSetData(uint8_t advHandle, uint16_t len, uint8_t *pData, uint16_t bufLen)
|
||||
{
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
if (appSlaveExtAdvMode())
|
||||
{
|
||||
// set periodic advertising data
|
||||
appPerAdvSetData(advHandle, len, pData, bufLen);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start periodic advertising for the given advertising handle.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppPerAdvStart(uint8_t advHandle)
|
||||
{
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
if (appSlaveExtAdvMode())
|
||||
{
|
||||
/* initialize periodic advertising state */
|
||||
appExtSlaveCb.perAdvState[advHandle] = APP_ADV_STATE1;
|
||||
|
||||
/* if entire periodic adv data has been sent to LL */
|
||||
if (appExtSlaveCb.perAdvDataSynced[advHandle])
|
||||
{
|
||||
/* start advertising from beginning of advertising data */
|
||||
appExtSlaveCb.perAdvDataOffset[advHandle] = 0;
|
||||
|
||||
/* force update of advertising data */
|
||||
appExtSlaveCb.perAdvDataSynced[advHandle] = FALSE;
|
||||
}
|
||||
|
||||
/* start periodic advertising */
|
||||
appPerAdvStart(advHandle, pAppExtAdvCfg->perAdvInterval[advHandle]);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop periodic advertising for the given advertising handle.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppPerAdvStop(uint8_t advHandle)
|
||||
{
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
if (appSlaveExtAdvMode())
|
||||
{
|
||||
/* stop periodic advertising */
|
||||
DmPerAdvStop(advHandle);
|
||||
appExtSlaveCb.perAdvState[advHandle] = APP_ADV_STOPPED;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the value of an advertising data element in the periodic advertising data.
|
||||
* If the element already exists in the data then it is replaced with the new value.
|
||||
* If the element does not exist in the data it is appended to it, space permitting.
|
||||
*
|
||||
* There is special handling for the device name (AD type DM_ADV_TYPE_LOCAL_NAME).
|
||||
* If the name can only fit in the data if it is shortened, the name is shortened
|
||||
* and the AD type is changed to DM_ADV_TYPE_SHORT_NAME.
|
||||
*
|
||||
* \param advHandle Advertising handle.
|
||||
* \param adType Advertising data element type.
|
||||
* \param len Length of the value. Maximum length is 31 bytes if advertising set uses
|
||||
* legacy advertising PDUs with extended advertising.
|
||||
* \param pValue Pointer to the value.
|
||||
*
|
||||
* \return TRUE if the element was successfully added to the data, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AppPerAdvSetAdValue(uint8_t advHandle, uint8_t adType, uint8_t len, uint8_t *pValue)
|
||||
{
|
||||
WSF_ASSERT(advHandle < DM_NUM_ADV_SETS);
|
||||
|
||||
if (appSlaveExtAdvMode())
|
||||
{
|
||||
return appPerAdvSetAdValue(advHandle, adType, len, pValue);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
Vendored
+388
@@ -0,0 +1,388 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Application framework module for legacy slave. This module can be used with both
|
||||
* DM legacy and extended advertising.
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "dm_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_main.h"
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Utility function to start legacy advertising.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appSlaveLegAdvStart(void)
|
||||
{
|
||||
uint8_t advHandle;
|
||||
uint8_t maxEaEvents;
|
||||
uint16_t interval;
|
||||
|
||||
interval = pAppAdvCfg->advInterval[appSlaveCb.advState[DM_ADV_HANDLE_DEFAULT]];
|
||||
|
||||
/* if this advertising state is being used */
|
||||
if (interval > 0)
|
||||
{
|
||||
advHandle = DM_ADV_HANDLE_DEFAULT;
|
||||
maxEaEvents = 0;
|
||||
|
||||
appAdvStart(1, &advHandle, &interval,
|
||||
&(pAppAdvCfg->advDuration[appSlaveCb.advState[DM_ADV_HANDLE_DEFAULT]]),
|
||||
&maxEaEvents, TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* done with all advertising states */
|
||||
appSlaveCb.advState[DM_ADV_HANDLE_DEFAULT] = APP_ADV_STOPPED;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Utility function to handle change in advertising type.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appSlaveLegAdvTypeChanged(dmEvt_t *pMsg)
|
||||
{
|
||||
/* clear advertising type changed flag */
|
||||
appSlaveCb.advTypeChanged[DM_ADV_HANDLE_DEFAULT] = FALSE;
|
||||
|
||||
/* set advertising state */
|
||||
appSlaveCb.advState[DM_ADV_HANDLE_DEFAULT] = APP_ADV_STATE1;
|
||||
|
||||
/* start advertising */
|
||||
appSlaveLegAdvStart();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the next advertising state.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appSlaveNextLegAdvState(dmEvt_t *pMsg)
|
||||
{
|
||||
/* go to next advertising state */
|
||||
appSlaveCb.advState[DM_ADV_HANDLE_DEFAULT]++;
|
||||
|
||||
/* if haven't reached stopped state then start advertising */
|
||||
if (appSlaveCb.advState[DM_ADV_HANDLE_DEFAULT] < APP_ADV_STOPPED)
|
||||
{
|
||||
appSlaveLegAdvStart();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle DM_ADV_SET_STOP_IND and DM_ADV_STOP_IND events.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appSlaveLegAdvStop(dmEvt_t *pMsg)
|
||||
{
|
||||
/* if legacy advertising PDUs are used with advertising extensions feature */
|
||||
if (pMsg->hdr.event == DM_ADV_SET_STOP_IND)
|
||||
{
|
||||
/* if advertising successfully ended with connection being created */
|
||||
if (pMsg->advSetStop.status == HCI_SUCCESS)
|
||||
{
|
||||
/* connection open indication event will determine next advertising state */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* if advertising was stopped for change to advertising type */
|
||||
if (appSlaveCb.advTypeChanged[DM_ADV_HANDLE_DEFAULT])
|
||||
{
|
||||
appSlaveLegAdvTypeChanged(pMsg);
|
||||
}
|
||||
/* else advertising ended for another reason */
|
||||
else
|
||||
{
|
||||
appSlaveNextLegAdvState(pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Restart advertising.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appSlaveLegAdvRestart(dmEvt_t *pMsg)
|
||||
{
|
||||
/* if connection closed */
|
||||
if (pMsg->hdr.event == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
/* if connectable directed advertising failed to establish connection or was cancelled */
|
||||
if (appSlaveCb.advDirected)
|
||||
{
|
||||
appSlaveCb.advDirected = FALSE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* else if connection opened */
|
||||
else if (pMsg->hdr.event == DM_CONN_OPEN_IND)
|
||||
{
|
||||
/* if connectable directed advertising */
|
||||
if (appSlaveCb.advDirected)
|
||||
{
|
||||
appSlaveCb.advDirected = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
/* advertising is stopped once a connection is opened */
|
||||
appSlaveCb.advState[DM_ADV_HANDLE_DEFAULT] = APP_ADV_STOPPED;
|
||||
}
|
||||
|
||||
/* if advertising stopped restart advertising */
|
||||
if (appSlaveCb.advState[DM_ADV_HANDLE_DEFAULT] == APP_ADV_STOPPED)
|
||||
{
|
||||
/* set advertising state */
|
||||
appSlaveCb.advState[DM_ADV_HANDLE_DEFAULT] = APP_ADV_STATE1;
|
||||
|
||||
/* start advertising */
|
||||
appSlaveLegAdvStart();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if current advertising mode is legacy advertising.
|
||||
*
|
||||
* \return TRUE if legacy advertising mode. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t appSlaveAdvMode(void)
|
||||
{
|
||||
/* legacy app slave works with both DM legacy and extended advertising */
|
||||
|
||||
/* if first time since last power-on or reset */
|
||||
if (appSlaveCb.advStopCback == NULL)
|
||||
{
|
||||
appSlaveCb.advStopCback = appSlaveLegAdvStop;
|
||||
appSlaveCb.advRestartCback = appSlaveLegAdvRestart;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (appSlaveCb.advStopCback == appSlaveLegAdvStop)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
APP_TRACE_WARN0("Invalid DM advertising mode; mode configured as extended");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set advertising data.
|
||||
*
|
||||
* \param location Data location.
|
||||
* \param len Length of the data. Maximum length is 31 bytes.
|
||||
* \param pData Pointer to the data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppAdvSetData(uint8_t location, uint8_t len, uint8_t *pData)
|
||||
{
|
||||
if (appSlaveAdvMode())
|
||||
{
|
||||
/* legacy advertising data length cannot exceed 31 bytes */
|
||||
if (len > HCI_ADV_DATA_LEN)
|
||||
{
|
||||
len = HCI_ADV_DATA_LEN;
|
||||
}
|
||||
|
||||
/* maximum advertising data length supported by Controller is 31 bytes */
|
||||
appAdvSetData(DM_ADV_HANDLE_DEFAULT, location, len, pData, HCI_ADV_DATA_LEN, HCI_ADV_DATA_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start advertising using the parameters for the given mode.
|
||||
*
|
||||
* \param mode Discoverable/connectable mode.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppAdvStart(uint8_t mode)
|
||||
{
|
||||
uint8_t advHandle;
|
||||
uint8_t maxEaEvents;
|
||||
|
||||
if (appSlaveAdvMode())
|
||||
{
|
||||
advHandle = DM_ADV_HANDLE_DEFAULT;
|
||||
maxEaEvents = 0;
|
||||
|
||||
/* initialize advertising state */
|
||||
appSlaveCb.advState[DM_ADV_HANDLE_DEFAULT] = APP_ADV_STATE1;
|
||||
|
||||
appSlaveAdvStart(1, &advHandle, &(pAppAdvCfg->advInterval[APP_ADV_STATE1]),
|
||||
&(pAppAdvCfg->advDuration[APP_ADV_STATE1]), &maxEaEvents, TRUE, mode);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop advertising.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppAdvStop(void)
|
||||
{
|
||||
uint8_t advHandle;
|
||||
|
||||
if (appSlaveAdvMode())
|
||||
{
|
||||
advHandle = DM_ADV_HANDLE_DEFAULT;
|
||||
|
||||
appAdvStop(1, &advHandle);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the value of an advertising data element in the advertising or scan
|
||||
* response data. If the element already exists in the data then it is replaced
|
||||
* with the new value. If the element does not exist in the data it is appended
|
||||
* to it, space permitting.
|
||||
*
|
||||
* There is special handling for the device name (AD type DM_ADV_TYPE_LOCAL_NAME).
|
||||
* If the name can only fit in the data if it is shortened, the name is shortened
|
||||
* and the AD type is changed to DM_ADV_TYPE_SHORT_NAME.
|
||||
*
|
||||
* \param location Data location.
|
||||
* \param adType Advertising data element type.
|
||||
* \param len Length of the value. Maximum length is 31 bytes.
|
||||
* \param pValue Pointer to the value.
|
||||
*
|
||||
* \return TRUE if the element was successfully added to the data, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AppAdvSetAdValue(uint8_t location, uint8_t adType, uint8_t len, uint8_t *pValue)
|
||||
{
|
||||
if (appSlaveAdvMode())
|
||||
{
|
||||
return appAdvSetAdValue(DM_ADV_HANDLE_DEFAULT, location, adType, len, pValue);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set advertising type.
|
||||
*
|
||||
* \param advType Advertising type.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppSetAdvType(uint8_t advType)
|
||||
{
|
||||
if (appSlaveAdvMode())
|
||||
{
|
||||
appSetAdvType(DM_ADV_HANDLE_DEFAULT, advType,
|
||||
pAppAdvCfg->advInterval[appSlaveCb.advState[DM_ADV_HANDLE_DEFAULT]],
|
||||
pAppAdvCfg->advDuration[appSlaveCb.advState[DM_ADV_HANDLE_DEFAULT]], 0, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set advertising peer address and its type.
|
||||
*
|
||||
* \param peerAddrType Peer address type.
|
||||
* \param pPeerAddr Peer address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppSetAdvPeerAddr(uint8_t peerAddrType, uint8_t *pPeerAddr)
|
||||
{
|
||||
appSlaveCb.peerAddrType[DM_ADV_HANDLE_DEFAULT] = peerAddrType;
|
||||
BdaCpy(appSlaveCb.peerAddr[DM_ADV_HANDLE_DEFAULT], pPeerAddr);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Accept a connection to a peer device with the given address.
|
||||
*
|
||||
* \param advType Advertising type.
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
* \param dbHdl Device database handle.
|
||||
*
|
||||
* \return Connection identifier.
|
||||
*/
|
||||
/************************************************************************************************/
|
||||
dmConnId_t AppConnAccept(uint8_t advType, uint8_t addrType, uint8_t *pAddr, appDbHdl_t dbHdl)
|
||||
{
|
||||
dmConnId_t connId;
|
||||
|
||||
if (appSlaveAdvMode())
|
||||
{
|
||||
/* advertising data is not supported with legacy directed advertising */
|
||||
connId = appConnAccept(DM_ADV_HANDLE_DEFAULT, advType, pAppAdvCfg->advInterval[APP_ADV_STATE1],
|
||||
pAppAdvCfg->advDuration[APP_ADV_STATE1], 0, addrType, pAddr, dbHdl,
|
||||
FALSE);
|
||||
if (connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
appSlaveCb.advDirected = TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* wrong advertising mode */
|
||||
connId = DM_CONN_ID_NONE;
|
||||
}
|
||||
|
||||
return connId;
|
||||
}
|
||||
Vendored
+204
@@ -0,0 +1,204 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Terminal handler.
|
||||
*
|
||||
* Copyright (c) 2015-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "util/terminal.h"
|
||||
#include "app_ui.h"
|
||||
#include "util/print.h"
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_bufio.h"
|
||||
#include "util/bstream.h"
|
||||
|
||||
#include "dm_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Function Prototypes
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! \brief Button Command Handler */
|
||||
static uint8_t appTerminalCommandBtnHandler(uint32_t argc, char **argv);
|
||||
|
||||
/*! \brief Security Pin Code Command Handler. */
|
||||
static uint8_t appTerminalPinCodeHandler(uint32_t argc, char **argv);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! \brief Button command. */
|
||||
static terminalCommand_t appTerminalButtonPress = { NULL, "btn", "btn <ID> <s|m|l|x>", appTerminalCommandBtnHandler };
|
||||
|
||||
/*! \brief Security Pin Code commands. */
|
||||
static terminalCommand_t appTerminalPinCode = { NULL, "pin", "pin <ConnID> <Pin Code>", appTerminalPinCodeHandler };
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize terminal.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppTerminalInit(void)
|
||||
{
|
||||
wsfHandlerId_t handlerId;
|
||||
|
||||
/* Initialize Serial Communication. */
|
||||
// WsfBufIoUartRegister(TerminalRx);
|
||||
// TerminalRegisterUartTxFunc(WsfBufIoWrite);
|
||||
handlerId = WsfOsSetNextHandler(TerminalHandler);
|
||||
TerminalInit(handlerId);
|
||||
|
||||
/* Register commands. */
|
||||
TerminalRegisterCommand(&appTerminalButtonPress);
|
||||
TerminalRegisterCommand(&appTerminalPinCode);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handler for a button press terminal command.
|
||||
*
|
||||
* \param argc The number of arguments passed to the command.
|
||||
* \param argv The array of arguments; the 0th argument is the command.
|
||||
*
|
||||
* \return Error code.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t appTerminalCommandBtnHandler(uint32_t argc, char **argv)
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
return TERMINAL_ERROR_TOO_FEW_ARGUMENTS;
|
||||
}
|
||||
else if (argc == 3)
|
||||
{
|
||||
uint8_t btnIndx;
|
||||
uint8_t event;
|
||||
|
||||
if (strcmp(argv[1], "1") == 0)
|
||||
{
|
||||
btnIndx = 1;
|
||||
}
|
||||
else if (strcmp(argv[1], "2") == 0)
|
||||
{
|
||||
btnIndx = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return TERMINAL_ERROR_BAD_ARGUMENTS;
|
||||
}
|
||||
|
||||
if (strcmp(argv[2], "d") == 0)
|
||||
{
|
||||
TerminalTxPrint("Button %s Press" TERMINAL_STRING_NEW_LINE, argv[1]);
|
||||
event = (btnIndx == 1? APP_UI_BTN_1_DOWN : APP_UI_BTN_2_DOWN);
|
||||
}
|
||||
else if (strcmp(argv[2], "s") == 0)
|
||||
{
|
||||
TerminalTxPrint("Short Button %s Press" TERMINAL_STRING_NEW_LINE, argv[1]);
|
||||
event = (btnIndx == 1? APP_UI_BTN_1_SHORT : APP_UI_BTN_2_SHORT);
|
||||
}
|
||||
else if (strcmp(argv[2], "m") == 0)
|
||||
{
|
||||
TerminalTxPrint("Medium Button %s Press" TERMINAL_STRING_NEW_LINE, argv[1]);
|
||||
event = (btnIndx == 1? APP_UI_BTN_1_MED : APP_UI_BTN_2_MED);
|
||||
}
|
||||
else if (strcmp(argv[2], "l") == 0)
|
||||
{
|
||||
TerminalTxPrint("Long Button %s Press" TERMINAL_STRING_NEW_LINE, argv[1]);
|
||||
event = (btnIndx == 1? APP_UI_BTN_1_LONG : APP_UI_BTN_2_LONG);
|
||||
}
|
||||
else if (strcmp(argv[2], "x") == 0)
|
||||
{
|
||||
TerminalTxPrint("Medium Button %s Press" TERMINAL_STRING_NEW_LINE, argv[1]);
|
||||
event = (btnIndx == 1? APP_UI_BTN_1_EX_LONG : APP_UI_BTN_2_EX_LONG);
|
||||
}
|
||||
else
|
||||
{
|
||||
return TERMINAL_ERROR_BAD_ARGUMENTS;
|
||||
}
|
||||
|
||||
AppUiBtnTest(event);
|
||||
}
|
||||
else
|
||||
{
|
||||
return TERMINAL_ERROR_TOO_MANY_ARGUMENTS;
|
||||
}
|
||||
|
||||
return TERMINAL_ERROR_OK;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handler for a pin code terminal command.
|
||||
*
|
||||
* \param argc The number of arguments passed to the command.
|
||||
* \param argv The array of arguments; the 0th argument is the command.
|
||||
*
|
||||
* \return Error code.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static uint8_t appTerminalPinCodeHandler(uint32_t argc, char **argv)
|
||||
{
|
||||
if (argc < 2)
|
||||
{
|
||||
return TERMINAL_ERROR_TOO_FEW_ARGUMENTS;
|
||||
}
|
||||
else if (argc == 3)
|
||||
{
|
||||
uint32_t passkey;
|
||||
uint8_t buf[SMP_PIN_LEN];
|
||||
uint8_t connId;
|
||||
|
||||
passkey = atoi(argv[2]);
|
||||
connId = atoi(argv[1]);
|
||||
|
||||
if (connId < 1 || connId > DM_CONN_MAX)
|
||||
{
|
||||
TerminalTxPrint("ConnID must be in the range [1 .. %d]\n", DM_CONN_MAX);
|
||||
return TERMINAL_ERROR_BAD_ARGUMENTS;
|
||||
}
|
||||
|
||||
passkey %= 1000000;
|
||||
|
||||
/* convert to byte buffer */
|
||||
buf[0] = UINT32_TO_BYTE0(passkey);
|
||||
buf[1] = UINT32_TO_BYTE1(passkey);
|
||||
buf[2] = UINT32_TO_BYTE2(passkey);
|
||||
|
||||
/* send authentication response to DM */
|
||||
DmSecAuthRsp((dmConnId_t) connId, SMP_PIN_LEN, buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
return TERMINAL_ERROR_TOO_MANY_ARGUMENTS;
|
||||
}
|
||||
|
||||
return TERMINAL_ERROR_OK;
|
||||
}
|
||||
Vendored
+873
@@ -0,0 +1,873 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Application framework device database example, using simple RAM-based storage.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "util/bda.h"
|
||||
#include "app_api.h"
|
||||
#include "app_main.h"
|
||||
#include "app_db.h"
|
||||
#include "app_cfg.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Database record */
|
||||
typedef struct
|
||||
{
|
||||
/*! Common for all roles */
|
||||
bdAddr_t peerAddr; /*! Peer address */
|
||||
uint8_t addrType; /*! Peer address type */
|
||||
dmSecIrk_t peerIrk; /*! Peer IRK */
|
||||
dmSecCsrk_t peerCsrk; /*! Peer CSRK */
|
||||
uint8_t keyValidMask; /*! Valid keys in this record */
|
||||
bool_t inUse; /*! TRUE if record in use */
|
||||
bool_t valid; /*! TRUE if record is valid */
|
||||
bool_t peerAddedToRl; /*! TRUE if peer device's been added to resolving list */
|
||||
bool_t peerRpao; /*! TRUE if RPA Only attribute's present on peer device */
|
||||
|
||||
/*! For slave local device */
|
||||
dmSecLtk_t localLtk; /*! Local LTK */
|
||||
uint8_t localLtkSecLevel; /*! Local LTK security level */
|
||||
bool_t peerAddrRes; /*! TRUE if address resolution's supported on peer device (master) */
|
||||
|
||||
/*! For master local device */
|
||||
dmSecLtk_t peerLtk; /*! Peer LTK */
|
||||
uint8_t peerLtkSecLevel; /*! Peer LTK security level */
|
||||
|
||||
/*! for ATT server local device */
|
||||
uint16_t cccTbl[APP_DB_NUM_CCCD]; /*! Client characteristic configuration descriptors */
|
||||
uint32_t peerSignCounter; /*! Peer Sign Counter */
|
||||
uint8_t changeAwareState; /*! Peer client awareness to state change in database */
|
||||
uint8_t csf[ATT_CSF_LEN]; /*! Peer client supported features record */
|
||||
|
||||
/*! for ATT client */
|
||||
bool_t cacheByHash; /*! TRUE if cached handles are maintained by comparing database hash */
|
||||
uint8_t dbHash[ATT_DATABASE_HASH_LEN]; /*! Peer database hash */
|
||||
uint16_t hdlList[APP_DB_HDL_LIST_LEN]; /*! Cached handle list */
|
||||
uint8_t discStatus; /*! Service discovery and configuration status */
|
||||
bool_t master_role; /*! True if local device is master for this record */
|
||||
} appDbRec_t;
|
||||
|
||||
/*! Database type */
|
||||
typedef struct
|
||||
{
|
||||
appDbRec_t rec[APP_DB_NUM_RECS]; /*! Device database records */
|
||||
char devName[ATT_DEFAULT_PAYLOAD_LEN]; /*! Device name */
|
||||
uint8_t devNameLen; /*! Device name length */
|
||||
uint8_t dbHash[ATT_DATABASE_HASH_LEN]; /*! Device GATT database hash */
|
||||
} appDb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Database */
|
||||
static appDb_t appDb;
|
||||
|
||||
/*! When all records are allocated use this index to determine which to overwrite */
|
||||
static appDbRec_t *pAppDbNewRec = appDb.rec;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the device database.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbInit(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Create a new device database record.
|
||||
*
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
*
|
||||
* \return Database record handle.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
appDbHdl_t AppDbNewRecord(uint8_t addrType, uint8_t *pAddr, bool_t master_role)
|
||||
{
|
||||
appDbRec_t *pRec = appDb.rec;
|
||||
uint8_t i;
|
||||
|
||||
/* find a free record */
|
||||
for (i = APP_DB_NUM_RECS; i > 0; i--, pRec++)
|
||||
{
|
||||
if (!pRec->inUse)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if all records were allocated */
|
||||
if (i == 0)
|
||||
{
|
||||
/* overwrite a record */
|
||||
pRec = pAppDbNewRec;
|
||||
|
||||
/* get next record to overwrite */
|
||||
pAppDbNewRec++;
|
||||
if (pAppDbNewRec == &appDb.rec[APP_DB_NUM_RECS])
|
||||
{
|
||||
pAppDbNewRec = appDb.rec;
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize record */
|
||||
memset(pRec, 0, sizeof(appDbRec_t));
|
||||
pRec->inUse = TRUE;
|
||||
pRec->addrType = addrType;
|
||||
BdaCpy(pRec->peerAddr, pAddr);
|
||||
pRec->peerAddedToRl = FALSE;
|
||||
pRec->peerRpao = FALSE;
|
||||
pRec->master_role = master_role;
|
||||
|
||||
return (appDbHdl_t) pRec;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the next database record for a given record. For the first record, the function
|
||||
* should be called with 'hdl' set to 'APP_DB_HDL_NONE'.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
*
|
||||
* \return Next record handle found. APP_DB_HDL_NONE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
appDbHdl_t AppDbGetNextRecord(appDbHdl_t hdl)
|
||||
{
|
||||
appDbRec_t *pRec;
|
||||
|
||||
/* if first record is requested */
|
||||
if (hdl == APP_DB_HDL_NONE)
|
||||
{
|
||||
pRec = appDb.rec;
|
||||
}
|
||||
/* if valid record passed in */
|
||||
else if (AppDbRecordInUse(hdl))
|
||||
{
|
||||
pRec = (appDbRec_t *)hdl;
|
||||
pRec++;
|
||||
}
|
||||
/* invalid record passed in */
|
||||
else
|
||||
{
|
||||
return APP_DB_HDL_NONE;
|
||||
}
|
||||
|
||||
/* look for next valid record */
|
||||
while (pRec < &appDb.rec[APP_DB_NUM_RECS])
|
||||
{
|
||||
/* if record is in use */
|
||||
if (pRec->inUse && pRec->valid)
|
||||
{
|
||||
/* record found */
|
||||
return (appDbHdl_t)pRec;
|
||||
}
|
||||
|
||||
/* look for next record */
|
||||
pRec++;
|
||||
}
|
||||
|
||||
/* end of records */
|
||||
return APP_DB_HDL_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Delete a new device database record.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbDeleteRecord(appDbHdl_t hdl)
|
||||
{
|
||||
((appDbRec_t *) hdl)->inUse = FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Validate a new device database record. This function is called when pairing is
|
||||
* successful and the devices are bonded.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
* \param keyMask Bitmask of keys to validate.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbValidateRecord(appDbHdl_t hdl, uint8_t keyMask)
|
||||
{
|
||||
((appDbRec_t *) hdl)->valid = TRUE;
|
||||
((appDbRec_t *) hdl)->keyValidMask = keyMask;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if a record has been validated. If it has not, delete it. This function
|
||||
* is typically called when the connection is closed.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbCheckValidRecord(appDbHdl_t hdl)
|
||||
{
|
||||
if (((appDbRec_t *) hdl)->valid == FALSE)
|
||||
{
|
||||
AppDbDeleteRecord(hdl);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if a database record is in use.
|
||||
|
||||
* \param hdl Database record handle.
|
||||
*
|
||||
* \return TURE if record in use. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AppDbRecordInUse(appDbHdl_t hdl)
|
||||
{
|
||||
appDbRec_t *pRec = appDb.rec;
|
||||
uint8_t i;
|
||||
|
||||
/* see if record is in database record list */
|
||||
for (i = APP_DB_NUM_RECS; i > 0; i--, pRec++)
|
||||
{
|
||||
if (pRec->inUse && pRec->valid && (pRec == ((appDbRec_t *)hdl)))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if there is a stored bond with any device.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
*
|
||||
* \return TRUE if a bonded device is found, FALSE otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AppDbCheckBonded(void)
|
||||
{
|
||||
appDbRec_t *pRec = appDb.rec;
|
||||
uint8_t i;
|
||||
|
||||
/* find a record */
|
||||
for (i = APP_DB_NUM_RECS; i > 0; i--, pRec++)
|
||||
{
|
||||
if (pRec->inUse && !pRec->master_role)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Delete all database records.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbDeleteAllRecords(void)
|
||||
{
|
||||
appDbRec_t *pRec = appDb.rec;
|
||||
uint8_t i;
|
||||
|
||||
/* set in use to false for all records */
|
||||
for (i = APP_DB_NUM_RECS; i > 0; i--, pRec++)
|
||||
{
|
||||
pRec->inUse = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find a device database record by peer address.
|
||||
*
|
||||
* \param addrType Address type.
|
||||
* \param pAddr Peer device address.
|
||||
*
|
||||
* \return Database record handle or APP_DB_HDL_NONE if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
appDbHdl_t AppDbFindByAddr(uint8_t addrType, uint8_t *pAddr)
|
||||
{
|
||||
appDbRec_t *pRec = appDb.rec;
|
||||
uint8_t peerAddrType = DmHostAddrType(addrType);
|
||||
uint8_t i;
|
||||
|
||||
/* find matching record */
|
||||
for (i = APP_DB_NUM_RECS; i > 0; i--, pRec++)
|
||||
{
|
||||
if (pRec->inUse && (pRec->addrType == peerAddrType) && BdaCmp(pRec->peerAddr, pAddr))
|
||||
{
|
||||
return (appDbHdl_t) pRec;
|
||||
}
|
||||
}
|
||||
|
||||
return APP_DB_HDL_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Find a device database record by data in an LTK request.
|
||||
*
|
||||
* \param encDiversifier Encryption diversifier associated with key.
|
||||
* \param pRandNum Pointer to random number associated with key.
|
||||
*
|
||||
* \return Database record handle or APP_DB_HDL_NONE if not found.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
appDbHdl_t AppDbFindByLtkReq(uint16_t encDiversifier, uint8_t *pRandNum)
|
||||
{
|
||||
appDbRec_t *pRec = appDb.rec;
|
||||
uint8_t i;
|
||||
|
||||
/* find matching record */
|
||||
for (i = APP_DB_NUM_RECS; i > 0; i--, pRec++)
|
||||
{
|
||||
if (pRec->inUse && (pRec->localLtk.ediv == encDiversifier) &&
|
||||
(memcmp(pRec->localLtk.rand, pRandNum, SMP_RAND8_LEN) == 0))
|
||||
{
|
||||
return (appDbHdl_t) pRec;
|
||||
}
|
||||
}
|
||||
|
||||
return APP_DB_HDL_NONE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get a key from a device database record.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
* \param type Type of key to get.
|
||||
* \param pSecLevel If the key is valid, the security level of the key.
|
||||
*
|
||||
* \return Pointer to key if key is valid or NULL if not valid.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
dmSecKey_t *AppDbGetKey(appDbHdl_t hdl, uint8_t type, uint8_t *pSecLevel)
|
||||
{
|
||||
dmSecKey_t *pKey = NULL;
|
||||
|
||||
/* if key valid */
|
||||
if ((type & ((appDbRec_t *) hdl)->keyValidMask) != 0)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case DM_KEY_LOCAL_LTK:
|
||||
*pSecLevel = ((appDbRec_t *) hdl)->localLtkSecLevel;
|
||||
pKey = (dmSecKey_t *) &((appDbRec_t *) hdl)->localLtk;
|
||||
break;
|
||||
|
||||
case DM_KEY_PEER_LTK:
|
||||
*pSecLevel = ((appDbRec_t *) hdl)->peerLtkSecLevel;
|
||||
pKey = (dmSecKey_t *) &((appDbRec_t *) hdl)->peerLtk;
|
||||
break;
|
||||
|
||||
case DM_KEY_IRK:
|
||||
pKey = (dmSecKey_t *)&((appDbRec_t *)hdl)->peerIrk;
|
||||
break;
|
||||
|
||||
case DM_KEY_CSRK:
|
||||
pKey = (dmSecKey_t *)&((appDbRec_t *)hdl)->peerCsrk;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pKey;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set a key in a device database record.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
* \param pKey Key data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbSetKey(appDbHdl_t hdl, dmSecKeyIndEvt_t *pKey)
|
||||
{
|
||||
switch(pKey->type)
|
||||
{
|
||||
case DM_KEY_LOCAL_LTK:
|
||||
((appDbRec_t *) hdl)->localLtkSecLevel = pKey->secLevel;
|
||||
((appDbRec_t *) hdl)->localLtk = pKey->keyData.ltk;
|
||||
break;
|
||||
|
||||
case DM_KEY_PEER_LTK:
|
||||
((appDbRec_t *) hdl)->peerLtkSecLevel = pKey->secLevel;
|
||||
((appDbRec_t *) hdl)->peerLtk = pKey->keyData.ltk;
|
||||
break;
|
||||
|
||||
case DM_KEY_IRK:
|
||||
((appDbRec_t *)hdl)->peerIrk = pKey->keyData.irk;
|
||||
|
||||
/* make sure peer record is stored using its identity address */
|
||||
((appDbRec_t *)hdl)->addrType = pKey->keyData.irk.addrType;
|
||||
BdaCpy(((appDbRec_t *)hdl)->peerAddr, pKey->keyData.irk.bdAddr);
|
||||
break;
|
||||
|
||||
case DM_KEY_CSRK:
|
||||
((appDbRec_t *)hdl)->peerCsrk = pKey->keyData.csrk;
|
||||
|
||||
/* sign counter must be initialized to zero when CSRK is generated */
|
||||
((appDbRec_t *)hdl)->peerSignCounter = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the peer's database hash.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
*
|
||||
* \return Pointer to database hash.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *AppDbGetPeerDbHash(appDbHdl_t hdl)
|
||||
{
|
||||
return ((appDbRec_t *) hdl)->dbHash;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set a new peer database hash.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
* \param pDbHash Pointer to new hash.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbSetPeerDbHash(appDbHdl_t hdl, uint8_t *pDbHash)
|
||||
{
|
||||
WSF_ASSERT(pDbHash != NULL);
|
||||
|
||||
memcpy(((appDbRec_t *) hdl)->dbHash, pDbHash, ATT_DATABASE_HASH_LEN);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Check if cached handles' validity is determined by reading the peer's database hash
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
*
|
||||
* \return \ref TRUE if peer's database hash must be read to verify handles have not changed.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AppDbIsCacheCheckedByHash(appDbHdl_t hdl)
|
||||
{
|
||||
return ((appDbRec_t *) hdl)->cacheByHash;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set if cached handles' validity is determined by reading the peer's database hash.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
* \param cacheByHash \ref TRUE if peer's database must be read to verify cached handles.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbSetCacheByHash(appDbHdl_t hdl, bool_t cacheByHash)
|
||||
{
|
||||
((appDbRec_t *) hdl)->cacheByHash = cacheByHash;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the client characteristic configuration descriptor table.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
*
|
||||
* \return Pointer to client characteristic configuration descriptor table.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t *AppDbGetCccTbl(appDbHdl_t hdl)
|
||||
{
|
||||
return ((appDbRec_t *) hdl)->cccTbl;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set a value in the client characteristic configuration table.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
* \param idx Table index.
|
||||
* \param value client characteristic configuration value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbSetCccTblValue(appDbHdl_t hdl, uint16_t idx, uint16_t value)
|
||||
{
|
||||
WSF_ASSERT(idx < APP_DB_NUM_CCCD);
|
||||
|
||||
((appDbRec_t *) hdl)->cccTbl[idx] = value;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the client supported features record.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
* \param pChangeAwareState Pointer to peer client's change aware status to a change in the database.
|
||||
* \param pCsf Pointer to csf value pointer.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbGetCsfRecord(appDbHdl_t hdl, uint8_t *pChangeAwareState, uint8_t **pCsf)
|
||||
{
|
||||
*pChangeAwareState = ((appDbRec_t *)hdl)->changeAwareState;
|
||||
*pCsf = ((appDbRec_t *) hdl)->csf;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set a client supported features record.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
* \param changeAwareState The state of awareness to a change, see ::attClientAwareStates.
|
||||
* \param pCsf Pointer to the client supported features value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbSetCsfRecord(appDbHdl_t hdl, uint8_t changeAwareState, uint8_t *pCsf)
|
||||
{
|
||||
if ((pCsf != NULL) && (hdl != APP_DB_HDL_NONE))
|
||||
{
|
||||
((appDbRec_t *) hdl)->changeAwareState = changeAwareState;
|
||||
memcpy(&((appDbRec_t *) hdl)->csf, pCsf, ATT_CSF_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set client's state of awareness to a change in the database.
|
||||
*
|
||||
* \param hdl Database record handle. If \ref hdl == \ref NULL, state is set for all
|
||||
* clients.
|
||||
* \param state The state of awareness to a change, see ::attClientAwareStates.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbSetClientsChangeAwareState(appDbHdl_t hdl, uint8_t state)
|
||||
{
|
||||
if (hdl == APP_DB_HDL_NONE)
|
||||
{
|
||||
appDbRec_t *pRec = appDb.rec;
|
||||
uint8_t i;
|
||||
|
||||
/* Set all clients status to change-unaware. */
|
||||
for (i = APP_DB_NUM_RECS; i > 0; i--, pRec++)
|
||||
{
|
||||
pRec->changeAwareState = state;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
((appDbRec_t *) hdl)->changeAwareState = state;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get device's GATT database hash.
|
||||
*
|
||||
* \return Pointer to database hash.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t *AppDbGetDbHash(void)
|
||||
{
|
||||
return appDb.dbHash;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set device's GATT database hash.
|
||||
*
|
||||
* \param pHash GATT database hash to store.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbSetDbHash(uint8_t *pHash)
|
||||
{
|
||||
if (pHash != NULL)
|
||||
{
|
||||
memcpy(appDb.dbHash, pHash, ATT_DATABASE_HASH_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the discovery status.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
*
|
||||
* \return Discovery status.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t AppDbGetDiscStatus(appDbHdl_t hdl)
|
||||
{
|
||||
return ((appDbRec_t *) hdl)->discStatus;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the discovery status.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
* \param state Discovery status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbSetDiscStatus(appDbHdl_t hdl, uint8_t status)
|
||||
{
|
||||
((appDbRec_t *) hdl)->discStatus = status;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the cached handle list.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
*
|
||||
* \return Pointer to handle list.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint16_t *AppDbGetHdlList(appDbHdl_t hdl)
|
||||
{
|
||||
return ((appDbRec_t *) hdl)->hdlList;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the cached handle list.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
* \param pHdlList Pointer to handle list.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbSetHdlList(appDbHdl_t hdl, uint16_t *pHdlList)
|
||||
{
|
||||
memcpy(((appDbRec_t *) hdl)->hdlList, pHdlList, sizeof(((appDbRec_t *) hdl)->hdlList));
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the device name.
|
||||
*
|
||||
* \param pLen Returned device name length.
|
||||
*
|
||||
* \return Pointer to UTF-8 string containing device name or NULL if not set.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
char *AppDbGetDevName(uint8_t *pLen)
|
||||
{
|
||||
/* if first character of name is NULL assume it is uninitialized */
|
||||
if (appDb.devName[0] == 0)
|
||||
{
|
||||
*pLen = 0;
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pLen = appDb.devNameLen;
|
||||
return appDb.devName;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the device name.
|
||||
*
|
||||
* \param len Device name length.
|
||||
* \param pStr UTF-8 string containing device name.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbSetDevName(uint8_t len, char *pStr)
|
||||
{
|
||||
/* check for maximum device length */
|
||||
len = (len <= sizeof(appDb.devName)) ? len : sizeof(appDb.devName);
|
||||
|
||||
memcpy(appDb.devName, pStr, len);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get address resolution attribute value read from a peer device.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
*
|
||||
* \return TRUE if address resolution is supported in peer device. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AppDbGetPeerAddrRes(appDbHdl_t hdl)
|
||||
{
|
||||
return ((appDbRec_t *)hdl)->peerAddrRes;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set address resolution attribute value for a peer device.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
* \param addrRes Address resolution attribue value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbSetPeerAddrRes(appDbHdl_t hdl, uint8_t addrRes)
|
||||
{
|
||||
((appDbRec_t *)hdl)->peerAddrRes = addrRes;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get sign counter for a peer device.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
*
|
||||
* \return Sign counter for peer device.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint32_t AppDbGetPeerSignCounter(appDbHdl_t hdl)
|
||||
{
|
||||
return ((appDbRec_t *)hdl)->peerSignCounter;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set sign counter for a peer device.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
* \param signCounter Sign counter for peer device.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbSetPeerSignCounter(appDbHdl_t hdl, uint32_t signCounter)
|
||||
{
|
||||
((appDbRec_t *)hdl)->peerSignCounter = signCounter;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the peer device added to resolving list flag value.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
*
|
||||
* \return TRUE if peer device's been added to resolving list. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AppDbGetPeerAddedToRl(appDbHdl_t hdl)
|
||||
{
|
||||
return ((appDbRec_t *)hdl)->peerAddedToRl;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the peer device added to resolving list flag to a given value.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
* \param peerAddedToRl Peer device added to resolving list flag value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbSetPeerAddedToRl(appDbHdl_t hdl, bool_t peerAddedToRl)
|
||||
{
|
||||
((appDbRec_t *)hdl)->peerAddedToRl = peerAddedToRl;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the resolvable private address only attribute flag for a given peer device.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
*
|
||||
* \return TRUE if RPA Only attribute is present on peer device. FALSE, otherwise.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AppDbGetPeerRpao(appDbHdl_t hdl)
|
||||
{
|
||||
return ((appDbRec_t *)hdl)->peerRpao;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the resolvable private address only attribute flag for a given peer device.
|
||||
*
|
||||
* \param hdl Database record handle.
|
||||
* \param peerRpao Resolvable private address only attribute flag value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppDbSetPeerRpao(appDbHdl_t hdl, bool_t peerRpao)
|
||||
{
|
||||
((appDbRec_t *)hdl)->peerRpao = peerRpao;
|
||||
}
|
||||
Vendored
+483
@@ -0,0 +1,483 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Application framework hardware interfaces.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_os.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "util/bstream.h"
|
||||
#include "app_hw.h"
|
||||
#include "svc_ch.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Simulated measurement RR interval, converted from bpm to 1/1024 sec units */
|
||||
#define APP_HR_MEAS_SIM_RR(heartRate) (((uint16_t) 1024 * 60) / (heartRate))
|
||||
|
||||
/*! Number of simulated blood pressure measurements */
|
||||
#define APP_NUM_BPM 3 /* measurements */
|
||||
#define APP_NUM_INT_BPM 3 /* intermediate measurements */
|
||||
|
||||
/*! Number of simulated weight scale measurements */
|
||||
#define APP_NUM_WSM 3
|
||||
|
||||
/*! Number of simulated temperature measurements */
|
||||
#define APP_NUM_TM 6
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Blood pressure simulated measurement structure */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t systolic; /*! Systolic pressure */
|
||||
uint16_t diastolic; /*! Diastolic pressure */
|
||||
uint16_t map; /*! Mean arterial pressure */
|
||||
uint16_t pulseRate; /*! Pulse rate */
|
||||
uint16_t measStatus; /*! Measurement status */
|
||||
} appHwSimBpm_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* battery level */
|
||||
static uint8_t appHwBattLevel = 100;
|
||||
|
||||
/* heart rate */
|
||||
static uint8_t appHwHeartRate = 78;
|
||||
|
||||
/* rr intervals */
|
||||
static uint16_t appHwRrInterval[2];
|
||||
|
||||
/* simluated blood pressure measurements */
|
||||
static const appHwSimBpm_t appHwBpm[APP_NUM_BPM] =
|
||||
{
|
||||
{
|
||||
SFLT_TO_UINT16(140, 0), /*! Systolic pressure */
|
||||
SFLT_TO_UINT16(80, 0), /*! Diastolic pressure */
|
||||
SFLT_TO_UINT16(100, 0), /*! Mean arterial pressure */
|
||||
SFLT_TO_UINT16(79, 0), /*! Pulse rate */
|
||||
0 /*! Measurement status */
|
||||
},
|
||||
{
|
||||
SFLT_TO_UINT16(141, 0), /*! Systolic pressure */
|
||||
SFLT_TO_UINT16(81, 0), /*! Diastolic pressure */
|
||||
SFLT_TO_UINT16(101, 0), /*! Mean arterial pressure */
|
||||
SFLT_TO_UINT16(80, 0), /*! Pulse rate */
|
||||
CH_BPM_MS_FLAG_MOVEMENT /*! Measurement status */
|
||||
},
|
||||
{
|
||||
SFLT_TO_UINT16(142, 0), /*! Systolic pressure */
|
||||
SFLT_TO_UINT16(82, 0), /*! Diastolic pressure */
|
||||
SFLT_TO_UINT16(102, 0), /*! Mean arterial pressure */
|
||||
SFLT_TO_UINT16(81, 0), /*! Pulse rate */
|
||||
CH_BPM_MS_FLAG_CUFF_FIT_LOOSE /*! Measurement status */
|
||||
}
|
||||
};
|
||||
|
||||
/* simluated intermediate blood pressure measurements */
|
||||
static const appHwSimBpm_t appHwIntBpm[APP_NUM_INT_BPM] =
|
||||
{
|
||||
{
|
||||
SFLT_TO_UINT16(103, 0), /*! Current cuff pressure */
|
||||
CH_SFLOAT_NAN, /*! Unused */
|
||||
CH_SFLOAT_NAN, /*! Unused */
|
||||
SFLT_TO_UINT16(76, 0), /*! Pulse rate */
|
||||
CH_BPM_MS_FLAG_IRR_PULSE /*! Measurement status */
|
||||
},
|
||||
{
|
||||
SFLT_TO_UINT16(104, 0), /*! Current cuff pressure */
|
||||
CH_SFLOAT_NAN, /*! Unused */
|
||||
CH_SFLOAT_NAN, /*! Unused */
|
||||
SFLT_TO_UINT16(77, 0), /*! Pulse rate */
|
||||
CH_BPM_MS_FLAG_MEAS_POS_ERR /*! Measurement status */
|
||||
},
|
||||
{
|
||||
SFLT_TO_UINT16(105, 0), /*! Current cuff pressure */
|
||||
CH_SFLOAT_NAN, /*! Unused */
|
||||
CH_SFLOAT_NAN, /*! Unused */
|
||||
SFLT_TO_UINT16(78, 0), /*! Pulse rate */
|
||||
CH_BPM_MS_FLAG_CUFF_FIT_LOOSE /*! Measurement status */
|
||||
}
|
||||
};
|
||||
|
||||
/* simulated measurement indexes */
|
||||
static uint8_t appHwBpmIdx = 0;
|
||||
static uint8_t appHwIntBpmIdx = 0;
|
||||
|
||||
/* timestamp */
|
||||
static appDateTime_t appHwDateTime =
|
||||
{
|
||||
2014, /*! Year */
|
||||
1, /*! Month */
|
||||
27, /*! Day */
|
||||
13, /*! Hour */
|
||||
10, /*! Minutes */
|
||||
0 /*! Seconds */
|
||||
};
|
||||
|
||||
/* simulated weight scale measurements in lbs */
|
||||
static const uint16_t appHwWeightLbs[APP_NUM_WSM] =
|
||||
{
|
||||
15010,
|
||||
15120,
|
||||
15230
|
||||
};
|
||||
|
||||
/* simulated weight scale measurements in kg */
|
||||
static const uint16_t appHwWeightKg[APP_NUM_WSM] =
|
||||
{
|
||||
6823,
|
||||
6873,
|
||||
6923
|
||||
};
|
||||
|
||||
/* simulated measurement index */
|
||||
static uint8_t appHwWsmIdx = 0;
|
||||
|
||||
/* simulated temperature measurements in C */
|
||||
static const uint32_t appHwTempC[APP_NUM_TM] =
|
||||
{
|
||||
FLT_TO_UINT32(369, -1),
|
||||
FLT_TO_UINT32(370, -1),
|
||||
FLT_TO_UINT32(371, -1),
|
||||
FLT_TO_UINT32(372, -1),
|
||||
FLT_TO_UINT32(373, -1),
|
||||
FLT_TO_UINT32(374, -1)
|
||||
};
|
||||
|
||||
/* simulated temperature measurements in F */
|
||||
static const uint32_t appHwTempF[APP_NUM_TM] =
|
||||
{
|
||||
FLT_TO_UINT32(985, -1),
|
||||
FLT_TO_UINT32(986, -1),
|
||||
FLT_TO_UINT32(987, -1),
|
||||
FLT_TO_UINT32(988, -1),
|
||||
FLT_TO_UINT32(989, -1),
|
||||
FLT_TO_UINT32(990, -1)
|
||||
};
|
||||
|
||||
/* simulated measurement index */
|
||||
static uint8_t appHwTmIdx = 0;
|
||||
|
||||
/* temperature units */
|
||||
static uint8_t appHwTmUnits = CH_TM_FLAG_UNITS_C;
|
||||
|
||||
/* weight units */
|
||||
static uint8_t appHwWmUnits = CH_WSM_FLAG_UNITS_LBS;
|
||||
|
||||
/* pulse oximeter continuous measurement */
|
||||
static const appPlxCm_t appHwPlxCm =
|
||||
{
|
||||
0, /*! Flags */
|
||||
SFLT_TO_UINT16(98, 0), /*! SpO2PR-Spot-Check - SpO2 */
|
||||
SFLT_TO_UINT16(60, 0), /*! SpO2PR-Spot-Check - Pulse Rate */
|
||||
SFLT_TO_UINT16(98, 0), /*! SpO2PR-Spot-Check Fast - SpO2 */
|
||||
SFLT_TO_UINT16(60, 0), /*! SpO2PR-Spot-Check Fast - Pulse Rate */
|
||||
SFLT_TO_UINT16(98, 0), /*! SpO2PR-Spot-Check Slow - SpO2 */
|
||||
SFLT_TO_UINT16(60, 0), /*! SpO2PR-Spot-Check Slow - Pulse Rate */
|
||||
0, /*! Measurement Status */
|
||||
0, /*! Device and Sensor Status */
|
||||
SFLT_TO_UINT16(60, 0) /*! Pulse Amplitude Index */
|
||||
};
|
||||
|
||||
/* pulse oximeter spot check measurement */
|
||||
static const appPlxScm_t appHwPlxScm =
|
||||
{
|
||||
0, /*! Flags */
|
||||
SFLT_TO_UINT16(98, 0), /*! SpO2PR-Spot-Check - SpO2 */
|
||||
SFLT_TO_UINT16(60, 0), /*! SpO2PR-Spot-Check - Pulse Rate */
|
||||
{
|
||||
2016, /*! Year */
|
||||
7, /*! Month */
|
||||
18, /*! Day */
|
||||
13, /*! Hour */
|
||||
10, /*! Minutes */
|
||||
0 /*! Seconds */
|
||||
},
|
||||
0, /*! Measurement Status */
|
||||
0, /*! Device and Sensor Status */
|
||||
SFLT_TO_UINT16(60, 0) /*! Pulse Amplitude Index */
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read the battery level. The battery level value returned in pLevel is the
|
||||
* percentage of remaining battery capacity (0-100%).
|
||||
*
|
||||
* \param pLevel Battery level return value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppHwBattRead(uint8_t *pLevel)
|
||||
{
|
||||
*pLevel = appHwBattLevel;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the battery level, for test purposes.
|
||||
*
|
||||
* \param level Battery level (0-100%).
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppHwBattTest(uint8_t level)
|
||||
{
|
||||
appHwBattLevel = level;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform a heart rate measurement. Return the heart rate along with any RR interval
|
||||
* data.
|
||||
*
|
||||
* \param pHrm Heart rate measurement return value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppHwHrmRead(appHrm_t *pHrm)
|
||||
{
|
||||
pHrm->heartRate = appHwHeartRate;
|
||||
|
||||
/* calculate simulated RR intervals from heart rate */
|
||||
appHwRrInterval[0] = APP_HR_MEAS_SIM_RR(appHwHeartRate);
|
||||
appHwRrInterval[1] = APP_HR_MEAS_SIM_RR(appHwHeartRate);
|
||||
|
||||
pHrm->pRrInterval = appHwRrInterval;
|
||||
pHrm->numIntervals = 2;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the heart rate, for test purposes.
|
||||
*
|
||||
* \param heartRate Heart rate.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppHwHrmTest(uint8_t heartRate)
|
||||
{
|
||||
appHwHeartRate = heartRate;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform a blood pressure measurement. Return the measurement data.
|
||||
*
|
||||
* \param intermed TRUE if this is an intermediate measurement.
|
||||
* \param pHrm Blood pressure measurement return value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppHwBpmRead(bool_t intermed, appBpm_t *pBpm)
|
||||
{
|
||||
appHwSimBpm_t *pMeas;
|
||||
|
||||
/* set pointer to simulated measurement data */
|
||||
if (intermed == TRUE)
|
||||
{
|
||||
pMeas = (appHwSimBpm_t *) &appHwIntBpm[appHwIntBpmIdx];
|
||||
|
||||
/* increment index to set up for next measurement */
|
||||
if (++appHwIntBpmIdx == APP_NUM_INT_BPM)
|
||||
{
|
||||
appHwIntBpmIdx = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pMeas = (appHwSimBpm_t *) &appHwBpm[appHwBpmIdx];
|
||||
|
||||
/* increment index to set up for next measurement */
|
||||
if (++appHwBpmIdx == APP_NUM_BPM)
|
||||
{
|
||||
appHwBpmIdx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* set measurement values */
|
||||
pBpm->diastolic = pMeas->diastolic;
|
||||
pBpm->systolic = pMeas->systolic;
|
||||
pBpm->map = pMeas->map;
|
||||
pBpm->pulseRate = pMeas->pulseRate;
|
||||
pBpm->measStatus = pMeas->measStatus;
|
||||
|
||||
/* set the timestamp */
|
||||
pBpm->timestamp = appHwDateTime;
|
||||
|
||||
/* increment seconds field to simulate time */
|
||||
appHwDateTime.sec += 2;
|
||||
if (appHwDateTime.sec > 59)
|
||||
{
|
||||
appHwDateTime.sec = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform a weight scale measurement. Return the measurement data.
|
||||
*
|
||||
* \param pWsm Weight scale measurement return value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppHwWsmRead(appWsm_t *pWsm)
|
||||
{
|
||||
|
||||
/* set measurement values */
|
||||
if (appHwWmUnits == CH_WSM_FLAG_UNITS_LBS)
|
||||
{
|
||||
pWsm->weight = appHwWeightLbs[appHwWsmIdx];
|
||||
}
|
||||
else
|
||||
{
|
||||
pWsm->weight = appHwWeightKg[appHwWsmIdx];
|
||||
}
|
||||
|
||||
/* set the timestamp */
|
||||
pWsm->timestamp = appHwDateTime;
|
||||
|
||||
/* increment minutes field to simulate time */
|
||||
appHwDateTime.min++;
|
||||
if (appHwDateTime.min > 59)
|
||||
{
|
||||
appHwDateTime.min = 0;
|
||||
}
|
||||
|
||||
/* increment index to set up for next simulated measurement */
|
||||
if (++appHwWsmIdx == APP_NUM_BPM)
|
||||
{
|
||||
appHwWsmIdx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform a temperature measurement. Return the measurement data.
|
||||
*
|
||||
* \param intermed TRUE if this is an intermediate measurement.
|
||||
* \param pBpm Temperature measurement return value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppHwTmRead(bool_t intermed, appTm_t *pTm)
|
||||
{
|
||||
/* set measurement value */
|
||||
if (appHwTmUnits == CH_TM_FLAG_UNITS_C)
|
||||
{
|
||||
pTm->temperature = appHwTempC[appHwTmIdx];
|
||||
}
|
||||
else
|
||||
{
|
||||
pTm->temperature = appHwTempF[appHwTmIdx];
|
||||
}
|
||||
|
||||
/* increment index to set up for next measurement */
|
||||
if (++appHwTmIdx == APP_NUM_TM)
|
||||
{
|
||||
appHwTmIdx = 0;
|
||||
}
|
||||
|
||||
/* set the temperature type */
|
||||
pTm->tempType = CH_TT_BODY;
|
||||
|
||||
/* set the timestamp */
|
||||
pTm->timestamp = appHwDateTime;
|
||||
|
||||
/* increment seconds field to simulate time */
|
||||
appHwDateTime.sec += 2;
|
||||
if (appHwDateTime.sec > 59)
|
||||
{
|
||||
appHwDateTime.sec = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the temperature measurement units.
|
||||
*
|
||||
* \param units CH_TM_FLAG_UNITS_C or CH_TM_FLAG_UNITS_F.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppHwTmSetUnits(uint8_t units)
|
||||
{
|
||||
appHwTmUnits = units;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the weight measurement units.
|
||||
*
|
||||
* \param units CH_WSM_FLAG_UNITS_KG or CH_WSM_FLAG_UNITS_LBS.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppHwWmSetUnits(uint8_t units)
|
||||
{
|
||||
appHwWmUnits = units;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform a pulse oximeter measurement.
|
||||
*
|
||||
* \param pPlxcm Pulse Oximeter measurement return value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppHwPlxcmRead(appPlxCm_t *pPlxcm)
|
||||
{
|
||||
memcpy(pPlxcm, &appHwPlxCm, sizeof(appPlxCm_t));
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform a pulse oximeter spot check measurement.
|
||||
*
|
||||
* \param pPlxcm Pulse Oximeter spot check measurement return value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppHwPlxscmRead(appPlxScm_t *pPlxscm)
|
||||
{
|
||||
memcpy(pPlxscm, &appHwPlxScm, sizeof(appPlxScm_t));
|
||||
}
|
||||
Vendored
+355
@@ -0,0 +1,355 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Application framework user interface.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_os.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "app_ui.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! \brief Callback struct */
|
||||
appUiCback_t appUiCbackTbl;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform a user interface action based on the event value passed to the function.
|
||||
*
|
||||
* \param event User interface event value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppUiAction(uint8_t event)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case APP_UI_NONE:
|
||||
/* no action */
|
||||
break;
|
||||
|
||||
case APP_UI_RESET_CMPL:
|
||||
APP_TRACE_INFO0(">>> Reset complete <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_ADV_START:
|
||||
APP_TRACE_INFO0(">>> Advertising started <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_ADV_STOP:
|
||||
APP_TRACE_INFO0(">>> Advertising stopped <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_SCAN_START:
|
||||
APP_TRACE_INFO0(">>> Scanning started <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_SCAN_STOP:
|
||||
APP_TRACE_INFO0(">>> Scanning stopped <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_SCAN_REPORT:
|
||||
APP_TRACE_INFO0(">>> Scan data received from peer <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_CONN_OPEN:
|
||||
APP_TRACE_INFO0(">>> Connection opened <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_CONN_CLOSE:
|
||||
APP_TRACE_INFO0(">>> Connection closed <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_SEC_PAIR_CMPL:
|
||||
APP_TRACE_INFO0(">>> Pairing completed successfully <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_SEC_PAIR_FAIL:
|
||||
APP_TRACE_INFO0(">>> Pairing failed <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_SEC_ENCRYPT:
|
||||
APP_TRACE_INFO0(">>> Connection encrypted <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_SEC_ENCRYPT_FAIL:
|
||||
APP_TRACE_INFO0(">>> Encryption failed <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_PASSKEY_PROMPT:
|
||||
APP_TRACE_INFO0(">>> Prompt user to enter passkey <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_ALERT_CANCEL:
|
||||
APP_TRACE_INFO0(">>> Cancel a low or high alert <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_ALERT_LOW:
|
||||
APP_TRACE_INFO0(">>> Low alert <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_ALERT_HIGH:
|
||||
APP_TRACE_INFO0(">>> High alert <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_ADV_SET_START_IND:
|
||||
APP_TRACE_INFO0(">>> Advertising sets started <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_ADV_SET_STOP_IND:
|
||||
APP_TRACE_INFO0(">>> Advertising sets stopped <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_SCAN_REQ_RCVD_IND:
|
||||
APP_TRACE_INFO0(">>> Scan request received <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_EXT_SCAN_START_IND:
|
||||
APP_TRACE_INFO0(">>> Extended scanning started <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_EXT_SCAN_STOP_IND:
|
||||
APP_TRACE_INFO0(">>> Extended scanning stopped <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_PER_ADV_SET_START_IND:
|
||||
APP_TRACE_INFO0(">>> Periodic advertising set started <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_PER_ADV_SET_STOP_IND:
|
||||
APP_TRACE_INFO0(">>> Periodic advertising set stopped <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_PER_ADV_SYNC_EST_IND:
|
||||
APP_TRACE_INFO0(">>> Periodic advertising sync established <<<");
|
||||
break;
|
||||
|
||||
case APP_UI_PER_ADV_SYNC_LOST_IND:
|
||||
APP_TRACE_INFO0(">>> Periodic advertising sync lost <<<");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (appUiCbackTbl.actionCback)
|
||||
{
|
||||
(*appUiCbackTbl.actionCback)(event);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Display a passkey.
|
||||
*
|
||||
* \param passkey Passkey to display.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppUiDisplayPasskey(uint32_t passkey)
|
||||
{
|
||||
APP_TRACE_INFO1(">>> Passkey: %d <<<", passkey);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Display a confirmation value.
|
||||
*
|
||||
* \param confirm Confirm value to display.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppUiDisplayConfirmValue(uint32_t confirm)
|
||||
{
|
||||
APP_TRACE_INFO1(">>> Confirm Value: %d <<<", confirm);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Display an RSSI value.
|
||||
*
|
||||
* \param rssi Rssi value to display.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppUiDisplayRssi(int8_t rssi)
|
||||
{
|
||||
APP_TRACE_INFO1(">>> RSSI: %d dBm <<<", rssi);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a UI timer expiration event.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void appUiTimerExpired(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform button press polling. This function is called to handle WSF
|
||||
* message APP_BTN_POLL_IND.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void appUiBtnPoll(void)
|
||||
{
|
||||
if (appUiCbackTbl.btnPollCback)
|
||||
{
|
||||
(*appUiCbackTbl.btnPollCback)();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a hardware button press. This function is called to handle WSF
|
||||
* event APP_BTN_DOWN_EVT.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppUiBtnPressed(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a callback function to receive application button press events.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note Registered by application to receive button events
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppUiBtnRegister(appUiBtnCback_t btnCback)
|
||||
{
|
||||
appUiCbackTbl.btnCback = btnCback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a callback function to receive action events.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note Registered by platform
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppUiActionRegister(appUiActionCback_t actionCback)
|
||||
{
|
||||
appUiCbackTbl.actionCback = actionCback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a callback function to receive APP_BTN_POLL_IND events.
|
||||
*
|
||||
* \return None.
|
||||
*
|
||||
* \note Registered by platform
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppUiBtnPollRegister(appUiBtnPollCback_t btnPollCback)
|
||||
{
|
||||
appUiCbackTbl.btnPollCback = btnPollCback;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Play a sound.
|
||||
*
|
||||
* \param pSound Pointer to sound tone/duration array.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppUiSoundPlay(const appUiSound_t *pSound)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop the sound that is currently playing.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppUiSoundStop(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*
|
||||
* \fn AppUiLedStart
|
||||
*
|
||||
* \brief Start LED blinking.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppUiLedStart(const appUiLed_t *pLed)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*
|
||||
* \fn AppUiLedStop
|
||||
*
|
||||
* \brief Stop LED blinking.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppUiLedStop(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Button test function-- for test purposes only.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AppUiBtnTest(uint8_t btn)
|
||||
{
|
||||
if (appUiCbackTbl.btnCback)
|
||||
{
|
||||
(*appUiCbackTbl.btnCback)(btn);
|
||||
}
|
||||
}
|
||||
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief User Interface - Console
|
||||
*
|
||||
* Copyright (c) 2017-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "util/bda.h"
|
||||
#include "app_api.h"
|
||||
#include "app_main.h"
|
||||
#include "app_db.h"
|
||||
#include "app_cfg.h"
|
||||
#include "ui_api.h"
|
||||
#include "wsf_trace.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Prototypes
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Action Function Prototypes */
|
||||
static void uiConsoleDispSplash(const UiSplashScreen_t *pSplash);
|
||||
static void uiConsoleDispMenu(const UiMenu_t *pMenu);
|
||||
static void uiConsoleDispDialog(const UiDialog_t *pDialog);
|
||||
static void uiConsoleProcessKey(uint8_t input);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Console Control Block */
|
||||
struct
|
||||
{
|
||||
int8_t alphaNumOffset;
|
||||
} uiConsoleCb;
|
||||
|
||||
/* Console display action functions */
|
||||
UiActionTbl_t uiConsoleActionTbl =
|
||||
{
|
||||
uiConsoleDispSplash,
|
||||
uiConsoleDispMenu,
|
||||
uiConsoleDispDialog,
|
||||
uiConsoleProcessKey
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a key press from the user
|
||||
*
|
||||
* \param key User keypress.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiConsoleProcessKey(uint8_t key)
|
||||
{
|
||||
UiDialog_t *pDialog;
|
||||
|
||||
switch(UiCb.activeScreenType)
|
||||
{
|
||||
case UI_SCREEN_SPLASH:
|
||||
if ((key >= '0' && key <= '9') || (key == '\r'))
|
||||
{
|
||||
/* Jump to main menu on any key */
|
||||
UiLoadMenu(UiCb.pMainMenu);
|
||||
}
|
||||
break;
|
||||
|
||||
case UI_SCREEN_MENU:
|
||||
if (key >= '0' && key <= '9')
|
||||
{
|
||||
UiSelection(key - '0');
|
||||
}
|
||||
break;
|
||||
|
||||
case UI_SCREEN_DIALOG:
|
||||
pDialog = (UiDialog_t*) UiCb.pActiveScreen;
|
||||
|
||||
if (pDialog->type == UI_DLG_TYPE_INPUT_SELECT)
|
||||
{
|
||||
if (key > '0' && key <= '9')
|
||||
{
|
||||
/* User pressed a number from 1 - 9 for selection */
|
||||
UiSelection(key - '0');
|
||||
}
|
||||
else if (key == '\r')
|
||||
{
|
||||
/* User pressed enter on pause screen */
|
||||
UiSelection(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (uiConsoleCb.alphaNumOffset < pDialog->entryMaxLen)
|
||||
{
|
||||
/* TODO: Add backspace key? */
|
||||
|
||||
/* User in process of entering alpha numeric input */
|
||||
pDialog->pEntry[uiConsoleCb.alphaNumOffset++] = key;
|
||||
}
|
||||
else
|
||||
{
|
||||
pDialog->pEntry[uiConsoleCb.alphaNumOffset] = '\0';
|
||||
uiConsoleCb.alphaNumOffset = 0;
|
||||
|
||||
/* Notify callback of selection */
|
||||
UiSelection(0);
|
||||
|
||||
/* Exit to parent */
|
||||
UiLoadMenu(pDialog->base.pParentMenu);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Display a splash screen on a Console
|
||||
*
|
||||
* \param pSplash Pointer to the splash screen object to display.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiConsoleDispSplash(const UiSplashScreen_t *pSplash)
|
||||
{
|
||||
/* Print the splash widget identifier */
|
||||
UiConsolePrintLn("{Splash}");
|
||||
|
||||
/* Print splash screen */
|
||||
UiConsolePrint(pSplash->pAppName);
|
||||
UiConsolePrint(", ");
|
||||
UiConsolePrintLn(pSplash->pAppVer);
|
||||
UiConsolePrintLn(pSplash->pCopyright);
|
||||
|
||||
UiConsoleFlush();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Display a menu on a Console
|
||||
*
|
||||
* \param pMenu Pointer to the menu object to display.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiConsoleDispMenu(const UiMenu_t *pMenu)
|
||||
{
|
||||
int8_t i;
|
||||
char ch[2];
|
||||
|
||||
/* Print the menu widget identifier */
|
||||
UiConsolePrint("\r\n");
|
||||
UiConsolePrintLn("{Menu}");
|
||||
|
||||
/* Print the title to the Console */
|
||||
UiConsolePrintLn(pMenu->pTitle);
|
||||
|
||||
/* Print the menu items */
|
||||
for (i = 0; i < pMenu->numItems; i++)
|
||||
{
|
||||
UiConsolePrint(" ");
|
||||
ch[0] = '1' + i;
|
||||
ch[1] = '\0';
|
||||
UiConsolePrint(ch);
|
||||
UiConsolePrint(". ");
|
||||
UiConsolePrintLn(pMenu->pItems[i]);
|
||||
}
|
||||
|
||||
UiConsolePrint("\r\n");
|
||||
UiConsolePrint("Choice? ");
|
||||
|
||||
UiConsoleFlush();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Display a dialog on a Console
|
||||
*
|
||||
* \param pDialog Pointer to the dialog object to display.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiConsoleDispDialog(const UiDialog_t *pDialog)
|
||||
{
|
||||
int8_t i;
|
||||
char ch[2];
|
||||
|
||||
/* Print the dialog widget identifier */
|
||||
UiConsolePrint("\r\n");
|
||||
UiConsolePrintLn("{Dialog}");
|
||||
|
||||
/* Print the title to the Console */
|
||||
UiConsolePrintLn(pDialog->pTitle);
|
||||
|
||||
/* Print the message to the Console */
|
||||
UiConsolePrintLn(pDialog->pMsg);
|
||||
|
||||
if (pDialog->type == UI_DLG_TYPE_INPUT_SELECT)
|
||||
{
|
||||
if (pDialog->numSelectItems == 0)
|
||||
{
|
||||
UiConsolePrintLn("ENTER to continue");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Print the dialog items */
|
||||
for (i = 0; i < pDialog->numSelectItems; i++)
|
||||
{
|
||||
UiConsolePrint(" ");
|
||||
ch[0] = '1' + i;
|
||||
ch[1] = '\0';
|
||||
UiConsolePrint(ch);
|
||||
UiConsolePrint(". ");
|
||||
UiConsolePrintLn(pDialog->pSelectItems[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Print prompt */
|
||||
UiConsolePrint("> ");
|
||||
UiConsolePrint(pDialog->pEntry);
|
||||
}
|
||||
|
||||
UiConsoleFlush();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the Console User Interface
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UiConsoleInit(void)
|
||||
{
|
||||
uiConsoleCb.alphaNumOffset = 0;
|
||||
UiRegisterDisplay(uiConsoleActionTbl, UI_DISPLAY_CONSOLE);
|
||||
}
|
||||
Vendored
+744
@@ -0,0 +1,744 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief User Interface - LCD
|
||||
*
|
||||
* Copyright (c) 2017-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "util/bda.h"
|
||||
#include "app_api.h"
|
||||
#include "app_main.h"
|
||||
#include "app_db.h"
|
||||
#include "app_cfg.h"
|
||||
#include "ui_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Get these values from driver? */
|
||||
#define LCD_NUM_LINES 4
|
||||
#define LCD_LINE_LEN 20
|
||||
#define LCD_SEL_COL_WIDTH 2
|
||||
|
||||
/* Map button press to LCD actions */
|
||||
#define LCD_KEYPRESS_UP UI_INPUT_BTN_UP
|
||||
#define LCD_KEYPRESS_DOWN UI_INPUT_BTN_DOWN
|
||||
#define LCD_KEYPRESS_SELECT UI_INPUT_BTN_SELECT
|
||||
|
||||
/* Scroll timeout in ms */
|
||||
#define LCD_SCROLL_LONG_TO 1000
|
||||
#define LCD_SCROLL_SHORT_TO 250
|
||||
|
||||
#define LCD_PROMPT_STR ">>"
|
||||
#define LCD_RO_PROMPT_STR "*>"
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Prototypes
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! LCD display action function prototypes */
|
||||
static void uiLcdDispSplash(const UiSplashScreen_t *pSplash);
|
||||
static void uiLcdDispMenu(const UiMenu_t *pMenu);
|
||||
static void uiLcdDispDialog(const UiDialog_t *pDialog);
|
||||
static void uiLcdProcessKey(uint8_t key);
|
||||
|
||||
/*! Process Key action functions */
|
||||
static void uiLcdProcessSplashKey(uint8_t key);
|
||||
static void uiLcdProcessMenuKey(uint8_t key);
|
||||
static void uiLcdProcessDialogKey(uint8_t key);
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* LCD Control Block */
|
||||
struct
|
||||
{
|
||||
int8_t scrollOffset;
|
||||
int8_t alphaNumOffset;
|
||||
} uiLcdCb;
|
||||
|
||||
/* LCD Process Key action functions */
|
||||
UiKeyPress_t uiLcdProcessKeyTbl[] =
|
||||
{
|
||||
uiLcdProcessSplashKey,
|
||||
uiLcdProcessMenuKey,
|
||||
uiLcdProcessDialogKey
|
||||
};
|
||||
|
||||
/* LCD display action functions */
|
||||
static UiActionTbl_t uiLcdActionTbl =
|
||||
{
|
||||
uiLcdDispSplash,
|
||||
uiLcdDispMenu,
|
||||
uiLcdDispDialog,
|
||||
uiLcdProcessKey
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a key press from the user
|
||||
*
|
||||
* \param key User keypress.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiLcdProcessKey(uint8_t key)
|
||||
{
|
||||
uiLcdProcessKeyTbl[UiCb.activeScreenType](key);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a key press from the user - Splash screens
|
||||
*
|
||||
* \param key User keypress.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiLcdProcessSplashKey(uint8_t key)
|
||||
{
|
||||
switch(key)
|
||||
{
|
||||
case LCD_KEYPRESS_UP:
|
||||
case LCD_KEYPRESS_DOWN:
|
||||
case LCD_KEYPRESS_SELECT:
|
||||
default:
|
||||
/* Proceed to main menu */
|
||||
UiLoadMenu(UiCb.pMainMenu);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a key press from the user - Menu screens
|
||||
*
|
||||
* \param key User keypress.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiLcdProcessMenuKey(uint8_t key)
|
||||
{
|
||||
UiMenu_t *pMenu = (UiMenu_t*) UiCb.pActiveScreen;
|
||||
|
||||
switch(key)
|
||||
{
|
||||
case LCD_KEYPRESS_UP:
|
||||
/* Move the highlight up or wrap down if at top */
|
||||
uiLcdCb.scrollOffset = 0;
|
||||
pMenu->highlight = (pMenu->highlight > 0) ? (pMenu->highlight - 1) : (pMenu->numItems - 1);
|
||||
UiRefresh();
|
||||
break;
|
||||
|
||||
case LCD_KEYPRESS_DOWN:
|
||||
/* Move the highlight down or wrap up if at bottom */
|
||||
uiLcdCb.scrollOffset = 0;
|
||||
pMenu->highlight = (pMenu->highlight + 1) % (pMenu->numItems);
|
||||
UiRefresh();
|
||||
break;
|
||||
|
||||
case LCD_KEYPRESS_SELECT:
|
||||
/* Select the highlighted item */
|
||||
uiLcdCb.scrollOffset = 0;
|
||||
UiSelection(pMenu->highlight + 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Select the next character depending on the type of dialog and if the up or down button
|
||||
* was pressed.
|
||||
*
|
||||
* \param pDialog Pointer to the dialog
|
||||
* \param up TRUE if up key is pressed, else the down key
|
||||
*
|
||||
* \return The next character to display
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static char uiLcdAlphaNumNextChar(const UiDialog_t *pDialog, bool_t up)
|
||||
{
|
||||
uint8_t offset = uiLcdCb.alphaNumOffset;
|
||||
char ch = pDialog->pEntry[offset];
|
||||
|
||||
if (pDialog->type == UI_DLG_TYPE_INPUT_NUM)
|
||||
{
|
||||
/* Cycle through 0-9 */
|
||||
if (up)
|
||||
{
|
||||
ch = (ch < '9') ? ((ch >= '0') ? ch + 1 : '0'): '0';
|
||||
}
|
||||
else
|
||||
{
|
||||
ch = (ch > '0') ? ((ch <= '9') ? ch - 1 : '9') : '9';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Cycle through space, 0-9, underscore, A-Z */
|
||||
if (up)
|
||||
{
|
||||
ch = (ch == ' ') ? '0' : (ch == '9') ? '_' : (ch == '_') ? 'A' : (ch == 'Z') ? ' ' : (ch < ' ' || ch > '_') ? ' ' : ch + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ch = (ch == ' ') ? 'Z' : (ch == 'A') ? '_' : (ch == '_') ? '9' : (ch == '0') ? ' ' : (ch < ' ' || ch > '_') ? 'Z' :ch - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ch;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a key press from the user - Numeric or alpha-numeric dialog screens
|
||||
*
|
||||
* \param key User keypress.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiLcdProcessDialogAlphaNum(uint8_t key)
|
||||
{
|
||||
UiDialog_t *pDialog = (UiDialog_t*) UiCb.pActiveScreen;
|
||||
uint8_t offset = uiLcdCb.alphaNumOffset;
|
||||
|
||||
switch(key)
|
||||
{
|
||||
case LCD_KEYPRESS_UP:
|
||||
if (offset < pDialog->entryMaxLen)
|
||||
{
|
||||
/* offset in range, change character */
|
||||
pDialog->pEntry[offset] = uiLcdAlphaNumNextChar(pDialog, TRUE);
|
||||
}
|
||||
else if (pDialog->highlight > 0)
|
||||
{
|
||||
/* offset out of range, move selection */
|
||||
pDialog->highlight--;
|
||||
}
|
||||
break;
|
||||
|
||||
case LCD_KEYPRESS_DOWN:
|
||||
if (offset < pDialog->entryMaxLen)
|
||||
{
|
||||
/* offset in range, change character */
|
||||
pDialog->pEntry[offset] = uiLcdAlphaNumNextChar(pDialog, FALSE);
|
||||
}
|
||||
else if (pDialog->highlight < 1)
|
||||
{
|
||||
/* offset out of range, move selection */
|
||||
pDialog->highlight++;
|
||||
}
|
||||
break;
|
||||
|
||||
case LCD_KEYPRESS_SELECT:
|
||||
if (offset < pDialog->entryMaxLen)
|
||||
{
|
||||
/* offset in range, move lower carrot to next character */
|
||||
uiLcdCb.alphaNumOffset++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* offset out of range, select the highlight */
|
||||
if (pDialog->highlight == 0)
|
||||
{
|
||||
uiLcdCb.alphaNumOffset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* User is done */
|
||||
uiLcdCb.alphaNumOffset = 0;
|
||||
pDialog->highlight = 0;
|
||||
|
||||
/* Notify callback of selection */
|
||||
UiSelection(0);
|
||||
|
||||
/* Exit to parent */
|
||||
UiLoadMenu(pDialog->base.pParentMenu);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
UiRefresh();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a key press from the user - Select Dialog Screens
|
||||
*
|
||||
* \param key User keypress.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiLcdProcessDialogSelect(uint8_t key)
|
||||
{
|
||||
UiDialog_t *pDialog = (UiDialog_t*) UiCb.pActiveScreen;
|
||||
|
||||
switch(key)
|
||||
{
|
||||
case LCD_KEYPRESS_UP:
|
||||
/* Move the highlight up */
|
||||
if (pDialog->highlight > 0)
|
||||
{
|
||||
uiLcdCb.scrollOffset = 0;
|
||||
pDialog->highlight--;
|
||||
UiRefresh();
|
||||
}
|
||||
break;
|
||||
|
||||
case LCD_KEYPRESS_DOWN:
|
||||
/* Move the highlight down */
|
||||
if (pDialog->highlight < pDialog->numSelectItems - 1)
|
||||
{
|
||||
uiLcdCb.scrollOffset = 0;
|
||||
pDialog->highlight++;
|
||||
UiRefresh();
|
||||
}
|
||||
break;
|
||||
|
||||
case LCD_KEYPRESS_SELECT:
|
||||
/* Select the highlighted item */
|
||||
uiLcdCb.scrollOffset = 0;
|
||||
UiSelection(pDialog->highlight + 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a key press from the user - Dialog Screens
|
||||
*
|
||||
* \param key User keypress.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiLcdProcessDialogKey(uint8_t key)
|
||||
{
|
||||
UiDialog_t *pDialog = (UiDialog_t*) UiCb.pActiveScreen;
|
||||
|
||||
/* process input based on dialog type (select vs alpha numeric input) */
|
||||
switch(pDialog->type)
|
||||
{
|
||||
case UI_DLG_TYPE_INPUT_NUM:
|
||||
case UI_DLG_TYPE_INPUT_ALPHANUM:
|
||||
uiLcdProcessDialogAlphaNum(key);
|
||||
break;
|
||||
|
||||
case UI_DLG_TYPE_INPUT_SELECT:
|
||||
uiLcdProcessDialogSelect(key);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a timer tick from the UI layer
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiLcdScrollCallback(void)
|
||||
{
|
||||
if (UiCb.activeScreenType == UI_SCREEN_MENU)
|
||||
{
|
||||
UiMenu_t *pMenu = (UiMenu_t *) UiCb.pActiveScreen;
|
||||
|
||||
/* scroll the higlighted menu item if necessary */
|
||||
if (++uiLcdCb.scrollOffset > (int8_t) (strlen(pMenu->pItems[pMenu->highlight]) - (LCD_LINE_LEN - LCD_SEL_COL_WIDTH)))
|
||||
{
|
||||
uiLcdCb.scrollOffset = 0;
|
||||
}
|
||||
|
||||
UiRefresh();
|
||||
}
|
||||
else if (UiCb.activeScreenType == UI_SCREEN_DIALOG)
|
||||
{
|
||||
UiDialog_t *pDialog = (UiDialog_t *) UiCb.pActiveScreen;
|
||||
|
||||
/* Scroll the highlighted select items if necessary */
|
||||
if (++uiLcdCb.scrollOffset > (int8_t) (strlen(pDialog->pSelectItems[pDialog->highlight]) - (LCD_LINE_LEN - LCD_SEL_COL_WIDTH)))
|
||||
{
|
||||
uiLcdCb.scrollOffset = 0;
|
||||
}
|
||||
|
||||
UiRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Display a splash screen on an LCD
|
||||
*
|
||||
* \param pSplash Pointer to the splash screen object to display.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiLcdDispSplash(const UiSplashScreen_t *pSplash)
|
||||
{
|
||||
/* Display the splash screen */
|
||||
UiLcdWriteLine(0, pSplash->pAppName);
|
||||
UiLcdWriteLine(1, pSplash->pAppVer);
|
||||
UiLcdWriteLine(2, "");
|
||||
UiLcdWriteLine(3, pSplash->pCopyright);
|
||||
|
||||
UiLcdSetDataPrepared();
|
||||
UiLcdFlush();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Display a title on an LCD with brackets around it
|
||||
*
|
||||
* \param title Title to display.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiLcdDispTitle(const char *title)
|
||||
{
|
||||
char line[LCD_LINE_LEN + 1];
|
||||
|
||||
memset(line, '\0', LCD_LINE_LEN+1);
|
||||
|
||||
/* Put brackets around the title */
|
||||
line[0] = '[';
|
||||
strncpy(line+1, title, LCD_LINE_LEN-2);
|
||||
strcat(line, "]");
|
||||
|
||||
/* Print the title to the LCD */
|
||||
UiLcdWriteLine(0, line);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Calculate first and last item displayed on the screen
|
||||
*
|
||||
* \param pStart Pointer hold the first item ID
|
||||
* \param pEnd Pointer hold the last item ID (one greater than last)
|
||||
* \param numLines Number of lines to display items on the screen
|
||||
* \param highlight ID of the highlighted item
|
||||
* \param numItems Total number of items
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void appCalcFirstAndLastItem(int8_t *pStart, int8_t *pEnd, uint8_t numLines,
|
||||
uint8_t highlight, uint8_t numItems)
|
||||
{
|
||||
int8_t start, end;
|
||||
|
||||
/* Assume the highligh will be in the center */
|
||||
start = highlight - (numLines - 1) / 2;
|
||||
|
||||
/* If centering the highlight puts the start below zero, start at the beginning */
|
||||
if (start < 0)
|
||||
{
|
||||
start = 0;
|
||||
}
|
||||
|
||||
/* Assume the end is the start plus the number of screen lines */
|
||||
end = start + numLines;
|
||||
|
||||
/* Check to see if the end overflows */
|
||||
if (end > numItems)
|
||||
{
|
||||
end = numItems;
|
||||
|
||||
/* The last item should be as close to the bottom of the screen as possible */
|
||||
/* Adjust the start to accomodate this */
|
||||
start = end - numLines;
|
||||
|
||||
if (start < 0)
|
||||
{
|
||||
start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Save the result */
|
||||
*pStart = start;
|
||||
*pEnd = end;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief process the scrolling of long highlight items
|
||||
*
|
||||
* \param pText Pointer to the highlighted text.
|
||||
* \param offset The offset of the scroll.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t uiLcdProcessScroll(const char *pText, uint8_t offset)
|
||||
{
|
||||
bool_t scrolling = FALSE;
|
||||
uint8_t len = (uint8_t) strlen(pText);
|
||||
|
||||
/* Check if the item length is grater than the LCD (minus space for the select carrot) */
|
||||
if (len > LCD_LINE_LEN - LCD_SEL_COL_WIDTH)
|
||||
{
|
||||
uint16_t timeout = LCD_SCROLL_SHORT_TO;
|
||||
|
||||
scrolling = TRUE;
|
||||
|
||||
/* Delay longer at the beginning and the end of the scroll */
|
||||
if ((offset == 0) || (offset == (len - (LCD_LINE_LEN - LCD_SEL_COL_WIDTH))))
|
||||
{
|
||||
timeout = LCD_SCROLL_LONG_TO;
|
||||
}
|
||||
|
||||
/* Start the tick timer */
|
||||
UiScrollTimerStart(uiLcdScrollCallback, timeout);
|
||||
}
|
||||
|
||||
return scrolling;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Display a menu on an LCD
|
||||
*
|
||||
* \param pMenu Pointer to the menu object to display.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiLcdDispMenu(const UiMenu_t *pMenu)
|
||||
{
|
||||
int8_t i, start, end, pos = 1;
|
||||
bool_t scrolling = FALSE;
|
||||
|
||||
WSF_ASSERT(pMenu);
|
||||
WSF_ASSERT(pMenu->pTitle);
|
||||
|
||||
/* Print the title to the LCD */
|
||||
uiLcdDispTitle(pMenu->pTitle);
|
||||
|
||||
/* Calculate first and last item displayed on the screen */
|
||||
appCalcFirstAndLastItem(&start, &end, LCD_NUM_LINES-1, pMenu->highlight, pMenu->numItems);
|
||||
|
||||
/* Display menu items */
|
||||
for (i = start; i < end; i++)
|
||||
{
|
||||
int8_t offset = 0;
|
||||
char line[LCD_LINE_LEN+1];
|
||||
|
||||
line[0] = ' ';
|
||||
line[1] = ' ';
|
||||
|
||||
if (i == pMenu->highlight)
|
||||
{
|
||||
/* Add the highlight to the line */
|
||||
if (pMenu->readOnlyMask & (1<<i))
|
||||
{
|
||||
strncpy(line, LCD_RO_PROMPT_STR, sizeof(line));
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(line, LCD_PROMPT_STR, sizeof(line));
|
||||
}
|
||||
|
||||
/* Check if we need to scroll through a long item */
|
||||
offset = uiLcdCb.scrollOffset;
|
||||
scrolling = uiLcdProcessScroll(pMenu->pItems[i], offset);
|
||||
}
|
||||
|
||||
strncpy(line+LCD_SEL_COL_WIDTH, pMenu->pItems[i] + offset, LCD_LINE_LEN-LCD_SEL_COL_WIDTH);
|
||||
UiLcdWriteLine(pos++, line);
|
||||
}
|
||||
|
||||
/* Clear unused lines */
|
||||
for (i = pos; i < LCD_NUM_LINES; i++)
|
||||
{
|
||||
UiLcdWriteLine(i, "");
|
||||
}
|
||||
|
||||
if (!scrolling)
|
||||
{
|
||||
UiScrollTimerStop();
|
||||
}
|
||||
|
||||
UiLcdSetDataPrepared();
|
||||
UiLcdFlush();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Complete display of dialog on an LCD - Dialog type alphanumeric or numeric
|
||||
*
|
||||
* \param pDialog Pointer to the dialog object to display.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiLcdDispDialogAlphaNum(const UiDialog_t *pDialog)
|
||||
{
|
||||
char line[LCD_LINE_LEN+1];
|
||||
char cmdLine[LCD_LINE_LEN+1];
|
||||
|
||||
line[0] = ' ';
|
||||
line[1] = ' ';
|
||||
|
||||
WSF_ASSERT(pDialog->pEntry);
|
||||
|
||||
strncpy(line + LCD_SEL_COL_WIDTH, pDialog->pEntry, LCD_LINE_LEN - LCD_SEL_COL_WIDTH);
|
||||
|
||||
memset(cmdLine, ' ', LCD_LINE_LEN);
|
||||
cmdLine[LCD_LINE_LEN] = '\0';
|
||||
|
||||
if (uiLcdCb.alphaNumOffset < pDialog->entryMaxLen)
|
||||
{
|
||||
/* Print a carrot under the active character */
|
||||
cmdLine[uiLcdCb.alphaNumOffset + LCD_SEL_COL_WIDTH] = '^';
|
||||
}
|
||||
else
|
||||
{
|
||||
/* After the user presses select on the last character, add a 'Done' entry item */
|
||||
strcpy(cmdLine, " Done");
|
||||
|
||||
if (pDialog->highlight == 0)
|
||||
{
|
||||
strncpy(line, LCD_PROMPT_STR, sizeof(line));
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(cmdLine, LCD_PROMPT_STR, sizeof(line));
|
||||
}
|
||||
}
|
||||
|
||||
/* Print to the LCD */
|
||||
UiLcdWriteLine(2, line);
|
||||
UiLcdWriteLine(3, cmdLine);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Complete display of dialog on an LCD - Dialog type select
|
||||
*
|
||||
* \param pDialog Pointer to the dialog object to display.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiLcdDispDialogSelect(const UiDialog_t *pDialog)
|
||||
{
|
||||
int8_t i, start, end, pos = 2;
|
||||
bool_t scrolling = FALSE;
|
||||
|
||||
/* Calculate first and last item displayed on the screen */
|
||||
appCalcFirstAndLastItem(&start, &end, LCD_NUM_LINES-2, pDialog->highlight, pDialog->numSelectItems);
|
||||
|
||||
/* Display dialog items */
|
||||
for (i = start; i < end; i++)
|
||||
{
|
||||
int8_t offset = 0;
|
||||
char line[LCD_LINE_LEN+1];
|
||||
|
||||
line[0] = ' ';
|
||||
line[1] = ' ';
|
||||
|
||||
if (i == pDialog->highlight)
|
||||
{
|
||||
/* Add the highlight to the line */
|
||||
strncpy(line, LCD_PROMPT_STR, sizeof(line));
|
||||
|
||||
/* Check if we need to scroll through a long item */
|
||||
offset = uiLcdCb.scrollOffset;
|
||||
scrolling = uiLcdProcessScroll(pDialog->pSelectItems[i], offset);
|
||||
}
|
||||
|
||||
strncpy(line+LCD_SEL_COL_WIDTH, pDialog->pSelectItems[i] + offset, LCD_LINE_LEN - LCD_SEL_COL_WIDTH);
|
||||
UiLcdWriteLine(pos++, line);
|
||||
}
|
||||
|
||||
if (!scrolling)
|
||||
{
|
||||
UiScrollTimerStop();
|
||||
}
|
||||
|
||||
/* Clear unused lines */
|
||||
for (i=pos; i<LCD_NUM_LINES; i++)
|
||||
{
|
||||
UiLcdWriteLine(i, "");
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Display a dialog on an LCD
|
||||
*
|
||||
* \param pDialog Pointer to the dialog object to display.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiLcdDispDialog(const UiDialog_t *pDialog)
|
||||
{
|
||||
/* Print the title to the LCD */
|
||||
uiLcdDispTitle(pDialog->pTitle);
|
||||
|
||||
/* Print the message to the LCD */
|
||||
UiLcdWriteLine(1, pDialog->pMsg);
|
||||
|
||||
if (pDialog->type == UI_DLG_TYPE_INPUT_SELECT)
|
||||
{
|
||||
uiLcdDispDialogSelect(pDialog);
|
||||
}
|
||||
else
|
||||
{
|
||||
uiLcdDispDialogAlphaNum(pDialog);
|
||||
}
|
||||
|
||||
UiLcdSetDataPrepared();
|
||||
UiLcdFlush();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the LCD User Interface
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UiLcdInit(void)
|
||||
{
|
||||
UiRegisterDisplay(uiLcdActionTbl, UI_DISPLAY_LCD);
|
||||
}
|
||||
Vendored
+1016
File diff suppressed because it is too large
Load Diff
+113
@@ -0,0 +1,113 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Interface between UI and platform drivers.
|
||||
*
|
||||
* Copyright (c) 2016-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_bufio.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
External Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Print a string to the console.
|
||||
*
|
||||
* \param pLine String with text to print
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UiConsolePrint(const char *pLine)
|
||||
{
|
||||
// WsfBufIoWrite((const uint8_t *) pLine, strlen(pLine));
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Print a string to the console followed by a new line.
|
||||
*
|
||||
* \param pLine String with text to print
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UiConsolePrintLn(const char *pLine)
|
||||
{
|
||||
UiConsolePrint(pLine);
|
||||
UiConsolePrint("\r\n");
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Flush the contents of the Console buffer to the display.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UiConsoleFlush()
|
||||
{
|
||||
/* Take no action. */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Write a line on the LCD
|
||||
*
|
||||
* \param line Line number
|
||||
* \param pLine String with text to write to LCD.
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UiLcdWriteLine(uint8_t line, const char *pLine)
|
||||
{
|
||||
/* Take no action. */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Flush the contents of the LCD buffer to the display
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UiLcdFlush(void)
|
||||
{
|
||||
/* Take no action. */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set flag UiDataPrepared to TRUE
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UiLcdSetDataPrepared(void)
|
||||
{
|
||||
/* Take no action. */
|
||||
}
|
||||
ambiq-hal-sys/ambiq-sparkfun-sdk/third_party/exactle/ble-profiles/sources/apps/app/common/ui_timer.c
Vendored
+209
@@ -0,0 +1,209 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief User Interface main module - WSF timer implementation.
|
||||
*
|
||||
* Copyright (c) 2018 - 2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_timer.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "ui_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Max number of active timers */
|
||||
#define UI_TIMER_WSF_MAX_TIMERS 3
|
||||
|
||||
/*! Unused timer ID */
|
||||
#define UI_TIMER_WSF_UNUSED 0
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* UI Timers */
|
||||
static wsfTimer_t uiTimerList[UI_TIMER_WSF_MAX_TIMERS];
|
||||
|
||||
/* UI timer task handler ID */
|
||||
wsfHandlerId_t iuTimerWsfHandlerId;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Add a WSF timer for a UI event.
|
||||
*
|
||||
* \param event Event to pass to UiProcEvent on timer expiration.
|
||||
*
|
||||
* \return WSF timer or NULL if all timers are in use.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static wsfTimer_t *uiTimerAddTimer(uint8_t event)
|
||||
{
|
||||
wsfTimer_t *pTimer = uiTimerList;
|
||||
int8_t i;
|
||||
|
||||
for (i = 0; i < UI_TIMER_WSF_MAX_TIMERS; i++, pTimer++)
|
||||
{
|
||||
if (pTimer->msg.event == UI_TIMER_WSF_UNUSED)
|
||||
{
|
||||
pTimer->msg.event = event;
|
||||
pTimer->handlerId = iuTimerWsfHandlerId;
|
||||
|
||||
return pTimer;
|
||||
}
|
||||
}
|
||||
|
||||
/* If ASSERT happens, increase UI_TIMER_WSF_MAX_TIMERS to needed number of active timers. */
|
||||
WSF_ASSERT(0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get the WSF timer for a UI event.
|
||||
*
|
||||
* \param event Timer event.
|
||||
* \param addTimer TRUE to add a timer if the event doesn't exist.
|
||||
*
|
||||
* \return WSF timer or NULL if timer for event does not exist.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static wsfTimer_t *uiTimerGetTimerByEvent(uint8_t event, bool_t addTimer)
|
||||
{
|
||||
wsfTimer_t *pTimer = uiTimerList;
|
||||
int8_t i;
|
||||
|
||||
for (i = 0; i < UI_TIMER_WSF_MAX_TIMERS; i++, pTimer++)
|
||||
{
|
||||
if (pTimer->msg.event == event)
|
||||
{
|
||||
return pTimer;
|
||||
}
|
||||
}
|
||||
|
||||
if (addTimer)
|
||||
{
|
||||
return uiTimerAddTimer(event);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function - for internal test.
|
||||
*
|
||||
* \param handlerID WSF handler ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void uiTimerWsfHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
iuTimerWsfHandlerId = handlerId;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for application - For internal test.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uiTimerWsfHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
uint8_t timerEvent = pMsg->event;
|
||||
wsfTimer_t *pTimer = uiTimerGetTimerByEvent(timerEvent, FALSE);
|
||||
|
||||
if (pTimer)
|
||||
{
|
||||
/* Free the timer */
|
||||
pTimer->msg.event = UI_TIMER_WSF_UNUSED;
|
||||
|
||||
/* Process the timer event */
|
||||
UiProcEvent(timerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start a UI timer.
|
||||
*
|
||||
* \param event Event to pass to UiProcEvent on timer expiration.
|
||||
* \param ms Time in milliseconds until timer expiration.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UiTimerStart(uint8_t event, uint32_t ms)
|
||||
{
|
||||
wsfTimer_t *pTimer = uiTimerGetTimerByEvent(event, TRUE);
|
||||
|
||||
if (pTimer)
|
||||
{
|
||||
WsfTimerStartMs(pTimer, ms);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Stop a UI timer.
|
||||
*
|
||||
* \param event Event to pass to UiProcEvent on timer expiration.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UiTimerStop(uint8_t event)
|
||||
{
|
||||
wsfTimer_t *pTimer = uiTimerGetTimerByEvent(event, FALSE);
|
||||
|
||||
if (pTimer)
|
||||
{
|
||||
/* Stop and free the timer */
|
||||
WsfTimerStop(pTimer);
|
||||
pTimer->msg.event = UI_TIMER_WSF_UNUSED;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize the UI Timer subsystem.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UiTimerInit(void)
|
||||
{
|
||||
/* Clear timers */
|
||||
memset(uiTimerList, 0, sizeof(uiTimerList));
|
||||
|
||||
/* Start timer handler */
|
||||
iuTimerWsfHandlerId = WsfOsSetNextHandler(uiTimerWsfHandler);
|
||||
uiTimerWsfHandlerInit(iuTimerWsfHandlerId);
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Asset Tracking Tag sample application.
|
||||
*
|
||||
* Copyright (c) 2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef ASSETTAG_API_H
|
||||
#define ASSETTAG_API_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AssetTagStart(void);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for App.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AssetTagHandlerInit(wsfHandlerId_t handlerId);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for the application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AssetTagHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* ASSETTAG_API_H */
|
||||
+622
@@ -0,0 +1,622 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Asset Tracking Tag sample application.
|
||||
*
|
||||
* Copyright (c) 2018 - 2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "util/bstream.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "hci_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_main.h"
|
||||
#include "app_db.h"
|
||||
#include "app_ui.h"
|
||||
#include "svc_ch.h"
|
||||
#include "svc_core.h"
|
||||
#include "svc_cte.h"
|
||||
#include "svc_batt.h"
|
||||
#include "bas/bas_api.h"
|
||||
#include "atps/atps_api.h"
|
||||
#include "gatt/gatt_api.h"
|
||||
#include "util/calc128.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Enumeration of client characteristic configuration descriptors */
|
||||
enum
|
||||
{
|
||||
ASSETTAG_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
|
||||
ASSETTAG_BATT_LVL_CCC_IDX, /*! Battery service, battery level characteristic */
|
||||
ASSETTAG_NUM_CCC_IDX
|
||||
};
|
||||
|
||||
/*! WSF message event starting value */
|
||||
#define ASSETTAG_MSG_START 0xA0
|
||||
|
||||
/*! WSF message event enumeration */
|
||||
enum
|
||||
{
|
||||
ASSETTAG_BATT_TIMER_IND = ASSETTAG_MSG_START, /*! Battery measurement timer expired */
|
||||
};
|
||||
|
||||
/*! Default MTU */
|
||||
#define ASSETTAG_DEFAULT_MTU 50
|
||||
|
||||
/**************************************************************************************************
|
||||
Configurable Parameters
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! configurable parameters for advertising */
|
||||
static const appAdvCfg_t assetTagAdvCfg =
|
||||
{
|
||||
{30000, 0, 0}, /*! Advertising durations in ms */
|
||||
{ 96, 1600, 0} /*! Advertising intervals in 0.625 ms units */
|
||||
};
|
||||
|
||||
/*! configurable parameters for slave */
|
||||
static const appSlaveCfg_t assetTagSlaveCfg =
|
||||
{
|
||||
1, /*! Maximum connections */
|
||||
};
|
||||
|
||||
/*! configurable parameters for security */
|
||||
static const appSecCfg_t assetTagSecCfg =
|
||||
{
|
||||
DM_AUTH_BOND_FLAG | SMP_AUTH_SC_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 */
|
||||
TRUE /*! TRUE to initiate security upon connection */
|
||||
};
|
||||
|
||||
/*! configurable parameters for connection parameter update */
|
||||
static const appUpdateCfg_t assetTagUpdateCfg =
|
||||
{
|
||||
0, /*! 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 */
|
||||
3, /*! Connection latency */
|
||||
600, /*! Supervision timeout in 10ms units */
|
||||
5 /*! Number of update attempts before giving up */
|
||||
};
|
||||
|
||||
/*! ATT configurable parameters (increase MTU) */
|
||||
static const attCfg_t assetTagAttCfg =
|
||||
{
|
||||
15, /* ATT server service discovery connection idle timeout in seconds */
|
||||
ASSETTAG_DEFAULT_MTU, /* desired ATT MTU */
|
||||
ATT_MAX_TRANS_TIMEOUT, /* transcation timeout in seconds */
|
||||
4 /* number of queued prepare writes supported by server */
|
||||
};
|
||||
|
||||
/*! 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 assetTagAdvDataDisc[] =
|
||||
{
|
||||
/*! flags */
|
||||
2, /*! length */
|
||||
DM_ADV_TYPE_FLAGS, /*! AD type */
|
||||
DM_FLAG_LE_GENERAL_DISC | /*! flags */
|
||||
DM_FLAG_LE_BREDR_NOT_SUP,
|
||||
|
||||
/*! manufacturer specific data */
|
||||
3, /*! length */
|
||||
DM_ADV_TYPE_MANUFACTURER, /*! AD type */
|
||||
UINT16_TO_BYTES(HCI_ID_ARM), /*! company ID */
|
||||
|
||||
/*! tx power */
|
||||
2, /*! length */
|
||||
DM_ADV_TYPE_TX_POWER, /*! AD type */
|
||||
0, /*! tx power */
|
||||
|
||||
/*! service UUID list */
|
||||
7, /*! length */
|
||||
DM_ADV_TYPE_16_UUID, /*! AD type */
|
||||
UINT16_TO_BYTES(ATT_UUID_CONSTANT_TONE_SERVICE),
|
||||
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE),
|
||||
UINT16_TO_BYTES(ATT_UUID_BATTERY_SERVICE),
|
||||
|
||||
/*! appearance */
|
||||
3, /*! length */
|
||||
DM_ADV_TYPE_APPEARANCE, /*! AD type */
|
||||
UINT16_TO_BYTES(CH_APPEAR_TAG), /*! appearance */
|
||||
};
|
||||
|
||||
/*! scan data, discoverable mode */
|
||||
static const uint8_t assetTagScanDataDisc[] =
|
||||
{
|
||||
/*! device name */
|
||||
10, /*! length */
|
||||
DM_ADV_TYPE_LOCAL_NAME, /*! AD type */
|
||||
'A',
|
||||
's',
|
||||
's',
|
||||
'e',
|
||||
't',
|
||||
' ',
|
||||
'T',
|
||||
'a',
|
||||
'g',
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Client Characteristic Configuration Descriptors
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
|
||||
static const attsCccSet_t assetTagCccSet[ASSETTAG_NUM_CCC_IDX] =
|
||||
{
|
||||
/* cccd handle value range security level */
|
||||
{ GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE }, /* ASSETTAG_GATT_SC_CCC_IDX */
|
||||
{ BATT_LVL_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE } /* ASSETTAG_BATT_LVL_CCC_IDX */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! application control block */
|
||||
static struct
|
||||
{
|
||||
wsfHandlerId_t handlerId; /* WSF handler ID */
|
||||
} assetTagCb;
|
||||
|
||||
/* Identifiers for antenna */
|
||||
static uint8_t assetTagAntennaIds[] = {0, 1};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application DM callback.
|
||||
*
|
||||
* \param pDmEvt DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void assetTagDmCback(dmEvt_t *pDmEvt)
|
||||
{
|
||||
dmEvt_t *pMsg;
|
||||
|
||||
if (pDmEvt->hdr.event == DM_SEC_ECC_KEY_IND)
|
||||
{
|
||||
DmSecSetEccKey(&pDmEvt->eccMsg.data.key);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t len = DmSizeOfEvt(pDmEvt);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(len)) != NULL)
|
||||
{
|
||||
memcpy(pMsg, pDmEvt, len);
|
||||
WsfMsgSend(assetTagCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATT callback.
|
||||
*
|
||||
* \param pEvt ATT callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void assetTagAttCback(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(assetTagCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform actions on connection open.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void assetTagOpen(dmEvt_t *pMsg)
|
||||
{
|
||||
/* Set the antenna identifiers for the connection */
|
||||
AtpsSetAntennaIds((dmConnId_t) pMsg->hdr.param, sizeof(assetTagAntennaIds), assetTagAntennaIds);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATTS client characteristic configuration callback.
|
||||
*
|
||||
* \param pMsg DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void assetTagProcCccState(attsCccEvt_t *pMsg)
|
||||
{
|
||||
APP_TRACE_INFO3("ccc state ind value:%d handle:%d idx:%d", pMsg->value, pMsg->handle, pMsg->idx);
|
||||
|
||||
/* handle battery level CCC */
|
||||
if (pMsg->idx == ASSETTAG_BATT_LVL_CCC_IDX)
|
||||
{
|
||||
if (pMsg->value == ATT_CLIENT_CFG_NOTIFY)
|
||||
{
|
||||
BasMeasBattStart((dmConnId_t)pMsg->hdr.param, ASSETTAG_BATT_TIMER_IND, ASSETTAG_BATT_LVL_CCC_IDX);
|
||||
}
|
||||
else
|
||||
{
|
||||
BasMeasBattStop((dmConnId_t)pMsg->hdr.param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATTS client characteristic configuration callback.
|
||||
*
|
||||
* \param pEvt DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void assetTagCccCback(attsCccEvt_t *pEvt)
|
||||
{
|
||||
attsCccEvt_t *pMsg;
|
||||
appDbHdl_t dbHdl;
|
||||
|
||||
/* if CCC not set from initialization and there's a device record and currently bonded */
|
||||
if ((pEvt->handle != ATT_HANDLE_NONE) &&
|
||||
((dbHdl = AppDbGetHdl((dmConnId_t)pEvt->hdr.param)) != APP_DB_HDL_NONE) &&
|
||||
AppCheckBonded((dmConnId_t)pEvt->hdr.param))
|
||||
{
|
||||
/* 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(assetTagCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Get peer key from a device database record.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return Pointer to peer key if key is valid or NULL if not valid.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static dmSecKey_t *assetTagGetPeerKey(dmEvt_t *pMsg)
|
||||
{
|
||||
appDbHdl_t dbHdl;
|
||||
|
||||
/* get device database record handle */
|
||||
dbHdl = AppDbGetHdl((dmConnId_t) pMsg->hdr.param);
|
||||
|
||||
/* if database record handle valid */
|
||||
if (dbHdl != APP_DB_HDL_NONE)
|
||||
{
|
||||
return AppDbGetKey(dbHdl, DM_KEY_IRK, NULL);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle add device to resolving list indication.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void assetTagPrivAddDevToResListInd(dmEvt_t *pMsg)
|
||||
{
|
||||
dmSecKey_t *pPeerKey;
|
||||
|
||||
/* if peer IRK present */
|
||||
if ((pPeerKey = assetTagGetPeerKey(pMsg)) != NULL)
|
||||
{
|
||||
/* set advertising peer address */
|
||||
AppSetAdvPeerAddr(pPeerKey->irk.addrType, pPeerKey->irk.bdAddr);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle remove device from resolving list indication.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void assetTagPrivRemDevFromResListInd(dmEvt_t *pMsg)
|
||||
{
|
||||
if (pMsg->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
if (AppDbGetHdl((dmConnId_t) pMsg->hdr.param) != APP_DB_HDL_NONE)
|
||||
{
|
||||
uint8_t addrZeros[BDA_ADDR_LEN] = { 0 };
|
||||
|
||||
/* clear advertising peer address and its type */
|
||||
AppSetAdvPeerAddr(HCI_ADDR_TYPE_PUBLIC, addrZeros);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set up advertising and other procedures that need to be performed after
|
||||
* device reset.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void assetTagSetup(dmEvt_t *pMsg)
|
||||
{
|
||||
/* set advertising and scan response data for discoverable mode */
|
||||
AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(assetTagAdvDataDisc), (uint8_t *) assetTagAdvDataDisc);
|
||||
AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(assetTagScanDataDisc), (uint8_t *) assetTagScanDataDisc);
|
||||
|
||||
/* set advertising and scan response data for connectable mode */
|
||||
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, sizeof(assetTagAdvDataDisc), (uint8_t *) assetTagAdvDataDisc);
|
||||
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, sizeof(assetTagScanDataDisc), (uint8_t *) assetTagScanDataDisc);
|
||||
|
||||
/* start advertising; automatically set connectable/discoverable mode and bondable mode */
|
||||
AppAdvStart(APP_MODE_AUTO_INIT);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void assetTagProcMsg(dmEvt_t *pMsg)
|
||||
{
|
||||
uint8_t uiEvent = APP_UI_NONE;
|
||||
|
||||
switch(pMsg->hdr.event)
|
||||
{
|
||||
case ATTS_CCC_STATE_IND:
|
||||
assetTagProcCccState((attsCccEvt_t*) pMsg);
|
||||
break;
|
||||
|
||||
case DM_RESET_CMPL_IND:
|
||||
AttsCalculateDbHash();
|
||||
DmSecGenerateEccKeyReq();
|
||||
assetTagSetup(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;
|
||||
break;
|
||||
|
||||
case DM_CONN_OPEN_IND:
|
||||
assetTagOpen(pMsg);
|
||||
uiEvent = APP_UI_CONN_OPEN;
|
||||
break;
|
||||
|
||||
case DM_CONN_CLOSE_IND:
|
||||
uiEvent = APP_UI_CONN_CLOSE;
|
||||
break;
|
||||
|
||||
case DM_SEC_PAIR_CMPL_IND:
|
||||
DmSecGenerateEccKeyReq();
|
||||
uiEvent = APP_UI_SEC_PAIR_CMPL;
|
||||
break;
|
||||
|
||||
case DM_SEC_PAIR_FAIL_IND:
|
||||
DmSecGenerateEccKeyReq();
|
||||
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_PRIV_ADD_DEV_TO_RES_LIST_IND:
|
||||
assetTagPrivAddDevToResListInd(pMsg);
|
||||
break;
|
||||
|
||||
case DM_PRIV_REM_DEV_FROM_RES_LIST_IND:
|
||||
assetTagPrivRemDevFromResListInd(pMsg);
|
||||
break;
|
||||
|
||||
case DM_ADV_NEW_ADDR_IND:
|
||||
break;
|
||||
|
||||
case DM_ADV_SET_START_IND:
|
||||
uiEvent = APP_UI_ADV_SET_START_IND;
|
||||
break;
|
||||
|
||||
case DM_ADV_SET_STOP_IND:
|
||||
uiEvent = APP_UI_ADV_SET_STOP_IND;
|
||||
break;
|
||||
|
||||
case DM_SCAN_REQ_RCVD_IND:
|
||||
uiEvent = APP_UI_SCAN_REQ_RCVD_IND;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (uiEvent != APP_UI_NONE)
|
||||
{
|
||||
AppUiAction(uiEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AssetTagHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
APP_TRACE_INFO0("AssetTagHandlerInit");
|
||||
|
||||
/* store handler ID */
|
||||
assetTagCb.handlerId = handlerId;
|
||||
|
||||
/* Set configuration pointers */
|
||||
pAppSlaveCfg = (appSlaveCfg_t *) &assetTagSlaveCfg;
|
||||
pAppAdvCfg = (appAdvCfg_t *) &assetTagAdvCfg;
|
||||
pAppSecCfg = (appSecCfg_t *) &assetTagSecCfg;
|
||||
pAppUpdateCfg = (appUpdateCfg_t *) &assetTagUpdateCfg;
|
||||
pAttCfg = (attCfg_t *) &assetTagAttCfg;
|
||||
|
||||
/* Initialize application framework */
|
||||
AppSlaveInit();
|
||||
AppServerInit();
|
||||
|
||||
/* Set IRK for the local device */
|
||||
DmSecSetLocalIrk(localIrk);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AssetTagHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
APP_TRACE_INFO1("AssetTag got evt %d", pMsg->event);
|
||||
|
||||
/* process ATT messages */
|
||||
if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
|
||||
{
|
||||
/* process server-related ATT messages */
|
||||
AppServerProcAttMsg(pMsg);
|
||||
}
|
||||
/* process DM messages */
|
||||
else if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
|
||||
{
|
||||
/* process advertising and connection-related messages */
|
||||
AppSlaveProcDmMsg((dmEvt_t *) pMsg);
|
||||
|
||||
/* process security-related messages */
|
||||
AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
|
||||
|
||||
/* process asset tracking profile-related messages */
|
||||
AtpsProcDmMsg((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/* perform profile and user interface-related operations */
|
||||
assetTagProcMsg((dmEvt_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void AssetTagStart(void)
|
||||
{
|
||||
/* Register for stack callbacks */
|
||||
DmRegister(assetTagDmCback);
|
||||
DmConnRegister(DM_CLIENT_ID_APP, assetTagDmCback);
|
||||
AttRegister(assetTagAttCback);
|
||||
AttConnRegister(AppServerConnCback);
|
||||
AttsCccRegister(ASSETTAG_NUM_CCC_IDX, (attsCccSet_t *) assetTagCccSet, assetTagCccCback);
|
||||
|
||||
/* Initialize attribute server database */
|
||||
SvcCoreGattCbackRegister(GattReadCback, GattWriteCback);
|
||||
SvcCoreAddGroup();
|
||||
SvcBattCbackRegister(BasReadCback, NULL);
|
||||
SvcBattAddGroup();
|
||||
|
||||
/* Set Service Changed CCCD index. */
|
||||
GattSetSvcChangedIdx(ASSETTAG_GATT_SC_CCC_IDX);
|
||||
|
||||
/* Initialize Asset Tracking Profile */
|
||||
AtpsInit();
|
||||
|
||||
/* Reset the device */
|
||||
DmDevReset();
|
||||
}
|
||||
ambiq-hal-sys/ambiq-sparkfun-sdk/third_party/exactle/ble-profiles/sources/apps/cycling/cycling_api.h
Vendored
+73
@@ -0,0 +1,73 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Cycling server sample application.
|
||||
*
|
||||
* Copyright (c) 2016-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef CYCLING_API_H
|
||||
#define CYCLING_API_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void CyclingStart(void);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for App.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void CyclingHandlerInit(wsfHandlerId_t handlerId);
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for the application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void CyclingHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* CYCLING_API_H */
|
||||
+777
@@ -0,0 +1,777 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Cycling server sample application.
|
||||
*
|
||||
* Copyright (c) 2016-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "util/bstream.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "hci_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_main.h"
|
||||
#include "app_db.h"
|
||||
#include "app_ui.h"
|
||||
#include "svc_ch.h"
|
||||
#include "svc_core.h"
|
||||
#include "svc_cps.h"
|
||||
#include "svc_cscs.h"
|
||||
#include "svc_batt.h"
|
||||
#include "bas/bas_api.h"
|
||||
#include "cpp/cpp_api.h"
|
||||
#include "cscp/cscp_api.h"
|
||||
#include "gatt/gatt_api.h"
|
||||
#include "util/calc128.h"
|
||||
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Enumeration of client characteristic configuration descriptors */
|
||||
enum
|
||||
{
|
||||
CYCLING_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
|
||||
CYCLING_CPS_CPM_CCC_IDX, /*! Cycling Power Measurement, service changed characteristic */
|
||||
CYCLING_CSCS_CSM_CCC_IDX, /*! Cycling Speed Measurement, service changed characteristic */
|
||||
CYCLING_BATT_LVL_CCC_IDX, /*! Battery service, battery level characteristic */
|
||||
CYCLING_NUM_CCC_IDX
|
||||
};
|
||||
|
||||
/*! WSF message event starting value */
|
||||
#define CYCLING_MSG_START 0xA0
|
||||
|
||||
/*! WSF message event enumeration */
|
||||
enum
|
||||
{
|
||||
CYCLING_CPP_PM_TIMER_IND = CYCLING_MSG_START, /*! Cycling power measurement timer expired */
|
||||
CYCLING_CSCP_SM_TIMER_IND, /*! Cycling speed measurement timer expired */
|
||||
CYCLING_BATT_TIMER_IND /*! Battery measurement timer expired */
|
||||
};
|
||||
|
||||
/* Default Cycling Power Measurement period (seconds) */
|
||||
#define CYCLING_DEFAULT_CPM_PERIOD 1
|
||||
|
||||
/* Default Cycling Speed Measurement period (seconds) */
|
||||
#define CYCLING_DEFAULT_CSM_PERIOD 1
|
||||
|
||||
/* Default MTU */
|
||||
#define CYCLING_DEFAULT_MTU 50
|
||||
|
||||
/**************************************************************************************************
|
||||
Configurable Parameters
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! configurable parameters for advertising */
|
||||
static const appAdvCfg_t cyclingAdvCfg =
|
||||
{
|
||||
{30000, 0, 0}, /*! Advertising durations in ms */
|
||||
{ 96, 1600, 0} /*! Advertising intervals in 0.625 ms units */
|
||||
};
|
||||
|
||||
/*! configurable parameters for slave */
|
||||
static const appSlaveCfg_t cyclingSlaveCfg =
|
||||
{
|
||||
1, /*! Maximum connections */
|
||||
};
|
||||
|
||||
/*! configurable parameters for security */
|
||||
static const appSecCfg_t cyclingSecCfg =
|
||||
{
|
||||
DM_AUTH_BOND_FLAG | SMP_AUTH_SC_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 */
|
||||
TRUE /*! TRUE to initiate security upon connection */
|
||||
};
|
||||
|
||||
/*! configurable parameters for connection parameter update */
|
||||
static const appUpdateCfg_t cyclingUpdateCfg =
|
||||
{
|
||||
0, /*! 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 */
|
||||
3, /*! Connection latency */
|
||||
600, /*! Supervision timeout in 10ms units */
|
||||
5 /*! Number of update attempts before giving up */
|
||||
};
|
||||
|
||||
/*! ATT configurable parameters (increase MTU) */
|
||||
static const attCfg_t cyclingAttCfg =
|
||||
{
|
||||
15, /* ATT server service discovery connection idle timeout in seconds */
|
||||
CYCLING_DEFAULT_MTU, /* desired ATT MTU */
|
||||
ATT_MAX_TRANS_TIMEOUT, /* transcation timeout in seconds */
|
||||
4 /* number of queued prepare writes supported by server */
|
||||
};
|
||||
|
||||
/*! 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 cyclingAdvDataDisc[] =
|
||||
{
|
||||
/*! flags */
|
||||
2, /*! length */
|
||||
DM_ADV_TYPE_FLAGS, /*! AD type */
|
||||
DM_FLAG_LE_GENERAL_DISC | /*! flags */
|
||||
DM_FLAG_LE_BREDR_NOT_SUP,
|
||||
|
||||
/*! manufacturer specific data */
|
||||
3, /*! length */
|
||||
DM_ADV_TYPE_MANUFACTURER, /*! AD type */
|
||||
UINT16_TO_BYTES(HCI_ID_ARM), /*! company ID */
|
||||
|
||||
/*! service UUID list */
|
||||
9, /*! length */
|
||||
DM_ADV_TYPE_16_UUID, /*! AD type */
|
||||
UINT16_TO_BYTES(ATT_UUID_CYCLING_POWER_SERVICE),
|
||||
UINT16_TO_BYTES(ATT_UUID_CYCLING_SPEED_SERVICE),
|
||||
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE),
|
||||
UINT16_TO_BYTES(ATT_UUID_BATTERY_SERVICE)
|
||||
};
|
||||
|
||||
/*! scan data, discoverable mode */
|
||||
static const uint8_t cyclingScanDataDisc[] =
|
||||
{
|
||||
/*! device name */
|
||||
8, /*! length */
|
||||
DM_ADV_TYPE_LOCAL_NAME, /*! AD type */
|
||||
'C',
|
||||
'y',
|
||||
'c',
|
||||
'l',
|
||||
'i',
|
||||
'n',
|
||||
'g'
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Client Characteristic Configuration Descriptors
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
|
||||
static const attsCccSet_t cyclingCccSet[CYCLING_NUM_CCC_IDX] =
|
||||
{
|
||||
/* cccd handle value range security level */
|
||||
{ GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE }, /* CYCLING_GATT_SC_CCC_IDX */
|
||||
{ CPS_CPM_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE }, /* CYCLING_CPS_CPM_CCC_IDX */
|
||||
{ CSCS_CSM_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE }, /* CYCLING_CSCS_CSM_CCC_IDX */
|
||||
{ BATT_LVL_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE } /* CYCLING_BATT_LVL_CCC_IDX */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! application control block */
|
||||
static struct
|
||||
{
|
||||
wsfHandlerId_t handlerId; /* WSF handler ID */
|
||||
wsfTimer_t cpmTimer; /* WSF Timer to send cycling power measurement data */
|
||||
wsfTimer_t csmTimer; /* WSF Timer to send cycling speed measurement data */
|
||||
} cyclingCb;
|
||||
|
||||
/* Cycling Power Measurement period - Can be changed at runtime to vary period */
|
||||
static uint16_t cyclingCpmPeriod = CYCLING_DEFAULT_CPM_PERIOD;
|
||||
|
||||
/* Cycling Speed Measurement period - Can be changed at runtime to vary period */
|
||||
static uint16_t cyclingCsmPeriod = CYCLING_DEFAULT_CSM_PERIOD;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application DM callback.
|
||||
*
|
||||
* \param pDmEvt DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void cyclingDmCback(dmEvt_t *pDmEvt)
|
||||
{
|
||||
dmEvt_t *pMsg;
|
||||
|
||||
if (pDmEvt->hdr.event == DM_SEC_ECC_KEY_IND)
|
||||
{
|
||||
DmSecSetEccKey(&pDmEvt->eccMsg.data.key);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16_t len = DmSizeOfEvt(pDmEvt);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(len)) != NULL)
|
||||
{
|
||||
memcpy(pMsg, pDmEvt, len);
|
||||
WsfMsgSend(cyclingCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATT callback.
|
||||
*
|
||||
* \param pEvt ATT callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void cyclingAttCback(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(cyclingCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a Cycling Speed Measurement Notification.
|
||||
*
|
||||
* \param connId connection ID
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void cyclingSendCyclingSpeedMeasurement(dmConnId_t connId)
|
||||
{
|
||||
static uint32_t revolutions = 1;
|
||||
static uint32_t lastTime = 1;
|
||||
|
||||
if (AttsCccEnabled(connId, CYCLING_CSCS_CSM_CCC_IDX))
|
||||
{
|
||||
/* TODO: Set Cycle Speed Measurement Parameters */
|
||||
|
||||
revolutions++;
|
||||
lastTime += 100;
|
||||
|
||||
CscpsSetParameter(CSCP_SM_PARAM_WHEEL_REVOLUTIONS, revolutions);
|
||||
CscpsSetParameter(CSCP_SM_PARAM_LAST_WHEEL_EVT_TIME, lastTime);
|
||||
CscpsSetParameter(CSCP_SM_PARAM_CRANK_REVOLUTIONS, revolutions);
|
||||
CscpsSetParameter(CSCP_SM_PARAM_LAST_CRANK_TIME, lastTime);
|
||||
|
||||
CscpsSendSpeedMeasurement(connId);
|
||||
}
|
||||
|
||||
/* Configure and start timer to send the next measurement */
|
||||
cyclingCb.csmTimer.msg.event = CYCLING_CSCP_SM_TIMER_IND;
|
||||
cyclingCb.csmTimer.msg.status = CYCLING_CSCS_CSM_CCC_IDX;
|
||||
cyclingCb.csmTimer.handlerId = cyclingCb.handlerId;
|
||||
cyclingCb.csmTimer.msg.param = connId;
|
||||
|
||||
WsfTimerStartSec(&cyclingCb.csmTimer, cyclingCsmPeriod);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a Cycling Power Measurement Notification.
|
||||
*
|
||||
* \param connId connection ID
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void cyclingSendCyclingPowerMeasurement(dmConnId_t connId)
|
||||
{
|
||||
static uint32_t revolutions = 1;
|
||||
static uint32_t lastTime = 1;
|
||||
|
||||
if (AttsCccEnabled(connId, CYCLING_CPS_CPM_CCC_IDX))
|
||||
{
|
||||
/* TODO: Set Cycle Power Measurement Parameters */
|
||||
|
||||
revolutions++;
|
||||
lastTime += 100;
|
||||
|
||||
CppsSetParameter(CPP_PM_PARAM_INSTANTANEOUS_POWER, 1);
|
||||
CppsSetParameter(CPP_PM_PARAM_PEDAL_POWER, 2);
|
||||
CppsSetParameter(CPP_PM_PARAM_ACCUMULATED_TORQUE, 4);
|
||||
CppsSetParameter(CPP_PM_PARAM_WHEEL_REVOLUTIONS, revolutions);
|
||||
CppsSetParameter(CPP_PM_PARAM_LAST_WHEEL_REV_TIME, lastTime);
|
||||
CppsSetParameter(CPP_PM_PARAM_CRANK_REVOLUTIONS, revolutions);
|
||||
CppsSetParameter(CPP_PM_PARAM_LAST_CRANK_TIME, lastTime);
|
||||
CppsSetParameter(CPP_PM_PARAM_MAX_FORCE_MAGNITUDE, 9);
|
||||
CppsSetParameter(CPP_PM_PARAM_MIN_FORCE_MAGNITUDE, 10);
|
||||
CppsSetParameter(CPP_PM_PARAM_MAX_TORQUE_MAGNITUDE, 11);
|
||||
CppsSetParameter(CPP_PM_PARAM_MIN_TORQUE_MAGNITUDE, 12);
|
||||
CppsSetParameter(CPP_PM_PARAM_MAX_EXTREME_ANGLE, 13);
|
||||
CppsSetParameter(CPP_PM_PARAM_MIN_EXTREME_ANGLE, 14);
|
||||
CppsSetParameter(CPP_PM_PARAM_TOP_DEAD_SPOT, 15);
|
||||
CppsSetParameter(CPP_PM_PARAM_BOTTOM_DEAD_SPOT, 16);
|
||||
CppsSetParameter(CPP_PM_PARAM_ACCUMULATED_ENERGY, 17);
|
||||
|
||||
CppsSendPowerMeasurement(connId);
|
||||
}
|
||||
|
||||
/* Configure and start timer to send the next measurement */
|
||||
cyclingCb.cpmTimer.msg.event = CYCLING_CPP_PM_TIMER_IND;
|
||||
cyclingCb.cpmTimer.msg.status = CYCLING_CPS_CPM_CCC_IDX;
|
||||
cyclingCb.cpmTimer.handlerId = cyclingCb.handlerId;
|
||||
cyclingCb.cpmTimer.msg.param = connId;
|
||||
|
||||
WsfTimerStartSec(&cyclingCb.cpmTimer, cyclingCpmPeriod);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATTS client characteristic configuration callback.
|
||||
*
|
||||
* \param pDmEvt DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void cyclingProcCccState(attsCccEvt_t *pMsg)
|
||||
{
|
||||
APP_TRACE_INFO3("ccc state ind value:%d handle:%d idx:%d", pMsg->value, pMsg->handle, pMsg->idx);
|
||||
|
||||
/* handle cycling power measurement CCC */
|
||||
if (pMsg->idx == CYCLING_CPS_CPM_CCC_IDX)
|
||||
{
|
||||
if (pMsg->value == ATT_CLIENT_CFG_NOTIFY)
|
||||
{
|
||||
cyclingSendCyclingPowerMeasurement((dmConnId_t)pMsg->hdr.param);
|
||||
}
|
||||
else
|
||||
{
|
||||
WsfTimerStop(&cyclingCb.cpmTimer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* handle cycling speed measurement CCC */
|
||||
if (pMsg->idx == CYCLING_CSCS_CSM_CCC_IDX)
|
||||
{
|
||||
if (pMsg->value == ATT_CLIENT_CFG_NOTIFY)
|
||||
{
|
||||
cyclingSendCyclingSpeedMeasurement((dmConnId_t)pMsg->hdr.param);
|
||||
}
|
||||
else
|
||||
{
|
||||
WsfTimerStop(&cyclingCb.csmTimer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* handle battery level CCC */
|
||||
if (pMsg->idx == CYCLING_BATT_LVL_CCC_IDX)
|
||||
{
|
||||
if (pMsg->value == ATT_CLIENT_CFG_NOTIFY)
|
||||
{
|
||||
BasMeasBattStart((dmConnId_t)pMsg->hdr.param, CYCLING_BATT_TIMER_IND, CYCLING_BATT_LVL_CCC_IDX);
|
||||
}
|
||||
else
|
||||
{
|
||||
BasMeasBattStop((dmConnId_t)pMsg->hdr.param);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATTS client characteristic configuration callback.
|
||||
*
|
||||
* \param pDmEvt DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void cyclingCccCback(attsCccEvt_t *pEvt)
|
||||
{
|
||||
attsCccEvt_t *pMsg;
|
||||
appDbHdl_t dbHdl;
|
||||
|
||||
/* If CCC not set from initialization and there's a bond record and currently bonded */
|
||||
if ((pEvt->handle != ATT_HANDLE_NONE) &&
|
||||
((dbHdl = AppDbGetHdl((dmConnId_t)pEvt->hdr.param)) != APP_DB_HDL_NONE) &&
|
||||
AppCheckBonded((dmConnId_t)pEvt->hdr.param))
|
||||
{
|
||||
/* 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(cyclingCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn cyclingGetPeerKey
|
||||
*
|
||||
* \brief Get peer key from a device database record.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return Pointer to peer key if key is valid or NULL if not valid.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static dmSecKey_t *cyclingGetPeerKey(dmEvt_t *pMsg)
|
||||
{
|
||||
appDbHdl_t dbHdl;
|
||||
|
||||
/* get device database record handle */
|
||||
dbHdl = AppDbGetHdl((dmConnId_t) pMsg->hdr.param);
|
||||
|
||||
/* if database record handle valid */
|
||||
if (dbHdl != APP_DB_HDL_NONE)
|
||||
{
|
||||
return AppDbGetKey(dbHdl, DM_KEY_IRK, NULL);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn datsPrivAddDevToResListInd
|
||||
*
|
||||
* \brief Handle add device to resolving list indication.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void cyclingPrivAddDevToResListInd(dmEvt_t *pMsg)
|
||||
{
|
||||
dmSecKey_t *pPeerKey;
|
||||
|
||||
/* if peer IRK present */
|
||||
if ((pPeerKey = cyclingGetPeerKey(pMsg)) != NULL)
|
||||
{
|
||||
/* set advertising peer address */
|
||||
AppSetAdvPeerAddr(pPeerKey->irk.addrType, pPeerKey->irk.bdAddr);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn cyclingPrivRemDevFromResListInd
|
||||
*
|
||||
* \brief Handle remove device from resolving list indication.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void cyclingPrivRemDevFromResListInd(dmEvt_t *pMsg)
|
||||
{
|
||||
if (pMsg->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
if (AppDbGetHdl((dmConnId_t) pMsg->hdr.param) != APP_DB_HDL_NONE)
|
||||
{
|
||||
uint8_t addrZeros[BDA_ADDR_LEN] = { 0 };
|
||||
|
||||
/* clear advertising peer address and its type */
|
||||
AppSetAdvPeerAddr(HCI_ADDR_TYPE_PUBLIC, addrZeros);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \fn cyclingSetup
|
||||
*
|
||||
* \brief Set up advertising and other procedures that need to be performed after
|
||||
* device reset.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void cyclingSetup(dmEvt_t *pMsg)
|
||||
{
|
||||
/* set advertising and scan response data for discoverable mode */
|
||||
AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(cyclingAdvDataDisc), (uint8_t *) cyclingAdvDataDisc);
|
||||
AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(cyclingScanDataDisc), (uint8_t *) cyclingScanDataDisc);
|
||||
|
||||
/* set advertising and scan response data for connectable mode */
|
||||
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, sizeof(cyclingAdvDataDisc), (uint8_t *) cyclingAdvDataDisc);
|
||||
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, sizeof(cyclingScanDataDisc), (uint8_t *) cyclingScanDataDisc);
|
||||
|
||||
/* start advertising; automatically set connectable/discoverable mode and bondable mode */
|
||||
AppAdvStart(APP_MODE_AUTO_INIT);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void cyclingProcMsg(dmEvt_t *pMsg)
|
||||
{
|
||||
uint8_t uiEvent = APP_UI_NONE;
|
||||
|
||||
switch(pMsg->hdr.event)
|
||||
{
|
||||
case CYCLING_CPP_PM_TIMER_IND:
|
||||
cyclingSendCyclingPowerMeasurement((dmConnId_t) pMsg->hdr.param);
|
||||
break;
|
||||
|
||||
case CYCLING_CSCP_SM_TIMER_IND:
|
||||
cyclingSendCyclingSpeedMeasurement((dmConnId_t) pMsg->hdr.param);
|
||||
break;
|
||||
|
||||
case ATTS_CCC_STATE_IND:
|
||||
cyclingProcCccState((attsCccEvt_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();
|
||||
cyclingSetup(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;
|
||||
break;
|
||||
|
||||
case DM_CONN_OPEN_IND:
|
||||
CppsConnOpen((dmConnId_t)pMsg->hdr.param);
|
||||
uiEvent = APP_UI_CONN_OPEN;
|
||||
break;
|
||||
|
||||
case DM_CONN_CLOSE_IND:
|
||||
WsfTimerStop(&cyclingCb.cpmTimer);
|
||||
WsfTimerStop(&cyclingCb.csmTimer);
|
||||
uiEvent = APP_UI_CONN_CLOSE;
|
||||
break;
|
||||
|
||||
case DM_SEC_PAIR_CMPL_IND:
|
||||
DmSecGenerateEccKeyReq();
|
||||
uiEvent = APP_UI_SEC_PAIR_CMPL;
|
||||
break;
|
||||
|
||||
case DM_SEC_PAIR_FAIL_IND:
|
||||
DmSecGenerateEccKeyReq();
|
||||
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_PRIV_ADD_DEV_TO_RES_LIST_IND:
|
||||
cyclingPrivAddDevToResListInd(pMsg);
|
||||
break;
|
||||
|
||||
case DM_PRIV_REM_DEV_FROM_RES_LIST_IND:
|
||||
cyclingPrivRemDevFromResListInd(pMsg);
|
||||
break;
|
||||
|
||||
case DM_ADV_NEW_ADDR_IND:
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void CyclingHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
APP_TRACE_INFO0("CyclingHandlerInit");
|
||||
|
||||
/* store handler ID */
|
||||
cyclingCb.handlerId = handlerId;
|
||||
|
||||
/* Set configuration pointers */
|
||||
pAppSlaveCfg = (appSlaveCfg_t *) &cyclingSlaveCfg;
|
||||
pAppAdvCfg = (appAdvCfg_t *) &cyclingAdvCfg;
|
||||
pAppSecCfg = (appSecCfg_t *) &cyclingSecCfg;
|
||||
pAppUpdateCfg = (appUpdateCfg_t *) &cyclingUpdateCfg;
|
||||
pAttCfg = (attCfg_t *) &cyclingAttCfg;
|
||||
|
||||
/* Initialize application framework */
|
||||
AppSlaveInit();
|
||||
AppServerInit();
|
||||
|
||||
/* Set IRK for the local device */
|
||||
DmSecSetLocalIrk(localIrk);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void CyclingHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
APP_TRACE_INFO1("Cycling got evt %d", pMsg->event);
|
||||
|
||||
/* process ATT messages */
|
||||
if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
|
||||
{
|
||||
/* process server-related ATT messages */
|
||||
AppServerProcAttMsg(pMsg);
|
||||
}
|
||||
/* process DM messages */
|
||||
else if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
|
||||
{
|
||||
/* process advertising and connection-related messages */
|
||||
AppSlaveProcDmMsg((dmEvt_t *) pMsg);
|
||||
|
||||
/* process security-related messages */
|
||||
AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/* perform profile and user interface-related operations */
|
||||
cyclingProcMsg((dmEvt_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void CyclingStart(void)
|
||||
{
|
||||
/* Register for stack callbacks */
|
||||
DmRegister(cyclingDmCback);
|
||||
DmConnRegister(DM_CLIENT_ID_APP, cyclingDmCback);
|
||||
AttRegister(cyclingAttCback);
|
||||
AttConnRegister(AppServerConnCback);
|
||||
AttsCccRegister(CYCLING_NUM_CCC_IDX, (attsCccSet_t *) cyclingCccSet, cyclingCccCback);
|
||||
|
||||
/* Initialize attribute server database */
|
||||
SvcCoreGattCbackRegister(GattReadCback, GattWriteCback);
|
||||
SvcCoreAddGroup();
|
||||
SvcCpsAddGroup();
|
||||
SvcCscsAddGroup();
|
||||
SvcBattCbackRegister(BasReadCback, NULL);
|
||||
SvcBattAddGroup();
|
||||
|
||||
/* Set Service Changed CCCD index. */
|
||||
GattSetSvcChangedIdx(CYCLING_GATT_SC_CCC_IDX);
|
||||
|
||||
/* Set the cycling power features */
|
||||
CppsSetFeatures(CPP_ALL_FEATURES);
|
||||
|
||||
/* Set the cycling speed and cadence features */
|
||||
CscpsSetFeatures(CSCS_ALL_FEATURES);
|
||||
|
||||
/* Reset the device */
|
||||
DmDevReset();
|
||||
}
|
||||
Vendored
+73
@@ -0,0 +1,73 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Proprietary data transfer client sample application.
|
||||
*
|
||||
* Copyright (c) 2012-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DATC_API_H
|
||||
#define DATC_API_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DatcStart(void);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for App.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DatcHandlerInit(wsfHandlerId_t handlerId);
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for the application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DatcHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DATC_API_H */
|
||||
Vendored
+1253
File diff suppressed because it is too large
Load Diff
Vendored
+77
@@ -0,0 +1,77 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Proprietary data transfer server sample application.
|
||||
*
|
||||
* Copyright (c) 2012-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef DATS_API_H
|
||||
#define DATS_API_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef WDXS_INCLUDED
|
||||
#define WDXS_INCLUDED FALSE
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DatsStart(void);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for App.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DatsHandlerInit(wsfHandlerId_t handlerId);
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for the application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DatsHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* DATS_API_H */
|
||||
Vendored
+855
@@ -0,0 +1,855 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Data transmitter sample application.
|
||||
*
|
||||
* Copyright (c) 2012-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "util/bstream.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_buf.h"
|
||||
#include "hci_api.h"
|
||||
#include "sec_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "smp_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_main.h"
|
||||
#include "app_db.h"
|
||||
#include "app_ui.h"
|
||||
#include "svc_ch.h"
|
||||
#include "svc_core.h"
|
||||
#include "svc_wp.h"
|
||||
#include "util/calc128.h"
|
||||
#include "gatt/gatt_api.h"
|
||||
#include "dats/dats_api.h"
|
||||
#if WDXS_INCLUDED == TRUE
|
||||
#include "wsf_efs.h"
|
||||
#include "svc_wdxs.h"
|
||||
#include "wdxs/wdxs_api.h"
|
||||
#include "wdxs/wdxs_main.h"
|
||||
#include "wdxs/wdxs_stream.h"
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
#if CS50_INCLUDED == TRUE
|
||||
|
||||
/* PHY Test Modes */
|
||||
#define DATS_PHY_1M 1
|
||||
#define DATS_PHY_2M 2
|
||||
#define DATS_PHY_CODED 3
|
||||
|
||||
#endif /* CS50_INCLUDED */
|
||||
|
||||
/*! Enumeration of client characteristic configuration descriptors */
|
||||
enum
|
||||
{
|
||||
#if WDXS_INCLUDED == TRUE
|
||||
WDXS_DC_CH_CCC_IDX, /*! WDXS DC service, service changed characteristic */
|
||||
WDXS_FTC_CH_CCC_IDX, /*! WDXS FTC service, service changed characteristic */
|
||||
WDXS_FTD_CH_CCC_IDX, /*! WDXS FTD service, service changed characteristic */
|
||||
WDXS_AU_CH_CCC_IDX, /*! WDXS AU service, service changed characteristic */
|
||||
#endif /* WDXS_INCLUDED */
|
||||
DATS_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
|
||||
DATS_WP_DAT_CCC_IDX, /*! Arm Ltd. proprietary service, data transfer characteristic */
|
||||
DATS_NUM_CCC_IDX
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Configurable Parameters
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! configurable parameters for advertising */
|
||||
static const appAdvCfg_t datsAdvCfg =
|
||||
{
|
||||
{30000, 0, 0}, /*! Advertising durations in ms */
|
||||
{ 96, 1600, 0} /*! Advertising intervals in 0.625 ms units */
|
||||
};
|
||||
|
||||
/*! configurable parameters for slave */
|
||||
static const appSlaveCfg_t datsSlaveCfg =
|
||||
{
|
||||
1, /*! Maximum connections */
|
||||
};
|
||||
|
||||
/*! configurable parameters for security */
|
||||
static const appSecCfg_t datsSecCfg =
|
||||
{
|
||||
DM_AUTH_BOND_FLAG | DM_AUTH_SC_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 */
|
||||
TRUE /*! TRUE to initiate security upon connection */
|
||||
};
|
||||
|
||||
/*! TRUE if Out-of-band pairing data is to be sent */
|
||||
static const bool_t datsSendOobData = FALSE;
|
||||
|
||||
/*! SMP security parameter configuration */
|
||||
static const smpCfg_t datsSmpCfg =
|
||||
{
|
||||
500, /*! 'Repeated attempts' timeout in msec */
|
||||
SMP_IO_NO_IN_NO_OUT, /*! I/O Capability */
|
||||
7, /*! Minimum encryption key length */
|
||||
16, /*! Maximum encryption key length */
|
||||
1, /*! Attempts to trigger 'repeated attempts' timeout */
|
||||
0, /*! Device authentication requirements */
|
||||
64000, /*! Maximum repeated attempts timeout in msec */
|
||||
64000, /*! Time msec before attemptExp decreases */
|
||||
2 /*! Repeated attempts multiplier exponent */
|
||||
};
|
||||
|
||||
/*! configurable parameters for connection parameter update */
|
||||
static const appUpdateCfg_t datsUpdateCfg =
|
||||
{
|
||||
0, /*! 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 */
|
||||
3, /*! Connection latency */
|
||||
600, /*! Supervision timeout in 10ms units */
|
||||
5 /*! Number of update attempts before giving up */
|
||||
};
|
||||
|
||||
/*! ATT configurable parameters (increase MTU) */
|
||||
static const attCfg_t datsAttCfg =
|
||||
{
|
||||
15, /* ATT server service discovery connection idle timeout in seconds */
|
||||
241, /* desired ATT MTU */
|
||||
ATT_MAX_TRANS_TIMEOUT, /* transcation timeout in seconds */
|
||||
4 /* number of queued prepare writes supported by server */
|
||||
};
|
||||
|
||||
/*! 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 datsAdvDataDisc[] =
|
||||
{
|
||||
/*! flags */
|
||||
2, /*! length */
|
||||
DM_ADV_TYPE_FLAGS, /*! AD type */
|
||||
DM_FLAG_LE_GENERAL_DISC | /*! flags */
|
||||
DM_FLAG_LE_BREDR_NOT_SUP,
|
||||
|
||||
/*! manufacturer specific data */
|
||||
3, /*! length */
|
||||
DM_ADV_TYPE_MANUFACTURER, /*! AD type */
|
||||
UINT16_TO_BYTES(HCI_ID_ARM) /*! company ID */
|
||||
};
|
||||
|
||||
/*! scan data, discoverable mode */
|
||||
static const uint8_t datsScanDataDisc[] =
|
||||
{
|
||||
/*! device name */
|
||||
8, /*! length */
|
||||
DM_ADV_TYPE_LOCAL_NAME, /*! AD type */
|
||||
'D',
|
||||
'a',
|
||||
't',
|
||||
'a',
|
||||
' ',
|
||||
'T',
|
||||
'X'
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Client Characteristic Configuration Descriptors
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
|
||||
static const attsCccSet_t datsCccSet[DATS_NUM_CCC_IDX] =
|
||||
{
|
||||
/* cccd handle value range security level */
|
||||
#if WDXS_INCLUDED == TRUE
|
||||
{WDXS_DC_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* WDXS_DC_CH_CCC_IDX */
|
||||
{WDXS_FTC_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* WDXS_FTC_CH_CCC_IDX */
|
||||
{WDXS_FTD_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* WDXS_FTD_CH_CCC_IDX */
|
||||
{WDXS_AU_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* WDXS_AU_CH_CCC_IDX */
|
||||
#endif /* WDXS_INCLUDED */
|
||||
{GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE}, /* DATS_GATT_SC_CCC_IDX */
|
||||
{WP_DAT_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE} /* DATS_WP_DAT_CCC_IDX */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! application control block */
|
||||
static struct
|
||||
{
|
||||
wsfHandlerId_t handlerId; /* WSF handler ID */
|
||||
#if CS50_INCLUDED == TRUE
|
||||
uint8_t phyMode; /*! PHY Test Mode */
|
||||
#endif /* CS50_INCLUDED */
|
||||
} datsCb;
|
||||
|
||||
/* LESC OOB configuration */
|
||||
static dmSecLescOobCfg_t *datsOobCfg;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send notification containing data.
|
||||
*
|
||||
* \param connId DM connection ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void datsSendData(dmConnId_t connId)
|
||||
{
|
||||
uint8_t str[] = "hello hello hello hello hello hello.....from dats";
|
||||
|
||||
if (AttsCccEnabled(connId, DATS_WP_DAT_CCC_IDX))
|
||||
{
|
||||
/* send notification */
|
||||
AttsHandleValueNtf(connId, WP_DAT_HDL, sizeof(str), str);
|
||||
AttsHandleValueNtf(connId, WP_DAT_HDL, sizeof(str), str);
|
||||
AttsHandleValueNtf(connId, WP_DAT_HDL, sizeof(str), str);
|
||||
AttsHandleValueNtf(connId, WP_DAT_HDL, sizeof(str), str);
|
||||
AttsHandleValueNtf(connId, WP_DAT_HDL, sizeof(str), str);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application DM callback.
|
||||
*
|
||||
* \param pDmEvt DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void datsDmCback(dmEvt_t *pDmEvt)
|
||||
{
|
||||
dmEvt_t *pMsg;
|
||||
uint16_t len;
|
||||
|
||||
if (pDmEvt->hdr.event == DM_SEC_ECC_KEY_IND)
|
||||
{
|
||||
DmSecSetEccKey(&pDmEvt->eccMsg.data.key);
|
||||
|
||||
/* If the local device sends OOB data. */
|
||||
if (datsSendOobData)
|
||||
{
|
||||
uint8_t oobLocalRandom[SMP_RAND_LEN];
|
||||
SecRand(oobLocalRandom, SMP_RAND_LEN);
|
||||
DmSecCalcOobReq(oobLocalRandom, pDmEvt->eccMsg.data.key.pubKey_x);
|
||||
}
|
||||
}
|
||||
else if (pDmEvt->hdr.event == DM_SEC_CALC_OOB_IND)
|
||||
{
|
||||
if (datsOobCfg == NULL)
|
||||
{
|
||||
datsOobCfg = WsfBufAlloc(sizeof(dmSecLescOobCfg_t));
|
||||
}
|
||||
|
||||
if (datsOobCfg)
|
||||
{
|
||||
Calc128Cpy(datsOobCfg->localConfirm, pDmEvt->oobCalcInd.confirm);
|
||||
Calc128Cpy(datsOobCfg->localRandom, pDmEvt->oobCalcInd.random);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
len = DmSizeOfEvt(pDmEvt);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(len)) != NULL)
|
||||
{
|
||||
memcpy(pMsg, pDmEvt, len);
|
||||
WsfMsgSend(datsCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATT callback.
|
||||
*
|
||||
* \param pEvt ATT callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void datsAttCback(attEvt_t *pEvt)
|
||||
{
|
||||
#if WDXS_INCLUDED == TRUE
|
||||
WdxsAttCback(pEvt);
|
||||
#endif /* WDXS_INCLUDED */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATTS client characteristic configuration callback.
|
||||
*
|
||||
* \param pDmEvt DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void datsCccCback(attsCccEvt_t *pEvt)
|
||||
{
|
||||
appDbHdl_t dbHdl;
|
||||
|
||||
/* If CCC not set from initialization and there's a device record and currently bonded */
|
||||
if ((pEvt->handle != ATT_HANDLE_NONE) &&
|
||||
((dbHdl = AppDbGetHdl((dmConnId_t) pEvt->hdr.param)) != APP_DB_HDL_NONE) &&
|
||||
AppCheckBonded((dmConnId_t)pEvt->hdr.param))
|
||||
{
|
||||
/* Store value in device database. */
|
||||
AppDbSetCccTblValue(dbHdl, pEvt->idx, pEvt->value);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief ATTS write callback for proprietary data service.
|
||||
*
|
||||
* \return ATT status.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
uint8_t datsWpWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
|
||||
uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr)
|
||||
{
|
||||
/* print received data */
|
||||
APP_TRACE_INFO0((const char*) pValue);
|
||||
|
||||
/* send back some data */
|
||||
datsSendData(connId);
|
||||
|
||||
return ATT_SUCCESS;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
*
|
||||
* \brief Get peer key from a device database record.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return Pointer to peer key if key is valid or NULL if not valid.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static dmSecKey_t *datsGetPeerKey(dmEvt_t *pMsg)
|
||||
{
|
||||
appDbHdl_t dbHdl;
|
||||
|
||||
/* get device database record handle */
|
||||
dbHdl = AppDbGetHdl((dmConnId_t) pMsg->hdr.param);
|
||||
|
||||
/* if database record handle valid */
|
||||
if (dbHdl != APP_DB_HDL_NONE)
|
||||
{
|
||||
return AppDbGetKey(dbHdl, DM_KEY_IRK, NULL);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
*
|
||||
* \brief Handle add device to resolving list indication.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void datsPrivAddDevToResListInd(dmEvt_t *pMsg)
|
||||
{
|
||||
dmSecKey_t *pPeerKey;
|
||||
|
||||
/* if peer IRK present */
|
||||
if ((pPeerKey = datsGetPeerKey(pMsg)) != NULL)
|
||||
{
|
||||
/* set advertising peer address */
|
||||
AppSetAdvPeerAddr(pPeerKey->irk.addrType, pPeerKey->irk.bdAddr);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
*
|
||||
* \brief Handle remove device from resolving list indication.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void datsPrivRemDevFromResListInd(dmEvt_t *pMsg)
|
||||
{
|
||||
if (pMsg->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
if (AppDbGetHdl((dmConnId_t) pMsg->hdr.param) != APP_DB_HDL_NONE)
|
||||
{
|
||||
uint8_t addrZeros[BDA_ADDR_LEN] = { 0 };
|
||||
|
||||
/* clear advertising peer address and its type */
|
||||
AppSetAdvPeerAddr(HCI_ADDR_TYPE_PUBLIC, addrZeros);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
*
|
||||
* \brief Display stack version.
|
||||
*
|
||||
* \param version version number.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void datsDisplayStackVersion(const char *pVersion)
|
||||
{
|
||||
APP_TRACE_INFO1("Stack Version: %s", pVersion);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set up advertising and other procedures that need to be performed after
|
||||
* device reset.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void datsSetup(dmEvt_t *pMsg)
|
||||
{
|
||||
/* set advertising and scan response data for discoverable mode */
|
||||
AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(datsAdvDataDisc), (uint8_t *) datsAdvDataDisc);
|
||||
AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(datsScanDataDisc), (uint8_t *) datsScanDataDisc);
|
||||
|
||||
/* set advertising and scan response data for connectable mode */
|
||||
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, sizeof(datsAdvDataDisc), (uint8_t *) datsAdvDataDisc);
|
||||
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, sizeof(datsScanDataDisc), (uint8_t *) datsScanDataDisc);
|
||||
|
||||
/* start advertising; automatically set connectable/discoverable mode and bondable mode */
|
||||
AppAdvStart(APP_MODE_AUTO_INIT);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void datsProcMsg(dmEvt_t *pMsg)
|
||||
{
|
||||
uint8_t uiEvent = APP_UI_NONE;
|
||||
|
||||
switch(pMsg->hdr.event)
|
||||
{
|
||||
case ATT_MTU_UPDATE_IND:
|
||||
APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu);
|
||||
break;
|
||||
|
||||
case DM_RESET_CMPL_IND:
|
||||
AttsCalculateDbHash();
|
||||
DmSecGenerateEccKeyReq();
|
||||
datsSetup(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;
|
||||
break;
|
||||
|
||||
case DM_CONN_OPEN_IND:
|
||||
uiEvent = APP_UI_CONN_OPEN;
|
||||
#if CS50_INCLUDED == TRUE
|
||||
datsCb.phyMode = DATS_PHY_1M;
|
||||
#endif /* CS50_INCLUDED */
|
||||
break;
|
||||
|
||||
case DM_CONN_CLOSE_IND:
|
||||
uiEvent = APP_UI_CONN_CLOSE;
|
||||
break;
|
||||
|
||||
case DM_SEC_PAIR_CMPL_IND:
|
||||
DmSecGenerateEccKeyReq();
|
||||
uiEvent = APP_UI_SEC_PAIR_CMPL;
|
||||
break;
|
||||
|
||||
case DM_SEC_PAIR_FAIL_IND:
|
||||
DmSecGenerateEccKeyReq();
|
||||
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:
|
||||
|
||||
if (pMsg->authReq.oob)
|
||||
{
|
||||
dmConnId_t connId = (dmConnId_t) pMsg->hdr.param;
|
||||
|
||||
/* TODO: Perform OOB Exchange with the peer. */
|
||||
|
||||
|
||||
/* TODO: Fill datsOobCfg peerConfirm and peerRandom with value passed out of band */
|
||||
|
||||
if (datsOobCfg != NULL)
|
||||
{
|
||||
DmSecSetOob(connId, datsOobCfg);
|
||||
}
|
||||
|
||||
DmSecAuthRsp(connId, 0, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
AppHandlePasskey(&pMsg->authReq);
|
||||
}
|
||||
break;
|
||||
|
||||
case DM_SEC_COMPARE_IND:
|
||||
AppHandleNumericComparison(&pMsg->cnfInd);
|
||||
break;
|
||||
|
||||
case DM_PRIV_ADD_DEV_TO_RES_LIST_IND:
|
||||
datsPrivAddDevToResListInd(pMsg);
|
||||
break;
|
||||
|
||||
case DM_PRIV_REM_DEV_FROM_RES_LIST_IND:
|
||||
datsPrivRemDevFromResListInd(pMsg);
|
||||
break;
|
||||
|
||||
case DM_ADV_NEW_ADDR_IND:
|
||||
break;
|
||||
|
||||
case DM_PRIV_CLEAR_RES_LIST_IND:
|
||||
APP_TRACE_INFO1("Clear resolving list status 0x%02x", pMsg->hdr.status);
|
||||
break;
|
||||
|
||||
#if CS50_INCLUDED == TRUE
|
||||
case DM_PHY_UPDATE_IND:
|
||||
APP_TRACE_INFO2("DM_PHY_UPDATE_IND - RX: %d, TX: %d", pMsg->phyUpdate.rxPhy, pMsg->phyUpdate.txPhy);
|
||||
datsCb.phyMode = pMsg->phyUpdate.rxPhy;
|
||||
break;
|
||||
#endif /* CS50_INCLUDED */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DatsHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
APP_TRACE_INFO0("DatsHandlerInit");
|
||||
|
||||
/* store handler ID */
|
||||
datsCb.handlerId = handlerId;
|
||||
|
||||
/* Set configuration pointers */
|
||||
pAppSlaveCfg = (appSlaveCfg_t *) &datsSlaveCfg;
|
||||
pAppAdvCfg = (appAdvCfg_t *) &datsAdvCfg;
|
||||
pAppSecCfg = (appSecCfg_t *) &datsSecCfg;
|
||||
pAppUpdateCfg = (appUpdateCfg_t *) &datsUpdateCfg;
|
||||
pSmpCfg = (smpCfg_t *) &datsSmpCfg;
|
||||
pAttCfg = (attCfg_t *) &datsAttCfg;
|
||||
|
||||
/* Initialize application framework */
|
||||
AppSlaveInit();
|
||||
AppServerInit();
|
||||
|
||||
/* Set IRK for the local device */
|
||||
DmSecSetLocalIrk(localIrk);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Button press callback.
|
||||
*
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void datsBtnCback(uint8_t btn)
|
||||
{
|
||||
#if WDXS_INCLUDED == TRUE
|
||||
static uint8_t waveform = WDXS_STREAM_WAVEFORM_SINE;
|
||||
#endif /* WDXS_INCLUDED */
|
||||
|
||||
#if CS50_INCLUDED == TRUE
|
||||
dmConnId_t connId;
|
||||
if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE)
|
||||
#else
|
||||
if (AppConnIsOpen() != DM_CONN_ID_NONE)
|
||||
#endif /* CS50_INCLUDED */
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
#if CS50_INCLUDED == TRUE
|
||||
case APP_UI_BTN_2_SHORT:
|
||||
|
||||
/* Toggle PHY Test Mode */
|
||||
if (datsCb.phyMode == DATS_PHY_1M)
|
||||
{
|
||||
APP_TRACE_INFO0("2 MBit TX and RX PHY Requested");
|
||||
DmSetPhy(connId, HCI_ALL_PHY_ALL_PREFERENCES, HCI_PHY_LE_2M_BIT, HCI_PHY_LE_2M_BIT, HCI_PHY_OPTIONS_NONE);
|
||||
}
|
||||
else if (datsCb.phyMode == DATS_PHY_2M)
|
||||
{
|
||||
APP_TRACE_INFO0("LE Coded TX and RX PHY Requested");
|
||||
DmSetPhy(connId, HCI_ALL_PHY_ALL_PREFERENCES, HCI_PHY_LE_CODED_BIT, HCI_PHY_LE_CODED_BIT, HCI_PHY_OPTIONS_NONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
APP_TRACE_INFO0("1 MBit TX and RX PHY Requested");
|
||||
DmSetPhy(connId, HCI_ALL_PHY_ALL_PREFERENCES, HCI_PHY_LE_1M_BIT, HCI_PHY_LE_1M_BIT, HCI_PHY_OPTIONS_NONE);
|
||||
}
|
||||
break;
|
||||
|
||||
#endif /* CS50_INCLUDED */
|
||||
|
||||
#if WDXS_INCLUDED == TRUE
|
||||
case APP_UI_BTN_1_SHORT:
|
||||
/* Change stream waveform */
|
||||
waveform++;
|
||||
|
||||
if (waveform > WDXS_STREAM_WAVEFORM_SAWTOOTH)
|
||||
waveform = WDXS_STREAM_WAVEFORM_SINE;
|
||||
|
||||
wdxsSetStreamWaveform(waveform);
|
||||
break;
|
||||
#endif /* WDXS_INCLUDED */
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
case APP_UI_BTN_1_SHORT:
|
||||
{
|
||||
const char *pVersion;
|
||||
|
||||
StackGetVersionNumber(&pVersion);
|
||||
|
||||
datsDisplayStackVersion(pVersion);
|
||||
}
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_1_MED:
|
||||
/* Enter bondable mode */
|
||||
AppSetBondable(TRUE);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_1_LONG:
|
||||
/* clear all bonding info */
|
||||
AppSlaveClearAllBondingInfo();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Callback for WSF buffer diagnostic messages.
|
||||
*
|
||||
* \param pInfo Diagnostics message
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void datsWsfBufDiagnostics(WsfBufDiag_t *pInfo)
|
||||
{
|
||||
if (pInfo->type == WSF_BUF_ALLOC_FAILED)
|
||||
{
|
||||
APP_TRACE_INFO2("Dats got WSF Buffer Allocation Failure - Task: %d Len: %d",
|
||||
pInfo->param.alloc.taskId, pInfo->param.alloc.len);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DatsHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
APP_TRACE_INFO1("Dats got evt %d", pMsg->event);
|
||||
|
||||
/* process ATT messages */
|
||||
if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
|
||||
{
|
||||
/* process server-related ATT messages */
|
||||
AppServerProcAttMsg(pMsg);
|
||||
}
|
||||
/* process DM messages */
|
||||
else if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
|
||||
{
|
||||
/* process advertising and connection-related messages */
|
||||
AppSlaveProcDmMsg((dmEvt_t *) pMsg);
|
||||
|
||||
/* process security-related messages */
|
||||
AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
|
||||
|
||||
#if WDXS_INCLUDED == TRUE
|
||||
/* process WDXS-related messages */
|
||||
WdxsProcDmMsg((dmEvt_t*) pMsg);
|
||||
#endif /* WDXS_INCLUDED */
|
||||
}
|
||||
|
||||
/* perform profile and user interface-related operations */
|
||||
datsProcMsg((dmEvt_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void DatsStart(void)
|
||||
{
|
||||
/* Register for stack callbacks */
|
||||
DmRegister(datsDmCback);
|
||||
DmConnRegister(DM_CLIENT_ID_APP, datsDmCback);
|
||||
AttRegister(datsAttCback);
|
||||
AttConnRegister(AppServerConnCback);
|
||||
AttsCccRegister(DATS_NUM_CCC_IDX, (attsCccSet_t *) datsCccSet, datsCccCback);
|
||||
|
||||
/* Initialize attribute server database */
|
||||
SvcCoreGattCbackRegister(GattReadCback, GattWriteCback);
|
||||
SvcCoreAddGroup();
|
||||
SvcWpCbackRegister(NULL, datsWpWriteCback);
|
||||
SvcWpAddGroup();
|
||||
|
||||
/* Set Service Changed CCCD index. */
|
||||
GattSetSvcChangedIdx(DATS_GATT_SC_CCC_IDX);
|
||||
|
||||
/* Register for app framework button callbacks */
|
||||
AppUiBtnRegister(datsBtnCback);
|
||||
|
||||
#if WDXS_INCLUDED == TRUE
|
||||
|
||||
/* Initialize the OTA File Media */
|
||||
WdxsOtaMediaInit();
|
||||
|
||||
/* Initialize the WDXS Stream */
|
||||
wdxsStreamInit();
|
||||
|
||||
/* Set the WDXS CCC Identifiers */
|
||||
WdxsSetCccIdx(WDXS_DC_CH_CCC_IDX, WDXS_AU_CH_CCC_IDX, WDXS_FTC_CH_CCC_IDX, WDXS_FTD_CH_CCC_IDX);
|
||||
|
||||
#if CS50_INCLUDED == TRUE
|
||||
WdxsPhyInit();
|
||||
#endif /* CS50_INCLUDED */
|
||||
|
||||
#endif /* WDXS_INCLUDED */
|
||||
|
||||
#if CS50_INCLUDED == TRUE
|
||||
DmPhyInit();
|
||||
#endif /* CS50_INCLUDED */
|
||||
|
||||
WsfBufDiagRegister(datsWsfBufDiagnostics);
|
||||
|
||||
/* Reset the device */
|
||||
DmDevReset();
|
||||
}
|
||||
Vendored
+81
@@ -0,0 +1,81 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Fitness sample application interface.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef FIT_API_H
|
||||
#define FIT_API_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
#ifndef FIT_CONN_MAX
|
||||
#define FIT_CONN_MAX 1
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void FitStart(void);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for App.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void FitHandlerInit(wsfHandlerId_t handlerId);
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for the application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void FitHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* FIT_API_H */
|
||||
Vendored
+830
@@ -0,0 +1,830 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Fitness sample application for the following profiles:
|
||||
* Heart Rate profile
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "util/bstream.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "hci_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "smp_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_db.h"
|
||||
#include "app_ui.h"
|
||||
#include "app_hw.h"
|
||||
#include "app_main.h"
|
||||
#include "svc_ch.h"
|
||||
#include "svc_core.h"
|
||||
#include "svc_hrs.h"
|
||||
#include "svc_dis.h"
|
||||
#include "svc_batt.h"
|
||||
#include "svc_rscs.h"
|
||||
#include "gatt/gatt_api.h"
|
||||
#include "bas/bas_api.h"
|
||||
#include "hrps/hrps_api.h"
|
||||
#include "rscp/rscp_api.h"
|
||||
#include "fit_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! WSF message event starting value */
|
||||
#define FIT_MSG_START 0xA0
|
||||
|
||||
/* Default Running Speed and Cadence Measurement period (seconds) */
|
||||
#define FIT_DEFAULT_RSCM_PERIOD 1
|
||||
|
||||
/*! WSF message event enumeration */
|
||||
enum
|
||||
{
|
||||
FIT_HR_TIMER_IND = FIT_MSG_START, /*! Heart rate measurement timer expired */
|
||||
FIT_BATT_TIMER_IND, /*! Battery measurement timer expired */
|
||||
FIT_RUNNING_TIMER_IND /*! Running speed and cadence measurement timer expired */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Application message type */
|
||||
typedef union
|
||||
{
|
||||
wsfMsgHdr_t hdr;
|
||||
dmEvt_t dm;
|
||||
attsCccEvt_t ccc;
|
||||
attEvt_t att;
|
||||
} fitMsg_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Configurable Parameters
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! configurable parameters for advertising */
|
||||
static const appAdvCfg_t fitAdvCfg =
|
||||
{
|
||||
{0, 0, 0}, /*! Advertising durations in ms */
|
||||
{ 800, 0, 0} /*! Advertising intervals in 0.625 ms units */
|
||||
};
|
||||
|
||||
/*! configurable parameters for slave */
|
||||
static const appSlaveCfg_t fitSlaveCfg =
|
||||
{
|
||||
FIT_CONN_MAX, /*! Maximum connections */
|
||||
};
|
||||
|
||||
/*! configurable parameters for security */
|
||||
static const appSecCfg_t fitSecCfg =
|
||||
{
|
||||
#ifndef PAIRING_TEST
|
||||
DM_AUTH_BOND_FLAG |
|
||||
#endif
|
||||
DM_AUTH_SC_FLAG, /*! Authentication and bonding flags */
|
||||
0, /*! Initiator key distribution flags */
|
||||
DM_KEY_DIST_LTK, /*! 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 fitUpdateCfg =
|
||||
{
|
||||
3000, /*! Connection idle period in ms before attempting
|
||||
connection parameter update; set to zero to disable */
|
||||
48, /*! Minimum connection interval in 1.25ms units */
|
||||
60, /*! Maximum connection interval in 1.25ms units */
|
||||
4, /*! Connection latency */
|
||||
600, /*! Supervision timeout in 10ms units */
|
||||
5 /*! Number of update attempts before giving up */
|
||||
};
|
||||
|
||||
/*! heart rate measurement configuration */
|
||||
static const hrpsCfg_t fitHrpsCfg =
|
||||
{
|
||||
100 /*! Measurement timer expiration period in ms */
|
||||
};
|
||||
|
||||
/*! battery measurement configuration */
|
||||
static const basCfg_t fitBasCfg =
|
||||
{
|
||||
30, /*! Battery measurement timer expiration period in seconds */
|
||||
1, /*! Perform battery measurement after this many timer periods */
|
||||
100 /*! Send battery level notification to peer when below this level. */
|
||||
};
|
||||
|
||||
/*! SMP security parameter configuration */
|
||||
static const smpCfg_t fitSmpCfg =
|
||||
{
|
||||
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 */
|
||||
64000, /*! Maximum repeated attempts timeout in msec */
|
||||
64000, /*! Time msec before attemptExp decreases */
|
||||
2 /*! Repeated attempts multiplier exponent */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Advertising Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! advertising data, discoverable mode */
|
||||
static const uint8_t fitAdvDataDisc[] =
|
||||
{
|
||||
/*! flags */
|
||||
2, /*! length */
|
||||
DM_ADV_TYPE_FLAGS, /*! AD type */
|
||||
DM_FLAG_LE_GENERAL_DISC | /*! flags */
|
||||
DM_FLAG_LE_BREDR_NOT_SUP,
|
||||
|
||||
/*! tx power */
|
||||
2, /*! length */
|
||||
DM_ADV_TYPE_TX_POWER, /*! AD type */
|
||||
0, /*! tx power */
|
||||
|
||||
/*! service UUID list */
|
||||
9, /*! length */
|
||||
DM_ADV_TYPE_16_UUID, /*! AD type */
|
||||
UINT16_TO_BYTES(ATT_UUID_HEART_RATE_SERVICE),
|
||||
UINT16_TO_BYTES(ATT_UUID_RUNNING_SPEED_SERVICE),
|
||||
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE),
|
||||
UINT16_TO_BYTES(ATT_UUID_BATTERY_SERVICE)
|
||||
};
|
||||
|
||||
/*! scan data, discoverable mode */
|
||||
static const uint8_t fitScanDataDisc[] =
|
||||
{
|
||||
/*! device name */
|
||||
4, /*! length */
|
||||
DM_ADV_TYPE_LOCAL_NAME, /*! AD type */
|
||||
'F',
|
||||
'i',
|
||||
't'
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Client Characteristic Configuration Descriptors
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! enumeration of client characteristic configuration descriptors */
|
||||
enum
|
||||
{
|
||||
FIT_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
|
||||
FIT_HRS_HRM_CCC_IDX, /*! Heart rate service, heart rate monitor characteristic */
|
||||
FIT_BATT_LVL_CCC_IDX, /*! Battery service, battery level characteristic */
|
||||
FIT_RSCS_SM_CCC_IDX, /*! Runninc speed and cadence measurement characteristic */
|
||||
FIT_NUM_CCC_IDX
|
||||
};
|
||||
|
||||
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
|
||||
static const attsCccSet_t fitCccSet[FIT_NUM_CCC_IDX] =
|
||||
{
|
||||
/* cccd handle value range security level */
|
||||
{GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE}, /* FIT_GATT_SC_CCC_IDX */
|
||||
{HRS_HRM_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* FIT_HRS_HRM_CCC_IDX */
|
||||
{BATT_LVL_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* FIT_BATT_LVL_CCC_IDX */
|
||||
{RSCS_RSM_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE} /* FIT_RSCS_SM_CCC_IDX */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! WSF handler ID */
|
||||
wsfHandlerId_t fitHandlerId;
|
||||
|
||||
/* WSF Timer to send running speed and cadence measurement data */
|
||||
wsfTimer_t fitRscmTimer;
|
||||
|
||||
/* Running Speed and Cadence Measurement period - Can be changed at runtime to vary period */
|
||||
static uint16_t fitRscmPeriod = FIT_DEFAULT_RSCM_PERIOD;
|
||||
|
||||
/* Heart Rate Monitor feature flags */
|
||||
static uint8_t fitHrmFlags = CH_HRM_FLAGS_VALUE_8BIT | CH_HRM_FLAGS_ENERGY_EXP;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application DM callback.
|
||||
*
|
||||
* \param pDmEvt DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void fitDmCback(dmEvt_t *pDmEvt)
|
||||
{
|
||||
dmEvt_t *pMsg;
|
||||
uint16_t len;
|
||||
|
||||
len = DmSizeOfEvt(pDmEvt);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(len)) != NULL)
|
||||
{
|
||||
memcpy(pMsg, pDmEvt, len);
|
||||
WsfMsgSend(fitHandlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATT callback.
|
||||
*
|
||||
* \param pEvt ATT callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void fitAttCback(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(fitHandlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATTS client characteristic configuration callback.
|
||||
*
|
||||
* \param pDmEvt DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void fitCccCback(attsCccEvt_t *pEvt)
|
||||
{
|
||||
attsCccEvt_t *pMsg;
|
||||
appDbHdl_t dbHdl;
|
||||
|
||||
/* If CCC not set from initialization and there's a device record and currently bonded */
|
||||
if ((pEvt->handle != ATT_HANDLE_NONE) &&
|
||||
((dbHdl = AppDbGetHdl((dmConnId_t) pEvt->hdr.param)) != APP_DB_HDL_NONE) &&
|
||||
AppCheckBonded((dmConnId_t)pEvt->hdr.param))
|
||||
{
|
||||
/* 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(fitHandlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send a Running Speed and Cadence Measurement Notification.
|
||||
*
|
||||
* \param connId connection ID
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void fitSendRunningSpeedMeasurement(dmConnId_t connId)
|
||||
{
|
||||
if (AttsCccEnabled(connId, FIT_RSCS_SM_CCC_IDX))
|
||||
{
|
||||
static uint8_t walk_run = 1;
|
||||
|
||||
/* TODO: Set Running Speed and Cadence Measurement Parameters */
|
||||
|
||||
RscpsSetParameter(RSCP_SM_PARAM_SPEED, 1);
|
||||
RscpsSetParameter(RSCP_SM_PARAM_CADENCE, 2);
|
||||
RscpsSetParameter(RSCP_SM_PARAM_STRIDE_LENGTH, 3);
|
||||
RscpsSetParameter(RSCP_SM_PARAM_TOTAL_DISTANCE, 4);
|
||||
|
||||
/* Toggle running/walking */
|
||||
walk_run = walk_run? 0 : 1;
|
||||
RscpsSetParameter(RSCP_SM_PARAM_STATUS, walk_run);
|
||||
|
||||
RscpsSendSpeedMeasurement(connId);
|
||||
}
|
||||
|
||||
/* Configure and start timer to send the next measurement */
|
||||
fitRscmTimer.msg.event = FIT_RUNNING_TIMER_IND;
|
||||
fitRscmTimer.msg.status = FIT_RSCS_SM_CCC_IDX;
|
||||
fitRscmTimer.handlerId = fitHandlerId;
|
||||
fitRscmTimer.msg.param = connId;
|
||||
|
||||
WsfTimerStartSec(&fitRscmTimer, fitRscmPeriod);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process CCC state change.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void fitProcCccState(fitMsg_t *pMsg)
|
||||
{
|
||||
APP_TRACE_INFO3("ccc state ind value:%d handle:%d idx:%d", pMsg->ccc.value, pMsg->ccc.handle, pMsg->ccc.idx);
|
||||
|
||||
/* handle heart rate measurement CCC */
|
||||
if (pMsg->ccc.idx == FIT_HRS_HRM_CCC_IDX)
|
||||
{
|
||||
if (pMsg->ccc.value == ATT_CLIENT_CFG_NOTIFY)
|
||||
{
|
||||
HrpsMeasStart((dmConnId_t) pMsg->ccc.hdr.param, FIT_HR_TIMER_IND, FIT_HRS_HRM_CCC_IDX);
|
||||
}
|
||||
else
|
||||
{
|
||||
HrpsMeasStop((dmConnId_t) pMsg->ccc.hdr.param);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* handle running speed and cadence measurement CCC */
|
||||
if (pMsg->ccc.idx == FIT_RSCS_SM_CCC_IDX)
|
||||
{
|
||||
if (pMsg->ccc.value == ATT_CLIENT_CFG_NOTIFY)
|
||||
{
|
||||
fitSendRunningSpeedMeasurement((dmConnId_t)pMsg->ccc.hdr.param);
|
||||
}
|
||||
else
|
||||
{
|
||||
WsfTimerStop(&fitRscmTimer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* handle battery level CCC */
|
||||
if (pMsg->ccc.idx == FIT_BATT_LVL_CCC_IDX)
|
||||
{
|
||||
if (pMsg->ccc.value == ATT_CLIENT_CFG_NOTIFY)
|
||||
{
|
||||
BasMeasBattStart((dmConnId_t) pMsg->ccc.hdr.param, FIT_BATT_TIMER_IND, FIT_BATT_LVL_CCC_IDX);
|
||||
}
|
||||
else
|
||||
{
|
||||
BasMeasBattStop((dmConnId_t) pMsg->ccc.hdr.param);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform UI actions on connection close.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void fitClose(fitMsg_t *pMsg)
|
||||
{
|
||||
/* stop heart rate measurement */
|
||||
HrpsMeasStop((dmConnId_t) pMsg->hdr.param);
|
||||
|
||||
/* stop battery measurement */
|
||||
BasMeasBattStop((dmConnId_t) pMsg->hdr.param);
|
||||
|
||||
/* Stop running speed and cadence timer */
|
||||
WsfTimerStop(&fitRscmTimer);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set up advertising and other procedures that need to be performed after
|
||||
* device reset.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void fitSetup(fitMsg_t *pMsg)
|
||||
{
|
||||
/* set advertising and scan response data for discoverable mode */
|
||||
AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(fitAdvDataDisc), (uint8_t *) fitAdvDataDisc);
|
||||
AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(fitScanDataDisc), (uint8_t *) fitScanDataDisc);
|
||||
|
||||
/* set advertising and scan response data for connectable mode */
|
||||
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, 0, NULL);
|
||||
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, 0, NULL);
|
||||
|
||||
/* start advertising; automatically set connectable/discoverable mode and bondable mode */
|
||||
AppAdvStart(APP_MODE_AUTO_INIT);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Button press callback.
|
||||
*
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void fitBtnCback(uint8_t btn)
|
||||
{
|
||||
dmConnId_t connId;
|
||||
static uint8_t heartRate = 78; /* for testing/demonstration */
|
||||
|
||||
/* button actions when connected */
|
||||
if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE)
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
case APP_UI_BTN_1_SHORT:
|
||||
/* increment the heart rate */
|
||||
AppHwHrmTest(++heartRate);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_1_MED:
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_1_LONG:
|
||||
AppConnClose(connId);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_SHORT:
|
||||
/* decrement the heart rate */
|
||||
AppHwHrmTest(--heartRate);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_MED:
|
||||
/* Toggle HRM Sensor DET flags */
|
||||
if (!(fitHrmFlags & (CH_HRM_FLAGS_SENSOR_DET | CH_HRM_FLAGS_SENSOR_NOT_DET)))
|
||||
{
|
||||
fitHrmFlags |= CH_HRM_FLAGS_SENSOR_DET;
|
||||
}
|
||||
else if (fitHrmFlags & CH_HRM_FLAGS_SENSOR_DET)
|
||||
{
|
||||
fitHrmFlags &= ~CH_HRM_FLAGS_SENSOR_DET;
|
||||
fitHrmFlags |= CH_HRM_FLAGS_SENSOR_NOT_DET;
|
||||
}
|
||||
else
|
||||
{
|
||||
fitHrmFlags &= ~CH_HRM_FLAGS_SENSOR_NOT_DET;
|
||||
}
|
||||
|
||||
HrpsSetFlags(fitHrmFlags);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_LONG:
|
||||
/* Toggle HRM RR Interval feature flag */
|
||||
if (fitHrmFlags & CH_HRM_FLAGS_RR_INTERVAL)
|
||||
{
|
||||
fitHrmFlags &= ~CH_HRM_FLAGS_RR_INTERVAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
fitHrmFlags |= CH_HRM_FLAGS_RR_INTERVAL;
|
||||
}
|
||||
|
||||
HrpsSetFlags(fitHrmFlags);
|
||||
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 all bonding info */
|
||||
AppSlaveClearAllBondingInfo();
|
||||
|
||||
/* restart advertising */
|
||||
AppAdvStart(APP_MODE_AUTO_INIT);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_SHORT:
|
||||
/* Toggle HRM Flag for 8 and 16 bit values */
|
||||
if (fitHrmFlags & CH_HRM_FLAGS_VALUE_16BIT)
|
||||
{
|
||||
fitHrmFlags &= ~CH_HRM_FLAGS_VALUE_16BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
fitHrmFlags |= CH_HRM_FLAGS_VALUE_16BIT;
|
||||
}
|
||||
|
||||
HrpsSetFlags(fitHrmFlags);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void fitProcMsg(fitMsg_t *pMsg)
|
||||
{
|
||||
uint8_t uiEvent = APP_UI_NONE;
|
||||
|
||||
switch(pMsg->hdr.event)
|
||||
{
|
||||
case FIT_RUNNING_TIMER_IND:
|
||||
fitSendRunningSpeedMeasurement((dmConnId_t)pMsg->ccc.hdr.param);
|
||||
break;
|
||||
|
||||
case FIT_HR_TIMER_IND:
|
||||
HrpsProcMsg(&pMsg->hdr);
|
||||
break;
|
||||
|
||||
case FIT_BATT_TIMER_IND:
|
||||
BasProcMsg(&pMsg->hdr);
|
||||
break;
|
||||
|
||||
case ATTS_HANDLE_VALUE_CNF:
|
||||
HrpsProcMsg(&pMsg->hdr);
|
||||
BasProcMsg(&pMsg->hdr);
|
||||
break;
|
||||
|
||||
case ATTS_CCC_STATE_IND:
|
||||
fitProcCccState(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();
|
||||
fitSetup(pMsg);
|
||||
uiEvent = APP_UI_RESET_CMPL;
|
||||
break;
|
||||
|
||||
case DM_ADV_SET_START_IND:
|
||||
uiEvent = APP_UI_ADV_SET_START_IND;
|
||||
break;
|
||||
|
||||
case DM_ADV_SET_STOP_IND:
|
||||
uiEvent = APP_UI_ADV_SET_STOP_IND;
|
||||
break;
|
||||
|
||||
case DM_ADV_START_IND:
|
||||
uiEvent = APP_UI_ADV_START;
|
||||
break;
|
||||
|
||||
case DM_ADV_STOP_IND:
|
||||
uiEvent = APP_UI_ADV_STOP;
|
||||
break;
|
||||
|
||||
case DM_CONN_OPEN_IND:
|
||||
HrpsProcMsg(&pMsg->hdr);
|
||||
BasProcMsg(&pMsg->hdr);
|
||||
// AppSlaveSecurityReq(1);
|
||||
uiEvent = APP_UI_CONN_OPEN;
|
||||
break;
|
||||
|
||||
case DM_CONN_CLOSE_IND:
|
||||
fitClose(pMsg);
|
||||
uiEvent = APP_UI_CONN_CLOSE;
|
||||
break;
|
||||
|
||||
case DM_SEC_PAIR_CMPL_IND:
|
||||
DmSecGenerateEccKeyReq();
|
||||
uiEvent = APP_UI_SEC_PAIR_CMPL;
|
||||
break;
|
||||
|
||||
case DM_SEC_PAIR_FAIL_IND:
|
||||
DmSecGenerateEccKeyReq();
|
||||
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->dm.authReq);
|
||||
break;
|
||||
|
||||
case DM_SEC_ECC_KEY_IND:
|
||||
|
||||
DmSecSetEccKey(&pMsg->dm.eccMsg.data.key);
|
||||
break;
|
||||
|
||||
case DM_SEC_COMPARE_IND:
|
||||
AppHandleNumericComparison(&pMsg->dm.cnfInd);
|
||||
break;
|
||||
|
||||
case DM_PRIV_CLEAR_RES_LIST_IND:
|
||||
APP_TRACE_INFO1("Clear resolving list status 0x%02x", pMsg->hdr.status);
|
||||
break;
|
||||
|
||||
case DM_HW_ERROR_IND:
|
||||
uiEvent = APP_UI_HW_ERROR;
|
||||
break;
|
||||
|
||||
case DM_VENDOR_SPEC_CMD_CMPL_IND:
|
||||
{
|
||||
#if defined(AM_PART_APOLLO) || defined(AM_PART_APOLLO2)
|
||||
|
||||
uint8_t *param_ptr = &pMsg->dm.vendorSpecCmdCmpl.param[0];
|
||||
|
||||
switch (pMsg->dm.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->dm.vendorSpecCmdCmpl.opcode,
|
||||
pMsg->hdr.status,
|
||||
read_value);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
APP_TRACE_INFO2("VSC 0x%0x complete status %x",
|
||||
pMsg->dm.vendorSpecCmdCmpl.opcode,
|
||||
pMsg->hdr.status);
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (uiEvent != APP_UI_NONE)
|
||||
{
|
||||
AppUiAction(uiEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void FitHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
APP_TRACE_INFO0("FitHandlerInit");
|
||||
|
||||
/* store handler ID */
|
||||
fitHandlerId = handlerId;
|
||||
|
||||
/* Set configuration pointers */
|
||||
pAppAdvCfg = (appAdvCfg_t *) &fitAdvCfg;
|
||||
pAppSlaveCfg = (appSlaveCfg_t *) &fitSlaveCfg;
|
||||
pAppSecCfg = (appSecCfg_t *) &fitSecCfg;
|
||||
pAppUpdateCfg = (appUpdateCfg_t *) &fitUpdateCfg;
|
||||
|
||||
/* Initialize application framework */
|
||||
AppSlaveInit();
|
||||
AppServerInit();
|
||||
|
||||
/* Set stack configuration pointers */
|
||||
pSmpCfg = (smpCfg_t *) &fitSmpCfg;
|
||||
|
||||
/* initialize heart rate profile sensor */
|
||||
HrpsInit(handlerId, (hrpsCfg_t *) &fitHrpsCfg);
|
||||
HrpsSetFlags(fitHrmFlags);
|
||||
|
||||
/* initialize battery service server */
|
||||
BasInit(handlerId, (basCfg_t *) &fitBasCfg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void FitHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
APP_TRACE_INFO1("Fit got evt %d", pMsg->event);
|
||||
|
||||
/* process ATT messages */
|
||||
if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
|
||||
{
|
||||
/* process server-related ATT messages */
|
||||
AppServerProcAttMsg(pMsg);
|
||||
}
|
||||
/* process DM messages */
|
||||
else if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
|
||||
{
|
||||
/* process advertising and connection-related messages */
|
||||
AppSlaveProcDmMsg((dmEvt_t *) pMsg);
|
||||
|
||||
/* process security-related messages */
|
||||
AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/* perform profile and user interface-related operations */
|
||||
fitProcMsg((fitMsg_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void FitStart(void)
|
||||
{
|
||||
/* Register for stack callbacks */
|
||||
DmRegister(fitDmCback);
|
||||
DmConnRegister(DM_CLIENT_ID_APP, fitDmCback);
|
||||
AttRegister(fitAttCback);
|
||||
AttConnRegister(AppServerConnCback);
|
||||
AttsCccRegister(FIT_NUM_CCC_IDX, (attsCccSet_t *) fitCccSet, fitCccCback);
|
||||
|
||||
/* Register for app framework callbacks */
|
||||
AppUiBtnRegister(fitBtnCback);
|
||||
|
||||
/* Initialize attribute server database */
|
||||
SvcCoreGattCbackRegister(GattReadCback, GattWriteCback);
|
||||
SvcCoreAddGroup();
|
||||
SvcHrsCbackRegister(NULL, HrpsWriteCback);
|
||||
SvcHrsAddGroup();
|
||||
SvcDisAddGroup();
|
||||
SvcBattCbackRegister(BasReadCback, NULL);
|
||||
SvcBattAddGroup();
|
||||
SvcRscsAddGroup();
|
||||
|
||||
/* Set Service Changed CCCD index. */
|
||||
GattSetSvcChangedIdx(FIT_GATT_SC_CCC_IDX);
|
||||
|
||||
/* Set running speed and cadence features */
|
||||
RscpsSetFeatures(RSCS_ALL_FEATURES);
|
||||
|
||||
/* Reset the device */
|
||||
DmDevReset();
|
||||
}
|
||||
Vendored
+73
@@ -0,0 +1,73 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Glucose sensor sample application interface.
|
||||
*
|
||||
* Copyright (c) 2012-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef GLUC_API_H
|
||||
#define GLUC_API_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void GlucStart(void);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for App.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void GlucHandlerInit(wsfHandlerId_t handlerId);
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for the application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void GlucHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* GLUC_API_H */
|
||||
Vendored
+538
@@ -0,0 +1,538 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Glucose sensor sample application.
|
||||
*
|
||||
* Copyright (c) 2012-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "util/bstream.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "hci_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_db.h"
|
||||
#include "app_ui.h"
|
||||
#include "app_hw.h"
|
||||
#include "app_main.h"
|
||||
#include "svc_ch.h"
|
||||
#include "svc_core.h"
|
||||
#include "svc_gls.h"
|
||||
#include "svc_dis.h"
|
||||
#include "gatt/gatt_api.h"
|
||||
#include "glps/glps_api.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! enumeration of client characteristic configuration descriptors */
|
||||
enum
|
||||
{
|
||||
GLUC_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
|
||||
GLUC_GLS_GLM_CCC_IDX, /*! Glucose service, glucose measurement characteristic */
|
||||
GLUC_GLS_GLMC_CCC_IDX, /*! Glucose service, glucose measurement context characteristic */
|
||||
GLUC_GLS_RACP_CCC_IDX, /*! Glucose service, record access control point characteristic */
|
||||
GLUC_NUM_CCC_IDX
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Configurable Parameters
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! configurable parameters for advertising */
|
||||
static const appAdvCfg_t glucAdvCfg =
|
||||
{
|
||||
{30000, 30000, 0}, /*! Advertising durations in ms */
|
||||
{ 48, 1600, 0} /*! Advertising intervals in 0.625 ms units */
|
||||
};
|
||||
|
||||
/*! configurable parameters for slave */
|
||||
static const appSlaveCfg_t glucSlaveCfg =
|
||||
{
|
||||
1, /*! Maximum connections */
|
||||
};
|
||||
|
||||
/*! configurable parameters for security */
|
||||
static const appSecCfg_t glucSecCfg =
|
||||
{
|
||||
DM_AUTH_BOND_FLAG, /*! Authentication and bonding flags */
|
||||
0, /*! Initiator key distribution flags */
|
||||
DM_KEY_DIST_LTK, /*! Responder key distribution flags */
|
||||
FALSE, /*! TRUE if Out-of-band pairing data is present */
|
||||
TRUE /*! TRUE to initiate security upon connection */
|
||||
};
|
||||
|
||||
/*! configurable parameters for connection parameter update */
|
||||
static const appUpdateCfg_t glucUpdateCfg =
|
||||
{
|
||||
0, /*! 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 */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Advertising Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! advertising data, discoverable mode */
|
||||
static const uint8_t glucAdvDataDisc[] =
|
||||
{
|
||||
/*! flags */
|
||||
2, /*! length */
|
||||
DM_ADV_TYPE_FLAGS, /*! AD type */
|
||||
DM_FLAG_LE_LIMITED_DISC | /*! flags */
|
||||
DM_FLAG_LE_BREDR_NOT_SUP,
|
||||
|
||||
/*! service UUID list */
|
||||
5, /*! length */
|
||||
DM_ADV_TYPE_16_UUID, /*! AD type */
|
||||
UINT16_TO_BYTES(ATT_UUID_GLUCOSE_SERVICE),
|
||||
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE)
|
||||
};
|
||||
|
||||
/*! scan data, discoverable mode */
|
||||
static const uint8_t glucScanDataDisc[] =
|
||||
{
|
||||
/*! device name */
|
||||
12, /*! length */
|
||||
DM_ADV_TYPE_LOCAL_NAME, /*! AD type */
|
||||
'G',
|
||||
'l',
|
||||
'u',
|
||||
'c',
|
||||
' ',
|
||||
'S',
|
||||
'e',
|
||||
'n',
|
||||
's',
|
||||
'o',
|
||||
'r'
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Client Characteristic Configuration Descriptors
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
|
||||
static const attsCccSet_t glucCccSet[GLUC_NUM_CCC_IDX] =
|
||||
{
|
||||
/* cccd handle value range security level */
|
||||
{GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_ENC}, /* GLUC_GATT_SC_CCC_IDX */
|
||||
{GLS_GLM_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_ENC}, /* GLUC_GLS_GLM_CCC_IDX */
|
||||
{GLS_GLMC_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_ENC}, /* GLUC_GLS_GLMC_CCC_IDX */
|
||||
{GLS_RACP_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_ENC} /* GLUC_GLS_RACP_CCC_IDX */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! application control block */
|
||||
static struct
|
||||
{
|
||||
wsfHandlerId_t handlerId;
|
||||
} glucCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application DM callback.
|
||||
*
|
||||
* \param pDmEvt DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void glucDmCback(dmEvt_t *pDmEvt)
|
||||
{
|
||||
dmEvt_t *pMsg;
|
||||
uint16_t len;
|
||||
|
||||
len = DmSizeOfEvt(pDmEvt);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(len)) != NULL)
|
||||
{
|
||||
memcpy(pMsg, pDmEvt, len);
|
||||
WsfMsgSend(glucCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATT callback.
|
||||
*
|
||||
* \param pEvt ATT callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void glucAttCback(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(glucCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATTS client characteristic configuration callback.
|
||||
*
|
||||
* \param pDmEvt DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void glucCccCback(attsCccEvt_t *pEvt)
|
||||
{
|
||||
appDbHdl_t dbHdl;
|
||||
|
||||
/* If CCC not set from initialization and there's a device record and currently bonded */
|
||||
if ((pEvt->handle != ATT_HANDLE_NONE) &&
|
||||
((dbHdl = AppDbGetHdl((dmConnId_t) pEvt->hdr.param)) != APP_DB_HDL_NONE) &&
|
||||
AppCheckBonded((dmConnId_t)pEvt->hdr.param))
|
||||
{
|
||||
/* Store value in device database. */
|
||||
AppDbSetCccTblValue(dbHdl, pEvt->idx, pEvt->value);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set up advertising and other procedures that need to be performed after
|
||||
* device reset.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void glucSetup(dmEvt_t *pMsg)
|
||||
{
|
||||
/* set advertising and scan response data for discoverable mode */
|
||||
AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(glucAdvDataDisc), (uint8_t *) glucAdvDataDisc);
|
||||
AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(glucScanDataDisc), (uint8_t *) glucScanDataDisc);
|
||||
|
||||
/* set advertising and scan response data for connectable mode */
|
||||
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, 0, NULL);
|
||||
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, 0, NULL);
|
||||
|
||||
/* start advertising; automatically set connectable/discoverable mode and bondable mode */
|
||||
AppAdvStart(APP_MODE_AUTO_INIT);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Button press callback.
|
||||
*
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void glucBtnCback(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:
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_1_MED:
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_1_LONG:
|
||||
AppConnClose(connId);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_SHORT:
|
||||
break;
|
||||
|
||||
default:
|
||||
/* all other button presses-- send to profile */
|
||||
GlpsBtn(connId, btn);
|
||||
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 all bonding info */
|
||||
AppSlaveClearAllBondingInfo();
|
||||
|
||||
/* restart advertising */
|
||||
AppAdvStart(APP_MODE_AUTO_INIT);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* all other button presses-- send to profile */
|
||||
GlpsBtn(connId, btn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void glucProcMsg(dmEvt_t *pMsg)
|
||||
{
|
||||
uint8_t uiEvent = APP_UI_NONE;
|
||||
|
||||
switch(pMsg->hdr.event)
|
||||
{
|
||||
case ATTS_HANDLE_VALUE_CNF:
|
||||
GlpsProcMsg(&pMsg->hdr);
|
||||
break;
|
||||
|
||||
case ATT_MTU_UPDATE_IND:
|
||||
APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu);
|
||||
break;
|
||||
|
||||
case DM_RESET_CMPL_IND:
|
||||
AttsCalculateDbHash();
|
||||
glucSetup(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;
|
||||
break;
|
||||
|
||||
case DM_CONN_OPEN_IND:
|
||||
GlpsProcMsg(&pMsg->hdr);
|
||||
uiEvent = APP_UI_CONN_OPEN;
|
||||
break;
|
||||
|
||||
case DM_CONN_CLOSE_IND:
|
||||
GlpsProcMsg(&pMsg->hdr);
|
||||
uiEvent = APP_UI_CONN_CLOSE;
|
||||
break;
|
||||
|
||||
case DM_SEC_PAIR_CMPL_IND:
|
||||
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_PRIV_CLEAR_RES_LIST_IND:
|
||||
APP_TRACE_INFO1("Clear resolving list status 0x%02x", pMsg->hdr.status);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void GlucHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
APP_TRACE_INFO0("GlucHandlerInit");
|
||||
|
||||
/* store handler ID */
|
||||
glucCb.handlerId = handlerId;
|
||||
|
||||
/* Set configuration pointers */
|
||||
pAppSlaveCfg = (appSlaveCfg_t *) &glucSlaveCfg;
|
||||
pAppAdvCfg = (appAdvCfg_t *) &glucAdvCfg;
|
||||
pAppSecCfg = (appSecCfg_t *) &glucSecCfg;
|
||||
pAppUpdateCfg = (appUpdateCfg_t *) &glucUpdateCfg;
|
||||
|
||||
/* Initialize application framework */
|
||||
AppSlaveInit();
|
||||
AppServerInit();
|
||||
|
||||
/* initialize glucose profile sensor */
|
||||
GlpsInit();
|
||||
GlpsSetCccIdx(GLUC_GLS_GLM_CCC_IDX, GLUC_GLS_GLMC_CCC_IDX, GLUC_GLS_RACP_CCC_IDX);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void GlucHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
APP_TRACE_INFO1("Gluc got evt %d", pMsg->event);
|
||||
|
||||
/* process ATT messages */
|
||||
if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
|
||||
{
|
||||
/* process server-related ATT messages */
|
||||
AppServerProcAttMsg(pMsg);
|
||||
}
|
||||
/* process DM messages */
|
||||
else if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
|
||||
{
|
||||
/* process advertising and connection-related messages */
|
||||
AppSlaveProcDmMsg((dmEvt_t *) pMsg);
|
||||
|
||||
/* process security-related messages */
|
||||
AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/* perform profile and user interface-related operations */
|
||||
glucProcMsg((dmEvt_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void GlucStart(void)
|
||||
{
|
||||
/* Register for stack callbacks */
|
||||
DmRegister(glucDmCback);
|
||||
DmConnRegister(DM_CLIENT_ID_APP, glucDmCback);
|
||||
AttRegister(glucAttCback);
|
||||
AttConnRegister(AppServerConnCback);
|
||||
AttsCccRegister(GLUC_NUM_CCC_IDX, (attsCccSet_t *) glucCccSet, glucCccCback);
|
||||
|
||||
/* Register for app framework callbacks */
|
||||
AppUiBtnRegister(glucBtnCback);
|
||||
|
||||
/* Initialize attribute server database */
|
||||
SvcCoreGattCbackRegister(GattReadCback, GattWriteCback);
|
||||
SvcCoreAddGroup();
|
||||
SvcGlsAddGroup();
|
||||
SvcGlsCbackRegister(NULL, GlpsRacpWriteCback);
|
||||
SvcDisAddGroup();
|
||||
|
||||
/* Set Service Changed CCCD index. */
|
||||
GattSetSvcChangedIdx(GLUC_GATT_SC_CCC_IDX);
|
||||
|
||||
/* Set supported features after starting database */
|
||||
GlpsSetFeature(CH_GLF_LOW_BATT | CH_GLF_MALFUNC | CH_GLF_SAMPLE_SIZE | CH_GLF_INSERT_ERR |
|
||||
CH_GLF_TYPE_ERR | CH_GLF_RES_HIGH_LOW | CH_GLF_TEMP_HIGH_LOW | CH_GLF_READ_INT |
|
||||
CH_GLF_GENERAL_FAULT);
|
||||
|
||||
/* Reset the device */
|
||||
DmDevReset();
|
||||
}
|
||||
Vendored
+71
@@ -0,0 +1,71 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief HidApp sample application.
|
||||
*
|
||||
* Copyright (c) 2015-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef HIDAPP_API_H
|
||||
#define HIDAPP_API_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HidAppStart(void);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for App.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HidAppHandlerInit(wsfHandlerId_t handlerId);
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for the application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void HidAppHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* HIDAPP_API_H */
|
||||
Vendored
+1392
File diff suppressed because it is too large
Load Diff
ambiq-hal-sys/ambiq-sparkfun-sdk/third_party/exactle/ble-profiles/sources/apps/locator/locator_api.h
Vendored
+73
@@ -0,0 +1,73 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Proprietary asset tracking locator sample application.
|
||||
*
|
||||
* Copyright (c) 2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef LOCATOR_API_H
|
||||
#define LOCATOR_API_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void LocatorStart(void);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for App.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void LocatorHandlerInit(wsfHandlerId_t handlerId);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for the application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void LocatorHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* LOCATOR_API_H */
|
||||
+1045
File diff suppressed because it is too large
Load Diff
Vendored
+102
@@ -0,0 +1,102 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Health/medical collector sample application.
|
||||
*
|
||||
* Copyright (c) 2012-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef MEDC_API_H
|
||||
#define MEDC_API_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Profile identifier used for MedcSetProfile() */
|
||||
enum
|
||||
{
|
||||
MEDC_ID_HRP, /*! Heart rate profile */
|
||||
MEDC_ID_BLP, /*! Blood pressure profile */
|
||||
MEDC_ID_GLP, /*! Glucose profile */
|
||||
MEDC_ID_WSP, /*! Weight scale profile */
|
||||
MEDC_ID_HTP, /*! Health thermometer profile */
|
||||
MEDC_ID_PLX /*! Pulse oximeter profile */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void MedcStart(void);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for App.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void MedcHandlerInit(wsfHandlerId_t handlerId);
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for the application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void MedcHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the profile to be used by the application. This function is called internally
|
||||
* by MedcHandlerInit() with a default value. It may also be called by the system
|
||||
* to configure the profile after executing MedcHandlerInit() and before executing
|
||||
* MedcStart().
|
||||
*
|
||||
* \param profile Profile identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void MedcSetProfile(uint8_t profile);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* MEDC_API_H */
|
||||
Vendored
+220
@@ -0,0 +1,220 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Health/medical collector, Blood Pressure profile
|
||||
*
|
||||
* Copyright (c) 2012-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_cfg.h"
|
||||
#include "app_api.h"
|
||||
#include "app_db.h"
|
||||
#include "svc_ch.h"
|
||||
#include "blpc/blpc_api.h"
|
||||
#include "medc/medc_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT Client Discovery Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Start of cached blood pressure service handles; begins after DIS */
|
||||
#define MEDC_DISC_BPS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
|
||||
|
||||
/* Total cached handle list length */
|
||||
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_BPS_START + BLPC_BPS_HDL_LIST_LEN)
|
||||
|
||||
/*! Pointers into handle list for blood pressure service handles */
|
||||
static uint16_t *pMedcBpsHdlList = &medcCb.hdlList[MEDC_DISC_BPS_START];
|
||||
|
||||
/* sanity check: make sure handle list length is <= app db handle list length */
|
||||
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT Client Configuration Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/* List of characteristics to configure after service discovery */
|
||||
static const attcDiscCfg_t medcCfgBpsList[] =
|
||||
{
|
||||
/* Read: Blood pressure feature */
|
||||
{NULL, 0, BLPC_BPS_BPF_HDL_IDX},
|
||||
|
||||
/* Write: Blood pressure measurement CCC descriptor */
|
||||
{medcCccIndVal, sizeof(medcCccIndVal), BLPC_BPS_BPM_CCC_HDL_IDX},
|
||||
|
||||
/* Write: Intermediate cuff pressure CCC descriptor */
|
||||
{medcCccNtfVal, sizeof(medcCccNtfVal), BLPC_BPS_ICP_CCC_HDL_IDX},
|
||||
};
|
||||
|
||||
/* Characteristic configuration list length */
|
||||
#define MEDC_CFG_BPS_LIST_LEN (sizeof(medcCfgBpsList) / sizeof(attcDiscCfg_t))
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
static void medcBlpInit(void);
|
||||
static bool_t medcBlpDiscover(dmConnId_t connId);
|
||||
static void medcBlpConfigure(dmConnId_t connId, uint8_t status);
|
||||
static void medcBlpProcMsg(wsfMsgHdr_t *pMsg);
|
||||
static void medcBlpBtn(dmConnId_t connId, uint8_t btn);
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! profile interface pointer */
|
||||
medcIf_t medcBlpIf =
|
||||
{
|
||||
medcBlpInit,
|
||||
medcBlpDiscover,
|
||||
medcBlpConfigure,
|
||||
medcBlpProcMsg,
|
||||
medcBlpBtn
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a received ATT read response, notification, or indication.
|
||||
*
|
||||
* \param pMsg Pointer to ATT callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcBpsValueUpdate(attEvt_t *pMsg)
|
||||
{
|
||||
if (pMsg->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
/* determine which profile the handle belongs to; start with most likely */
|
||||
|
||||
/* blood pressure */
|
||||
if (BlpcBpsValueUpdate(pMedcBpsHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* device information */
|
||||
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* GATT */
|
||||
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcBlpProcMsg(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
switch(pMsg->event)
|
||||
{
|
||||
case ATTC_READ_RSP:
|
||||
case ATTC_HANDLE_VALUE_NTF:
|
||||
case ATTC_HANDLE_VALUE_IND:
|
||||
medcBpsValueUpdate((attEvt_t *) pMsg);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Profile initialization function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcBlpInit(void)
|
||||
{
|
||||
/* set handle list length */
|
||||
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
|
||||
|
||||
/* set autoconnect UUID */
|
||||
medcCb.autoUuid[0] = ATT_UUID_BLOOD_PRESSURE_SERVICE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Discover service for profile.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t medcBlpDiscover(dmConnId_t connId)
|
||||
{
|
||||
/* discover blood pressure service */
|
||||
BlpcBpsDiscover(connId, pMedcBpsHdlList);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Configure service for profile.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcBlpConfigure(dmConnId_t connId, uint8_t status)
|
||||
{
|
||||
/* configure blood pressure service */
|
||||
AppDiscConfigure(connId, status, MEDC_CFG_BPS_LIST_LEN,
|
||||
(attcDiscCfg_t *) medcCfgBpsList,
|
||||
BLPC_BPS_HDL_LIST_LEN, pMedcBpsHdlList);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a button press.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcBlpBtn(dmConnId_t connId, uint8_t btn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Vendored
+344
@@ -0,0 +1,344 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Health/medical collector, glucose profile
|
||||
*
|
||||
* Copyright (c) 2012-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_cfg.h"
|
||||
#include "app_api.h"
|
||||
#include "app_db.h"
|
||||
#include "app_ui.h"
|
||||
#include "svc_ch.h"
|
||||
#include "glpc/glpc_api.h"
|
||||
#include "medc/medc_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT Client Discovery Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Start of cached glucose service handles; begins after DIS */
|
||||
#define MEDC_DISC_GLS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
|
||||
|
||||
/* Total cached handle list length */
|
||||
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_GLS_START + GLPC_GLS_HDL_LIST_LEN)
|
||||
|
||||
/*! Pointers into handle list for glucose service handles */
|
||||
static uint16_t *pMedcGlsHdlList = &medcCb.hdlList[MEDC_DISC_GLS_START];
|
||||
|
||||
/* sanity check: make sure handle list length is <= app db handle list length */
|
||||
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT Client Configuration Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/* List of characteristics to configure after service discovery */
|
||||
static const attcDiscCfg_t medcCfgGlsList[] =
|
||||
{
|
||||
/* Read: glucose feature */
|
||||
{NULL, 0, GLPC_GLS_GLF_HDL_IDX},
|
||||
|
||||
/* Write: glucose measurement CCC descriptor */
|
||||
{medcCccNtfVal, sizeof(medcCccIndVal), GLPC_GLS_GLM_CCC_HDL_IDX},
|
||||
|
||||
/* Write: glucose measurement context CCC descriptor */
|
||||
{medcCccNtfVal, sizeof(medcCccIndVal), GLPC_GLS_GLMC_CCC_HDL_IDX},
|
||||
|
||||
/* Write: record access control point CCC descriptor */
|
||||
{medcCccIndVal, sizeof(medcCccIndVal), GLPC_GLS_RACP_CCC_HDL_IDX}
|
||||
};
|
||||
|
||||
/* Characteristic configuration list length */
|
||||
#define MEDC_CFG_GLS_LIST_LEN (sizeof(medcCfgGlsList) / sizeof(attcDiscCfg_t))
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
static struct
|
||||
{
|
||||
wsfTimer_t racpTimer; /*! RACP procedure timer */
|
||||
bool_t inProgress; /*! RACP procedure in progress */
|
||||
} medcGlpCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
static void medcGlpInit(void);
|
||||
static bool_t medcGlpDiscover(dmConnId_t connId);
|
||||
static void medcGlpConfigure(dmConnId_t connId, uint8_t status);
|
||||
static void medcGlpProcMsg(wsfMsgHdr_t *pMsg);
|
||||
static void medcGlpBtn(dmConnId_t connId, uint8_t btn);
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! profile interface pointer */
|
||||
medcIf_t medcGlpIf =
|
||||
{
|
||||
medcGlpInit,
|
||||
medcGlpDiscover,
|
||||
medcGlpConfigure,
|
||||
medcGlpProcMsg,
|
||||
medcGlpBtn
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a received ATT read response, notification, or indication.
|
||||
*
|
||||
* \param pMsg Pointer to ATT callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcGlsValueUpdate(attEvt_t *pMsg)
|
||||
{
|
||||
if (pMsg->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
/* determine which profile the handle belongs to; start with most likely */
|
||||
|
||||
/* glucose */
|
||||
if (GlpcGlsValueUpdate(pMedcGlsHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* device information */
|
||||
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* GATT */
|
||||
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Profile initialization function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcGlpInit(void)
|
||||
{
|
||||
/* set handle list length */
|
||||
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
|
||||
|
||||
/* set autoconnect UUID */
|
||||
medcCb.autoUuid[0] = ATT_UUID_GLUCOSE_SERVICE;
|
||||
|
||||
/* initialize timer */
|
||||
medcGlpCb.racpTimer.handlerId = medcCb.handlerId;
|
||||
medcGlpCb.racpTimer.msg.event = MEDC_TIMER_IND;
|
||||
|
||||
/* initialize sequence number */
|
||||
GlpcGlsSetLastSeqNum(1);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Discover service for profile.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t medcGlpDiscover(dmConnId_t connId)
|
||||
{
|
||||
/* discover glucose service */
|
||||
GlpcGlsDiscover(connId, pMedcGlsHdlList);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Configure service for profile.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcGlpConfigure(dmConnId_t connId, uint8_t status)
|
||||
{
|
||||
/* configure glucose service */
|
||||
AppDiscConfigure(connId, status, MEDC_CFG_GLS_LIST_LEN,
|
||||
(attcDiscCfg_t *) medcCfgGlsList,
|
||||
GLPC_GLS_HDL_LIST_LEN, pMedcGlsHdlList);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcGlpProcMsg(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
switch(pMsg->event)
|
||||
{
|
||||
case ATTC_READ_RSP:
|
||||
case ATTC_HANDLE_VALUE_NTF:
|
||||
/* process value update */
|
||||
medcGlsValueUpdate((attEvt_t *) pMsg);
|
||||
break;
|
||||
|
||||
case ATTC_WRITE_RSP:
|
||||
/* if write to RACP was successful, start procedure timer */
|
||||
if ((((attEvt_t *) pMsg)->hdr.status == ATT_SUCCESS) &&
|
||||
(((attEvt_t *) pMsg)->handle == pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX]))
|
||||
{
|
||||
medcGlpCb.inProgress = TRUE;
|
||||
medcGlpCb.racpTimer.msg.param = pMsg->param; /* conn ID */
|
||||
WsfTimerStartSec(&medcGlpCb.racpTimer, ATT_MAX_TRANS_TIMEOUT);
|
||||
}
|
||||
break;
|
||||
|
||||
case ATTC_HANDLE_VALUE_IND:
|
||||
/* if procedure in progress stop procedure timer */
|
||||
if (medcGlpCb.inProgress)
|
||||
{
|
||||
medcGlpCb.inProgress = FALSE;
|
||||
WsfTimerStop(&medcGlpCb.racpTimer);
|
||||
}
|
||||
|
||||
/* process value */
|
||||
medcGlsValueUpdate((attEvt_t *) pMsg);
|
||||
break;
|
||||
|
||||
case DM_CONN_CLOSE_IND:
|
||||
/* if procedure in progress stop procedure timer */
|
||||
if (medcGlpCb.inProgress)
|
||||
{
|
||||
medcGlpCb.inProgress = FALSE;
|
||||
WsfTimerStop(&medcGlpCb.racpTimer);
|
||||
}
|
||||
break;
|
||||
|
||||
case MEDC_TIMER_IND:
|
||||
/* if procedure in progress then close connection */
|
||||
if (medcGlpCb.inProgress && pMsg->param != DM_CONN_ID_NONE)
|
||||
{
|
||||
medcGlpCb.inProgress = FALSE;
|
||||
|
||||
/* if configured to disconnect upon ATT transaction timeout */
|
||||
if (pAppCfg->disconnect)
|
||||
{
|
||||
AppConnClose((dmConnId_t)pMsg->param);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a button press.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcGlpBtn(dmConnId_t connId, uint8_t btn)
|
||||
{
|
||||
glpcFilter_t filter;
|
||||
|
||||
/* button actions when connected */
|
||||
if (connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
/* handle must be set to send RACP command */
|
||||
if (pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX] == ATT_HANDLE_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (btn)
|
||||
{
|
||||
case APP_UI_BTN_1_SHORT:
|
||||
/* report all records */
|
||||
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
|
||||
CH_RACP_OPCODE_REPORT, CH_RACP_OPERATOR_ALL, NULL);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_1_MED:
|
||||
/* report records greater than sequence number */
|
||||
filter.type = CH_RACP_GLS_FILTER_SEQ;
|
||||
filter.param.seqNum = GlpcGlsGetLastSeqNum();
|
||||
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
|
||||
CH_RACP_OPCODE_REPORT, CH_RACP_OPERATOR_GTEQ, &filter);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_SHORT:
|
||||
/* report number of records */
|
||||
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
|
||||
CH_RACP_OPCODE_REPORT_NUM, CH_RACP_OPERATOR_ALL, NULL);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_MED:
|
||||
/* report number of records greater than sequence number */
|
||||
filter.type = CH_RACP_GLS_FILTER_SEQ;
|
||||
filter.param.seqNum = GlpcGlsGetLastSeqNum();
|
||||
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
|
||||
CH_RACP_OPCODE_REPORT_NUM, CH_RACP_OPERATOR_GTEQ, &filter);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_LONG:
|
||||
/* abort */
|
||||
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
|
||||
CH_RACP_OPCODE_ABORT, CH_RACP_OPERATOR_NULL, NULL);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_EX_LONG:
|
||||
/* delete all records */
|
||||
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
|
||||
CH_RACP_OPCODE_DELETE, CH_RACP_OPERATOR_ALL, NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+223
@@ -0,0 +1,223 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Health/medical collector, Heart Rate profile
|
||||
*
|
||||
* Copyright (c) 2012-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_cfg.h"
|
||||
#include "app_api.h"
|
||||
#include "app_db.h"
|
||||
#include "svc_ch.h"
|
||||
#include "hrpc/hrpc_api.h"
|
||||
#include "medc/medc_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT Client Discovery Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Start of cached heart rate service handles; begins after DIS */
|
||||
#define MEDC_DISC_HRS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
|
||||
|
||||
/* Total cached handle list length */
|
||||
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_HRS_START + HRPC_HRS_HDL_LIST_LEN)
|
||||
|
||||
/*! Pointers into handle list heart rate service handles */
|
||||
static uint16_t *pMedcHrsHdlList = &medcCb.hdlList[MEDC_DISC_HRS_START];
|
||||
|
||||
/* sanity check: make sure handle list length is <= app db handle list length */
|
||||
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT Client Configuration Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/* HRS Control point "Reset Energy Expended" */
|
||||
static const uint8_t medcHrsRstEnExp[] = {CH_HRCP_RESET_ENERGY_EXP};
|
||||
|
||||
/* List of characteristics to configure after service discovery */
|
||||
static const attcDiscCfg_t medcCfgHrsList[] =
|
||||
{
|
||||
/* Read: HRS Body sensor location */
|
||||
{NULL, 0, HRPC_HRS_BSL_HDL_IDX},
|
||||
|
||||
/* Write: HRS Control point "Reset Energy Expended" */
|
||||
{medcHrsRstEnExp, sizeof(medcHrsRstEnExp), HRPC_HRS_HRCP_HDL_IDX},
|
||||
|
||||
/* Write: HRS Heart rate measurement CCC descriptor */
|
||||
{medcCccNtfVal, sizeof(medcCccNtfVal), HRPC_HRS_HRM_CCC_HDL_IDX},
|
||||
};
|
||||
|
||||
/* Characteristic configuration list length */
|
||||
#define MEDC_CFG_HRS_LIST_LEN (sizeof(medcCfgHrsList) / sizeof(attcDiscCfg_t))
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
static void medcHrpInit(void);
|
||||
static bool_t medcHrpDiscover(dmConnId_t connId);
|
||||
static void medcHrpConfigure(dmConnId_t connId, uint8_t status);
|
||||
static void medcHrpProcMsg(wsfMsgHdr_t *pMsg);
|
||||
static void medcHrpBtn(dmConnId_t connId, uint8_t btn);
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! profile interface pointer */
|
||||
medcIf_t medcHrpIf =
|
||||
{
|
||||
medcHrpInit,
|
||||
medcHrpDiscover,
|
||||
medcHrpConfigure,
|
||||
medcHrpProcMsg,
|
||||
medcHrpBtn
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a received ATT read response, notification, or indication.
|
||||
*
|
||||
* \param pMsg Pointer to ATT callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcHrsValueUpdate(attEvt_t *pMsg)
|
||||
{
|
||||
if (pMsg->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
/* determine which profile the handle belongs to; start with most likely */
|
||||
|
||||
/* heart rate */
|
||||
if (HrpcHrsValueUpdate(pMedcHrsHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* device information */
|
||||
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* GATT */
|
||||
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcHrpProcMsg(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
switch(pMsg->event)
|
||||
{
|
||||
case ATTC_READ_RSP:
|
||||
case ATTC_HANDLE_VALUE_NTF:
|
||||
case ATTC_HANDLE_VALUE_IND:
|
||||
medcHrsValueUpdate((attEvt_t *) pMsg);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Profile initialization function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcHrpInit(void)
|
||||
{
|
||||
/* set handle list length */
|
||||
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
|
||||
|
||||
/* set autoconnect UUID */
|
||||
medcCb.autoUuid[0] = ATT_UUID_HEART_RATE_SERVICE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Discover service for profile.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t medcHrpDiscover(dmConnId_t connId)
|
||||
{
|
||||
/* discover heart rate service */
|
||||
HrpcHrsDiscover(connId, pMedcHrsHdlList);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Configure service for profile.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcHrpConfigure(dmConnId_t connId, uint8_t status)
|
||||
{
|
||||
/* configure heart rate service */
|
||||
AppDiscConfigure(connId, status, MEDC_CFG_HRS_LIST_LEN,
|
||||
(attcDiscCfg_t *) medcCfgHrsList,
|
||||
HRPC_HRS_HDL_LIST_LEN, pMedcHrsHdlList);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a button press.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcHrpBtn(dmConnId_t connId, uint8_t btn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Vendored
+220
@@ -0,0 +1,220 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Health/medical collector, Health Thermometer profile
|
||||
*
|
||||
* Copyright (c) 2012-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_cfg.h"
|
||||
#include "app_api.h"
|
||||
#include "app_db.h"
|
||||
#include "svc_ch.h"
|
||||
#include "htpc/htpc_api.h"
|
||||
#include "medc/medc_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT Client Discovery Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Start of cached health thermometer service handles; begins after DIS */
|
||||
#define MEDC_DISC_HTS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
|
||||
|
||||
/* Total cached handle list length */
|
||||
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_HTS_START + HTPC_HTS_HDL_LIST_LEN)
|
||||
|
||||
/*! Pointers into handle list for health thermometer service handles */
|
||||
static uint16_t *pMedcHtsHdlList = &medcCb.hdlList[MEDC_DISC_HTS_START];
|
||||
|
||||
/* sanity check: make sure handle list length is <= app db handle list length */
|
||||
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT Client Configuration Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/* List of characteristics to configure after service discovery */
|
||||
static const attcDiscCfg_t medcCfgHtsList[] =
|
||||
{
|
||||
/* Read: Temperature type */
|
||||
{NULL, 0, HTPC_HTS_TT_HDL_IDX},
|
||||
|
||||
/* Write: Temperature measurement CCC descriptor */
|
||||
{medcCccIndVal, sizeof(medcCccIndVal), HTPC_HTS_TM_CCC_HDL_IDX},
|
||||
|
||||
/* Write: Intermediate temperature CCC descriptor */
|
||||
{medcCccNtfVal, sizeof(medcCccNtfVal), HTPC_HTS_IT_CCC_HDL_IDX},
|
||||
};
|
||||
|
||||
/* Characteristic configuration list length */
|
||||
#define MEDC_CFG_HTS_LIST_LEN (sizeof(medcCfgHtsList) / sizeof(attcDiscCfg_t))
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
static void medcHtpInit(void);
|
||||
static bool_t medcHtpDiscover(dmConnId_t connId);
|
||||
static void medcHtpConfigure(dmConnId_t connId, uint8_t status);
|
||||
static void medcHtpProcMsg(wsfMsgHdr_t *pMsg);
|
||||
static void medcHtpBtn(dmConnId_t connId, uint8_t btn);
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! profile interface pointer */
|
||||
medcIf_t medcHtpIf =
|
||||
{
|
||||
medcHtpInit,
|
||||
medcHtpDiscover,
|
||||
medcHtpConfigure,
|
||||
medcHtpProcMsg,
|
||||
medcHtpBtn
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a received ATT read response, notification, or indication.
|
||||
*
|
||||
* \param pMsg Pointer to ATT callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcHtsValueUpdate(attEvt_t *pMsg)
|
||||
{
|
||||
if (pMsg->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
/* determine which profile the handle belongs to; start with most likely */
|
||||
|
||||
/* health thermometer */
|
||||
if (HtpcHtsValueUpdate(pMedcHtsHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* device information */
|
||||
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* GATT */
|
||||
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcHtpProcMsg(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
switch(pMsg->event)
|
||||
{
|
||||
case ATTC_READ_RSP:
|
||||
case ATTC_HANDLE_VALUE_NTF:
|
||||
case ATTC_HANDLE_VALUE_IND:
|
||||
medcHtsValueUpdate((attEvt_t *) pMsg);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Profile initialization function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcHtpInit(void)
|
||||
{
|
||||
/* set handle list length */
|
||||
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
|
||||
|
||||
/* set autoconnect UUID */
|
||||
medcCb.autoUuid[0] = ATT_UUID_HEALTH_THERM_SERVICE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Discover service for profile.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t medcHtpDiscover(dmConnId_t connId)
|
||||
{
|
||||
/* discover health thermometer service */
|
||||
HtpcHtsDiscover(connId, pMedcHtsHdlList);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Configure service for profile.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcHtpConfigure(dmConnId_t connId, uint8_t status)
|
||||
{
|
||||
/* configure health thermometer service */
|
||||
AppDiscConfigure(connId, status, MEDC_CFG_HTS_LIST_LEN,
|
||||
(attcDiscCfg_t *) medcCfgHtsList,
|
||||
HTPC_HTS_HDL_LIST_LEN, pMedcHtsHdlList);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a button press.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcHtpBtn(dmConnId_t connId, uint8_t btn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Vendored
+940
@@ -0,0 +1,940 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Health/medical collector sample application for the following profiles:
|
||||
* Heart Rate profile collector
|
||||
* Blood Pressure profile collector
|
||||
* Glucose profile collector
|
||||
* Weight scale profile collector
|
||||
* Health Thermometer profile collector
|
||||
* Pulse Oximeter profile collector
|
||||
*
|
||||
* Copyright (c) 2012-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "util/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_db.h"
|
||||
#include "app_ui.h"
|
||||
#include "svc_ch.h"
|
||||
#include "svc_core.h"
|
||||
#include "gatt/gatt_api.h"
|
||||
#include "dis/dis_api.h"
|
||||
#include "medc/medc_api.h"
|
||||
#include "medc/medc_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Profile Configuration
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Heart rate profile included */
|
||||
#ifndef MEDC_HRP_INCLUDED
|
||||
#define MEDC_HRP_INCLUDED TRUE
|
||||
#endif
|
||||
|
||||
/* Blood pressure profile included */
|
||||
#ifndef MEDC_BLP_INCLUDED
|
||||
#define MEDC_BLP_INCLUDED TRUE
|
||||
#endif
|
||||
|
||||
/* Glucose profile included */
|
||||
#ifndef MEDC_GLP_INCLUDED
|
||||
#define MEDC_GLP_INCLUDED TRUE
|
||||
#endif
|
||||
|
||||
/* Weight scale profile included */
|
||||
#ifndef MEDC_WSP_INCLUDED
|
||||
#define MEDC_WSP_INCLUDED TRUE
|
||||
#endif
|
||||
|
||||
/* Health thermometer profile included */
|
||||
#ifndef MEDC_HTP_INCLUDED
|
||||
#define MEDC_HTP_INCLUDED TRUE
|
||||
#endif
|
||||
|
||||
/* Pulse oximeter profile included */
|
||||
#ifndef MEDC_PLX_INCLUDED
|
||||
#define MEDC_PLX_INCLUDED TRUE
|
||||
#endif
|
||||
|
||||
/* Default profile to use */
|
||||
#ifndef MEDC_PROFILE
|
||||
#define MEDC_PROFILE MEDC_ID_HRP
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Configurable Parameters
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! configurable parameters for master */
|
||||
const appMasterCfg_t medcMasterCfg =
|
||||
{
|
||||
96, /*! The scan interval, in 0.625 ms units */
|
||||
48, /*! The scan window, in 0.625 ms units */
|
||||
4000, /*! The scan duration in ms */
|
||||
DM_DISC_MODE_NONE, /*! The GAP discovery mode */
|
||||
DM_SCAN_TYPE_ACTIVE /*! The scan type (active or passive) */
|
||||
};
|
||||
|
||||
/*! configurable parameters for security */
|
||||
static const appSecCfg_t medcSecCfg =
|
||||
{
|
||||
DM_AUTH_BOND_FLAG, /*! Authentication and bonding flags */
|
||||
0, /*! Initiator key distribution flags */
|
||||
DM_KEY_DIST_LTK, /*! Responder key distribution flags */
|
||||
FALSE, /*! TRUE if Out-of-band pairing data is present */
|
||||
FALSE /*! TRUE to initiate security upon connection */
|
||||
};
|
||||
|
||||
/*! SMP security parameter configuration */
|
||||
static const smpCfg_t medcSmpCfg =
|
||||
{
|
||||
500, /*! 'Repeated attempts' timeout in msec */
|
||||
SMP_IO_DISP_ONLY, /*! I/O Capability */
|
||||
7, /*! Minimum encryption key length */
|
||||
16, /*! Maximum encryption key length */
|
||||
1, /*! Attempts to trigger 'repeated attempts' timeout */
|
||||
0, /*! Device authentication requirements */
|
||||
64000, /*! Maximum repeated attempts timeout in msec */
|
||||
64000, /*! Time msec before attemptExp decreases */
|
||||
2 /*! Repeated attempts multiplier exponent */
|
||||
};
|
||||
|
||||
/*! Connection parameters */
|
||||
static const hciConnSpec_t medcConnCfg =
|
||||
{
|
||||
40, /*! Minimum connection interval in 1.25ms units */
|
||||
40, /*! Maximum connection interval in 1.25ms units */
|
||||
0, /*! Connection latency */
|
||||
600, /*! Supervision timeout in 10ms units */
|
||||
0, /*! Unused */
|
||||
0 /*! Unused */
|
||||
};
|
||||
|
||||
/*! Configurable parameters for service and characteristic discovery */
|
||||
static const appDiscCfg_t medcDiscCfg =
|
||||
{
|
||||
FALSE, /*! TRUE to wait for a secure connection before initiating discovery */
|
||||
FALSE /*! TRUE to fall back on database hash to verify handles when no bond exists. */
|
||||
};
|
||||
|
||||
static const appCfg_t medcAppCfg =
|
||||
{
|
||||
TRUE, /*! TRUE to abort service discovery if service not found */
|
||||
TRUE /*! TRUE to disconnect if ATT transaction times out */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT Client Discovery Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Discovery states: enumeration of services to be discovered */
|
||||
enum
|
||||
{
|
||||
MEDC_DISC_GATT_SVC, /*! GATT service */
|
||||
MEDC_DISC_DIS_SVC, /*! Device Information service */
|
||||
MEDC_DISC_MED_SVC, /*! Configured med. service */
|
||||
MEDC_DISC_SVC_MAX /*! Discovery complete */
|
||||
};
|
||||
|
||||
/*! Pointers into handle list for each service's handles */
|
||||
uint16_t *pMedcGattHdlList = &medcCb.hdlList[MEDC_DISC_GATT_START];
|
||||
uint16_t *pMedcDisHdlList = &medcCb.hdlList[MEDC_DISC_DIS_START];
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT Client Configuration Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Configuration states: enumeration of services to be discovered */
|
||||
enum
|
||||
{
|
||||
MEDC_CFG_DIS_SVC, /*! DIS services */
|
||||
MEDC_CFG_GATT_SVC, /*! GATT services */
|
||||
MEDC_CFG_MED_SVC, /*! Configured med. service */
|
||||
MEDC_CFG_SVC_MAX /*! Configuration complete */
|
||||
};
|
||||
|
||||
/*
|
||||
* Data for configuration after service discovery
|
||||
*/
|
||||
|
||||
/* Default value for CCC indications */
|
||||
const uint8_t medcCccIndVal[2] = {UINT16_TO_BYTES(ATT_CLIENT_CFG_INDICATE)};
|
||||
|
||||
/* Default value for CCC notifications */
|
||||
const uint8_t medcCccNtfVal[2] = {UINT16_TO_BYTES(ATT_CLIENT_CFG_NOTIFY)};
|
||||
|
||||
/* Default value for Client Supported Features (enable Robust Caching) */
|
||||
const uint8_t medcCsfVal[1] = {ATTS_CSF_ROBUST_CACHING};
|
||||
|
||||
/* HRS Control point "Reset Energy Expended" */
|
||||
/* static const uint8_t medcHrsRstEnExp[] = {CH_HRCP_RESET_ENERGY_EXP}; */\
|
||||
|
||||
/* List of DIS characteristics to configure after service discovery */
|
||||
static const attcDiscCfg_t medcCfgDisList[] =
|
||||
{
|
||||
/* Read: DIS Manufacturer name string */
|
||||
{NULL, 0, DIS_MFNS_HDL_IDX},
|
||||
|
||||
/* Read: DIS Model number string */
|
||||
{NULL, 0, DIS_MNS_HDL_IDX},
|
||||
|
||||
/* Read: DIS Serial number string */
|
||||
{NULL, 0, DIS_SNS_HDL_IDX},
|
||||
|
||||
/* Read: DIS Hardware revision string */
|
||||
{NULL, 0, DIS_HRS_HDL_IDX},
|
||||
|
||||
/* Read: DIS Firmware revision string */
|
||||
{NULL, 0, DIS_FRS_HDL_IDX},
|
||||
|
||||
/* Read: DIS Software revision string */
|
||||
{NULL, 0, DIS_SRS_HDL_IDX},
|
||||
|
||||
/* Read: DIS System ID */
|
||||
{NULL, 0, DIS_SID_HDL_IDX}
|
||||
};
|
||||
|
||||
/* List of GATT characteristics to configure after service discovery */
|
||||
static const attcDiscCfg_t medcCfgGattList[] =
|
||||
{
|
||||
/* Write: GATT service changed ccc descriptor */
|
||||
{medcCccIndVal, sizeof(medcCccIndVal), GATT_SC_CCC_HDL_IDX},
|
||||
|
||||
/* Write: GATT client supported features */
|
||||
{medcCsfVal, sizeof(medcCsfVal), GATT_CSF_HDL_IDX},
|
||||
};
|
||||
|
||||
/* Characteristic configuration list length */
|
||||
#define MEDC_CFG_GATT_LIST_LEN (sizeof(medcCfgGattList) / sizeof(attcDiscCfg_t))
|
||||
#define MEDC_CFG_DIS_LIST_LEN (sizeof(medcCfgDisList) / sizeof(attcDiscCfg_t))
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! application control block */
|
||||
medcCb_t medcCb;
|
||||
|
||||
/*! connection control block */
|
||||
typedef struct {
|
||||
appDbHdl_t dbHdl; /*! Device database record handle type */
|
||||
uint8_t addrType; /*! Type of address of device to connect to */
|
||||
bdAddr_t addr; /*! Address of device to connect to */
|
||||
bool_t doConnect; /*! TRUE to issue connect on scan complete */
|
||||
} medcConnInfo_t;
|
||||
|
||||
medcConnInfo_t medcConnInfo;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application DM callback.
|
||||
*
|
||||
* \param pDmEvt DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcDmCback(dmEvt_t *pDmEvt)
|
||||
{
|
||||
dmEvt_t *pMsg;
|
||||
uint16_t len;
|
||||
uint16_t reportLen;
|
||||
|
||||
len = DmSizeOfEvt(pDmEvt);
|
||||
|
||||
if (pDmEvt->hdr.event == DM_SCAN_REPORT_IND)
|
||||
{
|
||||
reportLen = pDmEvt->scanReport.len;
|
||||
}
|
||||
else
|
||||
{
|
||||
reportLen = 0;
|
||||
}
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(len + reportLen)) != NULL)
|
||||
{
|
||||
memcpy(pMsg, pDmEvt, len);
|
||||
if (pDmEvt->hdr.event == DM_SCAN_REPORT_IND)
|
||||
{
|
||||
pMsg->scanReport.pData = (uint8_t *) ((uint8_t *) pMsg + len);
|
||||
memcpy(pMsg->scanReport.pData, pDmEvt->scanReport.pData, reportLen);
|
||||
}
|
||||
WsfMsgSend(medcCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATT callback.
|
||||
*
|
||||
* \param pEvt ATT callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcAttCback(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(medcCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform actions on scan start.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcScanStart(dmEvt_t *pMsg)
|
||||
{
|
||||
if (pMsg->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
medcCb.scanning = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform actions on scan stop.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcScanStop(dmEvt_t *pMsg)
|
||||
{
|
||||
if (pMsg->hdr.status == HCI_SUCCESS)
|
||||
{
|
||||
medcCb.scanning = FALSE;
|
||||
medcCb.autoConnect = FALSE;
|
||||
|
||||
/* Open connection */
|
||||
if (medcConnInfo.doConnect)
|
||||
{
|
||||
APP_TRACE_INFO0("medcScanStop: Opening Connection");
|
||||
|
||||
AppConnOpen(medcConnInfo.addrType, medcConnInfo.addr, medcConnInfo.dbHdl);
|
||||
medcConnInfo.doConnect = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a scan report.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcScanReport(dmEvt_t *pMsg)
|
||||
{
|
||||
uint8_t *pData;
|
||||
uint8_t len;
|
||||
appDbHdl_t dbHdl;
|
||||
bool_t connect = FALSE;
|
||||
|
||||
/* disregard if not scanning or autoconnecting */
|
||||
if (!medcCb.scanning || !medcCb.autoConnect)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* if we already have a bond with this device then connect to it */
|
||||
if ((dbHdl = AppDbFindByAddr(pMsg->scanReport.addrType, pMsg->scanReport.addr)) != APP_DB_HDL_NONE)
|
||||
{
|
||||
connect = TRUE;
|
||||
}
|
||||
/* otherwise look for desired service in advertising data */
|
||||
else
|
||||
{
|
||||
/* find Service UUID list; if full list not found search for partial */
|
||||
if ((pData = DmFindAdType(DM_ADV_TYPE_16_UUID, pMsg->scanReport.len,
|
||||
pMsg->scanReport.pData)) == NULL)
|
||||
{
|
||||
pData = DmFindAdType(DM_ADV_TYPE_16_UUID_PART, pMsg->scanReport.len,
|
||||
pMsg->scanReport.pData);
|
||||
}
|
||||
|
||||
/* if found and length checks out ok */
|
||||
if (pData != NULL && pData[DM_AD_LEN_IDX] >= (ATT_16_UUID_LEN + 1))
|
||||
{
|
||||
len = pData[DM_AD_LEN_IDX] - 1;
|
||||
pData += DM_AD_DATA_IDX;
|
||||
|
||||
while ((!connect) && (len >= ATT_16_UUID_LEN))
|
||||
{
|
||||
int8_t i;
|
||||
|
||||
for (i=0; i < MEDC_MAX_AUTO_UUID; i++)
|
||||
{
|
||||
if (medcCb.autoUuid[i] == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BYTES_UINT16_CMP(pData, medcCb.autoUuid[i]))
|
||||
{
|
||||
connect = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pData += ATT_16_UUID_LEN;
|
||||
len -= ATT_16_UUID_LEN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (connect)
|
||||
{
|
||||
APP_TRACE_INFO0("medcScanReport: Peer found.");
|
||||
|
||||
/* stop scanning and connect */
|
||||
medcCb.autoConnect = FALSE;
|
||||
AppScanStop();
|
||||
|
||||
/* Store peer information for connect on scan stop */
|
||||
medcConnInfo.addrType = pMsg->scanReport.addrType;
|
||||
memcpy(medcConnInfo.addr, pMsg->scanReport.addr, sizeof(bdAddr_t));
|
||||
medcConnInfo.dbHdl = dbHdl;
|
||||
medcConnInfo.doConnect = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform UI actions on connection open.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcOpen(dmEvt_t *pMsg)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set up procedures that need to be performed after device reset.
|
||||
*
|
||||
* \param pMsg Pointer to DM callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcSetup(dmEvt_t *pMsg)
|
||||
{
|
||||
medcCb.scanning = FALSE;
|
||||
medcCb.autoConnect = FALSE;
|
||||
medcConnInfo.doConnect = FALSE;
|
||||
|
||||
DmConnSetConnSpec((hciConnSpec_t *) &medcConnCfg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Button press callback.
|
||||
*
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcBtnCback(uint8_t btn)
|
||||
{
|
||||
dmConnId_t connId;
|
||||
|
||||
/* button actions when connected */
|
||||
if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE)
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
case APP_UI_BTN_1_LONG:
|
||||
/* disconnect */
|
||||
AppConnClose(connId);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* all other button presses-- send to profile */
|
||||
medcCb.pIf->btn(connId, btn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* button actions when not connected */
|
||||
else
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
case APP_UI_BTN_1_SHORT:
|
||||
/* if scanning cancel scanning */
|
||||
if (medcCb.scanning)
|
||||
{
|
||||
AppScanStop();
|
||||
}
|
||||
/* else auto connect */
|
||||
else if (!medcCb.autoConnect)
|
||||
{
|
||||
medcCb.autoConnect = TRUE;
|
||||
medcConnInfo.doConnect = FALSE;
|
||||
AppScanStart(medcMasterCfg.discMode, medcMasterCfg.scanType,
|
||||
medcMasterCfg.scanDuration);
|
||||
}
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_1_LONG:
|
||||
/* clear all bonding info */
|
||||
AppClearAllBondingInfo();
|
||||
break;
|
||||
|
||||
default:
|
||||
/* all other button presses-- send to profile */
|
||||
medcCb.pIf->btn(connId, btn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Discovery callback.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param status Service or configuration status.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcDiscCback(dmConnId_t connId, uint8_t status)
|
||||
{
|
||||
switch(status)
|
||||
{
|
||||
case APP_DISC_INIT:
|
||||
/* set handle list when initialization requested */
|
||||
AppDiscSetHdlList(connId, medcCb.hdlListLen, medcCb.hdlList);
|
||||
break;
|
||||
|
||||
case APP_DISC_READ_DATABASE_HASH:
|
||||
/* Read peer's database hash */
|
||||
AppDiscReadDatabaseHash(connId);
|
||||
break;
|
||||
|
||||
case APP_DISC_SEC_REQUIRED:
|
||||
/* initiate security */
|
||||
AppMasterSecurityReq(connId);
|
||||
break;
|
||||
|
||||
case APP_DISC_START:
|
||||
/* initialize discovery state */
|
||||
medcCb.discState = MEDC_DISC_GATT_SVC;
|
||||
|
||||
/* discover GATT service */
|
||||
GattDiscover(connId, pMedcGattHdlList);
|
||||
break;
|
||||
|
||||
case APP_DISC_FAILED:
|
||||
if (pAppCfg->abortDisc)
|
||||
{
|
||||
/* if discovery failed for desired service then disconnect */
|
||||
if (medcCb.discState == MEDC_DISC_MED_SVC)
|
||||
{
|
||||
AppConnClose(connId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Else falls through. */
|
||||
|
||||
case APP_DISC_CMPL:
|
||||
/* next discovery state */
|
||||
medcCb.discState++;
|
||||
|
||||
if (medcCb.discState == MEDC_DISC_DIS_SVC)
|
||||
{
|
||||
/* discover device information service */
|
||||
DisDiscover(connId, pMedcDisHdlList);
|
||||
}
|
||||
else if (medcCb.discState == MEDC_DISC_MED_SVC)
|
||||
{
|
||||
/* discover med profile service */
|
||||
if (medcCb.pIf->discover(connId) == FALSE)
|
||||
{
|
||||
/* There are other profiles to discover. Stay in the MEDC_DISC_MED_SVC state */
|
||||
medcCb.discState = MEDC_DISC_MED_SVC - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* discovery complete */
|
||||
AppDiscComplete(connId, APP_DISC_CMPL);
|
||||
|
||||
/* start configuration: configure DIS service */
|
||||
medcCb.cfgState = MEDC_CFG_DIS_SVC;
|
||||
AppDiscConfigure(connId, APP_DISC_CFG_START, MEDC_CFG_DIS_LIST_LEN,
|
||||
(attcDiscCfg_t *) medcCfgDisList,
|
||||
DIS_HDL_LIST_LEN, pMedcDisHdlList);
|
||||
}
|
||||
break;
|
||||
|
||||
case APP_DISC_CFG_START:
|
||||
/* start configuration: configure DIS service */
|
||||
medcCb.cfgState = MEDC_CFG_DIS_SVC;
|
||||
AppDiscConfigure(connId, APP_DISC_CFG_START, MEDC_CFG_DIS_LIST_LEN,
|
||||
(attcDiscCfg_t *) medcCfgDisList,
|
||||
DIS_HDL_LIST_LEN, pMedcDisHdlList);
|
||||
break;
|
||||
|
||||
case APP_DISC_CFG_CMPL:
|
||||
/* next configuration state */
|
||||
medcCb.cfgState++;
|
||||
|
||||
if (medcCb.cfgState == MEDC_CFG_GATT_SVC)
|
||||
{
|
||||
/* configure GATT */
|
||||
AppDiscConfigure(connId, APP_DISC_CFG_START, MEDC_CFG_GATT_LIST_LEN,
|
||||
(attcDiscCfg_t *) medcCfgGattList,
|
||||
GATT_HDL_LIST_LEN, pMedcGattHdlList);
|
||||
}
|
||||
else if (medcCb.cfgState == MEDC_CFG_MED_SVC)
|
||||
{
|
||||
/* configure med profile service */
|
||||
medcCb.pIf->configure(connId, APP_DISC_CFG_START);
|
||||
}
|
||||
else
|
||||
{
|
||||
AppDiscComplete(connId, status);
|
||||
}
|
||||
break;
|
||||
|
||||
case APP_DISC_CFG_CONN_START:
|
||||
/* no connection setup configuration */
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcProcMsg(dmEvt_t *pMsg)
|
||||
{
|
||||
uint8_t uiEvent = APP_UI_NONE;
|
||||
|
||||
switch(pMsg->hdr.event)
|
||||
{
|
||||
case ATTC_READ_RSP:
|
||||
case ATTC_WRITE_RSP:
|
||||
case ATTC_HANDLE_VALUE_NTF:
|
||||
case ATTC_HANDLE_VALUE_IND:
|
||||
case MEDC_TIMER_IND:
|
||||
medcCb.pIf->procMsg(&pMsg->hdr);
|
||||
break;
|
||||
|
||||
case ATT_MTU_UPDATE_IND:
|
||||
APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu);
|
||||
break;
|
||||
|
||||
case DM_RESET_CMPL_IND:
|
||||
AttsCalculateDbHash();
|
||||
medcSetup(pMsg);
|
||||
uiEvent = APP_UI_RESET_CMPL;
|
||||
break;
|
||||
|
||||
case DM_SCAN_START_IND:
|
||||
medcScanStart(pMsg);
|
||||
uiEvent = APP_UI_SCAN_START;
|
||||
break;
|
||||
|
||||
case DM_SCAN_STOP_IND:
|
||||
medcScanStop(pMsg);
|
||||
uiEvent = APP_UI_SCAN_STOP;
|
||||
break;
|
||||
|
||||
case DM_SCAN_REPORT_IND:
|
||||
medcScanReport(pMsg);
|
||||
break;
|
||||
|
||||
case DM_CONN_OPEN_IND:
|
||||
medcOpen(pMsg);
|
||||
uiEvent = APP_UI_CONN_OPEN;
|
||||
break;
|
||||
|
||||
case DM_CONN_CLOSE_IND:
|
||||
medcCb.pIf->procMsg(&pMsg->hdr);
|
||||
uiEvent = APP_UI_CONN_CLOSE;
|
||||
break;
|
||||
|
||||
case DM_SEC_PAIR_CMPL_IND:
|
||||
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:
|
||||
medcCb.pIf->procMsg(&pMsg->hdr);
|
||||
uiEvent = APP_UI_SEC_ENCRYPT;
|
||||
break;
|
||||
|
||||
case DM_SEC_ENCRYPT_FAIL_IND:
|
||||
medcCb.pIf->procMsg(&pMsg->hdr);
|
||||
uiEvent = APP_UI_SEC_ENCRYPT_FAIL;
|
||||
break;
|
||||
|
||||
case DM_SEC_AUTH_REQ_IND:
|
||||
AppHandlePasskey(&pMsg->authReq);
|
||||
break;
|
||||
|
||||
case DM_PRIV_CLEAR_RES_LIST_IND:
|
||||
APP_TRACE_INFO1("Clear resolving list status 0x%02x", pMsg->hdr.status);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void MedcHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
APP_TRACE_INFO0("MedcHandlerInit");
|
||||
|
||||
/* store handler ID */
|
||||
medcCb.handlerId = handlerId;
|
||||
|
||||
/* Set configuration pointers */
|
||||
pAppMasterCfg = (appMasterCfg_t *) &medcMasterCfg;
|
||||
pAppSecCfg = (appSecCfg_t *) &medcSecCfg;
|
||||
pAppDiscCfg = (appDiscCfg_t *) &medcDiscCfg;
|
||||
pAppCfg = (appCfg_t *) &medcAppCfg;
|
||||
|
||||
/* Set stack configuration pointers */
|
||||
pSmpCfg = (smpCfg_t *) &medcSmpCfg;
|
||||
|
||||
/* Initialize application framework */
|
||||
AppMasterInit();
|
||||
AppDiscInit();
|
||||
|
||||
/* Set default profile to use */
|
||||
MedcSetProfile(MEDC_PROFILE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void MedcHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
APP_TRACE_INFO1("Medc got evt %d", pMsg->event);
|
||||
|
||||
/* process ATT messages */
|
||||
if (pMsg->event <= ATT_CBACK_END)
|
||||
{
|
||||
/* process discovery-related ATT messages */
|
||||
AppDiscProcAttMsg((attEvt_t *) pMsg);
|
||||
|
||||
/* process server-related ATT messages */
|
||||
AppServerProcAttMsg(pMsg);
|
||||
}
|
||||
/* process DM messages */
|
||||
else if (pMsg->event <= DM_CBACK_END)
|
||||
{
|
||||
/* process advertising and connection-related messages */
|
||||
AppMasterProcDmMsg((dmEvt_t *) pMsg);
|
||||
|
||||
/* process security-related messages */
|
||||
AppMasterSecProcDmMsg((dmEvt_t *) pMsg);
|
||||
|
||||
/* process discovery-related messages */
|
||||
AppDiscProcDmMsg((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/* perform profile and user interface-related operations */
|
||||
medcProcMsg((dmEvt_t *) pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void MedcStart(void)
|
||||
{
|
||||
/* Register for stack callbacks */
|
||||
DmRegister(medcDmCback);
|
||||
DmConnRegister(DM_CLIENT_ID_APP, medcDmCback);
|
||||
AttRegister(medcAttCback);
|
||||
|
||||
/* Register for app framework button callbacks */
|
||||
AppUiBtnRegister(medcBtnCback);
|
||||
|
||||
/* Initialize attribute server database */
|
||||
SvcCoreAddGroup();
|
||||
|
||||
/* Register for app framework discovery callbacks */
|
||||
AppDiscRegister(medcDiscCback);
|
||||
|
||||
/* Reset the device */
|
||||
DmDevReset();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the profile to be used by the application. This function is called internally
|
||||
* by MedcHandlerInit() with a default value. It may also be called by the system
|
||||
* to configure the profile after executing MedcHandlerInit() and before executing
|
||||
* MedcStart().
|
||||
*
|
||||
* \param profile Profile identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void MedcSetProfile(uint8_t profile)
|
||||
{
|
||||
switch (profile)
|
||||
{
|
||||
#if MEDC_HRP_INCLUDED == TRUE
|
||||
case MEDC_ID_HRP:
|
||||
medcCb.pIf = &medcHrpIf;
|
||||
medcCb.pIf->init();
|
||||
break;
|
||||
#endif
|
||||
#if MEDC_BLP_INCLUDED == TRUE
|
||||
case MEDC_ID_BLP:
|
||||
medcCb.pIf = &medcBlpIf;
|
||||
medcCb.pIf->init();
|
||||
break;
|
||||
#endif
|
||||
#if MEDC_GLP_INCLUDED == TRUE
|
||||
case MEDC_ID_GLP:
|
||||
medcCb.pIf = &medcGlpIf;
|
||||
medcCb.pIf->init();
|
||||
break;
|
||||
#endif
|
||||
#if MEDC_WSP_INCLUDED == TRUE
|
||||
case MEDC_ID_WSP:
|
||||
medcCb.pIf = &medcWspIf;
|
||||
medcCb.pIf->init();
|
||||
break;
|
||||
#endif
|
||||
#if MEDC_HTP_INCLUDED == TRUE
|
||||
case MEDC_ID_HTP:
|
||||
medcCb.pIf = &medcHtpIf;
|
||||
medcCb.pIf->init();
|
||||
break;
|
||||
#endif
|
||||
#if MEDC_PLX_INCLUDED == TRUE
|
||||
case MEDC_ID_PLX:
|
||||
medcCb.pIf = &medcPlxpIf;
|
||||
medcCb.pIf->init();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
APP_TRACE_WARN1("MedcSetProfile invalid profile:%d", profile);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Vendored
+138
@@ -0,0 +1,138 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Health/medical collector sample application interface file.
|
||||
*
|
||||
* Copyright (c) 2012-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#ifndef MEDC_MAIN_H
|
||||
#define MEDC_MAIN_H
|
||||
|
||||
#include "gatt/gatt_api.h"
|
||||
#include "dis/dis_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! the Client handle list, medcCb.hdlList[], is set as follows:
|
||||
*
|
||||
* ------------------------------- <- MEDC_DISC_GATT_START
|
||||
* | GATT handles |
|
||||
* |... |
|
||||
* ------------------------------- <- MEDC_DISC_DIS_START
|
||||
* | DIS handles |
|
||||
* | ... |
|
||||
* ------------------------------- <- Configured med service start
|
||||
* | Med service handles |
|
||||
* | ... |
|
||||
* -------------------------------
|
||||
*/
|
||||
|
||||
/*! Maximum number of Service UUID for autoconnect */
|
||||
#define MEDC_MAX_AUTO_UUID 2
|
||||
|
||||
/*! Start of each service's handles in the the handle list */
|
||||
#define MEDC_DISC_GATT_START 0
|
||||
#define MEDC_DISC_DIS_START (MEDC_DISC_GATT_START + GATT_HDL_LIST_LEN)
|
||||
|
||||
/*! WSF message event starting value */
|
||||
#define MEDC_MSG_START 0xA0
|
||||
|
||||
/*! WSF message event enumeration */
|
||||
enum
|
||||
{
|
||||
MEDC_TIMER_IND = MEDC_MSG_START, /*! Timer expired */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! profile interface callback functions */
|
||||
typedef void (*medcInitCback_t)(void);
|
||||
typedef bool_t (*medcDiscoverCback_t)(dmConnId_t connId);
|
||||
typedef void (*medcConfigureCback_t)(dmConnId_t connId, uint8_t status);
|
||||
typedef void (*medcProcMsgCback_t)(wsfMsgHdr_t *pMsg);
|
||||
typedef void (*medcBtnCback_t)(dmConnId_t connId, uint8_t btn);
|
||||
|
||||
/*! profile interface structure */
|
||||
typedef struct
|
||||
{
|
||||
medcInitCback_t init;
|
||||
medcDiscoverCback_t discover;
|
||||
medcConfigureCback_t configure;
|
||||
medcProcMsgCback_t procMsg;
|
||||
medcBtnCback_t btn;
|
||||
} medcIf_t;
|
||||
|
||||
/*! application control block */
|
||||
typedef struct
|
||||
{
|
||||
uint16_t hdlList[APP_DB_HDL_LIST_LEN]; /*! Cached handle list */
|
||||
medcIf_t *pIf; /*! Profile interface */
|
||||
wsfHandlerId_t handlerId; /*! WSF hander ID */
|
||||
uint16_t autoUuid[MEDC_MAX_AUTO_UUID]; /*! Service UUID for autoconnect */
|
||||
bool_t scanning; /*! TRUE if scanning */
|
||||
bool_t autoConnect; /*! TRUE if auto-connecting */
|
||||
uint8_t hdlListLen; /*! Cached handle list length */
|
||||
uint8_t discState; /*! Service discovery state */
|
||||
uint8_t cfgState; /*! Service configuration state */
|
||||
} medcCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Default value for CCC indications and notifications */
|
||||
extern const uint8_t medcCccIndVal[2];
|
||||
|
||||
/*! Default value for CCC notifications */
|
||||
extern const uint8_t medcCccNtfVal[2];
|
||||
|
||||
/*! Pointers into handle list for GATT and DIS service handles */
|
||||
extern uint16_t *pMedcGattHdlList;
|
||||
extern uint16_t *pMedcDisHdlList;
|
||||
|
||||
/*! application control block */
|
||||
extern medcCb_t medcCb;
|
||||
|
||||
/*! profile interface pointers */
|
||||
extern medcIf_t medcHrpIf; /* heart rate profile */
|
||||
extern medcIf_t medcBlpIf; /* blood pressure profile */
|
||||
extern medcIf_t medcGlpIf; /* glucose profile */
|
||||
extern medcIf_t medcWspIf; /* weight scale profile */
|
||||
extern medcIf_t medcHtpIf; /* health thermometer profile */
|
||||
extern medcIf_t medcPlxpIf; /* pulse oximeter profile */
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* MEDC_MAIN_H */
|
||||
|
||||
Vendored
+381
@@ -0,0 +1,381 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Health/medical collector, Pulse Oximeter profile
|
||||
*
|
||||
* Copyright (c) 2016-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_cfg.h"
|
||||
#include "app_api.h"
|
||||
#include "app_db.h"
|
||||
#include "app_ui.h"
|
||||
#include "app_main.h"
|
||||
#include "svc_ch.h"
|
||||
#include "plxpc/plxpc_api.h"
|
||||
#include "medc/medc_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT Client Discovery Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Start of cached health pulse oximeter service handles; begins after DIS */
|
||||
#define MEDC_DISC_PLXS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
|
||||
|
||||
/* Total cached handle list length */
|
||||
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_PLXS_START + PLXPC_PLXS_HDL_LIST_LEN)
|
||||
|
||||
/*! Pointers into handle list for health pulse oximeter service handles */
|
||||
static uint16_t *pMedcPlxsHdlList = &medcCb.hdlList[MEDC_DISC_PLXS_START];
|
||||
|
||||
/* sanity check: make sure handle list length is <= app db handle list length */
|
||||
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
|
||||
|
||||
/* Default MTU */
|
||||
#define MEDC_PLX_DEFAULT_MTU 50
|
||||
|
||||
/**************************************************************************************************
|
||||
Configurable Parameters
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! ATT configurable parameters (increase MTU) */
|
||||
static const attCfg_t medcPlxpAttCfg =
|
||||
{
|
||||
15, /* ATT server service discovery connection idle timeout in seconds */
|
||||
MEDC_PLX_DEFAULT_MTU, /* desired ATT MTU */
|
||||
ATT_MAX_TRANS_TIMEOUT, /* transcation timeout in seconds */
|
||||
4 /* number of queued prepare writes supported by server */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT Client Configuration Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/* List of characteristics to configure after service discovery */
|
||||
static const attcDiscCfg_t medcCfgPlxsList[] =
|
||||
{
|
||||
/* Read: Features */
|
||||
{NULL, 0, PLXPC_PLXS_PLXF_HDL_IDX},
|
||||
|
||||
/* Write: Spot Check CCC descriptor */
|
||||
{medcCccIndVal, sizeof(medcCccIndVal), PLXPC_PLXS_PLXSC_CCC_HDL_IDX},
|
||||
|
||||
/* Write: Continuous Measurement CCC descriptor */
|
||||
{medcCccNtfVal, sizeof(medcCccNtfVal), PLXPC_PLXS_PLXC_CCC_HDL_IDX},
|
||||
|
||||
/* Write: Record Access Control Point CCC descriptor */
|
||||
{medcCccIndVal, sizeof(medcCccIndVal), PLXPC_PLXS_RACP_CCC_HDL_IDX},
|
||||
};
|
||||
|
||||
/* Characteristic configuration list length */
|
||||
#define MEDC_CFG_PLXS_LIST_LEN (sizeof(medcCfgPlxsList) / sizeof(attcDiscCfg_t))
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Control block */
|
||||
static struct
|
||||
{
|
||||
wsfTimer_t racpTimer; /*! RACP procedure timer */
|
||||
bool_t inProgress; /*! RACP procedure in progress */
|
||||
bool_t restoreConnection; /*! Indicates if a dropped connection should be restored */
|
||||
} medcPlxCb;
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
static void medcPlxpInit(void);
|
||||
static bool_t medcPlxpDiscover(dmConnId_t connId);
|
||||
static void medcPlxpConfigure(dmConnId_t connId, uint8_t status);
|
||||
static void medcPlxpProcMsg(wsfMsgHdr_t *pMsg);
|
||||
static void medcPlxpBtn(dmConnId_t connId, uint8_t btn);
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! profile interface pointer */
|
||||
medcIf_t medcPlxpIf =
|
||||
{
|
||||
medcPlxpInit,
|
||||
medcPlxpDiscover,
|
||||
medcPlxpConfigure,
|
||||
medcPlxpProcMsg,
|
||||
medcPlxpBtn
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
External Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! MEDC application master configuration */
|
||||
extern const appMasterCfg_t medcMasterCfg;
|
||||
|
||||
/*! App connection control block */
|
||||
extern appConnCb_t appConnCb[DM_CONN_MAX];
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a received ATT read response, notification, or indication.
|
||||
*
|
||||
* \param pMsg Pointer to ATT callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcPlxsValueUpdate(attEvt_t *pMsg)
|
||||
{
|
||||
if (pMsg->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
/* determine which profile the handle belongs to; start with most likely */
|
||||
|
||||
/* pulse oximeter */
|
||||
if (PlxpcPlxsValueUpdate(pMedcPlxsHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* device information */
|
||||
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* GATT */
|
||||
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcPlxpProcMsg(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
appConnCb_t *pCb;
|
||||
|
||||
switch(pMsg->event)
|
||||
{
|
||||
case DM_CONN_CLOSE_IND:
|
||||
/* if procedure in progress stop procedure timer */
|
||||
if (medcPlxCb.inProgress)
|
||||
{
|
||||
medcPlxCb.inProgress = FALSE;
|
||||
WsfTimerStop(&medcPlxCb.racpTimer);
|
||||
}
|
||||
|
||||
if (medcPlxCb.restoreConnection == TRUE)
|
||||
{
|
||||
medcCb.autoConnect = TRUE;
|
||||
AppScanStart(medcMasterCfg.discMode, medcMasterCfg.scanType,
|
||||
medcMasterCfg.scanDuration);
|
||||
}
|
||||
break;
|
||||
|
||||
case DM_SEC_ENCRYPT_IND:
|
||||
if (medcPlxCb.restoreConnection == TRUE)
|
||||
{
|
||||
AppDiscConfigure((dmConnId_t) pMsg->param, HCI_SUCCESS, MEDC_CFG_PLXS_LIST_LEN,
|
||||
(attcDiscCfg_t *) medcCfgPlxsList,
|
||||
PLXPC_PLXS_HDL_LIST_LEN, pMedcPlxsHdlList);
|
||||
|
||||
medcPlxCb.restoreConnection = FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case DM_SEC_ENCRYPT_FAIL_IND:
|
||||
if (medcPlxCb.restoreConnection == TRUE)
|
||||
{
|
||||
/* look up app connection control block from DM connection ID */
|
||||
pCb = &appConnCb[pMsg->param - 1];
|
||||
|
||||
pCb->initiatingSec = FALSE;
|
||||
|
||||
/* if database record handle valid */
|
||||
if (pCb->dbHdl != APP_DB_HDL_NONE)
|
||||
{
|
||||
AppDbDeleteRecord(pCb->dbHdl);
|
||||
pCb->dbHdl = APP_DB_HDL_NONE;
|
||||
}
|
||||
|
||||
AppMasterSecurityReq((dmConnId_t) pMsg->param);
|
||||
}
|
||||
break;
|
||||
|
||||
case ATTC_READ_RSP:
|
||||
case ATTC_HANDLE_VALUE_NTF:
|
||||
medcPlxsValueUpdate((attEvt_t *) pMsg);
|
||||
break;
|
||||
|
||||
case ATTC_WRITE_RSP:
|
||||
/* if write to RACP was successful, start procedure timer */
|
||||
if ((((attEvt_t *) pMsg)->hdr.status == ATT_SUCCESS) &&
|
||||
(((attEvt_t *) pMsg)->handle == pMedcPlxsHdlList[PLXPC_PLXS_RACP_HDL_IDX]))
|
||||
{
|
||||
medcPlxCb.inProgress = TRUE;
|
||||
medcPlxCb.racpTimer.msg.param = pMsg->param; /* conn ID */
|
||||
WsfTimerStartSec(&medcPlxCb.racpTimer, ATT_MAX_TRANS_TIMEOUT);
|
||||
}
|
||||
break;
|
||||
|
||||
case ATTC_HANDLE_VALUE_IND:
|
||||
/* if procedure in progress stop procedure timer */
|
||||
if (medcPlxCb.inProgress)
|
||||
{
|
||||
medcPlxCb.inProgress = FALSE;
|
||||
WsfTimerStop(&medcPlxCb.racpTimer);
|
||||
}
|
||||
|
||||
medcPlxsValueUpdate((attEvt_t *) pMsg);
|
||||
break;
|
||||
|
||||
case MEDC_TIMER_IND:
|
||||
/* if procedure in progress then close connection */
|
||||
if (medcPlxCb.inProgress && pMsg->param != DM_CONN_ID_NONE)
|
||||
{
|
||||
medcPlxCb.inProgress = FALSE;
|
||||
|
||||
/* if configured to disconnect upon ATT transaction timeout */
|
||||
if (pAppCfg->disconnect)
|
||||
{
|
||||
AppConnClose((dmConnId_t)pMsg->param);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Profile initialization function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcPlxpInit(void)
|
||||
{
|
||||
/* Set configuration pointers */
|
||||
pAttCfg = (attCfg_t *) &medcPlxpAttCfg;
|
||||
|
||||
/* set handle list length */
|
||||
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
|
||||
|
||||
/* set autoconnect UUID */
|
||||
medcCb.autoUuid[0] = ATT_UUID_PULSE_OXIMITER_SERVICE;
|
||||
|
||||
/* initialize timer */
|
||||
medcPlxCb.racpTimer.handlerId = medcCb.handlerId;
|
||||
medcPlxCb.racpTimer.msg.event = MEDC_TIMER_IND;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Discover service for profile.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t medcPlxpDiscover(dmConnId_t connId)
|
||||
{
|
||||
/* discover health pulse oximeter service */
|
||||
PlxpcPlxsDiscover(connId, pMedcPlxsHdlList);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Configure service for profile.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcPlxpConfigure(dmConnId_t connId, uint8_t status)
|
||||
{
|
||||
/* configure health pulse oximeter service */
|
||||
AppDiscConfigure(connId, status, MEDC_CFG_PLXS_LIST_LEN,
|
||||
(attcDiscCfg_t *) medcCfgPlxsList,
|
||||
PLXPC_PLXS_HDL_LIST_LEN, pMedcPlxsHdlList);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a button press.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcPlxpBtn(dmConnId_t connId, uint8_t btn)
|
||||
{
|
||||
/* button actions when connected */
|
||||
if (connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
case APP_UI_BTN_1_MED:
|
||||
medcPlxCb.restoreConnection = TRUE;
|
||||
AppConnClose(connId);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_SHORT:
|
||||
PlxpcPlxsRacpSend(connId, pMedcPlxsHdlList[PLXPC_PLXS_RACP_HDL_IDX], CH_RACP_OPCODE_DELETE, CH_RACP_OPERATOR_ALL);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_MED:
|
||||
PlxpcPlxsRacpSend(connId, pMedcPlxsHdlList[PLXPC_PLXS_RACP_HDL_IDX], CH_RACP_OPCODE_REPORT, CH_RACP_OPERATOR_ALL);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_LONG:
|
||||
PlxpcPlxsRacpSend(connId, pMedcPlxsHdlList[PLXPC_PLXS_RACP_HDL_IDX], CH_RACP_OPCODE_REPORT_NUM, CH_RACP_OPERATOR_ALL);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_EX_LONG:
|
||||
PlxpcPlxsRacpSend(connId, pMedcPlxsHdlList[PLXPC_PLXS_RACP_HDL_IDX], CH_RACP_OPCODE_ABORT, CH_RACP_OPERATOR_ALL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+315
@@ -0,0 +1,315 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Health/medical collector, Weight Scale profile
|
||||
*
|
||||
* Copyright (c) 2012-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_assert.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_cfg.h"
|
||||
#include "app_api.h"
|
||||
#include "app_db.h"
|
||||
#include "app_ui.h"
|
||||
#include "svc_ch.h"
|
||||
#include "wspc/wspc_api.h"
|
||||
#include "udsc/udsc_api.h"
|
||||
#include "medc/medc_main.h"
|
||||
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT Client Discovery Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Discovery states: enumeration of services to be discovered */
|
||||
enum
|
||||
{
|
||||
MEDC_DISC_WSP_UDS_SVC, /*! User data service */
|
||||
MEDC_DISC_WSP_WSS_SVC, /*! Weight scale service */
|
||||
};
|
||||
|
||||
/* Start of cached weight scale service handles; begins after DIS */
|
||||
#define MEDC_DISC_WSS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
|
||||
|
||||
/* Start of User Data Service handles; begins after weight scale serivce */
|
||||
#define MEDC_DISC_UDS_START (MEDC_DISC_WSS_START + WSPC_WSS_HDL_LIST_LEN)
|
||||
|
||||
/* Total cached handle list length */
|
||||
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_UDS_START + UDSC_HDL_LIST_LEN)
|
||||
|
||||
/*! Pointers into handle list for weight scale service handles */
|
||||
static uint16_t *pMedcWssHdlList = &medcCb.hdlList[MEDC_DISC_WSS_START];
|
||||
|
||||
/*! Pointers into handle list for weight scale service handles */
|
||||
static uint16_t *pMedcUdsHdlList = &medcCb.hdlList[MEDC_DISC_UDS_START];
|
||||
|
||||
/*! Weight Scale Profile discovery state */
|
||||
static uint8_t pMedcWspDiscState;
|
||||
|
||||
/* sanity check: make sure handle list length is <= app db handle list length */
|
||||
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
|
||||
|
||||
/**************************************************************************************************
|
||||
ATT Client Configuration Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/* List of characteristics to configure after service discovery */
|
||||
static const attcDiscCfg_t medcCfgWssList[] =
|
||||
{
|
||||
/* Read: Weight scale feature */
|
||||
{NULL, 0, MEDC_DISC_WSS_START + WSPC_WSS_WSF_HDL_IDX},
|
||||
|
||||
/* Write: Weight scale measurement CCC descriptor */
|
||||
{medcCccIndVal, sizeof(medcCccIndVal), MEDC_DISC_WSS_START + WSPC_WSS_WSM_CCC_HDL_IDX},
|
||||
|
||||
/* Write: Database Change Interval CCC descriptor */
|
||||
{medcCccNtfVal, sizeof(medcCccNtfVal), MEDC_DISC_UDS_START + UDSC_DCBI_CCC_HDL_IDX},
|
||||
|
||||
/* Write: User Data Service Control Point CCC descriptor */
|
||||
{medcCccIndVal, sizeof(medcCccIndVal), MEDC_DISC_UDS_START + UDSC_UCP_CCC_HDL_IDX}
|
||||
};
|
||||
|
||||
/* Characteristic configuration list length */
|
||||
#define MEDC_CFG_WSS_LIST_LEN (sizeof(medcCfgWssList) / sizeof(attcDiscCfg_t))
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
static void medcWspInit(void);
|
||||
static bool_t medcWspDiscover(dmConnId_t connId);
|
||||
static void medcWspConfigure(dmConnId_t connId, uint8_t status);
|
||||
static void medcWspProcMsg(wsfMsgHdr_t *pMsg);
|
||||
static void medcWspBtn(dmConnId_t connId, uint8_t btn);
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! profile interface pointer */
|
||||
medcIf_t medcWspIf =
|
||||
{
|
||||
medcWspInit,
|
||||
medcWspDiscover,
|
||||
medcWspConfigure,
|
||||
medcWspProcMsg,
|
||||
medcWspBtn
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process a received ATT read response, notification, or indication.
|
||||
*
|
||||
* \param pMsg Pointer to ATT callback event message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcWssValueUpdate(attEvt_t *pMsg)
|
||||
{
|
||||
if (pMsg->hdr.status == ATT_SUCCESS)
|
||||
{
|
||||
/* determine which profile the handle belongs to; start with most likely */
|
||||
|
||||
/* weight scale */
|
||||
if (WspcWssValueUpdate(pMedcWssHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* user data service */
|
||||
if (UdscValueUpdate(pMedcUdsHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* device information */
|
||||
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* GATT */
|
||||
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcWspProcMsg(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
switch(pMsg->event)
|
||||
{
|
||||
case DM_CONN_CLOSE_IND:
|
||||
/* initialize discovery state */
|
||||
pMedcWspDiscState = MEDC_DISC_WSP_UDS_SVC;
|
||||
|
||||
/* Notify UDSC the connection closed */
|
||||
UdscClose();
|
||||
break;
|
||||
|
||||
case ATTC_HANDLE_VALUE_IND:
|
||||
medcWssValueUpdate((attEvt_t *) pMsg);
|
||||
break;
|
||||
|
||||
case MEDC_TIMER_IND:
|
||||
APP_TRACE_INFO0("medcWspProcMsg - Timeout waiting for UDS control point response.");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief UDS Control Point Response Callback.
|
||||
*
|
||||
* \param connId Connection ID.
|
||||
* \param opcode Cmd opcode being responded to.
|
||||
* \param response Response code.
|
||||
* \param index USer index (only set when opcode is UDSC_UCP_OPCODE_RNU)
|
||||
*
|
||||
* \return None
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcWspUdsRspCallback(dmConnId_t connId, uint8_t opcode, uint8_t response, uint8_t index)
|
||||
{
|
||||
APP_TRACE_INFO3("medcWspUdsRspCallback - op: %d rsp: %d index: %d", opcode, response, index);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Profile initialization function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcWspInit(void)
|
||||
{
|
||||
/* set handle list length */
|
||||
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
|
||||
|
||||
/* set autoconnect UUID */
|
||||
medcCb.autoUuid[0] = ATT_UUID_WEIGHT_SCALE_SERVICE;
|
||||
medcCb.autoUuid[1] = ATT_UUID_USER_DATA_SERVICE;
|
||||
|
||||
/* Register the UDS Control Point Callback */
|
||||
UdscInit(medcCb.handlerId, MEDC_TIMER_IND, medcWspUdsRspCallback);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Discover service for profile.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
*
|
||||
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static bool_t medcWspDiscover(dmConnId_t connId)
|
||||
{
|
||||
if (pMedcWspDiscState == MEDC_DISC_WSP_UDS_SVC)
|
||||
{
|
||||
/* user data service */
|
||||
UdscDiscover(connId, pMedcUdsHdlList);
|
||||
pMedcWspDiscState++;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (pMedcWspDiscState == MEDC_DISC_WSP_WSS_SVC)
|
||||
{
|
||||
/* discover weight scale service */
|
||||
WspcWssDiscover(connId, pMedcWssHdlList);
|
||||
pMedcWspDiscState++;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Configure service for profile.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcWspConfigure(dmConnId_t connId, uint8_t status)
|
||||
{
|
||||
/* configure weight scale service */
|
||||
AppDiscConfigure(connId, status, MEDC_CFG_WSS_LIST_LEN,
|
||||
(attcDiscCfg_t *) medcCfgWssList,
|
||||
WSPC_WSS_HDL_LIST_LEN, medcCb.hdlList);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Handle a button press.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medcWspBtn(dmConnId_t connId, uint8_t btn)
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
case APP_UI_BTN_2_SHORT:
|
||||
UdscRegisterNewUser(connId, pMedcUdsHdlList[UDSC_UCP_IDX], 1234);
|
||||
break;
|
||||
case APP_UI_BTN_2_MED:
|
||||
UdscConsent(connId, pMedcUdsHdlList[UDSC_UCP_IDX], 2, 1234);
|
||||
break;
|
||||
case APP_UI_BTN_2_LONG:
|
||||
UdscDeleteUserData(connId, pMedcUdsHdlList[UDSC_UCP_IDX]);
|
||||
break;
|
||||
case APP_UI_BTN_2_EX_LONG:
|
||||
UdscDeleteUserData(connId, pMedcUdsHdlList[UDSC_UCP_IDX]);
|
||||
break;
|
||||
case APP_UI_BTN_1_SHORT:
|
||||
UdscReadDatabaseChangeIncrement(connId, pMedcUdsHdlList[UDSC_DBCI_HDL_IDX]);
|
||||
break;
|
||||
case APP_UI_BTN_1_MED:
|
||||
UdscReadUserIndex(connId, pMedcUdsHdlList[UDSC_UI_HDL_IDX]);
|
||||
break;
|
||||
case APP_UI_BTN_1_EX_LONG:
|
||||
UdscWriteDatabaseChangeIncrement(connId, pMedcUdsHdlList[UDSC_DBCI_HDL_IDX], 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Vendored
+101
@@ -0,0 +1,101 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Medical sensor sample application interface.
|
||||
*
|
||||
* Copyright (c) 2012-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef MEDS_API_H
|
||||
#define MEDS_API_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Profile identifier used for MedsSetProfile() */
|
||||
enum
|
||||
{
|
||||
MEDS_ID_BLP, /*! Blood pressure profile */
|
||||
MEDS_ID_WSP, /*! Weight scale profile */
|
||||
MEDS_ID_HTP, /*! Health thermometer profile */
|
||||
MEDS_ID_PLX, /*! Pulse Oximeter profile */
|
||||
MEDS_ID_GLP /*! Glucose profile */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void MedsStart(void);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for App.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void MedsHandlerInit(wsfHandlerId_t handlerId);
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for the application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void MedsHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the profile to be used by the application. This function is called internally
|
||||
* by MedsHandlerInit() with a default value. It may also be called by the system
|
||||
* to configure the profile after executing MedsHandlerInit() and before executing
|
||||
* MedsStart().
|
||||
*
|
||||
* \param profile Profile identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void MedsSetProfile(uint8_t profile);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* MEDS_API_H */
|
||||
Vendored
+322
@@ -0,0 +1,322 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Medical sensor sample, blood pressure profile
|
||||
*
|
||||
* Copyright (c) 2012-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "util/bstream.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "hci_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_ui.h"
|
||||
#include "svc_ch.h"
|
||||
#include "svc_bps.h"
|
||||
#include "svc_dis.h"
|
||||
#include "svc_core.h"
|
||||
#include "blps/blps_api.h"
|
||||
#include "gatt/gatt_api.h"
|
||||
#include "meds/meds_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! enumeration of client characteristic configuration descriptors */
|
||||
enum
|
||||
{
|
||||
MEDS_BLP_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
|
||||
MEDS_BLP_BPS_BPM_CCC_IDX, /*! Blood pressure service, blood pressure measurement characteristic */
|
||||
MEDS_BLP_BPS_ICP_CCC_IDX, /*! Blood pressure service, intermediate cuff pressure characteristic */
|
||||
MEDS_BLP_NUM_CCC_IDX
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Configurable Parameters
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! blood pressure measurement configuration */
|
||||
static const blpsCfg_t medsBlpsCfg =
|
||||
{
|
||||
2000 /*! Measurement timer expiration period in ms */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Advertising Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Service UUID list */
|
||||
static const uint8_t medsSvcUuidList[] =
|
||||
{
|
||||
UINT16_TO_BYTES(ATT_UUID_BLOOD_PRESSURE_SERVICE),
|
||||
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE)
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Client Characteristic Configuration Descriptors
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
|
||||
static const attsCccSet_t medsBlpCccSet[MEDS_BLP_NUM_CCC_IDX] =
|
||||
{
|
||||
/* cccd handle value range security level */
|
||||
{GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_ENC}, /* MEDS_BLP_GATT_SC_CCC_IDX */
|
||||
{BPS_BPM_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_ENC}, /* MEDS_BLP_BPS_BPM_CCC_IDX */
|
||||
{BPS_ICP_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_ENC} /* MEDS_BLP_BPS_ICP_CCC_IDX */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
static void medsBlpStart(void);
|
||||
static void medsBlpProcMsg(wsfMsgHdr_t *pMsg);
|
||||
static void medsBlpBtn(dmConnId_t connId, uint8_t btn);
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! profile interface pointer */
|
||||
medsIf_t medsBlpIf =
|
||||
{
|
||||
NULL,
|
||||
medsBlpStart,
|
||||
medsBlpProcMsg,
|
||||
medsBlpBtn
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! application control block */
|
||||
static struct
|
||||
{
|
||||
bool_t measuring;
|
||||
bool_t storedMeasurement;
|
||||
} medsBlpCb;
|
||||
|
||||
static uint8_t medsBpmFlags = CH_BPM_FLAG_UNITS_MMHG | CH_BPM_FLAG_TIMESTAMP |
|
||||
CH_BPM_FLAG_PULSE_RATE | CH_BPM_FLAG_MEAS_STATUS;
|
||||
static uint8_t medsIcpFlags = CH_BPM_FLAG_UNITS_MMHG | CH_BPM_FLAG_PULSE_RATE |
|
||||
CH_BPM_FLAG_MEAS_STATUS;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform UI actions on connection close.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsBlpClose(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* stop blood pressure measurement */
|
||||
BlpsMeasStop();
|
||||
medsBlpCb.measuring = FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsBlpStart(void)
|
||||
{
|
||||
/* set up CCCD table and callback */
|
||||
AttsCccRegister(MEDS_BLP_NUM_CCC_IDX, (attsCccSet_t *) medsBlpCccSet, medsCccCback);
|
||||
|
||||
/* add blood pressure service */
|
||||
SvcBpsAddGroup();
|
||||
|
||||
/* Set Service Changed CCCD index. */
|
||||
GattSetSvcChangedIdx(MEDS_BLP_GATT_SC_CCC_IDX);
|
||||
|
||||
/* initialize blood pressure profile sensor */
|
||||
BlpsInit(medsCb.handlerId, (blpsCfg_t *) &medsBlpsCfg);
|
||||
BlpsSetBpmFlags(medsBpmFlags);
|
||||
BlpsSetIcpFlags(medsIcpFlags);
|
||||
|
||||
/* set advertising data */
|
||||
AppAdvSetAdValue(APP_ADV_DATA_DISCOVERABLE, DM_ADV_TYPE_16_UUID, sizeof(medsSvcUuidList),
|
||||
(uint8_t *) medsSvcUuidList);
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send stored blood pressure measurement.
|
||||
*
|
||||
* \param connId Connection ID to send stored measurement to.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsBlpSendStoredMeasurement(dmConnId_t connId)
|
||||
{
|
||||
medsBlpCb.storedMeasurement = FALSE;
|
||||
|
||||
BlpsMeasComplete((dmConnId_t)connId, MEDS_BLP_BPS_BPM_CCC_IDX);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsBlpProcMsg(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
switch(pMsg->event)
|
||||
{
|
||||
case MEDS_TIMER_IND:
|
||||
BlpsProcMsg(pMsg);
|
||||
break;
|
||||
|
||||
case ATTS_CCC_STATE_IND:
|
||||
/* Check if stored measurement exists and indications on measurements enabled */
|
||||
if (medsBlpCb.storedMeasurement &&
|
||||
AttsCccEnabled((dmConnId_t)pMsg->param, MEDS_BLP_BPS_BPM_CCC_IDX))
|
||||
{
|
||||
medsBlpSendStoredMeasurement((dmConnId_t)pMsg->param);
|
||||
}
|
||||
break;
|
||||
|
||||
case DM_CONN_CLOSE_IND:
|
||||
medsBlpClose(pMsg);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Button press callback.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsBlpBtn(dmConnId_t connId, uint8_t btn)
|
||||
{
|
||||
/* button actions when connected */
|
||||
if (connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
case APP_UI_BTN_1_SHORT:
|
||||
/* start or complete measurement */
|
||||
if (!medsBlpCb.measuring)
|
||||
{
|
||||
BlpsMeasStart(connId, MEDS_TIMER_IND, MEDS_BLP_BPS_ICP_CCC_IDX);
|
||||
medsBlpCb.measuring = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
BlpsMeasComplete(connId, MEDS_BLP_BPS_BPM_CCC_IDX);
|
||||
medsBlpCb.measuring = FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else /* if not connected */
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
case APP_UI_BTN_1_EX_LONG:
|
||||
/* Toggle flags in service for testing purposes */
|
||||
SvcBpsToggleFeatureFlags(CH_BPF_FLAG_MULTI_BOND);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_SHORT:
|
||||
/* Toggle flags in measurements for testing purposes */
|
||||
if (!(medsBpmFlags & CH_BPM_FLAG_USER_ID))
|
||||
{
|
||||
medsBpmFlags |= CH_BPM_FLAG_USER_ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
medsBpmFlags &= ~CH_BPM_FLAG_USER_ID;
|
||||
}
|
||||
|
||||
BlpsSetBpmFlags(medsBpmFlags);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_MED:
|
||||
/* Toggle flags in intermediate measurements for testing purposes */
|
||||
if (!(medsIcpFlags & CH_BPM_FLAG_USER_ID))
|
||||
{
|
||||
medsIcpFlags |= CH_BPM_FLAG_USER_ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
medsIcpFlags &= ~CH_BPM_FLAG_USER_ID;
|
||||
}
|
||||
|
||||
BlpsSetIcpFlags(medsIcpFlags);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_LONG:
|
||||
/* Toggle flags in intermediate measurements for testing purposes */
|
||||
if (!(medsIcpFlags & CH_BPM_FLAG_TIMESTAMP))
|
||||
{
|
||||
medsIcpFlags |= CH_BPM_FLAG_TIMESTAMP;
|
||||
}
|
||||
else
|
||||
{
|
||||
medsIcpFlags &= ~CH_BPM_FLAG_TIMESTAMP;
|
||||
}
|
||||
|
||||
BlpsSetIcpFlags(medsIcpFlags);
|
||||
break;
|
||||
|
||||
|
||||
case APP_UI_BTN_2_EX_LONG:
|
||||
if (connId == DM_CONN_ID_NONE)
|
||||
{
|
||||
/* Store a measurement to be sent when connected */
|
||||
medsBlpCb.storedMeasurement = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+166
@@ -0,0 +1,166 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Medical sensor sample, glucose profile
|
||||
*
|
||||
* Copyright (c) 2016-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "util/bstream.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "hci_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_ui.h"
|
||||
#include "app_hw.h"
|
||||
#include "svc_ch.h"
|
||||
#include "svc_gls.h"
|
||||
#include "svc_dis.h"
|
||||
#include "svc_core.h"
|
||||
#include "gatt/gatt_api.h"
|
||||
#include "glps/glps_api.h"
|
||||
#include "meds/meds_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! enumeration of client characteristic configuration descriptors */
|
||||
enum
|
||||
{
|
||||
MEDS_GLS_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
|
||||
MEDS_GLS_GLS_GLM_CCC_IDX, /*! Glucose measurement characteristic */
|
||||
MEDS_GLS_GLS_GLMC_CCC_IDX, /*! Glucose measurement context characteristic */
|
||||
MEDS_GLS_GLS_RACP_CCC_IDX, /*! Glucose record access control point characteristic */
|
||||
MEDS_GLS_NUM_CCC_IDX
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Advertising Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Service UUID list */
|
||||
static const uint8_t medsSvcUuidList[] =
|
||||
{
|
||||
UINT16_TO_BYTES(ATT_UUID_GLUCOSE_SERVICE),
|
||||
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE)
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Client Characteristic Configuration Descriptors
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
|
||||
static const attsCccSet_t medsGlpCccSet[MEDS_GLS_NUM_CCC_IDX] =
|
||||
{
|
||||
/* cccd handle value range security level */
|
||||
{GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE}, /* MEDS_GLS_GATT_SC_CCC_IDX */
|
||||
{GLS_GLM_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* MEDS_GLS_GLS_GLM_CCC_IDX */
|
||||
{GLS_GLMC_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* MEDS_GLS_GLS_GLMC_CCC_IDX */
|
||||
{GLS_RACP_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE} /* MEDS_GLS_GLS_RACP_CCC_IDX */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
static void medsGlpStart(void);
|
||||
static void medsGlpProcMsg(wsfMsgHdr_t *pMsg);
|
||||
static void medsGlpBtn(dmConnId_t connId, uint8_t btn);
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! profile interface pointer */
|
||||
medsIf_t medsGlpIf =
|
||||
{
|
||||
NULL,
|
||||
medsGlpStart,
|
||||
medsGlpProcMsg,
|
||||
medsGlpBtn
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsGlpStart(void)
|
||||
{
|
||||
/* set up CCCD table and callback */
|
||||
AttsCccRegister(MEDS_GLS_NUM_CCC_IDX, (attsCccSet_t *) medsGlpCccSet, medsCccCback);
|
||||
|
||||
/* add glucose service */
|
||||
SvcGlsAddGroup();
|
||||
SvcGlsCbackRegister(NULL, GlpsRacpWriteCback);
|
||||
|
||||
/* Set Service Changed CCCD index. */
|
||||
GattSetSvcChangedIdx(MEDS_GLS_GATT_SC_CCC_IDX);
|
||||
|
||||
/* initialize glucose profile sensor */
|
||||
GlpsInit();
|
||||
GlpsSetCccIdx(MEDS_GLS_GLS_GLM_CCC_IDX, MEDS_GLS_GLS_GLMC_CCC_IDX, MEDS_GLS_GLS_RACP_CCC_IDX);
|
||||
|
||||
/* TODO: Define glucose features */
|
||||
GlpsSetFeature(GLP_ALL_SUPPORTED_FEATURES);
|
||||
|
||||
/* set advertising data */
|
||||
AppAdvSetAdValue(APP_ADV_DATA_DISCOVERABLE, DM_ADV_TYPE_16_UUID, sizeof(medsSvcUuidList),
|
||||
(uint8_t *) medsSvcUuidList);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsGlpProcMsg(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
GlpsProcMsg(pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Button press callback.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsGlpBtn(dmConnId_t connId, uint8_t btn)
|
||||
{
|
||||
GlpsBtn(connId, btn);
|
||||
}
|
||||
Vendored
+293
@@ -0,0 +1,293 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Medical sensor sample, health thermometer profile
|
||||
*
|
||||
* Copyright (c) 2012-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "util/bstream.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "hci_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_hw.h"
|
||||
#include "app_ui.h"
|
||||
#include "svc_ch.h"
|
||||
#include "svc_hts.h"
|
||||
#include "svc_dis.h"
|
||||
#include "svc_core.h"
|
||||
#include "gatt/gatt_api.h"
|
||||
#include "htps/htps_api.h"
|
||||
#include "meds/meds_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! enumeration of client characteristic configuration descriptors */
|
||||
enum
|
||||
{
|
||||
MEDS_HTP_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
|
||||
MEDS_HTP_HTS_TM_CCC_IDX, /*! Health thermometer service, temperature measurement characteristic */
|
||||
MEDS_HTP_HTS_IT_CCC_IDX, /*! Health thermometer service, intermediate temperature characteristic */
|
||||
MEDS_HTP_NUM_CCC_IDX
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Configurable Parameters
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! health thermometer measurement configuration */
|
||||
static const htpsCfg_t medsHtpsCfg =
|
||||
{
|
||||
2000 /*! Measurement timer expiration period in ms */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Advertising Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Service UUID list */
|
||||
static const uint8_t medsSvcUuidList[] =
|
||||
{
|
||||
UINT16_TO_BYTES(ATT_UUID_HEALTH_THERM_SERVICE),
|
||||
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE)
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Client Characteristic Configuration Descriptors
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
|
||||
static const attsCccSet_t medsHtpCccSet[MEDS_HTP_NUM_CCC_IDX] =
|
||||
{
|
||||
/* cccd handle value range security level */
|
||||
{GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_ENC}, /* MEDS_HTP_GATT_SC_CCC_IDX */
|
||||
{HTS_TM_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE}, /* MEDS_HTP_HTS_TM_CCC_IDX */
|
||||
{HTS_IT_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_ENC} /* MEDS_HTP_HTS_IT_CCC_IDX */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
static void medsHtpStart(void);
|
||||
static void medsHtpProcMsg(wsfMsgHdr_t *pMsg);
|
||||
static void medsHtpBtn(dmConnId_t connId, uint8_t btn);
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! profile interface pointer */
|
||||
medsIf_t medsHtpIf =
|
||||
{
|
||||
NULL,
|
||||
medsHtpStart,
|
||||
medsHtpProcMsg,
|
||||
medsHtpBtn
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! application control block */
|
||||
static struct
|
||||
{
|
||||
bool_t measuring;
|
||||
bool_t storedMeasurement;
|
||||
} medsHtpCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set temperature measurement units.
|
||||
*
|
||||
* \param units CH_TM_FLAG_UNITS_C or CH_TM_FLAG_UNITS_F.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsHtpSetUnits(uint8_t units)
|
||||
{
|
||||
HtpsSetTmFlags(units | CH_TM_FLAG_TIMESTAMP);
|
||||
HtpsSetItFlags(units);
|
||||
AppHwTmSetUnits(units);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform UI actions on connection close.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsHtpClose(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* stop health thermometer measurement */
|
||||
HtpsMeasStop();
|
||||
medsHtpCb.measuring = FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsHtpStart(void)
|
||||
{
|
||||
/* set up CCCD table and callback */
|
||||
AttsCccRegister(MEDS_HTP_NUM_CCC_IDX, (attsCccSet_t *) medsHtpCccSet, medsCccCback);
|
||||
|
||||
/* add health thermometer service */
|
||||
SvcHtsAddGroup();
|
||||
|
||||
/* Set Service Changed CCCD index. */
|
||||
GattSetSvcChangedIdx(MEDS_HTP_GATT_SC_CCC_IDX);
|
||||
|
||||
/* initialize health thermometer profile sensor */
|
||||
HtpsInit(medsCb.handlerId, (htpsCfg_t *) &medsHtpsCfg);
|
||||
medsHtpSetUnits(CH_TM_FLAG_UNITS_C);
|
||||
|
||||
/* set advertising data */
|
||||
AppAdvSetAdValue(APP_ADV_DATA_DISCOVERABLE, DM_ADV_TYPE_16_UUID, sizeof(medsSvcUuidList),
|
||||
(uint8_t *) medsSvcUuidList);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Send stored temperature measurement.
|
||||
*
|
||||
* \param connId connection ID to send stored measurement to.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsHtpSendStoredMeasurement(dmConnId_t connId)
|
||||
{
|
||||
medsHtpCb.storedMeasurement = FALSE;
|
||||
|
||||
HtpsMeasComplete((dmConnId_t)connId, MEDS_HTP_HTS_TM_CCC_IDX);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsHtpProcMsg(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
switch(pMsg->event)
|
||||
{
|
||||
case MEDS_TIMER_IND:
|
||||
HtpsProcMsg(pMsg);
|
||||
break;
|
||||
|
||||
case ATTS_CCC_STATE_IND:
|
||||
/* Check if stored measurement exists and indications on temperature measurement enabled */
|
||||
if (medsHtpCb.storedMeasurement && AttsCccEnabled((dmConnId_t)pMsg->param, MEDS_HTP_HTS_TM_CCC_IDX))
|
||||
{
|
||||
medsHtpSendStoredMeasurement((dmConnId_t)pMsg->param);
|
||||
}
|
||||
break;
|
||||
|
||||
case DM_CONN_CLOSE_IND:
|
||||
medsHtpClose(pMsg);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Button press callback.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsHtpBtn(dmConnId_t connId, uint8_t btn)
|
||||
{
|
||||
static bool_t advNonconn = FALSE;
|
||||
|
||||
switch (btn)
|
||||
{
|
||||
case APP_UI_BTN_1_SHORT:
|
||||
/* if connected */
|
||||
if (connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
/* start or complete measurement */
|
||||
if (!medsHtpCb.measuring)
|
||||
{
|
||||
HtpsMeasStart(connId, MEDS_TIMER_IND, MEDS_HTP_HTS_IT_CCC_IDX);
|
||||
medsHtpCb.measuring = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
HtpsMeasComplete(connId, MEDS_HTP_HTS_TM_CCC_IDX);
|
||||
medsHtpCb.measuring = FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_SHORT:
|
||||
/* set units */
|
||||
medsHtpSetUnits(CH_TM_FLAG_UNITS_F);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_MED:
|
||||
/* set units */
|
||||
medsHtpSetUnits(CH_TM_FLAG_UNITS_C);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_LONG:
|
||||
/* set new advertising type */
|
||||
advNonconn = !advNonconn;
|
||||
AppSetAdvType(advNonconn ? DM_ADV_NONCONN_UNDIRECT : DM_ADV_CONN_UNDIRECT);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_EX_LONG:
|
||||
if (connId == DM_CONN_ID_NONE)
|
||||
{
|
||||
/* Store a temperature measurement to be sent when connected */
|
||||
medsHtpCb.storedMeasurement = TRUE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Vendored
+542
@@ -0,0 +1,542 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Medical sensor sample application for the following profiles:
|
||||
* Blood Pressure profile sensor
|
||||
* Weight Scale profile sensor
|
||||
* Health Thermometer profile sensor
|
||||
*
|
||||
* Copyright (c) 2012-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "util/bstream.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "hci_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_db.h"
|
||||
#include "app_ui.h"
|
||||
#include "app_hw.h"
|
||||
#include "app_main.h"
|
||||
#include "svc_ch.h"
|
||||
#include "svc_core.h"
|
||||
#include "svc_dis.h"
|
||||
#include "gatt/gatt_api.h"
|
||||
#include "meds/meds_api.h"
|
||||
#include "meds/meds_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Profile Configuration
|
||||
**************************************************************************************************/
|
||||
|
||||
/* Blood pressure profile included */
|
||||
#ifndef MEDS_BLP_INCLUDED
|
||||
#define MEDS_BLP_INCLUDED TRUE
|
||||
#endif
|
||||
|
||||
/* Weight scale profile included */
|
||||
#ifndef MEDS_WSP_INCLUDED
|
||||
#define MEDS_WSP_INCLUDED TRUE
|
||||
#endif
|
||||
|
||||
/* Health thermometer profile included */
|
||||
#ifndef MEDS_HTP_INCLUDED
|
||||
#define MEDS_HTP_INCLUDED TRUE
|
||||
#endif
|
||||
|
||||
/* Pulse Oximeter profile included */
|
||||
#ifndef MEDS_PLX_INCLUDED
|
||||
#define MEDS_PLX_INCLUDED TRUE
|
||||
#endif
|
||||
|
||||
/* Glucose profile included */
|
||||
#ifndef MEDS_GLP_INCLUDED
|
||||
#define MEDS_GLP_INCLUDED TRUE
|
||||
#endif
|
||||
|
||||
/* Default profile to use */
|
||||
#ifndef MEDS_PROFILE
|
||||
#define MEDS_PROFILE MEDS_ID_BLP
|
||||
#endif
|
||||
|
||||
|
||||
/**************************************************************************************************
|
||||
Configurable Parameters
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! configurable parameters for advertising */
|
||||
static const appAdvCfg_t medsAdvCfg =
|
||||
{
|
||||
{60000, 30000, 0}, /*! Advertising durations in ms */
|
||||
{ 48, 1600, 0} /*! Advertising intervals in 0.625 ms units */
|
||||
};
|
||||
|
||||
/*! configurable parameters for slave */
|
||||
static const appSlaveCfg_t medsSlaveCfg =
|
||||
{
|
||||
1, /*! Maximum connections */
|
||||
};
|
||||
|
||||
/*! configurable parameters for security */
|
||||
static const appSecCfg_t medsSecCfg =
|
||||
{
|
||||
DM_AUTH_BOND_FLAG, /*! Authentication and bonding flags */
|
||||
0, /*! Initiator key distribution flags */
|
||||
DM_KEY_DIST_LTK, /*! Responder key distribution flags */
|
||||
FALSE, /*! TRUE if Out-of-band pairing data is present */
|
||||
TRUE /*! TRUE to initiate security upon connection */
|
||||
};
|
||||
|
||||
/*! configurable parameters for connection parameter update */
|
||||
static const appUpdateCfg_t medsUpdateCfg =
|
||||
{
|
||||
0, /*! 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 */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Advertising Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! advertising data flags */
|
||||
static const uint8_t medsAdvDataFlags[] =
|
||||
{
|
||||
DM_FLAG_LE_LIMITED_DISC |
|
||||
DM_FLAG_LE_BREDR_NOT_SUP
|
||||
};
|
||||
|
||||
/*! advertising data buffer (value is set in medsSetup) */
|
||||
static uint8_t medsAdvDataDisc[HCI_ADV_DATA_LEN];
|
||||
|
||||
/*! scan data, discoverable mode */
|
||||
static const uint8_t medsScanDataDisc[] =
|
||||
{
|
||||
/*! device name */
|
||||
11, /*! length */
|
||||
DM_ADV_TYPE_LOCAL_NAME, /*! AD type */
|
||||
'M',
|
||||
'e',
|
||||
'd',
|
||||
' ',
|
||||
'S',
|
||||
'e',
|
||||
'n',
|
||||
's',
|
||||
'o',
|
||||
'r'
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! application control block */
|
||||
medsCb_t medsCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application DM callback.
|
||||
*
|
||||
* \param pDmEvt DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsDmCback(dmEvt_t *pDmEvt)
|
||||
{
|
||||
dmEvt_t *pMsg;
|
||||
uint16_t len;
|
||||
|
||||
len = DmSizeOfEvt(pDmEvt);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(len)) != NULL)
|
||||
{
|
||||
memcpy(pMsg, pDmEvt, len);
|
||||
WsfMsgSend(medsCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATT callback.
|
||||
*
|
||||
* \param pEvt ATT callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsAttCback(attEvt_t *pEvt)
|
||||
{
|
||||
medsCb.pIf->procMsg((wsfMsgHdr_t*) pEvt);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATTS client characteristic configuration callback.
|
||||
*
|
||||
* \param pDmEvt DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void medsCccCback(attsCccEvt_t *pEvt)
|
||||
{
|
||||
attsCccEvt_t *pMsg;
|
||||
appDbHdl_t dbHdl;
|
||||
|
||||
/* If CCC not set from initialization and there's a device record and currently bonded */
|
||||
if ((pEvt->handle != ATT_HANDLE_NONE) &&
|
||||
((dbHdl = AppDbGetHdl((dmConnId_t) pEvt->hdr.param)) != APP_DB_HDL_NONE) &&
|
||||
AppCheckBonded((dmConnId_t)pEvt->hdr.param))
|
||||
{
|
||||
/* 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(medsCb.handlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set up advertising and other procedures that need to be performed after
|
||||
* device reset.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsSetup(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* start advertising; automatically set connectable/discoverable mode and bondable mode */
|
||||
AppAdvStart(APP_MODE_AUTO_INIT);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Button press callback.
|
||||
*
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsBtnCback(uint8_t btn)
|
||||
{
|
||||
dmConnId_t connId;
|
||||
|
||||
/* button actions when connected */
|
||||
if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE)
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
case APP_UI_BTN_1_LONG:
|
||||
AppConnClose(connId);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* all other button presses-- send to profile */
|
||||
medsCb.pIf->btn(connId, btn);
|
||||
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 all bonding info */
|
||||
AppSlaveClearAllBondingInfo();
|
||||
|
||||
/* restart advertising */
|
||||
AppAdvStart(APP_MODE_AUTO_INIT);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* all other button presses-- send to profile */
|
||||
medsCb.pIf->btn(connId, btn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsProcMsg(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
uint8_t uiEvent = APP_UI_NONE;
|
||||
|
||||
switch(pMsg->event)
|
||||
{
|
||||
case MEDS_TIMER_IND:
|
||||
break;
|
||||
|
||||
case ATT_MTU_UPDATE_IND:
|
||||
APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu);
|
||||
break;
|
||||
|
||||
case DM_RESET_CMPL_IND:
|
||||
AttsCalculateDbHash();
|
||||
medsSetup(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;
|
||||
break;
|
||||
|
||||
case DM_CONN_OPEN_IND:
|
||||
uiEvent = APP_UI_CONN_OPEN;
|
||||
break;
|
||||
|
||||
case DM_CONN_CLOSE_IND:
|
||||
uiEvent = APP_UI_CONN_CLOSE;
|
||||
break;
|
||||
|
||||
case DM_SEC_PAIR_CMPL_IND:
|
||||
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(&((dmEvt_t *)pMsg)->authReq);
|
||||
break;
|
||||
|
||||
case DM_PRIV_CLEAR_RES_LIST_IND:
|
||||
APP_TRACE_INFO1("Clear resolving list status 0x%02x", pMsg->status);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
medsCb.pIf->procMsg(pMsg);
|
||||
|
||||
if (uiEvent != APP_UI_NONE)
|
||||
{
|
||||
AppUiAction(uiEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void MedsHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
APP_TRACE_INFO0("MedsHandlerInit");
|
||||
|
||||
/* store handler ID */
|
||||
medsCb.handlerId = handlerId;
|
||||
|
||||
/* Set configuration pointers */
|
||||
pAppSlaveCfg = (appSlaveCfg_t *) &medsSlaveCfg;
|
||||
pAppAdvCfg = (appAdvCfg_t *) &medsAdvCfg;
|
||||
pAppSecCfg = (appSecCfg_t *) &medsSecCfg;
|
||||
pAppUpdateCfg = (appUpdateCfg_t *) &medsUpdateCfg;
|
||||
|
||||
/* Initialize application framework */
|
||||
AppSlaveInit();
|
||||
AppServerInit();
|
||||
|
||||
/* Set default profile to use */
|
||||
MedsSetProfile(MEDS_PROFILE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void MedsHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
APP_TRACE_INFO1("Meds got evt %d", pMsg->event);
|
||||
|
||||
/* process ATT messages */
|
||||
if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
|
||||
{
|
||||
/* process server-related ATT messages */
|
||||
AppServerProcAttMsg(pMsg);
|
||||
}
|
||||
/* process DM messages */
|
||||
else if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
|
||||
{
|
||||
/* process advertising and connection-related messages */
|
||||
AppSlaveProcDmMsg((dmEvt_t *) pMsg);
|
||||
|
||||
/* process security-related messages */
|
||||
AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/* perform profile and user interface-related operations */
|
||||
medsProcMsg(pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void MedsStart(void)
|
||||
{
|
||||
/* Register for stack callbacks */
|
||||
DmRegister(medsDmCback);
|
||||
DmConnRegister(DM_CLIENT_ID_APP, medsDmCback);
|
||||
AttRegister(medsAttCback);
|
||||
AttConnRegister(AppServerConnCback);
|
||||
|
||||
/* Register for app framework callbacks */
|
||||
AppUiBtnRegister(medsBtnCback);
|
||||
|
||||
/* Initialize attribute server database */
|
||||
SvcCoreGattCbackRegister(GattReadCback, GattWriteCback);
|
||||
SvcCoreAddGroup();
|
||||
SvcDisAddGroup();
|
||||
|
||||
/* set advertising and scan response data for discoverable mode */
|
||||
AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, 0, (uint8_t *) medsAdvDataDisc);
|
||||
AppAdvSetAdValue(APP_ADV_DATA_DISCOVERABLE, DM_ADV_TYPE_FLAGS, sizeof(medsAdvDataFlags),
|
||||
(uint8_t *) medsAdvDataFlags);
|
||||
AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(medsScanDataDisc), (uint8_t *) medsScanDataDisc);
|
||||
|
||||
/* set advertising and scan response data for connectable mode */
|
||||
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, 0, NULL);
|
||||
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, 0, NULL);
|
||||
|
||||
/* call profile start function */
|
||||
medsCb.pIf->start();
|
||||
|
||||
/* Reset the device */
|
||||
DmDevReset();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the profile to be used by the application. This function is called internally
|
||||
* by MedsHandlerInit() with a default value. It may also be called by the system
|
||||
* to configure the profile after executing MedsHandlerInit() and before executing
|
||||
* MedsStart().
|
||||
*
|
||||
* \param profile Profile identifier.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void MedsSetProfile(uint8_t profile)
|
||||
{
|
||||
switch (profile)
|
||||
{
|
||||
#if MEDS_BLP_INCLUDED == TRUE
|
||||
case MEDS_ID_BLP:
|
||||
medsCb.pIf = &medsBlpIf;
|
||||
break;
|
||||
#endif
|
||||
#if MEDS_WSP_INCLUDED == TRUE
|
||||
case MEDS_ID_WSP:
|
||||
medsCb.pIf = &medsWspIf;
|
||||
break;
|
||||
#endif
|
||||
#if MEDS_HTP_INCLUDED == TRUE
|
||||
case MEDS_ID_HTP:
|
||||
medsCb.pIf = &medsHtpIf;
|
||||
break;
|
||||
#endif
|
||||
#if MEDS_PLX_INCLUDED == TRUE
|
||||
case MEDS_ID_PLX:
|
||||
medsCb.pIf = &medsPlxIf;
|
||||
break;
|
||||
#endif
|
||||
#if MEDS_GLP_INCLUDED == TRUE
|
||||
case MEDS_ID_GLP:
|
||||
medsCb.pIf = &medsGlpIf;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
APP_TRACE_WARN1("MedsSetProfile invalid profile:%d", profile);
|
||||
medsCb.pIf = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((medsCb.pIf != NULL) && (medsCb.pIf->init != NULL))
|
||||
{
|
||||
medsCb.pIf->init();
|
||||
}
|
||||
}
|
||||
Vendored
+96
@@ -0,0 +1,96 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Health/medical sensor sample application interface file.
|
||||
*
|
||||
* Copyright (c) 2012-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#ifndef MEDS_MAIN_H
|
||||
#define MEDS_MAIN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! WSF message event starting value */
|
||||
#define MEDS_MSG_START 0xA0
|
||||
|
||||
/*! WSF message event enumeration */
|
||||
enum
|
||||
{
|
||||
MEDS_TIMER_IND = MEDS_MSG_START, /*! Timer expired */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Data Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! profile interface callback functions */
|
||||
typedef void (*medsInitCback_t)(void);
|
||||
typedef void (*medsStartCback_t)(void);
|
||||
typedef void (*medsProcMsgCback_t)(wsfMsgHdr_t *pMsg);
|
||||
typedef void (*medsBtnCback_t)(dmConnId_t connId, uint8_t btn);
|
||||
|
||||
/*! profile interface structure */
|
||||
typedef struct
|
||||
{
|
||||
medsInitCback_t init;
|
||||
medsStartCback_t start;
|
||||
medsProcMsgCback_t procMsg;
|
||||
medsBtnCback_t btn;
|
||||
} medsIf_t;
|
||||
|
||||
/*! application control block */
|
||||
typedef struct
|
||||
{
|
||||
medsIf_t *pIf; /*! Profile interface */
|
||||
wsfHandlerId_t handlerId; /*! WSF hander ID */
|
||||
} medsCb_t;
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! application control block */
|
||||
extern medsCb_t medsCb;
|
||||
|
||||
/*! profile interface pointers */
|
||||
extern medsIf_t medsBlpIf; /* blood pressure profile */
|
||||
extern medsIf_t medsWspIf; /* weight scale profile */
|
||||
extern medsIf_t medsHtpIf; /* health thermometer profile */
|
||||
extern medsIf_t medsPlxIf; /* pulse oximeter profile */
|
||||
extern medsIf_t medsGlpIf; /* glucose profile */
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
void medsCccCback(attsCccEvt_t *pEvt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* MEDS_MAIN_H */
|
||||
|
||||
Vendored
+263
@@ -0,0 +1,263 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Medical sensor sample, pulse oximeter profile
|
||||
*
|
||||
* Copyright (c) 2016-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "util/bstream.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "hci_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_ui.h"
|
||||
#include "app_hw.h"
|
||||
#include "svc_ch.h"
|
||||
#include "svc_plxs.h"
|
||||
#include "svc_dis.h"
|
||||
#include "svc_core.h"
|
||||
#include "gatt/gatt_api.h"
|
||||
#include "plxps/plxps_api.h"
|
||||
#include "plxps/plxps_main.h"
|
||||
#include "meds/meds_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! enumeration of client characteristic configuration descriptors */
|
||||
enum
|
||||
{
|
||||
MEDS_PLX_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
|
||||
MEDS_PLX_PLX_SCM_CCC_IDX, /*! Pulse Oximeter Spot Check measurement characteristic */
|
||||
MEDS_PLX_PLX_CM_CCC_IDX, /*! Pulse Oximeter Continuous characteristic */
|
||||
MEDS_PLX_PLX_RACP_CCC_IDX, /*! Pulse Oximeter record access control point characteristic */
|
||||
MEDS_PLX_NUM_CCC_IDX
|
||||
};
|
||||
|
||||
/* Default MTU */
|
||||
#define MEDS_PLX_DEFAULT_MTU 50
|
||||
|
||||
/**************************************************************************************************
|
||||
Configurable Parameters
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Pulse Oximeter Continuous measurement configuration */
|
||||
static const plxpsCfg_t medsPlxpsCfg =
|
||||
{
|
||||
2000 /*! Measurement timer expiration period in ms */
|
||||
};
|
||||
|
||||
/*! ATT configurable parameters (increase MTU) */
|
||||
static const attCfg_t medsPlxAttCfg =
|
||||
{
|
||||
15, /* ATT server service discovery connection idle timeout in seconds */
|
||||
MEDS_PLX_DEFAULT_MTU, /* desired ATT MTU */
|
||||
ATT_MAX_TRANS_TIMEOUT, /* transcation timeout in seconds */
|
||||
4 /* number of queued prepare writes supported by server */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Advertising Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Service UUID list */
|
||||
static const uint8_t medsSvcUuidList[] =
|
||||
{
|
||||
UINT16_TO_BYTES(ATT_UUID_PULSE_OXIMITER_SERVICE),
|
||||
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE)
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Client Characteristic Configuration Descriptors
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
|
||||
static const attsCccSet_t medsPlxCccSet[MEDS_PLX_NUM_CCC_IDX] =
|
||||
{
|
||||
/* cccd handle value range security level */
|
||||
{GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE}, /* MEDS_PLX_GATT_SC_CCC_IDX */
|
||||
{PLXS_SPOT_CHECK_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE}, /* MEDS_PLX_PLX_SCM_CCC_IDX */
|
||||
{PLXS_CONTINUOUS_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* MEDS_PLX_PLX_CM_CCC_IDX */
|
||||
{PLXS_RECORD_ACCESS_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE} /* MEDS_PLX_PLX_RACP_CCC_IDX */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
static void medsPlxInit(void);
|
||||
static void medsPlxStart(void);
|
||||
static void medsPlxProcMsg(wsfMsgHdr_t *pMsg);
|
||||
static void medsPlxBtn(dmConnId_t connId, uint8_t btn);
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! profile interface pointer */
|
||||
medsIf_t medsPlxIf =
|
||||
{
|
||||
medsPlxInit,
|
||||
medsPlxStart,
|
||||
medsPlxProcMsg,
|
||||
medsPlxBtn
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! application control block */
|
||||
static struct
|
||||
{
|
||||
bool_t measuring;
|
||||
} medsPlxCb;
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Perform actions on connection close.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsPlxClose(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* Reset control information */
|
||||
medsPlxCb.measuring = FALSE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsPlxStart(void)
|
||||
{
|
||||
/* set up CCCD table and callback */
|
||||
AttsCccRegister(MEDS_PLX_NUM_CCC_IDX, (attsCccSet_t *) medsPlxCccSet, medsCccCback);
|
||||
|
||||
/* add pulse oximeter service */
|
||||
SvcPlxsAddGroup();
|
||||
SvcPlxsCbackRegister(NULL, PlxpsWriteCback);
|
||||
|
||||
/* Set Service Changed CCCD index. */
|
||||
GattSetSvcChangedIdx(MEDS_PLX_GATT_SC_CCC_IDX);
|
||||
|
||||
/* initialize pulse oximeter profile sensor */
|
||||
PlxpsInit(medsCb.handlerId, (plxpsCfg_t *) &medsPlxpsCfg);
|
||||
PlxpsSetCccIdx(MEDS_PLX_PLX_SCM_CCC_IDX, MEDS_PLX_PLX_CM_CCC_IDX, MEDS_PLX_PLX_RACP_CCC_IDX);
|
||||
|
||||
/* TODO: Define pulse oximeter features */
|
||||
PlxpsSetFeature(CH_PLF_FLAG_SENSOR_STATUS_SUP, 0, 0);
|
||||
|
||||
/* set advertising data */
|
||||
AppAdvSetAdValue(APP_ADV_DATA_DISCOVERABLE, DM_ADV_TYPE_16_UUID, sizeof(medsSvcUuidList),
|
||||
(uint8_t *) medsSvcUuidList);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsPlxProcMsg(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
if (pMsg->event == DM_CONN_CLOSE_IND)
|
||||
{
|
||||
medsPlxClose(pMsg);
|
||||
}
|
||||
|
||||
PlxpsProcMsg(pMsg);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Profile initialization function.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsPlxInit(void)
|
||||
{
|
||||
/* Set configuration pointers */
|
||||
pAttCfg = (attCfg_t *) &medsPlxAttCfg;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Button press callback.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsPlxBtn(dmConnId_t connId, uint8_t btn)
|
||||
{
|
||||
appPlxCm_t record;
|
||||
|
||||
/* button actions when connected */
|
||||
if (connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
case APP_UI_BTN_2_SHORT:
|
||||
AppHwPlxcmRead(&record);
|
||||
record.flags = CH_PLXC_FLAG_PULSE_AMP_INDX;
|
||||
plxpsSendContinuousMeas(connId, &record);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_2_MED:
|
||||
plxpsDbDeleteRecords(CH_RACP_OPERATOR_ALL);
|
||||
break;
|
||||
|
||||
case APP_UI_BTN_1_SHORT:
|
||||
/* start or complete measurement */
|
||||
if (!medsPlxCb.measuring)
|
||||
{
|
||||
PlxpsMeasStart(connId, MEDS_TIMER_IND, MEDS_PLX_PLX_CM_CCC_IDX);
|
||||
medsPlxCb.measuring = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
PlxpsMeasStop();
|
||||
medsPlxCb.measuring = FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+165
@@ -0,0 +1,165 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Medical sensor sample, weight scale profile
|
||||
*
|
||||
* Copyright (c) 2012-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
#include "wsf_types.h"
|
||||
#include "util/bstream.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "hci_api.h"
|
||||
#include "dm_api.h"
|
||||
#include "att_api.h"
|
||||
#include "app_api.h"
|
||||
#include "app_ui.h"
|
||||
#include "svc_ch.h"
|
||||
#include "svc_wss.h"
|
||||
#include "svc_dis.h"
|
||||
#include "svc_core.h"
|
||||
#include "gatt/gatt_api.h"
|
||||
#include "wsps/wsps_api.h"
|
||||
#include "meds/meds_main.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! enumeration of client characteristic configuration descriptors */
|
||||
enum
|
||||
{
|
||||
MEDS_WSP_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
|
||||
MEDS_WSP_WSS_WSM_CCC_IDX, /*! Weight scale service, weight scale measurement characteristic */
|
||||
MEDS_WSP_NUM_CCC_IDX
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Advertising Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! Service UUID list */
|
||||
static const uint8_t medsSvcUuidList[] =
|
||||
{
|
||||
UINT16_TO_BYTES(ATT_UUID_WEIGHT_SCALE_SERVICE),
|
||||
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE)
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Client Characteristic Configuration Descriptors
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
|
||||
static const attsCccSet_t medsWspCccSet[MEDS_WSP_NUM_CCC_IDX] =
|
||||
{
|
||||
/* cccd handle value range security level */
|
||||
{GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_ENC}, /* MEDS_WSP_GATT_SC_CCC_IDX */
|
||||
{WSS_WM_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_ENC} /* MEDS_WSP_WSS_WSM_CCC_IDX */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Local Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
static void medsWspStart(void);
|
||||
static void medsWspProcMsg(wsfMsgHdr_t *pMsg);
|
||||
static void medsWspBtn(dmConnId_t connId, uint8_t btn);
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! profile interface pointer */
|
||||
medsIf_t medsWspIf =
|
||||
{
|
||||
NULL,
|
||||
medsWspStart,
|
||||
medsWspProcMsg,
|
||||
medsWspBtn
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsWspStart(void)
|
||||
{
|
||||
/* set up CCCD table and callback */
|
||||
AttsCccRegister(MEDS_WSP_NUM_CCC_IDX, (attsCccSet_t *) medsWspCccSet, medsCccCback);
|
||||
|
||||
/* add weight scale service */
|
||||
SvcWssAddGroup();
|
||||
|
||||
/* Set Service Changed CCCD index. */
|
||||
GattSetSvcChangedIdx(MEDS_WSP_GATT_SC_CCC_IDX);
|
||||
|
||||
/* initialize weight scale profile sensor */
|
||||
WspsSetWsmFlags(CH_WSM_FLAG_UNITS_LBS | CH_WSM_FLAG_TIMESTAMP);
|
||||
|
||||
/* set advertising data */
|
||||
AppAdvSetAdValue(APP_ADV_DATA_DISCOVERABLE, DM_ADV_TYPE_16_UUID, sizeof(medsSvcUuidList),
|
||||
(uint8_t *) medsSvcUuidList);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsWspProcMsg(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Button press callback.
|
||||
*
|
||||
* \param connId Connection identifier.
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void medsWspBtn(dmConnId_t connId, uint8_t btn)
|
||||
{
|
||||
/* button actions when connected */
|
||||
if (connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
case APP_UI_BTN_1_SHORT:
|
||||
/* send measurement */
|
||||
WspsMeasComplete(connId, MEDS_WSP_WSS_WSM_CCC_IDX);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+121
@@ -0,0 +1,121 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Sensor sample application.
|
||||
*
|
||||
* Copyright (c) 2015-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#ifndef SENSOR_API_H
|
||||
#define SENSOR_API_H
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_os.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Callback Function Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief App extension callback.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
typedef void (*sensorExtCback_t)(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start sensor application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SensorStart(void);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SensorHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SensorHandlerInit(wsfHandlerId_t handlerId);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a callback to receive events for the purpose of extending the sensor app.
|
||||
*
|
||||
* \param extCback Callback function
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SensorRegisterExtensionCback(sensorExtCback_t extCback);
|
||||
|
||||
/**************************************************************************************************
|
||||
Application Functions
|
||||
**************************************************************************************************/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read gyroscope sensor.
|
||||
*
|
||||
* \param pX Storage for gyroscope x-axis reading.
|
||||
* \param pY Storage for gyroscope y-axis reading.
|
||||
* \param pZ Storage for gyroscope z-axis reading.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AppReadGyro(int16_t *pX, int16_t *pY, int16_t *pZ);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read temperature sensor.
|
||||
*
|
||||
* \param pTemp Storage for temperature reading.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AppReadTemp(int16_t *pTemp);
|
||||
|
||||
#endif /* SENSOR_API_H */
|
||||
Vendored
+646
@@ -0,0 +1,646 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Sensor sample application.
|
||||
*
|
||||
* Copyright (c) 2015-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "app_api.h"
|
||||
#include "app_db.h"
|
||||
#include "app_ui.h"
|
||||
#include "att_defs.h"
|
||||
#include "att_api.h"
|
||||
#include "util/bstream.h"
|
||||
#include "dm_api.h"
|
||||
#include "svc_core.h"
|
||||
#include "svc_dis.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "hci_handler.h"
|
||||
#include "dm_handler.h"
|
||||
#include "l2c_handler.h"
|
||||
#include "att_handler.h"
|
||||
#include "smp_handler.h"
|
||||
#include "smp_api.h"
|
||||
#include "l2c_api.h"
|
||||
#include "gatt/gatt_api.h"
|
||||
#include "sensor_api.h"
|
||||
#include "sensor/gyro_api.h"
|
||||
#include "svc_gyro.h"
|
||||
#include "sensor/temp_api.h"
|
||||
#include "svc_temp.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
#define SENSOR_MSG_START 0xA0
|
||||
|
||||
enum
|
||||
{
|
||||
SENSOR_GYRO_TIMER_IND = SENSOR_MSG_START,
|
||||
SENSOR_TEMP_TIMER_IND
|
||||
};
|
||||
|
||||
/*! Advertising timer timeout */
|
||||
#define SENSOR_ADV_TIMEOUT_SEC 30000
|
||||
|
||||
/*! MAX ACL receive length */
|
||||
#define SENSOR_MAX_RX_ACL_LEN 100
|
||||
|
||||
/***************************************************************************************************
|
||||
Global Variables
|
||||
***************************************************************************************************/
|
||||
|
||||
/* Task handler ID */
|
||||
static wsfHandlerId_t sensorHandlerId;
|
||||
|
||||
/* App extention callback */
|
||||
static sensorExtCback_t sensorExtCback;
|
||||
|
||||
/**************************************************************************************************
|
||||
Configurable Parameters
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! configurable parameters for slave */
|
||||
static const appAdvCfg_t sensorAdvCfg =
|
||||
{
|
||||
{SENSOR_ADV_TIMEOUT_SEC, 0, 0}, /*! Advertising durations in ms */
|
||||
{200, 0, 0}, /*! Advertising intervals in 0.625 ms units */
|
||||
};
|
||||
|
||||
/*! configurable parameters for slave */
|
||||
static appSlaveCfg_t sensorSlaveCfg =
|
||||
{
|
||||
1 /*! Maximum connections */
|
||||
};
|
||||
|
||||
/*! configurable parameters for security */
|
||||
static const appSecCfg_t sensorSecCfg =
|
||||
{
|
||||
DM_AUTH_BOND_FLAG, /*! Authentication and bonding flags */
|
||||
0, /*! Initiator key distribution flags */
|
||||
DM_KEY_DIST_LTK, /*! 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 sensorUpdateCfg =
|
||||
{
|
||||
0, /*! 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 */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Advertising Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/* advertising data */
|
||||
static uint8_t sensorAdvDataDisc[HCI_ADV_DATA_LEN]; /* value is set in sensorStart() */
|
||||
static uint8_t sensorAdvDataConn[HCI_ADV_DATA_LEN]; /* value is set in sensorStart() */
|
||||
|
||||
static const uint8_t sensorAdvDataFlags[] =
|
||||
{
|
||||
DM_FLAG_LE_LIMITED_DISC | DM_FLAG_LE_BREDR_NOT_SUP
|
||||
};
|
||||
|
||||
/* scan data */
|
||||
static uint8_t sensorScanDataDisc[HCI_ADV_DATA_LEN]; /* value is set in sensorStart() */
|
||||
static uint8_t sensorScanDataConn[HCI_ADV_DATA_LEN]; /* value is set in sensorStart() */
|
||||
static const char sensorScanDataLocalName[] =
|
||||
{
|
||||
'S', 'e', 'n', 's', 'o', 'r'
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
CCC Data
|
||||
**************************************************************************************************/
|
||||
|
||||
/* enumeration of client characteristic configuration descriptors */
|
||||
enum
|
||||
{
|
||||
APPS_MAIN_GYRO_DATA_CLIENT_CHR_CONFIG_CCC_IDX,
|
||||
APPS_MAIN_GYRO_TEMPDATA_CLIENT_CHR_CONFIG_CCC_IDX,
|
||||
APPS_MAIN_TEMP_DATA_CLIENT_CHR_CONFIG_CCC_IDX,
|
||||
APPS_MAIN_NUM_CCC_IDX
|
||||
};
|
||||
|
||||
/* client characteristic configuration descriptors settings, indexed by above enumeration */
|
||||
static const attsCccSet_t MainSensor_CccSet[APPS_MAIN_NUM_CCC_IDX] =
|
||||
{
|
||||
/* cccd handle value range security level */
|
||||
{GYRO_HANDLE_DATA_CLIENT_CHR_CONFIG, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE},
|
||||
{GYRO_HANDLE_TEMPDATA_CLIENT_CHR_CONFIG, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE},
|
||||
{TEMP_HANDLE_DATA_CLIENT_CHR_CONFIG, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application DM callback.
|
||||
*
|
||||
* \param pDmEvt DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void sensorDmCback(dmEvt_t *pDmEvt)
|
||||
{
|
||||
dmEvt_t *pMsg;
|
||||
uint16_t len;
|
||||
|
||||
len = DmSizeOfEvt(pDmEvt);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(len)) != NULL)
|
||||
{
|
||||
memcpy(pMsg, pDmEvt, len);
|
||||
WsfMsgSend(sensorHandlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATT callback.
|
||||
*
|
||||
* \param pAttEvt ATT callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void sensorAttCback(attEvt_t *pEvt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application CCC callback.
|
||||
*
|
||||
* \param pEvt CCC callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void sensorCccCback(attsCccEvt_t *pEvt)
|
||||
{
|
||||
APP_TRACE_INFO3("ccc state ind value:%d handle:%d idx:%d", pEvt->value, pEvt->handle, pEvt->idx);
|
||||
|
||||
switch (pEvt->idx)
|
||||
{
|
||||
case APPS_MAIN_GYRO_DATA_CLIENT_CHR_CONFIG_CCC_IDX:
|
||||
{
|
||||
if (pEvt->value == ATT_CLIENT_CFG_NOTIFY)
|
||||
{
|
||||
GyroMeasStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
GyroMeasStop();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case APPS_MAIN_TEMP_DATA_CLIENT_CHR_CONFIG_CCC_IDX:
|
||||
{
|
||||
if (pEvt->value == ATT_CLIENT_CFG_NOTIFY)
|
||||
{
|
||||
TempMeasStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
TempMeasStop();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the system ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void sensorSetSystemId(void)
|
||||
{
|
||||
uint8_t *bdaddr = HciGetBdAddr();
|
||||
uint8_t sys_id[8];
|
||||
|
||||
/* Formatted according to GATT specification for System ID characteristic (0x2A23). */
|
||||
sys_id[0] = bdaddr[0];
|
||||
sys_id[1] = bdaddr[1];
|
||||
sys_id[2] = bdaddr[2];
|
||||
sys_id[3] = 0xFE;
|
||||
sys_id[4] = 0xFF;
|
||||
sys_id[5] = bdaddr[3];
|
||||
sys_id[6] = bdaddr[4];
|
||||
sys_id[7] = bdaddr[5];
|
||||
AttsSetAttr(DIS_SID_HDL, sizeof(sys_id), sys_id);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set up advertising and other procedures that need to be performed after
|
||||
* device reset.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void sensorSetup(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* set advertising response data for discoverable mode */
|
||||
memset(sensorAdvDataDisc, 0, sizeof(sensorAdvDataDisc));
|
||||
AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, 0, (uint8_t *) sensorAdvDataDisc);
|
||||
AppAdvSetAdValue(APP_ADV_DATA_DISCOVERABLE, DM_ADV_TYPE_FLAGS, sizeof(sensorAdvDataFlags),
|
||||
(uint8_t *) sensorAdvDataFlags);
|
||||
|
||||
/* set scan response data for discoverable mode */
|
||||
memset(sensorScanDataDisc, 0, sizeof(sensorScanDataDisc));
|
||||
AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, 0, (uint8_t *) sensorScanDataDisc);
|
||||
AppAdvSetAdValue(APP_SCAN_DATA_DISCOVERABLE, DM_ADV_TYPE_LOCAL_NAME, sizeof(sensorScanDataLocalName), (uint8_t *) sensorScanDataLocalName);
|
||||
|
||||
/* set advertising response data for connectable mode */
|
||||
memset(sensorAdvDataConn, 0, sizeof(sensorAdvDataConn));
|
||||
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, 0, (uint8_t *) sensorAdvDataConn);
|
||||
AppAdvSetAdValue(APP_ADV_DATA_CONNECTABLE, DM_ADV_TYPE_FLAGS, sizeof(sensorAdvDataFlags),
|
||||
(uint8_t *) sensorAdvDataFlags);
|
||||
|
||||
/* set scan response data for connectable mode */
|
||||
memset(sensorScanDataConn, 0, sizeof(sensorScanDataConn));
|
||||
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, 0, (uint8_t *) sensorScanDataConn);
|
||||
AppAdvSetAdValue(APP_SCAN_DATA_CONNECTABLE, DM_ADV_TYPE_LOCAL_NAME, sizeof(sensorScanDataLocalName), (uint8_t *) sensorScanDataLocalName);
|
||||
|
||||
/* set system ID according to BDADDR */
|
||||
sensorSetSystemId();
|
||||
|
||||
/* start advertising */
|
||||
AppSetAdvType(DM_ADV_CONN_UNDIRECT);
|
||||
AppAdvStart(APP_MODE_AUTO_INIT);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Button press callback.
|
||||
*
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void sensorBtnCback(uint8_t btn)
|
||||
{
|
||||
dmConnId_t connId;
|
||||
|
||||
/* button actions when connected */
|
||||
connId = AppConnIsOpen();
|
||||
if (connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
/* ignore button when connected */
|
||||
case APP_UI_BTN_1_SHORT:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* button actions when not connected */
|
||||
else
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
case APP_UI_BTN_1_SHORT:
|
||||
/* start connectable advertising and reset timer */
|
||||
AppAdvStop();
|
||||
AppAdvStart(APP_MODE_CONNECTABLE);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void sensorProcMsg(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
uint8_t uiEvent = APP_UI_NONE;
|
||||
|
||||
switch (pMsg->event)
|
||||
{
|
||||
case ATT_MTU_UPDATE_IND:
|
||||
APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu);
|
||||
break;
|
||||
|
||||
case DM_RESET_CMPL_IND:
|
||||
{
|
||||
AttsCalculateDbHash();
|
||||
sensorSetup(pMsg);
|
||||
uiEvent = APP_UI_RESET_CMPL;
|
||||
break;
|
||||
}
|
||||
|
||||
case DM_CONN_OPEN_IND:
|
||||
{
|
||||
GyroMeasStart();
|
||||
TempMeasStart();
|
||||
uiEvent = APP_UI_CONN_OPEN;
|
||||
break;
|
||||
}
|
||||
|
||||
case DM_CONN_CLOSE_IND:
|
||||
{
|
||||
GyroMeasStop();
|
||||
TempMeasStop();
|
||||
uiEvent = APP_UI_CONN_CLOSE;
|
||||
break;
|
||||
}
|
||||
|
||||
case DM_ADV_START_IND:
|
||||
{
|
||||
uiEvent = APP_UI_ADV_START;
|
||||
break;
|
||||
}
|
||||
|
||||
case DM_ADV_STOP_IND:
|
||||
{
|
||||
uiEvent = APP_UI_ADV_STOP;
|
||||
break;
|
||||
}
|
||||
|
||||
case SENSOR_GYRO_TIMER_IND:
|
||||
{
|
||||
dmConnId_t connId = AppConnIsOpen();
|
||||
if (connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
int16_t x, y, z;
|
||||
if (AppReadGyro(&x, &y, &z))
|
||||
{
|
||||
GyroMeasComplete(connId, x, y, z);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SENSOR_TEMP_TIMER_IND:
|
||||
{
|
||||
dmConnId_t connId = AppConnIsOpen();
|
||||
if (connId != DM_CONN_ID_NONE)
|
||||
{
|
||||
int16_t temp;
|
||||
if (AppReadTemp(&temp))
|
||||
{
|
||||
TempMeasComplete(connId, temp);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (uiEvent != APP_UI_NONE)
|
||||
{
|
||||
AppUiAction(uiEvent);
|
||||
}
|
||||
|
||||
if (sensorExtCback != NULL)
|
||||
{
|
||||
sensorExtCback(pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef APP_SENSOR_OVERRIDE
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read gyroscope sensor.
|
||||
*
|
||||
* \param pX Storage for gyroscope x-axis reading.
|
||||
* \param pY Storage for gyroscope y-axis reading.
|
||||
* \param pZ Storage for gyroscope z-axis reading.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AppReadGyro(int16_t *pX, int16_t *pY, int16_t *pZ)
|
||||
{
|
||||
static int16_t x = -100;
|
||||
static int16_t y = 0;
|
||||
static int16_t z = 100;
|
||||
*pX = x++;
|
||||
*pY = y++;
|
||||
*pZ = z++;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Read temperature sensor.
|
||||
*
|
||||
* \param pTemp Storage for temperature reading.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
bool_t AppReadTemp(int16_t *pTemp)
|
||||
{
|
||||
static int16_t temp = 0;
|
||||
*pTemp = temp++;
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SensorHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
/* store handler ID */
|
||||
sensorHandlerId = handlerId;
|
||||
|
||||
/* Set configuration pointers */
|
||||
pAppAdvCfg = (appAdvCfg_t *)&sensorAdvCfg;
|
||||
pAppSlaveCfg = (appSlaveCfg_t *)&sensorSlaveCfg;
|
||||
pAppSecCfg = (appSecCfg_t *)&sensorSecCfg;
|
||||
pAppUpdateCfg = (appUpdateCfg_t *)&sensorUpdateCfg;
|
||||
|
||||
/* Initialize application framework */
|
||||
AppSlaveInit();
|
||||
AppServerInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SensorHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
APP_TRACE_INFO1("sensor got evt %d", pMsg->event);
|
||||
|
||||
/* process ATT messages */
|
||||
if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
|
||||
{
|
||||
/* process server-related ATT messages */
|
||||
AppServerProcAttMsg(pMsg);
|
||||
}
|
||||
/* process DM messages */
|
||||
else if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
|
||||
{
|
||||
/* process advertising and connection-related messages */
|
||||
AppSlaveProcDmMsg((dmEvt_t *) pMsg);
|
||||
|
||||
/* process security-related messages */
|
||||
AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/* perform profile and user interface-related operations */
|
||||
sensorProcMsg(pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Initialize stack.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void sensorStackInit(void)
|
||||
{
|
||||
SecInit();
|
||||
SecAesInit();
|
||||
SecCmacInit();
|
||||
SecEccInit();
|
||||
|
||||
HciHandlerInit(WsfOsSetNextHandler(HciHandler));
|
||||
|
||||
DmDevVsInit(0);
|
||||
DmAdvInit();
|
||||
DmConnInit();
|
||||
DmConnSlaveInit();
|
||||
DmSecInit();
|
||||
DmHandlerInit(WsfOsSetNextHandler(DmHandler));
|
||||
|
||||
L2cSlaveHandlerInit(WsfOsSetNextHandler(L2cSlaveHandler));
|
||||
L2cInit();
|
||||
L2cSlaveInit();
|
||||
|
||||
AttHandlerInit(WsfOsSetNextHandler(AttHandler));
|
||||
AttsInit();
|
||||
AttsIndInit();
|
||||
|
||||
SmpHandlerInit(WsfOsSetNextHandler(SmpHandler));
|
||||
SmprInit();
|
||||
SmprScInit();
|
||||
HciSetMaxRxAclLen(SENSOR_MAX_RX_ACL_LEN);
|
||||
|
||||
AppHandlerInit(WsfOsSetNextHandler(AppHandler));
|
||||
SensorHandlerInit(WsfOsSetNextHandler(SensorHandler));
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start sensor application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SensorStart(void)
|
||||
{
|
||||
APP_TRACE_INFO0("sensor starting app");
|
||||
|
||||
/* Initialize stack */
|
||||
sensorStackInit();
|
||||
|
||||
/* Register for stack callbacks */
|
||||
DmRegister(sensorDmCback);
|
||||
DmConnRegister(DM_CLIENT_ID_APP, sensorDmCback);
|
||||
AttRegister(sensorAttCback);
|
||||
AttConnRegister(AppServerConnCback);
|
||||
|
||||
/* Register for app framework callbacks */
|
||||
AppUiBtnRegister(sensorBtnCback);
|
||||
|
||||
/* Initialize attribute server database */
|
||||
SvcCoreGattCbackRegister(GattReadCback, GattWriteCback);
|
||||
SvcCoreAddGroup();
|
||||
SvcDisAddGroup();
|
||||
GyroStart(sensorHandlerId, SENSOR_GYRO_TIMER_IND);
|
||||
TempStart(sensorHandlerId, SENSOR_TEMP_TIMER_IND);
|
||||
|
||||
/* set up CCCD table and callback */
|
||||
AttsCccRegister(APPS_MAIN_NUM_CCC_IDX, (attsCccSet_t *) MainSensor_CccSet, sensorCccCback);
|
||||
|
||||
/* Reset the device */
|
||||
DmDevReset();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a callback to receive events for the purpose of extending the sensor app.
|
||||
*
|
||||
* \param extCback Callback function
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void SensorRegisterExtensionCback(sensorExtCback_t extCback)
|
||||
{
|
||||
sensorExtCback = extCback;
|
||||
}
|
||||
|
||||
Vendored
+73
@@ -0,0 +1,73 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Tag sample application interface.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef TAG_API_H
|
||||
#define TAG_API_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the proximity profile reporter application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void TagStart(void);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Proximity reporter handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for App.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void TagHandlerInit(wsfHandlerId_t handlerId);
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for proximity reporter.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void TagHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* TAG_API_H */
|
||||
Vendored
+1144
File diff suppressed because it is too large
Load Diff
Vendored
+1185
File diff suppressed because it is too large
Load Diff
+121
@@ -0,0 +1,121 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief UriBeacon sample application.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#ifndef URIBEACON_API_H
|
||||
#define URIBEACON_API_H
|
||||
|
||||
#include "wsf_types.h"
|
||||
#include "wsf_os.h"
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! UriBeacon parameter IDs */
|
||||
enum
|
||||
{
|
||||
URI_BEACON_PARAM_LOCK_STATE = 4, /*!< lock state (for UriBeacon) [1 byte] */
|
||||
URI_BEACON_PARAM_URI_DATA = 5, /*!< URI data (for UriBeacon) [18 bytes] */
|
||||
URI_BEACON_PARAM_URI_FLAGS = 6, /*!< URI flags (for UriBeacon) [1 byte] */
|
||||
URI_BEACON_PARAM_ADVERTISED_TX_POWER_LEVELS = 7, /*!< advertised tx power levels (for UriBeacon) [4 bytes] */
|
||||
URI_BEACON_PARAM_TX_POWER_MODE = 8, /*!< tx power mode (for UriBeacon) [1 byte] */
|
||||
URI_BEACON_PARAM_BEACON_PERIOD = 9, /*!< beacon period (for beacon) [2 bytes] */
|
||||
URI_BEACON_PARAM_LOCK = 10 /*!< lock [16 bytes] */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Callback Function Types
|
||||
**************************************************************************************************/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief App extension callback.
|
||||
*
|
||||
* \param pMsg Pointer to message structure.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
typedef void (*uribeaconExtCback_t)(wsfMsgHdr_t *pMsg);
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start UriBeacon application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UriBeaconStart(void);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UriBeaconHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UriBeaconHandlerInit(wsfHandlerId_t handlerId);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Called prior to starting Uribeacon app to override the beacon data in nv memory.
|
||||
*
|
||||
* \param pUriData Pointer to URI data.
|
||||
* \param dataLen Length of pUriData in bytes.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UriBeaconSetUriOverride(const uint8_t *pUriData, uint8_t dataLen);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a callback to receive events for the purpose of extending the URI beacon app.
|
||||
*
|
||||
* \param extCback Callback function
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UriBeaconRegisterExtensionCback(uribeaconExtCback_t extCback);
|
||||
|
||||
#endif /* URIBEACON_API_H */
|
||||
+973
@@ -0,0 +1,973 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief UriBeacon sample application.
|
||||
*
|
||||
* Copyright (c) 2011-2019 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "uribeacon/uricfg_api.h"
|
||||
#include "uribeacon/uricfg_defs.h"
|
||||
#include "svc_uricfg.h"
|
||||
#include "app_api.h"
|
||||
#include "app_db.h"
|
||||
#include "app_ui.h"
|
||||
#include "app_main.h"
|
||||
#include "att_defs.h"
|
||||
#include "util/bstream.h"
|
||||
#include "dm_api.h"
|
||||
#include "sec_api.h"
|
||||
#include "svc_core.h"
|
||||
#include "svc_dis.h"
|
||||
#include "gatt/gatt_api.h"
|
||||
#include "wsf_msg.h"
|
||||
#include "wsf_trace.h"
|
||||
#include "wsf_types.h"
|
||||
// #include "wsf_nvm.h"
|
||||
#include "uribeacon/uribeacon_api.h"
|
||||
|
||||
#define WsfNvmWriteData(A, B, C, D) 0
|
||||
#define WsfNvmReadData(A, B, C, D) 0
|
||||
|
||||
/**************************************************************************************************
|
||||
Macros
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! default lock state */
|
||||
#define URIBEACON_LOCKSTATE_DEFAULT 0
|
||||
|
||||
/*! default URI data value ("http://www.arm.com") */
|
||||
#define URIBEACON_URIDATA_DEFAULT 0x00, 'a', 'r', 'm', 0x07
|
||||
|
||||
/*! clamp beacon period to allowed range */
|
||||
#define URIBEACON_BEACONPERIOD_CLAMP(bp) (((bp) < URICFG_ATT_BEACONPERIOD_MIN) ? URICFG_ATT_BEACONPERIOD_MIN : \
|
||||
((bp) > URICFG_ATT_BEACONPERIOD_MAX) ? URICFG_ATT_BEACONPERIOD_MAX : \
|
||||
(bp))
|
||||
/*! convert beacon period from ms to 0.625-ms units */
|
||||
#define URIBEACON_BEACONPERIOD_TO_INTERVAL(bp) (((URIBEACON_BEACONPERIOD_CLAMP(bp)) * 8u) / 5u)
|
||||
|
||||
/*! size of advertising information */
|
||||
#define URIBEACON_INFO_SIZE 24
|
||||
|
||||
/*! offsets of information within advertising data */
|
||||
#define URIBEACON_INFO_OFFSET 3 /*! offset of advertising information within advertising data */
|
||||
#define URIBEACON_INFO_SVCDATASIZE_OFFSET 7 /*! offset of service data size */
|
||||
#define URIBEACON_INFO_URIFLAGS_OFFSET 11 /*! offset of URI flags. */
|
||||
#define URIBEACON_INFO_TXPWRLEVEL_OFFSET 12 /*! offset of Tx power level */
|
||||
#define URIBEACON_INFO_URIDATA_OFFSET 13 /*! offset of URI data */
|
||||
|
||||
/*! default adv Tx power levels */
|
||||
static const int8_t uriAdvertisedTxPwrLevelsDefault[] = {-80, -60, -40, -20};
|
||||
|
||||
/*! WSF message event starting value */
|
||||
#define URIBEACON_MSG_START 0xA0
|
||||
|
||||
/*! WSF message event enumeration */
|
||||
enum
|
||||
{
|
||||
URIBEACON_ADV_TIMER_IND = URIBEACON_MSG_START /*! Advertising timer expired */
|
||||
};
|
||||
|
||||
/*! Advertising timer timeout */
|
||||
#define URIBEACON_ADV_TIMEOUT_SEC 30
|
||||
|
||||
/**************************************************************************************************
|
||||
Client Characteristic Configuration Descriptors
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! enumeration of client characteristic configuration descriptors */
|
||||
enum
|
||||
{
|
||||
URIBEACON_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
|
||||
URIBEACON_NUM_CCC_IDX
|
||||
};
|
||||
|
||||
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
|
||||
static const attsCccSet_t uriBeaconCccSet[URIBEACON_NUM_CCC_IDX] =
|
||||
{
|
||||
/* cccd handle value range security level */
|
||||
{ GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE } /* URIBEACON_GATT_SC_CCC_IDX */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Global Variables
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! WSF handler ID */
|
||||
static wsfHandlerId_t uriBeaconHandlerId;
|
||||
|
||||
/*! Advertising mode timer */
|
||||
static wsfTimer_t uriBeaconAdvTimer;
|
||||
static uint8_t uriBeaconAdvType;
|
||||
|
||||
/* App extention callback */
|
||||
static uribeaconExtCback_t uriBeaconExtCback;
|
||||
|
||||
/**************************************************************************************************
|
||||
Configurable Parameters
|
||||
**************************************************************************************************/
|
||||
|
||||
/*! configurable parameters for advertising */
|
||||
static appAdvCfg_t uriBeaconAdvCfg =
|
||||
{
|
||||
{0, 0, 0}, /*! Advertising durations in ms */
|
||||
{0, 0, 0}, /*! Advertising intervals in 0.625 ms units */
|
||||
};
|
||||
|
||||
/*! configurable parameters for slave */
|
||||
static appSlaveCfg_t uriBeaconSlaveCfg =
|
||||
{
|
||||
1 /*! Maximum connections */
|
||||
};
|
||||
|
||||
/*! configurable parameters for security */
|
||||
static const appSecCfg_t uriBeaconSecCfg =
|
||||
{
|
||||
DM_AUTH_BOND_FLAG, /*! Authentication and bonding flags */
|
||||
0, /*! Initiator key distribution flags */
|
||||
DM_KEY_DIST_LTK, /*! 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 uriBeaconUpdateCfg =
|
||||
{
|
||||
0, /*! 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 */
|
||||
};
|
||||
|
||||
/**************************************************************************************************
|
||||
Advertising Data
|
||||
**************************************************************************************************/
|
||||
static int8_t uriBeaconAdvertisedTxPwrLevels[URICFG_SIZE_TXPWRLEVELS_ATT];
|
||||
static uint8_t uriBeaconTxPwrMode;
|
||||
static const uint8_t uriBeaconUriData[] = {URIBEACON_URIDATA_DEFAULT};
|
||||
|
||||
/* URI data param override */
|
||||
static uint8_t *uriDataOverride;
|
||||
static uint8_t uriDataOverrideLen;
|
||||
|
||||
/* advertising data, discoverable mode */
|
||||
static uint8_t uriBeaconAdvDataDisc[HCI_ADV_DATA_LEN] =
|
||||
{
|
||||
/* advertising type flags */
|
||||
1u + sizeof(uint8_t),
|
||||
DM_ADV_TYPE_FLAGS,
|
||||
DM_FLAG_LE_LIMITED_DISC | DM_FLAG_LE_BREDR_NOT_SUP,
|
||||
|
||||
/* UriBeacon service UUID */
|
||||
3u,
|
||||
DM_ADV_TYPE_16_UUID,
|
||||
UINT16_TO_BYTES(URICFG_SERVICE_UUID),
|
||||
|
||||
/* UriBeacon service data */
|
||||
5u + sizeof(uriBeaconUriData), /* length (5-23) */
|
||||
DM_ADV_TYPE_SERVICE_DATA,
|
||||
UINT16_TO_BYTES(URICFG_SERVICE_UUID),
|
||||
0u, /* flags */
|
||||
0, /* tx power level */
|
||||
URIBEACON_URIDATA_DEFAULT /* URI */
|
||||
};
|
||||
|
||||
/* scan data, discoverable and connectable modes */
|
||||
static uint8_t uriBeaconScanDataDisc[HCI_ADV_DATA_LEN];
|
||||
static uint8_t uriBeaconScanDataConn[HCI_ADV_DATA_LEN];
|
||||
static const uint8_t uriBeaconScanDataLocalName[] = { 'U', 'r', 'i', 'B', 'c', 'n' };
|
||||
static const uint8_t uriBeaconScanData128Uuid[] = { URICFG_UUID_BYTES(URICFG_UUID_SVC) };
|
||||
|
||||
static const char * const uriBeaconPrefixes[] =
|
||||
{
|
||||
"http://www.", "https://www.", "http://", "https://", "urn:uuid:"
|
||||
};
|
||||
static const char * const uriBeaconCodes[] =
|
||||
{
|
||||
".com/", ".org/", ".edu/", ".net/", ".info/", ".biz/", ".gov/",
|
||||
".com", ".org", ".edu", ".net", ".info", ".biz", ".gov",
|
||||
};
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Print URI data as URI.
|
||||
*
|
||||
* \param pUriData URI data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uriBeaconPrintUri(const uint8_t *pUriData)
|
||||
{
|
||||
uint8_t c;
|
||||
uint8_t len = 0u;
|
||||
char uri[2 * URICFG_MAXSIZE_URIDATA_ATT];
|
||||
uint8_t uriOffset;
|
||||
|
||||
memset(uri, 0, sizeof(uri));
|
||||
uriOffset = 0;
|
||||
|
||||
c = *pUriData++;
|
||||
len++;
|
||||
if (c < sizeof(uriBeaconPrefixes) / sizeof(uriBeaconPrefixes[0]))
|
||||
{
|
||||
memcpy(uri + uriOffset, uriBeaconPrefixes[c], strlen(uriBeaconPrefixes[c]));
|
||||
uriOffset += (uint8_t) strlen(uriBeaconPrefixes[c]);
|
||||
}
|
||||
else
|
||||
{
|
||||
uri[uriOffset] = '?';
|
||||
APP_TRACE_INFO1("URI = %s", uri);
|
||||
return;
|
||||
}
|
||||
|
||||
while (len < URICFG_MAXSIZE_URIDATA_ATT)
|
||||
{
|
||||
c = *pUriData++;
|
||||
if ((c > 0x20u) && (c < 0x7F))
|
||||
{
|
||||
uri[uriOffset++] = (char)c;
|
||||
}
|
||||
else if (c < sizeof(uriBeaconCodes) / sizeof(uriBeaconCodes[0]))
|
||||
{
|
||||
memcpy(uri + uriOffset, uriBeaconCodes[c], strlen(uriBeaconCodes[c]));
|
||||
uriOffset += (uint8_t) strlen(uriBeaconCodes[c]);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
APP_TRACE_INFO1("URI = %s", uri);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set advertising type.
|
||||
*
|
||||
* \param advType Advertising type
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uriBeaconSetAdvType(uint8_t advType)
|
||||
{
|
||||
uriBeaconAdvType = advType;
|
||||
AppSetAdvType(advType);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Update URI data in advertising data.
|
||||
*
|
||||
* \param pUriData URI data.
|
||||
* \param uriDataLen URI data length.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uriBeaconUpdateAdvUriData(const uint8_t *pUriData, uint8_t uriDataLen)
|
||||
{
|
||||
uint16_t len;
|
||||
|
||||
for (len = 0u; len < uriDataLen; len++)
|
||||
{
|
||||
if (pUriData[len] == 0xFFu)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
memcpy(&uriBeaconAdvDataDisc[URIBEACON_INFO_URIDATA_OFFSET], pUriData, len);
|
||||
uriBeaconAdvDataDisc[URIBEACON_INFO_SVCDATASIZE_OFFSET] = 5u + len;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Update URI flags in advertising data.
|
||||
*
|
||||
* \param uriFlags URI flags.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uriBeaconUpdateAdvUriFlags(uint8_t uriFlags)
|
||||
{
|
||||
uriBeaconAdvDataDisc[URIBEACON_INFO_URIFLAGS_OFFSET] = uriFlags;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Update Tx power level in advertising data.
|
||||
*
|
||||
* \param txPwrLevel Tx power level.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uriBeaconUpdateAdvTxPwrLevel(int8_t txPwrLevel)
|
||||
{
|
||||
uriBeaconAdvDataDisc[URIBEACON_INFO_TXPWRLEVEL_OFFSET] = (uint8_t)txPwrLevel;
|
||||
|
||||
/* TODO: SEND VS COMMAND TO ACTUALLY CHANGE POWER LEVEL */
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Update beacon period (advertising interval).
|
||||
*
|
||||
* \param beaconPeriod Beacon period in milliseconds.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uriBeaconUpdateBeaconPeriod(uint16_t beaconPeriod)
|
||||
{
|
||||
/* this never occurs while we're advertising, so we just need to update the datum */
|
||||
if (beaconPeriod == URICFG_ATT_BEACONPERIOD_DISABLE)
|
||||
{
|
||||
uriBeaconAdvCfg.advInterval[0] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
uriBeaconAdvCfg.advInterval[0] = URIBEACON_BEACONPERIOD_TO_INTERVAL(beaconPeriod);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set the system ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uriBeaconSetSystemId(void)
|
||||
{
|
||||
uint8_t *bdaddr = HciGetBdAddr();
|
||||
uint8_t sysId[8];
|
||||
|
||||
/* formatted according to GATT specification for System ID characteristic (0x2A23) */
|
||||
sysId[0] = bdaddr[0];
|
||||
sysId[1] = bdaddr[1];
|
||||
sysId[2] = bdaddr[2];
|
||||
sysId[3] = 0xFE;
|
||||
sysId[4] = 0xFF;
|
||||
sysId[5] = bdaddr[3];
|
||||
sysId[6] = bdaddr[4];
|
||||
sysId[7] = bdaddr[5];
|
||||
AttsSetAttr(DIS_SID_HDL, sizeof(sysId), sysId);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Assign a random address.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uriBeaconAssignRandomAddress(void)
|
||||
{
|
||||
uint8_t addr[BDA_ADDR_LEN];
|
||||
SecRand(addr, BDA_ADDR_LEN);
|
||||
DM_RAND_ADDR_SET(addr, DM_RAND_ADDR_STATIC);
|
||||
DmDevSetRandAddr(addr);
|
||||
DmAdvSetAddrType(DM_ADDR_RANDOM);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application DM callback.
|
||||
*
|
||||
* \param pDmEvt DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uriBeaconDmCback(dmEvt_t *pDmEvt)
|
||||
{
|
||||
dmEvt_t *pMsg;
|
||||
uint16_t len;
|
||||
|
||||
len = DmSizeOfEvt(pDmEvt);
|
||||
|
||||
if ((pMsg = WsfMsgAlloc(len)) != NULL)
|
||||
{
|
||||
memcpy(pMsg, pDmEvt, len);
|
||||
WsfMsgSend(uriBeaconHandlerId, pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATT callback.
|
||||
*
|
||||
* \param pEvt ATT callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uriBeaconAttCback(attEvt_t *pEvt)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application ATTS client characteristic configuration callback.
|
||||
*
|
||||
* \param pDmEvt DM callback event
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uriBeaconCccCback(attsCccEvt_t *pEvt)
|
||||
{
|
||||
appDbHdl_t dbHdl;
|
||||
|
||||
/* If CCC not set from initialization and there's a device record and currently bonded */
|
||||
if ((pEvt->handle != ATT_HANDLE_NONE) &&
|
||||
((dbHdl = AppDbGetHdl((dmConnId_t)pEvt->hdr.param)) != APP_DB_HDL_NONE) &&
|
||||
AppCheckBonded((dmConnId_t)pEvt->hdr.param))
|
||||
{
|
||||
/* Store value in device database. */
|
||||
AppDbSetCccTblValue(dbHdl, pEvt->idx, pEvt->value);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Attribute write callback.
|
||||
*
|
||||
* \param handle Attribute handle.
|
||||
* \param valueLen Length of value data.
|
||||
* \param pValue Pointer to value data.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uriBeaconAttWriteCback(uint16_t handle, uint16_t valueLen, const uint8_t *pValue)
|
||||
{
|
||||
uint8_t id;
|
||||
bool_t updatedAdvData = FALSE;
|
||||
uint8_t uriData[URICFG_MAXSIZE_URIDATA_ATT];
|
||||
|
||||
switch (handle)
|
||||
{
|
||||
case URICFG_HANDLE_URIDATA:
|
||||
{
|
||||
/* pad data to maximum length so setting does not need to resize */
|
||||
memset(uriData, 0xFF, sizeof(uriData));
|
||||
memcpy(uriData, pValue, valueLen);
|
||||
pValue = uriData;
|
||||
valueLen = URICFG_MAXSIZE_URIDATA_ATT;
|
||||
|
||||
APP_TRACE_INFO0("URI upd URI data");
|
||||
uriBeaconPrintUri(uriData);
|
||||
uriBeaconUpdateAdvUriData(uriData, (uint8_t) valueLen);
|
||||
updatedAdvData = TRUE;
|
||||
id = URI_BEACON_PARAM_URI_DATA;
|
||||
break;
|
||||
}
|
||||
case URICFG_HANDLE_URIFLAGS:
|
||||
{
|
||||
APP_TRACE_INFO1("URI upd URI flags %02X", *pValue);
|
||||
uriBeaconUpdateAdvUriFlags(*pValue);
|
||||
updatedAdvData = TRUE;
|
||||
id = URI_BEACON_PARAM_URI_FLAGS;
|
||||
break;
|
||||
}
|
||||
case URICFG_HANDLE_TXPWRLEVELS:
|
||||
{
|
||||
APP_TRACE_INFO2("URI upd adv tx pwr lvls {%d, %d,", pValue[0], pValue[1]);
|
||||
APP_TRACE_INFO2(" %d, %d}", pValue[2], pValue[3]);
|
||||
|
||||
memcpy(uriBeaconAdvertisedTxPwrLevels, pValue, valueLen);
|
||||
uriBeaconUpdateAdvTxPwrLevel(uriBeaconAdvertisedTxPwrLevels[uriBeaconTxPwrMode]);
|
||||
updatedAdvData = TRUE;
|
||||
id = URI_BEACON_PARAM_ADVERTISED_TX_POWER_LEVELS;
|
||||
break;
|
||||
}
|
||||
case URICFG_HANDLE_TXPWRMODE:
|
||||
{
|
||||
APP_TRACE_INFO1("URI upd tx pwr mode %u", *pValue);
|
||||
uriBeaconTxPwrMode = *pValue;
|
||||
uriBeaconUpdateAdvTxPwrLevel(uriBeaconAdvertisedTxPwrLevels[uriBeaconTxPwrMode]);
|
||||
updatedAdvData = TRUE;
|
||||
id = URI_BEACON_PARAM_TX_POWER_MODE;
|
||||
break;
|
||||
}
|
||||
case URICFG_HANDLE_BEACONPERIOD:
|
||||
{
|
||||
uint16_t beaconPeriod;
|
||||
|
||||
BYTES_TO_UINT16(beaconPeriod, pValue);
|
||||
APP_TRACE_INFO1("URI upd beacon period %u", beaconPeriod);
|
||||
uriBeaconUpdateBeaconPeriod(beaconPeriod);
|
||||
if (beaconPeriod == URICFG_ATT_BEACONPERIOD_DISABLE)
|
||||
{
|
||||
/* do not store 'disable' beacon period */
|
||||
return;
|
||||
}
|
||||
id = URI_BEACON_PARAM_BEACON_PERIOD;
|
||||
break;
|
||||
}
|
||||
case URICFG_HANDLE_LOCK:
|
||||
{
|
||||
/* only called during reset */
|
||||
APP_TRACE_INFO0("URI upd lock");
|
||||
id = URI_BEACON_PARAM_LOCK;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
APP_TRACE_INFO0("URI upd unknown attr");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!WsfNvmWriteData(id, pValue, valueLen, NULL))
|
||||
{
|
||||
APP_TRACE_ERR0("URI failed to wr hostcfg");
|
||||
}
|
||||
|
||||
if (updatedAdvData)
|
||||
{
|
||||
/* Set as Connectable so that data is updated after connection closes. */
|
||||
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, sizeof(uriBeaconAdvDataDisc), (uint8_t *)uriBeaconAdvDataDisc);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Lock change callback.
|
||||
*
|
||||
* \param lockState New lock state.
|
||||
* \param lock Lock value.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uriBeaconLockChangeCback(uint8_t lockState, const uint8_t lock[URICFG_SIZE_LOCK_ATT])
|
||||
{
|
||||
APP_TRACE_INFO1("URI upd lock state %u", lockState);
|
||||
|
||||
/* only update lock if locking */
|
||||
if (lockState)
|
||||
{
|
||||
if (!WsfNvmWriteData(URI_BEACON_PARAM_LOCK, lock, URICFG_SIZE_LOCK_ATT, NULL))
|
||||
{
|
||||
APP_TRACE_ERR0("URI failed to wr lock state");
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* always update lock state */
|
||||
if (!WsfNvmWriteData(URI_BEACON_PARAM_LOCK_STATE, &lockState, sizeof(lockState), NULL))
|
||||
{
|
||||
APP_TRACE_ERR0("URI failed to wr lcok state");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Set up advertising and other procedures that need to be performed after
|
||||
* device reset.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uriBeaconSetup(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
/* set advertising and scan response data for discoverable mode */
|
||||
AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(uriBeaconAdvDataDisc), (uint8_t *)uriBeaconAdvDataDisc);
|
||||
|
||||
/* set scan response data for discoverable mode */
|
||||
memset(uriBeaconScanDataDisc, 0, sizeof(uriBeaconScanDataDisc));
|
||||
AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, 0, (uint8_t *) uriBeaconScanDataDisc);
|
||||
AppAdvSetAdValue(APP_SCAN_DATA_DISCOVERABLE, DM_ADV_TYPE_LOCAL_NAME, sizeof(uriBeaconScanDataLocalName), (uint8_t *) uriBeaconScanDataLocalName);
|
||||
AppAdvSetAdValue(APP_SCAN_DATA_DISCOVERABLE, DM_ADV_TYPE_128_UUID, sizeof(uriBeaconScanData128Uuid), (uint8_t *) uriBeaconScanData128Uuid);
|
||||
|
||||
/* set advertising and scan response data for connectable mode */
|
||||
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, sizeof(uriBeaconAdvDataDisc), (uint8_t *)uriBeaconAdvDataDisc);
|
||||
|
||||
/* set scan response data for connectable mode */
|
||||
memset(uriBeaconScanDataDisc, 0, sizeof(uriBeaconScanDataDisc));
|
||||
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, 0, (uint8_t *) uriBeaconScanDataConn);
|
||||
AppAdvSetAdValue(APP_SCAN_DATA_CONNECTABLE, DM_ADV_TYPE_LOCAL_NAME, sizeof(uriBeaconScanDataLocalName), (uint8_t *) uriBeaconScanDataLocalName);
|
||||
AppAdvSetAdValue(APP_SCAN_DATA_CONNECTABLE, DM_ADV_TYPE_128_UUID, sizeof(uriBeaconScanData128Uuid), (uint8_t *) uriBeaconScanData128Uuid);
|
||||
|
||||
/* set system ID according to BDADDR */
|
||||
uriBeaconSetSystemId();
|
||||
|
||||
/* assign a random address before we start advertising */
|
||||
uriBeaconAssignRandomAddress();
|
||||
|
||||
/* start timer for advertising mode change */
|
||||
memset(&uriBeaconAdvTimer, 0, sizeof(wsfTimer_t));
|
||||
uriBeaconAdvTimer.handlerId = uriBeaconHandlerId;
|
||||
uriBeaconAdvTimer.msg.event = URIBEACON_ADV_TIMER_IND;
|
||||
WsfTimerStartSec(&uriBeaconAdvTimer, URIBEACON_ADV_TIMEOUT_SEC);
|
||||
|
||||
/* start advertising */
|
||||
uriBeaconSetAdvType(DM_ADV_CONN_UNDIRECT);
|
||||
AppAdvStart(APP_MODE_CONNECTABLE);
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Button press callback.
|
||||
*
|
||||
* \param btn Button press.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uriBeaconBtnCback(uint8_t btn)
|
||||
{
|
||||
/* button actions when connected */
|
||||
if (AppConnIsOpen() != DM_CONN_ID_NONE)
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
/* ignore button when connected */
|
||||
case APP_UI_BTN_1_SHORT:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* button actions when not connected */
|
||||
else
|
||||
{
|
||||
switch (btn)
|
||||
{
|
||||
case APP_UI_BTN_1_SHORT:
|
||||
/* start connectable advertising and reset timer */
|
||||
uriBeaconSetAdvType(DM_ADV_CONN_UNDIRECT);
|
||||
WsfTimerStartSec(&uriBeaconAdvTimer, URIBEACON_ADV_TIMEOUT_SEC);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Process messages from the event handler.
|
||||
*
|
||||
* \param pMsg Pointer to message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
static void uriBeaconProcMsg(wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
uint8_t uiEvent = APP_UI_NONE;
|
||||
|
||||
switch (pMsg->event)
|
||||
{
|
||||
/* revert to non-connectable advertising */
|
||||
case URIBEACON_ADV_TIMER_IND:
|
||||
uriBeaconSetAdvType(DM_ADV_NONCONN_UNDIRECT);
|
||||
break;
|
||||
|
||||
case DM_ADV_START_IND:
|
||||
if (pMsg->status == HCI_SUCCESS)
|
||||
{
|
||||
uiEvent = (uriBeaconAdvType == DM_ADV_NONCONN_UNDIRECT) ? APP_UI_DISCOVERABLE : APP_UI_ADV_START;
|
||||
}
|
||||
break;
|
||||
|
||||
case ATT_MTU_UPDATE_IND:
|
||||
APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu);
|
||||
break;
|
||||
|
||||
case DM_RESET_CMPL_IND:
|
||||
AttsCalculateDbHash();
|
||||
uriBeaconSetup(pMsg);
|
||||
uiEvent = APP_UI_RESET_CMPL;
|
||||
break;
|
||||
|
||||
/* stop advertising timeout */
|
||||
case DM_CONN_OPEN_IND:
|
||||
WsfTimerStop(&uriBeaconAdvTimer);
|
||||
uiEvent = APP_UI_CONN_OPEN;
|
||||
break;
|
||||
|
||||
/* re-start connectable advertising, with timeout */
|
||||
case DM_CONN_CLOSE_IND:
|
||||
uriBeaconSetAdvType(DM_ADV_CONN_UNDIRECT);
|
||||
uriBeaconAdvTimer.handlerId = uriBeaconHandlerId;
|
||||
uriBeaconAdvTimer.msg.event = URIBEACON_ADV_TIMER_IND;
|
||||
WsfTimerStartSec(&uriBeaconAdvTimer, URIBEACON_ADV_TIMEOUT_SEC);
|
||||
uiEvent = APP_UI_ADV_START;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (uiEvent != APP_UI_NONE)
|
||||
{
|
||||
AppUiAction(uiEvent);
|
||||
}
|
||||
|
||||
if (uriBeaconExtCback != NULL)
|
||||
{
|
||||
uriBeaconExtCback(pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UriBeaconHandlerInit(wsfHandlerId_t handlerId)
|
||||
{
|
||||
/* store handler ID */
|
||||
uriBeaconHandlerId = handlerId;
|
||||
|
||||
/* set configuration pointers */
|
||||
pAppAdvCfg = (appAdvCfg_t *)&uriBeaconAdvCfg;
|
||||
pAppSlaveCfg = (appSlaveCfg_t *)&uriBeaconSlaveCfg;
|
||||
pAppSecCfg = (appSecCfg_t *)&uriBeaconSecCfg;
|
||||
pAppUpdateCfg = (appUpdateCfg_t *)&uriBeaconUpdateCfg;
|
||||
|
||||
/* initialize application framework */
|
||||
AppSlaveInit();
|
||||
AppServerInit();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UriBeaconHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
|
||||
{
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
APP_TRACE_INFO1("UriBeacon got evt %d", pMsg->event);
|
||||
|
||||
/* process ATT messages */
|
||||
if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
|
||||
{
|
||||
/* process server-related ATT messages */
|
||||
AppServerProcAttMsg(pMsg);
|
||||
}
|
||||
/* process DM messages */
|
||||
else if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
|
||||
{
|
||||
/* process advertising and connection-related messages */
|
||||
AppSlaveProcDmMsg((dmEvt_t *) pMsg);
|
||||
|
||||
/* process security-related messages */
|
||||
AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
|
||||
}
|
||||
|
||||
/* perform profile and user interface-related operations */
|
||||
uriBeaconProcMsg(pMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start UriBeacon application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UriBeaconStart(void)
|
||||
{
|
||||
uint8_t lockState;
|
||||
uint8_t uriData[URICFG_MAXSIZE_URIDATA_ATT];
|
||||
uint8_t uriDataLen = 0;
|
||||
uint8_t uriDataReset[URICFG_MAXSIZE_URIDATA_ATT];
|
||||
uint8_t uriFlags;
|
||||
uint16_t beaconPeriod;
|
||||
uint8_t lock[URICFG_SIZE_LOCK_ATT];
|
||||
|
||||
APP_TRACE_INFO0("URI starting app");
|
||||
|
||||
lockState = URIBEACON_LOCKSTATE_DEFAULT;
|
||||
/* try to load settings from NV memory */
|
||||
if (WsfNvmReadData(URI_BEACON_PARAM_LOCK_STATE, (uint8_t *)&lockState, sizeof(lockState), NULL) )
|
||||
{
|
||||
APP_TRACE_INFO1("URI rd lock state from param DB %u", lockState);
|
||||
}
|
||||
else
|
||||
{
|
||||
lockState = URIBEACON_LOCKSTATE_DEFAULT;
|
||||
}
|
||||
|
||||
if (uriDataOverride != NULL)
|
||||
{
|
||||
uriDataLen = uriDataOverrideLen;
|
||||
memcpy(uriData, uriDataOverride, uriDataLen);
|
||||
}
|
||||
else if (WsfNvmReadData(URI_BEACON_PARAM_URI_DATA, &uriData[0], sizeof(uriData), NULL))
|
||||
{
|
||||
APP_TRACE_INFO0("URI rd URI data from param DB ");
|
||||
uriBeaconPrintUri(uriData);
|
||||
|
||||
uint8_t *pUriData = uriData;
|
||||
while ((*pUriData != 0xFF) && (uriDataLen < URICFG_MAXSIZE_URIDATA_ATT))
|
||||
{
|
||||
pUriData++;
|
||||
uriDataLen++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(uriData, uriBeaconUriData, sizeof(uriBeaconUriData));
|
||||
uriDataLen = sizeof(uriBeaconUriData);
|
||||
}
|
||||
|
||||
uriFlags = URICFG_ATT_URIFLAGS_DEFAULT;
|
||||
if (WsfNvmReadData(URI_BEACON_PARAM_URI_FLAGS, (uint8_t *)&uriFlags, sizeof(uriFlags), NULL))
|
||||
{
|
||||
APP_TRACE_INFO1("URI rd URI flags from param DB %02X", uriFlags);
|
||||
}
|
||||
else
|
||||
{
|
||||
uriFlags = URICFG_ATT_URIFLAGS_DEFAULT;
|
||||
}
|
||||
|
||||
if (WsfNvmReadData(URI_BEACON_PARAM_ADVERTISED_TX_POWER_LEVELS, (uint8_t *)&uriBeaconAdvertisedTxPwrLevels, sizeof(uriBeaconAdvertisedTxPwrLevels), NULL))
|
||||
{
|
||||
APP_TRACE_INFO2("URI rd adv tx pwr lvls from param DB {%d, %d,", uriBeaconAdvertisedTxPwrLevels[0], uriBeaconAdvertisedTxPwrLevels[1]);
|
||||
APP_TRACE_INFO2(" %d, %d}", uriBeaconAdvertisedTxPwrLevels[2], uriBeaconAdvertisedTxPwrLevels[3]);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(uriBeaconAdvertisedTxPwrLevels, uriAdvertisedTxPwrLevelsDefault, sizeof(uriBeaconAdvertisedTxPwrLevels));
|
||||
}
|
||||
|
||||
if (WsfNvmReadData(URI_BEACON_PARAM_TX_POWER_MODE, (uint8_t *)&uriBeaconTxPwrMode, sizeof(uriBeaconTxPwrMode), NULL))
|
||||
{
|
||||
APP_TRACE_INFO1("URI rd tx pwr mode from param DB %u", uriBeaconTxPwrMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
uriBeaconTxPwrMode = URICFG_ATT_TXPWRMODE_DEFAULT;
|
||||
}
|
||||
|
||||
beaconPeriod = URICFG_ATT_BEACONPERIOD_DEFAULT;
|
||||
|
||||
if (WsfNvmReadData(URI_BEACON_PARAM_BEACON_PERIOD, (uint8_t *)&beaconPeriod, sizeof(beaconPeriod), NULL))
|
||||
{
|
||||
APP_TRACE_INFO1("URI rd bcn per from param DB %u", beaconPeriod);
|
||||
}
|
||||
else
|
||||
{
|
||||
beaconPeriod = URICFG_ATT_BEACONPERIOD_DEFAULT;
|
||||
}
|
||||
|
||||
if (WsfNvmReadData(URI_BEACON_PARAM_LOCK, &lock[0], sizeof(lock), NULL))
|
||||
{
|
||||
APP_TRACE_INFO0("URI rd lock from param DB");
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(lock, 0x00u, sizeof(lock));
|
||||
}
|
||||
|
||||
/* register for stack callbacks */
|
||||
DmRegister(uriBeaconDmCback);
|
||||
DmConnRegister(DM_CLIENT_ID_APP, uriBeaconDmCback);
|
||||
AttRegister(uriBeaconAttCback);
|
||||
AttConnRegister(AppServerConnCback);
|
||||
AttsCccRegister(URIBEACON_NUM_CCC_IDX, (attsCccSet_t *)uriBeaconCccSet, uriBeaconCccCback);
|
||||
|
||||
/* set advertising and scan response data for discoverable mode */
|
||||
uriBeaconUpdateAdvUriData(uriData, uriDataLen);
|
||||
uriBeaconUpdateAdvUriFlags(uriFlags);
|
||||
uriBeaconUpdateAdvTxPwrLevel(uriBeaconAdvertisedTxPwrLevels[uriBeaconTxPwrMode]);
|
||||
|
||||
/* register for app framework callbacks */
|
||||
AppUiBtnRegister(uriBeaconBtnCback);
|
||||
|
||||
/* initialize attribute server database */
|
||||
SvcCoreGattCbackRegister(GattReadCback, GattWriteCback);
|
||||
SvcCoreAddGroup();
|
||||
SvcDisAddGroup();
|
||||
UriCfgStart(uriData, uriDataLen, uriFlags, uriBeaconAdvertisedTxPwrLevels, uriBeaconTxPwrMode, beaconPeriod);
|
||||
UriCfgAttWriteCbackRegister(uriBeaconAttWriteCback);
|
||||
UriCfgMakeLockable(lockState, lock, uriBeaconLockChangeCback);
|
||||
memset(uriDataReset, 0xFFu, sizeof(uriDataReset));
|
||||
memcpy(uriDataReset, uriBeaconUriData, sizeof(uriBeaconUriData));
|
||||
UriCfgSetUriDataResetValue(uriDataReset);
|
||||
|
||||
GattSetSvcChangedIdx(URIBEACON_GATT_SC_CCC_IDX);
|
||||
|
||||
/* update beacon period (advertising interval) */
|
||||
uriBeaconUpdateBeaconPeriod(beaconPeriod);
|
||||
|
||||
/* reset the device */
|
||||
DmDevReset();
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Called prior to starting Uribeacon app to override the beacon data in nv memory.
|
||||
*
|
||||
* \param pUriData Pointer to URI data.
|
||||
* \param dataLen Length of pUriData in bytes.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UriBeaconSetUriOverride(const uint8_t *pUriData, uint8_t dataLen)
|
||||
{
|
||||
uriDataOverride = (uint8_t *) pUriData;
|
||||
uriDataOverrideLen = dataLen;
|
||||
}
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Register a callback to receive events for the purpose of extending the URI beacon app.
|
||||
*
|
||||
* \param extCback Callback function
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void UriBeaconRegisterExtensionCback(uribeaconExtCback_t extCback)
|
||||
{
|
||||
uriBeaconExtCback = extCback;
|
||||
}
|
||||
Vendored
+73
@@ -0,0 +1,73 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file
|
||||
*
|
||||
* \brief Watch sample application interface.
|
||||
*
|
||||
* Copyright (c) 2011-2018 Arm Ltd.
|
||||
*
|
||||
* Copyright (c) 2019 Packetcraft, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef WATCH_API_H
|
||||
#define WATCH_API_H
|
||||
|
||||
#include "wsf_os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**************************************************************************************************
|
||||
Function Declarations
|
||||
**************************************************************************************************/
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Start the application.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void WatchStart(void);
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief Application handler init function called during system initialization.
|
||||
*
|
||||
* \param handlerID WSF handler ID for App.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void WatchHandlerInit(wsfHandlerId_t handlerId);
|
||||
|
||||
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \brief WSF event handler for the application.
|
||||
*
|
||||
* \param event WSF event mask.
|
||||
* \param pMsg WSF message.
|
||||
*
|
||||
* \return None.
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
void WatchHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* WATCH_API_H */
|
||||
Vendored
+1436
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user