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);
|
||||
}
|
||||
Reference in New Issue
Block a user