initial commit

This commit is contained in:
2022-10-23 23:45:43 -07:00
commit e190fa5193
6450 changed files with 8626944 additions and 0 deletions
@@ -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;
}
@@ -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);
}
}
}
@@ -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 */
@@ -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;
}
}
}
@@ -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;
}
@@ -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;
}
@@ -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);
}
File diff suppressed because it is too large Load Diff
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -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));
}
@@ -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);
}
}
@@ -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);
}
@@ -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);
}
@@ -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. */
}
@@ -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);
}
@@ -0,0 +1,73 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Asset Tracking Tag sample application.
*
* Copyright (c) 2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef ASSETTAG_API_H
#define ASSETTAG_API_H
#include "wsf_os.h"
#ifdef __cplusplus
extern "C" {
#endif
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void AssetTagStart(void);
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID for App.
*
* \return None.
*/
/*************************************************************************************************/
void AssetTagHandlerInit(wsfHandlerId_t handlerId);
/*************************************************************************************************/
/*!
* \brief WSF event handler for the application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void AssetTagHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
#ifdef __cplusplus
};
#endif
#endif /* ASSETTAG_API_H */
@@ -0,0 +1,622 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Asset Tracking Tag sample application.
*
* Copyright (c) 2018 - 2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_buf.h"
#include "hci_api.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_api.h"
#include "app_main.h"
#include "app_db.h"
#include "app_ui.h"
#include "svc_ch.h"
#include "svc_core.h"
#include "svc_cte.h"
#include "svc_batt.h"
#include "bas/bas_api.h"
#include "atps/atps_api.h"
#include "gatt/gatt_api.h"
#include "util/calc128.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! Enumeration of client characteristic configuration descriptors */
enum
{
ASSETTAG_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
ASSETTAG_BATT_LVL_CCC_IDX, /*! Battery service, battery level characteristic */
ASSETTAG_NUM_CCC_IDX
};
/*! WSF message event starting value */
#define ASSETTAG_MSG_START 0xA0
/*! WSF message event enumeration */
enum
{
ASSETTAG_BATT_TIMER_IND = ASSETTAG_MSG_START, /*! Battery measurement timer expired */
};
/*! Default MTU */
#define ASSETTAG_DEFAULT_MTU 50
/**************************************************************************************************
Configurable Parameters
**************************************************************************************************/
/*! configurable parameters for advertising */
static const appAdvCfg_t assetTagAdvCfg =
{
{30000, 0, 0}, /*! Advertising durations in ms */
{ 96, 1600, 0} /*! Advertising intervals in 0.625 ms units */
};
/*! configurable parameters for slave */
static const appSlaveCfg_t assetTagSlaveCfg =
{
1, /*! Maximum connections */
};
/*! configurable parameters for security */
static const appSecCfg_t assetTagSecCfg =
{
DM_AUTH_BOND_FLAG | SMP_AUTH_SC_FLAG, /*! Authentication and bonding flags */
DM_KEY_DIST_IRK, /*! Initiator key distribution flags */
DM_KEY_DIST_LTK | DM_KEY_DIST_IRK, /*! Responder key distribution flags */
FALSE, /*! TRUE if Out-of-band pairing data is present */
TRUE /*! TRUE to initiate security upon connection */
};
/*! configurable parameters for connection parameter update */
static const appUpdateCfg_t assetTagUpdateCfg =
{
0, /*! Connection idle period in ms before attempting
connection parameter update; set to zero to disable */
640, /*! Minimum connection interval in 1.25ms units */
800, /*! Maximum connection interval in 1.25ms units */
3, /*! Connection latency */
600, /*! Supervision timeout in 10ms units */
5 /*! Number of update attempts before giving up */
};
/*! ATT configurable parameters (increase MTU) */
static const attCfg_t assetTagAttCfg =
{
15, /* ATT server service discovery connection idle timeout in seconds */
ASSETTAG_DEFAULT_MTU, /* desired ATT MTU */
ATT_MAX_TRANS_TIMEOUT, /* transcation timeout in seconds */
4 /* number of queued prepare writes supported by server */
};
/*! local IRK */
static uint8_t localIrk[] =
{
0x95, 0xC8, 0xEE, 0x6F, 0xC5, 0x0D, 0xEF, 0x93, 0x35, 0x4E, 0x7C, 0x57, 0x08, 0xE2, 0xA3, 0x85
};
/**************************************************************************************************
Advertising Data
**************************************************************************************************/
/*! advertising data, discoverable mode */
static const uint8_t assetTagAdvDataDisc[] =
{
/*! flags */
2, /*! length */
DM_ADV_TYPE_FLAGS, /*! AD type */
DM_FLAG_LE_GENERAL_DISC | /*! flags */
DM_FLAG_LE_BREDR_NOT_SUP,
/*! manufacturer specific data */
3, /*! length */
DM_ADV_TYPE_MANUFACTURER, /*! AD type */
UINT16_TO_BYTES(HCI_ID_ARM), /*! company ID */
/*! tx power */
2, /*! length */
DM_ADV_TYPE_TX_POWER, /*! AD type */
0, /*! tx power */
/*! service UUID list */
7, /*! length */
DM_ADV_TYPE_16_UUID, /*! AD type */
UINT16_TO_BYTES(ATT_UUID_CONSTANT_TONE_SERVICE),
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE),
UINT16_TO_BYTES(ATT_UUID_BATTERY_SERVICE),
/*! appearance */
3, /*! length */
DM_ADV_TYPE_APPEARANCE, /*! AD type */
UINT16_TO_BYTES(CH_APPEAR_TAG), /*! appearance */
};
/*! scan data, discoverable mode */
static const uint8_t assetTagScanDataDisc[] =
{
/*! device name */
10, /*! length */
DM_ADV_TYPE_LOCAL_NAME, /*! AD type */
'A',
's',
's',
'e',
't',
' ',
'T',
'a',
'g',
};
/**************************************************************************************************
Client Characteristic Configuration Descriptors
**************************************************************************************************/
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
static const attsCccSet_t assetTagCccSet[ASSETTAG_NUM_CCC_IDX] =
{
/* cccd handle value range security level */
{ GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE }, /* ASSETTAG_GATT_SC_CCC_IDX */
{ BATT_LVL_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE } /* ASSETTAG_BATT_LVL_CCC_IDX */
};
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! application control block */
static struct
{
wsfHandlerId_t handlerId; /* WSF handler ID */
} assetTagCb;
/* Identifiers for antenna */
static uint8_t assetTagAntennaIds[] = {0, 1};
/*************************************************************************************************/
/*!
* \brief Application DM callback.
*
* \param pDmEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void assetTagDmCback(dmEvt_t *pDmEvt)
{
dmEvt_t *pMsg;
if (pDmEvt->hdr.event == DM_SEC_ECC_KEY_IND)
{
DmSecSetEccKey(&pDmEvt->eccMsg.data.key);
}
else
{
uint16_t len = DmSizeOfEvt(pDmEvt);
if ((pMsg = WsfMsgAlloc(len)) != NULL)
{
memcpy(pMsg, pDmEvt, len);
WsfMsgSend(assetTagCb.handlerId, pMsg);
}
}
}
/*************************************************************************************************/
/*!
* \brief Application ATT callback.
*
* \param pEvt ATT callback event
*
* \return None.
*/
/*************************************************************************************************/
static void assetTagAttCback(attEvt_t *pEvt)
{
attEvt_t *pMsg;
if ((pMsg = WsfMsgAlloc(sizeof(attEvt_t) + pEvt->valueLen)) != NULL)
{
memcpy(pMsg, pEvt, sizeof(attEvt_t));
pMsg->pValue = (uint8_t *)(pMsg + 1);
memcpy(pMsg->pValue, pEvt->pValue, pEvt->valueLen);
WsfMsgSend(assetTagCb.handlerId, pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Perform actions on connection open.
*
* \param pMsg Pointer to DM callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void assetTagOpen(dmEvt_t *pMsg)
{
/* Set the antenna identifiers for the connection */
AtpsSetAntennaIds((dmConnId_t) pMsg->hdr.param, sizeof(assetTagAntennaIds), assetTagAntennaIds);
}
/*************************************************************************************************/
/*!
* \brief Application ATTS client characteristic configuration callback.
*
* \param pMsg DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void assetTagProcCccState(attsCccEvt_t *pMsg)
{
APP_TRACE_INFO3("ccc state ind value:%d handle:%d idx:%d", pMsg->value, pMsg->handle, pMsg->idx);
/* handle battery level CCC */
if (pMsg->idx == ASSETTAG_BATT_LVL_CCC_IDX)
{
if (pMsg->value == ATT_CLIENT_CFG_NOTIFY)
{
BasMeasBattStart((dmConnId_t)pMsg->hdr.param, ASSETTAG_BATT_TIMER_IND, ASSETTAG_BATT_LVL_CCC_IDX);
}
else
{
BasMeasBattStop((dmConnId_t)pMsg->hdr.param);
}
}
}
/*************************************************************************************************/
/*!
* \brief Application ATTS client characteristic configuration callback.
*
* \param pEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void assetTagCccCback(attsCccEvt_t *pEvt)
{
attsCccEvt_t *pMsg;
appDbHdl_t dbHdl;
/* if CCC not set from initialization and there's a device record and currently bonded */
if ((pEvt->handle != ATT_HANDLE_NONE) &&
((dbHdl = AppDbGetHdl((dmConnId_t)pEvt->hdr.param)) != APP_DB_HDL_NONE) &&
AppCheckBonded((dmConnId_t)pEvt->hdr.param))
{
/* store value in device database */
AppDbSetCccTblValue(dbHdl, pEvt->idx, pEvt->value);
}
if ((pMsg = WsfMsgAlloc(sizeof(attsCccEvt_t))) != NULL)
{
memcpy(pMsg, pEvt, sizeof(attsCccEvt_t));
WsfMsgSend(assetTagCb.handlerId, pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Get peer key from a device database record.
*
* \param pMsg Pointer to DM callback event message.
*
* \return Pointer to peer key if key is valid or NULL if not valid.
*/
/*************************************************************************************************/
static dmSecKey_t *assetTagGetPeerKey(dmEvt_t *pMsg)
{
appDbHdl_t dbHdl;
/* get device database record handle */
dbHdl = AppDbGetHdl((dmConnId_t) pMsg->hdr.param);
/* if database record handle valid */
if (dbHdl != APP_DB_HDL_NONE)
{
return AppDbGetKey(dbHdl, DM_KEY_IRK, NULL);
}
return NULL;
}
/*************************************************************************************************/
/*!
* \brief Handle add device to resolving list indication.
*
* \param pMsg Pointer to DM callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void assetTagPrivAddDevToResListInd(dmEvt_t *pMsg)
{
dmSecKey_t *pPeerKey;
/* if peer IRK present */
if ((pPeerKey = assetTagGetPeerKey(pMsg)) != NULL)
{
/* set advertising peer address */
AppSetAdvPeerAddr(pPeerKey->irk.addrType, pPeerKey->irk.bdAddr);
}
}
/*************************************************************************************************/
/*!
* \brief Handle remove device from resolving list indication.
*
* \param pMsg Pointer to DM callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void assetTagPrivRemDevFromResListInd(dmEvt_t *pMsg)
{
if (pMsg->hdr.status == HCI_SUCCESS)
{
if (AppDbGetHdl((dmConnId_t) pMsg->hdr.param) != APP_DB_HDL_NONE)
{
uint8_t addrZeros[BDA_ADDR_LEN] = { 0 };
/* clear advertising peer address and its type */
AppSetAdvPeerAddr(HCI_ADDR_TYPE_PUBLIC, addrZeros);
}
}
}
/*************************************************************************************************/
/*!
* \brief Set up advertising and other procedures that need to be performed after
* device reset.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void assetTagSetup(dmEvt_t *pMsg)
{
/* set advertising and scan response data for discoverable mode */
AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(assetTagAdvDataDisc), (uint8_t *) assetTagAdvDataDisc);
AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(assetTagScanDataDisc), (uint8_t *) assetTagScanDataDisc);
/* set advertising and scan response data for connectable mode */
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, sizeof(assetTagAdvDataDisc), (uint8_t *) assetTagAdvDataDisc);
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, sizeof(assetTagScanDataDisc), (uint8_t *) assetTagScanDataDisc);
/* start advertising; automatically set connectable/discoverable mode and bondable mode */
AppAdvStart(APP_MODE_AUTO_INIT);
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void assetTagProcMsg(dmEvt_t *pMsg)
{
uint8_t uiEvent = APP_UI_NONE;
switch(pMsg->hdr.event)
{
case ATTS_CCC_STATE_IND:
assetTagProcCccState((attsCccEvt_t*) pMsg);
break;
case DM_RESET_CMPL_IND:
AttsCalculateDbHash();
DmSecGenerateEccKeyReq();
assetTagSetup(pMsg);
uiEvent = APP_UI_RESET_CMPL;
break;
case DM_ADV_START_IND:
uiEvent = APP_UI_ADV_START;
break;
case DM_ADV_STOP_IND:
uiEvent = APP_UI_ADV_STOP;
break;
case DM_CONN_OPEN_IND:
assetTagOpen(pMsg);
uiEvent = APP_UI_CONN_OPEN;
break;
case DM_CONN_CLOSE_IND:
uiEvent = APP_UI_CONN_CLOSE;
break;
case DM_SEC_PAIR_CMPL_IND:
DmSecGenerateEccKeyReq();
uiEvent = APP_UI_SEC_PAIR_CMPL;
break;
case DM_SEC_PAIR_FAIL_IND:
DmSecGenerateEccKeyReq();
uiEvent = APP_UI_SEC_PAIR_FAIL;
break;
case DM_SEC_ENCRYPT_IND:
uiEvent = APP_UI_SEC_ENCRYPT;
break;
case DM_SEC_ENCRYPT_FAIL_IND:
uiEvent = APP_UI_SEC_ENCRYPT_FAIL;
break;
case DM_SEC_AUTH_REQ_IND:
AppHandlePasskey(&pMsg->authReq);
break;
case DM_SEC_COMPARE_IND:
AppHandleNumericComparison(&pMsg->cnfInd);
break;
case DM_PRIV_ADD_DEV_TO_RES_LIST_IND:
assetTagPrivAddDevToResListInd(pMsg);
break;
case DM_PRIV_REM_DEV_FROM_RES_LIST_IND:
assetTagPrivRemDevFromResListInd(pMsg);
break;
case DM_ADV_NEW_ADDR_IND:
break;
case DM_ADV_SET_START_IND:
uiEvent = APP_UI_ADV_SET_START_IND;
break;
case DM_ADV_SET_STOP_IND:
uiEvent = APP_UI_ADV_SET_STOP_IND;
break;
case DM_SCAN_REQ_RCVD_IND:
uiEvent = APP_UI_SCAN_REQ_RCVD_IND;
break;
default:
break;
}
if (uiEvent != APP_UI_NONE)
{
AppUiAction(uiEvent);
}
}
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID.
*
* \return None.
*/
/*************************************************************************************************/
void AssetTagHandlerInit(wsfHandlerId_t handlerId)
{
APP_TRACE_INFO0("AssetTagHandlerInit");
/* store handler ID */
assetTagCb.handlerId = handlerId;
/* Set configuration pointers */
pAppSlaveCfg = (appSlaveCfg_t *) &assetTagSlaveCfg;
pAppAdvCfg = (appAdvCfg_t *) &assetTagAdvCfg;
pAppSecCfg = (appSecCfg_t *) &assetTagSecCfg;
pAppUpdateCfg = (appUpdateCfg_t *) &assetTagUpdateCfg;
pAttCfg = (attCfg_t *) &assetTagAttCfg;
/* Initialize application framework */
AppSlaveInit();
AppServerInit();
/* Set IRK for the local device */
DmSecSetLocalIrk(localIrk);
}
/*************************************************************************************************/
/*!
* \brief WSF event handler for application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void AssetTagHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
{
if (pMsg != NULL)
{
APP_TRACE_INFO1("AssetTag got evt %d", pMsg->event);
/* process ATT messages */
if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
{
/* process server-related ATT messages */
AppServerProcAttMsg(pMsg);
}
/* process DM messages */
else if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
{
/* process advertising and connection-related messages */
AppSlaveProcDmMsg((dmEvt_t *) pMsg);
/* process security-related messages */
AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
/* process asset tracking profile-related messages */
AtpsProcDmMsg((dmEvt_t *) pMsg);
}
/* perform profile and user interface-related operations */
assetTagProcMsg((dmEvt_t *) pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void AssetTagStart(void)
{
/* Register for stack callbacks */
DmRegister(assetTagDmCback);
DmConnRegister(DM_CLIENT_ID_APP, assetTagDmCback);
AttRegister(assetTagAttCback);
AttConnRegister(AppServerConnCback);
AttsCccRegister(ASSETTAG_NUM_CCC_IDX, (attsCccSet_t *) assetTagCccSet, assetTagCccCback);
/* Initialize attribute server database */
SvcCoreGattCbackRegister(GattReadCback, GattWriteCback);
SvcCoreAddGroup();
SvcBattCbackRegister(BasReadCback, NULL);
SvcBattAddGroup();
/* Set Service Changed CCCD index. */
GattSetSvcChangedIdx(ASSETTAG_GATT_SC_CCC_IDX);
/* Initialize Asset Tracking Profile */
AtpsInit();
/* Reset the device */
DmDevReset();
}
@@ -0,0 +1,73 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Cycling server sample application.
*
* Copyright (c) 2016-2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef CYCLING_API_H
#define CYCLING_API_H
#include "wsf_os.h"
#ifdef __cplusplus
extern "C" {
#endif
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void CyclingStart(void);
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID for App.
*
* \return None.
*/
/*************************************************************************************************/
void CyclingHandlerInit(wsfHandlerId_t handlerId);
/*************************************************************************************************/
/*!
* \brief WSF event handler for the application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void CyclingHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
#ifdef __cplusplus
};
#endif
#endif /* CYCLING_API_H */
@@ -0,0 +1,777 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Cycling server sample application.
*
* Copyright (c) 2016-2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_buf.h"
#include "hci_api.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_api.h"
#include "app_main.h"
#include "app_db.h"
#include "app_ui.h"
#include "svc_ch.h"
#include "svc_core.h"
#include "svc_cps.h"
#include "svc_cscs.h"
#include "svc_batt.h"
#include "bas/bas_api.h"
#include "cpp/cpp_api.h"
#include "cscp/cscp_api.h"
#include "gatt/gatt_api.h"
#include "util/calc128.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! Enumeration of client characteristic configuration descriptors */
enum
{
CYCLING_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
CYCLING_CPS_CPM_CCC_IDX, /*! Cycling Power Measurement, service changed characteristic */
CYCLING_CSCS_CSM_CCC_IDX, /*! Cycling Speed Measurement, service changed characteristic */
CYCLING_BATT_LVL_CCC_IDX, /*! Battery service, battery level characteristic */
CYCLING_NUM_CCC_IDX
};
/*! WSF message event starting value */
#define CYCLING_MSG_START 0xA0
/*! WSF message event enumeration */
enum
{
CYCLING_CPP_PM_TIMER_IND = CYCLING_MSG_START, /*! Cycling power measurement timer expired */
CYCLING_CSCP_SM_TIMER_IND, /*! Cycling speed measurement timer expired */
CYCLING_BATT_TIMER_IND /*! Battery measurement timer expired */
};
/* Default Cycling Power Measurement period (seconds) */
#define CYCLING_DEFAULT_CPM_PERIOD 1
/* Default Cycling Speed Measurement period (seconds) */
#define CYCLING_DEFAULT_CSM_PERIOD 1
/* Default MTU */
#define CYCLING_DEFAULT_MTU 50
/**************************************************************************************************
Configurable Parameters
**************************************************************************************************/
/*! configurable parameters for advertising */
static const appAdvCfg_t cyclingAdvCfg =
{
{30000, 0, 0}, /*! Advertising durations in ms */
{ 96, 1600, 0} /*! Advertising intervals in 0.625 ms units */
};
/*! configurable parameters for slave */
static const appSlaveCfg_t cyclingSlaveCfg =
{
1, /*! Maximum connections */
};
/*! configurable parameters for security */
static const appSecCfg_t cyclingSecCfg =
{
DM_AUTH_BOND_FLAG | SMP_AUTH_SC_FLAG, /*! Authentication and bonding flags */
DM_KEY_DIST_IRK, /*! Initiator key distribution flags */
DM_KEY_DIST_LTK | DM_KEY_DIST_IRK, /*! Responder key distribution flags */
FALSE, /*! TRUE if Out-of-band pairing data is present */
TRUE /*! TRUE to initiate security upon connection */
};
/*! configurable parameters for connection parameter update */
static const appUpdateCfg_t cyclingUpdateCfg =
{
0, /*! Connection idle period in ms before attempting
connection parameter update; set to zero to disable */
640, /*! Minimum connection interval in 1.25ms units */
800, /*! Maximum connection interval in 1.25ms units */
3, /*! Connection latency */
600, /*! Supervision timeout in 10ms units */
5 /*! Number of update attempts before giving up */
};
/*! ATT configurable parameters (increase MTU) */
static const attCfg_t cyclingAttCfg =
{
15, /* ATT server service discovery connection idle timeout in seconds */
CYCLING_DEFAULT_MTU, /* desired ATT MTU */
ATT_MAX_TRANS_TIMEOUT, /* transcation timeout in seconds */
4 /* number of queued prepare writes supported by server */
};
/*! local IRK */
static uint8_t localIrk[] =
{
0x95, 0xC8, 0xEE, 0x6F, 0xC5, 0x0D, 0xEF, 0x93, 0x35, 0x4E, 0x7C, 0x57, 0x08, 0xE2, 0xA3, 0x85
};
/**************************************************************************************************
Advertising Data
**************************************************************************************************/
/*! advertising data, discoverable mode */
static const uint8_t cyclingAdvDataDisc[] =
{
/*! flags */
2, /*! length */
DM_ADV_TYPE_FLAGS, /*! AD type */
DM_FLAG_LE_GENERAL_DISC | /*! flags */
DM_FLAG_LE_BREDR_NOT_SUP,
/*! manufacturer specific data */
3, /*! length */
DM_ADV_TYPE_MANUFACTURER, /*! AD type */
UINT16_TO_BYTES(HCI_ID_ARM), /*! company ID */
/*! service UUID list */
9, /*! length */
DM_ADV_TYPE_16_UUID, /*! AD type */
UINT16_TO_BYTES(ATT_UUID_CYCLING_POWER_SERVICE),
UINT16_TO_BYTES(ATT_UUID_CYCLING_SPEED_SERVICE),
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE),
UINT16_TO_BYTES(ATT_UUID_BATTERY_SERVICE)
};
/*! scan data, discoverable mode */
static const uint8_t cyclingScanDataDisc[] =
{
/*! device name */
8, /*! length */
DM_ADV_TYPE_LOCAL_NAME, /*! AD type */
'C',
'y',
'c',
'l',
'i',
'n',
'g'
};
/**************************************************************************************************
Client Characteristic Configuration Descriptors
**************************************************************************************************/
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
static const attsCccSet_t cyclingCccSet[CYCLING_NUM_CCC_IDX] =
{
/* cccd handle value range security level */
{ GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE }, /* CYCLING_GATT_SC_CCC_IDX */
{ CPS_CPM_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE }, /* CYCLING_CPS_CPM_CCC_IDX */
{ CSCS_CSM_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE }, /* CYCLING_CSCS_CSM_CCC_IDX */
{ BATT_LVL_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE } /* CYCLING_BATT_LVL_CCC_IDX */
};
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! application control block */
static struct
{
wsfHandlerId_t handlerId; /* WSF handler ID */
wsfTimer_t cpmTimer; /* WSF Timer to send cycling power measurement data */
wsfTimer_t csmTimer; /* WSF Timer to send cycling speed measurement data */
} cyclingCb;
/* Cycling Power Measurement period - Can be changed at runtime to vary period */
static uint16_t cyclingCpmPeriod = CYCLING_DEFAULT_CPM_PERIOD;
/* Cycling Speed Measurement period - Can be changed at runtime to vary period */
static uint16_t cyclingCsmPeriod = CYCLING_DEFAULT_CSM_PERIOD;
/*************************************************************************************************/
/*!
* \brief Application DM callback.
*
* \param pDmEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void cyclingDmCback(dmEvt_t *pDmEvt)
{
dmEvt_t *pMsg;
if (pDmEvt->hdr.event == DM_SEC_ECC_KEY_IND)
{
DmSecSetEccKey(&pDmEvt->eccMsg.data.key);
}
else
{
uint16_t len = DmSizeOfEvt(pDmEvt);
if ((pMsg = WsfMsgAlloc(len)) != NULL)
{
memcpy(pMsg, pDmEvt, len);
WsfMsgSend(cyclingCb.handlerId, pMsg);
}
}
}
/*************************************************************************************************/
/*!
* \brief Application ATT callback.
*
* \param pEvt ATT callback event
*
* \return None.
*/
/*************************************************************************************************/
static void cyclingAttCback(attEvt_t *pEvt)
{
attEvt_t *pMsg;
if ((pMsg = WsfMsgAlloc(sizeof(attEvt_t) + pEvt->valueLen)) != NULL)
{
memcpy(pMsg, pEvt, sizeof(attEvt_t));
pMsg->pValue = (uint8_t *)(pMsg + 1);
memcpy(pMsg->pValue, pEvt->pValue, pEvt->valueLen);
WsfMsgSend(cyclingCb.handlerId, pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Send a Cycling Speed Measurement Notification.
*
* \param connId connection ID
*
* \return None.
*/
/*************************************************************************************************/
static void cyclingSendCyclingSpeedMeasurement(dmConnId_t connId)
{
static uint32_t revolutions = 1;
static uint32_t lastTime = 1;
if (AttsCccEnabled(connId, CYCLING_CSCS_CSM_CCC_IDX))
{
/* TODO: Set Cycle Speed Measurement Parameters */
revolutions++;
lastTime += 100;
CscpsSetParameter(CSCP_SM_PARAM_WHEEL_REVOLUTIONS, revolutions);
CscpsSetParameter(CSCP_SM_PARAM_LAST_WHEEL_EVT_TIME, lastTime);
CscpsSetParameter(CSCP_SM_PARAM_CRANK_REVOLUTIONS, revolutions);
CscpsSetParameter(CSCP_SM_PARAM_LAST_CRANK_TIME, lastTime);
CscpsSendSpeedMeasurement(connId);
}
/* Configure and start timer to send the next measurement */
cyclingCb.csmTimer.msg.event = CYCLING_CSCP_SM_TIMER_IND;
cyclingCb.csmTimer.msg.status = CYCLING_CSCS_CSM_CCC_IDX;
cyclingCb.csmTimer.handlerId = cyclingCb.handlerId;
cyclingCb.csmTimer.msg.param = connId;
WsfTimerStartSec(&cyclingCb.csmTimer, cyclingCsmPeriod);
}
/*************************************************************************************************/
/*!
* \brief Send a Cycling Power Measurement Notification.
*
* \param connId connection ID
*
* \return None.
*/
/*************************************************************************************************/
static void cyclingSendCyclingPowerMeasurement(dmConnId_t connId)
{
static uint32_t revolutions = 1;
static uint32_t lastTime = 1;
if (AttsCccEnabled(connId, CYCLING_CPS_CPM_CCC_IDX))
{
/* TODO: Set Cycle Power Measurement Parameters */
revolutions++;
lastTime += 100;
CppsSetParameter(CPP_PM_PARAM_INSTANTANEOUS_POWER, 1);
CppsSetParameter(CPP_PM_PARAM_PEDAL_POWER, 2);
CppsSetParameter(CPP_PM_PARAM_ACCUMULATED_TORQUE, 4);
CppsSetParameter(CPP_PM_PARAM_WHEEL_REVOLUTIONS, revolutions);
CppsSetParameter(CPP_PM_PARAM_LAST_WHEEL_REV_TIME, lastTime);
CppsSetParameter(CPP_PM_PARAM_CRANK_REVOLUTIONS, revolutions);
CppsSetParameter(CPP_PM_PARAM_LAST_CRANK_TIME, lastTime);
CppsSetParameter(CPP_PM_PARAM_MAX_FORCE_MAGNITUDE, 9);
CppsSetParameter(CPP_PM_PARAM_MIN_FORCE_MAGNITUDE, 10);
CppsSetParameter(CPP_PM_PARAM_MAX_TORQUE_MAGNITUDE, 11);
CppsSetParameter(CPP_PM_PARAM_MIN_TORQUE_MAGNITUDE, 12);
CppsSetParameter(CPP_PM_PARAM_MAX_EXTREME_ANGLE, 13);
CppsSetParameter(CPP_PM_PARAM_MIN_EXTREME_ANGLE, 14);
CppsSetParameter(CPP_PM_PARAM_TOP_DEAD_SPOT, 15);
CppsSetParameter(CPP_PM_PARAM_BOTTOM_DEAD_SPOT, 16);
CppsSetParameter(CPP_PM_PARAM_ACCUMULATED_ENERGY, 17);
CppsSendPowerMeasurement(connId);
}
/* Configure and start timer to send the next measurement */
cyclingCb.cpmTimer.msg.event = CYCLING_CPP_PM_TIMER_IND;
cyclingCb.cpmTimer.msg.status = CYCLING_CPS_CPM_CCC_IDX;
cyclingCb.cpmTimer.handlerId = cyclingCb.handlerId;
cyclingCb.cpmTimer.msg.param = connId;
WsfTimerStartSec(&cyclingCb.cpmTimer, cyclingCpmPeriod);
}
/*************************************************************************************************/
/*!
* \brief Application ATTS client characteristic configuration callback.
*
* \param pDmEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void cyclingProcCccState(attsCccEvt_t *pMsg)
{
APP_TRACE_INFO3("ccc state ind value:%d handle:%d idx:%d", pMsg->value, pMsg->handle, pMsg->idx);
/* handle cycling power measurement CCC */
if (pMsg->idx == CYCLING_CPS_CPM_CCC_IDX)
{
if (pMsg->value == ATT_CLIENT_CFG_NOTIFY)
{
cyclingSendCyclingPowerMeasurement((dmConnId_t)pMsg->hdr.param);
}
else
{
WsfTimerStop(&cyclingCb.cpmTimer);
}
return;
}
/* handle cycling speed measurement CCC */
if (pMsg->idx == CYCLING_CSCS_CSM_CCC_IDX)
{
if (pMsg->value == ATT_CLIENT_CFG_NOTIFY)
{
cyclingSendCyclingSpeedMeasurement((dmConnId_t)pMsg->hdr.param);
}
else
{
WsfTimerStop(&cyclingCb.csmTimer);
}
return;
}
/* handle battery level CCC */
if (pMsg->idx == CYCLING_BATT_LVL_CCC_IDX)
{
if (pMsg->value == ATT_CLIENT_CFG_NOTIFY)
{
BasMeasBattStart((dmConnId_t)pMsg->hdr.param, CYCLING_BATT_TIMER_IND, CYCLING_BATT_LVL_CCC_IDX);
}
else
{
BasMeasBattStop((dmConnId_t)pMsg->hdr.param);
}
return;
}
}
/*************************************************************************************************/
/*!
* \brief Application ATTS client characteristic configuration callback.
*
* \param pDmEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void cyclingCccCback(attsCccEvt_t *pEvt)
{
attsCccEvt_t *pMsg;
appDbHdl_t dbHdl;
/* If CCC not set from initialization and there's a bond record and currently bonded */
if ((pEvt->handle != ATT_HANDLE_NONE) &&
((dbHdl = AppDbGetHdl((dmConnId_t)pEvt->hdr.param)) != APP_DB_HDL_NONE) &&
AppCheckBonded((dmConnId_t)pEvt->hdr.param))
{
/* Store value in device database. */
AppDbSetCccTblValue(dbHdl, pEvt->idx, pEvt->value);
}
if ((pMsg = WsfMsgAlloc(sizeof(attsCccEvt_t))) != NULL)
{
memcpy(pMsg, pEvt, sizeof(attsCccEvt_t));
WsfMsgSend(cyclingCb.handlerId, pMsg);
}
}
/*************************************************************************************************/
/*!
* \fn cyclingGetPeerKey
*
* \brief Get peer key from a device database record.
*
* \param pMsg Pointer to DM callback event message.
*
* \return Pointer to peer key if key is valid or NULL if not valid.
*/
/*************************************************************************************************/
static dmSecKey_t *cyclingGetPeerKey(dmEvt_t *pMsg)
{
appDbHdl_t dbHdl;
/* get device database record handle */
dbHdl = AppDbGetHdl((dmConnId_t) pMsg->hdr.param);
/* if database record handle valid */
if (dbHdl != APP_DB_HDL_NONE)
{
return AppDbGetKey(dbHdl, DM_KEY_IRK, NULL);
}
return NULL;
}
/*************************************************************************************************/
/*!
* \fn datsPrivAddDevToResListInd
*
* \brief Handle add device to resolving list indication.
*
* \param pMsg Pointer to DM callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void cyclingPrivAddDevToResListInd(dmEvt_t *pMsg)
{
dmSecKey_t *pPeerKey;
/* if peer IRK present */
if ((pPeerKey = cyclingGetPeerKey(pMsg)) != NULL)
{
/* set advertising peer address */
AppSetAdvPeerAddr(pPeerKey->irk.addrType, pPeerKey->irk.bdAddr);
}
}
/*************************************************************************************************/
/*!
* \fn cyclingPrivRemDevFromResListInd
*
* \brief Handle remove device from resolving list indication.
*
* \param pMsg Pointer to DM callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void cyclingPrivRemDevFromResListInd(dmEvt_t *pMsg)
{
if (pMsg->hdr.status == HCI_SUCCESS)
{
if (AppDbGetHdl((dmConnId_t) pMsg->hdr.param) != APP_DB_HDL_NONE)
{
uint8_t addrZeros[BDA_ADDR_LEN] = { 0 };
/* clear advertising peer address and its type */
AppSetAdvPeerAddr(HCI_ADDR_TYPE_PUBLIC, addrZeros);
}
}
}
/*************************************************************************************************/
/*!
* \fn cyclingSetup
*
* \brief Set up advertising and other procedures that need to be performed after
* device reset.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void cyclingSetup(dmEvt_t *pMsg)
{
/* set advertising and scan response data for discoverable mode */
AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(cyclingAdvDataDisc), (uint8_t *) cyclingAdvDataDisc);
AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(cyclingScanDataDisc), (uint8_t *) cyclingScanDataDisc);
/* set advertising and scan response data for connectable mode */
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, sizeof(cyclingAdvDataDisc), (uint8_t *) cyclingAdvDataDisc);
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, sizeof(cyclingScanDataDisc), (uint8_t *) cyclingScanDataDisc);
/* start advertising; automatically set connectable/discoverable mode and bondable mode */
AppAdvStart(APP_MODE_AUTO_INIT);
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void cyclingProcMsg(dmEvt_t *pMsg)
{
uint8_t uiEvent = APP_UI_NONE;
switch(pMsg->hdr.event)
{
case CYCLING_CPP_PM_TIMER_IND:
cyclingSendCyclingPowerMeasurement((dmConnId_t) pMsg->hdr.param);
break;
case CYCLING_CSCP_SM_TIMER_IND:
cyclingSendCyclingSpeedMeasurement((dmConnId_t) pMsg->hdr.param);
break;
case ATTS_CCC_STATE_IND:
cyclingProcCccState((attsCccEvt_t*) pMsg);
break;
case ATT_MTU_UPDATE_IND:
APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu);
break;
case DM_RESET_CMPL_IND:
AttsCalculateDbHash();
DmSecGenerateEccKeyReq();
cyclingSetup(pMsg);
uiEvent = APP_UI_RESET_CMPL;
break;
case DM_ADV_START_IND:
uiEvent = APP_UI_ADV_START;
break;
case DM_ADV_STOP_IND:
uiEvent = APP_UI_ADV_STOP;
break;
case DM_CONN_OPEN_IND:
CppsConnOpen((dmConnId_t)pMsg->hdr.param);
uiEvent = APP_UI_CONN_OPEN;
break;
case DM_CONN_CLOSE_IND:
WsfTimerStop(&cyclingCb.cpmTimer);
WsfTimerStop(&cyclingCb.csmTimer);
uiEvent = APP_UI_CONN_CLOSE;
break;
case DM_SEC_PAIR_CMPL_IND:
DmSecGenerateEccKeyReq();
uiEvent = APP_UI_SEC_PAIR_CMPL;
break;
case DM_SEC_PAIR_FAIL_IND:
DmSecGenerateEccKeyReq();
uiEvent = APP_UI_SEC_PAIR_FAIL;
break;
case DM_SEC_ENCRYPT_IND:
uiEvent = APP_UI_SEC_ENCRYPT;
break;
case DM_SEC_ENCRYPT_FAIL_IND:
uiEvent = APP_UI_SEC_ENCRYPT_FAIL;
break;
case DM_SEC_AUTH_REQ_IND:
AppHandlePasskey(&pMsg->authReq);
break;
case DM_SEC_COMPARE_IND:
AppHandleNumericComparison(&pMsg->cnfInd);
break;
case DM_PRIV_ADD_DEV_TO_RES_LIST_IND:
cyclingPrivAddDevToResListInd(pMsg);
break;
case DM_PRIV_REM_DEV_FROM_RES_LIST_IND:
cyclingPrivRemDevFromResListInd(pMsg);
break;
case DM_ADV_NEW_ADDR_IND:
break;
case DM_VENDOR_SPEC_CMD_CMPL_IND:
{
#if defined(AM_PART_APOLLO) || defined(AM_PART_APOLLO2)
uint8_t *param_ptr = &pMsg->vendorSpecCmdCmpl.param[0];
switch (pMsg->vendorSpecCmdCmpl.opcode)
{
case 0xFC20: //read at address
{
uint32_t read_value;
BSTREAM_TO_UINT32(read_value, param_ptr);
APP_TRACE_INFO3("VSC 0x%0x complete status %x param %x",
pMsg->vendorSpecCmdCmpl.opcode,
pMsg->hdr.status,
read_value);
}
break;
default:
APP_TRACE_INFO2("VSC 0x%0x complete status %x",
pMsg->vendorSpecCmdCmpl.opcode,
pMsg->hdr.status);
break;
}
#endif
}
break;
default:
break;
}
if (uiEvent != APP_UI_NONE)
{
AppUiAction(uiEvent);
}
}
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID.
*
* \return None.
*/
/*************************************************************************************************/
void CyclingHandlerInit(wsfHandlerId_t handlerId)
{
APP_TRACE_INFO0("CyclingHandlerInit");
/* store handler ID */
cyclingCb.handlerId = handlerId;
/* Set configuration pointers */
pAppSlaveCfg = (appSlaveCfg_t *) &cyclingSlaveCfg;
pAppAdvCfg = (appAdvCfg_t *) &cyclingAdvCfg;
pAppSecCfg = (appSecCfg_t *) &cyclingSecCfg;
pAppUpdateCfg = (appUpdateCfg_t *) &cyclingUpdateCfg;
pAttCfg = (attCfg_t *) &cyclingAttCfg;
/* Initialize application framework */
AppSlaveInit();
AppServerInit();
/* Set IRK for the local device */
DmSecSetLocalIrk(localIrk);
}
/*************************************************************************************************/
/*!
* \brief WSF event handler for application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void CyclingHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
{
if (pMsg != NULL)
{
APP_TRACE_INFO1("Cycling got evt %d", pMsg->event);
/* process ATT messages */
if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
{
/* process server-related ATT messages */
AppServerProcAttMsg(pMsg);
}
/* process DM messages */
else if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
{
/* process advertising and connection-related messages */
AppSlaveProcDmMsg((dmEvt_t *) pMsg);
/* process security-related messages */
AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
}
/* perform profile and user interface-related operations */
cyclingProcMsg((dmEvt_t *) pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void CyclingStart(void)
{
/* Register for stack callbacks */
DmRegister(cyclingDmCback);
DmConnRegister(DM_CLIENT_ID_APP, cyclingDmCback);
AttRegister(cyclingAttCback);
AttConnRegister(AppServerConnCback);
AttsCccRegister(CYCLING_NUM_CCC_IDX, (attsCccSet_t *) cyclingCccSet, cyclingCccCback);
/* Initialize attribute server database */
SvcCoreGattCbackRegister(GattReadCback, GattWriteCback);
SvcCoreAddGroup();
SvcCpsAddGroup();
SvcCscsAddGroup();
SvcBattCbackRegister(BasReadCback, NULL);
SvcBattAddGroup();
/* Set Service Changed CCCD index. */
GattSetSvcChangedIdx(CYCLING_GATT_SC_CCC_IDX);
/* Set the cycling power features */
CppsSetFeatures(CPP_ALL_FEATURES);
/* Set the cycling speed and cadence features */
CscpsSetFeatures(CSCS_ALL_FEATURES);
/* Reset the device */
DmDevReset();
}
@@ -0,0 +1,73 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Proprietary data transfer client sample application.
*
* Copyright (c) 2012-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef DATC_API_H
#define DATC_API_H
#include "wsf_os.h"
#ifdef __cplusplus
extern "C" {
#endif
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void DatcStart(void);
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID for App.
*
* \return None.
*/
/*************************************************************************************************/
void DatcHandlerInit(wsfHandlerId_t handlerId);
/*************************************************************************************************/
/*!
* \brief WSF event handler for the application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void DatcHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
#ifdef __cplusplus
};
#endif
#endif /* DATC_API_H */
@@ -0,0 +1,77 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Proprietary data transfer server sample application.
*
* Copyright (c) 2012-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef DATS_API_H
#define DATS_API_H
#include "wsf_os.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef WDXS_INCLUDED
#define WDXS_INCLUDED FALSE
#endif
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void DatsStart(void);
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID for App.
*
* \return None.
*/
/*************************************************************************************************/
void DatsHandlerInit(wsfHandlerId_t handlerId);
/*************************************************************************************************/
/*!
* \brief WSF event handler for the application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void DatsHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
#ifdef __cplusplus
};
#endif
#endif /* DATS_API_H */
@@ -0,0 +1,855 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Data transmitter sample application.
*
* Copyright (c) 2012-2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_buf.h"
#include "hci_api.h"
#include "sec_api.h"
#include "dm_api.h"
#include "smp_api.h"
#include "att_api.h"
#include "app_api.h"
#include "app_main.h"
#include "app_db.h"
#include "app_ui.h"
#include "svc_ch.h"
#include "svc_core.h"
#include "svc_wp.h"
#include "util/calc128.h"
#include "gatt/gatt_api.h"
#include "dats/dats_api.h"
#if WDXS_INCLUDED == TRUE
#include "wsf_efs.h"
#include "svc_wdxs.h"
#include "wdxs/wdxs_api.h"
#include "wdxs/wdxs_main.h"
#include "wdxs/wdxs_stream.h"
#endif
/**************************************************************************************************
Macros
**************************************************************************************************/
#if CS50_INCLUDED == TRUE
/* PHY Test Modes */
#define DATS_PHY_1M 1
#define DATS_PHY_2M 2
#define DATS_PHY_CODED 3
#endif /* CS50_INCLUDED */
/*! Enumeration of client characteristic configuration descriptors */
enum
{
#if WDXS_INCLUDED == TRUE
WDXS_DC_CH_CCC_IDX, /*! WDXS DC service, service changed characteristic */
WDXS_FTC_CH_CCC_IDX, /*! WDXS FTC service, service changed characteristic */
WDXS_FTD_CH_CCC_IDX, /*! WDXS FTD service, service changed characteristic */
WDXS_AU_CH_CCC_IDX, /*! WDXS AU service, service changed characteristic */
#endif /* WDXS_INCLUDED */
DATS_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
DATS_WP_DAT_CCC_IDX, /*! Arm Ltd. proprietary service, data transfer characteristic */
DATS_NUM_CCC_IDX
};
/**************************************************************************************************
Configurable Parameters
**************************************************************************************************/
/*! configurable parameters for advertising */
static const appAdvCfg_t datsAdvCfg =
{
{30000, 0, 0}, /*! Advertising durations in ms */
{ 96, 1600, 0} /*! Advertising intervals in 0.625 ms units */
};
/*! configurable parameters for slave */
static const appSlaveCfg_t datsSlaveCfg =
{
1, /*! Maximum connections */
};
/*! configurable parameters for security */
static const appSecCfg_t datsSecCfg =
{
DM_AUTH_BOND_FLAG | DM_AUTH_SC_FLAG, /*! Authentication and bonding flags */
DM_KEY_DIST_IRK, /*! Initiator key distribution flags */
DM_KEY_DIST_LTK | DM_KEY_DIST_IRK, /*! Responder key distribution flags */
FALSE, /*! TRUE if Out-of-band pairing data is present */
TRUE /*! TRUE to initiate security upon connection */
};
/*! TRUE if Out-of-band pairing data is to be sent */
static const bool_t datsSendOobData = FALSE;
/*! SMP security parameter configuration */
static const smpCfg_t datsSmpCfg =
{
500, /*! 'Repeated attempts' timeout in msec */
SMP_IO_NO_IN_NO_OUT, /*! I/O Capability */
7, /*! Minimum encryption key length */
16, /*! Maximum encryption key length */
1, /*! Attempts to trigger 'repeated attempts' timeout */
0, /*! Device authentication requirements */
64000, /*! Maximum repeated attempts timeout in msec */
64000, /*! Time msec before attemptExp decreases */
2 /*! Repeated attempts multiplier exponent */
};
/*! configurable parameters for connection parameter update */
static const appUpdateCfg_t datsUpdateCfg =
{
0, /*! Connection idle period in ms before attempting
connection parameter update; set to zero to disable */
640, /*! Minimum connection interval in 1.25ms units */
800, /*! Maximum connection interval in 1.25ms units */
3, /*! Connection latency */
600, /*! Supervision timeout in 10ms units */
5 /*! Number of update attempts before giving up */
};
/*! ATT configurable parameters (increase MTU) */
static const attCfg_t datsAttCfg =
{
15, /* ATT server service discovery connection idle timeout in seconds */
241, /* desired ATT MTU */
ATT_MAX_TRANS_TIMEOUT, /* transcation timeout in seconds */
4 /* number of queued prepare writes supported by server */
};
/*! local IRK */
static uint8_t localIrk[] =
{
0x95, 0xC8, 0xEE, 0x6F, 0xC5, 0x0D, 0xEF, 0x93, 0x35, 0x4E, 0x7C, 0x57, 0x08, 0xE2, 0xA3, 0x85
};
/**************************************************************************************************
Advertising Data
**************************************************************************************************/
/*! advertising data, discoverable mode */
static const uint8_t datsAdvDataDisc[] =
{
/*! flags */
2, /*! length */
DM_ADV_TYPE_FLAGS, /*! AD type */
DM_FLAG_LE_GENERAL_DISC | /*! flags */
DM_FLAG_LE_BREDR_NOT_SUP,
/*! manufacturer specific data */
3, /*! length */
DM_ADV_TYPE_MANUFACTURER, /*! AD type */
UINT16_TO_BYTES(HCI_ID_ARM) /*! company ID */
};
/*! scan data, discoverable mode */
static const uint8_t datsScanDataDisc[] =
{
/*! device name */
8, /*! length */
DM_ADV_TYPE_LOCAL_NAME, /*! AD type */
'D',
'a',
't',
'a',
' ',
'T',
'X'
};
/**************************************************************************************************
Client Characteristic Configuration Descriptors
**************************************************************************************************/
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
static const attsCccSet_t datsCccSet[DATS_NUM_CCC_IDX] =
{
/* cccd handle value range security level */
#if WDXS_INCLUDED == TRUE
{WDXS_DC_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* WDXS_DC_CH_CCC_IDX */
{WDXS_FTC_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* WDXS_FTC_CH_CCC_IDX */
{WDXS_FTD_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* WDXS_FTD_CH_CCC_IDX */
{WDXS_AU_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* WDXS_AU_CH_CCC_IDX */
#endif /* WDXS_INCLUDED */
{GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE}, /* DATS_GATT_SC_CCC_IDX */
{WP_DAT_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE} /* DATS_WP_DAT_CCC_IDX */
};
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! application control block */
static struct
{
wsfHandlerId_t handlerId; /* WSF handler ID */
#if CS50_INCLUDED == TRUE
uint8_t phyMode; /*! PHY Test Mode */
#endif /* CS50_INCLUDED */
} datsCb;
/* LESC OOB configuration */
static dmSecLescOobCfg_t *datsOobCfg;
/*************************************************************************************************/
/*!
* \brief Send notification containing data.
*
* \param connId DM connection ID.
*
* \return None.
*/
/*************************************************************************************************/
static void datsSendData(dmConnId_t connId)
{
uint8_t str[] = "hello hello hello hello hello hello.....from dats";
if (AttsCccEnabled(connId, DATS_WP_DAT_CCC_IDX))
{
/* send notification */
AttsHandleValueNtf(connId, WP_DAT_HDL, sizeof(str), str);
AttsHandleValueNtf(connId, WP_DAT_HDL, sizeof(str), str);
AttsHandleValueNtf(connId, WP_DAT_HDL, sizeof(str), str);
AttsHandleValueNtf(connId, WP_DAT_HDL, sizeof(str), str);
AttsHandleValueNtf(connId, WP_DAT_HDL, sizeof(str), str);
}
}
/*************************************************************************************************/
/*!
* \brief Application DM callback.
*
* \param pDmEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void datsDmCback(dmEvt_t *pDmEvt)
{
dmEvt_t *pMsg;
uint16_t len;
if (pDmEvt->hdr.event == DM_SEC_ECC_KEY_IND)
{
DmSecSetEccKey(&pDmEvt->eccMsg.data.key);
/* If the local device sends OOB data. */
if (datsSendOobData)
{
uint8_t oobLocalRandom[SMP_RAND_LEN];
SecRand(oobLocalRandom, SMP_RAND_LEN);
DmSecCalcOobReq(oobLocalRandom, pDmEvt->eccMsg.data.key.pubKey_x);
}
}
else if (pDmEvt->hdr.event == DM_SEC_CALC_OOB_IND)
{
if (datsOobCfg == NULL)
{
datsOobCfg = WsfBufAlloc(sizeof(dmSecLescOobCfg_t));
}
if (datsOobCfg)
{
Calc128Cpy(datsOobCfg->localConfirm, pDmEvt->oobCalcInd.confirm);
Calc128Cpy(datsOobCfg->localRandom, pDmEvt->oobCalcInd.random);
}
}
else
{
len = DmSizeOfEvt(pDmEvt);
if ((pMsg = WsfMsgAlloc(len)) != NULL)
{
memcpy(pMsg, pDmEvt, len);
WsfMsgSend(datsCb.handlerId, pMsg);
}
}
}
/*************************************************************************************************/
/*!
* \brief Application ATT callback.
*
* \param pEvt ATT callback event
*
* \return None.
*/
/*************************************************************************************************/
static void datsAttCback(attEvt_t *pEvt)
{
#if WDXS_INCLUDED == TRUE
WdxsAttCback(pEvt);
#endif /* WDXS_INCLUDED */
}
/*************************************************************************************************/
/*!
* \brief Application ATTS client characteristic configuration callback.
*
* \param pDmEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void datsCccCback(attsCccEvt_t *pEvt)
{
appDbHdl_t dbHdl;
/* If CCC not set from initialization and there's a device record and currently bonded */
if ((pEvt->handle != ATT_HANDLE_NONE) &&
((dbHdl = AppDbGetHdl((dmConnId_t) pEvt->hdr.param)) != APP_DB_HDL_NONE) &&
AppCheckBonded((dmConnId_t)pEvt->hdr.param))
{
/* Store value in device database. */
AppDbSetCccTblValue(dbHdl, pEvt->idx, pEvt->value);
}
}
/*************************************************************************************************/
/*!
* \brief ATTS write callback for proprietary data service.
*
* \return ATT status.
*/
/*************************************************************************************************/
uint8_t datsWpWriteCback(dmConnId_t connId, uint16_t handle, uint8_t operation,
uint16_t offset, uint16_t len, uint8_t *pValue, attsAttr_t *pAttr)
{
/* print received data */
APP_TRACE_INFO0((const char*) pValue);
/* send back some data */
datsSendData(connId);
return ATT_SUCCESS;
}
/*************************************************************************************************/
/*!
*
* \brief Get peer key from a device database record.
*
* \param pMsg Pointer to DM callback event message.
*
* \return Pointer to peer key if key is valid or NULL if not valid.
*/
/*************************************************************************************************/
static dmSecKey_t *datsGetPeerKey(dmEvt_t *pMsg)
{
appDbHdl_t dbHdl;
/* get device database record handle */
dbHdl = AppDbGetHdl((dmConnId_t) pMsg->hdr.param);
/* if database record handle valid */
if (dbHdl != APP_DB_HDL_NONE)
{
return AppDbGetKey(dbHdl, DM_KEY_IRK, NULL);
}
return NULL;
}
/*************************************************************************************************/
/*!
*
* \brief Handle add device to resolving list indication.
*
* \param pMsg Pointer to DM callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void datsPrivAddDevToResListInd(dmEvt_t *pMsg)
{
dmSecKey_t *pPeerKey;
/* if peer IRK present */
if ((pPeerKey = datsGetPeerKey(pMsg)) != NULL)
{
/* set advertising peer address */
AppSetAdvPeerAddr(pPeerKey->irk.addrType, pPeerKey->irk.bdAddr);
}
}
/*************************************************************************************************/
/*!
*
* \brief Handle remove device from resolving list indication.
*
* \param pMsg Pointer to DM callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void datsPrivRemDevFromResListInd(dmEvt_t *pMsg)
{
if (pMsg->hdr.status == HCI_SUCCESS)
{
if (AppDbGetHdl((dmConnId_t) pMsg->hdr.param) != APP_DB_HDL_NONE)
{
uint8_t addrZeros[BDA_ADDR_LEN] = { 0 };
/* clear advertising peer address and its type */
AppSetAdvPeerAddr(HCI_ADDR_TYPE_PUBLIC, addrZeros);
}
}
}
/*************************************************************************************************/
/*!
*
* \brief Display stack version.
*
* \param version version number.
*
* \return None.
*/
/*************************************************************************************************/
void datsDisplayStackVersion(const char *pVersion)
{
APP_TRACE_INFO1("Stack Version: %s", pVersion);
}
/*************************************************************************************************/
/*!
* \brief Set up advertising and other procedures that need to be performed after
* device reset.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void datsSetup(dmEvt_t *pMsg)
{
/* set advertising and scan response data for discoverable mode */
AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(datsAdvDataDisc), (uint8_t *) datsAdvDataDisc);
AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(datsScanDataDisc), (uint8_t *) datsScanDataDisc);
/* set advertising and scan response data for connectable mode */
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, sizeof(datsAdvDataDisc), (uint8_t *) datsAdvDataDisc);
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, sizeof(datsScanDataDisc), (uint8_t *) datsScanDataDisc);
/* start advertising; automatically set connectable/discoverable mode and bondable mode */
AppAdvStart(APP_MODE_AUTO_INIT);
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void datsProcMsg(dmEvt_t *pMsg)
{
uint8_t uiEvent = APP_UI_NONE;
switch(pMsg->hdr.event)
{
case ATT_MTU_UPDATE_IND:
APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu);
break;
case DM_RESET_CMPL_IND:
AttsCalculateDbHash();
DmSecGenerateEccKeyReq();
datsSetup(pMsg);
uiEvent = APP_UI_RESET_CMPL;
break;
case DM_ADV_START_IND:
uiEvent = APP_UI_ADV_START;
break;
case DM_ADV_STOP_IND:
uiEvent = APP_UI_ADV_STOP;
break;
case DM_CONN_OPEN_IND:
uiEvent = APP_UI_CONN_OPEN;
#if CS50_INCLUDED == TRUE
datsCb.phyMode = DATS_PHY_1M;
#endif /* CS50_INCLUDED */
break;
case DM_CONN_CLOSE_IND:
uiEvent = APP_UI_CONN_CLOSE;
break;
case DM_SEC_PAIR_CMPL_IND:
DmSecGenerateEccKeyReq();
uiEvent = APP_UI_SEC_PAIR_CMPL;
break;
case DM_SEC_PAIR_FAIL_IND:
DmSecGenerateEccKeyReq();
uiEvent = APP_UI_SEC_PAIR_FAIL;
break;
case DM_SEC_ENCRYPT_IND:
uiEvent = APP_UI_SEC_ENCRYPT;
break;
case DM_SEC_ENCRYPT_FAIL_IND:
uiEvent = APP_UI_SEC_ENCRYPT_FAIL;
break;
case DM_SEC_AUTH_REQ_IND:
if (pMsg->authReq.oob)
{
dmConnId_t connId = (dmConnId_t) pMsg->hdr.param;
/* TODO: Perform OOB Exchange with the peer. */
/* TODO: Fill datsOobCfg peerConfirm and peerRandom with value passed out of band */
if (datsOobCfg != NULL)
{
DmSecSetOob(connId, datsOobCfg);
}
DmSecAuthRsp(connId, 0, NULL);
}
else
{
AppHandlePasskey(&pMsg->authReq);
}
break;
case DM_SEC_COMPARE_IND:
AppHandleNumericComparison(&pMsg->cnfInd);
break;
case DM_PRIV_ADD_DEV_TO_RES_LIST_IND:
datsPrivAddDevToResListInd(pMsg);
break;
case DM_PRIV_REM_DEV_FROM_RES_LIST_IND:
datsPrivRemDevFromResListInd(pMsg);
break;
case DM_ADV_NEW_ADDR_IND:
break;
case DM_PRIV_CLEAR_RES_LIST_IND:
APP_TRACE_INFO1("Clear resolving list status 0x%02x", pMsg->hdr.status);
break;
#if CS50_INCLUDED == TRUE
case DM_PHY_UPDATE_IND:
APP_TRACE_INFO2("DM_PHY_UPDATE_IND - RX: %d, TX: %d", pMsg->phyUpdate.rxPhy, pMsg->phyUpdate.txPhy);
datsCb.phyMode = pMsg->phyUpdate.rxPhy;
break;
#endif /* CS50_INCLUDED */
case DM_VENDOR_SPEC_CMD_CMPL_IND:
{
#if defined(AM_PART_APOLLO) || defined(AM_PART_APOLLO2)
uint8_t *param_ptr = &pMsg->vendorSpecCmdCmpl.param[0];
switch (pMsg->vendorSpecCmdCmpl.opcode)
{
case 0xFC20: //read at address
{
uint32_t read_value;
BSTREAM_TO_UINT32(read_value, param_ptr);
APP_TRACE_INFO3("VSC 0x%0x complete status %x param %x",
pMsg->vendorSpecCmdCmpl.opcode,
pMsg->hdr.status,
read_value);
}
break;
default:
APP_TRACE_INFO2("VSC 0x%0x complete status %x",
pMsg->vendorSpecCmdCmpl.opcode,
pMsg->hdr.status);
break;
}
#endif
}
break;
default:
break;
}
if (uiEvent != APP_UI_NONE)
{
AppUiAction(uiEvent);
}
}
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID.
*
* \return None.
*/
/*************************************************************************************************/
void DatsHandlerInit(wsfHandlerId_t handlerId)
{
APP_TRACE_INFO0("DatsHandlerInit");
/* store handler ID */
datsCb.handlerId = handlerId;
/* Set configuration pointers */
pAppSlaveCfg = (appSlaveCfg_t *) &datsSlaveCfg;
pAppAdvCfg = (appAdvCfg_t *) &datsAdvCfg;
pAppSecCfg = (appSecCfg_t *) &datsSecCfg;
pAppUpdateCfg = (appUpdateCfg_t *) &datsUpdateCfg;
pSmpCfg = (smpCfg_t *) &datsSmpCfg;
pAttCfg = (attCfg_t *) &datsAttCfg;
/* Initialize application framework */
AppSlaveInit();
AppServerInit();
/* Set IRK for the local device */
DmSecSetLocalIrk(localIrk);
}
/*************************************************************************************************/
/*!
* \brief Button press callback.
*
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void datsBtnCback(uint8_t btn)
{
#if WDXS_INCLUDED == TRUE
static uint8_t waveform = WDXS_STREAM_WAVEFORM_SINE;
#endif /* WDXS_INCLUDED */
#if CS50_INCLUDED == TRUE
dmConnId_t connId;
if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE)
#else
if (AppConnIsOpen() != DM_CONN_ID_NONE)
#endif /* CS50_INCLUDED */
{
switch (btn)
{
#if CS50_INCLUDED == TRUE
case APP_UI_BTN_2_SHORT:
/* Toggle PHY Test Mode */
if (datsCb.phyMode == DATS_PHY_1M)
{
APP_TRACE_INFO0("2 MBit TX and RX PHY Requested");
DmSetPhy(connId, HCI_ALL_PHY_ALL_PREFERENCES, HCI_PHY_LE_2M_BIT, HCI_PHY_LE_2M_BIT, HCI_PHY_OPTIONS_NONE);
}
else if (datsCb.phyMode == DATS_PHY_2M)
{
APP_TRACE_INFO0("LE Coded TX and RX PHY Requested");
DmSetPhy(connId, HCI_ALL_PHY_ALL_PREFERENCES, HCI_PHY_LE_CODED_BIT, HCI_PHY_LE_CODED_BIT, HCI_PHY_OPTIONS_NONE);
}
else
{
APP_TRACE_INFO0("1 MBit TX and RX PHY Requested");
DmSetPhy(connId, HCI_ALL_PHY_ALL_PREFERENCES, HCI_PHY_LE_1M_BIT, HCI_PHY_LE_1M_BIT, HCI_PHY_OPTIONS_NONE);
}
break;
#endif /* CS50_INCLUDED */
#if WDXS_INCLUDED == TRUE
case APP_UI_BTN_1_SHORT:
/* Change stream waveform */
waveform++;
if (waveform > WDXS_STREAM_WAVEFORM_SAWTOOTH)
waveform = WDXS_STREAM_WAVEFORM_SINE;
wdxsSetStreamWaveform(waveform);
break;
#endif /* WDXS_INCLUDED */
default:
break;
}
}
else
{
switch (btn)
{
case APP_UI_BTN_1_SHORT:
{
const char *pVersion;
StackGetVersionNumber(&pVersion);
datsDisplayStackVersion(pVersion);
}
break;
case APP_UI_BTN_1_MED:
/* Enter bondable mode */
AppSetBondable(TRUE);
break;
case APP_UI_BTN_1_LONG:
/* clear all bonding info */
AppSlaveClearAllBondingInfo();
break;
default:
break;
}
}
}
/*************************************************************************************************/
/*!
* \brief Callback for WSF buffer diagnostic messages.
*
* \param pInfo Diagnostics message
*
* \return None.
*/
/*************************************************************************************************/
static void datsWsfBufDiagnostics(WsfBufDiag_t *pInfo)
{
if (pInfo->type == WSF_BUF_ALLOC_FAILED)
{
APP_TRACE_INFO2("Dats got WSF Buffer Allocation Failure - Task: %d Len: %d",
pInfo->param.alloc.taskId, pInfo->param.alloc.len);
}
}
/*************************************************************************************************/
/*!
* \brief WSF event handler for application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void DatsHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
{
if (pMsg != NULL)
{
APP_TRACE_INFO1("Dats got evt %d", pMsg->event);
/* process ATT messages */
if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
{
/* process server-related ATT messages */
AppServerProcAttMsg(pMsg);
}
/* process DM messages */
else if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
{
/* process advertising and connection-related messages */
AppSlaveProcDmMsg((dmEvt_t *) pMsg);
/* process security-related messages */
AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
#if WDXS_INCLUDED == TRUE
/* process WDXS-related messages */
WdxsProcDmMsg((dmEvt_t*) pMsg);
#endif /* WDXS_INCLUDED */
}
/* perform profile and user interface-related operations */
datsProcMsg((dmEvt_t *) pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void DatsStart(void)
{
/* Register for stack callbacks */
DmRegister(datsDmCback);
DmConnRegister(DM_CLIENT_ID_APP, datsDmCback);
AttRegister(datsAttCback);
AttConnRegister(AppServerConnCback);
AttsCccRegister(DATS_NUM_CCC_IDX, (attsCccSet_t *) datsCccSet, datsCccCback);
/* Initialize attribute server database */
SvcCoreGattCbackRegister(GattReadCback, GattWriteCback);
SvcCoreAddGroup();
SvcWpCbackRegister(NULL, datsWpWriteCback);
SvcWpAddGroup();
/* Set Service Changed CCCD index. */
GattSetSvcChangedIdx(DATS_GATT_SC_CCC_IDX);
/* Register for app framework button callbacks */
AppUiBtnRegister(datsBtnCback);
#if WDXS_INCLUDED == TRUE
/* Initialize the OTA File Media */
WdxsOtaMediaInit();
/* Initialize the WDXS Stream */
wdxsStreamInit();
/* Set the WDXS CCC Identifiers */
WdxsSetCccIdx(WDXS_DC_CH_CCC_IDX, WDXS_AU_CH_CCC_IDX, WDXS_FTC_CH_CCC_IDX, WDXS_FTD_CH_CCC_IDX);
#if CS50_INCLUDED == TRUE
WdxsPhyInit();
#endif /* CS50_INCLUDED */
#endif /* WDXS_INCLUDED */
#if CS50_INCLUDED == TRUE
DmPhyInit();
#endif /* CS50_INCLUDED */
WsfBufDiagRegister(datsWsfBufDiagnostics);
/* Reset the device */
DmDevReset();
}
@@ -0,0 +1,81 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Fitness sample application interface.
*
* Copyright (c) 2011-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef FIT_API_H
#define FIT_API_H
#include "wsf_os.h"
#ifdef __cplusplus
extern "C" {
#endif
/**************************************************************************************************
Macros
**************************************************************************************************/
#ifndef FIT_CONN_MAX
#define FIT_CONN_MAX 1
#endif
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void FitStart(void);
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID for App.
*
* \return None.
*/
/*************************************************************************************************/
void FitHandlerInit(wsfHandlerId_t handlerId);
/*************************************************************************************************/
/*!
* \brief WSF event handler for the application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void FitHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
#ifdef __cplusplus
};
#endif
#endif /* FIT_API_H */
@@ -0,0 +1,830 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Fitness sample application for the following profiles:
* Heart Rate profile
*
* Copyright (c) 2011-2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "hci_api.h"
#include "dm_api.h"
#include "att_api.h"
#include "smp_api.h"
#include "app_api.h"
#include "app_db.h"
#include "app_ui.h"
#include "app_hw.h"
#include "app_main.h"
#include "svc_ch.h"
#include "svc_core.h"
#include "svc_hrs.h"
#include "svc_dis.h"
#include "svc_batt.h"
#include "svc_rscs.h"
#include "gatt/gatt_api.h"
#include "bas/bas_api.h"
#include "hrps/hrps_api.h"
#include "rscp/rscp_api.h"
#include "fit_api.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! WSF message event starting value */
#define FIT_MSG_START 0xA0
/* Default Running Speed and Cadence Measurement period (seconds) */
#define FIT_DEFAULT_RSCM_PERIOD 1
/*! WSF message event enumeration */
enum
{
FIT_HR_TIMER_IND = FIT_MSG_START, /*! Heart rate measurement timer expired */
FIT_BATT_TIMER_IND, /*! Battery measurement timer expired */
FIT_RUNNING_TIMER_IND /*! Running speed and cadence measurement timer expired */
};
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! Application message type */
typedef union
{
wsfMsgHdr_t hdr;
dmEvt_t dm;
attsCccEvt_t ccc;
attEvt_t att;
} fitMsg_t;
/**************************************************************************************************
Configurable Parameters
**************************************************************************************************/
/*! configurable parameters for advertising */
static const appAdvCfg_t fitAdvCfg =
{
{0, 0, 0}, /*! Advertising durations in ms */
{ 800, 0, 0} /*! Advertising intervals in 0.625 ms units */
};
/*! configurable parameters for slave */
static const appSlaveCfg_t fitSlaveCfg =
{
FIT_CONN_MAX, /*! Maximum connections */
};
/*! configurable parameters for security */
static const appSecCfg_t fitSecCfg =
{
#ifndef PAIRING_TEST
DM_AUTH_BOND_FLAG |
#endif
DM_AUTH_SC_FLAG, /*! Authentication and bonding flags */
0, /*! Initiator key distribution flags */
DM_KEY_DIST_LTK, /*! Responder key distribution flags */
FALSE, /*! TRUE if Out-of-band pairing data is present */
FALSE /*! TRUE to initiate security upon connection */
};
/*! configurable parameters for connection parameter update */
static const appUpdateCfg_t fitUpdateCfg =
{
3000, /*! Connection idle period in ms before attempting
connection parameter update; set to zero to disable */
48, /*! Minimum connection interval in 1.25ms units */
60, /*! Maximum connection interval in 1.25ms units */
4, /*! Connection latency */
600, /*! Supervision timeout in 10ms units */
5 /*! Number of update attempts before giving up */
};
/*! heart rate measurement configuration */
static const hrpsCfg_t fitHrpsCfg =
{
100 /*! Measurement timer expiration period in ms */
};
/*! battery measurement configuration */
static const basCfg_t fitBasCfg =
{
30, /*! Battery measurement timer expiration period in seconds */
1, /*! Perform battery measurement after this many timer periods */
100 /*! Send battery level notification to peer when below this level. */
};
/*! SMP security parameter configuration */
static const smpCfg_t fitSmpCfg =
{
3000, /*! 'Repeated attempts' timeout in msec */
SMP_IO_NO_IN_NO_OUT, /*! I/O Capability */
7, /*! Minimum encryption key length */
16, /*! Maximum encryption key length */
3, /*! Attempts to trigger 'repeated attempts' timeout */
0, /*! Device authentication requirements */
64000, /*! Maximum repeated attempts timeout in msec */
64000, /*! Time msec before attemptExp decreases */
2 /*! Repeated attempts multiplier exponent */
};
/**************************************************************************************************
Advertising Data
**************************************************************************************************/
/*! advertising data, discoverable mode */
static const uint8_t fitAdvDataDisc[] =
{
/*! flags */
2, /*! length */
DM_ADV_TYPE_FLAGS, /*! AD type */
DM_FLAG_LE_GENERAL_DISC | /*! flags */
DM_FLAG_LE_BREDR_NOT_SUP,
/*! tx power */
2, /*! length */
DM_ADV_TYPE_TX_POWER, /*! AD type */
0, /*! tx power */
/*! service UUID list */
9, /*! length */
DM_ADV_TYPE_16_UUID, /*! AD type */
UINT16_TO_BYTES(ATT_UUID_HEART_RATE_SERVICE),
UINT16_TO_BYTES(ATT_UUID_RUNNING_SPEED_SERVICE),
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE),
UINT16_TO_BYTES(ATT_UUID_BATTERY_SERVICE)
};
/*! scan data, discoverable mode */
static const uint8_t fitScanDataDisc[] =
{
/*! device name */
4, /*! length */
DM_ADV_TYPE_LOCAL_NAME, /*! AD type */
'F',
'i',
't'
};
/**************************************************************************************************
Client Characteristic Configuration Descriptors
**************************************************************************************************/
/*! enumeration of client characteristic configuration descriptors */
enum
{
FIT_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
FIT_HRS_HRM_CCC_IDX, /*! Heart rate service, heart rate monitor characteristic */
FIT_BATT_LVL_CCC_IDX, /*! Battery service, battery level characteristic */
FIT_RSCS_SM_CCC_IDX, /*! Runninc speed and cadence measurement characteristic */
FIT_NUM_CCC_IDX
};
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
static const attsCccSet_t fitCccSet[FIT_NUM_CCC_IDX] =
{
/* cccd handle value range security level */
{GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE}, /* FIT_GATT_SC_CCC_IDX */
{HRS_HRM_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* FIT_HRS_HRM_CCC_IDX */
{BATT_LVL_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* FIT_BATT_LVL_CCC_IDX */
{RSCS_RSM_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE} /* FIT_RSCS_SM_CCC_IDX */
};
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! WSF handler ID */
wsfHandlerId_t fitHandlerId;
/* WSF Timer to send running speed and cadence measurement data */
wsfTimer_t fitRscmTimer;
/* Running Speed and Cadence Measurement period - Can be changed at runtime to vary period */
static uint16_t fitRscmPeriod = FIT_DEFAULT_RSCM_PERIOD;
/* Heart Rate Monitor feature flags */
static uint8_t fitHrmFlags = CH_HRM_FLAGS_VALUE_8BIT | CH_HRM_FLAGS_ENERGY_EXP;
/*************************************************************************************************/
/*!
* \brief Application DM callback.
*
* \param pDmEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void fitDmCback(dmEvt_t *pDmEvt)
{
dmEvt_t *pMsg;
uint16_t len;
len = DmSizeOfEvt(pDmEvt);
if ((pMsg = WsfMsgAlloc(len)) != NULL)
{
memcpy(pMsg, pDmEvt, len);
WsfMsgSend(fitHandlerId, pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Application ATT callback.
*
* \param pEvt ATT callback event
*
* \return None.
*/
/*************************************************************************************************/
static void fitAttCback(attEvt_t *pEvt)
{
attEvt_t *pMsg;
if ((pMsg = WsfMsgAlloc(sizeof(attEvt_t) + pEvt->valueLen)) != NULL)
{
memcpy(pMsg, pEvt, sizeof(attEvt_t));
pMsg->pValue = (uint8_t *) (pMsg + 1);
memcpy(pMsg->pValue, pEvt->pValue, pEvt->valueLen);
WsfMsgSend(fitHandlerId, pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Application ATTS client characteristic configuration callback.
*
* \param pDmEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void fitCccCback(attsCccEvt_t *pEvt)
{
attsCccEvt_t *pMsg;
appDbHdl_t dbHdl;
/* If CCC not set from initialization and there's a device record and currently bonded */
if ((pEvt->handle != ATT_HANDLE_NONE) &&
((dbHdl = AppDbGetHdl((dmConnId_t) pEvt->hdr.param)) != APP_DB_HDL_NONE) &&
AppCheckBonded((dmConnId_t)pEvt->hdr.param))
{
/* Store value in device database. */
AppDbSetCccTblValue(dbHdl, pEvt->idx, pEvt->value);
}
if ((pMsg = WsfMsgAlloc(sizeof(attsCccEvt_t))) != NULL)
{
memcpy(pMsg, pEvt, sizeof(attsCccEvt_t));
WsfMsgSend(fitHandlerId, pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Send a Running Speed and Cadence Measurement Notification.
*
* \param connId connection ID
*
* \return None.
*/
/*************************************************************************************************/
static void fitSendRunningSpeedMeasurement(dmConnId_t connId)
{
if (AttsCccEnabled(connId, FIT_RSCS_SM_CCC_IDX))
{
static uint8_t walk_run = 1;
/* TODO: Set Running Speed and Cadence Measurement Parameters */
RscpsSetParameter(RSCP_SM_PARAM_SPEED, 1);
RscpsSetParameter(RSCP_SM_PARAM_CADENCE, 2);
RscpsSetParameter(RSCP_SM_PARAM_STRIDE_LENGTH, 3);
RscpsSetParameter(RSCP_SM_PARAM_TOTAL_DISTANCE, 4);
/* Toggle running/walking */
walk_run = walk_run? 0 : 1;
RscpsSetParameter(RSCP_SM_PARAM_STATUS, walk_run);
RscpsSendSpeedMeasurement(connId);
}
/* Configure and start timer to send the next measurement */
fitRscmTimer.msg.event = FIT_RUNNING_TIMER_IND;
fitRscmTimer.msg.status = FIT_RSCS_SM_CCC_IDX;
fitRscmTimer.handlerId = fitHandlerId;
fitRscmTimer.msg.param = connId;
WsfTimerStartSec(&fitRscmTimer, fitRscmPeriod);
}
/*************************************************************************************************/
/*!
* \brief Process CCC state change.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void fitProcCccState(fitMsg_t *pMsg)
{
APP_TRACE_INFO3("ccc state ind value:%d handle:%d idx:%d", pMsg->ccc.value, pMsg->ccc.handle, pMsg->ccc.idx);
/* handle heart rate measurement CCC */
if (pMsg->ccc.idx == FIT_HRS_HRM_CCC_IDX)
{
if (pMsg->ccc.value == ATT_CLIENT_CFG_NOTIFY)
{
HrpsMeasStart((dmConnId_t) pMsg->ccc.hdr.param, FIT_HR_TIMER_IND, FIT_HRS_HRM_CCC_IDX);
}
else
{
HrpsMeasStop((dmConnId_t) pMsg->ccc.hdr.param);
}
return;
}
/* handle running speed and cadence measurement CCC */
if (pMsg->ccc.idx == FIT_RSCS_SM_CCC_IDX)
{
if (pMsg->ccc.value == ATT_CLIENT_CFG_NOTIFY)
{
fitSendRunningSpeedMeasurement((dmConnId_t)pMsg->ccc.hdr.param);
}
else
{
WsfTimerStop(&fitRscmTimer);
}
return;
}
/* handle battery level CCC */
if (pMsg->ccc.idx == FIT_BATT_LVL_CCC_IDX)
{
if (pMsg->ccc.value == ATT_CLIENT_CFG_NOTIFY)
{
BasMeasBattStart((dmConnId_t) pMsg->ccc.hdr.param, FIT_BATT_TIMER_IND, FIT_BATT_LVL_CCC_IDX);
}
else
{
BasMeasBattStop((dmConnId_t) pMsg->ccc.hdr.param);
}
return;
}
}
/*************************************************************************************************/
/*!
* \brief Perform UI actions on connection close.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void fitClose(fitMsg_t *pMsg)
{
/* stop heart rate measurement */
HrpsMeasStop((dmConnId_t) pMsg->hdr.param);
/* stop battery measurement */
BasMeasBattStop((dmConnId_t) pMsg->hdr.param);
/* Stop running speed and cadence timer */
WsfTimerStop(&fitRscmTimer);
}
/*************************************************************************************************/
/*!
* \brief Set up advertising and other procedures that need to be performed after
* device reset.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void fitSetup(fitMsg_t *pMsg)
{
/* set advertising and scan response data for discoverable mode */
AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(fitAdvDataDisc), (uint8_t *) fitAdvDataDisc);
AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(fitScanDataDisc), (uint8_t *) fitScanDataDisc);
/* set advertising and scan response data for connectable mode */
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, 0, NULL);
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, 0, NULL);
/* start advertising; automatically set connectable/discoverable mode and bondable mode */
AppAdvStart(APP_MODE_AUTO_INIT);
}
/*************************************************************************************************/
/*!
* \brief Button press callback.
*
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void fitBtnCback(uint8_t btn)
{
dmConnId_t connId;
static uint8_t heartRate = 78; /* for testing/demonstration */
/* button actions when connected */
if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE)
{
switch (btn)
{
case APP_UI_BTN_1_SHORT:
/* increment the heart rate */
AppHwHrmTest(++heartRate);
break;
case APP_UI_BTN_1_MED:
break;
case APP_UI_BTN_1_LONG:
AppConnClose(connId);
break;
case APP_UI_BTN_2_SHORT:
/* decrement the heart rate */
AppHwHrmTest(--heartRate);
break;
case APP_UI_BTN_2_MED:
/* Toggle HRM Sensor DET flags */
if (!(fitHrmFlags & (CH_HRM_FLAGS_SENSOR_DET | CH_HRM_FLAGS_SENSOR_NOT_DET)))
{
fitHrmFlags |= CH_HRM_FLAGS_SENSOR_DET;
}
else if (fitHrmFlags & CH_HRM_FLAGS_SENSOR_DET)
{
fitHrmFlags &= ~CH_HRM_FLAGS_SENSOR_DET;
fitHrmFlags |= CH_HRM_FLAGS_SENSOR_NOT_DET;
}
else
{
fitHrmFlags &= ~CH_HRM_FLAGS_SENSOR_NOT_DET;
}
HrpsSetFlags(fitHrmFlags);
break;
case APP_UI_BTN_2_LONG:
/* Toggle HRM RR Interval feature flag */
if (fitHrmFlags & CH_HRM_FLAGS_RR_INTERVAL)
{
fitHrmFlags &= ~CH_HRM_FLAGS_RR_INTERVAL;
}
else
{
fitHrmFlags |= CH_HRM_FLAGS_RR_INTERVAL;
}
HrpsSetFlags(fitHrmFlags);
break;
default:
break;
}
}
/* button actions when not connected */
else
{
switch (btn)
{
case APP_UI_BTN_1_SHORT:
/* start or restart advertising */
AppAdvStart(APP_MODE_AUTO_INIT);
break;
case APP_UI_BTN_1_MED:
/* enter discoverable and bondable mode mode */
AppSetBondable(TRUE);
AppAdvStart(APP_MODE_DISCOVERABLE);
break;
case APP_UI_BTN_1_LONG:
/* clear all bonding info */
AppSlaveClearAllBondingInfo();
/* restart advertising */
AppAdvStart(APP_MODE_AUTO_INIT);
break;
case APP_UI_BTN_2_SHORT:
/* Toggle HRM Flag for 8 and 16 bit values */
if (fitHrmFlags & CH_HRM_FLAGS_VALUE_16BIT)
{
fitHrmFlags &= ~CH_HRM_FLAGS_VALUE_16BIT;
}
else
{
fitHrmFlags |= CH_HRM_FLAGS_VALUE_16BIT;
}
HrpsSetFlags(fitHrmFlags);
break;
default:
break;
}
}
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void fitProcMsg(fitMsg_t *pMsg)
{
uint8_t uiEvent = APP_UI_NONE;
switch(pMsg->hdr.event)
{
case FIT_RUNNING_TIMER_IND:
fitSendRunningSpeedMeasurement((dmConnId_t)pMsg->ccc.hdr.param);
break;
case FIT_HR_TIMER_IND:
HrpsProcMsg(&pMsg->hdr);
break;
case FIT_BATT_TIMER_IND:
BasProcMsg(&pMsg->hdr);
break;
case ATTS_HANDLE_VALUE_CNF:
HrpsProcMsg(&pMsg->hdr);
BasProcMsg(&pMsg->hdr);
break;
case ATTS_CCC_STATE_IND:
fitProcCccState(pMsg);
break;
case ATT_MTU_UPDATE_IND:
APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu);
break;
case DM_RESET_CMPL_IND:
AttsCalculateDbHash();
DmSecGenerateEccKeyReq();
fitSetup(pMsg);
uiEvent = APP_UI_RESET_CMPL;
break;
case DM_ADV_SET_START_IND:
uiEvent = APP_UI_ADV_SET_START_IND;
break;
case DM_ADV_SET_STOP_IND:
uiEvent = APP_UI_ADV_SET_STOP_IND;
break;
case DM_ADV_START_IND:
uiEvent = APP_UI_ADV_START;
break;
case DM_ADV_STOP_IND:
uiEvent = APP_UI_ADV_STOP;
break;
case DM_CONN_OPEN_IND:
HrpsProcMsg(&pMsg->hdr);
BasProcMsg(&pMsg->hdr);
// AppSlaveSecurityReq(1);
uiEvent = APP_UI_CONN_OPEN;
break;
case DM_CONN_CLOSE_IND:
fitClose(pMsg);
uiEvent = APP_UI_CONN_CLOSE;
break;
case DM_SEC_PAIR_CMPL_IND:
DmSecGenerateEccKeyReq();
uiEvent = APP_UI_SEC_PAIR_CMPL;
break;
case DM_SEC_PAIR_FAIL_IND:
DmSecGenerateEccKeyReq();
uiEvent = APP_UI_SEC_PAIR_FAIL;
break;
case DM_SEC_ENCRYPT_IND:
uiEvent = APP_UI_SEC_ENCRYPT;
break;
case DM_SEC_ENCRYPT_FAIL_IND:
uiEvent = APP_UI_SEC_ENCRYPT_FAIL;
break;
case DM_SEC_AUTH_REQ_IND:
AppHandlePasskey(&pMsg->dm.authReq);
break;
case DM_SEC_ECC_KEY_IND:
DmSecSetEccKey(&pMsg->dm.eccMsg.data.key);
break;
case DM_SEC_COMPARE_IND:
AppHandleNumericComparison(&pMsg->dm.cnfInd);
break;
case DM_PRIV_CLEAR_RES_LIST_IND:
APP_TRACE_INFO1("Clear resolving list status 0x%02x", pMsg->hdr.status);
break;
case DM_HW_ERROR_IND:
uiEvent = APP_UI_HW_ERROR;
break;
case DM_VENDOR_SPEC_CMD_CMPL_IND:
{
#if defined(AM_PART_APOLLO) || defined(AM_PART_APOLLO2)
uint8_t *param_ptr = &pMsg->dm.vendorSpecCmdCmpl.param[0];
switch (pMsg->dm.vendorSpecCmdCmpl.opcode)
{
case 0xFC20: //read at address
{
uint32_t read_value;
BSTREAM_TO_UINT32(read_value, param_ptr);
APP_TRACE_INFO3("VSC 0x%0x complete status %x param %x",
pMsg->dm.vendorSpecCmdCmpl.opcode,
pMsg->hdr.status,
read_value);
}
break;
default:
APP_TRACE_INFO2("VSC 0x%0x complete status %x",
pMsg->dm.vendorSpecCmdCmpl.opcode,
pMsg->hdr.status);
break;
}
#endif
}
break;
default:
break;
}
if (uiEvent != APP_UI_NONE)
{
AppUiAction(uiEvent);
}
}
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID.
*
* \return None.
*/
/*************************************************************************************************/
void FitHandlerInit(wsfHandlerId_t handlerId)
{
APP_TRACE_INFO0("FitHandlerInit");
/* store handler ID */
fitHandlerId = handlerId;
/* Set configuration pointers */
pAppAdvCfg = (appAdvCfg_t *) &fitAdvCfg;
pAppSlaveCfg = (appSlaveCfg_t *) &fitSlaveCfg;
pAppSecCfg = (appSecCfg_t *) &fitSecCfg;
pAppUpdateCfg = (appUpdateCfg_t *) &fitUpdateCfg;
/* Initialize application framework */
AppSlaveInit();
AppServerInit();
/* Set stack configuration pointers */
pSmpCfg = (smpCfg_t *) &fitSmpCfg;
/* initialize heart rate profile sensor */
HrpsInit(handlerId, (hrpsCfg_t *) &fitHrpsCfg);
HrpsSetFlags(fitHrmFlags);
/* initialize battery service server */
BasInit(handlerId, (basCfg_t *) &fitBasCfg);
}
/*************************************************************************************************/
/*!
* \brief WSF event handler for application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void FitHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
{
if (pMsg != NULL)
{
APP_TRACE_INFO1("Fit got evt %d", pMsg->event);
/* process ATT messages */
if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
{
/* process server-related ATT messages */
AppServerProcAttMsg(pMsg);
}
/* process DM messages */
else if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
{
/* process advertising and connection-related messages */
AppSlaveProcDmMsg((dmEvt_t *) pMsg);
/* process security-related messages */
AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
}
/* perform profile and user interface-related operations */
fitProcMsg((fitMsg_t *) pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void FitStart(void)
{
/* Register for stack callbacks */
DmRegister(fitDmCback);
DmConnRegister(DM_CLIENT_ID_APP, fitDmCback);
AttRegister(fitAttCback);
AttConnRegister(AppServerConnCback);
AttsCccRegister(FIT_NUM_CCC_IDX, (attsCccSet_t *) fitCccSet, fitCccCback);
/* Register for app framework callbacks */
AppUiBtnRegister(fitBtnCback);
/* Initialize attribute server database */
SvcCoreGattCbackRegister(GattReadCback, GattWriteCback);
SvcCoreAddGroup();
SvcHrsCbackRegister(NULL, HrpsWriteCback);
SvcHrsAddGroup();
SvcDisAddGroup();
SvcBattCbackRegister(BasReadCback, NULL);
SvcBattAddGroup();
SvcRscsAddGroup();
/* Set Service Changed CCCD index. */
GattSetSvcChangedIdx(FIT_GATT_SC_CCC_IDX);
/* Set running speed and cadence features */
RscpsSetFeatures(RSCS_ALL_FEATURES);
/* Reset the device */
DmDevReset();
}
@@ -0,0 +1,73 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Glucose sensor sample application interface.
*
* Copyright (c) 2012-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef GLUC_API_H
#define GLUC_API_H
#include "wsf_os.h"
#ifdef __cplusplus
extern "C" {
#endif
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void GlucStart(void);
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID for App.
*
* \return None.
*/
/*************************************************************************************************/
void GlucHandlerInit(wsfHandlerId_t handlerId);
/*************************************************************************************************/
/*!
* \brief WSF event handler for the application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void GlucHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
#ifdef __cplusplus
};
#endif
#endif /* GLUC_API_H */
@@ -0,0 +1,538 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Glucose sensor sample application.
*
* Copyright (c) 2012-2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "hci_api.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_api.h"
#include "app_db.h"
#include "app_ui.h"
#include "app_hw.h"
#include "app_main.h"
#include "svc_ch.h"
#include "svc_core.h"
#include "svc_gls.h"
#include "svc_dis.h"
#include "gatt/gatt_api.h"
#include "glps/glps_api.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! enumeration of client characteristic configuration descriptors */
enum
{
GLUC_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
GLUC_GLS_GLM_CCC_IDX, /*! Glucose service, glucose measurement characteristic */
GLUC_GLS_GLMC_CCC_IDX, /*! Glucose service, glucose measurement context characteristic */
GLUC_GLS_RACP_CCC_IDX, /*! Glucose service, record access control point characteristic */
GLUC_NUM_CCC_IDX
};
/**************************************************************************************************
Configurable Parameters
**************************************************************************************************/
/*! configurable parameters for advertising */
static const appAdvCfg_t glucAdvCfg =
{
{30000, 30000, 0}, /*! Advertising durations in ms */
{ 48, 1600, 0} /*! Advertising intervals in 0.625 ms units */
};
/*! configurable parameters for slave */
static const appSlaveCfg_t glucSlaveCfg =
{
1, /*! Maximum connections */
};
/*! configurable parameters for security */
static const appSecCfg_t glucSecCfg =
{
DM_AUTH_BOND_FLAG, /*! Authentication and bonding flags */
0, /*! Initiator key distribution flags */
DM_KEY_DIST_LTK, /*! Responder key distribution flags */
FALSE, /*! TRUE if Out-of-band pairing data is present */
TRUE /*! TRUE to initiate security upon connection */
};
/*! configurable parameters for connection parameter update */
static const appUpdateCfg_t glucUpdateCfg =
{
0, /*! Connection idle period in ms before attempting
connection parameter update; set to zero to disable */
640, /*! Minimum connection interval in 1.25ms units */
800, /*! Maximum connection interval in 1.25ms units */
0, /*! Connection latency */
600, /*! Supervision timeout in 10ms units */
5 /*! Number of update attempts before giving up */
};
/**************************************************************************************************
Advertising Data
**************************************************************************************************/
/*! advertising data, discoverable mode */
static const uint8_t glucAdvDataDisc[] =
{
/*! flags */
2, /*! length */
DM_ADV_TYPE_FLAGS, /*! AD type */
DM_FLAG_LE_LIMITED_DISC | /*! flags */
DM_FLAG_LE_BREDR_NOT_SUP,
/*! service UUID list */
5, /*! length */
DM_ADV_TYPE_16_UUID, /*! AD type */
UINT16_TO_BYTES(ATT_UUID_GLUCOSE_SERVICE),
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE)
};
/*! scan data, discoverable mode */
static const uint8_t glucScanDataDisc[] =
{
/*! device name */
12, /*! length */
DM_ADV_TYPE_LOCAL_NAME, /*! AD type */
'G',
'l',
'u',
'c',
' ',
'S',
'e',
'n',
's',
'o',
'r'
};
/**************************************************************************************************
Client Characteristic Configuration Descriptors
**************************************************************************************************/
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
static const attsCccSet_t glucCccSet[GLUC_NUM_CCC_IDX] =
{
/* cccd handle value range security level */
{GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_ENC}, /* GLUC_GATT_SC_CCC_IDX */
{GLS_GLM_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_ENC}, /* GLUC_GLS_GLM_CCC_IDX */
{GLS_GLMC_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_ENC}, /* GLUC_GLS_GLMC_CCC_IDX */
{GLS_RACP_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_ENC} /* GLUC_GLS_RACP_CCC_IDX */
};
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! application control block */
static struct
{
wsfHandlerId_t handlerId;
} glucCb;
/*************************************************************************************************/
/*!
* \brief Application DM callback.
*
* \param pDmEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void glucDmCback(dmEvt_t *pDmEvt)
{
dmEvt_t *pMsg;
uint16_t len;
len = DmSizeOfEvt(pDmEvt);
if ((pMsg = WsfMsgAlloc(len)) != NULL)
{
memcpy(pMsg, pDmEvt, len);
WsfMsgSend(glucCb.handlerId, pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Application ATT callback.
*
* \param pEvt ATT callback event
*
* \return None.
*/
/*************************************************************************************************/
static void glucAttCback(attEvt_t *pEvt)
{
attEvt_t *pMsg;
if ((pMsg = WsfMsgAlloc(sizeof(attEvt_t) + pEvt->valueLen)) != NULL)
{
memcpy(pMsg, pEvt, sizeof(attEvt_t));
pMsg->pValue = (uint8_t *) (pMsg + 1);
memcpy(pMsg->pValue, pEvt->pValue, pEvt->valueLen);
WsfMsgSend(glucCb.handlerId, pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Application ATTS client characteristic configuration callback.
*
* \param pDmEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void glucCccCback(attsCccEvt_t *pEvt)
{
appDbHdl_t dbHdl;
/* If CCC not set from initialization and there's a device record and currently bonded */
if ((pEvt->handle != ATT_HANDLE_NONE) &&
((dbHdl = AppDbGetHdl((dmConnId_t) pEvt->hdr.param)) != APP_DB_HDL_NONE) &&
AppCheckBonded((dmConnId_t)pEvt->hdr.param))
{
/* Store value in device database. */
AppDbSetCccTblValue(dbHdl, pEvt->idx, pEvt->value);
}
}
/*************************************************************************************************/
/*!
* \brief Set up advertising and other procedures that need to be performed after
* device reset.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void glucSetup(dmEvt_t *pMsg)
{
/* set advertising and scan response data for discoverable mode */
AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(glucAdvDataDisc), (uint8_t *) glucAdvDataDisc);
AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(glucScanDataDisc), (uint8_t *) glucScanDataDisc);
/* set advertising and scan response data for connectable mode */
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, 0, NULL);
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, 0, NULL);
/* start advertising; automatically set connectable/discoverable mode and bondable mode */
AppAdvStart(APP_MODE_AUTO_INIT);
}
/*************************************************************************************************/
/*!
* \brief Button press callback.
*
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void glucBtnCback(uint8_t btn)
{
dmConnId_t connId;
/* button actions when connected */
if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE)
{
switch (btn)
{
case APP_UI_BTN_1_SHORT:
break;
case APP_UI_BTN_1_MED:
break;
case APP_UI_BTN_1_LONG:
AppConnClose(connId);
break;
case APP_UI_BTN_2_SHORT:
break;
default:
/* all other button presses-- send to profile */
GlpsBtn(connId, btn);
break;
}
}
/* button actions when not connected */
else
{
switch (btn)
{
case APP_UI_BTN_1_SHORT:
/* start or restart advertising */
AppAdvStart(APP_MODE_AUTO_INIT);
break;
case APP_UI_BTN_1_MED:
/* enter discoverable and bondable mode mode */
AppSetBondable(TRUE);
AppAdvStart(APP_MODE_DISCOVERABLE);
break;
case APP_UI_BTN_1_LONG:
/* clear all bonding info */
AppSlaveClearAllBondingInfo();
/* restart advertising */
AppAdvStart(APP_MODE_AUTO_INIT);
break;
default:
/* all other button presses-- send to profile */
GlpsBtn(connId, btn);
break;
}
}
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void glucProcMsg(dmEvt_t *pMsg)
{
uint8_t uiEvent = APP_UI_NONE;
switch(pMsg->hdr.event)
{
case ATTS_HANDLE_VALUE_CNF:
GlpsProcMsg(&pMsg->hdr);
break;
case ATT_MTU_UPDATE_IND:
APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu);
break;
case DM_RESET_CMPL_IND:
AttsCalculateDbHash();
glucSetup(pMsg);
uiEvent = APP_UI_RESET_CMPL;
break;
case DM_ADV_START_IND:
uiEvent = APP_UI_ADV_START;
break;
case DM_ADV_STOP_IND:
uiEvent = APP_UI_ADV_STOP;
break;
case DM_CONN_OPEN_IND:
GlpsProcMsg(&pMsg->hdr);
uiEvent = APP_UI_CONN_OPEN;
break;
case DM_CONN_CLOSE_IND:
GlpsProcMsg(&pMsg->hdr);
uiEvent = APP_UI_CONN_CLOSE;
break;
case DM_SEC_PAIR_CMPL_IND:
uiEvent = APP_UI_SEC_PAIR_CMPL;
break;
case DM_SEC_PAIR_FAIL_IND:
uiEvent = APP_UI_SEC_PAIR_FAIL;
break;
case DM_SEC_ENCRYPT_IND:
uiEvent = APP_UI_SEC_ENCRYPT;
break;
case DM_SEC_ENCRYPT_FAIL_IND:
uiEvent = APP_UI_SEC_ENCRYPT_FAIL;
break;
case DM_SEC_AUTH_REQ_IND:
AppHandlePasskey(&pMsg->authReq);
break;
case DM_PRIV_CLEAR_RES_LIST_IND:
APP_TRACE_INFO1("Clear resolving list status 0x%02x", pMsg->hdr.status);
break;
case DM_VENDOR_SPEC_CMD_CMPL_IND:
{
#if defined(AM_PART_APOLLO) || defined(AM_PART_APOLLO2)
uint8_t *param_ptr = &pMsg->vendorSpecCmdCmpl.param[0];
switch (pMsg->vendorSpecCmdCmpl.opcode)
{
case 0xFC20: //read at address
{
uint32_t read_value;
BSTREAM_TO_UINT32(read_value, param_ptr);
APP_TRACE_INFO3("VSC 0x%0x complete status %x param %x",
pMsg->vendorSpecCmdCmpl.opcode,
pMsg->hdr.status,
read_value);
}
break;
default:
APP_TRACE_INFO2("VSC 0x%0x complete status %x",
pMsg->vendorSpecCmdCmpl.opcode,
pMsg->hdr.status);
break;
}
#endif
}
break;
default:
break;
}
if (uiEvent != APP_UI_NONE)
{
AppUiAction(uiEvent);
}
}
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID.
*
* \return None.
*/
/*************************************************************************************************/
void GlucHandlerInit(wsfHandlerId_t handlerId)
{
APP_TRACE_INFO0("GlucHandlerInit");
/* store handler ID */
glucCb.handlerId = handlerId;
/* Set configuration pointers */
pAppSlaveCfg = (appSlaveCfg_t *) &glucSlaveCfg;
pAppAdvCfg = (appAdvCfg_t *) &glucAdvCfg;
pAppSecCfg = (appSecCfg_t *) &glucSecCfg;
pAppUpdateCfg = (appUpdateCfg_t *) &glucUpdateCfg;
/* Initialize application framework */
AppSlaveInit();
AppServerInit();
/* initialize glucose profile sensor */
GlpsInit();
GlpsSetCccIdx(GLUC_GLS_GLM_CCC_IDX, GLUC_GLS_GLMC_CCC_IDX, GLUC_GLS_RACP_CCC_IDX);
}
/*************************************************************************************************/
/*!
* \brief WSF event handler for application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void GlucHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
{
if (pMsg != NULL)
{
APP_TRACE_INFO1("Gluc got evt %d", pMsg->event);
/* process ATT messages */
if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
{
/* process server-related ATT messages */
AppServerProcAttMsg(pMsg);
}
/* process DM messages */
else if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
{
/* process advertising and connection-related messages */
AppSlaveProcDmMsg((dmEvt_t *) pMsg);
/* process security-related messages */
AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
}
/* perform profile and user interface-related operations */
glucProcMsg((dmEvt_t *) pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void GlucStart(void)
{
/* Register for stack callbacks */
DmRegister(glucDmCback);
DmConnRegister(DM_CLIENT_ID_APP, glucDmCback);
AttRegister(glucAttCback);
AttConnRegister(AppServerConnCback);
AttsCccRegister(GLUC_NUM_CCC_IDX, (attsCccSet_t *) glucCccSet, glucCccCback);
/* Register for app framework callbacks */
AppUiBtnRegister(glucBtnCback);
/* Initialize attribute server database */
SvcCoreGattCbackRegister(GattReadCback, GattWriteCback);
SvcCoreAddGroup();
SvcGlsAddGroup();
SvcGlsCbackRegister(NULL, GlpsRacpWriteCback);
SvcDisAddGroup();
/* Set Service Changed CCCD index. */
GattSetSvcChangedIdx(GLUC_GATT_SC_CCC_IDX);
/* Set supported features after starting database */
GlpsSetFeature(CH_GLF_LOW_BATT | CH_GLF_MALFUNC | CH_GLF_SAMPLE_SIZE | CH_GLF_INSERT_ERR |
CH_GLF_TYPE_ERR | CH_GLF_RES_HIGH_LOW | CH_GLF_TEMP_HIGH_LOW | CH_GLF_READ_INT |
CH_GLF_GENERAL_FAULT);
/* Reset the device */
DmDevReset();
}
@@ -0,0 +1,71 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief HidApp sample application.
*
* Copyright (c) 2015-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef HIDAPP_API_H
#define HIDAPP_API_H
#ifdef __cplusplus
extern "C" {
#endif
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void HidAppStart(void);
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID for App.
*
* \return None.
*/
/*************************************************************************************************/
void HidAppHandlerInit(wsfHandlerId_t handlerId);
/*************************************************************************************************/
/*!
* \brief WSF event handler for the application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void HidAppHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
#ifdef __cplusplus
};
#endif
#endif /* HIDAPP_API_H */
@@ -0,0 +1,73 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Proprietary asset tracking locator sample application.
*
* Copyright (c) 2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef LOCATOR_API_H
#define LOCATOR_API_H
#include "wsf_os.h"
#ifdef __cplusplus
extern "C" {
#endif
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void LocatorStart(void);
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID for App.
*
* \return None.
*/
/*************************************************************************************************/
void LocatorHandlerInit(wsfHandlerId_t handlerId);
/*************************************************************************************************/
/*!
* \brief WSF event handler for the application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void LocatorHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
#ifdef __cplusplus
};
#endif
#endif /* LOCATOR_API_H */
@@ -0,0 +1,102 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector sample application.
*
* Copyright (c) 2012-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef MEDC_API_H
#define MEDC_API_H
#include "wsf_os.h"
#ifdef __cplusplus
extern "C" {
#endif
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! Profile identifier used for MedcSetProfile() */
enum
{
MEDC_ID_HRP, /*! Heart rate profile */
MEDC_ID_BLP, /*! Blood pressure profile */
MEDC_ID_GLP, /*! Glucose profile */
MEDC_ID_WSP, /*! Weight scale profile */
MEDC_ID_HTP, /*! Health thermometer profile */
MEDC_ID_PLX /*! Pulse oximeter profile */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void MedcStart(void);
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID for App.
*
* \return None.
*/
/*************************************************************************************************/
void MedcHandlerInit(wsfHandlerId_t handlerId);
/*************************************************************************************************/
/*!
* \brief WSF event handler for the application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void MedcHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
/*************************************************************************************************/
/*!
* \brief Set the profile to be used by the application. This function is called internally
* by MedcHandlerInit() with a default value. It may also be called by the system
* to configure the profile after executing MedcHandlerInit() and before executing
* MedcStart().
*
* \param profile Profile identifier.
*
* \return None.
*/
/*************************************************************************************************/
void MedcSetProfile(uint8_t profile);
#ifdef __cplusplus
};
#endif
#endif /* MEDC_API_H */
@@ -0,0 +1,220 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector, Blood Pressure profile
*
* Copyright (c) 2012-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_cfg.h"
#include "app_api.h"
#include "app_db.h"
#include "svc_ch.h"
#include "blpc/blpc_api.h"
#include "medc/medc_main.h"
/**************************************************************************************************
ATT Client Discovery Data
**************************************************************************************************/
/* Start of cached blood pressure service handles; begins after DIS */
#define MEDC_DISC_BPS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
/* Total cached handle list length */
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_BPS_START + BLPC_BPS_HDL_LIST_LEN)
/*! Pointers into handle list for blood pressure service handles */
static uint16_t *pMedcBpsHdlList = &medcCb.hdlList[MEDC_DISC_BPS_START];
/* sanity check: make sure handle list length is <= app db handle list length */
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
/**************************************************************************************************
ATT Client Configuration Data
**************************************************************************************************/
/* List of characteristics to configure after service discovery */
static const attcDiscCfg_t medcCfgBpsList[] =
{
/* Read: Blood pressure feature */
{NULL, 0, BLPC_BPS_BPF_HDL_IDX},
/* Write: Blood pressure measurement CCC descriptor */
{medcCccIndVal, sizeof(medcCccIndVal), BLPC_BPS_BPM_CCC_HDL_IDX},
/* Write: Intermediate cuff pressure CCC descriptor */
{medcCccNtfVal, sizeof(medcCccNtfVal), BLPC_BPS_ICP_CCC_HDL_IDX},
};
/* Characteristic configuration list length */
#define MEDC_CFG_BPS_LIST_LEN (sizeof(medcCfgBpsList) / sizeof(attcDiscCfg_t))
/**************************************************************************************************
Local Functions
**************************************************************************************************/
static void medcBlpInit(void);
static bool_t medcBlpDiscover(dmConnId_t connId);
static void medcBlpConfigure(dmConnId_t connId, uint8_t status);
static void medcBlpProcMsg(wsfMsgHdr_t *pMsg);
static void medcBlpBtn(dmConnId_t connId, uint8_t btn);
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! profile interface pointer */
medcIf_t medcBlpIf =
{
medcBlpInit,
medcBlpDiscover,
medcBlpConfigure,
medcBlpProcMsg,
medcBlpBtn
};
/*************************************************************************************************/
/*!
* \brief Process a received ATT read response, notification, or indication.
*
* \param pMsg Pointer to ATT callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcBpsValueUpdate(attEvt_t *pMsg)
{
if (pMsg->hdr.status == ATT_SUCCESS)
{
/* determine which profile the handle belongs to; start with most likely */
/* blood pressure */
if (BlpcBpsValueUpdate(pMedcBpsHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* device information */
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* GATT */
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
}
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcBlpProcMsg(wsfMsgHdr_t *pMsg)
{
switch(pMsg->event)
{
case ATTC_READ_RSP:
case ATTC_HANDLE_VALUE_NTF:
case ATTC_HANDLE_VALUE_IND:
medcBpsValueUpdate((attEvt_t *) pMsg);
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Profile initialization function.
*
* \return None.
*/
/*************************************************************************************************/
static void medcBlpInit(void)
{
/* set handle list length */
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
/* set autoconnect UUID */
medcCb.autoUuid[0] = ATT_UUID_BLOOD_PRESSURE_SERVICE;
}
/*************************************************************************************************/
/*!
* \brief Discover service for profile.
*
* \param connId Connection identifier.
*
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
*/
/*************************************************************************************************/
static bool_t medcBlpDiscover(dmConnId_t connId)
{
/* discover blood pressure service */
BlpcBpsDiscover(connId, pMedcBpsHdlList);
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Configure service for profile.
*
* \param connId Connection identifier.
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
*
* \return None.
*/
/*************************************************************************************************/
static void medcBlpConfigure(dmConnId_t connId, uint8_t status)
{
/* configure blood pressure service */
AppDiscConfigure(connId, status, MEDC_CFG_BPS_LIST_LEN,
(attcDiscCfg_t *) medcCfgBpsList,
BLPC_BPS_HDL_LIST_LEN, pMedcBpsHdlList);
}
/*************************************************************************************************/
/*!
* \brief Handle a button press.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medcBlpBtn(dmConnId_t connId, uint8_t btn)
{
return;
}
@@ -0,0 +1,344 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector, glucose profile
*
* Copyright (c) 2012-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_cfg.h"
#include "app_api.h"
#include "app_db.h"
#include "app_ui.h"
#include "svc_ch.h"
#include "glpc/glpc_api.h"
#include "medc/medc_main.h"
/**************************************************************************************************
ATT Client Discovery Data
**************************************************************************************************/
/* Start of cached glucose service handles; begins after DIS */
#define MEDC_DISC_GLS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
/* Total cached handle list length */
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_GLS_START + GLPC_GLS_HDL_LIST_LEN)
/*! Pointers into handle list for glucose service handles */
static uint16_t *pMedcGlsHdlList = &medcCb.hdlList[MEDC_DISC_GLS_START];
/* sanity check: make sure handle list length is <= app db handle list length */
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
/**************************************************************************************************
ATT Client Configuration Data
**************************************************************************************************/
/* List of characteristics to configure after service discovery */
static const attcDiscCfg_t medcCfgGlsList[] =
{
/* Read: glucose feature */
{NULL, 0, GLPC_GLS_GLF_HDL_IDX},
/* Write: glucose measurement CCC descriptor */
{medcCccNtfVal, sizeof(medcCccIndVal), GLPC_GLS_GLM_CCC_HDL_IDX},
/* Write: glucose measurement context CCC descriptor */
{medcCccNtfVal, sizeof(medcCccIndVal), GLPC_GLS_GLMC_CCC_HDL_IDX},
/* Write: record access control point CCC descriptor */
{medcCccIndVal, sizeof(medcCccIndVal), GLPC_GLS_RACP_CCC_HDL_IDX}
};
/* Characteristic configuration list length */
#define MEDC_CFG_GLS_LIST_LEN (sizeof(medcCfgGlsList) / sizeof(attcDiscCfg_t))
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/* Control block */
static struct
{
wsfTimer_t racpTimer; /*! RACP procedure timer */
bool_t inProgress; /*! RACP procedure in progress */
} medcGlpCb;
/**************************************************************************************************
Local Functions
**************************************************************************************************/
static void medcGlpInit(void);
static bool_t medcGlpDiscover(dmConnId_t connId);
static void medcGlpConfigure(dmConnId_t connId, uint8_t status);
static void medcGlpProcMsg(wsfMsgHdr_t *pMsg);
static void medcGlpBtn(dmConnId_t connId, uint8_t btn);
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! profile interface pointer */
medcIf_t medcGlpIf =
{
medcGlpInit,
medcGlpDiscover,
medcGlpConfigure,
medcGlpProcMsg,
medcGlpBtn
};
/*************************************************************************************************/
/*!
* \brief Process a received ATT read response, notification, or indication.
*
* \param pMsg Pointer to ATT callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcGlsValueUpdate(attEvt_t *pMsg)
{
if (pMsg->hdr.status == ATT_SUCCESS)
{
/* determine which profile the handle belongs to; start with most likely */
/* glucose */
if (GlpcGlsValueUpdate(pMedcGlsHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* device information */
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* GATT */
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
}
}
/*************************************************************************************************/
/*!
* \brief Profile initialization function.
*
* \return None.
*/
/*************************************************************************************************/
static void medcGlpInit(void)
{
/* set handle list length */
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
/* set autoconnect UUID */
medcCb.autoUuid[0] = ATT_UUID_GLUCOSE_SERVICE;
/* initialize timer */
medcGlpCb.racpTimer.handlerId = medcCb.handlerId;
medcGlpCb.racpTimer.msg.event = MEDC_TIMER_IND;
/* initialize sequence number */
GlpcGlsSetLastSeqNum(1);
}
/*************************************************************************************************/
/*!
* \brief Discover service for profile.
*
* \param connId Connection identifier.
*
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
*/
/*************************************************************************************************/
static bool_t medcGlpDiscover(dmConnId_t connId)
{
/* discover glucose service */
GlpcGlsDiscover(connId, pMedcGlsHdlList);
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Configure service for profile.
*
* \param connId Connection identifier.
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
*
* \return None.
*/
/*************************************************************************************************/
static void medcGlpConfigure(dmConnId_t connId, uint8_t status)
{
/* configure glucose service */
AppDiscConfigure(connId, status, MEDC_CFG_GLS_LIST_LEN,
(attcDiscCfg_t *) medcCfgGlsList,
GLPC_GLS_HDL_LIST_LEN, pMedcGlsHdlList);
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcGlpProcMsg(wsfMsgHdr_t *pMsg)
{
switch(pMsg->event)
{
case ATTC_READ_RSP:
case ATTC_HANDLE_VALUE_NTF:
/* process value update */
medcGlsValueUpdate((attEvt_t *) pMsg);
break;
case ATTC_WRITE_RSP:
/* if write to RACP was successful, start procedure timer */
if ((((attEvt_t *) pMsg)->hdr.status == ATT_SUCCESS) &&
(((attEvt_t *) pMsg)->handle == pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX]))
{
medcGlpCb.inProgress = TRUE;
medcGlpCb.racpTimer.msg.param = pMsg->param; /* conn ID */
WsfTimerStartSec(&medcGlpCb.racpTimer, ATT_MAX_TRANS_TIMEOUT);
}
break;
case ATTC_HANDLE_VALUE_IND:
/* if procedure in progress stop procedure timer */
if (medcGlpCb.inProgress)
{
medcGlpCb.inProgress = FALSE;
WsfTimerStop(&medcGlpCb.racpTimer);
}
/* process value */
medcGlsValueUpdate((attEvt_t *) pMsg);
break;
case DM_CONN_CLOSE_IND:
/* if procedure in progress stop procedure timer */
if (medcGlpCb.inProgress)
{
medcGlpCb.inProgress = FALSE;
WsfTimerStop(&medcGlpCb.racpTimer);
}
break;
case MEDC_TIMER_IND:
/* if procedure in progress then close connection */
if (medcGlpCb.inProgress && pMsg->param != DM_CONN_ID_NONE)
{
medcGlpCb.inProgress = FALSE;
/* if configured to disconnect upon ATT transaction timeout */
if (pAppCfg->disconnect)
{
AppConnClose((dmConnId_t)pMsg->param);
}
}
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Handle a button press.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medcGlpBtn(dmConnId_t connId, uint8_t btn)
{
glpcFilter_t filter;
/* button actions when connected */
if (connId != DM_CONN_ID_NONE)
{
/* handle must be set to send RACP command */
if (pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX] == ATT_HANDLE_NONE)
{
return;
}
switch (btn)
{
case APP_UI_BTN_1_SHORT:
/* report all records */
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
CH_RACP_OPCODE_REPORT, CH_RACP_OPERATOR_ALL, NULL);
break;
case APP_UI_BTN_1_MED:
/* report records greater than sequence number */
filter.type = CH_RACP_GLS_FILTER_SEQ;
filter.param.seqNum = GlpcGlsGetLastSeqNum();
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
CH_RACP_OPCODE_REPORT, CH_RACP_OPERATOR_GTEQ, &filter);
break;
case APP_UI_BTN_2_SHORT:
/* report number of records */
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
CH_RACP_OPCODE_REPORT_NUM, CH_RACP_OPERATOR_ALL, NULL);
break;
case APP_UI_BTN_2_MED:
/* report number of records greater than sequence number */
filter.type = CH_RACP_GLS_FILTER_SEQ;
filter.param.seqNum = GlpcGlsGetLastSeqNum();
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
CH_RACP_OPCODE_REPORT_NUM, CH_RACP_OPERATOR_GTEQ, &filter);
break;
case APP_UI_BTN_2_LONG:
/* abort */
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
CH_RACP_OPCODE_ABORT, CH_RACP_OPERATOR_NULL, NULL);
break;
case APP_UI_BTN_2_EX_LONG:
/* delete all records */
GlpcGlsRacpSend(connId, pMedcGlsHdlList[GLPC_GLS_RACP_HDL_IDX],
CH_RACP_OPCODE_DELETE, CH_RACP_OPERATOR_ALL, NULL);
break;
default:
break;
}
}
}
@@ -0,0 +1,223 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector, Heart Rate profile
*
* Copyright (c) 2012-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_cfg.h"
#include "app_api.h"
#include "app_db.h"
#include "svc_ch.h"
#include "hrpc/hrpc_api.h"
#include "medc/medc_main.h"
/**************************************************************************************************
ATT Client Discovery Data
**************************************************************************************************/
/* Start of cached heart rate service handles; begins after DIS */
#define MEDC_DISC_HRS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
/* Total cached handle list length */
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_HRS_START + HRPC_HRS_HDL_LIST_LEN)
/*! Pointers into handle list heart rate service handles */
static uint16_t *pMedcHrsHdlList = &medcCb.hdlList[MEDC_DISC_HRS_START];
/* sanity check: make sure handle list length is <= app db handle list length */
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
/**************************************************************************************************
ATT Client Configuration Data
**************************************************************************************************/
/* HRS Control point "Reset Energy Expended" */
static const uint8_t medcHrsRstEnExp[] = {CH_HRCP_RESET_ENERGY_EXP};
/* List of characteristics to configure after service discovery */
static const attcDiscCfg_t medcCfgHrsList[] =
{
/* Read: HRS Body sensor location */
{NULL, 0, HRPC_HRS_BSL_HDL_IDX},
/* Write: HRS Control point "Reset Energy Expended" */
{medcHrsRstEnExp, sizeof(medcHrsRstEnExp), HRPC_HRS_HRCP_HDL_IDX},
/* Write: HRS Heart rate measurement CCC descriptor */
{medcCccNtfVal, sizeof(medcCccNtfVal), HRPC_HRS_HRM_CCC_HDL_IDX},
};
/* Characteristic configuration list length */
#define MEDC_CFG_HRS_LIST_LEN (sizeof(medcCfgHrsList) / sizeof(attcDiscCfg_t))
/**************************************************************************************************
Local Functions
**************************************************************************************************/
static void medcHrpInit(void);
static bool_t medcHrpDiscover(dmConnId_t connId);
static void medcHrpConfigure(dmConnId_t connId, uint8_t status);
static void medcHrpProcMsg(wsfMsgHdr_t *pMsg);
static void medcHrpBtn(dmConnId_t connId, uint8_t btn);
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! profile interface pointer */
medcIf_t medcHrpIf =
{
medcHrpInit,
medcHrpDiscover,
medcHrpConfigure,
medcHrpProcMsg,
medcHrpBtn
};
/*************************************************************************************************/
/*!
* \brief Process a received ATT read response, notification, or indication.
*
* \param pMsg Pointer to ATT callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHrsValueUpdate(attEvt_t *pMsg)
{
if (pMsg->hdr.status == ATT_SUCCESS)
{
/* determine which profile the handle belongs to; start with most likely */
/* heart rate */
if (HrpcHrsValueUpdate(pMedcHrsHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* device information */
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* GATT */
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
}
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHrpProcMsg(wsfMsgHdr_t *pMsg)
{
switch(pMsg->event)
{
case ATTC_READ_RSP:
case ATTC_HANDLE_VALUE_NTF:
case ATTC_HANDLE_VALUE_IND:
medcHrsValueUpdate((attEvt_t *) pMsg);
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Profile initialization function.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHrpInit(void)
{
/* set handle list length */
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
/* set autoconnect UUID */
medcCb.autoUuid[0] = ATT_UUID_HEART_RATE_SERVICE;
}
/*************************************************************************************************/
/*!
* \brief Discover service for profile.
*
* \param connId Connection identifier.
*
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
*/
/*************************************************************************************************/
static bool_t medcHrpDiscover(dmConnId_t connId)
{
/* discover heart rate service */
HrpcHrsDiscover(connId, pMedcHrsHdlList);
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Configure service for profile.
*
* \param connId Connection identifier.
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHrpConfigure(dmConnId_t connId, uint8_t status)
{
/* configure heart rate service */
AppDiscConfigure(connId, status, MEDC_CFG_HRS_LIST_LEN,
(attcDiscCfg_t *) medcCfgHrsList,
HRPC_HRS_HDL_LIST_LEN, pMedcHrsHdlList);
}
/*************************************************************************************************/
/*!
* \brief Handle a button press.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHrpBtn(dmConnId_t connId, uint8_t btn)
{
return;
}
@@ -0,0 +1,220 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector, Health Thermometer profile
*
* Copyright (c) 2012-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_cfg.h"
#include "app_api.h"
#include "app_db.h"
#include "svc_ch.h"
#include "htpc/htpc_api.h"
#include "medc/medc_main.h"
/**************************************************************************************************
ATT Client Discovery Data
**************************************************************************************************/
/* Start of cached health thermometer service handles; begins after DIS */
#define MEDC_DISC_HTS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
/* Total cached handle list length */
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_HTS_START + HTPC_HTS_HDL_LIST_LEN)
/*! Pointers into handle list for health thermometer service handles */
static uint16_t *pMedcHtsHdlList = &medcCb.hdlList[MEDC_DISC_HTS_START];
/* sanity check: make sure handle list length is <= app db handle list length */
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
/**************************************************************************************************
ATT Client Configuration Data
**************************************************************************************************/
/* List of characteristics to configure after service discovery */
static const attcDiscCfg_t medcCfgHtsList[] =
{
/* Read: Temperature type */
{NULL, 0, HTPC_HTS_TT_HDL_IDX},
/* Write: Temperature measurement CCC descriptor */
{medcCccIndVal, sizeof(medcCccIndVal), HTPC_HTS_TM_CCC_HDL_IDX},
/* Write: Intermediate temperature CCC descriptor */
{medcCccNtfVal, sizeof(medcCccNtfVal), HTPC_HTS_IT_CCC_HDL_IDX},
};
/* Characteristic configuration list length */
#define MEDC_CFG_HTS_LIST_LEN (sizeof(medcCfgHtsList) / sizeof(attcDiscCfg_t))
/**************************************************************************************************
Local Functions
**************************************************************************************************/
static void medcHtpInit(void);
static bool_t medcHtpDiscover(dmConnId_t connId);
static void medcHtpConfigure(dmConnId_t connId, uint8_t status);
static void medcHtpProcMsg(wsfMsgHdr_t *pMsg);
static void medcHtpBtn(dmConnId_t connId, uint8_t btn);
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! profile interface pointer */
medcIf_t medcHtpIf =
{
medcHtpInit,
medcHtpDiscover,
medcHtpConfigure,
medcHtpProcMsg,
medcHtpBtn
};
/*************************************************************************************************/
/*!
* \brief Process a received ATT read response, notification, or indication.
*
* \param pMsg Pointer to ATT callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHtsValueUpdate(attEvt_t *pMsg)
{
if (pMsg->hdr.status == ATT_SUCCESS)
{
/* determine which profile the handle belongs to; start with most likely */
/* health thermometer */
if (HtpcHtsValueUpdate(pMedcHtsHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* device information */
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* GATT */
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
}
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHtpProcMsg(wsfMsgHdr_t *pMsg)
{
switch(pMsg->event)
{
case ATTC_READ_RSP:
case ATTC_HANDLE_VALUE_NTF:
case ATTC_HANDLE_VALUE_IND:
medcHtsValueUpdate((attEvt_t *) pMsg);
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Profile initialization function.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHtpInit(void)
{
/* set handle list length */
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
/* set autoconnect UUID */
medcCb.autoUuid[0] = ATT_UUID_HEALTH_THERM_SERVICE;
}
/*************************************************************************************************/
/*!
* \brief Discover service for profile.
*
* \param connId Connection identifier.
*
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
*/
/*************************************************************************************************/
static bool_t medcHtpDiscover(dmConnId_t connId)
{
/* discover health thermometer service */
HtpcHtsDiscover(connId, pMedcHtsHdlList);
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Configure service for profile.
*
* \param connId Connection identifier.
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHtpConfigure(dmConnId_t connId, uint8_t status)
{
/* configure health thermometer service */
AppDiscConfigure(connId, status, MEDC_CFG_HTS_LIST_LEN,
(attcDiscCfg_t *) medcCfgHtsList,
HTPC_HTS_HDL_LIST_LEN, pMedcHtsHdlList);
}
/*************************************************************************************************/
/*!
* \brief Handle a button press.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medcHtpBtn(dmConnId_t connId, uint8_t btn)
{
return;
}
@@ -0,0 +1,940 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector sample application for the following profiles:
* Heart Rate profile collector
* Blood Pressure profile collector
* Glucose profile collector
* Weight scale profile collector
* Health Thermometer profile collector
* Pulse Oximeter profile collector
*
* Copyright (c) 2012-2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "hci_api.h"
#include "dm_api.h"
#include "att_api.h"
#include "smp_api.h"
#include "app_cfg.h"
#include "app_api.h"
#include "app_db.h"
#include "app_ui.h"
#include "svc_ch.h"
#include "svc_core.h"
#include "gatt/gatt_api.h"
#include "dis/dis_api.h"
#include "medc/medc_api.h"
#include "medc/medc_main.h"
/**************************************************************************************************
Profile Configuration
**************************************************************************************************/
/* Heart rate profile included */
#ifndef MEDC_HRP_INCLUDED
#define MEDC_HRP_INCLUDED TRUE
#endif
/* Blood pressure profile included */
#ifndef MEDC_BLP_INCLUDED
#define MEDC_BLP_INCLUDED TRUE
#endif
/* Glucose profile included */
#ifndef MEDC_GLP_INCLUDED
#define MEDC_GLP_INCLUDED TRUE
#endif
/* Weight scale profile included */
#ifndef MEDC_WSP_INCLUDED
#define MEDC_WSP_INCLUDED TRUE
#endif
/* Health thermometer profile included */
#ifndef MEDC_HTP_INCLUDED
#define MEDC_HTP_INCLUDED TRUE
#endif
/* Pulse oximeter profile included */
#ifndef MEDC_PLX_INCLUDED
#define MEDC_PLX_INCLUDED TRUE
#endif
/* Default profile to use */
#ifndef MEDC_PROFILE
#define MEDC_PROFILE MEDC_ID_HRP
#endif
/**************************************************************************************************
Configurable Parameters
**************************************************************************************************/
/*! configurable parameters for master */
const appMasterCfg_t medcMasterCfg =
{
96, /*! The scan interval, in 0.625 ms units */
48, /*! The scan window, in 0.625 ms units */
4000, /*! The scan duration in ms */
DM_DISC_MODE_NONE, /*! The GAP discovery mode */
DM_SCAN_TYPE_ACTIVE /*! The scan type (active or passive) */
};
/*! configurable parameters for security */
static const appSecCfg_t medcSecCfg =
{
DM_AUTH_BOND_FLAG, /*! Authentication and bonding flags */
0, /*! Initiator key distribution flags */
DM_KEY_DIST_LTK, /*! Responder key distribution flags */
FALSE, /*! TRUE if Out-of-band pairing data is present */
FALSE /*! TRUE to initiate security upon connection */
};
/*! SMP security parameter configuration */
static const smpCfg_t medcSmpCfg =
{
500, /*! 'Repeated attempts' timeout in msec */
SMP_IO_DISP_ONLY, /*! I/O Capability */
7, /*! Minimum encryption key length */
16, /*! Maximum encryption key length */
1, /*! Attempts to trigger 'repeated attempts' timeout */
0, /*! Device authentication requirements */
64000, /*! Maximum repeated attempts timeout in msec */
64000, /*! Time msec before attemptExp decreases */
2 /*! Repeated attempts multiplier exponent */
};
/*! Connection parameters */
static const hciConnSpec_t medcConnCfg =
{
40, /*! Minimum connection interval in 1.25ms units */
40, /*! Maximum connection interval in 1.25ms units */
0, /*! Connection latency */
600, /*! Supervision timeout in 10ms units */
0, /*! Unused */
0 /*! Unused */
};
/*! Configurable parameters for service and characteristic discovery */
static const appDiscCfg_t medcDiscCfg =
{
FALSE, /*! TRUE to wait for a secure connection before initiating discovery */
FALSE /*! TRUE to fall back on database hash to verify handles when no bond exists. */
};
static const appCfg_t medcAppCfg =
{
TRUE, /*! TRUE to abort service discovery if service not found */
TRUE /*! TRUE to disconnect if ATT transaction times out */
};
/**************************************************************************************************
ATT Client Discovery Data
**************************************************************************************************/
/*! Discovery states: enumeration of services to be discovered */
enum
{
MEDC_DISC_GATT_SVC, /*! GATT service */
MEDC_DISC_DIS_SVC, /*! Device Information service */
MEDC_DISC_MED_SVC, /*! Configured med. service */
MEDC_DISC_SVC_MAX /*! Discovery complete */
};
/*! Pointers into handle list for each service's handles */
uint16_t *pMedcGattHdlList = &medcCb.hdlList[MEDC_DISC_GATT_START];
uint16_t *pMedcDisHdlList = &medcCb.hdlList[MEDC_DISC_DIS_START];
/**************************************************************************************************
ATT Client Configuration Data
**************************************************************************************************/
/*! Configuration states: enumeration of services to be discovered */
enum
{
MEDC_CFG_DIS_SVC, /*! DIS services */
MEDC_CFG_GATT_SVC, /*! GATT services */
MEDC_CFG_MED_SVC, /*! Configured med. service */
MEDC_CFG_SVC_MAX /*! Configuration complete */
};
/*
* Data for configuration after service discovery
*/
/* Default value for CCC indications */
const uint8_t medcCccIndVal[2] = {UINT16_TO_BYTES(ATT_CLIENT_CFG_INDICATE)};
/* Default value for CCC notifications */
const uint8_t medcCccNtfVal[2] = {UINT16_TO_BYTES(ATT_CLIENT_CFG_NOTIFY)};
/* Default value for Client Supported Features (enable Robust Caching) */
const uint8_t medcCsfVal[1] = {ATTS_CSF_ROBUST_CACHING};
/* HRS Control point "Reset Energy Expended" */
/* static const uint8_t medcHrsRstEnExp[] = {CH_HRCP_RESET_ENERGY_EXP}; */\
/* List of DIS characteristics to configure after service discovery */
static const attcDiscCfg_t medcCfgDisList[] =
{
/* Read: DIS Manufacturer name string */
{NULL, 0, DIS_MFNS_HDL_IDX},
/* Read: DIS Model number string */
{NULL, 0, DIS_MNS_HDL_IDX},
/* Read: DIS Serial number string */
{NULL, 0, DIS_SNS_HDL_IDX},
/* Read: DIS Hardware revision string */
{NULL, 0, DIS_HRS_HDL_IDX},
/* Read: DIS Firmware revision string */
{NULL, 0, DIS_FRS_HDL_IDX},
/* Read: DIS Software revision string */
{NULL, 0, DIS_SRS_HDL_IDX},
/* Read: DIS System ID */
{NULL, 0, DIS_SID_HDL_IDX}
};
/* List of GATT characteristics to configure after service discovery */
static const attcDiscCfg_t medcCfgGattList[] =
{
/* Write: GATT service changed ccc descriptor */
{medcCccIndVal, sizeof(medcCccIndVal), GATT_SC_CCC_HDL_IDX},
/* Write: GATT client supported features */
{medcCsfVal, sizeof(medcCsfVal), GATT_CSF_HDL_IDX},
};
/* Characteristic configuration list length */
#define MEDC_CFG_GATT_LIST_LEN (sizeof(medcCfgGattList) / sizeof(attcDiscCfg_t))
#define MEDC_CFG_DIS_LIST_LEN (sizeof(medcCfgDisList) / sizeof(attcDiscCfg_t))
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! application control block */
medcCb_t medcCb;
/*! connection control block */
typedef struct {
appDbHdl_t dbHdl; /*! Device database record handle type */
uint8_t addrType; /*! Type of address of device to connect to */
bdAddr_t addr; /*! Address of device to connect to */
bool_t doConnect; /*! TRUE to issue connect on scan complete */
} medcConnInfo_t;
medcConnInfo_t medcConnInfo;
/*************************************************************************************************/
/*!
* \brief Application DM callback.
*
* \param pDmEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void medcDmCback(dmEvt_t *pDmEvt)
{
dmEvt_t *pMsg;
uint16_t len;
uint16_t reportLen;
len = DmSizeOfEvt(pDmEvt);
if (pDmEvt->hdr.event == DM_SCAN_REPORT_IND)
{
reportLen = pDmEvt->scanReport.len;
}
else
{
reportLen = 0;
}
if ((pMsg = WsfMsgAlloc(len + reportLen)) != NULL)
{
memcpy(pMsg, pDmEvt, len);
if (pDmEvt->hdr.event == DM_SCAN_REPORT_IND)
{
pMsg->scanReport.pData = (uint8_t *) ((uint8_t *) pMsg + len);
memcpy(pMsg->scanReport.pData, pDmEvt->scanReport.pData, reportLen);
}
WsfMsgSend(medcCb.handlerId, pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Application ATT callback.
*
* \param pEvt ATT callback event
*
* \return None.
*/
/*************************************************************************************************/
static void medcAttCback(attEvt_t *pEvt)
{
attEvt_t *pMsg;
if ((pMsg = WsfMsgAlloc(sizeof(attEvt_t) + pEvt->valueLen)) != NULL)
{
memcpy(pMsg, pEvt, sizeof(attEvt_t));
pMsg->pValue = (uint8_t *) (pMsg + 1);
memcpy(pMsg->pValue, pEvt->pValue, pEvt->valueLen);
WsfMsgSend(medcCb.handlerId, pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Perform actions on scan start.
*
* \param pMsg Pointer to DM callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcScanStart(dmEvt_t *pMsg)
{
if (pMsg->hdr.status == HCI_SUCCESS)
{
medcCb.scanning = TRUE;
}
}
/*************************************************************************************************/
/*!
* \brief Perform actions on scan stop.
*
* \param pMsg Pointer to DM callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcScanStop(dmEvt_t *pMsg)
{
if (pMsg->hdr.status == HCI_SUCCESS)
{
medcCb.scanning = FALSE;
medcCb.autoConnect = FALSE;
/* Open connection */
if (medcConnInfo.doConnect)
{
APP_TRACE_INFO0("medcScanStop: Opening Connection");
AppConnOpen(medcConnInfo.addrType, medcConnInfo.addr, medcConnInfo.dbHdl);
medcConnInfo.doConnect = FALSE;
}
}
}
/*************************************************************************************************/
/*!
* \brief Handle a scan report.
*
* \param pMsg Pointer to DM callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcScanReport(dmEvt_t *pMsg)
{
uint8_t *pData;
uint8_t len;
appDbHdl_t dbHdl;
bool_t connect = FALSE;
/* disregard if not scanning or autoconnecting */
if (!medcCb.scanning || !medcCb.autoConnect)
{
return;
}
/* if we already have a bond with this device then connect to it */
if ((dbHdl = AppDbFindByAddr(pMsg->scanReport.addrType, pMsg->scanReport.addr)) != APP_DB_HDL_NONE)
{
connect = TRUE;
}
/* otherwise look for desired service in advertising data */
else
{
/* find Service UUID list; if full list not found search for partial */
if ((pData = DmFindAdType(DM_ADV_TYPE_16_UUID, pMsg->scanReport.len,
pMsg->scanReport.pData)) == NULL)
{
pData = DmFindAdType(DM_ADV_TYPE_16_UUID_PART, pMsg->scanReport.len,
pMsg->scanReport.pData);
}
/* if found and length checks out ok */
if (pData != NULL && pData[DM_AD_LEN_IDX] >= (ATT_16_UUID_LEN + 1))
{
len = pData[DM_AD_LEN_IDX] - 1;
pData += DM_AD_DATA_IDX;
while ((!connect) && (len >= ATT_16_UUID_LEN))
{
int8_t i;
for (i=0; i < MEDC_MAX_AUTO_UUID; i++)
{
if (medcCb.autoUuid[i] == 0)
{
continue;
}
if (BYTES_UINT16_CMP(pData, medcCb.autoUuid[i]))
{
connect = TRUE;
break;
}
}
pData += ATT_16_UUID_LEN;
len -= ATT_16_UUID_LEN;
}
}
}
if (connect)
{
APP_TRACE_INFO0("medcScanReport: Peer found.");
/* stop scanning and connect */
medcCb.autoConnect = FALSE;
AppScanStop();
/* Store peer information for connect on scan stop */
medcConnInfo.addrType = pMsg->scanReport.addrType;
memcpy(medcConnInfo.addr, pMsg->scanReport.addr, sizeof(bdAddr_t));
medcConnInfo.dbHdl = dbHdl;
medcConnInfo.doConnect = TRUE;
}
}
/*************************************************************************************************/
/*!
* \brief Perform UI actions on connection open.
*
* \param pMsg Pointer to DM callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcOpen(dmEvt_t *pMsg)
{
}
/*************************************************************************************************/
/*!
* \brief Set up procedures that need to be performed after device reset.
*
* \param pMsg Pointer to DM callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcSetup(dmEvt_t *pMsg)
{
medcCb.scanning = FALSE;
medcCb.autoConnect = FALSE;
medcConnInfo.doConnect = FALSE;
DmConnSetConnSpec((hciConnSpec_t *) &medcConnCfg);
}
/*************************************************************************************************/
/*!
* \brief Button press callback.
*
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medcBtnCback(uint8_t btn)
{
dmConnId_t connId;
/* button actions when connected */
if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE)
{
switch (btn)
{
case APP_UI_BTN_1_LONG:
/* disconnect */
AppConnClose(connId);
break;
default:
/* all other button presses-- send to profile */
medcCb.pIf->btn(connId, btn);
break;
}
}
/* button actions when not connected */
else
{
switch (btn)
{
case APP_UI_BTN_1_SHORT:
/* if scanning cancel scanning */
if (medcCb.scanning)
{
AppScanStop();
}
/* else auto connect */
else if (!medcCb.autoConnect)
{
medcCb.autoConnect = TRUE;
medcConnInfo.doConnect = FALSE;
AppScanStart(medcMasterCfg.discMode, medcMasterCfg.scanType,
medcMasterCfg.scanDuration);
}
break;
case APP_UI_BTN_1_LONG:
/* clear all bonding info */
AppClearAllBondingInfo();
break;
default:
/* all other button presses-- send to profile */
medcCb.pIf->btn(connId, btn);
break;
}
}
}
/*************************************************************************************************/
/*!
* \brief Discovery callback.
*
* \param connId Connection identifier.
* \param status Service or configuration status.
*
* \return None.
*/
/*************************************************************************************************/
static void medcDiscCback(dmConnId_t connId, uint8_t status)
{
switch(status)
{
case APP_DISC_INIT:
/* set handle list when initialization requested */
AppDiscSetHdlList(connId, medcCb.hdlListLen, medcCb.hdlList);
break;
case APP_DISC_READ_DATABASE_HASH:
/* Read peer's database hash */
AppDiscReadDatabaseHash(connId);
break;
case APP_DISC_SEC_REQUIRED:
/* initiate security */
AppMasterSecurityReq(connId);
break;
case APP_DISC_START:
/* initialize discovery state */
medcCb.discState = MEDC_DISC_GATT_SVC;
/* discover GATT service */
GattDiscover(connId, pMedcGattHdlList);
break;
case APP_DISC_FAILED:
if (pAppCfg->abortDisc)
{
/* if discovery failed for desired service then disconnect */
if (medcCb.discState == MEDC_DISC_MED_SVC)
{
AppConnClose(connId);
break;
}
}
/* Else falls through. */
case APP_DISC_CMPL:
/* next discovery state */
medcCb.discState++;
if (medcCb.discState == MEDC_DISC_DIS_SVC)
{
/* discover device information service */
DisDiscover(connId, pMedcDisHdlList);
}
else if (medcCb.discState == MEDC_DISC_MED_SVC)
{
/* discover med profile service */
if (medcCb.pIf->discover(connId) == FALSE)
{
/* There are other profiles to discover. Stay in the MEDC_DISC_MED_SVC state */
medcCb.discState = MEDC_DISC_MED_SVC - 1;
}
}
else
{
/* discovery complete */
AppDiscComplete(connId, APP_DISC_CMPL);
/* start configuration: configure DIS service */
medcCb.cfgState = MEDC_CFG_DIS_SVC;
AppDiscConfigure(connId, APP_DISC_CFG_START, MEDC_CFG_DIS_LIST_LEN,
(attcDiscCfg_t *) medcCfgDisList,
DIS_HDL_LIST_LEN, pMedcDisHdlList);
}
break;
case APP_DISC_CFG_START:
/* start configuration: configure DIS service */
medcCb.cfgState = MEDC_CFG_DIS_SVC;
AppDiscConfigure(connId, APP_DISC_CFG_START, MEDC_CFG_DIS_LIST_LEN,
(attcDiscCfg_t *) medcCfgDisList,
DIS_HDL_LIST_LEN, pMedcDisHdlList);
break;
case APP_DISC_CFG_CMPL:
/* next configuration state */
medcCb.cfgState++;
if (medcCb.cfgState == MEDC_CFG_GATT_SVC)
{
/* configure GATT */
AppDiscConfigure(connId, APP_DISC_CFG_START, MEDC_CFG_GATT_LIST_LEN,
(attcDiscCfg_t *) medcCfgGattList,
GATT_HDL_LIST_LEN, pMedcGattHdlList);
}
else if (medcCb.cfgState == MEDC_CFG_MED_SVC)
{
/* configure med profile service */
medcCb.pIf->configure(connId, APP_DISC_CFG_START);
}
else
{
AppDiscComplete(connId, status);
}
break;
case APP_DISC_CFG_CONN_START:
/* no connection setup configuration */
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcProcMsg(dmEvt_t *pMsg)
{
uint8_t uiEvent = APP_UI_NONE;
switch(pMsg->hdr.event)
{
case ATTC_READ_RSP:
case ATTC_WRITE_RSP:
case ATTC_HANDLE_VALUE_NTF:
case ATTC_HANDLE_VALUE_IND:
case MEDC_TIMER_IND:
medcCb.pIf->procMsg(&pMsg->hdr);
break;
case ATT_MTU_UPDATE_IND:
APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu);
break;
case DM_RESET_CMPL_IND:
AttsCalculateDbHash();
medcSetup(pMsg);
uiEvent = APP_UI_RESET_CMPL;
break;
case DM_SCAN_START_IND:
medcScanStart(pMsg);
uiEvent = APP_UI_SCAN_START;
break;
case DM_SCAN_STOP_IND:
medcScanStop(pMsg);
uiEvent = APP_UI_SCAN_STOP;
break;
case DM_SCAN_REPORT_IND:
medcScanReport(pMsg);
break;
case DM_CONN_OPEN_IND:
medcOpen(pMsg);
uiEvent = APP_UI_CONN_OPEN;
break;
case DM_CONN_CLOSE_IND:
medcCb.pIf->procMsg(&pMsg->hdr);
uiEvent = APP_UI_CONN_CLOSE;
break;
case DM_SEC_PAIR_CMPL_IND:
uiEvent = APP_UI_SEC_PAIR_CMPL;
break;
case DM_SEC_PAIR_FAIL_IND:
uiEvent = APP_UI_SEC_PAIR_FAIL;
break;
case DM_SEC_ENCRYPT_IND:
medcCb.pIf->procMsg(&pMsg->hdr);
uiEvent = APP_UI_SEC_ENCRYPT;
break;
case DM_SEC_ENCRYPT_FAIL_IND:
medcCb.pIf->procMsg(&pMsg->hdr);
uiEvent = APP_UI_SEC_ENCRYPT_FAIL;
break;
case DM_SEC_AUTH_REQ_IND:
AppHandlePasskey(&pMsg->authReq);
break;
case DM_PRIV_CLEAR_RES_LIST_IND:
APP_TRACE_INFO1("Clear resolving list status 0x%02x", pMsg->hdr.status);
break;
case DM_VENDOR_SPEC_CMD_CMPL_IND:
{
#if defined(AM_PART_APOLLO) || defined(AM_PART_APOLLO2)
uint8_t *param_ptr = &pMsg->vendorSpecCmdCmpl.param[0];
switch (pMsg->vendorSpecCmdCmpl.opcode)
{
case 0xFC20: //read at address
{
uint32_t read_value;
BSTREAM_TO_UINT32(read_value, param_ptr);
APP_TRACE_INFO3("VSC 0x%0x complete status %x param %x",
pMsg->vendorSpecCmdCmpl.opcode,
pMsg->hdr.status,
read_value);
}
break;
default:
APP_TRACE_INFO2("VSC 0x%0x complete status %x",
pMsg->vendorSpecCmdCmpl.opcode,
pMsg->hdr.status);
break;
}
#endif
}
break;
default:
break;
}
if (uiEvent != APP_UI_NONE)
{
AppUiAction(uiEvent);
}
}
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID.
*
* \return None.
*/
/*************************************************************************************************/
void MedcHandlerInit(wsfHandlerId_t handlerId)
{
APP_TRACE_INFO0("MedcHandlerInit");
/* store handler ID */
medcCb.handlerId = handlerId;
/* Set configuration pointers */
pAppMasterCfg = (appMasterCfg_t *) &medcMasterCfg;
pAppSecCfg = (appSecCfg_t *) &medcSecCfg;
pAppDiscCfg = (appDiscCfg_t *) &medcDiscCfg;
pAppCfg = (appCfg_t *) &medcAppCfg;
/* Set stack configuration pointers */
pSmpCfg = (smpCfg_t *) &medcSmpCfg;
/* Initialize application framework */
AppMasterInit();
AppDiscInit();
/* Set default profile to use */
MedcSetProfile(MEDC_PROFILE);
}
/*************************************************************************************************/
/*!
* \brief WSF event handler for application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void MedcHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
{
if (pMsg != NULL)
{
APP_TRACE_INFO1("Medc got evt %d", pMsg->event);
/* process ATT messages */
if (pMsg->event <= ATT_CBACK_END)
{
/* process discovery-related ATT messages */
AppDiscProcAttMsg((attEvt_t *) pMsg);
/* process server-related ATT messages */
AppServerProcAttMsg(pMsg);
}
/* process DM messages */
else if (pMsg->event <= DM_CBACK_END)
{
/* process advertising and connection-related messages */
AppMasterProcDmMsg((dmEvt_t *) pMsg);
/* process security-related messages */
AppMasterSecProcDmMsg((dmEvt_t *) pMsg);
/* process discovery-related messages */
AppDiscProcDmMsg((dmEvt_t *) pMsg);
}
/* perform profile and user interface-related operations */
medcProcMsg((dmEvt_t *) pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void MedcStart(void)
{
/* Register for stack callbacks */
DmRegister(medcDmCback);
DmConnRegister(DM_CLIENT_ID_APP, medcDmCback);
AttRegister(medcAttCback);
/* Register for app framework button callbacks */
AppUiBtnRegister(medcBtnCback);
/* Initialize attribute server database */
SvcCoreAddGroup();
/* Register for app framework discovery callbacks */
AppDiscRegister(medcDiscCback);
/* Reset the device */
DmDevReset();
}
/*************************************************************************************************/
/*!
* \brief Set the profile to be used by the application. This function is called internally
* by MedcHandlerInit() with a default value. It may also be called by the system
* to configure the profile after executing MedcHandlerInit() and before executing
* MedcStart().
*
* \param profile Profile identifier.
*
* \return None.
*/
/*************************************************************************************************/
void MedcSetProfile(uint8_t profile)
{
switch (profile)
{
#if MEDC_HRP_INCLUDED == TRUE
case MEDC_ID_HRP:
medcCb.pIf = &medcHrpIf;
medcCb.pIf->init();
break;
#endif
#if MEDC_BLP_INCLUDED == TRUE
case MEDC_ID_BLP:
medcCb.pIf = &medcBlpIf;
medcCb.pIf->init();
break;
#endif
#if MEDC_GLP_INCLUDED == TRUE
case MEDC_ID_GLP:
medcCb.pIf = &medcGlpIf;
medcCb.pIf->init();
break;
#endif
#if MEDC_WSP_INCLUDED == TRUE
case MEDC_ID_WSP:
medcCb.pIf = &medcWspIf;
medcCb.pIf->init();
break;
#endif
#if MEDC_HTP_INCLUDED == TRUE
case MEDC_ID_HTP:
medcCb.pIf = &medcHtpIf;
medcCb.pIf->init();
break;
#endif
#if MEDC_PLX_INCLUDED == TRUE
case MEDC_ID_PLX:
medcCb.pIf = &medcPlxpIf;
medcCb.pIf->init();
break;
#endif
default:
APP_TRACE_WARN1("MedcSetProfile invalid profile:%d", profile);
break;
}
}
@@ -0,0 +1,138 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector sample application interface file.
*
* Copyright (c) 2012-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef MEDC_MAIN_H
#define MEDC_MAIN_H
#include "gatt/gatt_api.h"
#include "dis/dis_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! the Client handle list, medcCb.hdlList[], is set as follows:
*
* ------------------------------- <- MEDC_DISC_GATT_START
* | GATT handles |
* |... |
* ------------------------------- <- MEDC_DISC_DIS_START
* | DIS handles |
* | ... |
* ------------------------------- <- Configured med service start
* | Med service handles |
* | ... |
* -------------------------------
*/
/*! Maximum number of Service UUID for autoconnect */
#define MEDC_MAX_AUTO_UUID 2
/*! Start of each service's handles in the the handle list */
#define MEDC_DISC_GATT_START 0
#define MEDC_DISC_DIS_START (MEDC_DISC_GATT_START + GATT_HDL_LIST_LEN)
/*! WSF message event starting value */
#define MEDC_MSG_START 0xA0
/*! WSF message event enumeration */
enum
{
MEDC_TIMER_IND = MEDC_MSG_START, /*! Timer expired */
};
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! profile interface callback functions */
typedef void (*medcInitCback_t)(void);
typedef bool_t (*medcDiscoverCback_t)(dmConnId_t connId);
typedef void (*medcConfigureCback_t)(dmConnId_t connId, uint8_t status);
typedef void (*medcProcMsgCback_t)(wsfMsgHdr_t *pMsg);
typedef void (*medcBtnCback_t)(dmConnId_t connId, uint8_t btn);
/*! profile interface structure */
typedef struct
{
medcInitCback_t init;
medcDiscoverCback_t discover;
medcConfigureCback_t configure;
medcProcMsgCback_t procMsg;
medcBtnCback_t btn;
} medcIf_t;
/*! application control block */
typedef struct
{
uint16_t hdlList[APP_DB_HDL_LIST_LEN]; /*! Cached handle list */
medcIf_t *pIf; /*! Profile interface */
wsfHandlerId_t handlerId; /*! WSF hander ID */
uint16_t autoUuid[MEDC_MAX_AUTO_UUID]; /*! Service UUID for autoconnect */
bool_t scanning; /*! TRUE if scanning */
bool_t autoConnect; /*! TRUE if auto-connecting */
uint8_t hdlListLen; /*! Cached handle list length */
uint8_t discState; /*! Service discovery state */
uint8_t cfgState; /*! Service configuration state */
} medcCb_t;
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! Default value for CCC indications and notifications */
extern const uint8_t medcCccIndVal[2];
/*! Default value for CCC notifications */
extern const uint8_t medcCccNtfVal[2];
/*! Pointers into handle list for GATT and DIS service handles */
extern uint16_t *pMedcGattHdlList;
extern uint16_t *pMedcDisHdlList;
/*! application control block */
extern medcCb_t medcCb;
/*! profile interface pointers */
extern medcIf_t medcHrpIf; /* heart rate profile */
extern medcIf_t medcBlpIf; /* blood pressure profile */
extern medcIf_t medcGlpIf; /* glucose profile */
extern medcIf_t medcWspIf; /* weight scale profile */
extern medcIf_t medcHtpIf; /* health thermometer profile */
extern medcIf_t medcPlxpIf; /* pulse oximeter profile */
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
#ifdef __cplusplus
};
#endif
#endif /* MEDC_MAIN_H */
@@ -0,0 +1,381 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector, Pulse Oximeter profile
*
* Copyright (c) 2016-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_cfg.h"
#include "app_api.h"
#include "app_db.h"
#include "app_ui.h"
#include "app_main.h"
#include "svc_ch.h"
#include "plxpc/plxpc_api.h"
#include "medc/medc_main.h"
/**************************************************************************************************
ATT Client Discovery Data
**************************************************************************************************/
/* Start of cached health pulse oximeter service handles; begins after DIS */
#define MEDC_DISC_PLXS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
/* Total cached handle list length */
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_PLXS_START + PLXPC_PLXS_HDL_LIST_LEN)
/*! Pointers into handle list for health pulse oximeter service handles */
static uint16_t *pMedcPlxsHdlList = &medcCb.hdlList[MEDC_DISC_PLXS_START];
/* sanity check: make sure handle list length is <= app db handle list length */
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
/* Default MTU */
#define MEDC_PLX_DEFAULT_MTU 50
/**************************************************************************************************
Configurable Parameters
**************************************************************************************************/
/*! ATT configurable parameters (increase MTU) */
static const attCfg_t medcPlxpAttCfg =
{
15, /* ATT server service discovery connection idle timeout in seconds */
MEDC_PLX_DEFAULT_MTU, /* desired ATT MTU */
ATT_MAX_TRANS_TIMEOUT, /* transcation timeout in seconds */
4 /* number of queued prepare writes supported by server */
};
/**************************************************************************************************
ATT Client Configuration Data
**************************************************************************************************/
/* List of characteristics to configure after service discovery */
static const attcDiscCfg_t medcCfgPlxsList[] =
{
/* Read: Features */
{NULL, 0, PLXPC_PLXS_PLXF_HDL_IDX},
/* Write: Spot Check CCC descriptor */
{medcCccIndVal, sizeof(medcCccIndVal), PLXPC_PLXS_PLXSC_CCC_HDL_IDX},
/* Write: Continuous Measurement CCC descriptor */
{medcCccNtfVal, sizeof(medcCccNtfVal), PLXPC_PLXS_PLXC_CCC_HDL_IDX},
/* Write: Record Access Control Point CCC descriptor */
{medcCccIndVal, sizeof(medcCccIndVal), PLXPC_PLXS_RACP_CCC_HDL_IDX},
};
/* Characteristic configuration list length */
#define MEDC_CFG_PLXS_LIST_LEN (sizeof(medcCfgPlxsList) / sizeof(attcDiscCfg_t))
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/* Control block */
static struct
{
wsfTimer_t racpTimer; /*! RACP procedure timer */
bool_t inProgress; /*! RACP procedure in progress */
bool_t restoreConnection; /*! Indicates if a dropped connection should be restored */
} medcPlxCb;
/**************************************************************************************************
Local Functions
**************************************************************************************************/
static void medcPlxpInit(void);
static bool_t medcPlxpDiscover(dmConnId_t connId);
static void medcPlxpConfigure(dmConnId_t connId, uint8_t status);
static void medcPlxpProcMsg(wsfMsgHdr_t *pMsg);
static void medcPlxpBtn(dmConnId_t connId, uint8_t btn);
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! profile interface pointer */
medcIf_t medcPlxpIf =
{
medcPlxpInit,
medcPlxpDiscover,
medcPlxpConfigure,
medcPlxpProcMsg,
medcPlxpBtn
};
/**************************************************************************************************
External Variables
**************************************************************************************************/
/*! MEDC application master configuration */
extern const appMasterCfg_t medcMasterCfg;
/*! App connection control block */
extern appConnCb_t appConnCb[DM_CONN_MAX];
/*************************************************************************************************/
/*!
* \brief Process a received ATT read response, notification, or indication.
*
* \param pMsg Pointer to ATT callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcPlxsValueUpdate(attEvt_t *pMsg)
{
if (pMsg->hdr.status == ATT_SUCCESS)
{
/* determine which profile the handle belongs to; start with most likely */
/* pulse oximeter */
if (PlxpcPlxsValueUpdate(pMedcPlxsHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* device information */
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* GATT */
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
}
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcPlxpProcMsg(wsfMsgHdr_t *pMsg)
{
appConnCb_t *pCb;
switch(pMsg->event)
{
case DM_CONN_CLOSE_IND:
/* if procedure in progress stop procedure timer */
if (medcPlxCb.inProgress)
{
medcPlxCb.inProgress = FALSE;
WsfTimerStop(&medcPlxCb.racpTimer);
}
if (medcPlxCb.restoreConnection == TRUE)
{
medcCb.autoConnect = TRUE;
AppScanStart(medcMasterCfg.discMode, medcMasterCfg.scanType,
medcMasterCfg.scanDuration);
}
break;
case DM_SEC_ENCRYPT_IND:
if (medcPlxCb.restoreConnection == TRUE)
{
AppDiscConfigure((dmConnId_t) pMsg->param, HCI_SUCCESS, MEDC_CFG_PLXS_LIST_LEN,
(attcDiscCfg_t *) medcCfgPlxsList,
PLXPC_PLXS_HDL_LIST_LEN, pMedcPlxsHdlList);
medcPlxCb.restoreConnection = FALSE;
}
break;
case DM_SEC_ENCRYPT_FAIL_IND:
if (medcPlxCb.restoreConnection == TRUE)
{
/* look up app connection control block from DM connection ID */
pCb = &appConnCb[pMsg->param - 1];
pCb->initiatingSec = FALSE;
/* if database record handle valid */
if (pCb->dbHdl != APP_DB_HDL_NONE)
{
AppDbDeleteRecord(pCb->dbHdl);
pCb->dbHdl = APP_DB_HDL_NONE;
}
AppMasterSecurityReq((dmConnId_t) pMsg->param);
}
break;
case ATTC_READ_RSP:
case ATTC_HANDLE_VALUE_NTF:
medcPlxsValueUpdate((attEvt_t *) pMsg);
break;
case ATTC_WRITE_RSP:
/* if write to RACP was successful, start procedure timer */
if ((((attEvt_t *) pMsg)->hdr.status == ATT_SUCCESS) &&
(((attEvt_t *) pMsg)->handle == pMedcPlxsHdlList[PLXPC_PLXS_RACP_HDL_IDX]))
{
medcPlxCb.inProgress = TRUE;
medcPlxCb.racpTimer.msg.param = pMsg->param; /* conn ID */
WsfTimerStartSec(&medcPlxCb.racpTimer, ATT_MAX_TRANS_TIMEOUT);
}
break;
case ATTC_HANDLE_VALUE_IND:
/* if procedure in progress stop procedure timer */
if (medcPlxCb.inProgress)
{
medcPlxCb.inProgress = FALSE;
WsfTimerStop(&medcPlxCb.racpTimer);
}
medcPlxsValueUpdate((attEvt_t *) pMsg);
break;
case MEDC_TIMER_IND:
/* if procedure in progress then close connection */
if (medcPlxCb.inProgress && pMsg->param != DM_CONN_ID_NONE)
{
medcPlxCb.inProgress = FALSE;
/* if configured to disconnect upon ATT transaction timeout */
if (pAppCfg->disconnect)
{
AppConnClose((dmConnId_t)pMsg->param);
}
}
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Profile initialization function.
*
* \return None.
*/
/*************************************************************************************************/
static void medcPlxpInit(void)
{
/* Set configuration pointers */
pAttCfg = (attCfg_t *) &medcPlxpAttCfg;
/* set handle list length */
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
/* set autoconnect UUID */
medcCb.autoUuid[0] = ATT_UUID_PULSE_OXIMITER_SERVICE;
/* initialize timer */
medcPlxCb.racpTimer.handlerId = medcCb.handlerId;
medcPlxCb.racpTimer.msg.event = MEDC_TIMER_IND;
}
/*************************************************************************************************/
/*!
* \brief Discover service for profile.
*
* \param connId Connection identifier.
*
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
*/
/*************************************************************************************************/
static bool_t medcPlxpDiscover(dmConnId_t connId)
{
/* discover health pulse oximeter service */
PlxpcPlxsDiscover(connId, pMedcPlxsHdlList);
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Configure service for profile.
*
* \param connId Connection identifier.
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
*
* \return None.
*/
/*************************************************************************************************/
static void medcPlxpConfigure(dmConnId_t connId, uint8_t status)
{
/* configure health pulse oximeter service */
AppDiscConfigure(connId, status, MEDC_CFG_PLXS_LIST_LEN,
(attcDiscCfg_t *) medcCfgPlxsList,
PLXPC_PLXS_HDL_LIST_LEN, pMedcPlxsHdlList);
}
/*************************************************************************************************/
/*!
* \brief Handle a button press.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medcPlxpBtn(dmConnId_t connId, uint8_t btn)
{
/* button actions when connected */
if (connId != DM_CONN_ID_NONE)
{
switch (btn)
{
case APP_UI_BTN_1_MED:
medcPlxCb.restoreConnection = TRUE;
AppConnClose(connId);
break;
case APP_UI_BTN_2_SHORT:
PlxpcPlxsRacpSend(connId, pMedcPlxsHdlList[PLXPC_PLXS_RACP_HDL_IDX], CH_RACP_OPCODE_DELETE, CH_RACP_OPERATOR_ALL);
break;
case APP_UI_BTN_2_MED:
PlxpcPlxsRacpSend(connId, pMedcPlxsHdlList[PLXPC_PLXS_RACP_HDL_IDX], CH_RACP_OPCODE_REPORT, CH_RACP_OPERATOR_ALL);
break;
case APP_UI_BTN_2_LONG:
PlxpcPlxsRacpSend(connId, pMedcPlxsHdlList[PLXPC_PLXS_RACP_HDL_IDX], CH_RACP_OPCODE_REPORT_NUM, CH_RACP_OPERATOR_ALL);
break;
case APP_UI_BTN_2_EX_LONG:
PlxpcPlxsRacpSend(connId, pMedcPlxsHdlList[PLXPC_PLXS_RACP_HDL_IDX], CH_RACP_OPCODE_ABORT, CH_RACP_OPERATOR_ALL);
break;
}
}
}
@@ -0,0 +1,315 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical collector, Weight Scale profile
*
* Copyright (c) 2012-2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_assert.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_cfg.h"
#include "app_api.h"
#include "app_db.h"
#include "app_ui.h"
#include "svc_ch.h"
#include "wspc/wspc_api.h"
#include "udsc/udsc_api.h"
#include "medc/medc_main.h"
/**************************************************************************************************
ATT Client Discovery Data
**************************************************************************************************/
/*! Discovery states: enumeration of services to be discovered */
enum
{
MEDC_DISC_WSP_UDS_SVC, /*! User data service */
MEDC_DISC_WSP_WSS_SVC, /*! Weight scale service */
};
/* Start of cached weight scale service handles; begins after DIS */
#define MEDC_DISC_WSS_START (MEDC_DISC_DIS_START + DIS_HDL_LIST_LEN)
/* Start of User Data Service handles; begins after weight scale serivce */
#define MEDC_DISC_UDS_START (MEDC_DISC_WSS_START + WSPC_WSS_HDL_LIST_LEN)
/* Total cached handle list length */
#define MEDC_DISC_HDL_LIST_LEN (MEDC_DISC_UDS_START + UDSC_HDL_LIST_LEN)
/*! Pointers into handle list for weight scale service handles */
static uint16_t *pMedcWssHdlList = &medcCb.hdlList[MEDC_DISC_WSS_START];
/*! Pointers into handle list for weight scale service handles */
static uint16_t *pMedcUdsHdlList = &medcCb.hdlList[MEDC_DISC_UDS_START];
/*! Weight Scale Profile discovery state */
static uint8_t pMedcWspDiscState;
/* sanity check: make sure handle list length is <= app db handle list length */
WSF_CT_ASSERT(MEDC_DISC_HDL_LIST_LEN <= APP_DB_HDL_LIST_LEN);
/**************************************************************************************************
ATT Client Configuration Data
**************************************************************************************************/
/* List of characteristics to configure after service discovery */
static const attcDiscCfg_t medcCfgWssList[] =
{
/* Read: Weight scale feature */
{NULL, 0, MEDC_DISC_WSS_START + WSPC_WSS_WSF_HDL_IDX},
/* Write: Weight scale measurement CCC descriptor */
{medcCccIndVal, sizeof(medcCccIndVal), MEDC_DISC_WSS_START + WSPC_WSS_WSM_CCC_HDL_IDX},
/* Write: Database Change Interval CCC descriptor */
{medcCccNtfVal, sizeof(medcCccNtfVal), MEDC_DISC_UDS_START + UDSC_DCBI_CCC_HDL_IDX},
/* Write: User Data Service Control Point CCC descriptor */
{medcCccIndVal, sizeof(medcCccIndVal), MEDC_DISC_UDS_START + UDSC_UCP_CCC_HDL_IDX}
};
/* Characteristic configuration list length */
#define MEDC_CFG_WSS_LIST_LEN (sizeof(medcCfgWssList) / sizeof(attcDiscCfg_t))
/**************************************************************************************************
Local Functions
**************************************************************************************************/
static void medcWspInit(void);
static bool_t medcWspDiscover(dmConnId_t connId);
static void medcWspConfigure(dmConnId_t connId, uint8_t status);
static void medcWspProcMsg(wsfMsgHdr_t *pMsg);
static void medcWspBtn(dmConnId_t connId, uint8_t btn);
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! profile interface pointer */
medcIf_t medcWspIf =
{
medcWspInit,
medcWspDiscover,
medcWspConfigure,
medcWspProcMsg,
medcWspBtn
};
/*************************************************************************************************/
/*!
* \brief Process a received ATT read response, notification, or indication.
*
* \param pMsg Pointer to ATT callback event message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcWssValueUpdate(attEvt_t *pMsg)
{
if (pMsg->hdr.status == ATT_SUCCESS)
{
/* determine which profile the handle belongs to; start with most likely */
/* weight scale */
if (WspcWssValueUpdate(pMedcWssHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* user data service */
if (UdscValueUpdate(pMedcUdsHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* device information */
if (DisValueUpdate(pMedcDisHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
/* GATT */
if (GattValueUpdate(pMedcGattHdlList, pMsg) == ATT_SUCCESS)
{
return;
}
}
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medcWspProcMsg(wsfMsgHdr_t *pMsg)
{
switch(pMsg->event)
{
case DM_CONN_CLOSE_IND:
/* initialize discovery state */
pMedcWspDiscState = MEDC_DISC_WSP_UDS_SVC;
/* Notify UDSC the connection closed */
UdscClose();
break;
case ATTC_HANDLE_VALUE_IND:
medcWssValueUpdate((attEvt_t *) pMsg);
break;
case MEDC_TIMER_IND:
APP_TRACE_INFO0("medcWspProcMsg - Timeout waiting for UDS control point response.");
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief UDS Control Point Response Callback.
*
* \param connId Connection ID.
* \param opcode Cmd opcode being responded to.
* \param response Response code.
* \param index USer index (only set when opcode is UDSC_UCP_OPCODE_RNU)
*
* \return None
*/
/*************************************************************************************************/
static void medcWspUdsRspCallback(dmConnId_t connId, uint8_t opcode, uint8_t response, uint8_t index)
{
APP_TRACE_INFO3("medcWspUdsRspCallback - op: %d rsp: %d index: %d", opcode, response, index);
}
/*************************************************************************************************/
/*!
* \brief Profile initialization function.
*
* \return None.
*/
/*************************************************************************************************/
static void medcWspInit(void)
{
/* set handle list length */
medcCb.hdlListLen = MEDC_DISC_HDL_LIST_LEN;
/* set autoconnect UUID */
medcCb.autoUuid[0] = ATT_UUID_WEIGHT_SCALE_SERVICE;
medcCb.autoUuid[1] = ATT_UUID_USER_DATA_SERVICE;
/* Register the UDS Control Point Callback */
UdscInit(medcCb.handlerId, MEDC_TIMER_IND, medcWspUdsRspCallback);
}
/*************************************************************************************************/
/*!
* \brief Discover service for profile.
*
* \param connId Connection identifier.
*
* \return TRUE - finished discovering services. FALSE - more services are to be discovered.
*/
/*************************************************************************************************/
static bool_t medcWspDiscover(dmConnId_t connId)
{
if (pMedcWspDiscState == MEDC_DISC_WSP_UDS_SVC)
{
/* user data service */
UdscDiscover(connId, pMedcUdsHdlList);
pMedcWspDiscState++;
return FALSE;
}
if (pMedcWspDiscState == MEDC_DISC_WSP_WSS_SVC)
{
/* discover weight scale service */
WspcWssDiscover(connId, pMedcWssHdlList);
pMedcWspDiscState++;
}
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Configure service for profile.
*
* \param connId Connection identifier.
* \param status APP_DISC_CFG_START or APP_DISC_CFG_CONN_START.
*
* \return None.
*/
/*************************************************************************************************/
static void medcWspConfigure(dmConnId_t connId, uint8_t status)
{
/* configure weight scale service */
AppDiscConfigure(connId, status, MEDC_CFG_WSS_LIST_LEN,
(attcDiscCfg_t *) medcCfgWssList,
WSPC_WSS_HDL_LIST_LEN, medcCb.hdlList);
}
/*************************************************************************************************/
/*!
* \brief Handle a button press.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medcWspBtn(dmConnId_t connId, uint8_t btn)
{
switch (btn)
{
case APP_UI_BTN_2_SHORT:
UdscRegisterNewUser(connId, pMedcUdsHdlList[UDSC_UCP_IDX], 1234);
break;
case APP_UI_BTN_2_MED:
UdscConsent(connId, pMedcUdsHdlList[UDSC_UCP_IDX], 2, 1234);
break;
case APP_UI_BTN_2_LONG:
UdscDeleteUserData(connId, pMedcUdsHdlList[UDSC_UCP_IDX]);
break;
case APP_UI_BTN_2_EX_LONG:
UdscDeleteUserData(connId, pMedcUdsHdlList[UDSC_UCP_IDX]);
break;
case APP_UI_BTN_1_SHORT:
UdscReadDatabaseChangeIncrement(connId, pMedcUdsHdlList[UDSC_DBCI_HDL_IDX]);
break;
case APP_UI_BTN_1_MED:
UdscReadUserIndex(connId, pMedcUdsHdlList[UDSC_UI_HDL_IDX]);
break;
case APP_UI_BTN_1_EX_LONG:
UdscWriteDatabaseChangeIncrement(connId, pMedcUdsHdlList[UDSC_DBCI_HDL_IDX], 1);
break;
default:
break;
}
}
@@ -0,0 +1,101 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Medical sensor sample application interface.
*
* Copyright (c) 2012-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef MEDS_API_H
#define MEDS_API_H
#include "wsf_os.h"
#ifdef __cplusplus
extern "C" {
#endif
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! Profile identifier used for MedsSetProfile() */
enum
{
MEDS_ID_BLP, /*! Blood pressure profile */
MEDS_ID_WSP, /*! Weight scale profile */
MEDS_ID_HTP, /*! Health thermometer profile */
MEDS_ID_PLX, /*! Pulse Oximeter profile */
MEDS_ID_GLP /*! Glucose profile */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void MedsStart(void);
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID for App.
*
* \return None.
*/
/*************************************************************************************************/
void MedsHandlerInit(wsfHandlerId_t handlerId);
/*************************************************************************************************/
/*!
* \brief WSF event handler for the application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void MedsHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
/*************************************************************************************************/
/*!
* \brief Set the profile to be used by the application. This function is called internally
* by MedsHandlerInit() with a default value. It may also be called by the system
* to configure the profile after executing MedsHandlerInit() and before executing
* MedsStart().
*
* \param profile Profile identifier.
*
* \return None.
*/
/*************************************************************************************************/
void MedsSetProfile(uint8_t profile);
#ifdef __cplusplus
};
#endif
#endif /* MEDS_API_H */
@@ -0,0 +1,322 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Medical sensor sample, blood pressure profile
*
* Copyright (c) 2012-2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "hci_api.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_api.h"
#include "app_ui.h"
#include "svc_ch.h"
#include "svc_bps.h"
#include "svc_dis.h"
#include "svc_core.h"
#include "blps/blps_api.h"
#include "gatt/gatt_api.h"
#include "meds/meds_main.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! enumeration of client characteristic configuration descriptors */
enum
{
MEDS_BLP_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
MEDS_BLP_BPS_BPM_CCC_IDX, /*! Blood pressure service, blood pressure measurement characteristic */
MEDS_BLP_BPS_ICP_CCC_IDX, /*! Blood pressure service, intermediate cuff pressure characteristic */
MEDS_BLP_NUM_CCC_IDX
};
/**************************************************************************************************
Configurable Parameters
**************************************************************************************************/
/*! blood pressure measurement configuration */
static const blpsCfg_t medsBlpsCfg =
{
2000 /*! Measurement timer expiration period in ms */
};
/**************************************************************************************************
Advertising Data
**************************************************************************************************/
/*! Service UUID list */
static const uint8_t medsSvcUuidList[] =
{
UINT16_TO_BYTES(ATT_UUID_BLOOD_PRESSURE_SERVICE),
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE)
};
/**************************************************************************************************
Client Characteristic Configuration Descriptors
**************************************************************************************************/
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
static const attsCccSet_t medsBlpCccSet[MEDS_BLP_NUM_CCC_IDX] =
{
/* cccd handle value range security level */
{GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_ENC}, /* MEDS_BLP_GATT_SC_CCC_IDX */
{BPS_BPM_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_ENC}, /* MEDS_BLP_BPS_BPM_CCC_IDX */
{BPS_ICP_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_ENC} /* MEDS_BLP_BPS_ICP_CCC_IDX */
};
/**************************************************************************************************
Local Functions
**************************************************************************************************/
static void medsBlpStart(void);
static void medsBlpProcMsg(wsfMsgHdr_t *pMsg);
static void medsBlpBtn(dmConnId_t connId, uint8_t btn);
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! profile interface pointer */
medsIf_t medsBlpIf =
{
NULL,
medsBlpStart,
medsBlpProcMsg,
medsBlpBtn
};
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! application control block */
static struct
{
bool_t measuring;
bool_t storedMeasurement;
} medsBlpCb;
static uint8_t medsBpmFlags = CH_BPM_FLAG_UNITS_MMHG | CH_BPM_FLAG_TIMESTAMP |
CH_BPM_FLAG_PULSE_RATE | CH_BPM_FLAG_MEAS_STATUS;
static uint8_t medsIcpFlags = CH_BPM_FLAG_UNITS_MMHG | CH_BPM_FLAG_PULSE_RATE |
CH_BPM_FLAG_MEAS_STATUS;
/*************************************************************************************************/
/*!
* \brief Perform UI actions on connection close.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medsBlpClose(wsfMsgHdr_t *pMsg)
{
/* stop blood pressure measurement */
BlpsMeasStop();
medsBlpCb.measuring = FALSE;
}
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
static void medsBlpStart(void)
{
/* set up CCCD table and callback */
AttsCccRegister(MEDS_BLP_NUM_CCC_IDX, (attsCccSet_t *) medsBlpCccSet, medsCccCback);
/* add blood pressure service */
SvcBpsAddGroup();
/* Set Service Changed CCCD index. */
GattSetSvcChangedIdx(MEDS_BLP_GATT_SC_CCC_IDX);
/* initialize blood pressure profile sensor */
BlpsInit(medsCb.handlerId, (blpsCfg_t *) &medsBlpsCfg);
BlpsSetBpmFlags(medsBpmFlags);
BlpsSetIcpFlags(medsIcpFlags);
/* set advertising data */
AppAdvSetAdValue(APP_ADV_DATA_DISCOVERABLE, DM_ADV_TYPE_16_UUID, sizeof(medsSvcUuidList),
(uint8_t *) medsSvcUuidList);
}
/*************************************************************************************************/
/*!
* \brief Send stored blood pressure measurement.
*
* \param connId Connection ID to send stored measurement to.
*
* \return None.
*/
/*************************************************************************************************/
static void medsBlpSendStoredMeasurement(dmConnId_t connId)
{
medsBlpCb.storedMeasurement = FALSE;
BlpsMeasComplete((dmConnId_t)connId, MEDS_BLP_BPS_BPM_CCC_IDX);
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medsBlpProcMsg(wsfMsgHdr_t *pMsg)
{
switch(pMsg->event)
{
case MEDS_TIMER_IND:
BlpsProcMsg(pMsg);
break;
case ATTS_CCC_STATE_IND:
/* Check if stored measurement exists and indications on measurements enabled */
if (medsBlpCb.storedMeasurement &&
AttsCccEnabled((dmConnId_t)pMsg->param, MEDS_BLP_BPS_BPM_CCC_IDX))
{
medsBlpSendStoredMeasurement((dmConnId_t)pMsg->param);
}
break;
case DM_CONN_CLOSE_IND:
medsBlpClose(pMsg);
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Button press callback.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medsBlpBtn(dmConnId_t connId, uint8_t btn)
{
/* button actions when connected */
if (connId != DM_CONN_ID_NONE)
{
switch (btn)
{
case APP_UI_BTN_1_SHORT:
/* start or complete measurement */
if (!medsBlpCb.measuring)
{
BlpsMeasStart(connId, MEDS_TIMER_IND, MEDS_BLP_BPS_ICP_CCC_IDX);
medsBlpCb.measuring = TRUE;
}
else
{
BlpsMeasComplete(connId, MEDS_BLP_BPS_BPM_CCC_IDX);
medsBlpCb.measuring = FALSE;
}
break;
default:
break;
}
}
else /* if not connected */
{
switch (btn)
{
case APP_UI_BTN_1_EX_LONG:
/* Toggle flags in service for testing purposes */
SvcBpsToggleFeatureFlags(CH_BPF_FLAG_MULTI_BOND);
break;
case APP_UI_BTN_2_SHORT:
/* Toggle flags in measurements for testing purposes */
if (!(medsBpmFlags & CH_BPM_FLAG_USER_ID))
{
medsBpmFlags |= CH_BPM_FLAG_USER_ID;
}
else
{
medsBpmFlags &= ~CH_BPM_FLAG_USER_ID;
}
BlpsSetBpmFlags(medsBpmFlags);
break;
case APP_UI_BTN_2_MED:
/* Toggle flags in intermediate measurements for testing purposes */
if (!(medsIcpFlags & CH_BPM_FLAG_USER_ID))
{
medsIcpFlags |= CH_BPM_FLAG_USER_ID;
}
else
{
medsIcpFlags &= ~CH_BPM_FLAG_USER_ID;
}
BlpsSetIcpFlags(medsIcpFlags);
break;
case APP_UI_BTN_2_LONG:
/* Toggle flags in intermediate measurements for testing purposes */
if (!(medsIcpFlags & CH_BPM_FLAG_TIMESTAMP))
{
medsIcpFlags |= CH_BPM_FLAG_TIMESTAMP;
}
else
{
medsIcpFlags &= ~CH_BPM_FLAG_TIMESTAMP;
}
BlpsSetIcpFlags(medsIcpFlags);
break;
case APP_UI_BTN_2_EX_LONG:
if (connId == DM_CONN_ID_NONE)
{
/* Store a measurement to be sent when connected */
medsBlpCb.storedMeasurement = TRUE;
}
break;
default:
break;
}
}
}
@@ -0,0 +1,166 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Medical sensor sample, glucose profile
*
* Copyright (c) 2016-2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "hci_api.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_api.h"
#include "app_ui.h"
#include "app_hw.h"
#include "svc_ch.h"
#include "svc_gls.h"
#include "svc_dis.h"
#include "svc_core.h"
#include "gatt/gatt_api.h"
#include "glps/glps_api.h"
#include "meds/meds_main.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! enumeration of client characteristic configuration descriptors */
enum
{
MEDS_GLS_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
MEDS_GLS_GLS_GLM_CCC_IDX, /*! Glucose measurement characteristic */
MEDS_GLS_GLS_GLMC_CCC_IDX, /*! Glucose measurement context characteristic */
MEDS_GLS_GLS_RACP_CCC_IDX, /*! Glucose record access control point characteristic */
MEDS_GLS_NUM_CCC_IDX
};
/**************************************************************************************************
Advertising Data
**************************************************************************************************/
/*! Service UUID list */
static const uint8_t medsSvcUuidList[] =
{
UINT16_TO_BYTES(ATT_UUID_GLUCOSE_SERVICE),
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE)
};
/**************************************************************************************************
Client Characteristic Configuration Descriptors
**************************************************************************************************/
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
static const attsCccSet_t medsGlpCccSet[MEDS_GLS_NUM_CCC_IDX] =
{
/* cccd handle value range security level */
{GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE}, /* MEDS_GLS_GATT_SC_CCC_IDX */
{GLS_GLM_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* MEDS_GLS_GLS_GLM_CCC_IDX */
{GLS_GLMC_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* MEDS_GLS_GLS_GLMC_CCC_IDX */
{GLS_RACP_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE} /* MEDS_GLS_GLS_RACP_CCC_IDX */
};
/**************************************************************************************************
Local Functions
**************************************************************************************************/
static void medsGlpStart(void);
static void medsGlpProcMsg(wsfMsgHdr_t *pMsg);
static void medsGlpBtn(dmConnId_t connId, uint8_t btn);
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! profile interface pointer */
medsIf_t medsGlpIf =
{
NULL,
medsGlpStart,
medsGlpProcMsg,
medsGlpBtn
};
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
static void medsGlpStart(void)
{
/* set up CCCD table and callback */
AttsCccRegister(MEDS_GLS_NUM_CCC_IDX, (attsCccSet_t *) medsGlpCccSet, medsCccCback);
/* add glucose service */
SvcGlsAddGroup();
SvcGlsCbackRegister(NULL, GlpsRacpWriteCback);
/* Set Service Changed CCCD index. */
GattSetSvcChangedIdx(MEDS_GLS_GATT_SC_CCC_IDX);
/* initialize glucose profile sensor */
GlpsInit();
GlpsSetCccIdx(MEDS_GLS_GLS_GLM_CCC_IDX, MEDS_GLS_GLS_GLMC_CCC_IDX, MEDS_GLS_GLS_RACP_CCC_IDX);
/* TODO: Define glucose features */
GlpsSetFeature(GLP_ALL_SUPPORTED_FEATURES);
/* set advertising data */
AppAdvSetAdValue(APP_ADV_DATA_DISCOVERABLE, DM_ADV_TYPE_16_UUID, sizeof(medsSvcUuidList),
(uint8_t *) medsSvcUuidList);
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medsGlpProcMsg(wsfMsgHdr_t *pMsg)
{
GlpsProcMsg(pMsg);
}
/*************************************************************************************************/
/*!
* \brief Button press callback.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medsGlpBtn(dmConnId_t connId, uint8_t btn)
{
GlpsBtn(connId, btn);
}
@@ -0,0 +1,293 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Medical sensor sample, health thermometer profile
*
* Copyright (c) 2012-2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "hci_api.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_api.h"
#include "app_hw.h"
#include "app_ui.h"
#include "svc_ch.h"
#include "svc_hts.h"
#include "svc_dis.h"
#include "svc_core.h"
#include "gatt/gatt_api.h"
#include "htps/htps_api.h"
#include "meds/meds_main.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! enumeration of client characteristic configuration descriptors */
enum
{
MEDS_HTP_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
MEDS_HTP_HTS_TM_CCC_IDX, /*! Health thermometer service, temperature measurement characteristic */
MEDS_HTP_HTS_IT_CCC_IDX, /*! Health thermometer service, intermediate temperature characteristic */
MEDS_HTP_NUM_CCC_IDX
};
/**************************************************************************************************
Configurable Parameters
**************************************************************************************************/
/*! health thermometer measurement configuration */
static const htpsCfg_t medsHtpsCfg =
{
2000 /*! Measurement timer expiration period in ms */
};
/**************************************************************************************************
Advertising Data
**************************************************************************************************/
/*! Service UUID list */
static const uint8_t medsSvcUuidList[] =
{
UINT16_TO_BYTES(ATT_UUID_HEALTH_THERM_SERVICE),
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE)
};
/**************************************************************************************************
Client Characteristic Configuration Descriptors
**************************************************************************************************/
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
static const attsCccSet_t medsHtpCccSet[MEDS_HTP_NUM_CCC_IDX] =
{
/* cccd handle value range security level */
{GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_ENC}, /* MEDS_HTP_GATT_SC_CCC_IDX */
{HTS_TM_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE}, /* MEDS_HTP_HTS_TM_CCC_IDX */
{HTS_IT_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_ENC} /* MEDS_HTP_HTS_IT_CCC_IDX */
};
/**************************************************************************************************
Local Functions
**************************************************************************************************/
static void medsHtpStart(void);
static void medsHtpProcMsg(wsfMsgHdr_t *pMsg);
static void medsHtpBtn(dmConnId_t connId, uint8_t btn);
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! profile interface pointer */
medsIf_t medsHtpIf =
{
NULL,
medsHtpStart,
medsHtpProcMsg,
medsHtpBtn
};
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! application control block */
static struct
{
bool_t measuring;
bool_t storedMeasurement;
} medsHtpCb;
/*************************************************************************************************/
/*!
* \brief Set temperature measurement units.
*
* \param units CH_TM_FLAG_UNITS_C or CH_TM_FLAG_UNITS_F.
*
* \return None.
*/
/*************************************************************************************************/
static void medsHtpSetUnits(uint8_t units)
{
HtpsSetTmFlags(units | CH_TM_FLAG_TIMESTAMP);
HtpsSetItFlags(units);
AppHwTmSetUnits(units);
}
/*************************************************************************************************/
/*!
* \brief Perform UI actions on connection close.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medsHtpClose(wsfMsgHdr_t *pMsg)
{
/* stop health thermometer measurement */
HtpsMeasStop();
medsHtpCb.measuring = FALSE;
}
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
static void medsHtpStart(void)
{
/* set up CCCD table and callback */
AttsCccRegister(MEDS_HTP_NUM_CCC_IDX, (attsCccSet_t *) medsHtpCccSet, medsCccCback);
/* add health thermometer service */
SvcHtsAddGroup();
/* Set Service Changed CCCD index. */
GattSetSvcChangedIdx(MEDS_HTP_GATT_SC_CCC_IDX);
/* initialize health thermometer profile sensor */
HtpsInit(medsCb.handlerId, (htpsCfg_t *) &medsHtpsCfg);
medsHtpSetUnits(CH_TM_FLAG_UNITS_C);
/* set advertising data */
AppAdvSetAdValue(APP_ADV_DATA_DISCOVERABLE, DM_ADV_TYPE_16_UUID, sizeof(medsSvcUuidList),
(uint8_t *) medsSvcUuidList);
}
/*************************************************************************************************/
/*!
* \brief Send stored temperature measurement.
*
* \param connId connection ID to send stored measurement to.
*
* \return None.
*/
/*************************************************************************************************/
static void medsHtpSendStoredMeasurement(dmConnId_t connId)
{
medsHtpCb.storedMeasurement = FALSE;
HtpsMeasComplete((dmConnId_t)connId, MEDS_HTP_HTS_TM_CCC_IDX);
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medsHtpProcMsg(wsfMsgHdr_t *pMsg)
{
switch(pMsg->event)
{
case MEDS_TIMER_IND:
HtpsProcMsg(pMsg);
break;
case ATTS_CCC_STATE_IND:
/* Check if stored measurement exists and indications on temperature measurement enabled */
if (medsHtpCb.storedMeasurement && AttsCccEnabled((dmConnId_t)pMsg->param, MEDS_HTP_HTS_TM_CCC_IDX))
{
medsHtpSendStoredMeasurement((dmConnId_t)pMsg->param);
}
break;
case DM_CONN_CLOSE_IND:
medsHtpClose(pMsg);
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Button press callback.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medsHtpBtn(dmConnId_t connId, uint8_t btn)
{
static bool_t advNonconn = FALSE;
switch (btn)
{
case APP_UI_BTN_1_SHORT:
/* if connected */
if (connId != DM_CONN_ID_NONE)
{
/* start or complete measurement */
if (!medsHtpCb.measuring)
{
HtpsMeasStart(connId, MEDS_TIMER_IND, MEDS_HTP_HTS_IT_CCC_IDX);
medsHtpCb.measuring = TRUE;
}
else
{
HtpsMeasComplete(connId, MEDS_HTP_HTS_TM_CCC_IDX);
medsHtpCb.measuring = FALSE;
}
}
break;
case APP_UI_BTN_2_SHORT:
/* set units */
medsHtpSetUnits(CH_TM_FLAG_UNITS_F);
break;
case APP_UI_BTN_2_MED:
/* set units */
medsHtpSetUnits(CH_TM_FLAG_UNITS_C);
break;
case APP_UI_BTN_2_LONG:
/* set new advertising type */
advNonconn = !advNonconn;
AppSetAdvType(advNonconn ? DM_ADV_NONCONN_UNDIRECT : DM_ADV_CONN_UNDIRECT);
break;
case APP_UI_BTN_2_EX_LONG:
if (connId == DM_CONN_ID_NONE)
{
/* Store a temperature measurement to be sent when connected */
medsHtpCb.storedMeasurement = TRUE;
}
break;
default:
break;
}
}
@@ -0,0 +1,542 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Medical sensor sample application for the following profiles:
* Blood Pressure profile sensor
* Weight Scale profile sensor
* Health Thermometer profile sensor
*
* Copyright (c) 2012-2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "hci_api.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_api.h"
#include "app_db.h"
#include "app_ui.h"
#include "app_hw.h"
#include "app_main.h"
#include "svc_ch.h"
#include "svc_core.h"
#include "svc_dis.h"
#include "gatt/gatt_api.h"
#include "meds/meds_api.h"
#include "meds/meds_main.h"
/**************************************************************************************************
Profile Configuration
**************************************************************************************************/
/* Blood pressure profile included */
#ifndef MEDS_BLP_INCLUDED
#define MEDS_BLP_INCLUDED TRUE
#endif
/* Weight scale profile included */
#ifndef MEDS_WSP_INCLUDED
#define MEDS_WSP_INCLUDED TRUE
#endif
/* Health thermometer profile included */
#ifndef MEDS_HTP_INCLUDED
#define MEDS_HTP_INCLUDED TRUE
#endif
/* Pulse Oximeter profile included */
#ifndef MEDS_PLX_INCLUDED
#define MEDS_PLX_INCLUDED TRUE
#endif
/* Glucose profile included */
#ifndef MEDS_GLP_INCLUDED
#define MEDS_GLP_INCLUDED TRUE
#endif
/* Default profile to use */
#ifndef MEDS_PROFILE
#define MEDS_PROFILE MEDS_ID_BLP
#endif
/**************************************************************************************************
Configurable Parameters
**************************************************************************************************/
/*! configurable parameters for advertising */
static const appAdvCfg_t medsAdvCfg =
{
{60000, 30000, 0}, /*! Advertising durations in ms */
{ 48, 1600, 0} /*! Advertising intervals in 0.625 ms units */
};
/*! configurable parameters for slave */
static const appSlaveCfg_t medsSlaveCfg =
{
1, /*! Maximum connections */
};
/*! configurable parameters for security */
static const appSecCfg_t medsSecCfg =
{
DM_AUTH_BOND_FLAG, /*! Authentication and bonding flags */
0, /*! Initiator key distribution flags */
DM_KEY_DIST_LTK, /*! Responder key distribution flags */
FALSE, /*! TRUE if Out-of-band pairing data is present */
TRUE /*! TRUE to initiate security upon connection */
};
/*! configurable parameters for connection parameter update */
static const appUpdateCfg_t medsUpdateCfg =
{
0, /*! Connection idle period in ms before attempting
connection parameter update; set to zero to disable */
640, /*! Minimum connection interval in 1.25ms units */
800, /*! Maximum connection interval in 1.25ms units */
0, /*! Connection latency */
600, /*! Supervision timeout in 10ms units */
5 /*! Number of update attempts before giving up */
};
/**************************************************************************************************
Advertising Data
**************************************************************************************************/
/*! advertising data flags */
static const uint8_t medsAdvDataFlags[] =
{
DM_FLAG_LE_LIMITED_DISC |
DM_FLAG_LE_BREDR_NOT_SUP
};
/*! advertising data buffer (value is set in medsSetup) */
static uint8_t medsAdvDataDisc[HCI_ADV_DATA_LEN];
/*! scan data, discoverable mode */
static const uint8_t medsScanDataDisc[] =
{
/*! device name */
11, /*! length */
DM_ADV_TYPE_LOCAL_NAME, /*! AD type */
'M',
'e',
'd',
' ',
'S',
'e',
'n',
's',
'o',
'r'
};
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! application control block */
medsCb_t medsCb;
/*************************************************************************************************/
/*!
* \brief Application DM callback.
*
* \param pDmEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void medsDmCback(dmEvt_t *pDmEvt)
{
dmEvt_t *pMsg;
uint16_t len;
len = DmSizeOfEvt(pDmEvt);
if ((pMsg = WsfMsgAlloc(len)) != NULL)
{
memcpy(pMsg, pDmEvt, len);
WsfMsgSend(medsCb.handlerId, pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Application ATT callback.
*
* \param pEvt ATT callback event
*
* \return None.
*/
/*************************************************************************************************/
static void medsAttCback(attEvt_t *pEvt)
{
medsCb.pIf->procMsg((wsfMsgHdr_t*) pEvt);
}
/*************************************************************************************************/
/*!
* \brief Application ATTS client characteristic configuration callback.
*
* \param pDmEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
void medsCccCback(attsCccEvt_t *pEvt)
{
attsCccEvt_t *pMsg;
appDbHdl_t dbHdl;
/* If CCC not set from initialization and there's a device record and currently bonded */
if ((pEvt->handle != ATT_HANDLE_NONE) &&
((dbHdl = AppDbGetHdl((dmConnId_t) pEvt->hdr.param)) != APP_DB_HDL_NONE) &&
AppCheckBonded((dmConnId_t)pEvt->hdr.param))
{
/* Store value in device database. */
AppDbSetCccTblValue(dbHdl, pEvt->idx, pEvt->value);
}
if ((pMsg = WsfMsgAlloc(sizeof(attsCccEvt_t))) != NULL)
{
memcpy(pMsg, pEvt, sizeof(attsCccEvt_t));
WsfMsgSend(medsCb.handlerId, pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Set up advertising and other procedures that need to be performed after
* device reset.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medsSetup(wsfMsgHdr_t *pMsg)
{
/* start advertising; automatically set connectable/discoverable mode and bondable mode */
AppAdvStart(APP_MODE_AUTO_INIT);
}
/*************************************************************************************************/
/*!
* \brief Button press callback.
*
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medsBtnCback(uint8_t btn)
{
dmConnId_t connId;
/* button actions when connected */
if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE)
{
switch (btn)
{
case APP_UI_BTN_1_LONG:
AppConnClose(connId);
break;
default:
/* all other button presses-- send to profile */
medsCb.pIf->btn(connId, btn);
break;
}
}
/* button actions when not connected */
else
{
switch (btn)
{
case APP_UI_BTN_1_SHORT:
/* start or restart advertising */
AppAdvStart(APP_MODE_AUTO_INIT);
break;
case APP_UI_BTN_1_MED:
/* enter discoverable and bondable mode mode */
AppSetBondable(TRUE);
AppAdvStart(APP_MODE_DISCOVERABLE);
break;
case APP_UI_BTN_1_LONG:
/* clear all bonding info */
AppSlaveClearAllBondingInfo();
/* restart advertising */
AppAdvStart(APP_MODE_AUTO_INIT);
break;
default:
/* all other button presses-- send to profile */
medsCb.pIf->btn(connId, btn);
break;
}
}
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medsProcMsg(wsfMsgHdr_t *pMsg)
{
uint8_t uiEvent = APP_UI_NONE;
switch(pMsg->event)
{
case MEDS_TIMER_IND:
break;
case ATT_MTU_UPDATE_IND:
APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu);
break;
case DM_RESET_CMPL_IND:
AttsCalculateDbHash();
medsSetup(pMsg);
uiEvent = APP_UI_RESET_CMPL;
break;
case DM_ADV_START_IND:
uiEvent = APP_UI_ADV_START;
break;
case DM_ADV_STOP_IND:
uiEvent = APP_UI_ADV_STOP;
break;
case DM_CONN_OPEN_IND:
uiEvent = APP_UI_CONN_OPEN;
break;
case DM_CONN_CLOSE_IND:
uiEvent = APP_UI_CONN_CLOSE;
break;
case DM_SEC_PAIR_CMPL_IND:
uiEvent = APP_UI_SEC_PAIR_CMPL;
break;
case DM_SEC_PAIR_FAIL_IND:
uiEvent = APP_UI_SEC_PAIR_FAIL;
break;
case DM_SEC_ENCRYPT_IND:
uiEvent = APP_UI_SEC_ENCRYPT;
break;
case DM_SEC_ENCRYPT_FAIL_IND:
uiEvent = APP_UI_SEC_ENCRYPT_FAIL;
break;
case DM_SEC_AUTH_REQ_IND:
AppHandlePasskey(&((dmEvt_t *)pMsg)->authReq);
break;
case DM_PRIV_CLEAR_RES_LIST_IND:
APP_TRACE_INFO1("Clear resolving list status 0x%02x", pMsg->status);
break;
default:
break;
}
medsCb.pIf->procMsg(pMsg);
if (uiEvent != APP_UI_NONE)
{
AppUiAction(uiEvent);
}
}
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID.
*
* \return None.
*/
/*************************************************************************************************/
void MedsHandlerInit(wsfHandlerId_t handlerId)
{
APP_TRACE_INFO0("MedsHandlerInit");
/* store handler ID */
medsCb.handlerId = handlerId;
/* Set configuration pointers */
pAppSlaveCfg = (appSlaveCfg_t *) &medsSlaveCfg;
pAppAdvCfg = (appAdvCfg_t *) &medsAdvCfg;
pAppSecCfg = (appSecCfg_t *) &medsSecCfg;
pAppUpdateCfg = (appUpdateCfg_t *) &medsUpdateCfg;
/* Initialize application framework */
AppSlaveInit();
AppServerInit();
/* Set default profile to use */
MedsSetProfile(MEDS_PROFILE);
}
/*************************************************************************************************/
/*!
* \brief WSF event handler for application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void MedsHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
{
if (pMsg != NULL)
{
APP_TRACE_INFO1("Meds got evt %d", pMsg->event);
/* process ATT messages */
if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
{
/* process server-related ATT messages */
AppServerProcAttMsg(pMsg);
}
/* process DM messages */
else if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
{
/* process advertising and connection-related messages */
AppSlaveProcDmMsg((dmEvt_t *) pMsg);
/* process security-related messages */
AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
}
/* perform profile and user interface-related operations */
medsProcMsg(pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void MedsStart(void)
{
/* Register for stack callbacks */
DmRegister(medsDmCback);
DmConnRegister(DM_CLIENT_ID_APP, medsDmCback);
AttRegister(medsAttCback);
AttConnRegister(AppServerConnCback);
/* Register for app framework callbacks */
AppUiBtnRegister(medsBtnCback);
/* Initialize attribute server database */
SvcCoreGattCbackRegister(GattReadCback, GattWriteCback);
SvcCoreAddGroup();
SvcDisAddGroup();
/* set advertising and scan response data for discoverable mode */
AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, 0, (uint8_t *) medsAdvDataDisc);
AppAdvSetAdValue(APP_ADV_DATA_DISCOVERABLE, DM_ADV_TYPE_FLAGS, sizeof(medsAdvDataFlags),
(uint8_t *) medsAdvDataFlags);
AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(medsScanDataDisc), (uint8_t *) medsScanDataDisc);
/* set advertising and scan response data for connectable mode */
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, 0, NULL);
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, 0, NULL);
/* call profile start function */
medsCb.pIf->start();
/* Reset the device */
DmDevReset();
}
/*************************************************************************************************/
/*!
* \brief Set the profile to be used by the application. This function is called internally
* by MedsHandlerInit() with a default value. It may also be called by the system
* to configure the profile after executing MedsHandlerInit() and before executing
* MedsStart().
*
* \param profile Profile identifier.
*
* \return None.
*/
/*************************************************************************************************/
void MedsSetProfile(uint8_t profile)
{
switch (profile)
{
#if MEDS_BLP_INCLUDED == TRUE
case MEDS_ID_BLP:
medsCb.pIf = &medsBlpIf;
break;
#endif
#if MEDS_WSP_INCLUDED == TRUE
case MEDS_ID_WSP:
medsCb.pIf = &medsWspIf;
break;
#endif
#if MEDS_HTP_INCLUDED == TRUE
case MEDS_ID_HTP:
medsCb.pIf = &medsHtpIf;
break;
#endif
#if MEDS_PLX_INCLUDED == TRUE
case MEDS_ID_PLX:
medsCb.pIf = &medsPlxIf;
break;
#endif
#if MEDS_GLP_INCLUDED == TRUE
case MEDS_ID_GLP:
medsCb.pIf = &medsGlpIf;
break;
#endif
default:
APP_TRACE_WARN1("MedsSetProfile invalid profile:%d", profile);
medsCb.pIf = NULL;
break;
}
if ((medsCb.pIf != NULL) && (medsCb.pIf->init != NULL))
{
medsCb.pIf->init();
}
}
@@ -0,0 +1,96 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Health/medical sensor sample application interface file.
*
* Copyright (c) 2012-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef MEDS_MAIN_H
#define MEDS_MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! WSF message event starting value */
#define MEDS_MSG_START 0xA0
/*! WSF message event enumeration */
enum
{
MEDS_TIMER_IND = MEDS_MSG_START, /*! Timer expired */
};
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! profile interface callback functions */
typedef void (*medsInitCback_t)(void);
typedef void (*medsStartCback_t)(void);
typedef void (*medsProcMsgCback_t)(wsfMsgHdr_t *pMsg);
typedef void (*medsBtnCback_t)(dmConnId_t connId, uint8_t btn);
/*! profile interface structure */
typedef struct
{
medsInitCback_t init;
medsStartCback_t start;
medsProcMsgCback_t procMsg;
medsBtnCback_t btn;
} medsIf_t;
/*! application control block */
typedef struct
{
medsIf_t *pIf; /*! Profile interface */
wsfHandlerId_t handlerId; /*! WSF hander ID */
} medsCb_t;
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! application control block */
extern medsCb_t medsCb;
/*! profile interface pointers */
extern medsIf_t medsBlpIf; /* blood pressure profile */
extern medsIf_t medsWspIf; /* weight scale profile */
extern medsIf_t medsHtpIf; /* health thermometer profile */
extern medsIf_t medsPlxIf; /* pulse oximeter profile */
extern medsIf_t medsGlpIf; /* glucose profile */
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
void medsCccCback(attsCccEvt_t *pEvt);
#ifdef __cplusplus
};
#endif
#endif /* MEDS_MAIN_H */
@@ -0,0 +1,263 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Medical sensor sample, pulse oximeter profile
*
* Copyright (c) 2016-2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "hci_api.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_api.h"
#include "app_ui.h"
#include "app_hw.h"
#include "svc_ch.h"
#include "svc_plxs.h"
#include "svc_dis.h"
#include "svc_core.h"
#include "gatt/gatt_api.h"
#include "plxps/plxps_api.h"
#include "plxps/plxps_main.h"
#include "meds/meds_main.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! enumeration of client characteristic configuration descriptors */
enum
{
MEDS_PLX_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
MEDS_PLX_PLX_SCM_CCC_IDX, /*! Pulse Oximeter Spot Check measurement characteristic */
MEDS_PLX_PLX_CM_CCC_IDX, /*! Pulse Oximeter Continuous characteristic */
MEDS_PLX_PLX_RACP_CCC_IDX, /*! Pulse Oximeter record access control point characteristic */
MEDS_PLX_NUM_CCC_IDX
};
/* Default MTU */
#define MEDS_PLX_DEFAULT_MTU 50
/**************************************************************************************************
Configurable Parameters
**************************************************************************************************/
/*! Pulse Oximeter Continuous measurement configuration */
static const plxpsCfg_t medsPlxpsCfg =
{
2000 /*! Measurement timer expiration period in ms */
};
/*! ATT configurable parameters (increase MTU) */
static const attCfg_t medsPlxAttCfg =
{
15, /* ATT server service discovery connection idle timeout in seconds */
MEDS_PLX_DEFAULT_MTU, /* desired ATT MTU */
ATT_MAX_TRANS_TIMEOUT, /* transcation timeout in seconds */
4 /* number of queued prepare writes supported by server */
};
/**************************************************************************************************
Advertising Data
**************************************************************************************************/
/*! Service UUID list */
static const uint8_t medsSvcUuidList[] =
{
UINT16_TO_BYTES(ATT_UUID_PULSE_OXIMITER_SERVICE),
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE)
};
/**************************************************************************************************
Client Characteristic Configuration Descriptors
**************************************************************************************************/
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
static const attsCccSet_t medsPlxCccSet[MEDS_PLX_NUM_CCC_IDX] =
{
/* cccd handle value range security level */
{GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE}, /* MEDS_PLX_GATT_SC_CCC_IDX */
{PLXS_SPOT_CHECK_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE}, /* MEDS_PLX_PLX_SCM_CCC_IDX */
{PLXS_CONTINUOUS_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* MEDS_PLX_PLX_CM_CCC_IDX */
{PLXS_RECORD_ACCESS_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE} /* MEDS_PLX_PLX_RACP_CCC_IDX */
};
/**************************************************************************************************
Local Functions
**************************************************************************************************/
static void medsPlxInit(void);
static void medsPlxStart(void);
static void medsPlxProcMsg(wsfMsgHdr_t *pMsg);
static void medsPlxBtn(dmConnId_t connId, uint8_t btn);
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! profile interface pointer */
medsIf_t medsPlxIf =
{
medsPlxInit,
medsPlxStart,
medsPlxProcMsg,
medsPlxBtn
};
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! application control block */
static struct
{
bool_t measuring;
} medsPlxCb;
/*************************************************************************************************/
/*!
* \brief Perform actions on connection close.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medsPlxClose(wsfMsgHdr_t *pMsg)
{
/* Reset control information */
medsPlxCb.measuring = FALSE;
}
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
static void medsPlxStart(void)
{
/* set up CCCD table and callback */
AttsCccRegister(MEDS_PLX_NUM_CCC_IDX, (attsCccSet_t *) medsPlxCccSet, medsCccCback);
/* add pulse oximeter service */
SvcPlxsAddGroup();
SvcPlxsCbackRegister(NULL, PlxpsWriteCback);
/* Set Service Changed CCCD index. */
GattSetSvcChangedIdx(MEDS_PLX_GATT_SC_CCC_IDX);
/* initialize pulse oximeter profile sensor */
PlxpsInit(medsCb.handlerId, (plxpsCfg_t *) &medsPlxpsCfg);
PlxpsSetCccIdx(MEDS_PLX_PLX_SCM_CCC_IDX, MEDS_PLX_PLX_CM_CCC_IDX, MEDS_PLX_PLX_RACP_CCC_IDX);
/* TODO: Define pulse oximeter features */
PlxpsSetFeature(CH_PLF_FLAG_SENSOR_STATUS_SUP, 0, 0);
/* set advertising data */
AppAdvSetAdValue(APP_ADV_DATA_DISCOVERABLE, DM_ADV_TYPE_16_UUID, sizeof(medsSvcUuidList),
(uint8_t *) medsSvcUuidList);
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medsPlxProcMsg(wsfMsgHdr_t *pMsg)
{
if (pMsg->event == DM_CONN_CLOSE_IND)
{
medsPlxClose(pMsg);
}
PlxpsProcMsg(pMsg);
}
/*************************************************************************************************/
/*!
* \brief Profile initialization function.
*
* \return None.
*/
/*************************************************************************************************/
static void medsPlxInit(void)
{
/* Set configuration pointers */
pAttCfg = (attCfg_t *) &medsPlxAttCfg;
}
/*************************************************************************************************/
/*!
* \brief Button press callback.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medsPlxBtn(dmConnId_t connId, uint8_t btn)
{
appPlxCm_t record;
/* button actions when connected */
if (connId != DM_CONN_ID_NONE)
{
switch (btn)
{
case APP_UI_BTN_2_SHORT:
AppHwPlxcmRead(&record);
record.flags = CH_PLXC_FLAG_PULSE_AMP_INDX;
plxpsSendContinuousMeas(connId, &record);
break;
case APP_UI_BTN_2_MED:
plxpsDbDeleteRecords(CH_RACP_OPERATOR_ALL);
break;
case APP_UI_BTN_1_SHORT:
/* start or complete measurement */
if (!medsPlxCb.measuring)
{
PlxpsMeasStart(connId, MEDS_TIMER_IND, MEDS_PLX_PLX_CM_CCC_IDX);
medsPlxCb.measuring = TRUE;
}
else
{
PlxpsMeasStop();
medsPlxCb.measuring = FALSE;
}
break;
default:
break;
}
}
}
@@ -0,0 +1,165 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Medical sensor sample, weight scale profile
*
* Copyright (c) 2012-2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "hci_api.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_api.h"
#include "app_ui.h"
#include "svc_ch.h"
#include "svc_wss.h"
#include "svc_dis.h"
#include "svc_core.h"
#include "gatt/gatt_api.h"
#include "wsps/wsps_api.h"
#include "meds/meds_main.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! enumeration of client characteristic configuration descriptors */
enum
{
MEDS_WSP_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
MEDS_WSP_WSS_WSM_CCC_IDX, /*! Weight scale service, weight scale measurement characteristic */
MEDS_WSP_NUM_CCC_IDX
};
/**************************************************************************************************
Advertising Data
**************************************************************************************************/
/*! Service UUID list */
static const uint8_t medsSvcUuidList[] =
{
UINT16_TO_BYTES(ATT_UUID_WEIGHT_SCALE_SERVICE),
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE)
};
/**************************************************************************************************
Client Characteristic Configuration Descriptors
**************************************************************************************************/
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
static const attsCccSet_t medsWspCccSet[MEDS_WSP_NUM_CCC_IDX] =
{
/* cccd handle value range security level */
{GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_ENC}, /* MEDS_WSP_GATT_SC_CCC_IDX */
{WSS_WM_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_ENC} /* MEDS_WSP_WSS_WSM_CCC_IDX */
};
/**************************************************************************************************
Local Functions
**************************************************************************************************/
static void medsWspStart(void);
static void medsWspProcMsg(wsfMsgHdr_t *pMsg);
static void medsWspBtn(dmConnId_t connId, uint8_t btn);
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! profile interface pointer */
medsIf_t medsWspIf =
{
NULL,
medsWspStart,
medsWspProcMsg,
medsWspBtn
};
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
static void medsWspStart(void)
{
/* set up CCCD table and callback */
AttsCccRegister(MEDS_WSP_NUM_CCC_IDX, (attsCccSet_t *) medsWspCccSet, medsCccCback);
/* add weight scale service */
SvcWssAddGroup();
/* Set Service Changed CCCD index. */
GattSetSvcChangedIdx(MEDS_WSP_GATT_SC_CCC_IDX);
/* initialize weight scale profile sensor */
WspsSetWsmFlags(CH_WSM_FLAG_UNITS_LBS | CH_WSM_FLAG_TIMESTAMP);
/* set advertising data */
AppAdvSetAdValue(APP_ADV_DATA_DISCOVERABLE, DM_ADV_TYPE_16_UUID, sizeof(medsSvcUuidList),
(uint8_t *) medsSvcUuidList);
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void medsWspProcMsg(wsfMsgHdr_t *pMsg)
{
return;
}
/*************************************************************************************************/
/*!
* \brief Button press callback.
*
* \param connId Connection identifier.
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void medsWspBtn(dmConnId_t connId, uint8_t btn)
{
/* button actions when connected */
if (connId != DM_CONN_ID_NONE)
{
switch (btn)
{
case APP_UI_BTN_1_SHORT:
/* send measurement */
WspsMeasComplete(connId, MEDS_WSP_WSS_WSM_CCC_IDX);
break;
default:
break;
}
}
}
@@ -0,0 +1,121 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Sensor sample application.
*
* Copyright (c) 2015-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef SENSOR_API_H
#define SENSOR_API_H
#include "wsf_types.h"
#include "wsf_os.h"
/**************************************************************************************************
Callback Function Types
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief App extension callback.
*
* \param pMsg Pointer to message structure.
*
* \return None.
*/
/*************************************************************************************************/
typedef void (*sensorExtCback_t)(wsfMsgHdr_t *pMsg);
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Start sensor application.
*
* \return None.
*/
/*************************************************************************************************/
void SensorStart(void);
/*************************************************************************************************/
/*!
* \brief WSF event handler for application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void SensorHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID.
*
* \return None.
*/
/*************************************************************************************************/
void SensorHandlerInit(wsfHandlerId_t handlerId);
/*************************************************************************************************/
/*!
* \brief Register a callback to receive events for the purpose of extending the sensor app.
*
* \param extCback Callback function
*
* \return None.
*/
/*************************************************************************************************/
void SensorRegisterExtensionCback(sensorExtCback_t extCback);
/**************************************************************************************************
Application Functions
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Read gyroscope sensor.
*
* \param pX Storage for gyroscope x-axis reading.
* \param pY Storage for gyroscope y-axis reading.
* \param pZ Storage for gyroscope z-axis reading.
*
* \return None.
*/
/*************************************************************************************************/
bool_t AppReadGyro(int16_t *pX, int16_t *pY, int16_t *pZ);
/*************************************************************************************************/
/*!
* \brief Read temperature sensor.
*
* \param pTemp Storage for temperature reading.
*
* \return None.
*/
/*************************************************************************************************/
bool_t AppReadTemp(int16_t *pTemp);
#endif /* SENSOR_API_H */
@@ -0,0 +1,646 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Sensor sample application.
*
* Copyright (c) 2015-2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "wsf_types.h"
#include "app_api.h"
#include "app_db.h"
#include "app_ui.h"
#include "att_defs.h"
#include "att_api.h"
#include "util/bstream.h"
#include "dm_api.h"
#include "svc_core.h"
#include "svc_dis.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "hci_handler.h"
#include "dm_handler.h"
#include "l2c_handler.h"
#include "att_handler.h"
#include "smp_handler.h"
#include "smp_api.h"
#include "l2c_api.h"
#include "gatt/gatt_api.h"
#include "sensor_api.h"
#include "sensor/gyro_api.h"
#include "svc_gyro.h"
#include "sensor/temp_api.h"
#include "svc_temp.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
#define SENSOR_MSG_START 0xA0
enum
{
SENSOR_GYRO_TIMER_IND = SENSOR_MSG_START,
SENSOR_TEMP_TIMER_IND
};
/*! Advertising timer timeout */
#define SENSOR_ADV_TIMEOUT_SEC 30000
/*! MAX ACL receive length */
#define SENSOR_MAX_RX_ACL_LEN 100
/***************************************************************************************************
Global Variables
***************************************************************************************************/
/* Task handler ID */
static wsfHandlerId_t sensorHandlerId;
/* App extention callback */
static sensorExtCback_t sensorExtCback;
/**************************************************************************************************
Configurable Parameters
**************************************************************************************************/
/*! configurable parameters for slave */
static const appAdvCfg_t sensorAdvCfg =
{
{SENSOR_ADV_TIMEOUT_SEC, 0, 0}, /*! Advertising durations in ms */
{200, 0, 0}, /*! Advertising intervals in 0.625 ms units */
};
/*! configurable parameters for slave */
static appSlaveCfg_t sensorSlaveCfg =
{
1 /*! Maximum connections */
};
/*! configurable parameters for security */
static const appSecCfg_t sensorSecCfg =
{
DM_AUTH_BOND_FLAG, /*! Authentication and bonding flags */
0, /*! Initiator key distribution flags */
DM_KEY_DIST_LTK, /*! Responder key distribution flags */
FALSE, /*! TRUE if Out-of-band pairing data is present */
FALSE /*! TRUE to initiate security upon connection */
};
/*! configurable parameters for connection parameter update */
static const appUpdateCfg_t sensorUpdateCfg =
{
0, /*! Connection idle period in ms before attempting
connection parameter update; set to zero to disable */
640, /*! Minimum connection interval in 1.25ms units */
800, /*! Maximum connection interval in 1.25ms units */
0, /*! Connection latency */
600, /*! Supervision timeout in 10ms units */
5 /*! Number of update attempts before giving up */
};
/**************************************************************************************************
Advertising Data
**************************************************************************************************/
/* advertising data */
static uint8_t sensorAdvDataDisc[HCI_ADV_DATA_LEN]; /* value is set in sensorStart() */
static uint8_t sensorAdvDataConn[HCI_ADV_DATA_LEN]; /* value is set in sensorStart() */
static const uint8_t sensorAdvDataFlags[] =
{
DM_FLAG_LE_LIMITED_DISC | DM_FLAG_LE_BREDR_NOT_SUP
};
/* scan data */
static uint8_t sensorScanDataDisc[HCI_ADV_DATA_LEN]; /* value is set in sensorStart() */
static uint8_t sensorScanDataConn[HCI_ADV_DATA_LEN]; /* value is set in sensorStart() */
static const char sensorScanDataLocalName[] =
{
'S', 'e', 'n', 's', 'o', 'r'
};
/**************************************************************************************************
CCC Data
**************************************************************************************************/
/* enumeration of client characteristic configuration descriptors */
enum
{
APPS_MAIN_GYRO_DATA_CLIENT_CHR_CONFIG_CCC_IDX,
APPS_MAIN_GYRO_TEMPDATA_CLIENT_CHR_CONFIG_CCC_IDX,
APPS_MAIN_TEMP_DATA_CLIENT_CHR_CONFIG_CCC_IDX,
APPS_MAIN_NUM_CCC_IDX
};
/* client characteristic configuration descriptors settings, indexed by above enumeration */
static const attsCccSet_t MainSensor_CccSet[APPS_MAIN_NUM_CCC_IDX] =
{
/* cccd handle value range security level */
{GYRO_HANDLE_DATA_CLIENT_CHR_CONFIG, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE},
{GYRO_HANDLE_TEMPDATA_CLIENT_CHR_CONFIG, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE},
{TEMP_HANDLE_DATA_CLIENT_CHR_CONFIG, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}
};
/*************************************************************************************************/
/*!
* \brief Application DM callback.
*
* \param pDmEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void sensorDmCback(dmEvt_t *pDmEvt)
{
dmEvt_t *pMsg;
uint16_t len;
len = DmSizeOfEvt(pDmEvt);
if ((pMsg = WsfMsgAlloc(len)) != NULL)
{
memcpy(pMsg, pDmEvt, len);
WsfMsgSend(sensorHandlerId, pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Application ATT callback.
*
* \param pAttEvt ATT callback event
*
* \return None.
*/
/*************************************************************************************************/
static void sensorAttCback(attEvt_t *pEvt)
{
return;
}
/*************************************************************************************************/
/*!
* \brief Application CCC callback.
*
* \param pEvt CCC callback event
*
* \return None.
*/
/*************************************************************************************************/
static void sensorCccCback(attsCccEvt_t *pEvt)
{
APP_TRACE_INFO3("ccc state ind value:%d handle:%d idx:%d", pEvt->value, pEvt->handle, pEvt->idx);
switch (pEvt->idx)
{
case APPS_MAIN_GYRO_DATA_CLIENT_CHR_CONFIG_CCC_IDX:
{
if (pEvt->value == ATT_CLIENT_CFG_NOTIFY)
{
GyroMeasStart();
}
else
{
GyroMeasStop();
}
break;
}
case APPS_MAIN_TEMP_DATA_CLIENT_CHR_CONFIG_CCC_IDX:
{
if (pEvt->value == ATT_CLIENT_CFG_NOTIFY)
{
TempMeasStart();
}
else
{
TempMeasStop();
}
break;
}
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Set the system ID.
*
* \return None.
*/
/*************************************************************************************************/
static void sensorSetSystemId(void)
{
uint8_t *bdaddr = HciGetBdAddr();
uint8_t sys_id[8];
/* Formatted according to GATT specification for System ID characteristic (0x2A23). */
sys_id[0] = bdaddr[0];
sys_id[1] = bdaddr[1];
sys_id[2] = bdaddr[2];
sys_id[3] = 0xFE;
sys_id[4] = 0xFF;
sys_id[5] = bdaddr[3];
sys_id[6] = bdaddr[4];
sys_id[7] = bdaddr[5];
AttsSetAttr(DIS_SID_HDL, sizeof(sys_id), sys_id);
}
/*************************************************************************************************/
/*!
* \brief Set up advertising and other procedures that need to be performed after
* device reset.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void sensorSetup(wsfMsgHdr_t *pMsg)
{
/* set advertising response data for discoverable mode */
memset(sensorAdvDataDisc, 0, sizeof(sensorAdvDataDisc));
AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, 0, (uint8_t *) sensorAdvDataDisc);
AppAdvSetAdValue(APP_ADV_DATA_DISCOVERABLE, DM_ADV_TYPE_FLAGS, sizeof(sensorAdvDataFlags),
(uint8_t *) sensorAdvDataFlags);
/* set scan response data for discoverable mode */
memset(sensorScanDataDisc, 0, sizeof(sensorScanDataDisc));
AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, 0, (uint8_t *) sensorScanDataDisc);
AppAdvSetAdValue(APP_SCAN_DATA_DISCOVERABLE, DM_ADV_TYPE_LOCAL_NAME, sizeof(sensorScanDataLocalName), (uint8_t *) sensorScanDataLocalName);
/* set advertising response data for connectable mode */
memset(sensorAdvDataConn, 0, sizeof(sensorAdvDataConn));
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, 0, (uint8_t *) sensorAdvDataConn);
AppAdvSetAdValue(APP_ADV_DATA_CONNECTABLE, DM_ADV_TYPE_FLAGS, sizeof(sensorAdvDataFlags),
(uint8_t *) sensorAdvDataFlags);
/* set scan response data for connectable mode */
memset(sensorScanDataConn, 0, sizeof(sensorScanDataConn));
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, 0, (uint8_t *) sensorScanDataConn);
AppAdvSetAdValue(APP_SCAN_DATA_CONNECTABLE, DM_ADV_TYPE_LOCAL_NAME, sizeof(sensorScanDataLocalName), (uint8_t *) sensorScanDataLocalName);
/* set system ID according to BDADDR */
sensorSetSystemId();
/* start advertising */
AppSetAdvType(DM_ADV_CONN_UNDIRECT);
AppAdvStart(APP_MODE_AUTO_INIT);
}
/*************************************************************************************************/
/*!
* \brief Button press callback.
*
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void sensorBtnCback(uint8_t btn)
{
dmConnId_t connId;
/* button actions when connected */
connId = AppConnIsOpen();
if (connId != DM_CONN_ID_NONE)
{
switch (btn)
{
/* ignore button when connected */
case APP_UI_BTN_1_SHORT:
break;
default:
break;
}
}
/* button actions when not connected */
else
{
switch (btn)
{
case APP_UI_BTN_1_SHORT:
/* start connectable advertising and reset timer */
AppAdvStop();
AppAdvStart(APP_MODE_CONNECTABLE);
break;
default:
break;
}
}
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void sensorProcMsg(wsfMsgHdr_t *pMsg)
{
uint8_t uiEvent = APP_UI_NONE;
switch (pMsg->event)
{
case ATT_MTU_UPDATE_IND:
APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu);
break;
case DM_RESET_CMPL_IND:
{
AttsCalculateDbHash();
sensorSetup(pMsg);
uiEvent = APP_UI_RESET_CMPL;
break;
}
case DM_CONN_OPEN_IND:
{
GyroMeasStart();
TempMeasStart();
uiEvent = APP_UI_CONN_OPEN;
break;
}
case DM_CONN_CLOSE_IND:
{
GyroMeasStop();
TempMeasStop();
uiEvent = APP_UI_CONN_CLOSE;
break;
}
case DM_ADV_START_IND:
{
uiEvent = APP_UI_ADV_START;
break;
}
case DM_ADV_STOP_IND:
{
uiEvent = APP_UI_ADV_STOP;
break;
}
case SENSOR_GYRO_TIMER_IND:
{
dmConnId_t connId = AppConnIsOpen();
if (connId != DM_CONN_ID_NONE)
{
int16_t x, y, z;
if (AppReadGyro(&x, &y, &z))
{
GyroMeasComplete(connId, x, y, z);
}
}
break;
}
case SENSOR_TEMP_TIMER_IND:
{
dmConnId_t connId = AppConnIsOpen();
if (connId != DM_CONN_ID_NONE)
{
int16_t temp;
if (AppReadTemp(&temp))
{
TempMeasComplete(connId, temp);
}
}
break;
}
default:
break;
}
if (uiEvent != APP_UI_NONE)
{
AppUiAction(uiEvent);
}
if (sensorExtCback != NULL)
{
sensorExtCback(pMsg);
}
}
#ifndef APP_SENSOR_OVERRIDE
/*************************************************************************************************/
/*!
* \brief Read gyroscope sensor.
*
* \param pX Storage for gyroscope x-axis reading.
* \param pY Storage for gyroscope y-axis reading.
* \param pZ Storage for gyroscope z-axis reading.
*
* \return None.
*/
/*************************************************************************************************/
bool_t AppReadGyro(int16_t *pX, int16_t *pY, int16_t *pZ)
{
static int16_t x = -100;
static int16_t y = 0;
static int16_t z = 100;
*pX = x++;
*pY = y++;
*pZ = z++;
return TRUE;
}
/*************************************************************************************************/
/*!
* \brief Read temperature sensor.
*
* \param pTemp Storage for temperature reading.
*
* \return None.
*/
/*************************************************************************************************/
bool_t AppReadTemp(int16_t *pTemp)
{
static int16_t temp = 0;
*pTemp = temp++;
return TRUE;
}
#endif
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID.
*
* \return None.
*/
/*************************************************************************************************/
void SensorHandlerInit(wsfHandlerId_t handlerId)
{
/* store handler ID */
sensorHandlerId = handlerId;
/* Set configuration pointers */
pAppAdvCfg = (appAdvCfg_t *)&sensorAdvCfg;
pAppSlaveCfg = (appSlaveCfg_t *)&sensorSlaveCfg;
pAppSecCfg = (appSecCfg_t *)&sensorSecCfg;
pAppUpdateCfg = (appUpdateCfg_t *)&sensorUpdateCfg;
/* Initialize application framework */
AppSlaveInit();
AppServerInit();
}
/*************************************************************************************************/
/*!
* \brief WSF event handler for application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void SensorHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
{
if (pMsg != NULL)
{
APP_TRACE_INFO1("sensor got evt %d", pMsg->event);
/* process ATT messages */
if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
{
/* process server-related ATT messages */
AppServerProcAttMsg(pMsg);
}
/* process DM messages */
else if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
{
/* process advertising and connection-related messages */
AppSlaveProcDmMsg((dmEvt_t *) pMsg);
/* process security-related messages */
AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
}
/* perform profile and user interface-related operations */
sensorProcMsg(pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Initialize stack.
*
* \return None.
*/
/*************************************************************************************************/
static void sensorStackInit(void)
{
SecInit();
SecAesInit();
SecCmacInit();
SecEccInit();
HciHandlerInit(WsfOsSetNextHandler(HciHandler));
DmDevVsInit(0);
DmAdvInit();
DmConnInit();
DmConnSlaveInit();
DmSecInit();
DmHandlerInit(WsfOsSetNextHandler(DmHandler));
L2cSlaveHandlerInit(WsfOsSetNextHandler(L2cSlaveHandler));
L2cInit();
L2cSlaveInit();
AttHandlerInit(WsfOsSetNextHandler(AttHandler));
AttsInit();
AttsIndInit();
SmpHandlerInit(WsfOsSetNextHandler(SmpHandler));
SmprInit();
SmprScInit();
HciSetMaxRxAclLen(SENSOR_MAX_RX_ACL_LEN);
AppHandlerInit(WsfOsSetNextHandler(AppHandler));
SensorHandlerInit(WsfOsSetNextHandler(SensorHandler));
}
/*************************************************************************************************/
/*!
* \brief Start sensor application.
*
* \return None.
*/
/*************************************************************************************************/
void SensorStart(void)
{
APP_TRACE_INFO0("sensor starting app");
/* Initialize stack */
sensorStackInit();
/* Register for stack callbacks */
DmRegister(sensorDmCback);
DmConnRegister(DM_CLIENT_ID_APP, sensorDmCback);
AttRegister(sensorAttCback);
AttConnRegister(AppServerConnCback);
/* Register for app framework callbacks */
AppUiBtnRegister(sensorBtnCback);
/* Initialize attribute server database */
SvcCoreGattCbackRegister(GattReadCback, GattWriteCback);
SvcCoreAddGroup();
SvcDisAddGroup();
GyroStart(sensorHandlerId, SENSOR_GYRO_TIMER_IND);
TempStart(sensorHandlerId, SENSOR_TEMP_TIMER_IND);
/* set up CCCD table and callback */
AttsCccRegister(APPS_MAIN_NUM_CCC_IDX, (attsCccSet_t *) MainSensor_CccSet, sensorCccCback);
/* Reset the device */
DmDevReset();
}
/*************************************************************************************************/
/*!
* \brief Register a callback to receive events for the purpose of extending the sensor app.
*
* \param extCback Callback function
*
* \return None.
*/
/*************************************************************************************************/
void SensorRegisterExtensionCback(sensorExtCback_t extCback)
{
sensorExtCback = extCback;
}
@@ -0,0 +1,73 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Tag sample application interface.
*
* Copyright (c) 2011-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef TAG_API_H
#define TAG_API_H
#include "wsf_os.h"
#ifdef __cplusplus
extern "C" {
#endif
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Start the proximity profile reporter application.
*
* \return None.
*/
/*************************************************************************************************/
void TagStart(void);
/*************************************************************************************************/
/*!
* \brief Proximity reporter handler init function called during system initialization.
*
* \param handlerID WSF handler ID for App.
*
* \return None.
*/
/*************************************************************************************************/
void TagHandlerInit(wsfHandlerId_t handlerId);
/*************************************************************************************************/
/*!
* \brief WSF event handler for proximity reporter.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void TagHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
#ifdef __cplusplus
};
#endif
#endif /* TAG_API_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,121 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief UriBeacon sample application.
*
* Copyright (c) 2011-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef URIBEACON_API_H
#define URIBEACON_API_H
#include "wsf_types.h"
#include "wsf_os.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! UriBeacon parameter IDs */
enum
{
URI_BEACON_PARAM_LOCK_STATE = 4, /*!< lock state (for UriBeacon) [1 byte] */
URI_BEACON_PARAM_URI_DATA = 5, /*!< URI data (for UriBeacon) [18 bytes] */
URI_BEACON_PARAM_URI_FLAGS = 6, /*!< URI flags (for UriBeacon) [1 byte] */
URI_BEACON_PARAM_ADVERTISED_TX_POWER_LEVELS = 7, /*!< advertised tx power levels (for UriBeacon) [4 bytes] */
URI_BEACON_PARAM_TX_POWER_MODE = 8, /*!< tx power mode (for UriBeacon) [1 byte] */
URI_BEACON_PARAM_BEACON_PERIOD = 9, /*!< beacon period (for beacon) [2 bytes] */
URI_BEACON_PARAM_LOCK = 10 /*!< lock [16 bytes] */
};
/**************************************************************************************************
Callback Function Types
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief App extension callback.
*
* \param pMsg Pointer to message structure.
*
* \return None.
*/
/*************************************************************************************************/
typedef void (*uribeaconExtCback_t)(wsfMsgHdr_t *pMsg);
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Start UriBeacon application.
*
* \return None.
*/
/*************************************************************************************************/
void UriBeaconStart(void);
/*************************************************************************************************/
/*!
* \brief WSF event handler for application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void UriBeaconHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID.
*
* \return None.
*/
/*************************************************************************************************/
void UriBeaconHandlerInit(wsfHandlerId_t handlerId);
/*************************************************************************************************/
/*!
* \brief Called prior to starting Uribeacon app to override the beacon data in nv memory.
*
* \param pUriData Pointer to URI data.
* \param dataLen Length of pUriData in bytes.
*
* \return None.
*/
/*************************************************************************************************/
void UriBeaconSetUriOverride(const uint8_t *pUriData, uint8_t dataLen);
/*************************************************************************************************/
/*!
* \brief Register a callback to receive events for the purpose of extending the URI beacon app.
*
* \param extCback Callback function
*
* \return None.
*/
/*************************************************************************************************/
void UriBeaconRegisterExtensionCback(uribeaconExtCback_t extCback);
#endif /* URIBEACON_API_H */
@@ -0,0 +1,973 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief UriBeacon sample application.
*
* Copyright (c) 2011-2019 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#include <string.h>
#include "uribeacon/uricfg_api.h"
#include "uribeacon/uricfg_defs.h"
#include "svc_uricfg.h"
#include "app_api.h"
#include "app_db.h"
#include "app_ui.h"
#include "app_main.h"
#include "att_defs.h"
#include "util/bstream.h"
#include "dm_api.h"
#include "sec_api.h"
#include "svc_core.h"
#include "svc_dis.h"
#include "gatt/gatt_api.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "wsf_types.h"
// #include "wsf_nvm.h"
#include "uribeacon/uribeacon_api.h"
#define WsfNvmWriteData(A, B, C, D) 0
#define WsfNvmReadData(A, B, C, D) 0
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! default lock state */
#define URIBEACON_LOCKSTATE_DEFAULT 0
/*! default URI data value ("http://www.arm.com") */
#define URIBEACON_URIDATA_DEFAULT 0x00, 'a', 'r', 'm', 0x07
/*! clamp beacon period to allowed range */
#define URIBEACON_BEACONPERIOD_CLAMP(bp) (((bp) < URICFG_ATT_BEACONPERIOD_MIN) ? URICFG_ATT_BEACONPERIOD_MIN : \
((bp) > URICFG_ATT_BEACONPERIOD_MAX) ? URICFG_ATT_BEACONPERIOD_MAX : \
(bp))
/*! convert beacon period from ms to 0.625-ms units */
#define URIBEACON_BEACONPERIOD_TO_INTERVAL(bp) (((URIBEACON_BEACONPERIOD_CLAMP(bp)) * 8u) / 5u)
/*! size of advertising information */
#define URIBEACON_INFO_SIZE 24
/*! offsets of information within advertising data */
#define URIBEACON_INFO_OFFSET 3 /*! offset of advertising information within advertising data */
#define URIBEACON_INFO_SVCDATASIZE_OFFSET 7 /*! offset of service data size */
#define URIBEACON_INFO_URIFLAGS_OFFSET 11 /*! offset of URI flags. */
#define URIBEACON_INFO_TXPWRLEVEL_OFFSET 12 /*! offset of Tx power level */
#define URIBEACON_INFO_URIDATA_OFFSET 13 /*! offset of URI data */
/*! default adv Tx power levels */
static const int8_t uriAdvertisedTxPwrLevelsDefault[] = {-80, -60, -40, -20};
/*! WSF message event starting value */
#define URIBEACON_MSG_START 0xA0
/*! WSF message event enumeration */
enum
{
URIBEACON_ADV_TIMER_IND = URIBEACON_MSG_START /*! Advertising timer expired */
};
/*! Advertising timer timeout */
#define URIBEACON_ADV_TIMEOUT_SEC 30
/**************************************************************************************************
Client Characteristic Configuration Descriptors
**************************************************************************************************/
/*! enumeration of client characteristic configuration descriptors */
enum
{
URIBEACON_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
URIBEACON_NUM_CCC_IDX
};
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
static const attsCccSet_t uriBeaconCccSet[URIBEACON_NUM_CCC_IDX] =
{
/* cccd handle value range security level */
{ GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE } /* URIBEACON_GATT_SC_CCC_IDX */
};
/**************************************************************************************************
Global Variables
**************************************************************************************************/
/*! WSF handler ID */
static wsfHandlerId_t uriBeaconHandlerId;
/*! Advertising mode timer */
static wsfTimer_t uriBeaconAdvTimer;
static uint8_t uriBeaconAdvType;
/* App extention callback */
static uribeaconExtCback_t uriBeaconExtCback;
/**************************************************************************************************
Configurable Parameters
**************************************************************************************************/
/*! configurable parameters for advertising */
static appAdvCfg_t uriBeaconAdvCfg =
{
{0, 0, 0}, /*! Advertising durations in ms */
{0, 0, 0}, /*! Advertising intervals in 0.625 ms units */
};
/*! configurable parameters for slave */
static appSlaveCfg_t uriBeaconSlaveCfg =
{
1 /*! Maximum connections */
};
/*! configurable parameters for security */
static const appSecCfg_t uriBeaconSecCfg =
{
DM_AUTH_BOND_FLAG, /*! Authentication and bonding flags */
0, /*! Initiator key distribution flags */
DM_KEY_DIST_LTK, /*! Responder key distribution flags */
FALSE, /*! TRUE if Out-of-band pairing data is present */
FALSE /*! TRUE to initiate security upon connection */
};
/*! configurable parameters for connection parameter update */
static const appUpdateCfg_t uriBeaconUpdateCfg =
{
0, /*! Connection idle period in ms before attempting
connection parameter update; set to zero to disable */
640, /*! Minimum connection interval in 1.25ms units */
800, /*! Maximum connection interval in 1.25ms units */
0, /*! Connection latency */
600, /*! Supervision timeout in 10ms units */
5 /*! Number of update attempts before giving up */
};
/**************************************************************************************************
Advertising Data
**************************************************************************************************/
static int8_t uriBeaconAdvertisedTxPwrLevels[URICFG_SIZE_TXPWRLEVELS_ATT];
static uint8_t uriBeaconTxPwrMode;
static const uint8_t uriBeaconUriData[] = {URIBEACON_URIDATA_DEFAULT};
/* URI data param override */
static uint8_t *uriDataOverride;
static uint8_t uriDataOverrideLen;
/* advertising data, discoverable mode */
static uint8_t uriBeaconAdvDataDisc[HCI_ADV_DATA_LEN] =
{
/* advertising type flags */
1u + sizeof(uint8_t),
DM_ADV_TYPE_FLAGS,
DM_FLAG_LE_LIMITED_DISC | DM_FLAG_LE_BREDR_NOT_SUP,
/* UriBeacon service UUID */
3u,
DM_ADV_TYPE_16_UUID,
UINT16_TO_BYTES(URICFG_SERVICE_UUID),
/* UriBeacon service data */
5u + sizeof(uriBeaconUriData), /* length (5-23) */
DM_ADV_TYPE_SERVICE_DATA,
UINT16_TO_BYTES(URICFG_SERVICE_UUID),
0u, /* flags */
0, /* tx power level */
URIBEACON_URIDATA_DEFAULT /* URI */
};
/* scan data, discoverable and connectable modes */
static uint8_t uriBeaconScanDataDisc[HCI_ADV_DATA_LEN];
static uint8_t uriBeaconScanDataConn[HCI_ADV_DATA_LEN];
static const uint8_t uriBeaconScanDataLocalName[] = { 'U', 'r', 'i', 'B', 'c', 'n' };
static const uint8_t uriBeaconScanData128Uuid[] = { URICFG_UUID_BYTES(URICFG_UUID_SVC) };
static const char * const uriBeaconPrefixes[] =
{
"http://www.", "https://www.", "http://", "https://", "urn:uuid:"
};
static const char * const uriBeaconCodes[] =
{
".com/", ".org/", ".edu/", ".net/", ".info/", ".biz/", ".gov/",
".com", ".org", ".edu", ".net", ".info", ".biz", ".gov",
};
/*************************************************************************************************/
/*!
* \brief Print URI data as URI.
*
* \param pUriData URI data.
*
* \return None.
*/
/*************************************************************************************************/
static void uriBeaconPrintUri(const uint8_t *pUriData)
{
uint8_t c;
uint8_t len = 0u;
char uri[2 * URICFG_MAXSIZE_URIDATA_ATT];
uint8_t uriOffset;
memset(uri, 0, sizeof(uri));
uriOffset = 0;
c = *pUriData++;
len++;
if (c < sizeof(uriBeaconPrefixes) / sizeof(uriBeaconPrefixes[0]))
{
memcpy(uri + uriOffset, uriBeaconPrefixes[c], strlen(uriBeaconPrefixes[c]));
uriOffset += (uint8_t) strlen(uriBeaconPrefixes[c]);
}
else
{
uri[uriOffset] = '?';
APP_TRACE_INFO1("URI = %s", uri);
return;
}
while (len < URICFG_MAXSIZE_URIDATA_ATT)
{
c = *pUriData++;
if ((c > 0x20u) && (c < 0x7F))
{
uri[uriOffset++] = (char)c;
}
else if (c < sizeof(uriBeaconCodes) / sizeof(uriBeaconCodes[0]))
{
memcpy(uri + uriOffset, uriBeaconCodes[c], strlen(uriBeaconCodes[c]));
uriOffset += (uint8_t) strlen(uriBeaconCodes[c]);
}
else
{
break;
}
}
APP_TRACE_INFO1("URI = %s", uri);
}
/*************************************************************************************************/
/*!
* \brief Set advertising type.
*
* \param advType Advertising type
*
* \return None.
*/
/*************************************************************************************************/
static void uriBeaconSetAdvType(uint8_t advType)
{
uriBeaconAdvType = advType;
AppSetAdvType(advType);
}
/*************************************************************************************************/
/*!
* \brief Update URI data in advertising data.
*
* \param pUriData URI data.
* \param uriDataLen URI data length.
*
* \return None.
*/
/*************************************************************************************************/
static void uriBeaconUpdateAdvUriData(const uint8_t *pUriData, uint8_t uriDataLen)
{
uint16_t len;
for (len = 0u; len < uriDataLen; len++)
{
if (pUriData[len] == 0xFFu)
{
break;
}
}
memcpy(&uriBeaconAdvDataDisc[URIBEACON_INFO_URIDATA_OFFSET], pUriData, len);
uriBeaconAdvDataDisc[URIBEACON_INFO_SVCDATASIZE_OFFSET] = 5u + len;
}
/*************************************************************************************************/
/*!
* \brief Update URI flags in advertising data.
*
* \param uriFlags URI flags.
*
* \return None.
*/
/*************************************************************************************************/
static void uriBeaconUpdateAdvUriFlags(uint8_t uriFlags)
{
uriBeaconAdvDataDisc[URIBEACON_INFO_URIFLAGS_OFFSET] = uriFlags;
}
/*************************************************************************************************/
/*!
* \brief Update Tx power level in advertising data.
*
* \param txPwrLevel Tx power level.
*
* \return None.
*/
/*************************************************************************************************/
static void uriBeaconUpdateAdvTxPwrLevel(int8_t txPwrLevel)
{
uriBeaconAdvDataDisc[URIBEACON_INFO_TXPWRLEVEL_OFFSET] = (uint8_t)txPwrLevel;
/* TODO: SEND VS COMMAND TO ACTUALLY CHANGE POWER LEVEL */
}
/*************************************************************************************************/
/*!
* \brief Update beacon period (advertising interval).
*
* \param beaconPeriod Beacon period in milliseconds.
*
* \return None.
*/
/*************************************************************************************************/
static void uriBeaconUpdateBeaconPeriod(uint16_t beaconPeriod)
{
/* this never occurs while we're advertising, so we just need to update the datum */
if (beaconPeriod == URICFG_ATT_BEACONPERIOD_DISABLE)
{
uriBeaconAdvCfg.advInterval[0] = 0;
}
else
{
uriBeaconAdvCfg.advInterval[0] = URIBEACON_BEACONPERIOD_TO_INTERVAL(beaconPeriod);
}
}
/*************************************************************************************************/
/*!
* \brief Set the system ID.
*
* \return None.
*/
/*************************************************************************************************/
static void uriBeaconSetSystemId(void)
{
uint8_t *bdaddr = HciGetBdAddr();
uint8_t sysId[8];
/* formatted according to GATT specification for System ID characteristic (0x2A23) */
sysId[0] = bdaddr[0];
sysId[1] = bdaddr[1];
sysId[2] = bdaddr[2];
sysId[3] = 0xFE;
sysId[4] = 0xFF;
sysId[5] = bdaddr[3];
sysId[6] = bdaddr[4];
sysId[7] = bdaddr[5];
AttsSetAttr(DIS_SID_HDL, sizeof(sysId), sysId);
}
/*************************************************************************************************/
/*!
* \brief Assign a random address.
*
* \return None.
*/
/*************************************************************************************************/
static void uriBeaconAssignRandomAddress(void)
{
uint8_t addr[BDA_ADDR_LEN];
SecRand(addr, BDA_ADDR_LEN);
DM_RAND_ADDR_SET(addr, DM_RAND_ADDR_STATIC);
DmDevSetRandAddr(addr);
DmAdvSetAddrType(DM_ADDR_RANDOM);
}
/*************************************************************************************************/
/*!
* \brief Application DM callback.
*
* \param pDmEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void uriBeaconDmCback(dmEvt_t *pDmEvt)
{
dmEvt_t *pMsg;
uint16_t len;
len = DmSizeOfEvt(pDmEvt);
if ((pMsg = WsfMsgAlloc(len)) != NULL)
{
memcpy(pMsg, pDmEvt, len);
WsfMsgSend(uriBeaconHandlerId, pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Application ATT callback.
*
* \param pEvt ATT callback event
*
* \return None.
*/
/*************************************************************************************************/
static void uriBeaconAttCback(attEvt_t *pEvt)
{
}
/*************************************************************************************************/
/*!
* \brief Application ATTS client characteristic configuration callback.
*
* \param pDmEvt DM callback event
*
* \return None.
*/
/*************************************************************************************************/
static void uriBeaconCccCback(attsCccEvt_t *pEvt)
{
appDbHdl_t dbHdl;
/* If CCC not set from initialization and there's a device record and currently bonded */
if ((pEvt->handle != ATT_HANDLE_NONE) &&
((dbHdl = AppDbGetHdl((dmConnId_t)pEvt->hdr.param)) != APP_DB_HDL_NONE) &&
AppCheckBonded((dmConnId_t)pEvt->hdr.param))
{
/* Store value in device database. */
AppDbSetCccTblValue(dbHdl, pEvt->idx, pEvt->value);
}
}
/*************************************************************************************************/
/*!
* \brief Attribute write callback.
*
* \param handle Attribute handle.
* \param valueLen Length of value data.
* \param pValue Pointer to value data.
*
* \return None.
*/
/*************************************************************************************************/
static void uriBeaconAttWriteCback(uint16_t handle, uint16_t valueLen, const uint8_t *pValue)
{
uint8_t id;
bool_t updatedAdvData = FALSE;
uint8_t uriData[URICFG_MAXSIZE_URIDATA_ATT];
switch (handle)
{
case URICFG_HANDLE_URIDATA:
{
/* pad data to maximum length so setting does not need to resize */
memset(uriData, 0xFF, sizeof(uriData));
memcpy(uriData, pValue, valueLen);
pValue = uriData;
valueLen = URICFG_MAXSIZE_URIDATA_ATT;
APP_TRACE_INFO0("URI upd URI data");
uriBeaconPrintUri(uriData);
uriBeaconUpdateAdvUriData(uriData, (uint8_t) valueLen);
updatedAdvData = TRUE;
id = URI_BEACON_PARAM_URI_DATA;
break;
}
case URICFG_HANDLE_URIFLAGS:
{
APP_TRACE_INFO1("URI upd URI flags %02X", *pValue);
uriBeaconUpdateAdvUriFlags(*pValue);
updatedAdvData = TRUE;
id = URI_BEACON_PARAM_URI_FLAGS;
break;
}
case URICFG_HANDLE_TXPWRLEVELS:
{
APP_TRACE_INFO2("URI upd adv tx pwr lvls {%d, %d,", pValue[0], pValue[1]);
APP_TRACE_INFO2(" %d, %d}", pValue[2], pValue[3]);
memcpy(uriBeaconAdvertisedTxPwrLevels, pValue, valueLen);
uriBeaconUpdateAdvTxPwrLevel(uriBeaconAdvertisedTxPwrLevels[uriBeaconTxPwrMode]);
updatedAdvData = TRUE;
id = URI_BEACON_PARAM_ADVERTISED_TX_POWER_LEVELS;
break;
}
case URICFG_HANDLE_TXPWRMODE:
{
APP_TRACE_INFO1("URI upd tx pwr mode %u", *pValue);
uriBeaconTxPwrMode = *pValue;
uriBeaconUpdateAdvTxPwrLevel(uriBeaconAdvertisedTxPwrLevels[uriBeaconTxPwrMode]);
updatedAdvData = TRUE;
id = URI_BEACON_PARAM_TX_POWER_MODE;
break;
}
case URICFG_HANDLE_BEACONPERIOD:
{
uint16_t beaconPeriod;
BYTES_TO_UINT16(beaconPeriod, pValue);
APP_TRACE_INFO1("URI upd beacon period %u", beaconPeriod);
uriBeaconUpdateBeaconPeriod(beaconPeriod);
if (beaconPeriod == URICFG_ATT_BEACONPERIOD_DISABLE)
{
/* do not store 'disable' beacon period */
return;
}
id = URI_BEACON_PARAM_BEACON_PERIOD;
break;
}
case URICFG_HANDLE_LOCK:
{
/* only called during reset */
APP_TRACE_INFO0("URI upd lock");
id = URI_BEACON_PARAM_LOCK;
break;
}
default:
{
APP_TRACE_INFO0("URI upd unknown attr");
return;
}
}
if (!WsfNvmWriteData(id, pValue, valueLen, NULL))
{
APP_TRACE_ERR0("URI failed to wr hostcfg");
}
if (updatedAdvData)
{
/* Set as Connectable so that data is updated after connection closes. */
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, sizeof(uriBeaconAdvDataDisc), (uint8_t *)uriBeaconAdvDataDisc);
}
}
/*************************************************************************************************/
/*!
* \brief Lock change callback.
*
* \param lockState New lock state.
* \param lock Lock value.
*
* \return None.
*/
/*************************************************************************************************/
static void uriBeaconLockChangeCback(uint8_t lockState, const uint8_t lock[URICFG_SIZE_LOCK_ATT])
{
APP_TRACE_INFO1("URI upd lock state %u", lockState);
/* only update lock if locking */
if (lockState)
{
if (!WsfNvmWriteData(URI_BEACON_PARAM_LOCK, lock, URICFG_SIZE_LOCK_ATT, NULL))
{
APP_TRACE_ERR0("URI failed to wr lock state");
return;
}
}
/* always update lock state */
if (!WsfNvmWriteData(URI_BEACON_PARAM_LOCK_STATE, &lockState, sizeof(lockState), NULL))
{
APP_TRACE_ERR0("URI failed to wr lcok state");
return;
}
}
/*************************************************************************************************/
/*!
* \brief Set up advertising and other procedures that need to be performed after
* device reset.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void uriBeaconSetup(wsfMsgHdr_t *pMsg)
{
/* set advertising and scan response data for discoverable mode */
AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(uriBeaconAdvDataDisc), (uint8_t *)uriBeaconAdvDataDisc);
/* set scan response data for discoverable mode */
memset(uriBeaconScanDataDisc, 0, sizeof(uriBeaconScanDataDisc));
AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, 0, (uint8_t *) uriBeaconScanDataDisc);
AppAdvSetAdValue(APP_SCAN_DATA_DISCOVERABLE, DM_ADV_TYPE_LOCAL_NAME, sizeof(uriBeaconScanDataLocalName), (uint8_t *) uriBeaconScanDataLocalName);
AppAdvSetAdValue(APP_SCAN_DATA_DISCOVERABLE, DM_ADV_TYPE_128_UUID, sizeof(uriBeaconScanData128Uuid), (uint8_t *) uriBeaconScanData128Uuid);
/* set advertising and scan response data for connectable mode */
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, sizeof(uriBeaconAdvDataDisc), (uint8_t *)uriBeaconAdvDataDisc);
/* set scan response data for connectable mode */
memset(uriBeaconScanDataDisc, 0, sizeof(uriBeaconScanDataDisc));
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, 0, (uint8_t *) uriBeaconScanDataConn);
AppAdvSetAdValue(APP_SCAN_DATA_CONNECTABLE, DM_ADV_TYPE_LOCAL_NAME, sizeof(uriBeaconScanDataLocalName), (uint8_t *) uriBeaconScanDataLocalName);
AppAdvSetAdValue(APP_SCAN_DATA_CONNECTABLE, DM_ADV_TYPE_128_UUID, sizeof(uriBeaconScanData128Uuid), (uint8_t *) uriBeaconScanData128Uuid);
/* set system ID according to BDADDR */
uriBeaconSetSystemId();
/* assign a random address before we start advertising */
uriBeaconAssignRandomAddress();
/* start timer for advertising mode change */
memset(&uriBeaconAdvTimer, 0, sizeof(wsfTimer_t));
uriBeaconAdvTimer.handlerId = uriBeaconHandlerId;
uriBeaconAdvTimer.msg.event = URIBEACON_ADV_TIMER_IND;
WsfTimerStartSec(&uriBeaconAdvTimer, URIBEACON_ADV_TIMEOUT_SEC);
/* start advertising */
uriBeaconSetAdvType(DM_ADV_CONN_UNDIRECT);
AppAdvStart(APP_MODE_CONNECTABLE);
}
/*************************************************************************************************/
/*!
* \brief Button press callback.
*
* \param btn Button press.
*
* \return None.
*/
/*************************************************************************************************/
static void uriBeaconBtnCback(uint8_t btn)
{
/* button actions when connected */
if (AppConnIsOpen() != DM_CONN_ID_NONE)
{
switch (btn)
{
/* ignore button when connected */
case APP_UI_BTN_1_SHORT:
break;
default:
break;
}
}
/* button actions when not connected */
else
{
switch (btn)
{
case APP_UI_BTN_1_SHORT:
/* start connectable advertising and reset timer */
uriBeaconSetAdvType(DM_ADV_CONN_UNDIRECT);
WsfTimerStartSec(&uriBeaconAdvTimer, URIBEACON_ADV_TIMEOUT_SEC);
break;
default:
break;
}
}
}
/*************************************************************************************************/
/*!
* \brief Process messages from the event handler.
*
* \param pMsg Pointer to message.
*
* \return None.
*/
/*************************************************************************************************/
static void uriBeaconProcMsg(wsfMsgHdr_t *pMsg)
{
uint8_t uiEvent = APP_UI_NONE;
switch (pMsg->event)
{
/* revert to non-connectable advertising */
case URIBEACON_ADV_TIMER_IND:
uriBeaconSetAdvType(DM_ADV_NONCONN_UNDIRECT);
break;
case DM_ADV_START_IND:
if (pMsg->status == HCI_SUCCESS)
{
uiEvent = (uriBeaconAdvType == DM_ADV_NONCONN_UNDIRECT) ? APP_UI_DISCOVERABLE : APP_UI_ADV_START;
}
break;
case ATT_MTU_UPDATE_IND:
APP_TRACE_INFO1("Negotiated MTU %d", ((attEvt_t *)pMsg)->mtu);
break;
case DM_RESET_CMPL_IND:
AttsCalculateDbHash();
uriBeaconSetup(pMsg);
uiEvent = APP_UI_RESET_CMPL;
break;
/* stop advertising timeout */
case DM_CONN_OPEN_IND:
WsfTimerStop(&uriBeaconAdvTimer);
uiEvent = APP_UI_CONN_OPEN;
break;
/* re-start connectable advertising, with timeout */
case DM_CONN_CLOSE_IND:
uriBeaconSetAdvType(DM_ADV_CONN_UNDIRECT);
uriBeaconAdvTimer.handlerId = uriBeaconHandlerId;
uriBeaconAdvTimer.msg.event = URIBEACON_ADV_TIMER_IND;
WsfTimerStartSec(&uriBeaconAdvTimer, URIBEACON_ADV_TIMEOUT_SEC);
uiEvent = APP_UI_ADV_START;
break;
default:
break;
}
if (uiEvent != APP_UI_NONE)
{
AppUiAction(uiEvent);
}
if (uriBeaconExtCback != NULL)
{
uriBeaconExtCback(pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID.
*
* \return None.
*/
/*************************************************************************************************/
void UriBeaconHandlerInit(wsfHandlerId_t handlerId)
{
/* store handler ID */
uriBeaconHandlerId = handlerId;
/* set configuration pointers */
pAppAdvCfg = (appAdvCfg_t *)&uriBeaconAdvCfg;
pAppSlaveCfg = (appSlaveCfg_t *)&uriBeaconSlaveCfg;
pAppSecCfg = (appSecCfg_t *)&uriBeaconSecCfg;
pAppUpdateCfg = (appUpdateCfg_t *)&uriBeaconUpdateCfg;
/* initialize application framework */
AppSlaveInit();
AppServerInit();
}
/*************************************************************************************************/
/*!
* \brief WSF event handler for application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void UriBeaconHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
{
if (pMsg != NULL)
{
APP_TRACE_INFO1("UriBeacon got evt %d", pMsg->event);
/* process ATT messages */
if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
{
/* process server-related ATT messages */
AppServerProcAttMsg(pMsg);
}
/* process DM messages */
else if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
{
/* process advertising and connection-related messages */
AppSlaveProcDmMsg((dmEvt_t *) pMsg);
/* process security-related messages */
AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
}
/* perform profile and user interface-related operations */
uriBeaconProcMsg(pMsg);
}
}
/*************************************************************************************************/
/*!
* \brief Start UriBeacon application.
*
* \return None.
*/
/*************************************************************************************************/
void UriBeaconStart(void)
{
uint8_t lockState;
uint8_t uriData[URICFG_MAXSIZE_URIDATA_ATT];
uint8_t uriDataLen = 0;
uint8_t uriDataReset[URICFG_MAXSIZE_URIDATA_ATT];
uint8_t uriFlags;
uint16_t beaconPeriod;
uint8_t lock[URICFG_SIZE_LOCK_ATT];
APP_TRACE_INFO0("URI starting app");
lockState = URIBEACON_LOCKSTATE_DEFAULT;
/* try to load settings from NV memory */
if (WsfNvmReadData(URI_BEACON_PARAM_LOCK_STATE, (uint8_t *)&lockState, sizeof(lockState), NULL) )
{
APP_TRACE_INFO1("URI rd lock state from param DB %u", lockState);
}
else
{
lockState = URIBEACON_LOCKSTATE_DEFAULT;
}
if (uriDataOverride != NULL)
{
uriDataLen = uriDataOverrideLen;
memcpy(uriData, uriDataOverride, uriDataLen);
}
else if (WsfNvmReadData(URI_BEACON_PARAM_URI_DATA, &uriData[0], sizeof(uriData), NULL))
{
APP_TRACE_INFO0("URI rd URI data from param DB ");
uriBeaconPrintUri(uriData);
uint8_t *pUriData = uriData;
while ((*pUriData != 0xFF) && (uriDataLen < URICFG_MAXSIZE_URIDATA_ATT))
{
pUriData++;
uriDataLen++;
}
}
else
{
memcpy(uriData, uriBeaconUriData, sizeof(uriBeaconUriData));
uriDataLen = sizeof(uriBeaconUriData);
}
uriFlags = URICFG_ATT_URIFLAGS_DEFAULT;
if (WsfNvmReadData(URI_BEACON_PARAM_URI_FLAGS, (uint8_t *)&uriFlags, sizeof(uriFlags), NULL))
{
APP_TRACE_INFO1("URI rd URI flags from param DB %02X", uriFlags);
}
else
{
uriFlags = URICFG_ATT_URIFLAGS_DEFAULT;
}
if (WsfNvmReadData(URI_BEACON_PARAM_ADVERTISED_TX_POWER_LEVELS, (uint8_t *)&uriBeaconAdvertisedTxPwrLevels, sizeof(uriBeaconAdvertisedTxPwrLevels), NULL))
{
APP_TRACE_INFO2("URI rd adv tx pwr lvls from param DB {%d, %d,", uriBeaconAdvertisedTxPwrLevels[0], uriBeaconAdvertisedTxPwrLevels[1]);
APP_TRACE_INFO2(" %d, %d}", uriBeaconAdvertisedTxPwrLevels[2], uriBeaconAdvertisedTxPwrLevels[3]);
}
else
{
memcpy(uriBeaconAdvertisedTxPwrLevels, uriAdvertisedTxPwrLevelsDefault, sizeof(uriBeaconAdvertisedTxPwrLevels));
}
if (WsfNvmReadData(URI_BEACON_PARAM_TX_POWER_MODE, (uint8_t *)&uriBeaconTxPwrMode, sizeof(uriBeaconTxPwrMode), NULL))
{
APP_TRACE_INFO1("URI rd tx pwr mode from param DB %u", uriBeaconTxPwrMode);
}
else
{
uriBeaconTxPwrMode = URICFG_ATT_TXPWRMODE_DEFAULT;
}
beaconPeriod = URICFG_ATT_BEACONPERIOD_DEFAULT;
if (WsfNvmReadData(URI_BEACON_PARAM_BEACON_PERIOD, (uint8_t *)&beaconPeriod, sizeof(beaconPeriod), NULL))
{
APP_TRACE_INFO1("URI rd bcn per from param DB %u", beaconPeriod);
}
else
{
beaconPeriod = URICFG_ATT_BEACONPERIOD_DEFAULT;
}
if (WsfNvmReadData(URI_BEACON_PARAM_LOCK, &lock[0], sizeof(lock), NULL))
{
APP_TRACE_INFO0("URI rd lock from param DB");
}
else
{
memset(lock, 0x00u, sizeof(lock));
}
/* register for stack callbacks */
DmRegister(uriBeaconDmCback);
DmConnRegister(DM_CLIENT_ID_APP, uriBeaconDmCback);
AttRegister(uriBeaconAttCback);
AttConnRegister(AppServerConnCback);
AttsCccRegister(URIBEACON_NUM_CCC_IDX, (attsCccSet_t *)uriBeaconCccSet, uriBeaconCccCback);
/* set advertising and scan response data for discoverable mode */
uriBeaconUpdateAdvUriData(uriData, uriDataLen);
uriBeaconUpdateAdvUriFlags(uriFlags);
uriBeaconUpdateAdvTxPwrLevel(uriBeaconAdvertisedTxPwrLevels[uriBeaconTxPwrMode]);
/* register for app framework callbacks */
AppUiBtnRegister(uriBeaconBtnCback);
/* initialize attribute server database */
SvcCoreGattCbackRegister(GattReadCback, GattWriteCback);
SvcCoreAddGroup();
SvcDisAddGroup();
UriCfgStart(uriData, uriDataLen, uriFlags, uriBeaconAdvertisedTxPwrLevels, uriBeaconTxPwrMode, beaconPeriod);
UriCfgAttWriteCbackRegister(uriBeaconAttWriteCback);
UriCfgMakeLockable(lockState, lock, uriBeaconLockChangeCback);
memset(uriDataReset, 0xFFu, sizeof(uriDataReset));
memcpy(uriDataReset, uriBeaconUriData, sizeof(uriBeaconUriData));
UriCfgSetUriDataResetValue(uriDataReset);
GattSetSvcChangedIdx(URIBEACON_GATT_SC_CCC_IDX);
/* update beacon period (advertising interval) */
uriBeaconUpdateBeaconPeriod(beaconPeriod);
/* reset the device */
DmDevReset();
}
/*************************************************************************************************/
/*!
* \brief Called prior to starting Uribeacon app to override the beacon data in nv memory.
*
* \param pUriData Pointer to URI data.
* \param dataLen Length of pUriData in bytes.
*
* \return None.
*/
/*************************************************************************************************/
void UriBeaconSetUriOverride(const uint8_t *pUriData, uint8_t dataLen)
{
uriDataOverride = (uint8_t *) pUriData;
uriDataOverrideLen = dataLen;
}
/*************************************************************************************************/
/*!
* \brief Register a callback to receive events for the purpose of extending the URI beacon app.
*
* \param extCback Callback function
*
* \return None.
*/
/*************************************************************************************************/
void UriBeaconRegisterExtensionCback(uribeaconExtCback_t extCback)
{
uriBeaconExtCback = extCback;
}
@@ -0,0 +1,73 @@
/*************************************************************************************************/
/*!
* \file
*
* \brief Watch sample application interface.
*
* Copyright (c) 2011-2018 Arm Ltd.
*
* Copyright (c) 2019 Packetcraft, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*************************************************************************************************/
#ifndef WATCH_API_H
#define WATCH_API_H
#include "wsf_os.h"
#ifdef __cplusplus
extern "C" {
#endif
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
/*************************************************************************************************/
/*!
* \brief Start the application.
*
* \return None.
*/
/*************************************************************************************************/
void WatchStart(void);
/*************************************************************************************************/
/*!
* \brief Application handler init function called during system initialization.
*
* \param handlerID WSF handler ID for App.
*
* \return None.
*/
/*************************************************************************************************/
void WatchHandlerInit(wsfHandlerId_t handlerId);
/*************************************************************************************************/
/*!
* \brief WSF event handler for the application.
*
* \param event WSF event mask.
* \param pMsg WSF message.
*
* \return None.
*/
/*************************************************************************************************/
void WatchHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
#ifdef __cplusplus
};
#endif
#endif /* WATCH_API_H */